@ricsam/r5d-worker 0.0.173 → 0.0.175
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/internal-r5dctl.cjs +1 -1
- package/dist/mjs/main.mjs +2 -2
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/runtime/workspace/authority.mjs +27 -4
- package/dist/mjs/runtime/workspace/files.mjs +6 -3
- package/dist/types/runtime/workspace/authority.d.ts +31 -7
- package/dist/types/runtime/workspace/files.d.ts +4 -0
- package/package.json +2 -2
package/dist/cjs/package.json
CHANGED
|
@@ -20605,7 +20605,7 @@ function resolveEntrypointPath(entrypoint) {
|
|
|
20605
20605
|
}
|
|
20606
20606
|
}
|
|
20607
20607
|
function getR5dctlVersion() {
|
|
20608
|
-
if (true) return "0.0.
|
|
20608
|
+
if (true) return "0.0.175";
|
|
20609
20609
|
const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
|
|
20610
20610
|
let current = entrypoint ? import_node_path2.default.dirname(entrypoint) : process.cwd();
|
|
20611
20611
|
for (let index = 0; index < 12; index += 1) {
|
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.175" : "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.175" : "development"
|
|
19
19
|
);
|
|
20
20
|
console.log(`Worker connected: ${runtime.resourceId}`);
|
|
21
21
|
let closing = false;
|
package/dist/mjs/package.json
CHANGED
|
@@ -164,6 +164,7 @@ class WorkspaceAuthority {
|
|
|
164
164
|
for (const id of await fs.readdir(path.join(config.root, "state"))) {
|
|
165
165
|
if (authority.benches.has(id)) continue;
|
|
166
166
|
const directory = path.join(config.root, "state", id);
|
|
167
|
+
if (!(await fs.lstat(directory)).isDirectory()) continue;
|
|
167
168
|
await noSymlinkAncestors(directory);
|
|
168
169
|
const b = ApprovedWorkbench.parse(JSON.parse((await readRegular(path.join(directory, "workbench.json"), 128 * 1024)).toString()));
|
|
169
170
|
if (b.id !== id || b.cwd !== authority.expectedCwd(b)) throw new WorkspaceError("wrong_binding", "Invalid durable dynamic workbench");
|
|
@@ -264,8 +265,13 @@ class WorkspaceAuthority {
|
|
|
264
265
|
const result = [];
|
|
265
266
|
for (const [operationId, metadata] of Object.entries(b.state.runs)) {
|
|
266
267
|
if (metadata.state === "rejected_capacity" || (metadata.sessionId ?? b.config.sessionId) !== identity.sessionId) continue;
|
|
267
|
-
|
|
268
|
-
|
|
268
|
+
try {
|
|
269
|
+
const poll = await this.poll(identity, { userId: identity.userId, sessionId: identity.sessionId, operationId, fence: route.workerFence }, 0, 0, 1);
|
|
270
|
+
result.push({ ...metadata, ...poll.receipt, workerLabel: route.workerFence.resourceId });
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (!(error instanceof ExecutorError && error.code === "run_not_found")) throw error;
|
|
273
|
+
result.push({ ...metadata, operationId, userId: identity.userId, sessionId: identity.sessionId, state: "unknown", pid: null, pty: false, lane: "general", stdoutBytes: 0, stderrBytes: 0, stdinClosed: true, exitCode: null, signal: null, reason: "run_not_found", workerLabel: route.workerFence.resourceId });
|
|
274
|
+
}
|
|
269
275
|
}
|
|
270
276
|
return result;
|
|
271
277
|
}
|
|
@@ -406,8 +412,20 @@ class WorkspaceAuthority {
|
|
|
406
412
|
return { head, treeHash, files: changes, ...patch === void 0 ? {} : { patch } };
|
|
407
413
|
});
|
|
408
414
|
}
|
|
415
|
+
/** Bench state is one in-memory object with several writers on different
|
|
416
|
+
* queues (checkout work, run receipts). Writes of its file are chained so
|
|
417
|
+
* they never overlap and the write that lands last always serializes the
|
|
418
|
+
* newest state; the chain covers only the write itself, never the work
|
|
419
|
+
* around it, so a run receipt does not wait for a publication. */
|
|
409
420
|
save(b) {
|
|
410
|
-
|
|
421
|
+
const write = (b.saving ?? Promise.resolve()).catch(() => {
|
|
422
|
+
}).then(() => durableJson(path.join(b.directory, "state.json"), b.state));
|
|
423
|
+
b.saving = write;
|
|
424
|
+
void write.finally(() => {
|
|
425
|
+
if (b.saving === write) b.saving = void 0;
|
|
426
|
+
}).catch(() => {
|
|
427
|
+
});
|
|
428
|
+
return write;
|
|
411
429
|
}
|
|
412
430
|
async owned(admitted = false) {
|
|
413
431
|
if (this.closing && !admitted) throw new WorkspaceError("authority_closed", "Workspace authority is closing");
|
|
@@ -1846,7 +1864,12 @@ class WorkspaceAuthority {
|
|
|
1846
1864
|
}
|
|
1847
1865
|
const nonmutating = Object.keys(requestedEnv).length === 0 && payload.credentials.length === 0 && b.config.nonmutatingArgv.some((argv) => canonicalJson(argv) === canonicalJson(payload.argv));
|
|
1848
1866
|
b.state.runs[operation.operationId] = { artifactEnvironment: true, sessionId: identity.sessionId, payloadHash: hash, mutating: !nonmutating, state: "unknown", argv: payload.argv, cwd: payload.cwd, startedAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1849
|
-
|
|
1867
|
+
try {
|
|
1868
|
+
await this.save(b);
|
|
1869
|
+
} catch (error) {
|
|
1870
|
+
delete b.state.runs[operation.operationId];
|
|
1871
|
+
throw error;
|
|
1872
|
+
}
|
|
1850
1873
|
for (let attempt = 0; ; attempt++) {
|
|
1851
1874
|
try {
|
|
1852
1875
|
const receipt = await route.client.start(operation);
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
5
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
6
6
|
import { safeTreePath, STORAGE_LIMITS } from "./storage-wire.mjs";
|
|
7
7
|
import { WorkspaceError } from "./contracts.mjs";
|
|
8
8
|
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
@@ -84,14 +84,17 @@ async function ensureAuthorityGitRepositoryLayout(repo) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
async function durableJson(file, value) {
|
|
87
|
-
const temporary = `${file}.next`;
|
|
87
|
+
const temporary = `${file}.${randomBytes(6).toString("hex")}.next`;
|
|
88
88
|
const handle = await fs.open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
|
|
89
89
|
try {
|
|
90
90
|
await handle.writeFile(JSON.stringify(value));
|
|
91
91
|
await handle.sync();
|
|
92
|
-
}
|
|
92
|
+
} catch (error) {
|
|
93
93
|
await handle.close();
|
|
94
|
+
await fs.rm(temporary, { force: true });
|
|
95
|
+
throw error;
|
|
94
96
|
}
|
|
97
|
+
await handle.close();
|
|
95
98
|
await fs.rename(temporary, file);
|
|
96
99
|
const dir = await fs.open(path.dirname(file), constants.O_RDONLY);
|
|
97
100
|
try {
|
|
@@ -57,7 +57,7 @@ export declare class WorkspaceAuthority {
|
|
|
57
57
|
content: string;
|
|
58
58
|
bytes: number;
|
|
59
59
|
}>;
|
|
60
|
-
processes(identity: WorkspaceIdentity): Promise<{
|
|
60
|
+
processes(identity: WorkspaceIdentity): Promise<({
|
|
61
61
|
workerLabel: string;
|
|
62
62
|
operationId: string;
|
|
63
63
|
userId: string;
|
|
@@ -80,7 +80,30 @@ export declare class WorkspaceAuthority {
|
|
|
80
80
|
startedAt?: string;
|
|
81
81
|
updatedAt?: string;
|
|
82
82
|
completedAt?: string;
|
|
83
|
-
}
|
|
83
|
+
} | {
|
|
84
|
+
operationId: string;
|
|
85
|
+
userId: string;
|
|
86
|
+
sessionId: string;
|
|
87
|
+
state: string;
|
|
88
|
+
pid: null;
|
|
89
|
+
pty: boolean;
|
|
90
|
+
lane: string;
|
|
91
|
+
stdoutBytes: number;
|
|
92
|
+
stderrBytes: number;
|
|
93
|
+
stdinClosed: boolean;
|
|
94
|
+
exitCode: null;
|
|
95
|
+
signal: null;
|
|
96
|
+
reason: string;
|
|
97
|
+
workerLabel: string;
|
|
98
|
+
artifactEnvironment?: boolean;
|
|
99
|
+
mutating: boolean;
|
|
100
|
+
payloadHash: string;
|
|
101
|
+
argv?: string[];
|
|
102
|
+
cwd?: string;
|
|
103
|
+
startedAt?: string;
|
|
104
|
+
updatedAt?: string;
|
|
105
|
+
completedAt?: string;
|
|
106
|
+
})[]>;
|
|
84
107
|
fileHistoryDates(identity: WorkspaceIdentity, file: string): Promise<{
|
|
85
108
|
createdAt: string | null;
|
|
86
109
|
updatedAt: string;
|
|
@@ -194,6 +217,11 @@ export declare class WorkspaceAuthority {
|
|
|
194
217
|
original?: undefined;
|
|
195
218
|
modified?: undefined;
|
|
196
219
|
}>;
|
|
220
|
+
/** Bench state is one in-memory object with several writers on different
|
|
221
|
+
* queues (checkout work, run receipts). Writes of its file are chained so
|
|
222
|
+
* they never overlap and the write that lands last always serializes the
|
|
223
|
+
* newest state; the chain covers only the write itself, never the work
|
|
224
|
+
* around it, so a run receipt does not wait for a publication. */
|
|
197
225
|
private save;
|
|
198
226
|
private owned;
|
|
199
227
|
private serial;
|
|
@@ -223,11 +251,7 @@ export declare class WorkspaceAuthority {
|
|
|
223
251
|
code: string;
|
|
224
252
|
message: string;
|
|
225
253
|
operationId?: string;
|
|
226
|
-
commit
|
|
227
|
-
* presence selects the outer-repository model: the workspace root is one
|
|
228
|
-
* repository whose files synchronize as a unit and whose project checkouts
|
|
229
|
-
* are mirrored, not published, per branch. Without it linked checkouts keep
|
|
230
|
-
* publishing per project, which the server workspace service relies on. */: string;
|
|
254
|
+
commit?: string;
|
|
231
255
|
};
|
|
232
256
|
mirroredHead?: string | null;
|
|
233
257
|
runs: Record<string, {
|
|
@@ -15,6 +15,10 @@ export declare function readHostRegular(file: string, limit?: number): Promise<B
|
|
|
15
15
|
* Restore only that directory skeleton after confirming the authority-owned
|
|
16
16
|
* repository markers; refs, objects, indexes and configuration are untouched. */
|
|
17
17
|
export declare function ensureAuthorityGitRepositoryLayout(repo: string): Promise<void>;
|
|
18
|
+
/** Writes `value` atomically: a private temporary file, fsync, rename, directory
|
|
19
|
+
* fsync. Concurrent writers of the same file each get their own temporary name,
|
|
20
|
+
* so neither fails on the other's; the rename that lands last wins, which is why
|
|
21
|
+
* callers that share in-memory state also serialize their writes. */
|
|
18
22
|
export declare function durableJson(file: string, value: unknown): Promise<void>;
|
|
19
23
|
/** All Git operates in authority-created repos; NEVER workbench .git/config/index.
|
|
20
24
|
* hash-object --no-filters and update-index --index-info replace git add/checkout.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5d-worker",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.175",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/mjs/main.mjs",
|
|
6
6
|
"module": "./dist/mjs/main.mjs",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"r5d-worker": "dist/mjs/main.mjs"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ricsam/r5d-api": "^0.0.
|
|
24
|
+
"@ricsam/r5d-api": "^0.0.175",
|
|
25
25
|
"node-pty": "1.1.0",
|
|
26
26
|
"zod": "^4.1.13",
|
|
27
27
|
"picomatch": "^4.0.3"
|