@ricsam/r5d-worker 0.0.143 → 0.0.145
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +2 -2
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/personal/runtime.mjs +50 -1
- package/dist/mjs/runtime/workspace/authority.mjs +29 -31
- package/dist/mjs/runtime/workspace/contracts.mjs +1 -4
- package/dist/types/personal/runtime.d.ts +8 -0
- package/dist/types/runtime/workspace/authority.d.ts +8 -4
- package/dist/types/runtime/workspace/contracts.d.ts +0 -11
- package/package.json +3 -3
package/dist/cjs/package.json
CHANGED
package/dist/mjs/main.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { startManagerRpc } from "./runtime/releases/rpc-main.mjs";
|
|
|
7
7
|
import { ManagerRpcConfig } from "./runtime/releases/rpc-protocol.mjs";
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
if (args.includes("--version")) {
|
|
10
|
-
console.log(`r5d-worker ${true ? "0.0.
|
|
10
|
+
console.log(`r5d-worker ${true ? "0.0.145" : "development"}`);
|
|
11
11
|
} else if (!args.length || args.includes("--help")) {
|
|
12
12
|
console.log(
|
|
13
13
|
"Usage: r5d-worker start --label <label> [--root <dir>] [--base-url <url>] [--token <worker-token>]\n r5d-worker executor /absolute/private/config.json\n r5d-worker manager /absolute/private/config.json\n\nRun independently supervised durable runtime services. Provision configuration with r5dinfra."
|
|
@@ -15,7 +15,7 @@ if (args.includes("--version")) {
|
|
|
15
15
|
} else if (args[0] === "start") {
|
|
16
16
|
const runtime = await startPersonalWorker(
|
|
17
17
|
parsePersonalWorkerOptions(args.slice(1)),
|
|
18
|
-
true ? "0.0.
|
|
18
|
+
true ? "0.0.145" : "development"
|
|
19
19
|
);
|
|
20
20
|
console.log(`Worker connected: ${runtime.resourceId}`);
|
|
21
21
|
let closing = false;
|
package/dist/mjs/package.json
CHANGED
|
@@ -132,7 +132,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
|
|
|
132
132
|
timeoutMs: 1e4
|
|
133
133
|
});
|
|
134
134
|
let current = grant, installed = false, retirementPending = false;
|
|
135
|
-
const manifests = /* @__PURE__ */ new Map(), routes = /* @__PURE__ */ new Map();
|
|
135
|
+
const manifests = /* @__PURE__ */ new Map(), routes = /* @__PURE__ */ new Map(), publicationSessions = /* @__PURE__ */ new Map();
|
|
136
136
|
const manifestFile = path.join(root, "workbenches.json");
|
|
137
137
|
try {
|
|
138
138
|
const rows = z.array(ApprovedWorkbench).parse(readPrivateJson(manifestFile));
|
|
@@ -140,6 +140,8 @@ exec ${quote(executable)} ${quote(cli)} "$@"
|
|
|
140
140
|
if (row.userId !== grant.userId || row.cwd !== workspaceRoot && !row.cwd.startsWith(workspaceRoot + path.sep))
|
|
141
141
|
throw new Error("Invalid retained workbench");
|
|
142
142
|
manifests.set(row.id, row);
|
|
143
|
+
routes.set(row.sessionId, row.id);
|
|
144
|
+
if (row.rootProfile === "project") publicationSessions.set(row.id, row.sessionId);
|
|
143
145
|
}
|
|
144
146
|
} catch (error) {
|
|
145
147
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -227,6 +229,7 @@ exec ${quote(executable)} ${quote(cli)} "$@"
|
|
|
227
229
|
await fs.rename(tmp, manifestFile);
|
|
228
230
|
}
|
|
229
231
|
routes.set(input.sessionId, input.id);
|
|
232
|
+
if (row.rootProfile === "project") publicationSessions.set(row.id, input.sessionId);
|
|
230
233
|
}
|
|
231
234
|
async function installCredentials(values) {
|
|
232
235
|
for (const source of values) {
|
|
@@ -247,6 +250,48 @@ exec ${quote(executable)} ${quote(cli)} "$@"
|
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
}
|
|
253
|
+
let publication = null, closing = false;
|
|
254
|
+
const synchronize = () => {
|
|
255
|
+
if (publication) return publication;
|
|
256
|
+
const next = (async () => {
|
|
257
|
+
const results = [];
|
|
258
|
+
for (const [workbenchId, sessionId] of publicationSessions) {
|
|
259
|
+
if (closing) break;
|
|
260
|
+
const manifest = manifests.get(workbenchId);
|
|
261
|
+
if (!manifest || manifest.rootProfile !== "project") continue;
|
|
262
|
+
try {
|
|
263
|
+
const result = await authority.publish({ userId: grant.userId, sessionId });
|
|
264
|
+
results.push({ workbenchId, ...result });
|
|
265
|
+
} catch (error) {
|
|
266
|
+
const code = error instanceof WorkspaceError ? error.code : "publication_failed";
|
|
267
|
+
results.push({ workbenchId, error: code });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return results;
|
|
271
|
+
})();
|
|
272
|
+
publication = next;
|
|
273
|
+
void next.then(
|
|
274
|
+
(results) => {
|
|
275
|
+
if (results.some((result) => result.error && result.error !== "not_initialized"))
|
|
276
|
+
process.stderr.write(`[r5d-worker] periodic workspace publication deferred: ${results.filter((result) => result.error).map((result) => `${result.workbenchId}:${result.error}`).join(", ")}
|
|
277
|
+
`);
|
|
278
|
+
if (publication === next) publication = null;
|
|
279
|
+
},
|
|
280
|
+
(error) => {
|
|
281
|
+
process.stderr.write(`[r5d-worker] periodic workspace publication failed: ${error instanceof WorkspaceError ? error.code : "publication_failed"}
|
|
282
|
+
`);
|
|
283
|
+
if (publication === next) publication = null;
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
return next;
|
|
287
|
+
};
|
|
288
|
+
const publicationIntervalMs = options.publicationIntervalMs ?? 6e4;
|
|
289
|
+
if (!Number.isSafeInteger(publicationIntervalMs) || publicationIntervalMs <= 0)
|
|
290
|
+
throw new Error("Publication interval must be a positive integer");
|
|
291
|
+
const publicationTimer = setInterval(() => {
|
|
292
|
+
void synchronize();
|
|
293
|
+
}, publicationIntervalMs);
|
|
294
|
+
publicationTimer.unref?.();
|
|
250
295
|
const tcp = new PersonalTcpManager(path.join(root, "tcp"));
|
|
251
296
|
async function dispatch(raw) {
|
|
252
297
|
const request = PersonalWorkspaceRequest.parse(raw);
|
|
@@ -357,7 +402,11 @@ exec ${quote(executable)} ${quote(cli)} "$@"
|
|
|
357
402
|
renew,
|
|
358
403
|
dispatch,
|
|
359
404
|
authority,
|
|
405
|
+
synchronize,
|
|
360
406
|
async close() {
|
|
407
|
+
closing = true;
|
|
408
|
+
clearInterval(publicationTimer);
|
|
409
|
+
await publication;
|
|
361
410
|
await tcp.close();
|
|
362
411
|
await authority.close();
|
|
363
412
|
await daemon.close();
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
ApprovedWorkbench,
|
|
11
11
|
WorkspaceConfig,
|
|
12
12
|
WorkspaceError,
|
|
13
|
-
WorkspacePublicationNotAdmitted,
|
|
14
13
|
DEFAULT_WORKSPACE_IGNORE
|
|
15
14
|
} from "./contracts.mjs";
|
|
16
15
|
import { SessionArtifactStore, SessionArtifactChunk, RESERVED_ARTIFACT_ENV } from "./artifacts.mjs";
|
|
@@ -403,13 +402,11 @@ class WorkspaceAuthority {
|
|
|
403
402
|
}
|
|
404
403
|
await this.save(b);
|
|
405
404
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
"A mutating command is active/unknown; poll original executor run to terminal before syncing"
|
|
412
|
-
);
|
|
405
|
+
/** Workspace synchronization is independent of process lifetime. The serial
|
|
406
|
+
* authority queue excludes other authority mutations, while shells and
|
|
407
|
+
* agents remain ordinary concurrent filesystem writers whose later changes
|
|
408
|
+
* are observed by a subsequent publication cycle. */
|
|
409
|
+
async assertAvailable(b, allowBlocked = false) {
|
|
413
410
|
if (b.state.blocked && !allowBlocked) throw new WorkspaceError(b.state.blocked.code, b.state.blocked.message);
|
|
414
411
|
}
|
|
415
412
|
async status(identity) {
|
|
@@ -425,7 +422,11 @@ class WorkspaceAuthority {
|
|
|
425
422
|
} catch {
|
|
426
423
|
}
|
|
427
424
|
}
|
|
428
|
-
|
|
425
|
+
const state = structuredClone(b.state);
|
|
426
|
+
state.runs = Object.fromEntries(
|
|
427
|
+
Object.entries(state.runs).filter(([, run]) => (run.sessionId ?? b.config.sessionId) === identity.sessionId)
|
|
428
|
+
);
|
|
429
|
+
return { workbench: { ...b.config, sessionId: identity.sessionId }, ...state, files };
|
|
429
430
|
});
|
|
430
431
|
}
|
|
431
432
|
async cleanRefreshBase(b, expectedBase) {
|
|
@@ -454,8 +455,8 @@ class WorkspaceAuthority {
|
|
|
454
455
|
if (!unchangedPublishedMetadata && (await git(b.repo, ["write-tree"], void 0, index)).toString().trim() !== expectedTree)
|
|
455
456
|
throw new WorkspaceError("dirty_workbench", "Staged changes are retained; resolve them before refreshing");
|
|
456
457
|
}
|
|
457
|
-
/** No-argument hydration is initial-only. Explicit expectedBase permits a
|
|
458
|
-
*
|
|
458
|
+
/** No-argument hydration is initial-only. Explicit expectedBase permits a
|
|
459
|
+
* clean workbench refresh; process/PTY liveness never gates synchronization. */
|
|
459
460
|
async hydrate(identity, expectedBase) {
|
|
460
461
|
const b = await this.bench(identity);
|
|
461
462
|
if (b.config.rootProfile === "account") return { head: "", unchanged: true };
|
|
@@ -463,7 +464,7 @@ class WorkspaceAuthority {
|
|
|
463
464
|
}
|
|
464
465
|
async hydrateIdle(b, identity, expectedBase) {
|
|
465
466
|
if (b.state.blocked?.code === "account_initialization_unknown") {
|
|
466
|
-
await this.
|
|
467
|
+
await this.assertAvailable(b, true);
|
|
467
468
|
await this.assertFreshAccountWorkbench(b, expectedBase);
|
|
468
469
|
const operationId = `account-initialize-${sha256(b.config.userId)}`;
|
|
469
470
|
if (b.state.blocked.operationId !== operationId) throw new WorkspaceError("wrong_binding", "Account initialization receipt identity changed");
|
|
@@ -474,10 +475,11 @@ class WorkspaceAuthority {
|
|
|
474
475
|
b.state.blocked = null;
|
|
475
476
|
await this.save(b);
|
|
476
477
|
}
|
|
477
|
-
await this.
|
|
478
|
+
await this.assertAvailable(b);
|
|
478
479
|
if (expectedBase !== void 0) {
|
|
479
480
|
GitOid.parse(expectedBase);
|
|
480
|
-
|
|
481
|
+
if (!b.state.initialized || b.state.head !== expectedBase)
|
|
482
|
+
throw new WorkspaceError("conflict", "Workbench base changed; inspect before refreshing");
|
|
481
483
|
} else if (b.state.initialized || (await fs.readdir(b.config.cwd)).length)
|
|
482
484
|
throw new WorkspaceError(
|
|
483
485
|
"nonempty_workbench",
|
|
@@ -507,6 +509,7 @@ class WorkspaceAuthority {
|
|
|
507
509
|
if (!head) throw new WorkspaceError("empty_repository", "Seed README explicitly for an empty canonical branch");
|
|
508
510
|
GitOid.parse(head);
|
|
509
511
|
if (expectedBase === head) return { head };
|
|
512
|
+
if (expectedBase !== void 0) await this.cleanRefreshBase(b, expectedBase);
|
|
510
513
|
b.state.blocked = {
|
|
511
514
|
code: expectedBase === void 0 ? "hydration_incomplete" : "refresh_incomplete",
|
|
512
515
|
message: "Source synchronization interrupted; preserve workbench and inspect staged tree/retained originals before maintenance"
|
|
@@ -732,7 +735,7 @@ class WorkspaceAuthority {
|
|
|
732
735
|
async seedReadme(identity, readme) {
|
|
733
736
|
const b = await this.bench(identity);
|
|
734
737
|
return this.serial(b, async () => {
|
|
735
|
-
await this.
|
|
738
|
+
await this.assertAvailable(b);
|
|
736
739
|
if (Buffer.byteLength(readme) > 1024 * 1024) throw new WorkspaceError("too_large", "README seed exceeds limit");
|
|
737
740
|
sourceBytes(Buffer.from(readme));
|
|
738
741
|
if (b.state.initialized || (await fs.readdir(b.config.cwd)).length)
|
|
@@ -948,7 +951,7 @@ class WorkspaceAuthority {
|
|
|
948
951
|
const head = GitOid.parse((await git(b.repo, ["rev-parse", "FETCH_HEAD"])).toString().trim());
|
|
949
952
|
return this.importCommit(b, head, input.id, identity);
|
|
950
953
|
}, async () => {
|
|
951
|
-
await this.
|
|
954
|
+
await this.assertAvailable(b);
|
|
952
955
|
if (b.state.initialized || (await fs.readdir(b.config.cwd)).length) throw new WorkspaceError("nonempty_workbench", "Import requires a new empty workbench");
|
|
953
956
|
}));
|
|
954
957
|
}
|
|
@@ -972,7 +975,7 @@ class WorkspaceAuthority {
|
|
|
972
975
|
if (source.config.repositoryId !== target.config.repositoryId || source.config.branch === target.config.branch) throw new WorkspaceError("forbidden", "Branch source must belong to the same project");
|
|
973
976
|
await this.ensureHydrated(sourceIdentity);
|
|
974
977
|
const prepared = await this.serial(source, async () => {
|
|
975
|
-
await this.
|
|
978
|
+
await this.assertAvailable(source);
|
|
976
979
|
let head = source.state.head;
|
|
977
980
|
if (input.workingTree === "carry") {
|
|
978
981
|
const tree = await snapshotTree(source.repo, source.config.cwd, head);
|
|
@@ -990,7 +993,7 @@ class WorkspaceAuthority {
|
|
|
990
993
|
await git(target.repo, ["bundle", "unbundle", file]);
|
|
991
994
|
return this.importCommit(target, prepared.head, input.id, identity);
|
|
992
995
|
}, async () => {
|
|
993
|
-
await this.
|
|
996
|
+
await this.assertAvailable(target);
|
|
994
997
|
if (target.state.initialized || (await fs.readdir(target.config.cwd)).length) throw new WorkspaceError("nonempty_workbench", "Branch already has a workbench");
|
|
995
998
|
}));
|
|
996
999
|
}
|
|
@@ -1070,7 +1073,7 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
|
|
|
1070
1073
|
await this.installGitPolicy(b, result.head);
|
|
1071
1074
|
return { ...result, treeHash: (await git(b.repo, ["rev-parse", `${result.head}^{tree}`])).toString().trim() };
|
|
1072
1075
|
}, async () => {
|
|
1073
|
-
await this.
|
|
1076
|
+
await this.assertAvailable(b);
|
|
1074
1077
|
if (input.expectedHead !== void 0 && input.expectedHead !== b.state.head) throw new WorkspaceError("conflict", "Workbench head changed");
|
|
1075
1078
|
}));
|
|
1076
1079
|
}
|
|
@@ -1085,7 +1088,7 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
|
|
|
1085
1088
|
await git(b.repo, ["-c", "protocol.https.allow=always", "push", "--porcelain", ...input.remote.expectedHead !== void 0 ? [`--force-with-lease=refs/heads/${input.remote.branch}:${input.remote.expectedHead ?? ""}`] : [], "--", input.remote.url, `${b.state.head}:refs/heads/${input.remote.branch}`], void 0, void 0, { token: input.remote.token });
|
|
1086
1089
|
return { head: b.state.head, pushed: true };
|
|
1087
1090
|
}, async () => {
|
|
1088
|
-
await this.
|
|
1091
|
+
await this.assertAvailable(b);
|
|
1089
1092
|
if (!b.state.head || input.expectedHead !== void 0 && input.expectedHead !== b.state.head) throw new WorkspaceError("conflict", "Workbench head changed");
|
|
1090
1093
|
}));
|
|
1091
1094
|
}
|
|
@@ -1105,13 +1108,13 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
|
|
|
1105
1108
|
b.state.blocked = { code: "archived", message: "This branch incarnation is archived; its workbench and source are retained" };
|
|
1106
1109
|
await this.save(b);
|
|
1107
1110
|
return { removed: true, receipt: result };
|
|
1108
|
-
}, () => this.
|
|
1111
|
+
}, () => this.assertAvailable(b)));
|
|
1109
1112
|
}
|
|
1110
1113
|
/** An explicit user reset retains the complete old tree/index before hydration. */
|
|
1111
1114
|
async reset(identity, input) {
|
|
1112
1115
|
const b = await this.bench(identity);
|
|
1113
1116
|
return this.serial(b, () => this.productAction(b, input.id, { method: "reset", expectedHead: input.expectedHead }, async () => {
|
|
1114
|
-
await this.
|
|
1117
|
+
await this.assertAvailable(b, true);
|
|
1115
1118
|
if (input.expectedHead !== void 0 && input.expectedHead !== b.state.head) throw new WorkspaceError("conflict", "Workbench base changed before reset");
|
|
1116
1119
|
if (b.state.blocked?.code === "archived") throw new WorkspaceError("archived", "A deleted branch cannot be reset");
|
|
1117
1120
|
const storage = await this.storage(b, identity);
|
|
@@ -1145,12 +1148,7 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
|
|
|
1145
1148
|
const b = await this.bench(identity);
|
|
1146
1149
|
if (b.config.rootProfile === "account") return { head: "", unchanged: true };
|
|
1147
1150
|
return this.serial(b, async () => {
|
|
1148
|
-
|
|
1149
|
-
await this.idle(b);
|
|
1150
|
-
} catch (error) {
|
|
1151
|
-
if (!(error instanceof WorkspaceError) || error.code !== "workbench_busy") throw error;
|
|
1152
|
-
throw new WorkspacePublicationNotAdmitted("workbench_busy", error.message);
|
|
1153
|
-
}
|
|
1151
|
+
await this.assertAvailable(b);
|
|
1154
1152
|
return this.publishIdle(b, identity);
|
|
1155
1153
|
});
|
|
1156
1154
|
}
|
|
@@ -1361,13 +1359,13 @@ ${DEFAULT_WORKSPACE_IGNORE}`, { mode: 384 });
|
|
|
1361
1359
|
async runRoute(identity, run) {
|
|
1362
1360
|
const b = await this.bench(identity);
|
|
1363
1361
|
if (run.userId !== identity.userId || run.sessionId !== identity.sessionId || !b.state.runs[run.operationId] || (b.state.runs[run.operationId].sessionId ?? b.config.sessionId) !== identity.sessionId)
|
|
1364
|
-
throw new WorkspaceError("forbidden", "Run does not belong to this workbench");
|
|
1362
|
+
throw new WorkspaceError("forbidden", "Run does not belong to this workbench", true);
|
|
1365
1363
|
if (b.state.runs[run.operationId].state === "rejected_capacity")
|
|
1366
|
-
throw new WorkspaceError("run_not_admitted", "This operation was definitively rejected; no executor run exists");
|
|
1364
|
+
throw new WorkspaceError("run_not_admitted", "This operation was definitively rejected; no executor run exists", true);
|
|
1367
1365
|
await this.owned();
|
|
1368
1366
|
const route = await this.route(b, identity);
|
|
1369
1367
|
if (run.fence.installationId !== this.config.installationId || run.fence.resourceType !== route.workerFence.resourceType || run.fence.resourceId !== route.workerFence.resourceId)
|
|
1370
|
-
throw new WorkspaceError("wrong_resource", "Wrong worker resource");
|
|
1368
|
+
throw new WorkspaceError("wrong_resource", "Wrong worker resource", true);
|
|
1371
1369
|
return { b, route };
|
|
1372
1370
|
}
|
|
1373
1371
|
action(fn) {
|
|
@@ -34,8 +34,6 @@ class WorkspaceError extends Error {
|
|
|
34
34
|
code;
|
|
35
35
|
rejectedBeforeAdmission;
|
|
36
36
|
}
|
|
37
|
-
class WorkspacePublicationNotAdmitted extends WorkspaceError {
|
|
38
|
-
}
|
|
39
37
|
const DEFAULT_WORKSPACE_IGNORE = `# Required destination workbench exclusions; keep these rules effective.
|
|
40
38
|
.env
|
|
41
39
|
.env.*
|
|
@@ -59,6 +57,5 @@ export {
|
|
|
59
57
|
ApprovedWorkbench,
|
|
60
58
|
DEFAULT_WORKSPACE_IGNORE,
|
|
61
59
|
WorkspaceConfig,
|
|
62
|
-
WorkspaceError
|
|
63
|
-
WorkspacePublicationNotAdmitted
|
|
60
|
+
WorkspaceError
|
|
64
61
|
};
|
|
@@ -101,6 +101,8 @@ export declare function openPersonalWorkerRuntime(options: {
|
|
|
101
101
|
grant: z.infer<typeof PersonalWorkerGrant>;
|
|
102
102
|
storage: (sessionId: string) => StorageTransport;
|
|
103
103
|
cliEntrypoint?: string;
|
|
104
|
+
/** Internal test/embedding override. Personal workers publish every minute. */
|
|
105
|
+
publicationIntervalMs?: number;
|
|
104
106
|
}): Promise<{
|
|
105
107
|
grant: {
|
|
106
108
|
[x: string]: unknown;
|
|
@@ -132,5 +134,11 @@ export declare function openPersonalWorkerRuntime(options: {
|
|
|
132
134
|
renew: (value: unknown) => Promise<void>;
|
|
133
135
|
dispatch: (raw: unknown) => Promise<any>;
|
|
134
136
|
authority: WorkspaceAuthority;
|
|
137
|
+
synchronize: () => Promise<{
|
|
138
|
+
workbenchId: string;
|
|
139
|
+
head?: string;
|
|
140
|
+
unchanged?: boolean;
|
|
141
|
+
error?: string;
|
|
142
|
+
}[]>;
|
|
135
143
|
close(): Promise<void>;
|
|
136
144
|
}>;
|
|
@@ -11,7 +11,7 @@ export interface WorkspaceAuthorityOptions {
|
|
|
11
11
|
/** Trusted catalog authorization; never a client-supplied filesystem path. */
|
|
12
12
|
resolveWorkbench?: (identity: WorkspaceIdentity) => Promise<ApprovedWorkbenchInput | null>;
|
|
13
13
|
}
|
|
14
|
-
/** Independent resource owner.
|
|
14
|
+
/** Independent resource owner. Run receipts and workbench never live in replaceable adapters/gateways.
|
|
15
15
|
* Private root is exclusively owned; a crash leaves lock+intent for explicit maintenance.
|
|
16
16
|
*/
|
|
17
17
|
export declare class WorkspaceAuthority {
|
|
@@ -179,7 +179,11 @@ export declare class WorkspaceAuthority {
|
|
|
179
179
|
private route;
|
|
180
180
|
private storage;
|
|
181
181
|
private refresh;
|
|
182
|
-
|
|
182
|
+
/** Workspace synchronization is independent of process lifetime. The serial
|
|
183
|
+
* authority queue excludes other authority mutations, while shells and
|
|
184
|
+
* agents remain ordinary concurrent filesystem writers whose later changes
|
|
185
|
+
* are observed by a subsequent publication cycle. */
|
|
186
|
+
private assertAvailable;
|
|
183
187
|
status(identity: WorkspaceIdentity): Promise<{
|
|
184
188
|
files: string[] | null;
|
|
185
189
|
initialized: boolean;
|
|
@@ -221,8 +225,8 @@ export declare class WorkspaceAuthority {
|
|
|
221
225
|
};
|
|
222
226
|
}>;
|
|
223
227
|
private cleanRefreshBase;
|
|
224
|
-
/** No-argument hydration is initial-only. Explicit expectedBase permits a
|
|
225
|
-
*
|
|
228
|
+
/** No-argument hydration is initial-only. Explicit expectedBase permits a
|
|
229
|
+
* clean workbench refresh; process/PTY liveness never gates synchronization. */
|
|
226
230
|
hydrate(identity: WorkspaceIdentity, expectedBase?: string): Promise<{
|
|
227
231
|
head: string;
|
|
228
232
|
} | {
|
|
@@ -54,20 +54,9 @@ export declare class WorkspaceError extends Error {
|
|
|
54
54
|
readonly rejectedBeforeAdmission: boolean;
|
|
55
55
|
constructor(code: string, message: string, rejectedBeforeAdmission?: boolean);
|
|
56
56
|
}
|
|
57
|
-
/** Constructed only by the authority before it admits publication work. */
|
|
58
|
-
export declare class WorkspacePublicationNotAdmitted extends WorkspaceError {
|
|
59
|
-
}
|
|
60
57
|
export type WorkspacePublishResult = {
|
|
61
58
|
head: string;
|
|
62
59
|
unchanged?: boolean;
|
|
63
|
-
rejectedBeforeAdmission?: never;
|
|
64
|
-
} | {
|
|
65
|
-
state: "rejected";
|
|
66
|
-
code: "capacity_busy";
|
|
67
|
-
rejectedBeforeAdmission: true;
|
|
68
|
-
message: string;
|
|
69
|
-
head?: never;
|
|
70
|
-
unchanged?: never;
|
|
71
60
|
};
|
|
72
61
|
export type WorkspaceState = {
|
|
73
62
|
initialized: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5d-worker",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.145",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/mjs/main.mjs",
|
|
6
6
|
"module": "./dist/mjs/main.mjs",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"r5d-worker": "dist/mjs/main.mjs"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ricsam/r5d-api": "^0.0.
|
|
25
|
-
"@ricsam/r5dctl": "0.0.
|
|
24
|
+
"@ricsam/r5d-api": "^0.0.145",
|
|
25
|
+
"@ricsam/r5dctl": "0.0.145",
|
|
26
26
|
"node-pty": "1.1.0",
|
|
27
27
|
"zod": "^4.1.13",
|
|
28
28
|
"picomatch": "^4.0.3"
|