@songsid/agend 2.2.0-beta.14 → 2.2.0-beta.15

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 (50) hide show
  1. package/dist/cli.js +39 -3
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config-validator.js +14 -0
  4. package/dist/config-validator.js.map +1 -1
  5. package/dist/doctor.js +7 -0
  6. package/dist/doctor.js.map +1 -1
  7. package/dist/fleet-context.d.ts +2 -0
  8. package/dist/fleet-manager.d.ts +14 -3
  9. package/dist/fleet-manager.js +54 -17
  10. package/dist/fleet-manager.js.map +1 -1
  11. package/dist/instance-avatar.d.ts +4 -3
  12. package/dist/instance-avatar.js +6 -5
  13. package/dist/instance-avatar.js.map +1 -1
  14. package/dist/launchd-handoff.d.ts +43 -0
  15. package/dist/launchd-handoff.js +56 -0
  16. package/dist/launchd-handoff.js.map +1 -0
  17. package/dist/locale.js +24 -4
  18. package/dist/locale.js.map +1 -1
  19. package/dist/login-shell.d.ts +21 -0
  20. package/dist/login-shell.js +98 -0
  21. package/dist/login-shell.js.map +1 -0
  22. package/dist/provider-probe.d.ts +29 -0
  23. package/dist/provider-probe.js +198 -2
  24. package/dist/provider-probe.js.map +1 -1
  25. package/dist/public-web-gateway.js +2 -0
  26. package/dist/public-web-gateway.js.map +1 -1
  27. package/dist/quickstart-api.d.ts +1 -1
  28. package/dist/quickstart-api.js +8 -6
  29. package/dist/quickstart-api.js.map +1 -1
  30. package/dist/quickstart.d.ts +12 -0
  31. package/dist/quickstart.js +36 -6
  32. package/dist/quickstart.js.map +1 -1
  33. package/dist/sd-notify.d.ts +22 -1
  34. package/dist/sd-notify.js +63 -10
  35. package/dist/sd-notify.js.map +1 -1
  36. package/dist/service-plan.d.ts +11 -0
  37. package/dist/service-plan.js +23 -0
  38. package/dist/service-plan.js.map +1 -1
  39. package/dist/topic-commands.d.ts +2 -0
  40. package/dist/topic-commands.js +9 -2
  41. package/dist/topic-commands.js.map +1 -1
  42. package/dist/ui/settings-dialogs.js +9 -4
  43. package/dist/ui/settings-strings.js +6 -2
  44. package/dist/ui/settings-token.js +7 -0
  45. package/dist/ui/settings-wizard.js +5 -3
  46. package/dist/ui/shared/panel-view.js +8 -0
  47. package/dist/ui/shared/view-strings.js +8 -0
  48. package/dist/view-api.js +26 -9
  49. package/dist/view-api.js.map +1 -1
  50. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -16,7 +16,7 @@ import { Command } from "commander";
16
16
  import { join, dirname } from "node:path";
17
17
  import { SchedulerDb } from "./scheduler/db.js";
18
18
  import { Cron } from "croner";
19
- import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, } from "node:fs";
19
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, openSync, writeSync, closeSync, } from "node:fs";
20
20
  import { homedir, totalmem, freemem, userInfo } from "node:os";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
@@ -38,6 +38,7 @@ import { DeliveryStatusArgs } from "./outbound-schemas.js";
38
38
  import { requestCpuProfile } from "./profile-control.js";
39
39
  import { requestSettingsConfirmation } from "./settings-control.js";
40
40
  import { canonicalCliEntry, selfCommand } from "./cli-entry.js";
41
+ import { handOff, shouldHandOff } from "./launchd-handoff.js";
41
42
  /** Prefix tmux args with -L when socket isolation is active. */
42
43
  function tmuxArgs(args) {
43
44
  const socket = getTmuxSocketName();
@@ -79,6 +80,28 @@ function planDeps() {
79
80
  now: () => new Date(),
80
81
  };
81
82
  }
83
+ /**
84
+ * #1871: hand this command to a detached copy that outlives the fleet's stop (see launchd-handoff.ts). True when it
85
+ * did; false to carry on here (not an agent session, or the copy could not be started — said so).
86
+ */
87
+ async function handOffLaunchdActivation(action) {
88
+ if (!shouldHandOff(process.env))
89
+ return false;
90
+ const handed = await handOff({ agendHome: DATA_DIR, self: selfCommand(process.argv.slice(2)), env: process.env, action }, {
91
+ open: path => openSync(path, "a", 0o600),
92
+ write: (fd, text) => writeSync(fd, text),
93
+ close: fd => closeSync(fd),
94
+ spawn: (command, args, options) => spawn(command, args, options),
95
+ now: () => new Date(),
96
+ });
97
+ if (handed.ok) {
98
+ console.log(` ↪ This session belongs to the fleet that launchd is about to stop, so \`agend ${action}\` goes on in the background.`);
99
+ console.log(` Its output: ${handed.log}`);
100
+ return true;
101
+ }
102
+ console.log(` ⚠ Could not continue in the background (${handed.reason}); going on in this session.`);
103
+ return false;
104
+ }
82
105
  const FLEET_CONFIG_PATH = join(DATA_DIR, "fleet.yaml");
83
106
  // CLI commands run outside FleetManager. Honor an explicit fleet locale while
84
107
  // keeping the historical English CLI default when config is missing/unset.
@@ -1446,6 +1469,12 @@ program
1446
1469
  }
1447
1470
  if (!gateFleetControl(DATA_DIR, "update", { yes: opts.yes }))
1448
1471
  process.exit(1);
1472
+ // #1871: on launchd the activation boots out the fleet this agent session runs in; the update must outlive that.
1473
+ {
1474
+ const { getServicePath, getSystemServicePath, detectPlatform } = await import("./service-installer.js");
1475
+ if (detectPlatform() === "macos" && !getSystemServicePath() && getServicePath() && await handOffLaunchdActivation("update"))
1476
+ return;
1477
+ }
1449
1478
  const { spawnSync } = await import("node:child_process");
1450
1479
  // The channel follows the version being replaced — this CLI's own package — so an alpha or beta stays on its channel.
1451
1480
  const tag = getUpdateSelector(opts, pkgVersion);
@@ -2347,8 +2376,13 @@ program
2347
2376
  if (printed.status === 0) {
2348
2377
  const plistPath = getServicePath() ?? join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
2349
2378
  // Path 2 (#1450 C6): a proven new plist waits on disk with its plan — exactly one bootout + bootstrap.
2350
- const { readPlan, admitPlan, planPath } = await import("./service-plan.js");
2351
- const plan = opts.force ? null : readPlan(DATA_DIR, deps);
2379
+ const { readPlan, admitPlan, planPath, planCarriedOut } = await import("./service-plan.js");
2380
+ const recorded = opts.force ? null : readPlan(DATA_DIR, deps);
2381
+ // #1871: an interrupted update's plan whose job launchd already runs (after `agend start`) is done: restart that
2382
+ // job as an unchanged one (path 1) instead of refusing it for its new pid.
2383
+ const plan = recorded && planCarriedOut(recorded, target, deps) ? null : recorded;
2384
+ if (recorded && !plan)
2385
+ console.log(" ℹ launchd already runs the job an interrupted update planned; restarting it as it is.");
2352
2386
  if (plan && expectation.ok) {
2353
2387
  const pkgDir = dirname(dirname(expectation.expected.entry));
2354
2388
  const admitted = admitPlan(plan, { target, expected: expectation.expected, pkgDir }, deps);
@@ -2357,6 +2391,8 @@ program
2357
2391
  process.exitCode = 1;
2358
2392
  return;
2359
2393
  }
2394
+ if (await handOffLaunchdActivation("restart"))
2395
+ return;
2360
2396
  const { activateService } = await import("./service-activation.js");
2361
2397
  const outcome = activateService({ kind: "launchd", label, plistPath: plan.plistPath, domain: `gui/${uid}` }, { bin: expectation.expected.entry, entry: expectation.expected.entry, node: expectation.expected.node, dir: pkgDir }, {
2362
2398
  ...deps,