@ricsam/r5d-worker 0.0.137 → 0.0.139
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/README.md +4 -94
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +46 -7543
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/personal/action-scheduler.mjs +31 -0
- package/dist/mjs/personal/client.mjs +215 -0
- package/dist/mjs/personal/runtime.mjs +359 -0
- package/dist/mjs/personal/tcp.mjs +217 -0
- package/dist/mjs/runtime/adapter.mjs +42 -0
- package/dist/mjs/runtime/client.mjs +95 -0
- package/dist/mjs/runtime/daemon.mjs +131 -0
- package/dist/mjs/runtime/executor.mjs +436 -0
- package/dist/mjs/runtime/index.mjs +15 -0
- package/dist/mjs/runtime/protocol.mjs +139 -0
- package/dist/mjs/runtime/pty-host.mjs +156 -0
- package/dist/mjs/runtime/pty.mjs +127 -0
- package/dist/mjs/runtime/releases/artifacts.mjs +201 -0
- package/dist/mjs/runtime/releases/grants.mjs +93 -0
- package/dist/mjs/runtime/releases/main.mjs +101 -0
- package/dist/mjs/runtime/releases/manager.mjs +541 -0
- package/dist/mjs/runtime/releases/process.mjs +88 -0
- package/dist/mjs/runtime/releases/retirement.mjs +18 -0
- package/dist/mjs/runtime/releases/rpc-client.mjs +184 -0
- package/dist/mjs/runtime/releases/rpc-main.mjs +213 -0
- package/dist/mjs/runtime/releases/rpc-protocol.mjs +103 -0
- package/dist/mjs/runtime/storage.mjs +156 -0
- package/dist/mjs/runtime/workspace/artifacts.mjs +121 -0
- package/dist/mjs/runtime/workspace/authority.mjs +1355 -0
- package/dist/mjs/runtime/workspace/contracts.mjs +60 -0
- package/dist/mjs/runtime/workspace/file-write.mjs +23 -0
- package/dist/mjs/runtime/workspace/files.mjs +308 -0
- package/dist/mjs/runtime/workspace/http-body.mjs +45 -0
- package/dist/mjs/runtime/workspace/storage-client.mjs +59 -0
- package/dist/mjs/runtime/workspace/storage-wire.mjs +53 -0
- package/dist/types/main.d.ts +0 -1167
- package/dist/types/personal/action-scheduler.d.ts +23 -0
- package/dist/types/personal/client.d.ts +14 -0
- package/dist/types/personal/runtime.d.ts +121 -0
- package/dist/types/personal/tcp.d.ts +14 -0
- package/dist/types/runtime/client.d.ts +3 -1
- package/dist/types/runtime/executor.d.ts +6 -1
- package/dist/types/runtime/protocol.d.ts +176 -4
- package/dist/types/runtime/pty.d.ts +1 -0
- package/dist/types/runtime/releases/artifacts.d.ts +51 -0
- package/dist/types/runtime/releases/grants.d.ts +16 -0
- package/dist/types/runtime/releases/manager.d.ts +145 -0
- package/dist/types/runtime/releases/process.d.ts +14 -0
- package/dist/types/runtime/releases/retirement.d.ts +15 -0
- package/dist/types/runtime/releases/rpc-client.d.ts +52 -0
- package/dist/types/runtime/releases/rpc-main.d.ts +10 -0
- package/dist/types/runtime/releases/rpc-protocol.d.ts +1139 -0
- package/dist/types/runtime/workspace/artifacts.d.ts +30 -0
- package/dist/types/runtime/workspace/authority.d.ts +338 -0
- package/dist/types/runtime/workspace/contracts.d.ts +86 -0
- package/dist/types/runtime/workspace/file-write.d.ts +14 -0
- package/dist/types/runtime/workspace/files.d.ts +21 -0
- package/dist/types/runtime/workspace/http-body.d.ts +9 -0
- package/dist/types/runtime/workspace/storage-client.d.ts +23 -0
- package/dist/types/runtime/workspace/storage-wire.d.ts +138 -0
- package/package.json +8 -6
- package/dist/cjs/atomic-rename.cjs +0 -303
- package/dist/cjs/browser-upload.cjs +0 -73
- package/dist/cjs/cli-update.cjs +0 -108
- package/dist/cjs/command-launcher.cjs +0 -423
- package/dist/cjs/control-command-policy.cjs +0 -177
- package/dist/cjs/git-blob-hash.cjs +0 -41
- package/dist/cjs/git-identity.cjs +0 -39
- package/dist/cjs/git-process-environment.cjs +0 -133
- package/dist/cjs/inline-output.cjs +0 -62
- package/dist/cjs/main.cjs +0 -7517
- package/dist/cjs/managed-paths.cjs +0 -215
- package/dist/cjs/plan-lint.cjs +0 -165
- package/dist/cjs/plan-parser.cjs +0 -127
- package/dist/cjs/plan-paths.cjs +0 -53
- package/dist/cjs/port-forward-client.cjs +0 -206
- package/dist/cjs/process-tree.cjs +0 -117
- package/dist/cjs/project-checkout-garbage.cjs +0 -219
- package/dist/cjs/project-mirror-fetch-policy.cjs +0 -82
- package/dist/cjs/project-mirror-refs-token.cjs +0 -85
- package/dist/cjs/project-workspace-state.cjs +0 -972
- package/dist/cjs/project-worktrees.cjs +0 -1713
- package/dist/cjs/pty-output-coalescer.cjs +0 -87
- package/dist/cjs/recovery-journal-protocol.cjs +0 -56
- package/dist/cjs/recovery-journal-runtime.cjs +0 -133
- package/dist/cjs/recovery-journal-thread.cjs +0 -9
- package/dist/cjs/recovery-journal.cjs +0 -742
- package/dist/cjs/recovery-output-window.cjs +0 -77
- package/dist/cjs/recovery-protocol.cjs +0 -31
- package/dist/cjs/recovery-store.cjs +0 -252
- package/dist/cjs/registry-auth.cjs +0 -310
- package/dist/cjs/repository-transition-policy.cjs +0 -49
- package/dist/cjs/runtime-version.cjs +0 -88
- package/dist/cjs/session-file-mutations.cjs +0 -61
- package/dist/cjs/supervisor.cjs +0 -147
- package/dist/cjs/three-way-merge.cjs +0 -346
- package/dist/cjs/working-tree-mirror.cjs +0 -1872
- package/dist/cjs/workspace-automatic-sync-policy.cjs +0 -79
- package/dist/cjs/workspace-branch-incarnation-policy.cjs +0 -37
- package/dist/cjs/workspace-command-sync-policy.cjs +0 -112
- package/dist/cjs/workspace-command-targets.cjs +0 -63
- package/dist/cjs/workspace-filesystem-executor-thread.cjs +0 -36
- package/dist/cjs/workspace-filesystem-executor.cjs +0 -327
- package/dist/cjs/workspace-filesystem-job-types.cjs +0 -144
- package/dist/cjs/workspace-filesystem-jobs.cjs +0 -59
- package/dist/cjs/workspace-git-sync.cjs +0 -3595
- package/dist/cjs/workspace-hydration-ledger.cjs +0 -83
- package/dist/cjs/workspace-hydration-merge.cjs +0 -448
- package/dist/cjs/workspace-hydration-recovery-state.cjs +0 -53
- package/dist/cjs/workspace-incident-state.cjs +0 -37
- package/dist/cjs/workspace-merge-projection.cjs +0 -628
- package/dist/cjs/workspace-mount-boundary.cjs +0 -71
- package/dist/cjs/workspace-mount-hold-fence.cjs +0 -120
- package/dist/cjs/workspace-mutation-gate.cjs +0 -127
- package/dist/cjs/workspace-path-move.cjs +0 -195
- package/dist/cjs/workspace-preserve-only-policy.cjs +0 -36
- package/dist/cjs/workspace-project-config-policy.cjs +0 -53
- package/dist/cjs/workspace-projection-ledger.cjs +0 -81
- package/dist/cjs/workspace-publication-evidence.cjs +0 -46
- package/dist/cjs/workspace-sync-coalescer.cjs +0 -58
- package/dist/mjs/atomic-rename.mjs +0 -261
- package/dist/mjs/browser-upload.mjs +0 -42
- package/dist/mjs/command-launcher.mjs +0 -393
- package/dist/mjs/control-command-policy.mjs +0 -146
- package/dist/mjs/git-blob-hash.mjs +0 -16
- package/dist/mjs/git-identity.mjs +0 -15
- package/dist/mjs/git-process-environment.mjs +0 -105
- package/dist/mjs/inline-output.mjs +0 -37
- package/dist/mjs/managed-paths.mjs +0 -174
- package/dist/mjs/plan-lint.mjs +0 -129
- package/dist/mjs/plan-parser.mjs +0 -94
- package/dist/mjs/plan-paths.mjs +0 -25
- package/dist/mjs/port-forward-client.mjs +0 -172
- package/dist/mjs/project-checkout-garbage.mjs +0 -178
- package/dist/mjs/project-mirror-fetch-policy.mjs +0 -56
- package/dist/mjs/project-mirror-refs-token.mjs +0 -53
- package/dist/mjs/project-workspace-state.mjs +0 -937
- package/dist/mjs/project-worktrees.mjs +0 -1657
- package/dist/mjs/pty-output-coalescer.mjs +0 -61
- package/dist/mjs/recovery-journal-protocol.mjs +0 -30
- package/dist/mjs/recovery-journal-runtime.mjs +0 -112
- package/dist/mjs/recovery-journal-thread.mjs +0 -8
- package/dist/mjs/recovery-journal.mjs +0 -694
- package/dist/mjs/recovery-output-window.mjs +0 -53
- package/dist/mjs/recovery-protocol.mjs +0 -6
- package/dist/mjs/recovery-store.mjs +0 -217
- package/dist/mjs/registry-auth.mjs +0 -269
- package/dist/mjs/repository-transition-policy.mjs +0 -22
- package/dist/mjs/runtime-version.mjs +0 -54
- package/dist/mjs/session-file-mutations.mjs +0 -37
- package/dist/mjs/supervisor.mjs +0 -114
- package/dist/mjs/three-way-merge.mjs +0 -318
- package/dist/mjs/working-tree-mirror.mjs +0 -1817
- package/dist/mjs/workspace-automatic-sync-policy.mjs +0 -48
- package/dist/mjs/workspace-branch-incarnation-policy.mjs +0 -13
- package/dist/mjs/workspace-command-sync-policy.mjs +0 -84
- package/dist/mjs/workspace-command-targets.mjs +0 -37
- package/dist/mjs/workspace-filesystem-executor-thread.mjs +0 -38
- package/dist/mjs/workspace-filesystem-executor.mjs +0 -287
- package/dist/mjs/workspace-filesystem-job-types.mjs +0 -116
- package/dist/mjs/workspace-filesystem-jobs.mjs +0 -55
- package/dist/mjs/workspace-git-sync.mjs +0 -3553
- package/dist/mjs/workspace-hydration-ledger.mjs +0 -58
- package/dist/mjs/workspace-hydration-merge.mjs +0 -414
- package/dist/mjs/workspace-hydration-recovery-state.mjs +0 -29
- package/dist/mjs/workspace-incident-state.mjs +0 -12
- package/dist/mjs/workspace-merge-projection.mjs +0 -594
- package/dist/mjs/workspace-mount-boundary.mjs +0 -37
- package/dist/mjs/workspace-mount-hold-fence.mjs +0 -95
- package/dist/mjs/workspace-mutation-gate.mjs +0 -103
- package/dist/mjs/workspace-path-move.mjs +0 -160
- package/dist/mjs/workspace-preserve-only-policy.mjs +0 -11
- package/dist/mjs/workspace-project-config-policy.mjs +0 -27
- package/dist/mjs/workspace-projection-ledger.mjs +0 -57
- package/dist/mjs/workspace-publication-evidence.mjs +0 -21
- package/dist/mjs/workspace-sync-coalescer.mjs +0 -34
- package/dist/types/atomic-rename.d.ts +0 -78
- package/dist/types/browser-upload.d.ts +0 -7
- package/dist/types/command-launcher.d.ts +0 -104
- package/dist/types/control-command-policy.d.ts +0 -53
- package/dist/types/git-blob-hash.d.ts +0 -10
- package/dist/types/git-identity.d.ts +0 -9
- package/dist/types/git-process-environment.d.ts +0 -9
- package/dist/types/inline-output.d.ts +0 -3
- package/dist/types/managed-paths.d.ts +0 -19
- package/dist/types/plan-lint.d.ts +0 -16
- package/dist/types/plan-parser.d.ts +0 -38
- package/dist/types/plan-paths.d.ts +0 -36
- package/dist/types/port-forward-client.d.ts +0 -8
- package/dist/types/project-checkout-garbage.d.ts +0 -86
- package/dist/types/project-mirror-fetch-policy.d.ts +0 -84
- package/dist/types/project-mirror-refs-token.d.ts +0 -53
- package/dist/types/project-workspace-state.d.ts +0 -188
- package/dist/types/project-worktrees.d.ts +0 -397
- package/dist/types/pty-output-coalescer.d.ts +0 -20
- package/dist/types/recovery-journal-protocol.d.ts +0 -35
- package/dist/types/recovery-journal-runtime.d.ts +0 -12
- package/dist/types/recovery-journal-stall-fixture.d.ts +0 -1
- package/dist/types/recovery-journal-thread.d.ts +0 -1
- package/dist/types/recovery-journal.d.ts +0 -248
- package/dist/types/recovery-output-window.d.ts +0 -19
- package/dist/types/recovery-protocol.d.ts +0 -59
- package/dist/types/recovery-store.d.ts +0 -52
- package/dist/types/registry-auth.d.ts +0 -47
- package/dist/types/repository-transition-policy.d.ts +0 -26
- package/dist/types/runtime-version.d.ts +0 -1
- package/dist/types/session-file-mutations.d.ts +0 -22
- package/dist/types/supervisor-daemonized-fixture.d.ts +0 -1
- package/dist/types/supervisor-launcher-cleanup-fixture.d.ts +0 -1
- package/dist/types/supervisor-signal-fixture.d.ts +0 -1
- package/dist/types/supervisor.d.ts +0 -23
- package/dist/types/three-way-merge.d.ts +0 -77
- package/dist/types/working-tree-mirror.d.ts +0 -424
- package/dist/types/workspace-automatic-sync-policy.d.ts +0 -57
- package/dist/types/workspace-branch-incarnation-policy.d.ts +0 -14
- package/dist/types/workspace-command-sync-policy.d.ts +0 -57
- package/dist/types/workspace-command-targets.d.ts +0 -37
- package/dist/types/workspace-filesystem-executor-thread.d.ts +0 -1
- package/dist/types/workspace-filesystem-executor.d.ts +0 -123
- package/dist/types/workspace-filesystem-job-types.d.ts +0 -302
- package/dist/types/workspace-filesystem-jobs.d.ts +0 -7
- package/dist/types/workspace-git-sync.d.ts +0 -514
- package/dist/types/workspace-hydration-ledger.d.ts +0 -76
- package/dist/types/workspace-hydration-merge.d.ts +0 -103
- package/dist/types/workspace-hydration-recovery-state.d.ts +0 -10
- package/dist/types/workspace-incident-state.d.ts +0 -19
- package/dist/types/workspace-merge-projection.d.ts +0 -69
- package/dist/types/workspace-mount-boundary.d.ts +0 -10
- package/dist/types/workspace-mount-hold-fence.d.ts +0 -42
- package/dist/types/workspace-mutation-gate.d.ts +0 -42
- package/dist/types/workspace-path-move.d.ts +0 -21
- package/dist/types/workspace-preserve-only-policy.d.ts +0 -15
- package/dist/types/workspace-project-config-policy.d.ts +0 -48
- package/dist/types/workspace-projection-ledger.d.ts +0 -59
- package/dist/types/workspace-publication-evidence.d.ts +0 -17
- package/dist/types/workspace-sync-coalescer.d.ts +0 -28
- /package/dist/types/{credential-authority-lock-fixture.d.ts → runtime/releases/main.d.ts} +0 -0
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const keyOf = (runId, stream) => `${runId}:${stream}`;
|
|
2
|
-
class WorkerOutputWindow {
|
|
3
|
-
constructor(windowBytes = 1024 * 1024) {
|
|
4
|
-
this.windowBytes = windowBytes;
|
|
5
|
-
}
|
|
6
|
-
windowBytes;
|
|
7
|
-
sent = /* @__PURE__ */ new Map();
|
|
8
|
-
acknowledged = /* @__PURE__ */ new Map();
|
|
9
|
-
outstanding = 0;
|
|
10
|
-
reset(cursors) {
|
|
11
|
-
this.sent.clear();
|
|
12
|
-
this.acknowledged.clear();
|
|
13
|
-
this.outstanding = 0;
|
|
14
|
-
for (const cursor of cursors) {
|
|
15
|
-
for (const stream of ["stdout", "stderr"]) {
|
|
16
|
-
const offset = stream === "stdout" ? cursor.stdoutBytes : cursor.stderrBytes;
|
|
17
|
-
this.sent.set(keyOf(cursor.runId, stream), offset);
|
|
18
|
-
this.acknowledged.set(keyOf(cursor.runId, stream), offset);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
send(frame, send, bufferedAmount = 0) {
|
|
23
|
-
const key = keyOf(frame.runId, frame.stream);
|
|
24
|
-
const sent = this.sent.get(key) ?? 0;
|
|
25
|
-
const bytes = Buffer.from(frame.data, "utf8");
|
|
26
|
-
const end = frame.offset + bytes.length;
|
|
27
|
-
if (end <= sent) return true;
|
|
28
|
-
if (frame.offset > sent) return false;
|
|
29
|
-
let cursor = sent;
|
|
30
|
-
while (cursor < end) {
|
|
31
|
-
let count = Math.min(end - cursor, 64 * 1024, this.windowBytes - this.outstanding);
|
|
32
|
-
while (count > 0 && ((bytes[cursor - frame.offset + count] ?? 0) & 192) === 128) count -= 1;
|
|
33
|
-
if (count <= 0 || bufferedAmount >= this.windowBytes) return false;
|
|
34
|
-
const data = bytes.subarray(cursor - frame.offset, cursor - frame.offset + count).toString("utf8");
|
|
35
|
-
if (!send({ ...frame, offset: cursor, data })) return false;
|
|
36
|
-
cursor += count;
|
|
37
|
-
this.sent.set(key, cursor);
|
|
38
|
-
this.outstanding += count;
|
|
39
|
-
}
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
acknowledge(runId, stream, offset) {
|
|
43
|
-
if (!Number.isSafeInteger(offset) || offset < 0) return;
|
|
44
|
-
const key = keyOf(runId, stream);
|
|
45
|
-
const previous = this.acknowledged.get(key) ?? 0;
|
|
46
|
-
const next = Math.max(previous, Math.min(this.sent.get(key) ?? 0, offset));
|
|
47
|
-
this.acknowledged.set(key, next);
|
|
48
|
-
this.outstanding = Math.max(0, this.outstanding - (next - previous));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
export {
|
|
52
|
-
WorkerOutputWindow
|
|
53
|
-
};
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { Database } from "bun:sqlite";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
|
-
function workerOperationDigest(message) {
|
|
6
|
-
const canonical = (value) => {
|
|
7
|
-
if (Array.isArray(value)) return value.map(canonical);
|
|
8
|
-
if (value && typeof value === "object")
|
|
9
|
-
return Object.fromEntries(
|
|
10
|
-
Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonical(item)])
|
|
11
|
-
);
|
|
12
|
-
return value;
|
|
13
|
-
};
|
|
14
|
-
const { requestId: _requestId, ...identity } = message;
|
|
15
|
-
return createHash("sha256").update(JSON.stringify(canonical(identity))).digest("hex");
|
|
16
|
-
}
|
|
17
|
-
class WorkerRecoveryStore {
|
|
18
|
-
db;
|
|
19
|
-
ledgerId;
|
|
20
|
-
constructor(filename) {
|
|
21
|
-
fs.mkdirSync(path.dirname(filename), { recursive: true, mode: 448 });
|
|
22
|
-
this.db = new Database(filename, { create: true });
|
|
23
|
-
fs.chmodSync(filename, 384);
|
|
24
|
-
this.db.exec(`PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL;
|
|
25
|
-
CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
26
|
-
CREATE TABLE IF NOT EXISTS operations (request_id TEXT PRIMARY KEY, run_id TEXT, session_id TEXT, request_type TEXT NOT NULL, digest TEXT NOT NULL, state TEXT NOT NULL, response TEXT);
|
|
27
|
-
CREATE UNIQUE INDEX IF NOT EXISTS operation_run_start ON operations(run_id) WHERE request_type='exec_start';
|
|
28
|
-
CREATE INDEX IF NOT EXISTS operation_run_state ON operations(run_id,state);
|
|
29
|
-
CREATE TABLE IF NOT EXISTS aliases (request_id TEXT PRIMARY KEY, operation_id TEXT NOT NULL);
|
|
30
|
-
CREATE TABLE IF NOT EXISTS streams (run_id TEXT NOT NULL, stream TEXT NOT NULL, bytes INTEGER NOT NULL DEFAULT 0, acknowledged INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(run_id, stream));
|
|
31
|
-
CREATE TABLE IF NOT EXISTS output (run_id TEXT NOT NULL, stream TEXT NOT NULL, offset INTEGER NOT NULL, bytes INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_id, stream, offset));
|
|
32
|
-
CREATE TABLE IF NOT EXISTS terminals (run_id TEXT PRIMARY KEY, message TEXT NOT NULL);
|
|
33
|
-
CREATE TABLE IF NOT EXISTS cancelled_requests (request_id TEXT PRIMARY KEY);
|
|
34
|
-
`);
|
|
35
|
-
this.db.run("INSERT OR IGNORE INTO metadata(key,value) VALUES ('ledgerId',?)", [crypto.randomUUID()]);
|
|
36
|
-
this.ledgerId = this.db.query("SELECT value FROM metadata WHERE key='ledgerId'").get().value;
|
|
37
|
-
this.db.run("UPDATE operations SET state='unknown' WHERE state IN ('accepted','preparing','running')");
|
|
38
|
-
}
|
|
39
|
-
close() {
|
|
40
|
-
this.db.close();
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Run several store operations as one durable commit (one FULL fsync).
|
|
44
|
-
* Operations that open their own transaction nest as savepoints. A throw
|
|
45
|
-
* rolls the whole group back; callers treat that as a journal failure.
|
|
46
|
-
*/
|
|
47
|
-
batch(operations) {
|
|
48
|
-
return this.db.transaction(operations)();
|
|
49
|
-
}
|
|
50
|
-
row(requestId) {
|
|
51
|
-
return this.db.query("SELECT * FROM operations WHERE request_id=? OR request_id=(SELECT operation_id FROM aliases WHERE request_id=?)").get(requestId, requestId);
|
|
52
|
-
}
|
|
53
|
-
admit(message) {
|
|
54
|
-
const digest = workerOperationDigest(message);
|
|
55
|
-
let existing = this.row(message.requestId);
|
|
56
|
-
if (!existing && message.type === "exec_start" && typeof message.runId === "string")
|
|
57
|
-
existing = this.db.query("SELECT * FROM operations WHERE run_id=? AND request_type='exec_start'").get(message.runId);
|
|
58
|
-
if (existing) {
|
|
59
|
-
if (existing.digest !== digest) return "conflict";
|
|
60
|
-
this.db.run("INSERT OR IGNORE INTO aliases(request_id, operation_id) VALUES (?,?)", [message.requestId, existing.request_id]);
|
|
61
|
-
return existing.state === "unknown" ? "unknown" : "duplicate";
|
|
62
|
-
}
|
|
63
|
-
this.db.run("INSERT INTO operations(request_id, run_id, session_id, request_type, digest, state) VALUES (?,?,?,?,?,'accepted')", [
|
|
64
|
-
message.requestId,
|
|
65
|
-
typeof message.runId === "string" ? message.runId : null,
|
|
66
|
-
typeof message.sessionId === "string" ? message.sessionId : null,
|
|
67
|
-
message.type,
|
|
68
|
-
digest
|
|
69
|
-
]);
|
|
70
|
-
return "new";
|
|
71
|
-
}
|
|
72
|
-
isUnknown(requestId) {
|
|
73
|
-
return this.row(requestId)?.state === "unknown";
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Re-admit an operation whose crash outcome is unknown so it can run again
|
|
77
|
-
* and record a result. Only a branch deletion qualifies: it is idempotent
|
|
78
|
-
* for its exact incarnation (durable tombstone plus incarnation preflight),
|
|
79
|
-
* so a rerun converges on the same outcome. Every other unknown operation
|
|
80
|
-
* keeps the no-replay invariant.
|
|
81
|
-
*/
|
|
82
|
-
readmitUnknownBranchDeletion(requestId) {
|
|
83
|
-
const row = this.row(requestId);
|
|
84
|
-
if (!row || row.state !== "unknown" || row.request_type !== "delete_project_branch") return false;
|
|
85
|
-
this.db.run("UPDATE operations SET state='accepted', response=NULL WHERE request_id=?", [row.request_id]);
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
unknown(requestId) {
|
|
89
|
-
const row = this.row(requestId);
|
|
90
|
-
if (row) this.db.run("UPDATE operations SET state='unknown' WHERE request_id=?", [row.request_id]);
|
|
91
|
-
}
|
|
92
|
-
unknownRun(runId) {
|
|
93
|
-
this.db.run("UPDATE operations SET state='unknown' WHERE run_id=? AND request_type IN ('exec_start','exec')", [runId]);
|
|
94
|
-
}
|
|
95
|
-
record(message) {
|
|
96
|
-
if (message.type === "exec_output" && typeof message.runId === "string" && (message.stream === "stdout" || message.stream === "stderr") && typeof message.data === "string") {
|
|
97
|
-
const runId = message.runId, stream = message.stream, data = message.data;
|
|
98
|
-
const offset = this.db.transaction(() => {
|
|
99
|
-
this.db.run("INSERT OR IGNORE INTO streams(run_id, stream) VALUES (?,?)", [runId, stream]);
|
|
100
|
-
const offset2 = this.db.query("SELECT bytes FROM streams WHERE run_id=? AND stream=?").get(runId, stream).bytes;
|
|
101
|
-
const bytes = Buffer.byteLength(data);
|
|
102
|
-
if (bytes) {
|
|
103
|
-
this.db.run("INSERT INTO output(run_id, stream, offset, bytes, data) VALUES (?,?,?,?,?)", [runId, stream, offset2, bytes, data]);
|
|
104
|
-
this.db.run("UPDATE streams SET bytes=bytes+? WHERE run_id=? AND stream=?", [bytes, runId, stream]);
|
|
105
|
-
}
|
|
106
|
-
return offset2;
|
|
107
|
-
})();
|
|
108
|
-
return { ...message, offset };
|
|
109
|
-
}
|
|
110
|
-
if ((message.type === "exec_exit" || message.type === "exec_error") && typeof message.runId === "string") {
|
|
111
|
-
const terminal = { ...message, ...this.lengths(message.runId) };
|
|
112
|
-
this.db.transaction(() => {
|
|
113
|
-
this.db.run("INSERT OR REPLACE INTO terminals(run_id,message) VALUES (?,?)", [message.runId, JSON.stringify(terminal)]);
|
|
114
|
-
this.db.run("UPDATE operations SET state='completed' WHERE run_id=? AND request_type='exec_start'", [message.runId]);
|
|
115
|
-
})();
|
|
116
|
-
return terminal;
|
|
117
|
-
}
|
|
118
|
-
if (typeof message.requestId === "string") {
|
|
119
|
-
const row = this.row(message.requestId);
|
|
120
|
-
if (row) {
|
|
121
|
-
const state = message.type === "exec_accepted" || message.type === "operation_queued" ? "preparing" : message.type === "exec_started" ? "running" : message.type === "operation_received" ? "accepted" : "completed";
|
|
122
|
-
if (message.type !== "operation_received")
|
|
123
|
-
this.db.run("UPDATE operations SET state=?, response=? WHERE request_id=?", [state, JSON.stringify(message), row.request_id]);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return message;
|
|
127
|
-
}
|
|
128
|
-
operations() {
|
|
129
|
-
return this.db.query(
|
|
130
|
-
"SELECT * FROM operations WHERE response IS NOT NULL OR state!='completed' UNION ALL SELECT aliases.request_id,operations.run_id,operations.session_id,operations.request_type,operations.digest,operations.state,operations.response FROM aliases JOIN operations ON operations.request_id=aliases.operation_id WHERE aliases.request_id!=operations.request_id AND (response IS NOT NULL OR state!='completed')"
|
|
131
|
-
).all().map((row) => ({
|
|
132
|
-
requestId: row.request_id,
|
|
133
|
-
requestType: row.request_type,
|
|
134
|
-
state: row.state,
|
|
135
|
-
...row.run_id ? { runId: row.run_id } : {},
|
|
136
|
-
...row.session_id ? { sessionId: row.session_id } : {}
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
response(requestId) {
|
|
140
|
-
const row = this.row(requestId);
|
|
141
|
-
return row?.response && row.state !== "unknown" ? { ...JSON.parse(row.response), requestId } : void 0;
|
|
142
|
-
}
|
|
143
|
-
responses(requestIds) {
|
|
144
|
-
const ids = /* @__PURE__ */ new Set([...requestIds, ...this.operations().map((operation) => operation.requestId)]);
|
|
145
|
-
return [...ids].flatMap((requestId) => {
|
|
146
|
-
const response = this.response(requestId);
|
|
147
|
-
return response ? [response] : [];
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
lengths(runId) {
|
|
151
|
-
const result = { stdoutBytes: 0, stderrBytes: 0 };
|
|
152
|
-
for (const row of this.db.query("SELECT stream,bytes FROM streams WHERE run_id=?").all(runId))
|
|
153
|
-
result[row.stream === "stdout" ? "stdoutBytes" : "stderrBytes"] = row.bytes;
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
outputCursors() {
|
|
157
|
-
const cursors = /* @__PURE__ */ new Map();
|
|
158
|
-
for (const stream of this.db.query("SELECT run_id,stream,acknowledged FROM streams").all()) {
|
|
159
|
-
const cursor = cursors.get(stream.run_id) ?? { runId: stream.run_id, stdoutBytes: 0, stderrBytes: 0 };
|
|
160
|
-
cursor[stream.stream === "stdout" ? "stdoutBytes" : "stderrBytes"] = stream.acknowledged;
|
|
161
|
-
cursors.set(stream.run_id, cursor);
|
|
162
|
-
}
|
|
163
|
-
return [...cursors.values()];
|
|
164
|
-
}
|
|
165
|
-
*output(offsets = []) {
|
|
166
|
-
const cursors = new Map(offsets.map((offset) => [offset.runId, offset]));
|
|
167
|
-
const pendingStreamFilter = offsets.length === 0 ? "bytes>acknowledged AND " : "";
|
|
168
|
-
for (const stream of this.db.query(`SELECT * FROM streams WHERE ${pendingStreamFilter}NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=streams.run_id AND operations.state='unknown') ORDER BY rowid`).all()) {
|
|
169
|
-
const supplied = cursors.get(stream.run_id);
|
|
170
|
-
const cursor = supplied ? stream.stream === "stdout" ? supplied.stdoutBytes : supplied.stderrBytes : stream.acknowledged;
|
|
171
|
-
if (cursor >= stream.bytes) continue;
|
|
172
|
-
const firstOffset = this.db.query("SELECT MAX(offset) AS offset FROM output WHERE run_id=? AND stream=? AND offset<=?").get(stream.run_id, stream.stream, cursor)?.offset ?? 0;
|
|
173
|
-
let nextChunk = firstOffset;
|
|
174
|
-
while (true) {
|
|
175
|
-
const row = this.db.query("SELECT offset,data FROM output WHERE run_id=? AND stream=? AND offset>=? AND offset<? AND offset+bytes>? ORDER BY offset LIMIT 1").get(stream.run_id, stream.stream, nextChunk, stream.bytes, cursor);
|
|
176
|
-
if (!row) break;
|
|
177
|
-
nextChunk = row.offset + 1;
|
|
178
|
-
yield { type: "exec_output", runId: stream.run_id, stream: stream.stream, offset: row.offset, data: row.data };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
terminals() {
|
|
183
|
-
return this.db.query(
|
|
184
|
-
"SELECT message FROM terminals WHERE NOT EXISTS (SELECT 1 FROM operations WHERE operations.run_id=terminals.run_id AND operations.state='unknown')"
|
|
185
|
-
).all().map((row) => JSON.parse(row.message));
|
|
186
|
-
}
|
|
187
|
-
acknowledgeOutput(runId, stream, offset) {
|
|
188
|
-
if (!Number.isSafeInteger(offset) || offset < 0) return;
|
|
189
|
-
this.db.run("UPDATE streams SET acknowledged=MAX(acknowledged,MIN(bytes,?)) WHERE run_id=? AND stream=?", [offset, runId, stream]);
|
|
190
|
-
}
|
|
191
|
-
acknowledgeResult(requestId) {
|
|
192
|
-
const row = this.row(requestId);
|
|
193
|
-
if (row && row.request_type !== "exec_start")
|
|
194
|
-
this.db.run("UPDATE operations SET response=NULL WHERE request_id=? AND state='completed'", [row.request_id]);
|
|
195
|
-
}
|
|
196
|
-
acknowledgeTerminal(runId) {
|
|
197
|
-
this.db.transaction(() => {
|
|
198
|
-
this.db.run("DELETE FROM terminals WHERE run_id=?", [runId]);
|
|
199
|
-
this.db.run("DELETE FROM output WHERE run_id=?", [runId]);
|
|
200
|
-
this.db.run("UPDATE streams SET acknowledged=bytes WHERE run_id=?", [runId]);
|
|
201
|
-
this.db.run("UPDATE operations SET response=NULL WHERE run_id=? AND request_type='exec_start' AND state='completed'", [runId]);
|
|
202
|
-
})();
|
|
203
|
-
}
|
|
204
|
-
cancelSession(sessionId) {
|
|
205
|
-
this.db.run("INSERT OR IGNORE INTO cancelled_requests SELECT request_id FROM operations WHERE session_id=? AND state!='completed'", [
|
|
206
|
-
sessionId
|
|
207
|
-
]);
|
|
208
|
-
}
|
|
209
|
-
isRequestCancelled(requestId) {
|
|
210
|
-
const row = this.row(requestId);
|
|
211
|
-
return !!row && this.db.query("SELECT request_id FROM cancelled_requests WHERE request_id=?").get(row.request_id) !== null;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
export {
|
|
215
|
-
WorkerRecoveryStore,
|
|
216
|
-
workerOperationDigest
|
|
217
|
-
};
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
const APP_MANAGED_GITHUB_REGISTRY = "ghcr.io";
|
|
4
|
-
class RegistryAuthConfigurationError extends Error {
|
|
5
|
-
constructor(cause) {
|
|
6
|
-
super("Could not authoritatively configure GitHub registry authentication", { cause });
|
|
7
|
-
this.name = "RegistryAuthConfigurationError";
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
function isJsonObject(value) {
|
|
11
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
-
}
|
|
13
|
-
function lstatIfExists(filePath) {
|
|
14
|
-
try {
|
|
15
|
-
return fs.lstatSync(filePath);
|
|
16
|
-
} catch (error) {
|
|
17
|
-
if (error.code === "ENOENT") return null;
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function readRegistryAuthConfig(filePath) {
|
|
22
|
-
const stat = lstatIfExists(filePath);
|
|
23
|
-
if (!stat) return null;
|
|
24
|
-
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error(`Registry auth config must be a regular file: ${filePath}`);
|
|
25
|
-
let parsed;
|
|
26
|
-
try {
|
|
27
|
-
parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
28
|
-
} catch (error) {
|
|
29
|
-
throw new Error(`Registry auth config is not valid JSON: ${filePath}`, { cause: error });
|
|
30
|
-
}
|
|
31
|
-
if (!isJsonObject(parsed)) throw new Error(`Registry auth config must contain a JSON object: ${filePath}`);
|
|
32
|
-
return parsed;
|
|
33
|
-
}
|
|
34
|
-
function registryAuthHasAppManagedGitHubCredential(filePath) {
|
|
35
|
-
const config = readRegistryAuthConfig(filePath);
|
|
36
|
-
if (!config) return false;
|
|
37
|
-
if (config.auths === void 0) return false;
|
|
38
|
-
if (!isJsonObject(config.auths)) throw new Error(`Registry auth config has an invalid auths object: ${filePath}`);
|
|
39
|
-
return Object.prototype.hasOwnProperty.call(config.auths, APP_MANAGED_GITHUB_REGISTRY);
|
|
40
|
-
}
|
|
41
|
-
function fsyncDirectory(directory, dependencies = {
|
|
42
|
-
platform: process.platform,
|
|
43
|
-
open: (target) => fs.openSync(target, "r"),
|
|
44
|
-
fsync: fs.fsyncSync,
|
|
45
|
-
close: fs.closeSync
|
|
46
|
-
}) {
|
|
47
|
-
if (dependencies.platform === "win32") return;
|
|
48
|
-
const descriptor = dependencies.open(directory);
|
|
49
|
-
try {
|
|
50
|
-
dependencies.fsync(descriptor);
|
|
51
|
-
} finally {
|
|
52
|
-
dependencies.close(descriptor);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function ensurePrivateParentDirectory(filePath) {
|
|
56
|
-
const directory = path.dirname(filePath);
|
|
57
|
-
let existingAncestor = directory;
|
|
58
|
-
while (!lstatIfExists(existingAncestor)) {
|
|
59
|
-
const parent = path.dirname(existingAncestor);
|
|
60
|
-
if (parent === existingAncestor) throw new Error(`Registry auth parent has no existing ancestor: ${directory}`);
|
|
61
|
-
existingAncestor = parent;
|
|
62
|
-
}
|
|
63
|
-
const ancestorStat = lstatIfExists(existingAncestor);
|
|
64
|
-
if (!ancestorStat || ancestorStat.isSymbolicLink() || !ancestorStat.isDirectory()) {
|
|
65
|
-
throw new Error(`Registry auth parent must descend from a real directory: ${directory}`);
|
|
66
|
-
}
|
|
67
|
-
fs.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
68
|
-
let current = directory;
|
|
69
|
-
while (true) {
|
|
70
|
-
const stat = lstatIfExists(current);
|
|
71
|
-
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
72
|
-
throw new Error(`Registry auth parent must be a real directory: ${current}`);
|
|
73
|
-
}
|
|
74
|
-
fsyncDirectory(current);
|
|
75
|
-
if (current === existingAncestor) break;
|
|
76
|
-
current = path.dirname(current);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function stagePrivateFile(filePath, content) {
|
|
80
|
-
ensurePrivateParentDirectory(filePath);
|
|
81
|
-
const temporaryPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.r5d-auth.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
82
|
-
let descriptor;
|
|
83
|
-
try {
|
|
84
|
-
descriptor = fs.openSync(temporaryPath, "wx", 384);
|
|
85
|
-
fs.writeFileSync(descriptor, content, "utf8");
|
|
86
|
-
fs.fsyncSync(descriptor);
|
|
87
|
-
fs.closeSync(descriptor);
|
|
88
|
-
descriptor = void 0;
|
|
89
|
-
fs.chmodSync(temporaryPath, 384);
|
|
90
|
-
return { filePath, temporaryPath };
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (descriptor !== void 0) fs.closeSync(descriptor);
|
|
93
|
-
fs.rmSync(temporaryPath, { force: true });
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function commitStagedPrivateFile(staged, beforeRename) {
|
|
98
|
-
beforeRename?.();
|
|
99
|
-
fs.renameSync(staged.temporaryPath, staged.filePath);
|
|
100
|
-
fs.chmodSync(staged.filePath, 384);
|
|
101
|
-
fsyncDirectory(path.dirname(staged.filePath));
|
|
102
|
-
}
|
|
103
|
-
function discardStagedPrivateFile(staged) {
|
|
104
|
-
fs.rmSync(staged.temporaryPath, { force: true });
|
|
105
|
-
}
|
|
106
|
-
function cleanupAbandonedStagedFiles(filePath) {
|
|
107
|
-
const directory = path.dirname(filePath);
|
|
108
|
-
const prefix = `.${path.basename(filePath)}.r5d-auth.`;
|
|
109
|
-
let entries;
|
|
110
|
-
try {
|
|
111
|
-
entries = fs.readdirSync(directory);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
if (error.code === "ENOENT") return;
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
let removed = false;
|
|
117
|
-
for (const name of entries) {
|
|
118
|
-
if (!name.startsWith(prefix) || !name.endsWith(".tmp")) continue;
|
|
119
|
-
const candidate = path.join(directory, name);
|
|
120
|
-
const stat = fs.lstatSync(candidate);
|
|
121
|
-
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
122
|
-
throw new Error(`Registry auth staging artifact must be a regular file: ${candidate}`);
|
|
123
|
-
}
|
|
124
|
-
fs.rmSync(candidate);
|
|
125
|
-
removed = true;
|
|
126
|
-
}
|
|
127
|
-
if (removed) fsyncDirectory(directory);
|
|
128
|
-
}
|
|
129
|
-
function atomicReplacePrivateFile(filePath, content, beforeRename) {
|
|
130
|
-
const staged = stagePrivateFile(filePath, content);
|
|
131
|
-
try {
|
|
132
|
-
commitStagedPrivateFile(staged, beforeRename);
|
|
133
|
-
} finally {
|
|
134
|
-
discardStagedPrivateFile(staged);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function preparePrivateAuthFileGeneration(updates) {
|
|
138
|
-
try {
|
|
139
|
-
const desiredByPath = /* @__PURE__ */ new Map();
|
|
140
|
-
for (const update of updates) {
|
|
141
|
-
const filePath = path.resolve(update.filePath);
|
|
142
|
-
const existing = desiredByPath.get(filePath);
|
|
143
|
-
const desired = { content: update.content, allowSymlinkRemoval: update.allowSymlinkRemoval === true };
|
|
144
|
-
if (existing && (existing.content !== desired.content || existing.allowSymlinkRemoval !== desired.allowSymlinkRemoval)) {
|
|
145
|
-
throw new Error(`Conflicting desired auth content for ${filePath}`);
|
|
146
|
-
}
|
|
147
|
-
desiredByPath.set(filePath, desired);
|
|
148
|
-
}
|
|
149
|
-
const changes = [];
|
|
150
|
-
const stagedFiles = [];
|
|
151
|
-
try {
|
|
152
|
-
for (const [filePath, desired] of desiredByPath) {
|
|
153
|
-
const { content, allowSymlinkRemoval } = desired;
|
|
154
|
-
cleanupAbandonedStagedFiles(filePath);
|
|
155
|
-
const status = lstatIfExists(filePath);
|
|
156
|
-
if (status && (status.isSymbolicLink() ? !(content === null && allowSymlinkRemoval) : !status.isFile())) {
|
|
157
|
-
throw new Error(`Private auth target must be a regular file: ${filePath}`);
|
|
158
|
-
}
|
|
159
|
-
const unchanged = content === null ? !status : Boolean(status && fs.readFileSync(filePath, "utf8") === content && (status.mode & 511) === 384);
|
|
160
|
-
if (unchanged) continue;
|
|
161
|
-
if (content === null) {
|
|
162
|
-
changes.push({ filePath, content });
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
const staged = stagePrivateFile(filePath, content);
|
|
166
|
-
stagedFiles.push(staged);
|
|
167
|
-
changes.push({ filePath, content, staged });
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
for (const staged of stagedFiles) discardStagedPrivateFile(staged);
|
|
171
|
-
throw error;
|
|
172
|
-
}
|
|
173
|
-
let finished = false;
|
|
174
|
-
const discard = () => {
|
|
175
|
-
for (const staged of stagedFiles) discardStagedPrivateFile(staged);
|
|
176
|
-
};
|
|
177
|
-
return {
|
|
178
|
-
changed: changes.length > 0,
|
|
179
|
-
commit(beforeMutation) {
|
|
180
|
-
if (finished) throw new Error("Private auth generation has already been finalized");
|
|
181
|
-
finished = true;
|
|
182
|
-
try {
|
|
183
|
-
changes.forEach((change, index) => {
|
|
184
|
-
beforeMutation?.(change.filePath, index);
|
|
185
|
-
if (change.content === null) {
|
|
186
|
-
fs.unlinkSync(change.filePath);
|
|
187
|
-
fsyncDirectory(path.dirname(change.filePath));
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
if (!change.staged) throw new Error(`Private auth replacement was not staged: ${change.filePath}`);
|
|
191
|
-
commitStagedPrivateFile(change.staged);
|
|
192
|
-
});
|
|
193
|
-
} catch (error) {
|
|
194
|
-
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
195
|
-
} finally {
|
|
196
|
-
discard();
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
discard() {
|
|
200
|
-
if (finished) return;
|
|
201
|
-
finished = true;
|
|
202
|
-
discard();
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
} catch (error) {
|
|
206
|
-
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function registryAuthFileContent(filePath, credential) {
|
|
210
|
-
if (credential && credential.registry !== APP_MANAGED_GITHUB_REGISTRY) {
|
|
211
|
-
throw new Error(`Unsupported app-managed registry: ${credential.registry}`);
|
|
212
|
-
}
|
|
213
|
-
const existing = readRegistryAuthConfig(filePath);
|
|
214
|
-
if (!existing && !credential) return null;
|
|
215
|
-
const config = { ...existing ?? {} };
|
|
216
|
-
const existingAuths = config.auths;
|
|
217
|
-
if (existingAuths !== void 0 && !isJsonObject(existingAuths)) {
|
|
218
|
-
throw new Error(`Registry auth config has an invalid auths object: ${filePath}`);
|
|
219
|
-
}
|
|
220
|
-
const auths = { ...existingAuths ?? {} };
|
|
221
|
-
delete auths[APP_MANAGED_GITHUB_REGISTRY];
|
|
222
|
-
if (credential) {
|
|
223
|
-
auths[APP_MANAGED_GITHUB_REGISTRY] = {
|
|
224
|
-
username: credential.username,
|
|
225
|
-
password: credential.token,
|
|
226
|
-
auth: Buffer.from(`${credential.username}:${credential.token}`).toString("base64")
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
config.auths = auths;
|
|
230
|
-
return `${JSON.stringify(config, null, 2)}
|
|
231
|
-
`;
|
|
232
|
-
}
|
|
233
|
-
function configureGitHubRegistryAuthFilesWithHook(filePaths, credential, beforeRename) {
|
|
234
|
-
try {
|
|
235
|
-
const prepared = preparePrivateAuthFileGeneration(planGitHubRegistryAuthFiles(filePaths, credential));
|
|
236
|
-
prepared.commit(beforeRename);
|
|
237
|
-
} catch (error) {
|
|
238
|
-
throw error instanceof RegistryAuthConfigurationError ? error : new RegistryAuthConfigurationError(error);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function planGitHubRegistryAuthFiles(filePaths, credential) {
|
|
242
|
-
const uniquePaths = [...new Set(filePaths.map((filePath) => path.resolve(filePath)))];
|
|
243
|
-
return uniquePaths.map((filePath) => ({ filePath, content: registryAuthFileContent(filePath, credential) }));
|
|
244
|
-
}
|
|
245
|
-
function updateGitHubRegistryAuthFile(filePath, credential) {
|
|
246
|
-
configureGitHubRegistryAuthFilesWithHook([filePath], credential);
|
|
247
|
-
}
|
|
248
|
-
function configureGitHubRegistryAuthFiles(filePaths, credential) {
|
|
249
|
-
configureGitHubRegistryAuthFilesWithHook(filePaths, credential);
|
|
250
|
-
}
|
|
251
|
-
const registryAuthTestHarness = {
|
|
252
|
-
fsyncDirectory,
|
|
253
|
-
atomicReplacePrivateFile(filePath, content, beforeRename) {
|
|
254
|
-
atomicReplacePrivateFile(filePath, content, beforeRename);
|
|
255
|
-
},
|
|
256
|
-
configureFiles(filePaths, credential, beforeRename) {
|
|
257
|
-
configureGitHubRegistryAuthFilesWithHook(filePaths, credential, beforeRename);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
export {
|
|
261
|
-
APP_MANAGED_GITHUB_REGISTRY,
|
|
262
|
-
RegistryAuthConfigurationError,
|
|
263
|
-
configureGitHubRegistryAuthFiles,
|
|
264
|
-
planGitHubRegistryAuthFiles,
|
|
265
|
-
preparePrivateAuthFileGeneration,
|
|
266
|
-
registryAuthHasAppManagedGitHubCredential,
|
|
267
|
-
registryAuthTestHarness,
|
|
268
|
-
updateGitHubRegistryAuthFile
|
|
269
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
function assertRepositoryTransitionState(project) {
|
|
2
|
-
if (project.repositoryTransitionId !== null && (typeof project.repositoryTransitionId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(project.repositoryTransitionId)) || typeof project.executionDisabled !== "boolean" || typeof project.mirrorWritesDisabled !== "boolean") {
|
|
3
|
-
throw new Error(`Project ${project.projectId} has invalid repository transition state`);
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
function assertRepositoryExecutionEnabled(project) {
|
|
7
|
-
if (project.executionDisabled) {
|
|
8
|
-
throw new Error(`Project ${project.projectId} execution is disabled while its repository connection is transitioning`);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
function repositoryMirrorWritesAllowed(project) {
|
|
12
|
-
return project?.executionDisabled !== true && project?.mirrorWritesDisabled !== true;
|
|
13
|
-
}
|
|
14
|
-
function enabledRepositoryTransitionRequiresReseed(input) {
|
|
15
|
-
return !input.project.executionDisabled && (!input.lastEnabled.known || input.lastEnabled.transitionId !== input.project.repositoryTransitionId);
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
assertRepositoryExecutionEnabled,
|
|
19
|
-
assertRepositoryTransitionState,
|
|
20
|
-
enabledRepositoryTransitionRequiresReseed,
|
|
21
|
-
repositoryMirrorWritesAllowed
|
|
22
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
|
|
4
|
-
function readVersionFile(filePath) {
|
|
5
|
-
try {
|
|
6
|
-
const version = fs.readFileSync(filePath, "utf8").trim();
|
|
7
|
-
return version || null;
|
|
8
|
-
} catch {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
function readInstalledPackageVersion(packageJsonPath) {
|
|
13
|
-
try {
|
|
14
|
-
const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
15
|
-
if (parsed.name === WORKER_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.trim()) {
|
|
16
|
-
return parsed.version.trim();
|
|
17
|
-
}
|
|
18
|
-
} catch {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
function resolveEntrypointPath(entrypoint) {
|
|
24
|
-
const resolved = path.resolve(entrypoint);
|
|
25
|
-
try {
|
|
26
|
-
return fs.realpathSync.native(resolved);
|
|
27
|
-
} catch {
|
|
28
|
-
return resolved;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function readWorkerVersion() {
|
|
32
|
-
const entrypoint = process.argv[1] ? resolveEntrypointPath(process.argv[1]) : null;
|
|
33
|
-
let current = entrypoint ? path.dirname(entrypoint) : process.cwd();
|
|
34
|
-
for (let index = 0; index < 12; index += 1) {
|
|
35
|
-
const packageVersion = readInstalledPackageVersion(path.join(current, "package.json"));
|
|
36
|
-
if (packageVersion) {
|
|
37
|
-
return packageVersion;
|
|
38
|
-
}
|
|
39
|
-
const sourceVersion = readVersionFile(path.join(current, "VERSION.txt"));
|
|
40
|
-
if (sourceVersion && path.basename(current) === "packages") {
|
|
41
|
-
return sourceVersion;
|
|
42
|
-
}
|
|
43
|
-
const parent = path.dirname(current);
|
|
44
|
-
if (parent === current) {
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
current = parent;
|
|
48
|
-
}
|
|
49
|
-
return "unknown";
|
|
50
|
-
}
|
|
51
|
-
const WORKER_RUNTIME_VERSION = readWorkerVersion();
|
|
52
|
-
export {
|
|
53
|
-
WORKER_RUNTIME_VERSION
|
|
54
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
class SessionFileMutationTracker {
|
|
2
|
-
inFlight = /* @__PURE__ */ new Map();
|
|
3
|
-
/** Register a started mutation; it is forgotten when it settles, however it settles. */
|
|
4
|
-
track(sessionId, mutation) {
|
|
5
|
-
let mutations = this.inFlight.get(sessionId);
|
|
6
|
-
if (!mutations) {
|
|
7
|
-
mutations = /* @__PURE__ */ new Set();
|
|
8
|
-
this.inFlight.set(sessionId, mutations);
|
|
9
|
-
}
|
|
10
|
-
mutations.add(mutation);
|
|
11
|
-
const forget = () => {
|
|
12
|
-
const current = this.inFlight.get(sessionId);
|
|
13
|
-
if (!current) return;
|
|
14
|
-
current.delete(mutation);
|
|
15
|
-
if (current.size === 0) this.inFlight.delete(sessionId);
|
|
16
|
-
};
|
|
17
|
-
mutation.then(forget, forget);
|
|
18
|
-
return mutation;
|
|
19
|
-
}
|
|
20
|
-
count(sessionId) {
|
|
21
|
-
return this.inFlight.get(sessionId)?.size ?? 0;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Resolves once every mutation tracked for the session at the time of the
|
|
25
|
-
* call has settled. Mutations started later are not joined: the session's
|
|
26
|
-
* cancellation marker, recorded before this is awaited, makes their
|
|
27
|
-
* admission recheck refuse them.
|
|
28
|
-
*/
|
|
29
|
-
settled(sessionId) {
|
|
30
|
-
const pending = [...this.inFlight.get(sessionId) ?? []];
|
|
31
|
-
if (pending.length === 0) return Promise.resolve();
|
|
32
|
-
return Promise.allSettled(pending).then(() => void 0);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
export {
|
|
36
|
-
SessionFileMutationTracker
|
|
37
|
-
};
|