@songsid/agend 2.3.0-beta.23 → 2.3.0-beta.24

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 (77) hide show
  1. package/dist/auth-api.js +46 -19
  2. package/dist/auth-api.js.map +1 -1
  3. package/dist/backend/grok.js +3 -1
  4. package/dist/backend/grok.js.map +1 -1
  5. package/dist/bug-report-cli.js +10 -9
  6. package/dist/bug-report-cli.js.map +1 -1
  7. package/dist/bug-report.js +11 -10
  8. package/dist/bug-report.js.map +1 -1
  9. package/dist/cli.js +36 -37
  10. package/dist/cli.js.map +1 -1
  11. package/dist/export-import.js +4 -3
  12. package/dist/export-import.js.map +1 -1
  13. package/dist/fleet-manager.js +23 -15
  14. package/dist/fleet-manager.js.map +1 -1
  15. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +48 -10
  16. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +1 -1
  17. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +1 -1
  18. package/dist/locale.js +178 -0
  19. package/dist/locale.js.map +1 -1
  20. package/dist/quickstart.js +3 -3
  21. package/dist/quickstart.js.map +1 -1
  22. package/dist/scheduler/task-board.js +2 -1
  23. package/dist/scheduler/task-board.js.map +1 -1
  24. package/dist/service-installer.js +9 -8
  25. package/dist/service-installer.js.map +1 -1
  26. package/dist/service-plan.d.ts +8 -0
  27. package/dist/service-plan.js +13 -0
  28. package/dist/service-plan.js.map +1 -1
  29. package/dist/settings-change.js +9 -8
  30. package/dist/settings-change.js.map +1 -1
  31. package/dist/settings-confirmation.d.ts +2 -0
  32. package/dist/settings-confirmation.js +14 -8
  33. package/dist/settings-confirmation.js.map +1 -1
  34. package/dist/settings-control.js +2 -1
  35. package/dist/settings-control.js.map +1 -1
  36. package/dist/setup-auth.d.ts +1 -0
  37. package/dist/setup-auth.js +2 -1
  38. package/dist/setup-auth.js.map +1 -1
  39. package/dist/setup-host.d.ts +1 -0
  40. package/dist/setup-host.js +6 -2
  41. package/dist/setup-host.js.map +1 -1
  42. package/dist/tmux-exec-helper.d.ts +8 -2
  43. package/dist/tmux-exec-helper.js +21 -8
  44. package/dist/tmux-exec-helper.js.map +1 -1
  45. package/dist/ui/fleet-task-common.js +2 -2
  46. package/dist/ui/settings-appearance.js +11 -2
  47. package/dist/ui/settings-dialogs.js +2 -2
  48. package/dist/ui/settings-mods.js +2 -0
  49. package/dist/ui/settings-strings.js +30 -0
  50. package/dist/ui/shared/app.css +13 -23
  51. package/dist/ui/shared/effect-stock-trust.js +4 -4
  52. package/dist/ui/shared/panel-view.js +14 -29
  53. package/dist/ui/shared/usage-dialog.js +29 -8
  54. package/dist/ui/shared/view-strings.js +2 -4
  55. package/dist/ui/web-terminal/terminal.html +10 -10
  56. package/dist/ui/web-terminal/terminal.js +58 -12
  57. package/dist/update-preflight.js +1 -1
  58. package/dist/web-api.js +11 -7
  59. package/dist/web-api.js.map +1 -1
  60. package/dist/web-app-icon-worker.js +2 -1
  61. package/dist/web-app-icon-worker.js.map +1 -1
  62. package/dist/web-app-icon.d.ts +10 -8
  63. package/dist/web-app-icon.js +14 -13
  64. package/dist/web-app-icon.js.map +1 -1
  65. package/dist/web-auth.d.ts +8 -4
  66. package/dist/web-auth.js +28 -17
  67. package/dist/web-auth.js.map +1 -1
  68. package/dist/web-cookie-names.d.ts +5 -0
  69. package/dist/web-cookie-names.js +12 -0
  70. package/dist/web-cookie-names.js.map +1 -0
  71. package/dist/web-png.d.ts +4 -0
  72. package/dist/web-png.js +10 -2
  73. package/dist/web-png.js.map +1 -1
  74. package/dist/web-returning-device.d.ts +4 -2
  75. package/dist/web-returning-device.js +12 -5
  76. package/dist/web-returning-device.js.map +1 -1
  77. package/package.json +11 -11
package/dist/cli.js CHANGED
@@ -35,7 +35,8 @@ import { limitFleetCoreDumps } from "./coredump-filter.js";
35
35
  import { SYSTEMD_RESTART_TIMEOUT_MS, getServicePath, getSystemServicePath } from "./service-installer.js";
36
36
  import { selectSystemdRestartTarget, SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE, } from "./service-restart-selection.js";
37
37
  import { loadRawFleetConfig } from "./config.js";
38
- import { setLocale, t } from "./locale.js";
38
+ import { detectLocale, setLocale, t } from "./locale.js";
39
+ import { settingsSourceLabel } from "./settings-confirmation.js";
39
40
  import { DeliveryOutbox, deliveryStatusSelector } from "./delivery-outbox.js";
40
41
  import { DeliveryStatusArgs } from "./outbound-schemas.js";
41
42
  import { requestCpuProfile } from "./profile-control.js";
@@ -107,14 +108,15 @@ async function handOffLaunchdActivation(action) {
107
108
  return false;
108
109
  }
109
110
  const FLEET_CONFIG_PATH = join(DATA_DIR, "fleet.yaml");
110
- // CLI commands run outside FleetManager. Honor an explicit fleet locale while
111
- // keeping the historical English CLI default when config is missing/unset.
111
+ // CLI commands run outside FleetManager, in the fleet's language as the fleet decides it (#2390 follow-up): an explicit
112
+ // defaults.locale, else the host's zone (UTC+8 → zh-TW), else English. A line another program reads (the updater, the
113
+ // Windows installer and shortcuts, the rehearsal scripts) is never a locale string: tests/cli-machine-lines-2390 pins them.
114
+ let configForLocale;
112
115
  try {
113
- const configuredLocale = loadRawFleetConfig(FLEET_CONFIG_PATH).defaults?.locale;
114
- if (configuredLocale === "en" || configuredLocale === "zh-TW")
115
- setLocale(configuredLocale);
116
+ configForLocale = loadRawFleetConfig(FLEET_CONFIG_PATH);
116
117
  }
117
- catch { /* malformed or missing config: keep the English CLI default */ }
118
+ catch { /* malformed or missing config: the zone decides */ }
119
+ setLocale(detectLocale(configForLocale));
118
120
  const program = new Command();
119
121
  function claimFleetSingleton() {
120
122
  try {
@@ -167,15 +169,15 @@ settingsCommand.command("confirm <id>").option("--yes", "Explicit noninteractive
167
169
  .action(async (id, options) => {
168
170
  try {
169
171
  const inspected = await requestSettingsConfirmation(DATA_DIR, { action: "inspect", id });
170
- console.log(`Source: ${inspected.pending_change.source}\nRequester: ${inspected.pending_change.requested_by}\n${inspected.pending_change.summary.join("\n")}`);
172
+ console.log([t("settings_confirm.source", settingsSourceLabel(inspected.pending_change.source)), t("settings_confirm.requester", inspected.pending_change.requested_by), ...inspected.pending_change.summary].join("\n"));
171
173
  let confirmed = options.yes === true;
172
174
  if (!confirmed) {
173
175
  if (!process.stdin.isTTY)
174
- throw new Error("Interactive confirmation required; --yes explicitly confirms the printed diff.");
176
+ throw new Error(t("settings_confirm.cli_interactive"));
175
177
  const { createInterface } = await import("node:readline/promises");
176
178
  const input = createInterface({ input: process.stdin, output: process.stdout });
177
179
  try {
178
- confirmed = (await input.question("Apply this exact change? [y/N] ")).trim().toLowerCase() === "y";
180
+ confirmed = (await input.question(t("settings_confirm.cli_question"))).trim().toLowerCase() === "y";
179
181
  }
180
182
  finally {
181
183
  input.close();
@@ -194,7 +196,7 @@ settingsCommand.command("confirm <id>").option("--yes", "Explicit noninteractive
194
196
  settingsCommand.command("reject <id>").description("Reject a pending Settings change on this host").action(async (id) => {
195
197
  try {
196
198
  const inspected = await requestSettingsConfirmation(DATA_DIR, { action: "inspect", id });
197
- console.log(`Source: ${inspected.pending_change.source}\nRequester: ${inspected.pending_change.requested_by}\n${inspected.pending_change.summary.join("\n")}`);
199
+ console.log([t("settings_confirm.source", settingsSourceLabel(inspected.pending_change.source)), t("settings_confirm.requester", inspected.pending_change.requested_by), ...inspected.pending_change.summary].join("\n"));
198
200
  const result = await requestSettingsConfirmation(DATA_DIR, { action: "reject", ticket: inspected.ticket });
199
201
  console.log(result.pending_change.outcome?.message ?? result.pending_change.state);
200
202
  }
@@ -232,7 +234,7 @@ fleet
232
234
  // #2356: the way out when the installed service cannot run here (never activated, no GUI session for launchd) — an
233
235
  // operator's own decision, as `restart --force` is; the fleet's singleton lock still refuses a second fleet.
234
236
  if (opts?.direct && !forceAllowed()) {
235
- console.error(" ✗ --direct is for an operator's own shell, not a fleet agent session or the fleet's own commands. Nothing was started.");
237
+ console.error(t("cli.direct_refused"));
236
238
  process.exit(1);
237
239
  }
238
240
  const direct = marked || !!opts?.direct;
@@ -367,7 +369,7 @@ fleet
367
369
  }
368
370
  }
369
371
  catch {
370
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
372
+ console.error(t("cli.fleet_unreachable", port));
371
373
  process.exit(1);
372
374
  }
373
375
  return;
@@ -421,7 +423,7 @@ fleet
421
423
  // under a Restart=on-failure unit the fleet stayed down (and this process, in an agent pane, died with the unit's
422
424
  // cgroup). It runs `agend restart` itself — this Node, this CLI — with the same gate, guard and exit code.
423
425
  const args = instance ? ["restart", instance] : ["restart", ...(opts?.yes ? ["--yes"] : [])];
424
- console.error(`⚠ \`agend fleet restart\` is deprecated and goes away in 2.4 — running \`agend ${args.join(" ")}\`.`);
426
+ console.error(t("cli.fleet_restart_deprecated", args.join(" ")));
425
427
  const self = selfCommand(args);
426
428
  const result = spawnSync(self.command, self.args, { stdio: "inherit" });
427
429
  process.exit(result.status ?? 1);
@@ -437,7 +439,7 @@ fleet
437
439
  .command("logs", { hidden: true })
438
440
  .description("Removed; use `agend logs`")
439
441
  .action(() => {
440
- console.error("`agend fleet logs` was removed. Use `agend logs`.");
442
+ console.error(t("cli.fleet_logs_removed"));
441
443
  process.exitCode = 1;
442
444
  });
443
445
  fleet
@@ -1381,7 +1383,7 @@ program
1381
1383
  // #2264 review: the old system copy is retired only for a confirmed 2.1-era layout — the copy this updater runs
1382
1384
  // from is a 2.1.x release (2.1's updater left it there). Never from "nvm" or "root" alone: a 2.2+ copy stays.
1383
1385
  if (verified.retireSystemCopy && !/^2\.1\./.test(pkgVersion)) {
1384
- console.log(` Note: the system install (v${pkgVersion}) is kept; only a 2.1-era copy is retired after a move to nvm.`);
1386
+ console.log(t("cli.system_install_kept", pkgVersion));
1385
1387
  delete verified.retireSystemCopy;
1386
1388
  }
1387
1389
  if (verified.retireSystemCopy) {
@@ -1654,7 +1656,7 @@ program
1654
1656
  };
1655
1657
  const viaInstaller = !needsSudo && targetVersion !== null && shipsInstaller();
1656
1658
  if (!needsSudo && !viaInstaller)
1657
- console.log(` ℹ ${pkg} has no installer of its own (a release from before #2264); this updater installs it.`);
1659
+ console.log(t("cli.no_own_installer", pkg));
1658
1660
  if (viaInstaller) {
1659
1661
  console.log(`\n Updating AgEnD v${pkgVersion} → v${targetVersion} (its own installer)...\n`);
1660
1662
  markUpdateInProgress(DATA_DIR);
@@ -1789,7 +1791,7 @@ program
1789
1791
  .option("--no-activate", "Write the service file without activating it")
1790
1792
  .action(async (opts) => {
1791
1793
  if (process.argv.includes("--activate")) {
1792
- console.error("`agend install --activate` was removed; service activation is the default.");
1794
+ console.error(t("cli.install_activate_removed"));
1793
1795
  }
1794
1796
  const { installService, activateService, detectPlatform, renderLaunchdPlist, launchdPlistPath, getServicePath, getSystemServicePath, serviceHomeFor } = await import("./service-installer.js");
1795
1797
  // #1631 B: an older updater (2.2.0-beta.1 and before) refreshes a hand-started fleet's service too, through this
@@ -1980,10 +1982,10 @@ program
1980
1982
  // #2356: the system unit first, as `agend start` and `agend restart` choose it; a SIGTERM would only stop the process.
1981
1983
  if (getSystemServicePath()) {
1982
1984
  if (stopSystemService()) {
1983
- console.log("Service stopped (system).");
1985
+ console.log(t("cli.system_service_stopped"));
1984
1986
  return;
1985
1987
  }
1986
- console.error("Failed to stop the system service: systemctl stop agend (run it as root).");
1988
+ console.error(t("cli.system_service_stop_failed"));
1987
1989
  process.exitCode = 1;
1988
1990
  return;
1989
1991
  }
@@ -2051,9 +2053,9 @@ program
2051
2053
  // A service file means the service manager owns this fleet. Falling back
2052
2054
  // to a detached process here creates a second fleet when the bus recovers.
2053
2055
  console.error("Failed to start the installed AgEnD service; refusing to start a detached duplicate.");
2054
- console.error(`Check your service manager, then retry: ${process.platform === "darwin" ? "launchctl print gui/$(id -u)/com.agend.fleet" : systemServicePath ? "systemctl status agend" : "systemctl --user status com.agend.fleet"}`);
2056
+ console.error(t("cli.service_retry", process.platform === "darwin" ? "launchctl print gui/$(id -u)/com.agend.fleet" : systemServicePath ? "systemctl status agend" : "systemctl --user status com.agend.fleet"));
2055
2057
  // #2356: a service file that cannot run here (never activated, no session for it) would otherwise leave no start.
2056
- console.error("If the service cannot run on this host, start the fleet without it: agend fleet start --direct");
2058
+ console.error(t("cli.service_direct_hint"));
2057
2059
  process.exitCode = 1;
2058
2060
  }
2059
2061
  });
@@ -2074,7 +2076,7 @@ function redirectToService(command, extra, directMarker) {
2074
2076
  const installed = getSystemServicePath() ?? getServicePath();
2075
2077
  if (!redirectsToService({ command, directMarker, byServiceManager, installedServicePath: installed }))
2076
2078
  return false;
2077
- console.error(`ℹ An AgEnD service is installed here (${installed}): running \`agend ${[command, ...extra].join(" ")}\` so the service manager owns the fleet.`);
2079
+ console.error(t("cli.service_redirect", String(installed), [command, ...extra].join(" ")));
2078
2080
  const self = selfCommand([command, ...extra]);
2079
2081
  const result = spawnSync(self.command, self.args, { stdio: "inherit" });
2080
2082
  process.exit(result.status ?? 1);
@@ -2097,15 +2099,15 @@ async function restartOneInstance(instance) {
2097
2099
  });
2098
2100
  const body = await resp.json();
2099
2101
  if (resp.ok) {
2100
- console.log(`Instance "${instance}" restarted (immediate)`);
2102
+ console.log(t("cli.instance_restarted", instance));
2101
2103
  }
2102
2104
  else {
2103
- console.error(`Restart failed: ${body.error ?? resp.statusText}`);
2105
+ console.error(t("cli.restart_failed", String(body.error ?? resp.statusText)));
2104
2106
  process.exit(1);
2105
2107
  }
2106
2108
  }
2107
2109
  catch {
2108
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
2110
+ console.error(t("cli.fleet_unreachable", port));
2109
2111
  process.exit(1);
2110
2112
  }
2111
2113
  }
@@ -2118,7 +2120,7 @@ program
2118
2120
  .action(async (instance, opts) => {
2119
2121
  if (instance) {
2120
2122
  if (opts.force) {
2121
- console.error("--force applies to the whole service, not one instance.");
2123
+ console.error(t("cli.force_whole_service"));
2122
2124
  process.exit(1);
2123
2125
  }
2124
2126
  await restartOneInstance(instance);
@@ -2304,7 +2306,7 @@ program
2304
2306
  .filter(([key, state]) => key !== "KillMode" && (state === "added" || state === "upgraded"))
2305
2307
  .map(([key]) => key);
2306
2308
  if (updated.length > 0)
2307
- console.log(` ✓ ${unitPath}: updated ${updated.join(", ")} (#908/#1071/#1113/#2331: graceful stop, small crash dumps, bounded start, start limit, the fleet's ready signal)`);
2309
+ console.log(t("cli.unit_updated", String(unitPath), updated.join(", ")));
2308
2310
  // #2331: what systemd LOADED — a drop-in that refuses the fleet's READY is overridden by AgEnD's own, leftovers
2309
2311
  // are named. Not a gate: the fleet still restarts, and the line says what will happen and what to remove.
2310
2312
  for (const line of loadedUnitCheck({ user: systemdTarget.user, unit: systemdTarget.unit, unitPath }))
@@ -2379,13 +2381,10 @@ program
2379
2381
  if (printed.status === 0) {
2380
2382
  const plistPath = getServicePath() ?? join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
2381
2383
  // Path 2 (#1450 C6): a proven new plist waits on disk with its plan — exactly one bootout + bootstrap.
2382
- const { readPlan, admitPlan, planPath, planCarriedOut } = await import("./service-plan.js");
2383
- const recorded = opts.force ? null : readPlan(DATA_DIR, deps);
2384
- // #1871: an interrupted update's plan whose job launchd already runs (after `agend start`) is done: restart that
2385
- // job as an unchanged one (path 1) instead of refusing it for its new pid.
2386
- const plan = recorded && planCarriedOut(recorded, target, deps) ? null : recorded;
2387
- if (recorded && !plan)
2388
- console.log(" ℹ launchd already runs the job an interrupted update planned; restarting it as it is.");
2384
+ const { pendingPlan, admitPlan, planPath } = await import("./service-plan.js");
2385
+ // #1871: a plan whose job launchd already runs (an interrupted update, then `agend start`) is done: restart that
2386
+ // job as an unchanged one (path 1) instead of refusing it for its new pid. #2375: quietly (no note on every restart).
2387
+ const plan = opts.force ? null : pendingPlan(DATA_DIR, target, deps);
2389
2388
  if (plan && expectation.ok) {
2390
2389
  const pkgDir = dirname(dirname(expectation.expected.entry));
2391
2390
  const admitted = admitPlan(plan, { target, expected: expectation.expected, pkgDir }, deps);
@@ -2713,7 +2712,7 @@ program
2713
2712
  minutes = Math.max(1, Math.round(((body.expiresAt ?? Date.now()) - Date.now()) / 60_000));
2714
2713
  }
2715
2714
  catch {
2716
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
2715
+ console.error(t("cli.fleet_unreachable", port));
2717
2716
  process.exit(1);
2718
2717
  }
2719
2718
  const signin = `http://localhost:${port}/signin`;
@@ -2988,7 +2987,7 @@ schedule
2988
2987
  .command("trigger", { hidden: true })
2989
2988
  .argument("[id]", "Schedule ID")
2990
2989
  .action(() => {
2991
- console.error("`agend schedule trigger` was removed. There is no Telegram trigger; use `agend schedule enable` or the Task Board.");
2990
+ console.error(t("cli.schedule_trigger_removed"));
2992
2991
  process.exitCode = 1;
2993
2992
  });
2994
2993
  // === Chat Export ===