@tablation/crew 0.36.0 → 0.37.0
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 +118 -36
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10616,7 +10616,7 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
10616
10616
|
}
|
|
10617
10617
|
|
|
10618
10618
|
// src/cli.ts
|
|
10619
|
-
import { readFileSync as
|
|
10619
|
+
import { readFileSync as readFileSync16, existsSync as existsSync23, mkdirSync as mkdirSync13, writeFileSync as writeFileSync13, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync2, statSync as statSync9 } from "fs";
|
|
10620
10620
|
import { createInterface } from "readline/promises";
|
|
10621
10621
|
import { dirname as dirOf, resolve as resolvePath } from "path";
|
|
10622
10622
|
|
|
@@ -11354,7 +11354,7 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
|
|
|
11354
11354
|
}
|
|
11355
11355
|
|
|
11356
11356
|
// src/daemon.ts
|
|
11357
|
-
import { readdirSync as readdirSync6, statSync as statSync8 } from "fs";
|
|
11357
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync15, readdirSync as readdirSync6, rmSync as rmSync8, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
|
|
11358
11358
|
import { join as join21 } from "path";
|
|
11359
11359
|
var DEFAULT_FLOOR_MS = 5e3;
|
|
11360
11360
|
var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
|
|
@@ -11401,9 +11401,38 @@ function latestMtime(path) {
|
|
|
11401
11401
|
function computeTreeSignature(paths) {
|
|
11402
11402
|
return paths.map((p) => String(latestMtime(p))).join(":");
|
|
11403
11403
|
}
|
|
11404
|
-
function makeStaleChecker(paths) {
|
|
11404
|
+
function makeStaleChecker(paths, opts = {}) {
|
|
11405
11405
|
const initial = computeTreeSignature(paths);
|
|
11406
|
-
return () => computeTreeSignature(paths) !== initial;
|
|
11406
|
+
return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync22(opts.reloadSentinel);
|
|
11407
|
+
}
|
|
11408
|
+
function reloadSentinelFor(stateDir) {
|
|
11409
|
+
return join21(stateDir, "reload-requested");
|
|
11410
|
+
}
|
|
11411
|
+
function daemonStampFor(stateDir) {
|
|
11412
|
+
return join21(stateDir, "daemon.started");
|
|
11413
|
+
}
|
|
11414
|
+
function noteDaemonStarted(stateDir, now = Date.now()) {
|
|
11415
|
+
mkdirSync12(stateDir, { recursive: true });
|
|
11416
|
+
rmSync8(reloadSentinelFor(stateDir), { force: true });
|
|
11417
|
+
writeFileSync12(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
|
|
11418
|
+
`);
|
|
11419
|
+
}
|
|
11420
|
+
function readDaemonStarted(stateDir) {
|
|
11421
|
+
try {
|
|
11422
|
+
const v = JSON.parse(readFileSync15(daemonStampFor(stateDir), "utf8")).startedAt;
|
|
11423
|
+
return typeof v === "number" ? v : null;
|
|
11424
|
+
} catch {
|
|
11425
|
+
return null;
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11428
|
+
function configChangedAfter(files, startedAt) {
|
|
11429
|
+
for (const f of files) {
|
|
11430
|
+
try {
|
|
11431
|
+
if (statSync8(f).mtimeMs > startedAt) return f;
|
|
11432
|
+
} catch {
|
|
11433
|
+
}
|
|
11434
|
+
}
|
|
11435
|
+
return null;
|
|
11407
11436
|
}
|
|
11408
11437
|
var SPAWN_PARK_AFTER = 3;
|
|
11409
11438
|
var SpawnBreaker = class {
|
|
@@ -11804,6 +11833,7 @@ function usage() {
|
|
|
11804
11833
|
crew watch [route] live view of what the crew is doing
|
|
11805
11834
|
crew status [route] [--json] paused/running state; --json for a machine reader
|
|
11806
11835
|
crew projects [--route R] [--json] the Projects rows each route can read (id, name, prefix, work mode)
|
|
11836
|
+
crew reload make the running daemon re-read crew.yaml (--now: restart it immediately)
|
|
11807
11837
|
crew doctor [route] [--fix] preflight; --fix enables clean routes and offers crew install
|
|
11808
11838
|
crew ports [route] which checkout owns which ports, and what is up
|
|
11809
11839
|
crew reap [route] kill orphaned servers, drop worktrees for closed tickets
|
|
@@ -11858,7 +11888,12 @@ var value = (name) => {
|
|
|
11858
11888
|
var positional = argv.filter((a, i) => !a.startsWith("--") && !argv[i - 1]?.startsWith("--"));
|
|
11859
11889
|
var command = positional[0];
|
|
11860
11890
|
if (!command) usage();
|
|
11861
|
-
|
|
11891
|
+
function daemonConfigPaths(c) {
|
|
11892
|
+
const files = [c.configFile];
|
|
11893
|
+
for (const r of c.routes) if (r.apiKeyFile && !files.includes(r.apiKeyFile)) files.push(r.apiKeyFile);
|
|
11894
|
+
return files;
|
|
11895
|
+
}
|
|
11896
|
+
var STATE_WRITING_COMMANDS = /* @__PURE__ */ new Set(["connect", "disconnect", "install", "uninstall", "reload", "release", "merge", "deploy"]);
|
|
11862
11897
|
var STATE_WRITING_SUBCOMMANDS = {
|
|
11863
11898
|
agents: /* @__PURE__ */ new Set(["sync"]),
|
|
11864
11899
|
skills: /* @__PURE__ */ new Set(["sync"])
|
|
@@ -11968,7 +12003,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
11968
12003
|
const releaseFleetWide = ["merge", "deploy", "release"].includes(command) && flag("fleet");
|
|
11969
12004
|
const DAEMON_SUBCOMMANDS = /* @__PURE__ */ new Set(["start", "stop", "status", "restart"]);
|
|
11970
12005
|
const named = command === "daemon" && DAEMON_SUBCOMMANDS.has(positional[1] ?? "") ? positional[2] : positional[1];
|
|
11971
|
-
const fleetWide = command === "inbox" || command === "connect" || command === "disconnect" || command === "agents" || command === "skills" || command === "logbook" || command === "repos" || command === "projects" || releaseFleetWide || command === "pause" || command === "resume" || FLEET_CAPABLE.has(command) && !named && cfg.routes.length > 1;
|
|
12006
|
+
const fleetWide = command === "inbox" || command === "connect" || command === "disconnect" || command === "agents" || command === "skills" || command === "logbook" || command === "repos" || command === "projects" || releaseFleetWide || command === "pause" || command === "resume" || command === "reload" || FLEET_CAPABLE.has(command) && !named && cfg.routes.length > 1;
|
|
11972
12007
|
let route;
|
|
11973
12008
|
try {
|
|
11974
12009
|
route = fleetWide ? cfg.routes[0] : findRoute(cfg, named);
|
|
@@ -12175,7 +12210,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
12175
12210
|
if (host === "linux" && hasSystemd) {
|
|
12176
12211
|
const label = labelFor(CREW_HOME, "run");
|
|
12177
12212
|
const staleTimer = join22(homedir7(), ".config", "systemd", "user", `${label}.timer`);
|
|
12178
|
-
if (
|
|
12213
|
+
if (existsSync23(staleTimer)) {
|
|
12179
12214
|
process.stdout.write(
|
|
12180
12215
|
`removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
|
|
12181
12216
|
`
|
|
@@ -12201,7 +12236,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
12201
12236
|
const tracker = new Tracker(c, cfg.ship);
|
|
12202
12237
|
problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
|
|
12203
12238
|
for (const r of await resolvedRepos(c)) {
|
|
12204
|
-
if (!
|
|
12239
|
+
if (!existsSync23(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
|
|
12205
12240
|
if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
|
|
12206
12241
|
problems.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
|
|
12207
12242
|
}
|
|
@@ -12219,7 +12254,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
12219
12254
|
}
|
|
12220
12255
|
async function doctorFix(o) {
|
|
12221
12256
|
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))));
|
|
12222
|
-
const unitExists = (p) =>
|
|
12257
|
+
const unitExists = (p) => existsSync23(p);
|
|
12223
12258
|
const crontab = o.host === "linux" ? spawnSync5("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
12224
12259
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run"), unitExists, crontab) : true;
|
|
12225
12260
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
@@ -12236,7 +12271,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
12236
12271
|
for (const a of plan.actions) {
|
|
12237
12272
|
if (a.kind === "enable") {
|
|
12238
12273
|
if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
|
|
12239
|
-
|
|
12274
|
+
writeFileSync13(cfg.configFile, planEnable(readFileSync16(cfg.configFile, "utf8"), a.route));
|
|
12240
12275
|
enabledSound.add(a.route);
|
|
12241
12276
|
changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
|
|
12242
12277
|
} else {
|
|
@@ -13141,6 +13176,9 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13141
13176
|
}
|
|
13142
13177
|
requireArmed("run the daemon loop");
|
|
13143
13178
|
const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join22(CREW_HOME, "src"), join22(CREW_HOME, "dist"), join22(CREW_HOME, "bin")];
|
|
13179
|
+
const configPaths = daemonConfigPaths(cfg);
|
|
13180
|
+
watchPaths.push(...configPaths);
|
|
13181
|
+
noteDaemonStarted(cfg.ship.stateDir);
|
|
13144
13182
|
const spawnBreaker = new SpawnBreaker();
|
|
13145
13183
|
const stopReason = await runDaemonLoop({
|
|
13146
13184
|
runPass: () => runOnePass({
|
|
@@ -13158,14 +13196,16 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13158
13196
|
`)
|
|
13159
13197
|
})
|
|
13160
13198
|
}),
|
|
13161
|
-
checkStale: makeStaleChecker(watchPaths),
|
|
13199
|
+
checkStale: makeStaleChecker(watchPaths, { reloadSentinel: reloadSentinelFor(cfg.ship.stateDir) }),
|
|
13162
13200
|
onPass: (result) => {
|
|
13163
13201
|
if (result.started.length) emit.emit(`daemon pass started: ${result.started.join(", ")}`);
|
|
13164
13202
|
for (const s of result.skipped) emit.emit(`daemon pass skipped ${s.role} \u2014 ${s.reason}`);
|
|
13165
13203
|
}
|
|
13166
13204
|
});
|
|
13167
13205
|
if (stopReason === "stale") {
|
|
13168
|
-
emit.emit(
|
|
13206
|
+
emit.emit(
|
|
13207
|
+
"crew daemon: installed tree, crew.yaml or a reload request changed on disk \u2014 exiting cleanly so the OS-level restart relaunches with the update"
|
|
13208
|
+
);
|
|
13169
13209
|
process.exit(0);
|
|
13170
13210
|
}
|
|
13171
13211
|
break;
|
|
@@ -13289,11 +13329,11 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13289
13329
|
`);
|
|
13290
13330
|
if (!dryRun) {
|
|
13291
13331
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
13292
|
-
const raw =
|
|
13332
|
+
const raw = existsSync23(resolvedPath) ? JSON.parse(readFileSync16(resolvedPath, "utf8")) : {};
|
|
13293
13333
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
13294
13334
|
raw.agentSkills = skillResult.agentSkills;
|
|
13295
|
-
|
|
13296
|
-
|
|
13335
|
+
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
13336
|
+
writeFileSync13(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
13297
13337
|
`);
|
|
13298
13338
|
}
|
|
13299
13339
|
if (personaResult.outcomes.some((o) => o.action === "diverged") || skillResult.outcomes.some((o) => o.action === "diverged")) {
|
|
@@ -13338,10 +13378,10 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13338
13378
|
`);
|
|
13339
13379
|
if (!dryRun) {
|
|
13340
13380
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
13341
|
-
const raw =
|
|
13381
|
+
const raw = existsSync23(resolvedPath) ? JSON.parse(readFileSync16(resolvedPath, "utf8")) : {};
|
|
13342
13382
|
raw.agentSkills = result.agentSkills;
|
|
13343
|
-
|
|
13344
|
-
|
|
13383
|
+
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
13384
|
+
writeFileSync13(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
13345
13385
|
`);
|
|
13346
13386
|
}
|
|
13347
13387
|
if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
|
|
@@ -13354,14 +13394,14 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13354
13394
|
process.exit(2);
|
|
13355
13395
|
};
|
|
13356
13396
|
if (sub !== "add" && sub !== "list") usage2();
|
|
13357
|
-
const configText =
|
|
13397
|
+
const configText = readFileSync16(cfg.configFile, "utf8");
|
|
13358
13398
|
let routeName = positional[2];
|
|
13359
13399
|
let addPath = ".";
|
|
13360
13400
|
let addName;
|
|
13361
13401
|
if (sub === "list" && !routeName) usage2();
|
|
13362
13402
|
if (sub === "add") {
|
|
13363
13403
|
try {
|
|
13364
|
-
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) =>
|
|
13404
|
+
const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync23(resolvePath(p.replace(/^~(?=\/|$)/, homedir7()))));
|
|
13365
13405
|
routeName = a.route;
|
|
13366
13406
|
addPath = a.path;
|
|
13367
13407
|
addName = a.name;
|
|
@@ -13376,9 +13416,9 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13376
13416
|
}
|
|
13377
13417
|
const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
|
|
13378
13418
|
let resolvedRemotes;
|
|
13379
|
-
if (
|
|
13419
|
+
if (existsSync23(resolvedFile)) {
|
|
13380
13420
|
try {
|
|
13381
|
-
resolvedRemotes = JSON.parse(
|
|
13421
|
+
resolvedRemotes = JSON.parse(readFileSync16(resolvedFile, "utf8"));
|
|
13382
13422
|
} catch {
|
|
13383
13423
|
}
|
|
13384
13424
|
}
|
|
@@ -13393,14 +13433,14 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13393
13433
|
for (const r of repos) {
|
|
13394
13434
|
const origin2 = originOf(r.dir);
|
|
13395
13435
|
const row = matchRepoName(origin2, resolvedRemotes);
|
|
13396
|
-
const note = !
|
|
13436
|
+
const note = !existsSync23(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
|
|
13397
13437
|
process.stdout.write(`${r.name} ${r.dir} ${note}
|
|
13398
13438
|
`);
|
|
13399
13439
|
}
|
|
13400
13440
|
break;
|
|
13401
13441
|
}
|
|
13402
13442
|
const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir7()));
|
|
13403
|
-
const top =
|
|
13443
|
+
const top = existsSync23(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
|
|
13404
13444
|
if (!top) throw new ReposError(`${target} is not a git checkout`);
|
|
13405
13445
|
const origin = originOf(top);
|
|
13406
13446
|
const plan = planRepoAdd({
|
|
@@ -13449,15 +13489,15 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13449
13489
|
const created = await rowTracker.createRepoRow(rowPayload);
|
|
13450
13490
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
13451
13491
|
`);
|
|
13452
|
-
if (
|
|
13453
|
-
const raw = JSON.parse(
|
|
13492
|
+
if (existsSync23(resolvedFile)) {
|
|
13493
|
+
const raw = JSON.parse(readFileSync16(resolvedFile, "utf8"));
|
|
13454
13494
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
13455
13495
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
13456
|
-
|
|
13496
|
+
writeFileSync13(resolvedFile, `${JSON.stringify(raw, null, 2)}
|
|
13457
13497
|
`);
|
|
13458
13498
|
}
|
|
13459
13499
|
}
|
|
13460
|
-
|
|
13500
|
+
writeFileSync13(cfg.configFile, plan.text);
|
|
13461
13501
|
process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
|
|
13462
13502
|
`);
|
|
13463
13503
|
} catch (e) {
|
|
@@ -13696,10 +13736,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
13696
13736
|
operator = await pickOperator(found.holds);
|
|
13697
13737
|
}
|
|
13698
13738
|
let previous;
|
|
13699
|
-
const hadPreviousFile =
|
|
13739
|
+
const hadPreviousFile = existsSync23(resolvedPath);
|
|
13700
13740
|
if (hadPreviousFile) {
|
|
13701
13741
|
try {
|
|
13702
|
-
previous = JSON.parse(
|
|
13742
|
+
previous = JSON.parse(readFileSync16(resolvedPath, "utf8"));
|
|
13703
13743
|
} catch {
|
|
13704
13744
|
}
|
|
13705
13745
|
}
|
|
@@ -13760,10 +13800,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
13760
13800
|
process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
|
|
13761
13801
|
`);
|
|
13762
13802
|
} else {
|
|
13763
|
-
|
|
13803
|
+
mkdirSync13(dirname12(resolvedPath), { recursive: true });
|
|
13764
13804
|
if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
|
|
13765
13805
|
const tmpPath = `${resolvedPath}.tmp${process.pid}`;
|
|
13766
|
-
|
|
13806
|
+
writeFileSync13(tmpPath, `${JSON.stringify(resolved, null, 2)}
|
|
13767
13807
|
`);
|
|
13768
13808
|
renameSync2(tmpPath, resolvedPath);
|
|
13769
13809
|
process.stderr.write(`Resolved ids written to ${resolvedPath}
|
|
@@ -13777,8 +13817,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
13777
13817
|
process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
|
|
13778
13818
|
`);
|
|
13779
13819
|
} else {
|
|
13780
|
-
|
|
13781
|
-
|
|
13820
|
+
mkdirSync13(dirname12(apiKeyFile), { recursive: true });
|
|
13821
|
+
writeFileSync13(apiKeyFile, `${apiKeyVar}=${apiKey}
|
|
13782
13822
|
`, { mode: 384 });
|
|
13783
13823
|
process.stderr.write(`API key written to ${apiKeyFile}
|
|
13784
13824
|
`);
|
|
@@ -14285,6 +14325,38 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14285
14325
|
if (!await installScheduler({ replace: flag("replace") })) process.exit(2);
|
|
14286
14326
|
break;
|
|
14287
14327
|
}
|
|
14328
|
+
case "reload": {
|
|
14329
|
+
const sentinel = reloadSentinelFor(cfg.ship.stateDir);
|
|
14330
|
+
if (dryRun) {
|
|
14331
|
+
process.stdout.write(`would write ${sentinel}
|
|
14332
|
+
`);
|
|
14333
|
+
break;
|
|
14334
|
+
}
|
|
14335
|
+
const host = hostPlatform();
|
|
14336
|
+
const control = host === "windows" ? null : planDaemonControl(CREW_HOME, host, detectSystemd());
|
|
14337
|
+
const unitStatus = control?.statusCommand ? spawnSync5(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
|
|
14338
|
+
const unitRunning = unitStatus !== null && !unitStatus.error && unitStatus.status === 0;
|
|
14339
|
+
if (!unitRunning) {
|
|
14340
|
+
process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
|
|
14341
|
+
break;
|
|
14342
|
+
}
|
|
14343
|
+
mkdirSync13(cfg.ship.stateDir, { recursive: true });
|
|
14344
|
+
writeFileSync13(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
14345
|
+
`);
|
|
14346
|
+
process.stdout.write("crew reload: requested \u2014 the daemon finishes its in-flight pass, then relaunches with the new config\n");
|
|
14347
|
+
if (flag("now") && control?.stopCommand && control.startCommand) {
|
|
14348
|
+
for (const cmd of [control.stopCommand, control.startCommand]) {
|
|
14349
|
+
const res = spawnSync5(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
14350
|
+
if (cmd === control.startCommand && (res.error || res.status !== 0)) {
|
|
14351
|
+
process.stderr.write(`crew reload: ${cmd.join(" ")} exited ${res.status ?? "error"}
|
|
14352
|
+
`);
|
|
14353
|
+
process.exitCode = 1;
|
|
14354
|
+
}
|
|
14355
|
+
}
|
|
14356
|
+
process.stdout.write("crew reload: restarted the daemon unit now\n");
|
|
14357
|
+
}
|
|
14358
|
+
break;
|
|
14359
|
+
}
|
|
14288
14360
|
case "uninstall": {
|
|
14289
14361
|
const host = hostPlatform();
|
|
14290
14362
|
if (host === "windows") {
|
|
@@ -14320,7 +14392,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14320
14392
|
}
|
|
14321
14393
|
case "log": {
|
|
14322
14394
|
try {
|
|
14323
|
-
const text =
|
|
14395
|
+
const text = readFileSync16(cfg.ship.logFile, "utf8").split("\n");
|
|
14324
14396
|
process.stdout.write(`${text.slice(-(Number(value("tail")) || 40)).join("\n")}
|
|
14325
14397
|
`);
|
|
14326
14398
|
} catch {
|
|
@@ -14530,13 +14602,23 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
14530
14602
|
);
|
|
14531
14603
|
try {
|
|
14532
14604
|
const evFile = eventFileFor(cfg.ship.stateDir);
|
|
14533
|
-
const evSize =
|
|
14605
|
+
const evSize = existsSync23(evFile) ? statSync9(evFile).size : 0;
|
|
14534
14606
|
process.stdout.write(
|
|
14535
14607
|
evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
|
|
14536
14608
|
` : "events log: ok\n"
|
|
14537
14609
|
);
|
|
14538
14610
|
} catch {
|
|
14539
14611
|
}
|
|
14612
|
+
const daemonStartedAt = readDaemonStarted(cfg.ship.stateDir);
|
|
14613
|
+
if (daemonStartedAt !== null) {
|
|
14614
|
+
const changed2 = configChangedAfter(daemonConfigPaths(cfg), daemonStartedAt);
|
|
14615
|
+
if (changed2) {
|
|
14616
|
+
process.stdout.write(
|
|
14617
|
+
`daemon config: WARN ${changed2} changed at ${new Date(statSync9(changed2).mtimeMs).toISOString()} but the daemon started at ${new Date(daemonStartedAt).toISOString()} \u2014 run \`crew reload\`
|
|
14618
|
+
`
|
|
14619
|
+
);
|
|
14620
|
+
}
|
|
14621
|
+
}
|
|
14540
14622
|
const tracker = new Tracker(route, cfg.ship);
|
|
14541
14623
|
const contractProblems = validateContract(tracker.contract);
|
|
14542
14624
|
process.stdout.write(
|
package/package.json
CHANGED