@tablation/crew 0.50.1 → 0.50.2
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/cli.js +122 -93
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/doctor-repo.ts
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
function doctorBaseReport(r, base, health) {
|
|
6
|
+
const pad = " ";
|
|
7
|
+
if (!existsSync(r.dir)) {
|
|
8
|
+
return {
|
|
9
|
+
text: `${pad}- ${r.dir} is missing on disk \u2014 no .crew.yaml read, settings above are defaults
|
|
10
|
+
`,
|
|
11
|
+
failed: true,
|
|
12
|
+
skipRest: true
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const bh = health(r);
|
|
17
|
+
if (bh.action === "not-applicable") return { text: "", failed: false, skipRest: false };
|
|
18
|
+
return {
|
|
19
|
+
text: `${pad}base ${base}: ${bh.unsafe ? "ERROR \u2014 " : ""}${bh.detail}
|
|
20
|
+
`,
|
|
21
|
+
failed: bh.unsafe,
|
|
22
|
+
skipRest: false
|
|
23
|
+
};
|
|
24
|
+
} catch (e) {
|
|
25
|
+
return {
|
|
26
|
+
text: `${pad}base ${base}: could not check \u2014 ${e instanceof Error ? e.message : String(e)}
|
|
27
|
+
`,
|
|
28
|
+
failed: true,
|
|
29
|
+
skipRest: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
// src/cli.ts
|
|
4
35
|
import { resolve as resolve4, dirname as dirname12, join as join25, basename as basename9 } from "path";
|
|
5
36
|
import { fileURLToPath } from "url";
|
|
@@ -62,7 +93,7 @@ function pickWorktreeRoute(ticketNumber, candidates) {
|
|
|
62
93
|
}
|
|
63
94
|
|
|
64
95
|
// src/config.ts
|
|
65
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
96
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
66
97
|
import { resolve, dirname as dirname3, isAbsolute, join as join7, basename as basename3 } from "path";
|
|
67
98
|
import { homedir as homedir5, tmpdir } from "os";
|
|
68
99
|
import { parse as parse2 } from "yaml";
|
|
@@ -71,7 +102,7 @@ import { parse as parse2 } from "yaml";
|
|
|
71
102
|
import {
|
|
72
103
|
appendFileSync,
|
|
73
104
|
mkdirSync,
|
|
74
|
-
existsSync,
|
|
105
|
+
existsSync as existsSync2,
|
|
75
106
|
statSync,
|
|
76
107
|
renameSync,
|
|
77
108
|
openSync,
|
|
@@ -90,7 +121,7 @@ var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
|
|
|
90
121
|
var ROTATE_LOCK_STALE_MS = 6e4;
|
|
91
122
|
function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
|
|
92
123
|
try {
|
|
93
|
-
if (!
|
|
124
|
+
if (!existsSync2(file) || statSync(file).size <= maxBytes) return void 0;
|
|
94
125
|
} catch {
|
|
95
126
|
return void 0;
|
|
96
127
|
}
|
|
@@ -111,7 +142,7 @@ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
|
|
|
111
142
|
if (statSync(file).size <= maxBytes) return void 0;
|
|
112
143
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
113
144
|
let dest = join(dirname(file), `events.${stamp}.jsonl`);
|
|
114
|
-
for (let n = 1;
|
|
145
|
+
for (let n = 1; existsSync2(dest); n++) dest = join(dirname(file), `events.${stamp}-${n}.jsonl`);
|
|
115
146
|
renameSync(file, dest);
|
|
116
147
|
return dest;
|
|
117
148
|
} finally {
|
|
@@ -213,7 +244,7 @@ var Emitter = class _Emitter {
|
|
|
213
244
|
var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
|
|
214
245
|
|
|
215
246
|
// src/pair-install.ts
|
|
216
|
-
import { existsSync as
|
|
247
|
+
import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
217
248
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
218
249
|
import { homedir as homedir4 } from "os";
|
|
219
250
|
import { join as join5 } from "path";
|
|
@@ -342,7 +373,7 @@ function removeClaudeMcp(o) {
|
|
|
342
373
|
|
|
343
374
|
// src/launcher.ts
|
|
344
375
|
import { basename, join as join3 } from "path";
|
|
345
|
-
import { existsSync as
|
|
376
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
346
377
|
import { homedir as homedir2 } from "os";
|
|
347
378
|
function isOriLauncher(spec) {
|
|
348
379
|
return !!spec.launcher && basename(spec.launcher) === "ori";
|
|
@@ -405,7 +436,7 @@ function omlxModelPresent(model, dirs) {
|
|
|
405
436
|
}
|
|
406
437
|
function readOmlxSettings(home = homedir2()) {
|
|
407
438
|
const p = join3(home, ".omlx", "settings.json");
|
|
408
|
-
return
|
|
439
|
+
return existsSync3(p) ? parseOmlxSettings(readFileSync3(p, "utf8")) : void 0;
|
|
409
440
|
}
|
|
410
441
|
|
|
411
442
|
// src/capability.ts
|
|
@@ -542,7 +573,7 @@ import { join as join4 } from "path";
|
|
|
542
573
|
import { rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
543
574
|
|
|
544
575
|
// src/pair-files.ts
|
|
545
|
-
import { existsSync as
|
|
576
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync, rmSync as rmSync2 } from "fs";
|
|
546
577
|
import { dirname as dirname2 } from "path";
|
|
547
578
|
var PAIR_BLOCK_START = "<!-- crew:pair start -->";
|
|
548
579
|
var PAIR_BLOCK_END = "<!-- crew:pair end -->";
|
|
@@ -583,7 +614,7 @@ function removePairBlock(text) {
|
|
|
583
614
|
return text.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
|
|
584
615
|
}
|
|
585
616
|
function readIfExists(path) {
|
|
586
|
-
return
|
|
617
|
+
return existsSync4(path) ? readFileSync4(path, "utf8") : void 0;
|
|
587
618
|
}
|
|
588
619
|
function writeFileMkdir(path, text) {
|
|
589
620
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
@@ -980,7 +1011,7 @@ function vscodeUserDirs(o) {
|
|
|
980
1011
|
const platform2 = o.platform ?? process.platform;
|
|
981
1012
|
const root = platform2 === "darwin" ? join5(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join5(home, "AppData", "Roaming") : join5(home, ".config");
|
|
982
1013
|
const all = VSCODE_FLAVOURS.map((f) => join5(root, f, "User"));
|
|
983
|
-
const present = all.filter((d) =>
|
|
1014
|
+
const present = all.filter((d) => existsSync5(d));
|
|
984
1015
|
return present.length > 0 ? present : [all[0]];
|
|
985
1016
|
}
|
|
986
1017
|
var COPILOT_FRONTMATTER = '---\napplyTo: "**"\n---\n\n';
|
|
@@ -1372,7 +1403,7 @@ function explain(ship, required) {
|
|
|
1372
1403
|
}
|
|
1373
1404
|
|
|
1374
1405
|
// src/repo-config.ts
|
|
1375
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
1406
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
1376
1407
|
import { join as join6, basename as basename2 } from "path";
|
|
1377
1408
|
import { parse } from "yaml";
|
|
1378
1409
|
var SPEC_VERSION = 1;
|
|
@@ -1456,7 +1487,7 @@ var PROVIDERS = ["github", "buildkite", "other", "none"];
|
|
|
1456
1487
|
function findRepoConfig(dir) {
|
|
1457
1488
|
for (const name of REPO_CONFIG_FILENAMES) {
|
|
1458
1489
|
const p = join6(dir, name);
|
|
1459
|
-
if (
|
|
1490
|
+
if (existsSync6(p)) return p;
|
|
1460
1491
|
}
|
|
1461
1492
|
return null;
|
|
1462
1493
|
}
|
|
@@ -2181,7 +2212,7 @@ function configSearchPath(crewHome) {
|
|
|
2181
2212
|
}
|
|
2182
2213
|
function findConfigFile(crewHome) {
|
|
2183
2214
|
const candidates = configSearchPath(crewHome);
|
|
2184
|
-
return candidates.find((c) =>
|
|
2215
|
+
return candidates.find((c) => existsSync7(c)) ?? candidates[candidates.length - 1];
|
|
2185
2216
|
}
|
|
2186
2217
|
var defaultConfigPath = () => join7(process.env.XDG_CONFIG_HOME ?? join7(homedir5(), ".config"), "crew", "crew.yaml");
|
|
2187
2218
|
function routeSlug(route) {
|
|
@@ -2204,7 +2235,7 @@ function splitRoute(route) {
|
|
|
2204
2235
|
}
|
|
2205
2236
|
function readResolvedFile(stateDir, route) {
|
|
2206
2237
|
const path = resolvedPathFor(stateDir, route);
|
|
2207
|
-
if (!
|
|
2238
|
+
if (!existsSync7(path)) return void 0;
|
|
2208
2239
|
try {
|
|
2209
2240
|
return JSON.parse(readFileSync6(path, "utf8"));
|
|
2210
2241
|
} catch (e) {
|
|
@@ -2263,7 +2294,7 @@ function parseResolved(raw, m, where) {
|
|
|
2263
2294
|
}
|
|
2264
2295
|
function loadConfig(crewHome, configFile, opts = {}) {
|
|
2265
2296
|
const file = configFile ?? findConfigFile(crewHome);
|
|
2266
|
-
if (opts.text === void 0 && !
|
|
2297
|
+
if (opts.text === void 0 && !existsSync7(file)) {
|
|
2267
2298
|
throw new ConfigError(
|
|
2268
2299
|
`no config found. Looked in:
|
|
2269
2300
|
` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
|
|
@@ -2573,7 +2604,7 @@ function repoIdForName(r, name) {
|
|
|
2573
2604
|
}
|
|
2574
2605
|
function resolveApiKey(r) {
|
|
2575
2606
|
if (r.apiKey) return r.apiKey;
|
|
2576
|
-
if (r.apiKeyFile && r.apiKeyVar &&
|
|
2607
|
+
if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
|
|
2577
2608
|
for (const line of readFileSync6(r.apiKeyFile, "utf8").split("\n")) {
|
|
2578
2609
|
const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
|
|
2579
2610
|
if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
|
|
@@ -2793,7 +2824,7 @@ function rosterMarkdown(roster, meId) {
|
|
|
2793
2824
|
}
|
|
2794
2825
|
|
|
2795
2826
|
// src/state.ts
|
|
2796
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as
|
|
2827
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3, rmSync as rmSync5, statSync as statSync2 } from "fs";
|
|
2797
2828
|
import { join as join8 } from "path";
|
|
2798
2829
|
var EPOCH = "1970-01-01T00:00:00.000Z";
|
|
2799
2830
|
var State = class _State {
|
|
@@ -2839,10 +2870,10 @@ var State = class _State {
|
|
|
2839
2870
|
}
|
|
2840
2871
|
/** Whole-crew pause, and the per-role sentinels one level down. */
|
|
2841
2872
|
isPaused() {
|
|
2842
|
-
return
|
|
2873
|
+
return existsSync8(this.path(".crew-paused"));
|
|
2843
2874
|
}
|
|
2844
2875
|
isRolePaused(role) {
|
|
2845
|
-
return
|
|
2876
|
+
return existsSync8(this.path(`.role-paused-${role}`));
|
|
2846
2877
|
}
|
|
2847
2878
|
pausedRoles(roles) {
|
|
2848
2879
|
return new Set(roles.filter((r) => this.isRolePaused(r)));
|
|
@@ -2859,7 +2890,7 @@ var State = class _State {
|
|
|
2859
2890
|
* while agents keep working.
|
|
2860
2891
|
*/
|
|
2861
2892
|
isReleasePaused() {
|
|
2862
|
-
return
|
|
2893
|
+
return existsSync8(this.path(".release-paused"));
|
|
2863
2894
|
}
|
|
2864
2895
|
setReleasePaused(paused) {
|
|
2865
2896
|
if (paused) writeFileSync4(this.path(".release-paused"), "");
|
|
@@ -3507,7 +3538,7 @@ function describeRelease(o, route) {
|
|
|
3507
3538
|
// src/install.ts
|
|
3508
3539
|
import { dirname as dirname4, join as join9, extname } from "path";
|
|
3509
3540
|
import { homedir as homedir6 } from "os";
|
|
3510
|
-
import { accessSync, constants, existsSync as
|
|
3541
|
+
import { accessSync, constants, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync5, unlinkSync, realpathSync } from "fs";
|
|
3511
3542
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
3512
3543
|
import { createHash } from "crypto";
|
|
3513
3544
|
|
|
@@ -3882,7 +3913,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
|
|
|
3882
3913
|
}
|
|
3883
3914
|
}
|
|
3884
3915
|
for (const path of plan.unitPaths) {
|
|
3885
|
-
if (!
|
|
3916
|
+
if (!existsSync9(path)) continue;
|
|
3886
3917
|
if (dryRun) {
|
|
3887
3918
|
log.emit(`would remove ${path}`);
|
|
3888
3919
|
continue;
|
|
@@ -3918,7 +3949,7 @@ function loadedCrewLabels() {
|
|
|
3918
3949
|
return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
|
|
3919
3950
|
}
|
|
3920
3951
|
function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
|
|
3921
|
-
const files =
|
|
3952
|
+
const files = existsSync9(dir) ? readdirSync2(dir) : [];
|
|
3922
3953
|
const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
|
|
3923
3954
|
const plistPath = join9(dir, f);
|
|
3924
3955
|
let program = null;
|
|
@@ -3930,7 +3961,7 @@ function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
|
|
|
3930
3961
|
label: f.slice(0, -".plist".length),
|
|
3931
3962
|
plistPath,
|
|
3932
3963
|
program,
|
|
3933
|
-
programExists: program !== null &&
|
|
3964
|
+
programExists: program !== null && existsSync9(program)
|
|
3934
3965
|
};
|
|
3935
3966
|
});
|
|
3936
3967
|
const seen = new Set(units.map((u) => u.label));
|
|
@@ -3949,9 +3980,9 @@ function removeLaunchdUnit(unit, dryRun, log) {
|
|
|
3949
3980
|
log.emit(`would unload and remove ${unit.label}`);
|
|
3950
3981
|
return;
|
|
3951
3982
|
}
|
|
3952
|
-
if (
|
|
3983
|
+
if (existsSync9(unit.plistPath)) spawnSync4("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
|
|
3953
3984
|
spawnSync4("launchctl", ["remove", unit.label], { encoding: "utf8" });
|
|
3954
|
-
if (
|
|
3985
|
+
if (existsSync9(unit.plistPath)) unlinkSync(unit.plistPath);
|
|
3955
3986
|
log.emit(`removed ${unit.label} (${unit.plistPath})`);
|
|
3956
3987
|
}
|
|
3957
3988
|
function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir, loaded) {
|
|
@@ -4034,7 +4065,7 @@ import { statfsSync } from "fs";
|
|
|
4034
4065
|
// src/git.ts
|
|
4035
4066
|
import { execFileSync } from "child_process";
|
|
4036
4067
|
import {
|
|
4037
|
-
existsSync as
|
|
4068
|
+
existsSync as existsSync10,
|
|
4038
4069
|
mkdirSync as mkdirSync5,
|
|
4039
4070
|
openSync as openSync2,
|
|
4040
4071
|
closeSync as closeSync2,
|
|
@@ -4434,7 +4465,7 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
|
|
|
4434
4465
|
try {
|
|
4435
4466
|
const st = statSync4(root);
|
|
4436
4467
|
if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
|
|
4437
|
-
if (
|
|
4468
|
+
if (existsSync10(join10(root, "checkout"))) continue;
|
|
4438
4469
|
rmSync6(root, { recursive: true, force: true });
|
|
4439
4470
|
removed.push(name);
|
|
4440
4471
|
} catch {
|
|
@@ -4477,7 +4508,7 @@ function originUrlOf(dir, remoteName) {
|
|
|
4477
4508
|
}
|
|
4478
4509
|
}
|
|
4479
4510
|
function ensureRepoCheckout(dir, remote, opts = {}) {
|
|
4480
|
-
if (
|
|
4511
|
+
if (existsSync10(`${dir}/.git`)) return false;
|
|
4481
4512
|
if (!remote) {
|
|
4482
4513
|
throw new GitError(
|
|
4483
4514
|
`${dir} does not exist and this repo has no discovered remote to clone from \u2014 re-run \`crew connect\`, or check out the repository yourself and point \`repos:\` at it`
|
|
@@ -4540,10 +4571,10 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
|
|
|
4540
4571
|
function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {}) {
|
|
4541
4572
|
const release = acquireCheckoutLock(dir);
|
|
4542
4573
|
try {
|
|
4543
|
-
if (!
|
|
4574
|
+
if (!existsSync10(`${dir}/.git`) && githubRemote) {
|
|
4544
4575
|
const url = cloneUrlFor(githubRemote, clone);
|
|
4545
4576
|
const marker = cloneFailureMarker(dir);
|
|
4546
|
-
if (
|
|
4577
|
+
if (existsSync10(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
|
|
4547
4578
|
return { action: "clone-failed", detail: readFileSync9(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
|
|
4548
4579
|
}
|
|
4549
4580
|
try {
|
|
@@ -7126,7 +7157,7 @@ var alreadyReported = (ticket, g, events) => events.some((e) => e.record_id ===
|
|
|
7126
7157
|
|
|
7127
7158
|
// src/agent.ts
|
|
7128
7159
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7129
|
-
import { readFileSync as readFileSync12, existsSync as
|
|
7160
|
+
import { readFileSync as readFileSync12, existsSync as existsSync13, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
|
|
7130
7161
|
import { StringDecoder } from "string_decoder";
|
|
7131
7162
|
import { finished } from "stream/promises";
|
|
7132
7163
|
import { join as join14, dirname as dirname6 } from "path";
|
|
@@ -7544,7 +7575,7 @@ function expandSkillMarkers(text, lookup, opts = {}) {
|
|
|
7544
7575
|
}
|
|
7545
7576
|
|
|
7546
7577
|
// src/skills.ts
|
|
7547
|
-
import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as
|
|
7578
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as existsSync12 } from "fs";
|
|
7548
7579
|
import { join as join13 } from "path";
|
|
7549
7580
|
import { parse as parseYaml } from "yaml";
|
|
7550
7581
|
import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError8, TablationApiError as TablationApiError2 } from "@tablation/client";
|
|
@@ -7561,7 +7592,7 @@ ${additions}`;
|
|
|
7561
7592
|
}
|
|
7562
7593
|
|
|
7563
7594
|
// src/agents.ts
|
|
7564
|
-
import { readFileSync as readFileSync10, existsSync as
|
|
7595
|
+
import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
7565
7596
|
import { join as join12 } from "path";
|
|
7566
7597
|
import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError7, TablationApiError } from "@tablation/client";
|
|
7567
7598
|
var PERSONA_NAME = {
|
|
@@ -7577,10 +7608,10 @@ var AgentsSyncError = class extends Error {
|
|
|
7577
7608
|
};
|
|
7578
7609
|
function personaDefaultPrompt(promptsDir, role) {
|
|
7579
7610
|
const briefPath = join12(promptsDir, "personas", `lane-${role}.md`);
|
|
7580
|
-
if (!
|
|
7611
|
+
if (!existsSync11(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
|
|
7581
7612
|
if (SELF_CONTAINED_ROLES.has(role)) return readFileSync10(briefPath, "utf8");
|
|
7582
7613
|
const commonPath = join12(promptsDir, "personas", "common.md");
|
|
7583
|
-
if (!
|
|
7614
|
+
if (!existsSync11(commonPath)) return readFileSync10(briefPath, "utf8");
|
|
7584
7615
|
return `${readFileSync10(commonPath, "utf8")}${readFileSync10(briefPath, "utf8")}`;
|
|
7585
7616
|
}
|
|
7586
7617
|
async function resolveAgentId(route, role, opts) {
|
|
@@ -7969,7 +8000,7 @@ function parseSkillFile(path) {
|
|
|
7969
8000
|
}
|
|
7970
8001
|
function listLocalSkills(promptsDir) {
|
|
7971
8002
|
const dir = join13(promptsDir, "skills");
|
|
7972
|
-
if (!
|
|
8003
|
+
if (!existsSync12(dir)) return [];
|
|
7973
8004
|
return readdirSync4(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join13(dir, f)));
|
|
7974
8005
|
}
|
|
7975
8006
|
async function syncSkills(route, opts) {
|
|
@@ -8222,17 +8253,17 @@ var AgentSpawnError = class extends AgentError {
|
|
|
8222
8253
|
};
|
|
8223
8254
|
function planAgentRun(o) {
|
|
8224
8255
|
const briefPath = join14(o.route.promptsDir, "personas", `lane-${o.role}.md`);
|
|
8225
|
-
if (!
|
|
8256
|
+
if (!existsSync13(briefPath)) {
|
|
8226
8257
|
throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
|
|
8227
8258
|
}
|
|
8228
8259
|
const commonPath = join14(o.route.promptsDir, "personas", "common.md");
|
|
8229
|
-
const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) &&
|
|
8260
|
+
const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync13(commonPath);
|
|
8230
8261
|
const now = o.now ?? (() => Date.now());
|
|
8231
8262
|
const routeLabel = routeSlug(o.route.route);
|
|
8232
8263
|
const digestPath2 = join14(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
|
|
8233
8264
|
let digest;
|
|
8234
8265
|
let ageSeconds;
|
|
8235
|
-
if (
|
|
8266
|
+
if (existsSync13(digestPath2)) {
|
|
8236
8267
|
ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
|
|
8237
8268
|
if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync12(digestPath2, "utf8");
|
|
8238
8269
|
}
|
|
@@ -8735,12 +8766,12 @@ async function reattachRun(streamPath, sc, emit, o = {}) {
|
|
|
8735
8766
|
}
|
|
8736
8767
|
|
|
8737
8768
|
// src/run-worktree.ts
|
|
8738
|
-
import { existsSync as
|
|
8769
|
+
import { existsSync as existsSync15 } from "fs";
|
|
8739
8770
|
import { dirname as dirname7, join as join16 } from "path";
|
|
8740
8771
|
|
|
8741
8772
|
// src/ports.ts
|
|
8742
8773
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
8743
|
-
import { existsSync as
|
|
8774
|
+
import { existsSync as existsSync14 } from "fs";
|
|
8744
8775
|
import { join as join15 } from "path";
|
|
8745
8776
|
var run = (cmd, args) => {
|
|
8746
8777
|
try {
|
|
@@ -8786,7 +8817,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
|
|
|
8786
8817
|
}
|
|
8787
8818
|
function worktreeExists(parent, prefix, n) {
|
|
8788
8819
|
for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
|
|
8789
|
-
if (
|
|
8820
|
+
if (existsSync14(join15(parent, name, ".git"))) return true;
|
|
8790
8821
|
}
|
|
8791
8822
|
return false;
|
|
8792
8823
|
}
|
|
@@ -8862,7 +8893,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
|
|
|
8862
8893
|
const existing = worktreeForNumber(dir, num);
|
|
8863
8894
|
const located = locateBranchForTicket(dir, cfg, t, role);
|
|
8864
8895
|
const baseRef = hasRemote ? `${remote}/${base}` : base;
|
|
8865
|
-
if (existing && !existing.prunable &&
|
|
8896
|
+
if (existing && !existing.prunable && existsSync15(existing.path)) {
|
|
8866
8897
|
const branch = existing.branch;
|
|
8867
8898
|
if (status(existing.path).length > 0) {
|
|
8868
8899
|
return { action: opts.resumeInterrupted ? "resume-dirty" : "leave-dirty", path: existing.path, branch };
|
|
@@ -8927,7 +8958,7 @@ function worktreeHoldsUnpushed(path, remote) {
|
|
|
8927
8958
|
}
|
|
8928
8959
|
async function finishRunWorktree(dir, cfg, branch) {
|
|
8929
8960
|
const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
|
|
8930
|
-
if (!wt || !
|
|
8961
|
+
if (!wt || !existsSync15(wt.path)) return { kind: "none" };
|
|
8931
8962
|
if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
|
|
8932
8963
|
if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
|
|
8933
8964
|
if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
|
|
@@ -8939,7 +8970,7 @@ async function finishRunWorktree(dir, cfg, branch) {
|
|
|
8939
8970
|
if (pids.length) await killGently(pids);
|
|
8940
8971
|
}
|
|
8941
8972
|
gitOk(dir, ["worktree", "remove", wt.path]);
|
|
8942
|
-
return
|
|
8973
|
+
return existsSync15(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
|
|
8943
8974
|
}
|
|
8944
8975
|
|
|
8945
8976
|
// src/handoff-guard.ts
|
|
@@ -9175,12 +9206,12 @@ async function guardRunStrays(route, ship, tracker, role, workingId, since2, log
|
|
|
9175
9206
|
|
|
9176
9207
|
// src/connect-wizard.ts
|
|
9177
9208
|
import { delimiter, dirname as dirname8, join as join17 } from "path";
|
|
9178
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
9209
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
9179
9210
|
import { stringify } from "yaml";
|
|
9180
9211
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
9181
9212
|
function shouldRunWizard(opts) {
|
|
9182
9213
|
if (!opts.isTTY) return false;
|
|
9183
|
-
const exists = opts.configExists ??
|
|
9214
|
+
const exists = opts.configExists ?? existsSync16;
|
|
9184
9215
|
return !configSearchPath(opts.crewHome).some((p) => exists(p));
|
|
9185
9216
|
}
|
|
9186
9217
|
function firstRunConfigPath() {
|
|
@@ -9292,7 +9323,7 @@ Wrote ${o.configPath}.
|
|
|
9292
9323
|
}
|
|
9293
9324
|
|
|
9294
9325
|
// src/release-run.ts
|
|
9295
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as
|
|
9326
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync17, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync7 } from "fs";
|
|
9296
9327
|
import { dirname as dirname9, join as join18 } from "path";
|
|
9297
9328
|
import { tmpdir as tmpdir2 } from "os";
|
|
9298
9329
|
|
|
@@ -9983,7 +10014,7 @@ async function bump(o, size) {
|
|
|
9983
10014
|
}
|
|
9984
10015
|
const files = o.repo.release.versionFiles.map((f) => join18(o.cwd, f));
|
|
9985
10016
|
const first = files[0];
|
|
9986
|
-
if (!
|
|
10017
|
+
if (!existsSync17(first)) {
|
|
9987
10018
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
9988
10019
|
return null;
|
|
9989
10020
|
}
|
|
@@ -10019,7 +10050,7 @@ function writeChangelog(o, version, entries) {
|
|
|
10019
10050
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
10020
10051
|
return;
|
|
10021
10052
|
}
|
|
10022
|
-
const existing =
|
|
10053
|
+
const existing = existsSync17(path) ? readFileSync13(path, "utf8") : "# Changelog\n";
|
|
10023
10054
|
writeFileSync10(path, insertChangelogSection(existing, section));
|
|
10024
10055
|
}
|
|
10025
10056
|
async function confirm(o, expected) {
|
|
@@ -10125,7 +10156,7 @@ function openReleaseCheckout(o) {
|
|
|
10125
10156
|
}
|
|
10126
10157
|
for (const f of o.repo.worktrees.copy) {
|
|
10127
10158
|
const src = join18(o.cwd, f);
|
|
10128
|
-
if (!
|
|
10159
|
+
if (!existsSync17(src)) continue;
|
|
10129
10160
|
mkdirSync8(dirname9(join18(dir, f)), { recursive: true });
|
|
10130
10161
|
copyFileSync(src, join18(dir, f));
|
|
10131
10162
|
}
|
|
@@ -10710,7 +10741,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
|
|
|
10710
10741
|
}
|
|
10711
10742
|
|
|
10712
10743
|
// src/watch.ts
|
|
10713
|
-
import { watch as watchFile, existsSync as
|
|
10744
|
+
import { watch as watchFile, existsSync as existsSync18, statSync as statSync6, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
|
|
10714
10745
|
var E = "\x1B[";
|
|
10715
10746
|
var dim = (s) => `${E}2m${s}${E}0m`;
|
|
10716
10747
|
var bold = (s) => `${E}1m${s}${E}0m`;
|
|
@@ -10811,7 +10842,7 @@ function renderHeaders(states, now = Date.now()) {
|
|
|
10811
10842
|
return states.map((s) => renderHeader(s, now)).join("");
|
|
10812
10843
|
}
|
|
10813
10844
|
function readFrom(file, from) {
|
|
10814
|
-
if (!
|
|
10845
|
+
if (!existsSync18(file)) return { events: [], offset: from };
|
|
10815
10846
|
const size = statSync6(file).size;
|
|
10816
10847
|
if (size < from) return readFrom(file, 0);
|
|
10817
10848
|
if (size === from) return { events: [], offset: from };
|
|
@@ -11830,7 +11861,7 @@ function schedulerInstalled(plan, exists, crontab) {
|
|
|
11830
11861
|
}
|
|
11831
11862
|
|
|
11832
11863
|
// src/worktree-sweep.ts
|
|
11833
|
-
import { existsSync as
|
|
11864
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11834
11865
|
import { basename as basename8 } from "path";
|
|
11835
11866
|
function planWorktreeSweep(target, tickets, contract) {
|
|
11836
11867
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
@@ -11866,7 +11897,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin")
|
|
|
11866
11897
|
let removed = 0;
|
|
11867
11898
|
let keptBranches = 0;
|
|
11868
11899
|
for (const a of actions) {
|
|
11869
|
-
if (!dryRun &&
|
|
11900
|
+
if (!dryRun && existsSync19(a.path)) {
|
|
11870
11901
|
let dirty = false;
|
|
11871
11902
|
try {
|
|
11872
11903
|
dirty = status(a.path).length > 0;
|
|
@@ -11880,7 +11911,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin")
|
|
|
11880
11911
|
continue;
|
|
11881
11912
|
}
|
|
11882
11913
|
}
|
|
11883
|
-
if (!dryRun && a.requirePushed && a.branch &&
|
|
11914
|
+
if (!dryRun && a.requirePushed && a.branch && existsSync19(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
|
|
11884
11915
|
log.warn(
|
|
11885
11916
|
`not removing worktree ${a.path} for ${a.ticket.issue_id} (${a.ticket.status}): ${a.branch} has commits that are not on ${remote}`,
|
|
11886
11917
|
{ ticket: a.ticket.issue_id, step: "worktree" }
|
|
@@ -12012,12 +12043,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
|
|
|
12012
12043
|
}
|
|
12013
12044
|
|
|
12014
12045
|
// src/stream-sweep.ts
|
|
12015
|
-
import { existsSync as
|
|
12046
|
+
import { existsSync as existsSync20, readdirSync as readdirSync5, rmSync as rmSync8, statSync as statSync7 } from "fs";
|
|
12016
12047
|
import { join as join19 } from "path";
|
|
12017
12048
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
12018
12049
|
function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
|
|
12019
12050
|
const dir = join19(stateDir, "streams");
|
|
12020
|
-
if (!
|
|
12051
|
+
if (!existsSync20(dir)) return [];
|
|
12021
12052
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
12022
12053
|
return readdirSync5(dir).map((name) => join19(dir, name)).filter((path) => {
|
|
12023
12054
|
try {
|
|
@@ -12045,7 +12076,7 @@ function applyStreamSweep(paths, dryRun, log) {
|
|
|
12045
12076
|
return removed;
|
|
12046
12077
|
}
|
|
12047
12078
|
function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
12048
|
-
if (!
|
|
12079
|
+
if (!existsSync20(stateDir)) return [];
|
|
12049
12080
|
const cutoff = now - retentionDays * DAY_MS2;
|
|
12050
12081
|
return readdirSync5(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join19(stateDir, name)).filter((path) => {
|
|
12051
12082
|
try {
|
|
@@ -12057,13 +12088,13 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
12057
12088
|
}
|
|
12058
12089
|
|
|
12059
12090
|
// src/cli.ts
|
|
12060
|
-
import { readFileSync as readFileSync19, existsSync as
|
|
12091
|
+
import { readFileSync as readFileSync19, existsSync as existsSync26, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync2, statSync as statSync9 } from "fs";
|
|
12061
12092
|
import { createInterface } from "readline/promises";
|
|
12062
12093
|
import { dirname as dirOf, resolve as resolvePath } from "path";
|
|
12063
12094
|
|
|
12064
12095
|
// src/ssh-keys.ts
|
|
12065
12096
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
12066
|
-
import { existsSync as
|
|
12097
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync14, chmodSync } from "fs";
|
|
12067
12098
|
import { dirname as dirname11, join as join20 } from "path";
|
|
12068
12099
|
var SshKeyError = class extends Error {
|
|
12069
12100
|
};
|
|
@@ -12073,7 +12104,7 @@ function shipSshKeyPathFor(stateDir) {
|
|
|
12073
12104
|
function ensureShipSshKeypair(stateDir) {
|
|
12074
12105
|
const privateKeyPath = shipSshKeyPathFor(stateDir);
|
|
12075
12106
|
const publicKeyPath = `${privateKeyPath}.pub`;
|
|
12076
|
-
if (!
|
|
12107
|
+
if (!existsSync21(privateKeyPath)) {
|
|
12077
12108
|
mkdirSync9(dirname11(privateKeyPath), { recursive: true });
|
|
12078
12109
|
try {
|
|
12079
12110
|
execFileSync3("ssh-keygen", [
|
|
@@ -12094,7 +12125,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
12094
12125
|
}
|
|
12095
12126
|
chmodSync(privateKeyPath, 384);
|
|
12096
12127
|
}
|
|
12097
|
-
if (!
|
|
12128
|
+
if (!existsSync21(publicKeyPath)) {
|
|
12098
12129
|
throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
|
|
12099
12130
|
}
|
|
12100
12131
|
const publicKey = readFileSync14(publicKeyPath, "utf8").trim();
|
|
@@ -12313,7 +12344,7 @@ function syncAllPassengerCheckouts(cfg) {
|
|
|
12313
12344
|
|
|
12314
12345
|
// src/tunnel.ts
|
|
12315
12346
|
import { spawn as spawn3 } from "child_process";
|
|
12316
|
-
import { existsSync as
|
|
12347
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as readdirSync6 } from "fs";
|
|
12317
12348
|
import { randomInt } from "crypto";
|
|
12318
12349
|
import { join as join21 } from "path";
|
|
12319
12350
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
@@ -12386,7 +12417,7 @@ function tunnelStatePath(stateDir, workspaceId) {
|
|
|
12386
12417
|
}
|
|
12387
12418
|
function readPersistedTunnel(stateDir, workspaceId) {
|
|
12388
12419
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
12389
|
-
if (!
|
|
12420
|
+
if (!existsSync22(path)) return void 0;
|
|
12390
12421
|
try {
|
|
12391
12422
|
return JSON.parse(readFileSync15(path, "utf8"));
|
|
12392
12423
|
} catch {
|
|
@@ -12494,7 +12525,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
|
|
|
12494
12525
|
}
|
|
12495
12526
|
function readAllPersistedWorkspaceIds(stateDir) {
|
|
12496
12527
|
const dir = join21(stateDir, "passengers");
|
|
12497
|
-
if (!
|
|
12528
|
+
if (!existsSync22(dir)) return [];
|
|
12498
12529
|
return readdirSync6(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
|
|
12499
12530
|
}
|
|
12500
12531
|
|
|
@@ -12615,7 +12646,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
12615
12646
|
import { createServer as createHttpServer } from "http";
|
|
12616
12647
|
import { spawn as spawn4 } from "child_process";
|
|
12617
12648
|
import { randomBytes } from "crypto";
|
|
12618
|
-
import { existsSync as
|
|
12649
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as readdirSync7 } from "fs";
|
|
12619
12650
|
import { join as join22 } from "path";
|
|
12620
12651
|
|
|
12621
12652
|
// src/secret-compare.ts
|
|
@@ -12709,7 +12740,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
|
|
|
12709
12740
|
}
|
|
12710
12741
|
function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
12711
12742
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
12712
|
-
if (!
|
|
12743
|
+
if (!existsSync23(path)) return void 0;
|
|
12713
12744
|
try {
|
|
12714
12745
|
return JSON.parse(readFileSync16(path, "utf8"));
|
|
12715
12746
|
} catch {
|
|
@@ -12740,7 +12771,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
|
|
|
12740
12771
|
}
|
|
12741
12772
|
function readAllPersistedSyncWorkspaceIds(stateDir) {
|
|
12742
12773
|
const dir = join22(stateDir, "passengers");
|
|
12743
|
-
if (!
|
|
12774
|
+
if (!existsSync23(dir)) return [];
|
|
12744
12775
|
const suffix = "-sync.json";
|
|
12745
12776
|
return readdirSync7(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
|
|
12746
12777
|
}
|
|
@@ -12797,11 +12828,11 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
|
|
|
12797
12828
|
}
|
|
12798
12829
|
|
|
12799
12830
|
// src/daemon.ts
|
|
12800
|
-
import { existsSync as
|
|
12831
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync9, rmSync as rmSync9, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
|
|
12801
12832
|
import { join as join24 } from "path";
|
|
12802
12833
|
|
|
12803
12834
|
// src/interrupted.ts
|
|
12804
|
-
import { existsSync as
|
|
12835
|
+
import { existsSync as existsSync24, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
|
|
12805
12836
|
import { join as join23 } from "path";
|
|
12806
12837
|
var INTERRUPTED_MARKER = "<!-- crew:interrupted -->";
|
|
12807
12838
|
function interruptedComment(b) {
|
|
@@ -12820,7 +12851,7 @@ var pidAlive = (pid) => {
|
|
|
12820
12851
|
};
|
|
12821
12852
|
function runFinished(streamPath) {
|
|
12822
12853
|
const path = `${streamPath}.meta.json`;
|
|
12823
|
-
if (!
|
|
12854
|
+
if (!existsSync24(path)) return false;
|
|
12824
12855
|
try {
|
|
12825
12856
|
return JSON.parse(readFileSync17(path, "utf8")).running !== true;
|
|
12826
12857
|
} catch {
|
|
@@ -12829,7 +12860,7 @@ function runFinished(streamPath) {
|
|
|
12829
12860
|
}
|
|
12830
12861
|
function orphanedStreams(o) {
|
|
12831
12862
|
const dir = join23(o.stateDir, "streams");
|
|
12832
|
-
if (!
|
|
12863
|
+
if (!existsSync24(dir)) return [];
|
|
12833
12864
|
const ownerAlive = o.isOwnerAlive ?? pidAlive;
|
|
12834
12865
|
const out = [];
|
|
12835
12866
|
for (const name of readdirSync8(dir)) {
|
|
@@ -12943,7 +12974,7 @@ function computeTreeSignature(paths) {
|
|
|
12943
12974
|
}
|
|
12944
12975
|
function makeStaleChecker(paths, opts = {}) {
|
|
12945
12976
|
const initial = computeTreeSignature(paths);
|
|
12946
|
-
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 &&
|
|
12977
|
+
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync25(opts.reloadSentinel);
|
|
12947
12978
|
}
|
|
12948
12979
|
function reloadSentinelFor(stateDir) {
|
|
12949
12980
|
return join24(stateDir, "reload-requested");
|
|
@@ -13829,7 +13860,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
13829
13860
|
if (host === "linux" && hasSystemd) {
|
|
13830
13861
|
const label = labelFor(CREW_HOME, "run");
|
|
13831
13862
|
const staleTimer = join25(homedir8(), ".config", "systemd", "user", `${label}.timer`);
|
|
13832
|
-
if (
|
|
13863
|
+
if (existsSync26(staleTimer)) {
|
|
13833
13864
|
process.stdout.write(
|
|
13834
13865
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
13835
13866
|
`
|
|
@@ -13855,7 +13886,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
13855
13886
|
const tracker = new Tracker(c, cfg.ship);
|
|
13856
13887
|
problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
|
|
13857
13888
|
for (const r of await resolvedRepos(c)) {
|
|
13858
|
-
if (!
|
|
13889
|
+
if (!existsSync26(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
|
|
13859
13890
|
if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
|
|
13860
13891
|
problems.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
|
|
13861
13892
|
}
|
|
@@ -13873,7 +13904,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
13873
13904
|
}
|
|
13874
13905
|
async function doctorFix(o) {
|
|
13875
13906
|
const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
|
|
13876
|
-
const unitExists = (p) =>
|
|
13907
|
+
const unitExists = (p) => existsSync26(p);
|
|
13877
13908
|
const crontab = o.host === "linux" ? spawnSync6("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
13878
13909
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run"), unitExists, crontab) : true;
|
|
13879
13910
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
@@ -15028,7 +15059,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15028
15059
|
`);
|
|
15029
15060
|
if (!dryRun) {
|
|
15030
15061
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
15031
|
-
const raw =
|
|
15062
|
+
const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
|
|
15032
15063
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
15033
15064
|
raw.agentSkills = skillResult.agentSkills;
|
|
15034
15065
|
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
@@ -15088,7 +15119,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15088
15119
|
`);
|
|
15089
15120
|
if (!dryRun) {
|
|
15090
15121
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
15091
|
-
const raw =
|
|
15122
|
+
const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
|
|
15092
15123
|
raw.agentSkills = result.agentSkills;
|
|
15093
15124
|
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
15094
15125
|
writeFileSync14(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -15111,7 +15142,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15111
15142
|
if (sub === "list" && !routeName) usage2();
|
|
15112
15143
|
if (sub === "add") {
|
|
15113
15144
|
try {
|
|
15114
|
-
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) =>
|
|
15145
|
+
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync26(resolvePath(p.replace(/^~(?=\/|$)/, homedir8()))));
|
|
15115
15146
|
routeName = a.route;
|
|
15116
15147
|
addPath = a.path;
|
|
15117
15148
|
addName = a.name;
|
|
@@ -15126,7 +15157,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15126
15157
|
}
|
|
15127
15158
|
const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
|
|
15128
15159
|
let resolvedRemotes;
|
|
15129
|
-
if (
|
|
15160
|
+
if (existsSync26(resolvedFile)) {
|
|
15130
15161
|
try {
|
|
15131
15162
|
resolvedRemotes = JSON.parse(readFileSync19(resolvedFile, "utf8"));
|
|
15132
15163
|
} catch {
|
|
@@ -15143,14 +15174,14 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15143
15174
|
for (const r of repos) {
|
|
15144
15175
|
const origin2 = originOf(r.dir);
|
|
15145
15176
|
const row = matchRepoName(origin2, resolvedRemotes);
|
|
15146
|
-
const note = !
|
|
15177
|
+
const note = !existsSync26(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
|
|
15147
15178
|
process.stdout.write(`${r.name} ${r.dir} ${note}
|
|
15148
15179
|
`);
|
|
15149
15180
|
}
|
|
15150
15181
|
break;
|
|
15151
15182
|
}
|
|
15152
15183
|
const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir8()));
|
|
15153
|
-
const top =
|
|
15184
|
+
const top = existsSync26(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
|
|
15154
15185
|
if (!top) throw new ReposError(`${target} is not a git checkout`);
|
|
15155
15186
|
const origin = originOf(top);
|
|
15156
15187
|
const plan = planRepoAdd({
|
|
@@ -15199,7 +15230,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
15199
15230
|
const created = await rowTracker.createRepoRow(rowPayload);
|
|
15200
15231
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
15201
15232
|
`);
|
|
15202
|
-
if (
|
|
15233
|
+
if (existsSync26(resolvedFile)) {
|
|
15203
15234
|
const raw = JSON.parse(readFileSync19(resolvedFile, "utf8"));
|
|
15204
15235
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
15205
15236
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
@@ -15446,7 +15477,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
15446
15477
|
operator = await pickOperator(found.holds);
|
|
15447
15478
|
}
|
|
15448
15479
|
let previous;
|
|
15449
|
-
const hadPreviousFile =
|
|
15480
|
+
const hadPreviousFile = existsSync26(resolvedPath);
|
|
15450
15481
|
if (hadPreviousFile) {
|
|
15451
15482
|
try {
|
|
15452
15483
|
previous = JSON.parse(readFileSync19(resolvedPath, "utf8"));
|
|
@@ -16351,7 +16382,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
16351
16382
|
);
|
|
16352
16383
|
try {
|
|
16353
16384
|
const evFile = eventFileFor(cfg.ship.stateDir);
|
|
16354
|
-
const evSize =
|
|
16385
|
+
const evSize = existsSync26(evFile) ? statSync9(evFile).size : 0;
|
|
16355
16386
|
process.stdout.write(
|
|
16356
16387
|
evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
|
|
16357
16388
|
` : "events log: ok\n"
|
|
@@ -16409,12 +16440,10 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
16409
16440
|
process.stdout.write(` - ${p2}
|
|
16410
16441
|
`);
|
|
16411
16442
|
}
|
|
16412
|
-
const
|
|
16413
|
-
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
if (bh.unsafe) process.exitCode = 1;
|
|
16417
|
-
}
|
|
16443
|
+
const dh = doctorBaseReport(r, config.branch.base, baseHealth);
|
|
16444
|
+
process.stdout.write(dh.text);
|
|
16445
|
+
if (dh.failed) process.exitCode = 1;
|
|
16446
|
+
if (dh.skipRest) continue;
|
|
16418
16447
|
if (config.release.ship) {
|
|
16419
16448
|
process.stdout.write(` release.ship ${config.release.ship}${config.release.ship === cfg.ship.name ? " (this ship)" : ""}
|
|
16420
16449
|
`);
|
package/package.json
CHANGED