@tablation/crew 0.10.1 → 0.10.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.
Files changed (2) hide show
  1. package/dist/cli.js +26 -15
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2323,9 +2323,14 @@ function programFromPlist(text) {
2323
2323
  const args = [...block[1].matchAll(/<string>([\s\S]*?)<\/string>/g)].map((m) => xmlUnescape(m[1]));
2324
2324
  return args.find((a) => a.endsWith("/bin/crew")) ?? args[0] ?? null;
2325
2325
  }
2326
- function listLaunchdUnits(dir = launchAgentsDir()) {
2327
- if (!existsSync5(dir)) return [];
2328
- return readdirSync(dir).filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
2326
+ function loadedCrewLabels() {
2327
+ const list = spawnSync("launchctl", ["list"], { encoding: "utf8" });
2328
+ if (list.status !== 0) return [];
2329
+ return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
2330
+ }
2331
+ function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
2332
+ const files = existsSync5(dir) ? readdirSync(dir) : [];
2333
+ const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
2329
2334
  const plistPath = join5(dir, f);
2330
2335
  let program = null;
2331
2336
  try {
@@ -2339,10 +2344,16 @@ function listLaunchdUnits(dir = launchAgentsDir()) {
2339
2344
  programExists: program !== null && existsSync5(program)
2340
2345
  };
2341
2346
  });
2347
+ const seen = new Set(units.map((u) => u.label));
2348
+ for (const label of [...loaded].sort()) {
2349
+ if (!label.startsWith(CREW_LAUNCHD_PREFIX) || seen.has(label)) continue;
2350
+ units.push({ label, plistPath: join5(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
2351
+ }
2352
+ return units;
2342
2353
  }
2343
- function foreignLaunchdUnits(crewHome, dir) {
2354
+ function foreignLaunchdUnits(crewHome, dir, loaded) {
2344
2355
  const own = new Set(["run", "release", "passengers"].map((job) => labelFor(crewHome, job)));
2345
- return listLaunchdUnits(dir).filter((u) => !own.has(u.label));
2356
+ return listLaunchdUnits(dir, loaded).filter((u) => !own.has(u.label));
2346
2357
  }
2347
2358
  function removeLaunchdUnit(unit, dryRun, log) {
2348
2359
  if (dryRun) {
@@ -2354,9 +2365,9 @@ function removeLaunchdUnit(unit, dryRun, log) {
2354
2365
  if (existsSync5(unit.plistPath)) unlinkSync(unit.plistPath);
2355
2366
  log.emit(`removed ${unit.label} (${unit.plistPath})`);
2356
2367
  }
2357
- function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir) {
2358
- const foreign = foreignLaunchdUnits(crewHome, dir);
2359
- const blocking = foreign.filter((u) => u.program === null || u.programExists);
2368
+ function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir, loaded) {
2369
+ const foreign = foreignLaunchdUnits(crewHome, dir, loaded);
2370
+ const blocking = foreign.filter((u) => !u.orphan && (u.program === null || u.programExists));
2360
2371
  if (blocking.length > 0 && !replace) {
2361
2372
  log.warn(
2362
2373
  `two crew installs target this machine: ${blocking.map((u) => `${u.label} runs ${u.program ?? "an unreadable ProgramArguments"}`).join("; ")}. Run \`crew uninstall\` from the other one, or pass --replace.`
@@ -2364,14 +2375,14 @@ function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir) {
2364
2375
  return false;
2365
2376
  }
2366
2377
  for (const unit of foreign) {
2367
- const why = unit.program === null ? "replacing a unit with no readable ProgramArguments" : unit.programExists ? "replacing a live other install" : "removing a stale unit whose program is gone";
2378
+ const why = unit.orphan ? "removing a loaded job whose plist is gone" : unit.program === null ? "replacing a unit with no readable ProgramArguments" : unit.programExists ? "replacing a live other install" : "removing a stale unit whose program is gone";
2368
2379
  log.emit(`${why}: ${unit.label}`);
2369
2380
  removeLaunchdUnit(unit, dryRun, log);
2370
2381
  }
2371
2382
  return true;
2372
2383
  }
2373
- function removeAllLaunchdUnits(dryRun, log, dir) {
2374
- for (const unit of listLaunchdUnits(dir)) removeLaunchdUnit(unit, dryRun, log);
2384
+ function removeAllLaunchdUnits(dryRun, log, dir, loaded) {
2385
+ for (const unit of listLaunchdUnits(dir, loaded)) removeLaunchdUnit(unit, dryRun, log);
2375
2386
  }
2376
2387
  function parseLaunchctlList(text) {
2377
2388
  const jobs = /* @__PURE__ */ new Map();
@@ -2383,13 +2394,13 @@ function parseLaunchctlList(text) {
2383
2394
  return jobs;
2384
2395
  }
2385
2396
  function describeForeignLaunchdUnits(crewHome, dir) {
2386
- const foreign = foreignLaunchdUnits(crewHome, dir);
2387
- if (foreign.length === 0) return [];
2388
2397
  const list = spawnSync("launchctl", ["list"], { encoding: "utf8" });
2389
2398
  const jobs = parseLaunchctlList(list.status === 0 ? list.stdout ?? "" : "");
2399
+ const foreign = foreignLaunchdUnits(crewHome, dir, jobs.keys());
2390
2400
  return foreign.map((u) => {
2391
2401
  const status2 = jobs.get(u.label);
2392
2402
  const loaded = status2 ? `loaded, last exit ${status2.lastExit}` : "not loaded";
2403
+ if (u.orphan) return ` ${u.label}: ${loaded}; plist gone \u2014 stale: \`crew install\` removes it`;
2393
2404
  const program = u.program ? `${u.program} (${u.programExists ? "present" : "GONE"})` : "no readable ProgramArguments";
2394
2405
  const stale = status2 && !u.programExists ? " \u2014 stale: `crew install` removes it" : "";
2395
2406
  return ` ${u.label}: ${loaded}; runs ${program}${stale}`;
@@ -9516,7 +9527,7 @@ if (command === "passenger-sync-daemon") {
9516
9527
  `),
9517
9528
  warn: (m) => process.stderr.write(`crew install: ${m}
9518
9529
  `)
9519
- });
9530
+ }, void 0, loadedCrewLabels());
9520
9531
  if (!ok) return false;
9521
9532
  }
9522
9533
  const installPathProblem = cfg.routes.some((r) => r.enabled && r.hostPassengers) ? dockerPathProblem(cfg.ship) : void 0;
@@ -11444,7 +11455,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
11444
11455
  `),
11445
11456
  warn: (m) => process.stderr.write(`crew uninstall: ${m}
11446
11457
  `)
11447
- });
11458
+ }, void 0, loadedCrewLabels());
11448
11459
  }
11449
11460
  break;
11450
11461
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",