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

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 (76) hide show
  1. package/dist/cli.js +58 -3
  2. package/dist/cli.js.map +1 -1
  3. package/dist/compact-watch.d.ts +78 -0
  4. package/dist/compact-watch.js +247 -0
  5. package/dist/compact-watch.js.map +1 -0
  6. package/dist/config-validator.js +14 -0
  7. package/dist/config-validator.js.map +1 -1
  8. package/dist/daemon.js +45 -5
  9. package/dist/daemon.js.map +1 -1
  10. package/dist/doctor.js +7 -0
  11. package/dist/doctor.js.map +1 -1
  12. package/dist/fleet-context.d.ts +2 -0
  13. package/dist/fleet-manager.d.ts +17 -3
  14. package/dist/fleet-manager.js +101 -19
  15. package/dist/fleet-manager.js.map +1 -1
  16. package/dist/instance-avatar.d.ts +4 -3
  17. package/dist/instance-avatar.js +6 -5
  18. package/dist/instance-avatar.js.map +1 -1
  19. package/dist/launchd-handoff.d.ts +43 -0
  20. package/dist/launchd-handoff.js +56 -0
  21. package/dist/launchd-handoff.js.map +1 -0
  22. package/dist/locale.js +24 -4
  23. package/dist/locale.js.map +1 -1
  24. package/dist/login-shell.d.ts +21 -0
  25. package/dist/login-shell.js +98 -0
  26. package/dist/login-shell.js.map +1 -0
  27. package/dist/provider-probe.d.ts +29 -0
  28. package/dist/provider-probe.js +198 -2
  29. package/dist/provider-probe.js.map +1 -1
  30. package/dist/public-web-gateway.js +2 -0
  31. package/dist/public-web-gateway.js.map +1 -1
  32. package/dist/quickstart-api.d.ts +1 -1
  33. package/dist/quickstart-api.js +8 -6
  34. package/dist/quickstart-api.js.map +1 -1
  35. package/dist/quickstart.d.ts +12 -0
  36. package/dist/quickstart.js +36 -6
  37. package/dist/quickstart.js.map +1 -1
  38. package/dist/sd-notify.d.ts +22 -1
  39. package/dist/sd-notify.js +63 -10
  40. package/dist/sd-notify.js.map +1 -1
  41. package/dist/service-installer.d.ts +24 -0
  42. package/dist/service-installer.js +49 -1
  43. package/dist/service-installer.js.map +1 -1
  44. package/dist/service-plan.d.ts +11 -0
  45. package/dist/service-plan.js +23 -0
  46. package/dist/service-plan.js.map +1 -1
  47. package/dist/topic-commands.d.ts +16 -0
  48. package/dist/topic-commands.js +39 -15
  49. package/dist/topic-commands.js.map +1 -1
  50. package/dist/transcript-jsonl.d.ts +7 -2
  51. package/dist/transcript-jsonl.js +13 -7
  52. package/dist/transcript-jsonl.js.map +1 -1
  53. package/dist/ui/chat-store.js +32 -2
  54. package/dist/ui/chat-strings.js +8 -2
  55. package/dist/ui/panel-chat.js +4 -1
  56. package/dist/ui/settings-dialogs.js +9 -4
  57. package/dist/ui/settings-strings.js +6 -2
  58. package/dist/ui/settings-token.js +7 -0
  59. package/dist/ui/settings-wizard.js +5 -3
  60. package/dist/ui/shared/app.css +1 -0
  61. package/dist/ui/shared/panel-view.js +8 -0
  62. package/dist/ui/shared/view-strings.js +8 -0
  63. package/dist/usage/providers.d.ts +28 -8
  64. package/dist/usage/providers.js +96 -41
  65. package/dist/usage/providers.js.map +1 -1
  66. package/dist/view-api.js +26 -9
  67. package/dist/view-api.js.map +1 -1
  68. package/dist/web-api.js +1 -1
  69. package/dist/web-api.js.map +1 -1
  70. package/dist/web-commands.d.ts +14 -1
  71. package/dist/web-commands.js +6 -3
  72. package/dist/web-commands.js.map +1 -1
  73. package/dist/web-upload.d.ts +7 -3
  74. package/dist/web-upload.js +20 -8
  75. package/dist/web-upload.js.map +1 -1
  76. 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);
@@ -1937,6 +1966,25 @@ program
1937
1966
  console.log(` ${outcome.message}`);
1938
1967
  return;
1939
1968
  }
1969
+ // #1884: a legacy system unit is the service a root fleet runs under; the refresh brings its ExecStart to this
1970
+ // install's selection too, or `agend restart` refuses it after an update (see refreshSystemUnitExecStart).
1971
+ const systemUnit = opts.activate === false && detectPlatform() !== "macos" ? getSystemServicePath() : null;
1972
+ if (systemUnit) {
1973
+ const { refreshSystemUnitExecStart } = await import("./service-installer.js");
1974
+ try {
1975
+ const refreshed = refreshSystemUnitExecStart(systemUnit, serviceVars, dirname(dirname(execPath)));
1976
+ if (refreshed.kind === "updated")
1977
+ console.log(` ✓ ${systemUnit}: ExecStart=${refreshed.execStart}`);
1978
+ else if (refreshed.kind === "foreign")
1979
+ console.log(` ℹ ${systemUnit} left as it is: ${refreshed.reason}.`);
1980
+ else if (refreshed.kind === "unreadable")
1981
+ console.log(` ⚠ ${systemUnit} could not be read; left as it is.`);
1982
+ }
1983
+ catch (err) {
1984
+ console.error(` ✗ ${systemUnit} could not be updated (${err.message}); \`agend restart\` will refuse until its ExecStart starts this install. Run the update as root, or: sudo agend install --no-activate`);
1985
+ process.exitCode = 1;
1986
+ }
1987
+ }
1940
1988
  const svcPath = installService(serviceVars);
1941
1989
  console.log(t("install.path", svcPath));
1942
1990
  if (opts.activate !== false) {
@@ -2347,8 +2395,13 @@ program
2347
2395
  if (printed.status === 0) {
2348
2396
  const plistPath = getServicePath() ?? join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
2349
2397
  // 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);
2398
+ const { readPlan, admitPlan, planPath, planCarriedOut } = await import("./service-plan.js");
2399
+ const recorded = opts.force ? null : readPlan(DATA_DIR, deps);
2400
+ // #1871: an interrupted update's plan whose job launchd already runs (after `agend start`) is done: restart that
2401
+ // job as an unchanged one (path 1) instead of refusing it for its new pid.
2402
+ const plan = recorded && planCarriedOut(recorded, target, deps) ? null : recorded;
2403
+ if (recorded && !plan)
2404
+ console.log(" ℹ launchd already runs the job an interrupted update planned; restarting it as it is.");
2352
2405
  if (plan && expectation.ok) {
2353
2406
  const pkgDir = dirname(dirname(expectation.expected.entry));
2354
2407
  const admitted = admitPlan(plan, { target, expected: expectation.expected, pkgDir }, deps);
@@ -2357,6 +2410,8 @@ program
2357
2410
  process.exitCode = 1;
2358
2411
  return;
2359
2412
  }
2413
+ if (await handOffLaunchdActivation("restart"))
2414
+ return;
2360
2415
  const { activateService } = await import("./service-activation.js");
2361
2416
  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
2417
  ...deps,