Skip to content

Commit 10bd962

Browse files
committed
chore: background charm service as a library, iterate on bgAdmin recipe
1 parent 1c0a023 commit 10bd962

File tree

14 files changed

+391
-238
lines changed

14 files changed

+391
-238
lines changed

background-charm-service/cast-admin.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
setBobbyServerUrl,
77
storage,
88
} from "@commontools/runner";
9-
import { type DID, Identity } from "@commontools/identity";
9+
import { type DID } from "@commontools/identity";
1010
import { createAdminSession } from "@commontools/identity";
1111
import {
1212
BG_CELL_CAUSE,
1313
BG_SYSTEM_SPACE_ID,
14-
bgUpdaterCharmsSchema,
15-
} from "@commontools/utils";
14+
BGCharmEntriesSchema,
15+
} from "./src/schema.ts";
1616
import { getIdentity } from "./src/utils.ts";
1717

1818
const { recipePath, quit } = parseArgs(
@@ -79,7 +79,7 @@ async function castRecipe() {
7979
const targetCell = getCell(
8080
spaceId as DID,
8181
cause,
82-
bgUpdaterCharmsSchema,
82+
BGCharmEntriesSchema,
8383
);
8484

8585
// Ensure the cell is synced

background-charm-service/deno.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"name": "@commontools/background-charm",
23
"tasks": {
34
"start": "deno run -A --unstable-worker-options src/main.ts",
45
"help": "deno run -A src/main.ts --help",
@@ -8,5 +9,9 @@
89
"lint": "deno lint",
910
"fmt": "deno fmt"
1011
},
11-
"imports": {}
12+
"imports": {},
13+
"exports": {
14+
".": "./src/lib.ts",
15+
"./schema": "./src/schema.ts"
16+
}
1217
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export {
2+
BackgroundCharmService,
3+
type BackgroundCharmServiceOptions,
4+
} from "./service.ts";
5+
export {
6+
BG_CELL_CAUSE,
7+
BG_SYSTEM_SPACE_ID,
8+
type BGCharmEntry,
9+
BGCharmEntrySchema,
10+
} from "./schema.ts";
11+
export { setBGCharm } from "./utils.ts";

background-charm-service/src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { storage } from "@commontools/runner";
12
import { BackgroundCharmService } from "./service.ts";
23
import { getIdentity, log } from "./utils.ts";
34
import { env } from "./env.ts";
@@ -6,6 +7,7 @@ const identity = await getIdentity(env.IDENTITY, env.OPERATOR_PASS);
67
const service = new BackgroundCharmService({
78
identity,
89
toolshedUrl: env.TOOLSHED_API_URL,
10+
storage,
911
});
1012

1113
const shutdown = () => {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { type JSONSchema, type Schema } from "@commontools/builder";
2+
3+
// This is the derived space id for toolshed-system
4+
export const BG_SYSTEM_SPACE_ID =
5+
"did:key:z6Mkfuw7h6jDwqVb6wimYGys14JFcyTem4Kqvdj9DjpFhY88";
6+
export const BG_CELL_CAUSE = "bgUpdater-2025-03-18";
7+
export const BGCharmEntrySchema = {
8+
type: "object",
9+
properties: {
10+
space: { type: "string" },
11+
charmId: { type: "string" },
12+
integration: { type: "string" },
13+
createdAt: { type: "number" },
14+
updatedAt: { type: "number" },
15+
disabledAt: { type: "number", default: 0 },
16+
lastRun: { type: "number", default: 0 },
17+
status: { type: "string", default: "" },
18+
},
19+
required: [
20+
"space",
21+
"charmId",
22+
"integration",
23+
"createdAt",
24+
"updatedAt",
25+
"lastRun",
26+
"status",
27+
],
28+
} as const as JSONSchema;
29+
export type BGCharmEntry = Schema<typeof BGCharmEntrySchema>;
30+
31+
export const BGCharmEntriesSchema = {
32+
type: "array",
33+
items: BGCharmEntrySchema,
34+
default: [],
35+
} as const satisfies JSONSchema;
36+
37+
export type BGCharmEntries = Schema<typeof BGCharmEntriesSchema>;

background-charm-service/src/service.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { Identity } from "@commontools/identity";
2-
import { type Cell, storage } from "@commontools/runner";
3-
import { type BGCharmEntry, getBGUpdaterCharmsCell } from "@commontools/utils";
4-
import { log } from "./utils.ts";
2+
import { type Cell, type Storage } from "@commontools/runner";
3+
import {
4+
BG_CELL_CAUSE,
5+
BG_SYSTEM_SPACE_ID,
6+
type BGCharmEntry,
7+
} from "./schema.ts";
8+
import { getBGCharms, log } from "./utils.ts";
59
import { SpaceManager } from "./space-manager.ts";
610
import { useCancelGroup } from "@commontools/runner";
711

812
export interface BackgroundCharmServiceOptions {
913
identity: Identity;
1014
toolshedUrl: string;
15+
storage: Storage;
16+
bgSpace?: string;
17+
bgCause?: string;
1118
}
1219

1320
export class BackgroundCharmService {
@@ -16,18 +23,24 @@ export class BackgroundCharmService {
1623
private charmSchedulers: Map<string, SpaceManager> = new Map();
1724
private identity: Identity;
1825
private toolshedUrl: string;
26+
private storage: Storage;
27+
private bgSpace: string;
28+
private bgCause: string;
1929

2030
constructor(options: BackgroundCharmServiceOptions) {
2131
this.identity = options.identity;
2232
this.toolshedUrl = options.toolshedUrl;
33+
this.storage = options.storage;
34+
this.bgSpace = options.bgSpace ?? BG_SYSTEM_SPACE_ID;
35+
this.bgCause = options.bgCause ?? BG_CELL_CAUSE;
2336
}
2437

2538
async initialize() {
26-
storage.setRemoteStorage(new URL(this.toolshedUrl));
27-
storage.setSigner(this.identity);
28-
this.charmsCell = await getBGUpdaterCharmsCell();
29-
await storage.syncCell(this.charmsCell, true);
30-
await storage.synced();
39+
this.storage.setRemoteStorage(new URL(this.toolshedUrl));
40+
this.storage.setSigner(this.identity);
41+
this.charmsCell = await getBGCharms({ bgSpace: this.bgSpace, bgCause: this.bgCause, storage: this.storage });
42+
await this.storage.syncCell(this.charmsCell, true);
43+
await this.storage.synced();
3144

3245
if (this.isRunning) {
3346
log("Service is already running");

background-charm-service/src/space-manager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { BGCharmEntry, sleep } from "@commontools/utils";
1+
import { sleep } from "@commontools/utils";
22
import { Cell } from "@commontools/runner";
3+
import { type Cancel, useCancelGroup } from "@commontools/runner";
34
import {
45
WorkerController,
56
WorkerControllerErrorEvent,
67
type WorkerOptions,
78
} from "./worker-controller.ts";
8-
import { type Cancel, useCancelGroup } from "@commontools/runner";
9+
import { type BGCharmEntry } from "./schema.ts";
910

1011
export interface CharmSchedulerOptions extends WorkerOptions {
1112
pollingIntervalMs?: number;

background-charm-service/src/utils.ts

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { Charm } from "@commontools/charm";
2-
import { Cell, getEntityId } from "@commontools/runner";
2+
import {
3+
type Cell,
4+
getCell,
5+
getEntityId,
6+
type Storage,
7+
} from "@commontools/runner";
38
import { Identity, type IdentityCreateConfig } from "@commontools/identity";
9+
import { ID, type JSONSchema } from "@commontools/builder";
10+
import {
11+
BG_CELL_CAUSE,
12+
BG_SYSTEM_SPACE_ID,
13+
type BGCharmEntry,
14+
BGCharmEntrySchema,
15+
} from "./schema.ts";
416

517
/**
618
* Custom logger that includes timestamp and optionally charm ID
@@ -87,3 +99,108 @@ export async function getIdentity(
8799
}
88100
throw new Error("No IDENTITY or OPERATOR_PASS environemnt set.");
89101
}
102+
103+
export async function setBGCharm({
104+
space,
105+
charmId,
106+
integration,
107+
storage,
108+
bgSpace,
109+
bgCause,
110+
}: {
111+
space: string;
112+
charmId: string;
113+
integration: string;
114+
storage: Storage;
115+
bgSpace?: string;
116+
bgCause?: string;
117+
}): Promise<boolean> {
118+
const charmsCell = await getBGCharms({
119+
bgSpace,
120+
bgCause,
121+
storage,
122+
});
123+
124+
console.log(
125+
"charmsCell",
126+
JSON.stringify(charmsCell.getAsCellLink(), null, 2),
127+
);
128+
129+
const charms = charmsCell.get() || [];
130+
131+
const existingCharmIndex = charms.findIndex(
132+
(charm: Cell<BGCharmEntry>) =>
133+
charm.get().space === space && charm.get().charmId === charmId,
134+
);
135+
136+
if (existingCharmIndex === -1) {
137+
console.log("Adding charm to BGUpdater charms cell");
138+
charmsCell.push({
139+
[ID]: `${space}/${charmId}`,
140+
space,
141+
charmId,
142+
integration,
143+
createdAt: Date.now(),
144+
updatedAt: Date.now(),
145+
disabledAt: undefined,
146+
lastRun: 0,
147+
status: "Initializing",
148+
} as unknown as Cell<BGCharmEntry>);
149+
150+
// Ensure changes are synced
151+
await storage.synced();
152+
153+
return true;
154+
} else {
155+
console.log("Charm already exists in BGUpdater charms cell, re-enabling");
156+
const existingCharm = charms[existingCharmIndex];
157+
existingCharm.update({
158+
disabledAt: 0,
159+
updatedAt: Date.now(),
160+
status: "Re-initializing",
161+
});
162+
163+
await storage.synced();
164+
165+
return false;
166+
}
167+
}
168+
169+
export async function getBGCharms(
170+
{ bgSpace, bgCause, storage }: {
171+
bgSpace?: string;
172+
bgCause?: string;
173+
storage: Storage;
174+
},
175+
): Promise<
176+
Cell<Cell<BGCharmEntry>[]>
177+
> {
178+
bgSpace = bgSpace ?? BG_SYSTEM_SPACE_ID;
179+
bgCause = bgCause ?? BG_CELL_CAUSE;
180+
181+
if (!storage.hasSigner()) {
182+
throw new Error("Storage has no signer");
183+
}
184+
185+
if (!storage.hasRemoteStorage()) {
186+
throw new Error("Storage has no remote storage");
187+
}
188+
const schema = {
189+
type: "array",
190+
items: {
191+
...BGCharmEntrySchema,
192+
asCell: true,
193+
},
194+
default: [],
195+
} as const satisfies JSONSchema;
196+
197+
const charmsCell = getCell(bgSpace, bgCause, schema);
198+
199+
// Ensure the cell is synced
200+
// FIXME(ja): does True do the right thing here? Does this mean: I REALLY REALLY
201+
// INSIST THAT YOU HAVE THIS CELL ON THE SERVER!
202+
await storage.syncCell(charmsCell, true);
203+
await storage.synced();
204+
205+
return charmsCell;
206+
}

background-charm-service/src/worker-controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { BGCharmEntry } from "@commontools/utils";
1+
import { BGCharmEntry } from "./schema.ts";
22
import { Cell } from "@commontools/runner";
33
import { Identity } from "@commontools/identity";
44
import { defer, type Deferred } from "@commontools/utils/defer";
55
import {
66
isWorkerIPCResponse,
77
WorkerIPCMessageType,
88
WorkerIPCRequest,
9-
WorkerIPCResponse,
109
} from "./worker-ipc.ts";
1110

1211
const DEFAULT_TASK_TIMEOUT = 60_000;

0 commit comments

Comments
 (0)