crewx-bridge 0.2.7 → 0.2.8
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 +78 -8
- package/dist/api.d.ts +10 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +102 -7
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/daemon-lock.d.ts +29 -0
- package/dist/daemon-lock.d.ts.map +1 -0
- package/dist/daemon-lock.js +257 -0
- package/dist/daemon-lock.js.map +1 -0
- package/dist/daemon.d.ts +21 -4
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +466 -140
- package/dist/daemon.js.map +1 -1
- package/dist/errors.d.ts +12 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +19 -5
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -19
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts +51 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +213 -4
- package/dist/journal.js.map +1 -1
- package/dist/outcomes.d.ts +12 -0
- package/dist/outcomes.d.ts.map +1 -0
- package/dist/outcomes.js +52 -0
- package/dist/outcomes.js.map +1 -0
- package/dist/service.d.ts +14 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +80 -1
- package/dist/service.js.map +1 -1
- package/dist/supervisor.d.ts +12 -2
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +105 -14
- package/dist/supervisor.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { constants as fsConstants, readFileSync } from "node:fs";
|
|
4
|
+
import { lstat, link, mkdir, open, rmdir, unlink, } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { bridgeStateDirectory, ensureSecureDirectory, readSecureText, } from "./config.js";
|
|
8
|
+
import { BridgeError } from "./errors.js";
|
|
9
|
+
const DaemonLockRecordSchema = z
|
|
10
|
+
.object({
|
|
11
|
+
version: z.literal(1),
|
|
12
|
+
pid: z.number().int().positive(),
|
|
13
|
+
nonce: z.uuid(),
|
|
14
|
+
started_at: z.iso.datetime({ offset: true }),
|
|
15
|
+
process_fingerprint: z.string().regex(/^[a-f0-9]{64}$/),
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
export function bridgeDaemonLockPath(environment = process.env, platform = process.platform) {
|
|
19
|
+
return join(bridgeStateDirectory(environment, platform), "daemon.lock");
|
|
20
|
+
}
|
|
21
|
+
function processIsAlive(pid) {
|
|
22
|
+
try {
|
|
23
|
+
process.kill(pid, 0);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return error.code === "EPERM";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function hashProcessIdentity(identity) {
|
|
31
|
+
return createHash("sha256").update(identity).digest("hex");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A PID alone is not an ownership proof: an OS can recycle it after the
|
|
35
|
+
* original daemon exits. Bind the lock to an immutable process-start identity
|
|
36
|
+
* so a replacement process with the same PID is still treated as stale.
|
|
37
|
+
*/
|
|
38
|
+
export function processFingerprint(pid, platform = process.platform) {
|
|
39
|
+
if (!processIsAlive(pid))
|
|
40
|
+
return undefined;
|
|
41
|
+
try {
|
|
42
|
+
if (platform === "linux") {
|
|
43
|
+
const stat = readFileSync(`/proc/${String(pid)}/stat`, "utf8");
|
|
44
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
45
|
+
if (commandEnd < 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
// The fields after comm begin at field 3; process start time is field 22.
|
|
48
|
+
const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
|
|
49
|
+
const startTime = fields[19];
|
|
50
|
+
if (!startTime)
|
|
51
|
+
return undefined;
|
|
52
|
+
return hashProcessIdentity(`linux:${String(pid)}:${startTime}`);
|
|
53
|
+
}
|
|
54
|
+
if (platform === "darwin") {
|
|
55
|
+
const identity = execFileSync("/bin/ps", ["-ww", "-p", String(pid), "-o", "lstart=", "-o", "command="], {
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
timeout: 2_000,
|
|
58
|
+
maxBuffer: 64 * 1024,
|
|
59
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
60
|
+
}).trim();
|
|
61
|
+
if (!identity)
|
|
62
|
+
return undefined;
|
|
63
|
+
return hashProcessIdentity(`darwin:${String(pid)}:${identity}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function delay(milliseconds) {
|
|
72
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
73
|
+
}
|
|
74
|
+
export class BridgeDaemonLock {
|
|
75
|
+
fingerprint;
|
|
76
|
+
now;
|
|
77
|
+
recoveryStaleMs;
|
|
78
|
+
path;
|
|
79
|
+
recoveryPath;
|
|
80
|
+
held;
|
|
81
|
+
constructor(environment = process.env, platform = process.platform, fingerprint = (pid) => processFingerprint(pid, platform), now = Date.now, recoveryStaleMs = 30_000) {
|
|
82
|
+
this.fingerprint = fingerprint;
|
|
83
|
+
this.now = now;
|
|
84
|
+
this.recoveryStaleMs = recoveryStaleMs;
|
|
85
|
+
this.path = bridgeDaemonLockPath(environment, platform);
|
|
86
|
+
this.recoveryPath = `${this.path}.recovery`;
|
|
87
|
+
}
|
|
88
|
+
async acquire() {
|
|
89
|
+
if (this.held)
|
|
90
|
+
return;
|
|
91
|
+
await ensureSecureDirectory(dirname(this.path));
|
|
92
|
+
const ownFingerprint = this.fingerprint(process.pid);
|
|
93
|
+
if (!ownFingerprint) {
|
|
94
|
+
throw new BridgeError("daemon_identity_unavailable", "CrewX Bridge could not verify the daemon process identity.", 1, { retryable: true });
|
|
95
|
+
}
|
|
96
|
+
const record = {
|
|
97
|
+
version: 1,
|
|
98
|
+
pid: process.pid,
|
|
99
|
+
nonce: randomUUID(),
|
|
100
|
+
started_at: new Date(this.now()).toISOString(),
|
|
101
|
+
process_fingerprint: ownFingerprint,
|
|
102
|
+
};
|
|
103
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
104
|
+
if (await this.tryCreate(record))
|
|
105
|
+
return;
|
|
106
|
+
let existing;
|
|
107
|
+
try {
|
|
108
|
+
existing = await this.readExisting();
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code === "ENOENT")
|
|
112
|
+
continue;
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
if (this.isCurrentOwner(existing)) {
|
|
116
|
+
throw this.alreadyRunning(existing.pid);
|
|
117
|
+
}
|
|
118
|
+
const ownsRecovery = await this.tryAcquireRecovery();
|
|
119
|
+
if (!ownsRecovery) {
|
|
120
|
+
await delay(10);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
// Recovery is serialized. Re-read after taking the recovery mutex so a
|
|
125
|
+
// replacement daemon created by another contender is never unlinked.
|
|
126
|
+
try {
|
|
127
|
+
existing = await this.readExisting();
|
|
128
|
+
if (this.isCurrentOwner(existing)) {
|
|
129
|
+
throw this.alreadyRunning(existing.pid);
|
|
130
|
+
}
|
|
131
|
+
await unlink(this.path);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (error.code !== "ENOENT")
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
if (await this.tryCreate(record))
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
await rmdir(this.recoveryPath).catch((error) => {
|
|
142
|
+
if (error.code !== "ENOENT")
|
|
143
|
+
throw error;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw new BridgeError("daemon_lock_contended", "CrewX Bridge could not acquire its daemon lock because another process is starting.", 1, { retryable: true });
|
|
148
|
+
}
|
|
149
|
+
async tryCreate(record) {
|
|
150
|
+
const temporary = join(dirname(this.path), `.daemon.lock.${record.nonce}.tmp`);
|
|
151
|
+
try {
|
|
152
|
+
const handle = await open(temporary, fsConstants.O_WRONLY |
|
|
153
|
+
fsConstants.O_CREAT |
|
|
154
|
+
fsConstants.O_EXCL |
|
|
155
|
+
(fsConstants.O_NOFOLLOW ?? 0), 0o600);
|
|
156
|
+
try {
|
|
157
|
+
await handle.writeFile(`${JSON.stringify(record)}\n`, "utf8");
|
|
158
|
+
await handle.sync();
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
await handle.close();
|
|
162
|
+
}
|
|
163
|
+
// link(2) publishes the fully-written inode only if the lock name does
|
|
164
|
+
// not exist, giving contenders an atomic create-with-content operation.
|
|
165
|
+
await link(temporary, this.path);
|
|
166
|
+
this.held = record;
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (error.code === "EEXIST")
|
|
171
|
+
return false;
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
await unlink(temporary).catch((error) => {
|
|
176
|
+
if (error.code !== "ENOENT")
|
|
177
|
+
throw error;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async tryAcquireRecovery() {
|
|
182
|
+
try {
|
|
183
|
+
await mkdir(this.recoveryPath, { mode: 0o700 });
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
if (error.code !== "EEXIST")
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
const info = await lstat(this.recoveryPath);
|
|
191
|
+
if (!info.isDirectory() ||
|
|
192
|
+
info.isSymbolicLink() ||
|
|
193
|
+
(info.mode & 0o077) !== 0) {
|
|
194
|
+
throw new BridgeError("invalid_daemon_lock", `CrewX Bridge daemon recovery lock at ${this.recoveryPath} is unsafe.`, 1, { retryable: false });
|
|
195
|
+
}
|
|
196
|
+
if (this.now() - info.mtimeMs <= this.recoveryStaleMs)
|
|
197
|
+
return false;
|
|
198
|
+
// There is no portable compare-and-delete primitive for directories. An
|
|
199
|
+
// automatic stale cleanup could remove a fresh contender's mutex between
|
|
200
|
+
// lstat(2) and rmdir(2), admitting two recovery owners. Fail closed and
|
|
201
|
+
// require explicit operator inspection for this exceptionally narrow
|
|
202
|
+
// crash window instead.
|
|
203
|
+
throw new BridgeError("stale_daemon_recovery_lock", `CrewX Bridge found crash residue at ${this.recoveryPath}. Confirm no Bridge process is starting, then remove that directory and restart the service.`, 1, { retryable: false });
|
|
204
|
+
}
|
|
205
|
+
isCurrentOwner(record) {
|
|
206
|
+
return this.fingerprint(record.pid) === record.process_fingerprint;
|
|
207
|
+
}
|
|
208
|
+
alreadyRunning(pid) {
|
|
209
|
+
return new BridgeError("daemon_already_running", `CrewX Bridge is already running as PID ${pid}. Use \`crewx-bridge service status\` or \`crewx-bridge doctor\` to inspect it.`, 1, { retryable: false });
|
|
210
|
+
}
|
|
211
|
+
async release() {
|
|
212
|
+
const held = this.held;
|
|
213
|
+
this.held = undefined;
|
|
214
|
+
if (!held)
|
|
215
|
+
return;
|
|
216
|
+
let existing;
|
|
217
|
+
try {
|
|
218
|
+
existing = await this.readExisting();
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
if (error.code === "ENOENT")
|
|
222
|
+
return;
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
if (existing.pid !== held.pid || existing.nonce !== held.nonce)
|
|
226
|
+
return;
|
|
227
|
+
await unlink(this.path).catch((error) => {
|
|
228
|
+
if (error.code !== "ENOENT")
|
|
229
|
+
throw error;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async readExisting() {
|
|
233
|
+
const raw = await readSecureText(this.path, 4 * 1024);
|
|
234
|
+
if (!raw) {
|
|
235
|
+
const missing = new Error(`Bridge daemon lock disappeared: ${this.path}`);
|
|
236
|
+
missing.code = "ENOENT";
|
|
237
|
+
throw missing;
|
|
238
|
+
}
|
|
239
|
+
let parsed;
|
|
240
|
+
try {
|
|
241
|
+
parsed = JSON.parse(raw);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
throw new BridgeError("invalid_daemon_lock", `CrewX Bridge daemon lock at ${this.path} is invalid JSON.`, 1, { cause: error, retryable: false });
|
|
245
|
+
}
|
|
246
|
+
const result = DaemonLockRecordSchema.safeParse(parsed);
|
|
247
|
+
if (!result.success) {
|
|
248
|
+
throw new BridgeError("invalid_daemon_lock", `CrewX Bridge daemon lock at ${this.path} has an unsupported shape.`, 1, { retryable: false });
|
|
249
|
+
}
|
|
250
|
+
const info = await lstat(this.path);
|
|
251
|
+
if (!info.isFile() || info.isSymbolicLink() || (info.mode & 0o077) !== 0) {
|
|
252
|
+
throw new BridgeError("invalid_daemon_lock", `CrewX Bridge daemon lock at ${this.path} is unsafe.`, 1, { retryable: false });
|
|
253
|
+
}
|
|
254
|
+
return result.data;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=daemon-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../src/daemon-lock.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { probeAdapter } from "crewx-agent-cli/adapters";
|
|
2
|
+
import { type AgentAdapter, type BridgeCapabilities, type BridgeClaimedCommand, type BridgeHealth, type BridgePolicySummary } from "crewx-agent-protocol";
|
|
2
3
|
import { BridgeApi } from "./api.js";
|
|
3
4
|
import { type BridgeConfig } from "./config.js";
|
|
5
|
+
import { type BridgeDaemonLockLike } from "./daemon-lock.js";
|
|
4
6
|
import { BridgeJournal } from "./journal.js";
|
|
5
7
|
import { BridgeRunLogs } from "./run-logs.js";
|
|
6
8
|
import { ProcessSupervisor } from "./supervisor.js";
|
|
7
9
|
export type BridgeLog = (message: string) => void;
|
|
8
|
-
export declare function
|
|
10
|
+
export declare function adaptiveIdleClaimDelay(serverMinimumMs: number, consecutiveEmptyClaims: number, random?: () => number): number;
|
|
11
|
+
export declare function probeCapabilities(runtimeProbe?: typeof probeAdapter): Promise<BridgeCapabilities>;
|
|
9
12
|
export declare function policySummary(config: BridgeConfig): BridgePolicySummary;
|
|
13
|
+
export declare function bridgeHealth(journal: BridgeJournal): BridgeHealth;
|
|
10
14
|
export declare class BridgeDaemon {
|
|
11
15
|
readonly config: BridgeConfig;
|
|
12
16
|
readonly api: BridgeApi;
|
|
13
17
|
readonly journal: BridgeJournal;
|
|
14
18
|
readonly supervisor: ProcessSupervisor;
|
|
15
|
-
|
|
19
|
+
private capabilities;
|
|
16
20
|
readonly log: BridgeLog;
|
|
17
21
|
readonly runLogs: BridgeRunLogs;
|
|
22
|
+
readonly capabilityProbe: () => Promise<BridgeCapabilities>;
|
|
23
|
+
readonly now: () => number;
|
|
24
|
+
readonly random: () => number;
|
|
18
25
|
private lastHeartbeat;
|
|
19
|
-
|
|
26
|
+
private lastCapabilityProbe;
|
|
27
|
+
private outcomeReconcileFailures;
|
|
28
|
+
private nextOutcomeReconcileAt;
|
|
29
|
+
private consecutiveEmptyClaims;
|
|
30
|
+
private nextClaimAt;
|
|
31
|
+
constructor(config: BridgeConfig, api: BridgeApi, journal: BridgeJournal, supervisor: ProcessSupervisor, capabilities: BridgeCapabilities, log?: BridgeLog, runLogs?: BridgeRunLogs, capabilityProbe?: () => Promise<BridgeCapabilities>, now?: () => number, random?: () => number);
|
|
20
32
|
initialize(): Promise<void>;
|
|
21
33
|
heartbeat(): Promise<void>;
|
|
22
34
|
tick(): Promise<number>;
|
|
35
|
+
private delayUntilHeartbeat;
|
|
36
|
+
reconcileOutcomes(force?: boolean): Promise<void>;
|
|
23
37
|
execute(command: BridgeClaimedCommand): Promise<void>;
|
|
38
|
+
private reportOutcome;
|
|
39
|
+
private recoverAppliedOutcome;
|
|
24
40
|
private start;
|
|
25
41
|
private stop;
|
|
26
42
|
}
|
|
@@ -34,6 +50,7 @@ export declare function runBridge(options: {
|
|
|
34
50
|
capabilities?: BridgeCapabilities;
|
|
35
51
|
log?: BridgeLog;
|
|
36
52
|
runLogs?: BridgeRunLogs;
|
|
53
|
+
daemonLock?: BridgeDaemonLockLike;
|
|
37
54
|
}): Promise<void>;
|
|
38
55
|
export declare function supportedAdapters(): readonly AgentAdapter[];
|
|
39
56
|
//# sourceMappingURL=daemon.d.ts.map
|
package/dist/daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|