@rallycry/conveyor-agent 10.13.17 → 10.13.50
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/{boot-NMXFM4GK.js → boot-I353EAK6.js} +156 -124
- package/dist/boot-I353EAK6.js.map +1 -0
- package/dist/{chunk-NI5L2R2N.js → chunk-36VMMHYD.js} +5607 -2115
- package/dist/chunk-36VMMHYD.js.map +1 -0
- package/dist/{chunk-5UYMDSAE.js → chunk-3Z4YQNJV.js} +59 -13
- package/dist/chunk-3Z4YQNJV.js.map +1 -0
- package/dist/chunk-5OQQSDVT.js +142 -0
- package/dist/chunk-5OQQSDVT.js.map +1 -0
- package/dist/{chunk-UZTJJD7Y.js → chunk-KCB7CSWJ.js} +43 -2
- package/dist/chunk-KCB7CSWJ.js.map +1 -0
- package/dist/{chunk-UDSRAHKP.js → chunk-QOJTJCYZ.js} +67 -19
- package/dist/chunk-QOJTJCYZ.js.map +1 -0
- package/dist/chunk-ULS4QPRE.js +138 -0
- package/dist/chunk-ULS4QPRE.js.map +1 -0
- package/dist/{chunk-JG54UJ6V.js → chunk-XR6H326I.js} +26 -2
- package/dist/chunk-XR6H326I.js.map +1 -0
- package/dist/cli.js +196 -334
- package/dist/cli.js.map +1 -1
- package/dist/{client-BU4XA7CV.js → client-LRVVHTNG.js} +2 -2
- package/dist/heartbeat-worker.js +10 -12
- package/dist/heartbeat-worker.js.map +1 -1
- package/dist/index.d.ts +154 -8
- package/dist/index.js +5 -5
- package/dist/oom-watchdog-U7JERHA2.js +11 -0
- package/dist/server-NIOBJ46G.js +10 -0
- package/dist/server-NIOBJ46G.js.map +1 -0
- package/package.json +3 -3
- package/runtime/entrypoint.sh +5 -4
- package/dist/boot-NMXFM4GK.js.map +0 -1
- package/dist/chunk-5UYMDSAE.js.map +0 -1
- package/dist/chunk-7TQO4ZF4.js +0 -60
- package/dist/chunk-7TQO4ZF4.js.map +0 -1
- package/dist/chunk-JG54UJ6V.js.map +0 -1
- package/dist/chunk-NI5L2R2N.js.map +0 -1
- package/dist/chunk-UDSRAHKP.js.map +0 -1
- package/dist/chunk-UZTJJD7Y.js.map +0 -1
- package/dist/server-CO5O7G4U.js +0 -9
- /package/dist/{client-BU4XA7CV.js.map → client-LRVVHTNG.js.map} +0 -0
- /package/dist/{server-CO5O7G4U.js.map → oom-watchdog-U7JERHA2.js.map} +0 -0
|
@@ -7,22 +7,26 @@ import {
|
|
|
7
7
|
defaultGit,
|
|
8
8
|
ensureDir,
|
|
9
9
|
readAgentVersion,
|
|
10
|
-
redactToken
|
|
11
|
-
|
|
10
|
+
redactToken,
|
|
11
|
+
reportBootMilestone
|
|
12
|
+
} from "./chunk-KCB7CSWJ.js";
|
|
12
13
|
import {
|
|
13
14
|
workbenchPort
|
|
14
15
|
} from "./chunk-4VUQ2NPF.js";
|
|
15
16
|
import {
|
|
16
17
|
startWorkbenchServer
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-3Z4YQNJV.js";
|
|
19
|
+
import "./chunk-XR6H326I.js";
|
|
19
20
|
import {
|
|
20
21
|
DEFAULT_WORKBENCH_PORT
|
|
21
22
|
} from "./chunk-JIGG755T.js";
|
|
23
|
+
import {
|
|
24
|
+
oomWatchdogOptionsFromEnv
|
|
25
|
+
} from "./chunk-ULS4QPRE.js";
|
|
22
26
|
|
|
23
27
|
// src/boot/index.ts
|
|
24
28
|
import { spawn } from "child_process";
|
|
25
|
-
import { statSync as
|
|
29
|
+
import { statSync as statSync4 } from "fs";
|
|
26
30
|
import { hostname as osHostname } from "os";
|
|
27
31
|
|
|
28
32
|
// src/boot/bundle.ts
|
|
@@ -262,95 +266,17 @@ async function pollBootstrapBundle(apiUrl, token, deps) {
|
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
// src/boot/
|
|
269
|
+
// src/boot/exec.ts
|
|
266
270
|
import { execFile } from "child_process";
|
|
267
271
|
import { promisify } from "util";
|
|
268
|
-
var AGENT_PACKAGE = "@rallycry/conveyor-agent";
|
|
269
|
-
var NPM_VIEW_TIMEOUT_MS = 3e3;
|
|
270
|
-
var NPM_INSTALL_TIMEOUT_MS = 12e4;
|
|
271
|
-
var NPM_CACHE_DIR = "/tmp/npm-cache";
|
|
272
|
-
var NPM_REGISTRY_ARG = "--@rallycry:registry=https://registry.npmjs.org/";
|
|
273
|
-
var SEMVER_RE = /^\d+\.\d+\.\d+([-+][A-Za-z0-9.-]+)?$/;
|
|
274
|
-
function isValidSemver(value) {
|
|
275
|
-
return SEMVER_RE.test(value);
|
|
276
|
-
}
|
|
277
|
-
function isStrictlyNewer(candidate, installed) {
|
|
278
|
-
const num = (v) => (v.split(/[-+]/)[0] ?? "").split(".").map((p) => Number(p));
|
|
279
|
-
const [a, b] = [num(candidate), num(installed)];
|
|
280
|
-
for (let i = 0; i < 3; i++) {
|
|
281
|
-
const av = a[i] ?? 0;
|
|
282
|
-
const bv = b[i] ?? 0;
|
|
283
|
-
if (av !== bv) return av > bv;
|
|
284
|
-
}
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
272
|
var execFileAsync = promisify(execFile);
|
|
288
273
|
function defaultExec(cmd, args, opts) {
|
|
289
274
|
return execFileAsync(cmd, args, { timeout: opts.timeoutMs });
|
|
290
275
|
}
|
|
291
|
-
async function runPreflightUpdate(deps) {
|
|
292
|
-
let latest = "";
|
|
293
|
-
try {
|
|
294
|
-
const { stdout } = await deps.exec(
|
|
295
|
-
"npm",
|
|
296
|
-
["view", "--cache", NPM_CACHE_DIR, NPM_REGISTRY_ARG, AGENT_PACKAGE, "version"],
|
|
297
|
-
{ timeoutMs: NPM_VIEW_TIMEOUT_MS }
|
|
298
|
-
);
|
|
299
|
-
latest = stdout.replace(/\s+/g, "");
|
|
300
|
-
} catch (err) {
|
|
301
|
-
deps.log.warn(
|
|
302
|
-
`[boot] WARN preflight npm view failed (${err instanceof Error ? err.message : err}); keeping baked version`
|
|
303
|
-
);
|
|
304
|
-
return "skipped";
|
|
305
|
-
}
|
|
306
|
-
if (!isValidSemver(latest)) {
|
|
307
|
-
deps.log.warn(
|
|
308
|
-
`[boot] WARN preflight got non-semver '${latest}' from npm; keeping baked version`
|
|
309
|
-
);
|
|
310
|
-
return "skipped";
|
|
311
|
-
}
|
|
312
|
-
let installed = "";
|
|
313
|
-
try {
|
|
314
|
-
installed = (await deps.installedVersion()).trim();
|
|
315
|
-
} catch (err) {
|
|
316
|
-
deps.log.warn(
|
|
317
|
-
`[boot] WARN preflight installed-version lookup failed (${err instanceof Error ? err.message : err}); keeping baked version`
|
|
318
|
-
);
|
|
319
|
-
return "skipped";
|
|
320
|
-
}
|
|
321
|
-
if (!isValidSemver(installed) || isStrictlyNewer(latest, installed)) {
|
|
322
|
-
deps.log.info(
|
|
323
|
-
`[boot] preflight updating ${AGENT_PACKAGE} ${installed || "unknown"} -> ${latest}`
|
|
324
|
-
);
|
|
325
|
-
try {
|
|
326
|
-
await deps.exec(
|
|
327
|
-
"sudo",
|
|
328
|
-
[
|
|
329
|
-
"npm",
|
|
330
|
-
"install",
|
|
331
|
-
"-g",
|
|
332
|
-
"--cache",
|
|
333
|
-
NPM_CACHE_DIR,
|
|
334
|
-
"--silent",
|
|
335
|
-
`${AGENT_PACKAGE}@${latest}`
|
|
336
|
-
],
|
|
337
|
-
{ timeoutMs: NPM_INSTALL_TIMEOUT_MS }
|
|
338
|
-
);
|
|
339
|
-
return "updated";
|
|
340
|
-
} catch (err) {
|
|
341
|
-
deps.log.warn(
|
|
342
|
-
`[boot] WARN preflight install failed (${err instanceof Error ? err.message : err}); keeping baked version`
|
|
343
|
-
);
|
|
344
|
-
return "skipped";
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
deps.log.info(`[boot] preflight agent ${installed} is current (registry ${latest})`);
|
|
348
|
-
return "current";
|
|
349
|
-
}
|
|
350
276
|
|
|
351
277
|
// src/boot/supervisor.ts
|
|
352
278
|
import { spawn as spawnChildProcess } from "child_process";
|
|
353
|
-
import { appendFileSync as appendFileSync2, writeFileSync } from "fs";
|
|
279
|
+
import { appendFileSync as appendFileSync2, statSync, writeFileSync } from "fs";
|
|
354
280
|
import { join } from "path";
|
|
355
281
|
|
|
356
282
|
// src/boot/crash-report.ts
|
|
@@ -377,6 +303,7 @@ async function reportAgentCrash(apiUrl, bootstrapToken, report, fetchFn = fetch)
|
|
|
377
303
|
// src/boot/supervisor.ts
|
|
378
304
|
var AGENT_CRASH_MAX = 3;
|
|
379
305
|
var AGENT_LOG_PATH = "/tmp/claudespace-agent.log";
|
|
306
|
+
var AGENT_LOG_MAX_BYTES = 16 * 1024 * 1024;
|
|
380
307
|
var LAUNCH_MARKER_NAME = "conveyor-agent-launching";
|
|
381
308
|
var RESTARTING_MARKER_NAME = "conveyor-agent-restarting";
|
|
382
309
|
var LAUNCH_MARKER = `/tmp/${LAUNCH_MARKER_NAME}`;
|
|
@@ -393,14 +320,31 @@ function touchMarker(path, log) {
|
|
|
393
320
|
);
|
|
394
321
|
}
|
|
395
322
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
|
|
323
|
+
function createLogAppender(path, maxBytes) {
|
|
324
|
+
let size = 0;
|
|
325
|
+
try {
|
|
326
|
+
size = statSync(path).size;
|
|
327
|
+
} catch {
|
|
328
|
+
size = 0;
|
|
329
|
+
}
|
|
330
|
+
return (chunk) => {
|
|
399
331
|
try {
|
|
400
|
-
|
|
332
|
+
if (size + chunk.length > maxBytes) {
|
|
333
|
+
writeFileSync(path, chunk);
|
|
334
|
+
size = chunk.length;
|
|
335
|
+
} else {
|
|
336
|
+
appendFileSync2(path, chunk);
|
|
337
|
+
size += chunk.length;
|
|
338
|
+
}
|
|
401
339
|
} catch {
|
|
402
340
|
}
|
|
403
341
|
};
|
|
342
|
+
}
|
|
343
|
+
function teeChildOutput(child, appendLog) {
|
|
344
|
+
const onData = (chunk) => {
|
|
345
|
+
process.stdout.write(chunk);
|
|
346
|
+
appendLog(chunk);
|
|
347
|
+
};
|
|
404
348
|
child.stdout?.on("data", onData);
|
|
405
349
|
child.stderr?.on("data", onData);
|
|
406
350
|
}
|
|
@@ -474,12 +418,13 @@ async function superviseRunner(opts) {
|
|
|
474
418
|
const launchMarkerPath = join(markerDir, LAUNCH_MARKER_NAME);
|
|
475
419
|
const restartingMarkerPath = join(markerDir, RESTARTING_MARKER_NAME);
|
|
476
420
|
touchMarker(launchMarkerPath, opts.log);
|
|
421
|
+
const appendLog = createLogAppender(AGENT_LOG_PATH, AGENT_LOG_MAX_BYTES);
|
|
477
422
|
let attempt = 0;
|
|
478
423
|
while (true) {
|
|
479
424
|
attempt++;
|
|
480
425
|
opts.log.info("[boot] Launching agent...");
|
|
481
426
|
const child = spawnAgent(spawnFn, opts);
|
|
482
|
-
teeChildOutput(child);
|
|
427
|
+
teeChildOutput(child, appendLog);
|
|
483
428
|
const exitPromise = waitForExit(child);
|
|
484
429
|
const winner = await Promise.race([
|
|
485
430
|
exitPromise.then((code) => ({ type: "exit", code })),
|
|
@@ -516,11 +461,13 @@ async function superviseRunner(opts) {
|
|
|
516
461
|
import {
|
|
517
462
|
chmodSync,
|
|
518
463
|
copyFileSync,
|
|
464
|
+
existsSync,
|
|
519
465
|
lstatSync,
|
|
520
466
|
mkdirSync,
|
|
521
467
|
readFileSync,
|
|
468
|
+
readdirSync,
|
|
522
469
|
rmSync,
|
|
523
|
-
statSync,
|
|
470
|
+
statSync as statSync2,
|
|
524
471
|
symlinkSync,
|
|
525
472
|
writeFileSync as writeFileSync2
|
|
526
473
|
} from "fs";
|
|
@@ -574,7 +521,7 @@ function seedClaudeJson(targetPath) {
|
|
|
574
521
|
}
|
|
575
522
|
function isDir(path) {
|
|
576
523
|
try {
|
|
577
|
-
return
|
|
524
|
+
return statSync2(path).isDirectory();
|
|
578
525
|
} catch {
|
|
579
526
|
return false;
|
|
580
527
|
}
|
|
@@ -608,6 +555,29 @@ function wireCredentials(opts) {
|
|
|
608
555
|
safely(log, "repoint credentials symlink", () => relink(podLocal, shared));
|
|
609
556
|
safely(log, "chmod pod-local credentials", () => chmodSync(podLocal, 384));
|
|
610
557
|
}
|
|
558
|
+
var AGENT_MEMORY_PROJECT_DIR = "-workspaces-repo";
|
|
559
|
+
function migrateMemories(from, to, log) {
|
|
560
|
+
let entries;
|
|
561
|
+
try {
|
|
562
|
+
entries = readdirSync(from);
|
|
563
|
+
} catch {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
for (const entry of entries) {
|
|
567
|
+
const target = join2(to, entry);
|
|
568
|
+
if (existsSync(target)) continue;
|
|
569
|
+
safely(log, `migrate memory ${entry}`, () => copyFileSync(join2(from, entry), target));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function wireSharedMemory(root, userHomeMount, projectId, log) {
|
|
573
|
+
const shared = join2(userHomeMount, "users", "_shared", "memory", projectId);
|
|
574
|
+
const link = join2(root, ".claude", "projects", AGENT_MEMORY_PROJECT_DIR, "memory");
|
|
575
|
+
safely(log, "mkdir shared memory dir", () => mkdirSync(shared, { recursive: true }));
|
|
576
|
+
safely(log, "migrate per-user memories", () => {
|
|
577
|
+
if (existsSync(link) && !lstatSync(link).isSymbolicLink()) migrateMemories(link, shared, log);
|
|
578
|
+
});
|
|
579
|
+
safely(log, "wire shared memory symlink", () => relink(shared, link));
|
|
580
|
+
}
|
|
611
581
|
function wireMountedUserHome(opts) {
|
|
612
582
|
const { home, userHomeMount, userId, projectId, sharedDir, log } = opts;
|
|
613
583
|
const root = join2(userHomeMount, "users", userId, projectId);
|
|
@@ -625,6 +595,7 @@ function wireMountedUserHome(opts) {
|
|
|
625
595
|
rmSync(join2(home, ".claude.json"), { recursive: true, force: true });
|
|
626
596
|
seedClaudeJson(join2(home, ".claude.json"));
|
|
627
597
|
});
|
|
598
|
+
wireSharedMemory(root, userHomeMount, projectId, log);
|
|
628
599
|
wireCredentials({ home, root, sharedDir, log });
|
|
629
600
|
safely(log, "mkdir opencode root", () => {
|
|
630
601
|
mkdirSync(join2(root, ".local", "share", "opencode"), { recursive: true });
|
|
@@ -658,6 +629,11 @@ function wireNoMountUserHome(opts) {
|
|
|
658
629
|
}
|
|
659
630
|
function wireUserHome(opts) {
|
|
660
631
|
const { home, userHomeMount, userId, projectId, sharedDir, log } = opts;
|
|
632
|
+
safely(
|
|
633
|
+
log,
|
|
634
|
+
"mkdir agent scratch dir",
|
|
635
|
+
() => mkdirSync(opts.scratchDir ?? "/tmp/claude-cmd", { recursive: true })
|
|
636
|
+
);
|
|
661
637
|
if (userId && projectId && isDir(userHomeMount)) {
|
|
662
638
|
wireMountedUserHome({ home, userHomeMount, userId, projectId, sharedDir, log });
|
|
663
639
|
} else {
|
|
@@ -695,15 +671,27 @@ async function reclaimHomeOwnership(home, exec, log) {
|
|
|
695
671
|
}
|
|
696
672
|
|
|
697
673
|
// src/boot/sshd.ts
|
|
698
|
-
import { chmodSync as chmodSync2, closeSync, existsSync, mkdirSync as mkdirSync2, openSync } from "fs";
|
|
674
|
+
import { chmodSync as chmodSync2, closeSync, copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, openSync } from "fs";
|
|
699
675
|
import { join as join3 } from "path";
|
|
676
|
+
function persistedAuthorizedKeys() {
|
|
677
|
+
return join3(
|
|
678
|
+
process.env.CONVEYOR_SHARED_DIR || "/var/run/conveyor-workbench",
|
|
679
|
+
"ssh",
|
|
680
|
+
"authorized_keys"
|
|
681
|
+
);
|
|
682
|
+
}
|
|
700
683
|
async function startWorkspaceSshd(exec, log, port = process.env.CONVEYOR_WORKSPACE_SSH_PORT || "2222", home = "/home/conveyor") {
|
|
701
684
|
try {
|
|
702
685
|
const sshDir = join3(home, ".ssh");
|
|
703
686
|
mkdirSync2(sshDir, { recursive: true });
|
|
704
687
|
chmodSync2(sshDir, 448);
|
|
705
688
|
const authorizedKeys = join3(sshDir, "authorized_keys");
|
|
706
|
-
|
|
689
|
+
const persisted = persistedAuthorizedKeys();
|
|
690
|
+
if (existsSync2(persisted)) {
|
|
691
|
+
copyFileSync2(persisted, authorizedKeys);
|
|
692
|
+
} else if (!existsSync2(authorizedKeys)) {
|
|
693
|
+
closeSync(openSync(authorizedKeys, "a"));
|
|
694
|
+
}
|
|
707
695
|
chmodSync2(authorizedKeys, 384);
|
|
708
696
|
} catch (err) {
|
|
709
697
|
log.warn(
|
|
@@ -711,7 +699,7 @@ async function startWorkspaceSshd(exec, log, port = process.env.CONVEYOR_WORKSPA
|
|
|
711
699
|
);
|
|
712
700
|
return;
|
|
713
701
|
}
|
|
714
|
-
if (!
|
|
702
|
+
if (!existsSync2("/usr/sbin/sshd")) {
|
|
715
703
|
log.warn("[boot] WARN: workspace SSHD unavailable");
|
|
716
704
|
return;
|
|
717
705
|
}
|
|
@@ -728,12 +716,15 @@ async function startWorkspaceSshd(exec, log, port = process.env.CONVEYOR_WORKSPA
|
|
|
728
716
|
|
|
729
717
|
// src/boot/workspace-extras.ts
|
|
730
718
|
import {
|
|
731
|
-
|
|
719
|
+
appendFileSync as appendFileSync3,
|
|
720
|
+
existsSync as existsSync3,
|
|
732
721
|
lstatSync as lstatSync2,
|
|
733
722
|
mkdirSync as mkdirSync3,
|
|
734
|
-
readdirSync,
|
|
723
|
+
readdirSync as readdirSync2,
|
|
724
|
+
readFileSync as readFileSync2,
|
|
725
|
+
readlinkSync,
|
|
735
726
|
rmSync as rmSync2,
|
|
736
|
-
statSync as
|
|
727
|
+
statSync as statSync3,
|
|
737
728
|
symlinkSync as symlinkSync2
|
|
738
729
|
} from "fs";
|
|
739
730
|
import { join as join4 } from "path";
|
|
@@ -742,7 +733,7 @@ function errText(err) {
|
|
|
742
733
|
}
|
|
743
734
|
function isDir2(path) {
|
|
744
735
|
try {
|
|
745
|
-
return
|
|
736
|
+
return statSync3(path).isDirectory();
|
|
746
737
|
} catch {
|
|
747
738
|
return false;
|
|
748
739
|
}
|
|
@@ -768,7 +759,7 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
|
|
|
768
759
|
const sourceDir = graphifyEnv.CONVEYOR_GRAPHIFY_DIR;
|
|
769
760
|
const graphFile = graphifyEnv.CONVEYOR_GRAPHIFY_GRAPH;
|
|
770
761
|
if (!slug || !sourceDir || !graphFile) return;
|
|
771
|
-
if (!
|
|
762
|
+
if (!existsSync3(graphFile)) {
|
|
772
763
|
log.info(`[boot] Graphify bundle not found for '${slug}' at ${sourceDir}`);
|
|
773
764
|
return;
|
|
774
765
|
}
|
|
@@ -782,8 +773,8 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
|
|
|
782
773
|
for (const rel of GRAPHIFY_BUNDLE_FILES) {
|
|
783
774
|
const source = join4(sourceDir, rel);
|
|
784
775
|
const target = join4(targetDir, rel);
|
|
785
|
-
if (!
|
|
786
|
-
if (
|
|
776
|
+
if (!existsSync3(source)) continue;
|
|
777
|
+
if (existsSync3(target) || isSymlink(target)) continue;
|
|
787
778
|
try {
|
|
788
779
|
symlinkSync2(source, target);
|
|
789
780
|
} catch {
|
|
@@ -793,7 +784,7 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
|
|
|
793
784
|
}
|
|
794
785
|
async function ensureGrimoireSubmodule(workspace, githubToken, git, log) {
|
|
795
786
|
try {
|
|
796
|
-
if (!
|
|
787
|
+
if (!existsSync3(join4(workspace, ".gitmodules"))) return;
|
|
797
788
|
let paths = "";
|
|
798
789
|
try {
|
|
799
790
|
({ stdout: paths } = await git(
|
|
@@ -831,6 +822,30 @@ async function ensureGrimoireSubmodule(workspace, githubToken, git, log) {
|
|
|
831
822
|
log.warn(`[boot] WARN: grimoire submodule check failed: ${errText(err)}`);
|
|
832
823
|
}
|
|
833
824
|
}
|
|
825
|
+
function excludeLinkedSkills(workspace, names, log) {
|
|
826
|
+
if (names.length === 0) return;
|
|
827
|
+
const infoDir = join4(workspace, ".git", "info");
|
|
828
|
+
if (!isDir2(join4(workspace, ".git"))) return;
|
|
829
|
+
try {
|
|
830
|
+
mkdirSync3(infoDir, { recursive: true });
|
|
831
|
+
const excludePath = join4(infoDir, "exclude");
|
|
832
|
+
const existing = existsSync3(excludePath) ? readFileSync2(excludePath, "utf8") : "";
|
|
833
|
+
const missing = names.map((name) => `.claude/skills/${name}`).filter((line) => !existing.split("\n").includes(line));
|
|
834
|
+
if (missing.length === 0) return;
|
|
835
|
+
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
836
|
+
appendFileSync3(excludePath, `${prefix}${missing.join("\n")}
|
|
837
|
+
`);
|
|
838
|
+
} catch (err) {
|
|
839
|
+
log.warn(`[boot] WARN: unable to exclude grimoire skill links: ${errText(err)}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function symlinkTargetsGrimoire(path) {
|
|
843
|
+
try {
|
|
844
|
+
return readlinkSync(path).includes(join4("grimoire", "skills"));
|
|
845
|
+
} catch {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
834
849
|
function linkGrimoireSkills(workspace, log) {
|
|
835
850
|
const sourceDir = join4(workspace, ".claude", "grimoire", "skills");
|
|
836
851
|
const targetDir = join4(workspace, ".claude", "skills");
|
|
@@ -841,28 +856,42 @@ function linkGrimoireSkills(workspace, log) {
|
|
|
841
856
|
log.warn(`[boot] WARN: unable to create ${targetDir}; skipping grimoire skill links`);
|
|
842
857
|
return;
|
|
843
858
|
}
|
|
844
|
-
|
|
859
|
+
const linkedNames = [];
|
|
845
860
|
let names = [];
|
|
846
861
|
try {
|
|
847
|
-
names =
|
|
862
|
+
names = readdirSync2(sourceDir);
|
|
848
863
|
} catch {
|
|
849
864
|
return;
|
|
850
865
|
}
|
|
851
866
|
for (const name of names) {
|
|
852
867
|
if (!isDir2(join4(sourceDir, name))) continue;
|
|
853
868
|
const target = join4(targetDir, name);
|
|
854
|
-
if (
|
|
869
|
+
if (existsSync3(target) && !isSymlink(target)) {
|
|
855
870
|
log.warn(`[boot] WARN: ${target} exists and is not a symlink; skipping`);
|
|
856
871
|
continue;
|
|
857
872
|
}
|
|
873
|
+
if (isSymlink(target) && !symlinkTargetsGrimoire(target)) continue;
|
|
858
874
|
try {
|
|
859
875
|
rmSync2(target, { force: true });
|
|
860
876
|
symlinkSync2(join4("..", "grimoire", "skills", name), target);
|
|
861
|
-
|
|
877
|
+
linkedNames.push(name);
|
|
862
878
|
} catch {
|
|
863
879
|
}
|
|
864
880
|
}
|
|
865
|
-
|
|
881
|
+
excludeLinkedSkills(workspace, linkedNames, log);
|
|
882
|
+
log.info(`[boot] Linked ${linkedNames.length} grimoire skills into ${targetDir}`);
|
|
883
|
+
}
|
|
884
|
+
async function neutralizeGrimoirePointer(workspace, git, log) {
|
|
885
|
+
if (!existsSync3(join4(workspace, ".claude", "grimoire"))) return;
|
|
886
|
+
try {
|
|
887
|
+
await git(["update-index", "--assume-unchanged", ".claude/grimoire"], {
|
|
888
|
+
cwd: workspace,
|
|
889
|
+
timeoutMs: 1e4
|
|
890
|
+
});
|
|
891
|
+
log.info("[boot] Marked .claude/grimoire assume-unchanged (submodule pointer drift is noise)");
|
|
892
|
+
} catch (err) {
|
|
893
|
+
log.warn(`[boot] WARN: grimoire assume-unchanged failed: ${errText(err)}`);
|
|
894
|
+
}
|
|
866
895
|
}
|
|
867
896
|
function parseReferenceRepos(refsJson) {
|
|
868
897
|
let parsed;
|
|
@@ -893,6 +922,7 @@ async function runPreReadyBinds(workspace, bundle, git, log) {
|
|
|
893
922
|
bindGraphifyBundle(workspace, graphifyEnv, log);
|
|
894
923
|
await ensureGrimoireSubmodule(workspace, bundle.githubToken, git, log);
|
|
895
924
|
linkGrimoireSkills(workspace, log);
|
|
925
|
+
await neutralizeGrimoirePointer(workspace, git, log);
|
|
896
926
|
}
|
|
897
927
|
async function cloneReferenceRepos(refsJson, git, log, referencesDir = "/workspaces/references") {
|
|
898
928
|
if (!refsJson) return;
|
|
@@ -905,7 +935,7 @@ async function cloneReferenceRepos(refsJson, git, log, referencesDir = "/workspa
|
|
|
905
935
|
}
|
|
906
936
|
for (const ref of refs) {
|
|
907
937
|
const dest = join4(referencesDir, ref.slug);
|
|
908
|
-
if (
|
|
938
|
+
if (existsSync3(join4(dest, ".git"))) continue;
|
|
909
939
|
try {
|
|
910
940
|
await git(
|
|
911
941
|
[
|
|
@@ -1014,16 +1044,22 @@ async function runWorkbenchBoot(ctx, overrides = {}) {
|
|
|
1014
1044
|
const job = new GitPrepJob(deps.gitPrepDeps(ctx, bundle), {
|
|
1015
1045
|
// Order pinned by the old entrypoint-grimoire test: binds BEFORE the
|
|
1016
1046
|
// ready flip (Claude must not spawn before skills exist), references after.
|
|
1017
|
-
onReady: () =>
|
|
1047
|
+
onReady: () => {
|
|
1048
|
+
const binds = deps.bindsBeforeReady(ctx, bundle);
|
|
1049
|
+
void reportBootMilestone({ key: "repo_synced" });
|
|
1050
|
+
return binds;
|
|
1051
|
+
},
|
|
1018
1052
|
afterReady: () => deps.cloneReferences(ctx, bundle)
|
|
1019
1053
|
});
|
|
1020
1054
|
await deps.startDaemon({
|
|
1021
1055
|
port: workbenchPort() ?? DEFAULT_WORKBENCH_PORT,
|
|
1022
1056
|
token: ctx.bootstrapToken,
|
|
1023
1057
|
version: readAgentVersion() ?? "unknown",
|
|
1024
|
-
getGitStatus: () => toGitStatusFrame(job.status)
|
|
1058
|
+
getGitStatus: () => toGitStatusFrame(job.status),
|
|
1059
|
+
oomWatchdog: oomWatchdogOptionsFromEnv(ctx.log)
|
|
1025
1060
|
});
|
|
1026
1061
|
ctx.log.info("[boot] step=startDaemon listen socket up (startupProbe gate open)");
|
|
1062
|
+
void reportBootMilestone({ key: "workbench_ready" });
|
|
1027
1063
|
try {
|
|
1028
1064
|
deps.ensureWorkspaceDir(ctx);
|
|
1029
1065
|
} catch (err) {
|
|
@@ -1051,13 +1087,9 @@ function defaultBootDeps() {
|
|
|
1051
1087
|
}),
|
|
1052
1088
|
spawnFn: spawn,
|
|
1053
1089
|
exec: defaultExec,
|
|
1054
|
-
installedVersion: () => {
|
|
1055
|
-
const version = readAgentVersion();
|
|
1056
|
-
return version ? Promise.resolve(version) : Promise.reject(new Error("could not read installed agent version"));
|
|
1057
|
-
},
|
|
1058
1090
|
dirExists: (path) => {
|
|
1059
1091
|
try {
|
|
1060
|
-
return
|
|
1092
|
+
return statSync4(path).isDirectory();
|
|
1061
1093
|
} catch {
|
|
1062
1094
|
return false;
|
|
1063
1095
|
}
|
|
@@ -1079,11 +1111,13 @@ function whenAborted2(signal) {
|
|
|
1079
1111
|
});
|
|
1080
1112
|
}
|
|
1081
1113
|
function agentBootSteps(deps = defaultBootDeps(), signal = new AbortController().signal) {
|
|
1114
|
+
let reclaim;
|
|
1082
1115
|
return [
|
|
1083
1116
|
{
|
|
1084
1117
|
name: "pollBootstrap",
|
|
1085
1118
|
critical: true,
|
|
1086
1119
|
run: async (ctx) => {
|
|
1120
|
+
reclaim = reclaimHomeOwnership(HOME_DIR2, deps.exec, ctx.log).catch(() => void 0);
|
|
1087
1121
|
const bundle = await pollBootstrapBundle(ctx.apiUrl, ctx.bootstrapToken, {
|
|
1088
1122
|
fetchFn: deps.fetchFn,
|
|
1089
1123
|
sleepFn: deps.sleepFn,
|
|
@@ -1122,7 +1156,7 @@ function agentBootSteps(deps = defaultBootDeps(), signal = new AbortController()
|
|
|
1122
1156
|
critical: false,
|
|
1123
1157
|
run: async (ctx) => {
|
|
1124
1158
|
if (!ctx.bundle) return;
|
|
1125
|
-
await reclaimHomeOwnership(HOME_DIR2, deps.exec, ctx.log);
|
|
1159
|
+
await (reclaim ?? reclaimHomeOwnership(HOME_DIR2, deps.exec, ctx.log));
|
|
1126
1160
|
}
|
|
1127
1161
|
},
|
|
1128
1162
|
{
|
|
@@ -1226,11 +1260,7 @@ async function runWorkbenchRole(ctx, deps, signal) {
|
|
|
1226
1260
|
async function runBoot(argv, deps = defaultBootDeps()) {
|
|
1227
1261
|
const { log } = deps;
|
|
1228
1262
|
if (argv.includes("--preflight-update")) {
|
|
1229
|
-
|
|
1230
|
-
exec: deps.exec,
|
|
1231
|
-
installedVersion: deps.installedVersion,
|
|
1232
|
-
log
|
|
1233
|
-
});
|
|
1263
|
+
log.info("[boot] --preflight-update is a no-op (baked agent, no runtime self-update)");
|
|
1234
1264
|
return 0;
|
|
1235
1265
|
}
|
|
1236
1266
|
const role = process.env.CONVEYOR_CONTAINER_ROLE;
|
|
@@ -1255,7 +1285,9 @@ async function runBoot(argv, deps = defaultBootDeps()) {
|
|
|
1255
1285
|
hostname: deps.hostname,
|
|
1256
1286
|
log
|
|
1257
1287
|
};
|
|
1258
|
-
|
|
1288
|
+
const code = await (role === "workbench" ? runWorkbenchRole(ctx, deps, controller.signal) : runAgentRole(ctx, deps, controller.signal));
|
|
1289
|
+
controller.abort();
|
|
1290
|
+
return code;
|
|
1259
1291
|
}
|
|
1260
1292
|
export {
|
|
1261
1293
|
TOMBSTONE_MESSAGE,
|
|
@@ -1265,4 +1297,4 @@ export {
|
|
|
1265
1297
|
runBoot,
|
|
1266
1298
|
workbenchBootSteps
|
|
1267
1299
|
};
|
|
1268
|
-
//# sourceMappingURL=boot-
|
|
1300
|
+
//# sourceMappingURL=boot-I353EAK6.js.map
|