@made-by-moonlight/athene-cli 0.9.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.
- package/LICENSE +22 -0
- package/dist/assets/plugin-registry.json +67 -0
- package/dist/assets/scripts/athene-doctor.ps1 +352 -0
- package/dist/assets/scripts/athene-doctor.sh +552 -0
- package/dist/assets/scripts/athene-update.ps1 +224 -0
- package/dist/assets/scripts/athene-update.sh +252 -0
- package/dist/commands/completion.d.ts +3 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +26 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +89 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +103 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +329 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/events.d.ts +3 -0
- package/dist/commands/events.d.ts.map +1 -0
- package/dist/commands/events.js +172 -0
- package/dist/commands/events.js.map +1 -0
- package/dist/commands/migrate-storage.d.ts +3 -0
- package/dist/commands/migrate-storage.d.ts.map +1 -0
- package/dist/commands/migrate-storage.js +78 -0
- package/dist/commands/migrate-storage.js.map +1 -0
- package/dist/commands/notify.d.ts +3 -0
- package/dist/commands/notify.d.ts.map +1 -0
- package/dist/commands/notify.js +143 -0
- package/dist/commands/notify.js.map +1 -0
- package/dist/commands/open.d.ts +3 -0
- package/dist/commands/open.d.ts.map +1 -0
- package/dist/commands/open.js +167 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/plugin.d.ts +3 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +462 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +143 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/report.d.ts +19 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +114 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/review-check.d.ts +3 -0
- package/dist/commands/review-check.d.ts.map +1 -0
- package/dist/commands/review-check.js +122 -0
- package/dist/commands/review-check.js.map +1 -0
- package/dist/commands/review.d.ts +3 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +215 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/send.d.ts +3 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +187 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/session.d.ts +3 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +439 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +297 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/spawn.d.ts +4 -0
- package/dist/commands/spawn.d.ts.map +1 -0
- package/dist/commands/spawn.js +436 -0
- package/dist/commands/spawn.js.map +1 -0
- package/dist/commands/start.d.ts +21 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +1836 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +556 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +15 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +652 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +131 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/bun-tmp-janitor.d.ts +18 -0
- package/dist/lib/bun-tmp-janitor.d.ts.map +1 -0
- package/dist/lib/bun-tmp-janitor.js +127 -0
- package/dist/lib/bun-tmp-janitor.js.map +1 -0
- package/dist/lib/caller-context.d.ts +13 -0
- package/dist/lib/caller-context.d.ts.map +1 -0
- package/dist/lib/caller-context.js +20 -0
- package/dist/lib/caller-context.js.map +1 -0
- package/dist/lib/cli-errors.d.ts +8 -0
- package/dist/lib/cli-errors.d.ts.map +1 -0
- package/dist/lib/cli-errors.js +20 -0
- package/dist/lib/cli-errors.js.map +1 -0
- package/dist/lib/completion.d.ts +13 -0
- package/dist/lib/completion.d.ts.map +1 -0
- package/dist/lib/completion.js +428 -0
- package/dist/lib/completion.js.map +1 -0
- package/dist/lib/composio-setup.d.ts +65 -0
- package/dist/lib/composio-setup.d.ts.map +1 -0
- package/dist/lib/composio-setup.js +3255 -0
- package/dist/lib/composio-setup.js.map +1 -0
- package/dist/lib/config-instruction.d.ts +2 -0
- package/dist/lib/config-instruction.d.ts.map +1 -0
- package/dist/lib/config-instruction.js +193 -0
- package/dist/lib/config-instruction.js.map +1 -0
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +3 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/create-session-manager.d.ts +26 -0
- package/dist/lib/create-session-manager.d.ts.map +1 -0
- package/dist/lib/create-session-manager.js +55 -0
- package/dist/lib/create-session-manager.js.map +1 -0
- package/dist/lib/credential-resolver.d.ts +37 -0
- package/dist/lib/credential-resolver.d.ts.map +1 -0
- package/dist/lib/credential-resolver.js +105 -0
- package/dist/lib/credential-resolver.js.map +1 -0
- package/dist/lib/daemon.d.ts +69 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/daemon.js +77 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/dashboard-rebuild.d.ts +53 -0
- package/dist/lib/dashboard-rebuild.d.ts.map +1 -0
- package/dist/lib/dashboard-rebuild.js +188 -0
- package/dist/lib/dashboard-rebuild.js.map +1 -0
- package/dist/lib/dashboard-setup.d.ts +14 -0
- package/dist/lib/dashboard-setup.d.ts.map +1 -0
- package/dist/lib/dashboard-setup.js +192 -0
- package/dist/lib/dashboard-setup.js.map +1 -0
- package/dist/lib/dashboard-url.d.ts +19 -0
- package/dist/lib/dashboard-url.d.ts.map +1 -0
- package/dist/lib/dashboard-url.js +25 -0
- package/dist/lib/dashboard-url.js.map +1 -0
- package/dist/lib/desktop-setup.d.ts +21 -0
- package/dist/lib/desktop-setup.d.ts.map +1 -0
- package/dist/lib/desktop-setup.js +556 -0
- package/dist/lib/desktop-setup.js.map +1 -0
- package/dist/lib/detect-agent.d.ts +24 -0
- package/dist/lib/detect-agent.d.ts.map +1 -0
- package/dist/lib/detect-agent.js +69 -0
- package/dist/lib/detect-agent.js.map +1 -0
- package/dist/lib/detect-env.d.ts +14 -0
- package/dist/lib/detect-env.d.ts.map +1 -0
- package/dist/lib/detect-env.js +46 -0
- package/dist/lib/detect-env.js.map +1 -0
- package/dist/lib/discord-setup.d.ts +20 -0
- package/dist/lib/discord-setup.d.ts.map +1 -0
- package/dist/lib/discord-setup.js +584 -0
- package/dist/lib/discord-setup.js.map +1 -0
- package/dist/lib/format.d.ts +11 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +116 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/git-utils.d.ts +14 -0
- package/dist/lib/git-utils.d.ts.map +1 -0
- package/dist/lib/git-utils.js +45 -0
- package/dist/lib/git-utils.js.map +1 -0
- package/dist/lib/install-helpers.d.ts +24 -0
- package/dist/lib/install-helpers.d.ts.map +1 -0
- package/dist/lib/install-helpers.js +76 -0
- package/dist/lib/install-helpers.js.map +1 -0
- package/dist/lib/lifecycle-service.d.ts +11 -0
- package/dist/lib/lifecycle-service.d.ts.map +1 -0
- package/dist/lib/lifecycle-service.js +65 -0
- package/dist/lib/lifecycle-service.js.map +1 -0
- package/dist/lib/notifier-routing.d.ts +35 -0
- package/dist/lib/notifier-routing.d.ts.map +1 -0
- package/dist/lib/notifier-routing.js +133 -0
- package/dist/lib/notifier-routing.js.map +1 -0
- package/dist/lib/notify-test.d.ts +72 -0
- package/dist/lib/notify-test.d.ts.map +1 -0
- package/dist/lib/notify-test.js +674 -0
- package/dist/lib/notify-test.js.map +1 -0
- package/dist/lib/openclaw-probe.d.ts +38 -0
- package/dist/lib/openclaw-probe.d.ts.map +1 -0
- package/dist/lib/openclaw-probe.js +146 -0
- package/dist/lib/openclaw-probe.js.map +1 -0
- package/dist/lib/openclaw-setup.d.ts +19 -0
- package/dist/lib/openclaw-setup.d.ts.map +1 -0
- package/dist/lib/openclaw-setup.js +684 -0
- package/dist/lib/openclaw-setup.js.map +1 -0
- package/dist/lib/path-equality.d.ts +29 -0
- package/dist/lib/path-equality.d.ts.map +1 -0
- package/dist/lib/path-equality.js +52 -0
- package/dist/lib/path-equality.js.map +1 -0
- package/dist/lib/plugin-marketplace.d.ts +24 -0
- package/dist/lib/plugin-marketplace.d.ts.map +1 -0
- package/dist/lib/plugin-marketplace.js +175 -0
- package/dist/lib/plugin-marketplace.js.map +1 -0
- package/dist/lib/plugin-scaffold.d.ts +14 -0
- package/dist/lib/plugin-scaffold.d.ts.map +1 -0
- package/dist/lib/plugin-scaffold.js +174 -0
- package/dist/lib/plugin-scaffold.js.map +1 -0
- package/dist/lib/plugin-store.d.ts +9 -0
- package/dist/lib/plugin-store.d.ts.map +1 -0
- package/dist/lib/plugin-store.js +121 -0
- package/dist/lib/plugin-store.js.map +1 -0
- package/dist/lib/plugins.d.ts +17 -0
- package/dist/lib/plugins.d.ts.map +1 -0
- package/dist/lib/plugins.js +65 -0
- package/dist/lib/plugins.js.map +1 -0
- package/dist/lib/portfolio-display.d.ts +10 -0
- package/dist/lib/portfolio-display.d.ts.map +1 -0
- package/dist/lib/portfolio-display.js +17 -0
- package/dist/lib/portfolio-display.js.map +1 -0
- package/dist/lib/preflight.d.ts +27 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +77 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/prevent-sleep.d.ts +34 -0
- package/dist/lib/prevent-sleep.d.ts.map +1 -0
- package/dist/lib/prevent-sleep.js +65 -0
- package/dist/lib/prevent-sleep.js.map +1 -0
- package/dist/lib/project-detection.d.ts +11 -0
- package/dist/lib/project-detection.d.ts.map +1 -0
- package/dist/lib/project-detection.js +206 -0
- package/dist/lib/project-detection.js.map +1 -0
- package/dist/lib/project-resolution.d.ts +10 -0
- package/dist/lib/project-resolution.d.ts.map +1 -0
- package/dist/lib/project-resolution.js +17 -0
- package/dist/lib/project-resolution.js.map +1 -0
- package/dist/lib/project-supervisor.d.ts +28 -0
- package/dist/lib/project-supervisor.d.ts.map +1 -0
- package/dist/lib/project-supervisor.js +167 -0
- package/dist/lib/project-supervisor.js.map +1 -0
- package/dist/lib/prompts.d.ts +7 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +37 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/repo-utils.d.ts +16 -0
- package/dist/lib/repo-utils.d.ts.map +1 -0
- package/dist/lib/repo-utils.js +26 -0
- package/dist/lib/repo-utils.js.map +1 -0
- package/dist/lib/resolve-project.d.ts +113 -0
- package/dist/lib/resolve-project.d.ts.map +1 -0
- package/dist/lib/resolve-project.js +433 -0
- package/dist/lib/resolve-project.js.map +1 -0
- package/dist/lib/routes.d.ts +2 -0
- package/dist/lib/routes.d.ts.map +1 -0
- package/dist/lib/routes.js +5 -0
- package/dist/lib/routes.js.map +1 -0
- package/dist/lib/running-state.d.ts +76 -0
- package/dist/lib/running-state.d.ts.map +1 -0
- package/dist/lib/running-state.js +338 -0
- package/dist/lib/running-state.js.map +1 -0
- package/dist/lib/script-runner.d.ts +10 -0
- package/dist/lib/script-runner.d.ts.map +1 -0
- package/dist/lib/script-runner.js +189 -0
- package/dist/lib/script-runner.js.map +1 -0
- package/dist/lib/session-utils.d.ts +14 -0
- package/dist/lib/session-utils.d.ts.map +1 -0
- package/dist/lib/session-utils.js +58 -0
- package/dist/lib/session-utils.js.map +1 -0
- package/dist/lib/shell.d.ts +17 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +90 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/shutdown.d.ts +30 -0
- package/dist/lib/shutdown.d.ts.map +1 -0
- package/dist/lib/shutdown.js +177 -0
- package/dist/lib/shutdown.js.map +1 -0
- package/dist/lib/slack-setup.d.ts +17 -0
- package/dist/lib/slack-setup.d.ts.map +1 -0
- package/dist/lib/slack-setup.js +485 -0
- package/dist/lib/slack-setup.js.map +1 -0
- package/dist/lib/startup-preflight.d.ts +36 -0
- package/dist/lib/startup-preflight.d.ts.map +1 -0
- package/dist/lib/startup-preflight.js +273 -0
- package/dist/lib/startup-preflight.js.map +1 -0
- package/dist/lib/update-channel-onboarding.d.ts +52 -0
- package/dist/lib/update-channel-onboarding.d.ts.map +1 -0
- package/dist/lib/update-channel-onboarding.js +107 -0
- package/dist/lib/update-channel-onboarding.js.map +1 -0
- package/dist/lib/update-check.d.ts +161 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +504 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/web-dir.d.ts +47 -0
- package/dist/lib/web-dir.d.ts.map +1 -0
- package/dist/lib/web-dir.js +179 -0
- package/dist/lib/web-dir.js.map +1 -0
- package/dist/lib/webhook-setup.d.ts +16 -0
- package/dist/lib/webhook-setup.d.ts.map +1 -0
- package/dist/lib/webhook-setup.js +383 -0
- package/dist/lib/webhook-setup.js.map +1 -0
- package/dist/options/version.d.ts +2 -0
- package/dist/options/version.d.ts.map +1 -0
- package/dist/options/version.js +7 -0
- package/dist/options/version.js.map +1 -0
- package/dist/program.d.ts +3 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +63 -0
- package/dist/program.js.map +1 -0
- package/package.json +80 -0
- package/templates/rules/base.md +4 -0
- package/templates/rules/go.md +8 -0
- package/templates/rules/javascript.md +4 -0
- package/templates/rules/nextjs.md +7 -0
- package/templates/rules/pnpm-workspaces.md +4 -0
- package/templates/rules/python.md +8 -0
- package/templates/rules/react.md +8 -0
- package/templates/rules/typescript.md +6 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { getGlobalConfigPath, isCanonicalGlobalConfigPath, isWindows, loadConfig, loadGlobalConfig, recordActivityEvent, } from "@made-by-moonlight/athene-core";
|
|
5
|
+
import { runRepoScript } from "../lib/script-runner.js";
|
|
6
|
+
import { checkForUpdate, detectInstallMethod, getCurrentVersion, getUpdateCommand, invalidateCache, readCachedUpdateInfo, resolveUpdateChannel, } from "../lib/update-check.js";
|
|
7
|
+
import { promptConfirm } from "../lib/prompts.js";
|
|
8
|
+
import { getSessionManager } from "../lib/create-session-manager.js";
|
|
9
|
+
import { getRunning } from "../lib/running-state.js";
|
|
10
|
+
/** Inline check instead of module-level constant so tests can control TTY state. */
|
|
11
|
+
function isTTY() {
|
|
12
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The dashboard's POST /api/update spawns `athene update` with `stdio: "ignore"`,
|
|
16
|
+
* which makes `isTTY()` return false. That used to be fatal: handleNpmUpdate
|
|
17
|
+
* fell into the "non-interactive — print and return" branch and never invoked
|
|
18
|
+
* the install. The route would respond 202 "started" and absolutely nothing
|
|
19
|
+
* would happen.
|
|
20
|
+
*
|
|
21
|
+
* /api/update sets `AO_NON_INTERACTIVE_INSTALL=1` on the spawn env so we can
|
|
22
|
+
* distinguish "API kicked this off, please install without prompting" from
|
|
23
|
+
* "user piped output and we shouldn't surprise-install."
|
|
24
|
+
*/
|
|
25
|
+
export const NON_INTERACTIVE_INSTALL_ENV = "AO_NON_INTERACTIVE_INSTALL";
|
|
26
|
+
function isApiInvoked() {
|
|
27
|
+
return process.env[NON_INTERACTIVE_INSTALL_ENV] === "1";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Statuses that mean "the agent is doing real work right now and updating
|
|
31
|
+
* `ao` would yank the rug out from under it."
|
|
32
|
+
*
|
|
33
|
+
* Mirrors the design doc (release-process.html §07): refuse, never auto-stop.
|
|
34
|
+
*/
|
|
35
|
+
const ACTIVE_SESSION_STATUSES = new Set([
|
|
36
|
+
"working",
|
|
37
|
+
"idle",
|
|
38
|
+
"needs_input",
|
|
39
|
+
"stuck",
|
|
40
|
+
]);
|
|
41
|
+
export function registerUpdate(program) {
|
|
42
|
+
program
|
|
43
|
+
.command("update")
|
|
44
|
+
.description("Check for updates and upgrade AO to the latest version")
|
|
45
|
+
.option("--skip-smoke", "Skip smoke tests after rebuilding (git installs only)")
|
|
46
|
+
.option("--smoke-only", "Run smoke tests without fetching or rebuilding (git installs only)")
|
|
47
|
+
.option("--check", "Print version info as JSON without upgrading")
|
|
48
|
+
.option("--no-restore", "Restart AO after updating but do not restore stopped sessions")
|
|
49
|
+
.action(async (opts) => {
|
|
50
|
+
if (opts.skipSmoke && opts.smokeOnly) {
|
|
51
|
+
console.error("`athene update` does not allow `--skip-smoke` together with `--smoke-only`.");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
if (opts.check) {
|
|
55
|
+
await handleCheck();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const method = detectInstallMethod();
|
|
59
|
+
recordActivityEvent({
|
|
60
|
+
source: "cli",
|
|
61
|
+
kind: "cli.update_invoked",
|
|
62
|
+
level: "info",
|
|
63
|
+
summary: `athene update invoked (method: ${method})`,
|
|
64
|
+
data: { method, options: opts },
|
|
65
|
+
});
|
|
66
|
+
// Reject git-only flags up front when the install isn't a git source.
|
|
67
|
+
// Without this, users copy/pasting `athene update --skip-smoke` from older
|
|
68
|
+
// docs would silently no-op on npm/pnpm/bun installs (the flag would be
|
|
69
|
+
// accepted, ignored, and the user would never know why smoke tests
|
|
70
|
+
// didn't run — because they never ran on these install methods anyway).
|
|
71
|
+
if ((opts.skipSmoke || opts.smokeOnly) && method !== "git") {
|
|
72
|
+
const flag = opts.skipSmoke ? "--skip-smoke" : "--smoke-only";
|
|
73
|
+
console.error(`${flag} only applies to git installs (current install: ${method}).`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
switch (method) {
|
|
77
|
+
case "git":
|
|
78
|
+
await handleGitUpdate(opts);
|
|
79
|
+
break;
|
|
80
|
+
case "homebrew":
|
|
81
|
+
await handleHomebrewUpdate();
|
|
82
|
+
break;
|
|
83
|
+
case "npm-global":
|
|
84
|
+
case "pnpm-global":
|
|
85
|
+
case "bun-global":
|
|
86
|
+
await handleNpmUpdate(method, { restore: opts.restore !== false });
|
|
87
|
+
break;
|
|
88
|
+
case "unknown":
|
|
89
|
+
await handleUnknownUpdate();
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// --check
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
async function handleCheck() {
|
|
98
|
+
const info = await checkForUpdate({ force: true });
|
|
99
|
+
console.log(JSON.stringify(info, null, 2));
|
|
100
|
+
}
|
|
101
|
+
async function getUpdateLifecyclePlan() {
|
|
102
|
+
let sessions;
|
|
103
|
+
let configPath;
|
|
104
|
+
let primaryProjectId;
|
|
105
|
+
let runningBeforeUpdate = false;
|
|
106
|
+
try {
|
|
107
|
+
// Live signal first: running.json lists whichever projects the active
|
|
108
|
+
// `athene start` daemon is currently polling. That can include local-only
|
|
109
|
+
// projects whose `agent-orchestrator.yaml` is NOT in the global registry
|
|
110
|
+
// (Dhruv edge case: user runs `athene start` from a repo with a local config
|
|
111
|
+
// and no global registration — sessions live on disk, would be clobbered
|
|
112
|
+
// if `athene update` proceeded).
|
|
113
|
+
//
|
|
114
|
+
// If a daemon is running, trust its configPath — it's the source of
|
|
115
|
+
// truth for "which sessions does the running ao instance own?"
|
|
116
|
+
const running = await getRunning();
|
|
117
|
+
if (running && running.projects.length > 0) {
|
|
118
|
+
runningBeforeUpdate = true;
|
|
119
|
+
configPath = running.configPath;
|
|
120
|
+
primaryProjectId = running.projects[0];
|
|
121
|
+
// running.configPath could be local-wrapped (a project's
|
|
122
|
+
// agent-orchestrator.yaml) OR the canonical global path. loadConfig
|
|
123
|
+
// dispatches based on the path shape — both cases produce a full
|
|
124
|
+
// OrchestratorConfig the SessionManager can enumerate.
|
|
125
|
+
const config = loadConfig(running.configPath);
|
|
126
|
+
const sm = await getSessionManager(config);
|
|
127
|
+
sessions = await sm.list();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// No live daemon. Fall back to the global registry — covers the case
|
|
131
|
+
// where the user ran `athene stop` (running.json gone) but stale sessions
|
|
132
|
+
// sit on disk under ~/.agent-orchestrator/{hash}-{projectId}/. The
|
|
133
|
+
// SessionManager's enrichment will reconcile any stale-runtime
|
|
134
|
+
// sessions to `killed`, so terminal statuses don't block the update.
|
|
135
|
+
const globalPath = getGlobalConfigPath();
|
|
136
|
+
if (!existsSync(globalPath)) {
|
|
137
|
+
return { runningBeforeUpdate, configPath, primaryProjectId, activeSessions: [] };
|
|
138
|
+
}
|
|
139
|
+
const globalConfig = loadGlobalConfig(globalPath);
|
|
140
|
+
if (!globalConfig || Object.keys(globalConfig.projects).length === 0) {
|
|
141
|
+
return { runningBeforeUpdate, configPath, primaryProjectId, activeSessions: [] };
|
|
142
|
+
}
|
|
143
|
+
if (!isCanonicalGlobalConfigPath(globalPath)) {
|
|
144
|
+
return { runningBeforeUpdate, configPath, primaryProjectId, activeSessions: [] };
|
|
145
|
+
}
|
|
146
|
+
configPath = globalPath;
|
|
147
|
+
const config = loadConfig(globalPath);
|
|
148
|
+
primaryProjectId = Object.keys(config.projects)[0];
|
|
149
|
+
const sm = await getSessionManager(config);
|
|
150
|
+
sessions = await sm.list();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// If we can't enumerate sessions, don't pretend there are zero — but
|
|
155
|
+
// also don't block the upgrade indefinitely. Surface a soft warning.
|
|
156
|
+
console.error(chalk.yellow("⚠ Could not check for active sessions before updating. Proceeding anyway."));
|
|
157
|
+
return { runningBeforeUpdate, configPath, primaryProjectId, activeSessions: [] };
|
|
158
|
+
}
|
|
159
|
+
const active = sessions.filter((s) => ACTIVE_SESSION_STATUSES.has(s.status));
|
|
160
|
+
return { runningBeforeUpdate, configPath, primaryProjectId, activeSessions: active };
|
|
161
|
+
}
|
|
162
|
+
async function pauseAoForUpdate(plan) {
|
|
163
|
+
const shouldStop = plan.runningBeforeUpdate || plan.activeSessions.length > 0;
|
|
164
|
+
if (!shouldStop)
|
|
165
|
+
return false;
|
|
166
|
+
if (plan.activeSessions.length > 0) {
|
|
167
|
+
const noun = plan.activeSessions.length === 1 ? "session" : "sessions";
|
|
168
|
+
console.log(chalk.yellow(`\n${plan.activeSessions.length} active ${noun} will be paused and restored after the update.`));
|
|
169
|
+
for (const s of plan.activeSessions.slice(0, 5)) {
|
|
170
|
+
console.log(chalk.dim(` • ${s.id} (${s.status})`));
|
|
171
|
+
}
|
|
172
|
+
if (plan.activeSessions.length > 5) {
|
|
173
|
+
console.log(chalk.dim(` … and ${plan.activeSessions.length - 5} more`));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log(chalk.dim("\nAO is running; it will be restarted after the update."));
|
|
178
|
+
}
|
|
179
|
+
const stopExit = await runAoLifecycleCommand(["stop", "--yes"], {
|
|
180
|
+
configPath: plan.configPath,
|
|
181
|
+
});
|
|
182
|
+
if (stopExit !== 0) {
|
|
183
|
+
recordActivityEvent({
|
|
184
|
+
source: "cli",
|
|
185
|
+
kind: "cli.update_failed",
|
|
186
|
+
level: "error",
|
|
187
|
+
summary: `athene update failed: internal athene stop exited non-zero`,
|
|
188
|
+
data: { exitCode: stopExit },
|
|
189
|
+
});
|
|
190
|
+
console.error(chalk.red(`\nAO update could not stop the running daemon (exit ${stopExit}).`));
|
|
191
|
+
process.exit(stopExit);
|
|
192
|
+
}
|
|
193
|
+
const afterStop = await getUpdateLifecyclePlan();
|
|
194
|
+
if (afterStop.runningBeforeUpdate || afterStop.activeSessions.length > 0) {
|
|
195
|
+
recordActivityEvent({
|
|
196
|
+
source: "cli",
|
|
197
|
+
kind: "cli.update_failed",
|
|
198
|
+
level: "error",
|
|
199
|
+
summary: `athene update failed: AO still appears active after internal athene stop`,
|
|
200
|
+
data: {
|
|
201
|
+
runningAfterStop: afterStop.runningBeforeUpdate,
|
|
202
|
+
activeSessionCount: afterStop.activeSessions.length,
|
|
203
|
+
activeSessionIds: afterStop.activeSessions.map((s) => s.id).slice(0, 20),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
console.error(chalk.red("\nAO update stopped before installing because AO still appears to be running after `athene stop --yes`."));
|
|
207
|
+
if (afterStop.activeSessions.length > 0) {
|
|
208
|
+
console.error(chalk.dim("Still-active sessions:"));
|
|
209
|
+
for (const s of afterStop.activeSessions.slice(0, 5)) {
|
|
210
|
+
console.error(chalk.dim(` • ${s.id} (${s.status})`));
|
|
211
|
+
}
|
|
212
|
+
if (afterStop.activeSessions.length > 5) {
|
|
213
|
+
console.error(chalk.dim(` … and ${afterStop.activeSessions.length - 5} more`));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
console.error(chalk.dim("Run `athene stop` and retry `athene update` after AO is fully stopped."));
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
return plan.runningBeforeUpdate;
|
|
220
|
+
}
|
|
221
|
+
async function restartAoAfterUpdate(plan, opts) {
|
|
222
|
+
const args = ["start"];
|
|
223
|
+
if (plan.primaryProjectId)
|
|
224
|
+
args.push(plan.primaryProjectId);
|
|
225
|
+
args.push(opts.restore ? "--restore" : "--no-restore");
|
|
226
|
+
console.log(chalk.dim(`\nRestarting AO: ao ${args.join(" ")}`));
|
|
227
|
+
const exitCode = await runAoLifecycleCommand(args, { configPath: plan.configPath });
|
|
228
|
+
if (exitCode !== 0) {
|
|
229
|
+
recordActivityEvent({
|
|
230
|
+
source: "cli",
|
|
231
|
+
kind: "cli.update_restart_failed",
|
|
232
|
+
level: "error",
|
|
233
|
+
summary: `athene update could not restart AO after install`,
|
|
234
|
+
data: { exitCode, args },
|
|
235
|
+
});
|
|
236
|
+
console.error(chalk.yellow(`\nAO was updated, but \`ao ${args.join(" ")}\` failed with exit ${exitCode}. ` +
|
|
237
|
+
`Run it manually to restore your sessions.`));
|
|
238
|
+
process.exit(exitCode);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function runAoLifecycleCommand(args, opts = {}) {
|
|
242
|
+
return new Promise((resolveExit) => {
|
|
243
|
+
const child = spawn("athene", args, {
|
|
244
|
+
stdio: "inherit",
|
|
245
|
+
shell: isWindows(),
|
|
246
|
+
windowsHide: true,
|
|
247
|
+
env: opts.configPath ? { ...process.env, AO_CONFIG_PATH: opts.configPath } : process.env,
|
|
248
|
+
});
|
|
249
|
+
child.on("error", (error) => {
|
|
250
|
+
console.error(chalk.yellow(`Could not run ao ${args.join(" ")}: ${error.message}`));
|
|
251
|
+
resolveExit(1);
|
|
252
|
+
});
|
|
253
|
+
child.on("exit", (code, signal) => {
|
|
254
|
+
resolveExit(signal ? 1 : (code ?? 1));
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// git install
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
async function handleGitUpdate(opts) {
|
|
262
|
+
const lifecyclePlan = await getUpdateLifecyclePlan();
|
|
263
|
+
const shouldRestart = await pauseAoForUpdate(lifecyclePlan);
|
|
264
|
+
const args = [];
|
|
265
|
+
if (opts.skipSmoke)
|
|
266
|
+
args.push("--skip-smoke");
|
|
267
|
+
if (opts.smokeOnly)
|
|
268
|
+
args.push("--smoke-only");
|
|
269
|
+
try {
|
|
270
|
+
const exitCode = await runRepoScript("athene-update.sh", args);
|
|
271
|
+
if (exitCode !== 0) {
|
|
272
|
+
recordActivityEvent({
|
|
273
|
+
source: "cli",
|
|
274
|
+
kind: "cli.update_failed",
|
|
275
|
+
level: "error",
|
|
276
|
+
summary: `athene update (git) failed: athene-update.sh exited non-zero`,
|
|
277
|
+
data: { method: "git", exitCode },
|
|
278
|
+
});
|
|
279
|
+
if (shouldRestart) {
|
|
280
|
+
await restartAoAfterUpdate(lifecyclePlan, { restore: opts.restore !== false });
|
|
281
|
+
}
|
|
282
|
+
process.exit(exitCode);
|
|
283
|
+
}
|
|
284
|
+
invalidateCache();
|
|
285
|
+
if (shouldRestart) {
|
|
286
|
+
await restartAoAfterUpdate(lifecyclePlan, { restore: opts.restore !== false });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
if (error instanceof Error && error.message.includes("Script not found: athene-update.sh")) {
|
|
291
|
+
recordActivityEvent({
|
|
292
|
+
source: "cli",
|
|
293
|
+
kind: "cli.update_failed",
|
|
294
|
+
level: "error",
|
|
295
|
+
summary: `athene update (git) failed: athene-update.sh missing from bundled assets`,
|
|
296
|
+
data: { method: "git", reason: "script_missing" },
|
|
297
|
+
});
|
|
298
|
+
console.error(chalk.red("athene-update.sh is missing from the bundled assets. " +
|
|
299
|
+
"If you're running from a source checkout, rebuild with `pnpm --filter @made-by-moonlight/athene-cli build`. " +
|
|
300
|
+
"If you're on a package install, reinstall the package."));
|
|
301
|
+
if (shouldRestart) {
|
|
302
|
+
await restartAoAfterUpdate(lifecyclePlan, { restore: opts.restore !== false });
|
|
303
|
+
}
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
recordActivityEvent({
|
|
307
|
+
source: "cli",
|
|
308
|
+
kind: "cli.update_failed",
|
|
309
|
+
level: "error",
|
|
310
|
+
summary: `athene update (git) failed`,
|
|
311
|
+
data: {
|
|
312
|
+
method: "git",
|
|
313
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
317
|
+
if (shouldRestart) {
|
|
318
|
+
await restartAoAfterUpdate(lifecyclePlan, { restore: opts.restore !== false });
|
|
319
|
+
}
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
// npm / pnpm / bun global install
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
async function handleNpmUpdate(method, opts) {
|
|
327
|
+
const channel = resolveUpdateChannel();
|
|
328
|
+
// Snapshot the previously cached channel BEFORE we force a refresh, so we
|
|
329
|
+
// can detect a channel switch (stable→nightly or vice versa). force:true
|
|
330
|
+
// would overwrite cache.channel before we can read it.
|
|
331
|
+
const previousChannel = readCachedUpdateInfo(method)?.channel;
|
|
332
|
+
let info;
|
|
333
|
+
try {
|
|
334
|
+
info = await checkForUpdate({ force: true, channel });
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
recordActivityEvent({
|
|
338
|
+
source: "cli",
|
|
339
|
+
kind: "cli.update_failed",
|
|
340
|
+
level: "error",
|
|
341
|
+
summary: `athene update (${method}) failed: npm registry lookup threw`,
|
|
342
|
+
data: {
|
|
343
|
+
method,
|
|
344
|
+
channel,
|
|
345
|
+
reason: "registry_lookup_threw",
|
|
346
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
console.error(chalk.red("Could not reach npm registry. Check your network and try again."));
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
if (!info.latestVersion) {
|
|
353
|
+
recordActivityEvent({
|
|
354
|
+
source: "cli",
|
|
355
|
+
kind: "cli.update_failed",
|
|
356
|
+
level: "error",
|
|
357
|
+
summary: `athene update (${method}) failed: npm registry lookup returned no version`,
|
|
358
|
+
data: { method, channel, reason: "registry_unreachable" },
|
|
359
|
+
});
|
|
360
|
+
console.error(chalk.red("Could not reach npm registry. Check your network and try again."));
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
// Detect a channel switch. When stable=0.5.0 and nightly=0.5.0-nightly-abc,
|
|
364
|
+
// isVersionOutdated returns false (per semver, prerelease < stable on equal
|
|
365
|
+
// base), so a stable→nightly user would see "Already on latest nightly"
|
|
366
|
+
// until the next numeric bump. Force the prompt instead — explicit consent
|
|
367
|
+
// is the right UX for a channel transition, and the install command we'd
|
|
368
|
+
// run is genuinely different even if the version-compare says "no".
|
|
369
|
+
const isChannelSwitch = !info.isOutdated && previousChannel !== undefined && previousChannel !== channel;
|
|
370
|
+
// First-channel opt-in. previousChannel === undefined means we've never
|
|
371
|
+
// installed via the auto-updater. A user who just ran `athene config set
|
|
372
|
+
// updateChannel nightly` (after a manual install) would otherwise see
|
|
373
|
+
// "Already on latest nightly" because semver says prerelease < stable.
|
|
374
|
+
// Treat any version mismatch as install-worthy in that case.
|
|
375
|
+
const isFirstChannelOptIn = !info.isOutdated &&
|
|
376
|
+
!isChannelSwitch &&
|
|
377
|
+
previousChannel === undefined &&
|
|
378
|
+
info.currentVersion !== info.latestVersion;
|
|
379
|
+
const needsInstall = info.isOutdated || isChannelSwitch || isFirstChannelOptIn;
|
|
380
|
+
if (!needsInstall) {
|
|
381
|
+
console.log(chalk.green(`Already on latest ${channel === "nightly" ? "nightly" : "version"} (${info.currentVersion}).`));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
console.log(`Current version: ${chalk.dim(info.currentVersion)}`);
|
|
385
|
+
console.log(`Latest version: ${chalk.green(info.latestVersion)}`);
|
|
386
|
+
console.log(`Channel: ${chalk.cyan(channel)}`);
|
|
387
|
+
if (isChannelSwitch) {
|
|
388
|
+
console.log(chalk.yellow(`\nChannel switch detected: was on ${previousChannel}, now ${channel}.`));
|
|
389
|
+
console.log(chalk.dim(" The version compare says you're current, but the install command picks a different dist-tag."));
|
|
390
|
+
}
|
|
391
|
+
else if (isFirstChannelOptIn) {
|
|
392
|
+
console.log(chalk.yellow(`\nFirst install via the ${channel} channel — installing the channel's current build.`));
|
|
393
|
+
}
|
|
394
|
+
console.log();
|
|
395
|
+
const command = getUpdateCommand(method, channel);
|
|
396
|
+
const apiInvoked = isApiInvoked();
|
|
397
|
+
const interactive = isTTY() && !apiInvoked;
|
|
398
|
+
const lifecyclePlan = await getUpdateLifecyclePlan();
|
|
399
|
+
// Non-interactive path: API-invoked OR piped output. We still plan the
|
|
400
|
+
// stop/start lifecycle, but we never bail out just because there's no
|
|
401
|
+
// terminal — the dashboard's "Update" click must actually install. The
|
|
402
|
+
// only thing we skip is the confirm prompt.
|
|
403
|
+
if (interactive) {
|
|
404
|
+
// Soft auto-install: when the user has opted into stable or nightly we
|
|
405
|
+
// skip the confirm prompt — they've already said "keep me on this channel."
|
|
406
|
+
// Manual users (and explicit channel switches / first opt-ins) still see
|
|
407
|
+
// the confirm so an unintended `athene update` doesn't wipe the version they
|
|
408
|
+
// pinned to.
|
|
409
|
+
if (channel === "manual" || isChannelSwitch || isFirstChannelOptIn) {
|
|
410
|
+
const promptText = isChannelSwitch || isFirstChannelOptIn
|
|
411
|
+
? `Switch to ${channel} via ${chalk.cyan(command)}?`
|
|
412
|
+
: `Run ${chalk.cyan(command)}?`;
|
|
413
|
+
const confirmed = await promptConfirm(promptText, !(isChannelSwitch || isFirstChannelOptIn));
|
|
414
|
+
if (!confirmed)
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
console.log(chalk.dim(`Updating: ${command}`));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else if (apiInvoked) {
|
|
422
|
+
console.log(chalk.dim(`Updating (api-invoked): ${command}`));
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
// Non-TTY but also not API-invoked (piped output). Keep the old
|
|
426
|
+
// "print the command and let the user run it" behavior so a script
|
|
427
|
+
// running `athene update | tee` doesn't get a surprise install.
|
|
428
|
+
console.log(`Run: ${chalk.cyan(command)}`);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const shouldRestart = await pauseAoForUpdate(lifecyclePlan);
|
|
432
|
+
const installResult = await runNpmInstall(command);
|
|
433
|
+
if (installResult.exitCode !== 0) {
|
|
434
|
+
recordActivityEvent({
|
|
435
|
+
source: "cli",
|
|
436
|
+
kind: "cli.update_failed",
|
|
437
|
+
level: "error",
|
|
438
|
+
summary: `athene update (${method}) failed: install command exited non-zero`,
|
|
439
|
+
data: {
|
|
440
|
+
method,
|
|
441
|
+
command,
|
|
442
|
+
exitCode: installResult.exitCode,
|
|
443
|
+
classification: classifyInstallFailure(installResult.output).kind,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
printInstallFailure({
|
|
447
|
+
method,
|
|
448
|
+
command,
|
|
449
|
+
channel,
|
|
450
|
+
currentVersion: info.currentVersion,
|
|
451
|
+
exitCode: installResult.exitCode,
|
|
452
|
+
output: installResult.output,
|
|
453
|
+
});
|
|
454
|
+
if (shouldRestart) {
|
|
455
|
+
console.log(chalk.dim("\nRestarting AO with the existing installation..."));
|
|
456
|
+
await restartAoAfterUpdate(lifecyclePlan, opts);
|
|
457
|
+
}
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
const verification = await verifyInstalledVersion(info.latestVersion, info.currentVersion);
|
|
461
|
+
if (!verification.ok) {
|
|
462
|
+
recordActivityEvent({
|
|
463
|
+
source: "cli",
|
|
464
|
+
kind: "cli.update_failed",
|
|
465
|
+
level: "error",
|
|
466
|
+
summary: `athene update (${method}) failed: installed version verification failed`,
|
|
467
|
+
data: {
|
|
468
|
+
method,
|
|
469
|
+
command,
|
|
470
|
+
expectedVersion: info.latestVersion,
|
|
471
|
+
actualVersion: verification.actualVersion,
|
|
472
|
+
output: verification.output,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
console.error(chalk.red(`\nAO was not verified after install.`));
|
|
476
|
+
console.error(chalk.yellow(verification.message));
|
|
477
|
+
console.error(chalk.dim(`Expected: ${info.latestVersion}`));
|
|
478
|
+
console.error(chalk.dim(`Current before update: ${info.currentVersion}`));
|
|
479
|
+
if (shouldRestart) {
|
|
480
|
+
console.log(chalk.dim("\nRestarting AO before exiting..."));
|
|
481
|
+
await restartAoAfterUpdate(lifecyclePlan, opts);
|
|
482
|
+
}
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
invalidateCache();
|
|
486
|
+
if (shouldRestart) {
|
|
487
|
+
await restartAoAfterUpdate(lifecyclePlan, opts);
|
|
488
|
+
}
|
|
489
|
+
console.log(chalk.green(`\nUpdate complete: ${info.currentVersion} → ${verification.actualVersion}.` +
|
|
490
|
+
(shouldRestart ? " AO restarted." : "")));
|
|
491
|
+
}
|
|
492
|
+
function runNpmInstall(command) {
|
|
493
|
+
const [cmd, ...args] = command.split(" ");
|
|
494
|
+
return runCommandCapture(cmd, args, { echo: true }).then((result) => {
|
|
495
|
+
if (result.exitCode !== 0) {
|
|
496
|
+
console.error(chalk.yellow(`\n${cmd} exited with code ${result.exitCode}.`));
|
|
497
|
+
}
|
|
498
|
+
return result;
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
function runCommandCapture(cmd, args, opts = {}) {
|
|
502
|
+
return new Promise((resolveExit) => {
|
|
503
|
+
// `shell: isWindows()` is required so PATHEXT gets consulted on Windows —
|
|
504
|
+
// npm/pnpm/bun install as `*.cmd` shims, and Node.js does not look at
|
|
505
|
+
// PATHEXT for non-shell spawns, so a bare `npm` / `pnpm` / `bun` lookup
|
|
506
|
+
// would silently ENOENT on every Windows install. `windowsHide: true`
|
|
507
|
+
// keeps the shell window from flashing. Same fix that landed for the
|
|
508
|
+
// dashboard's /api/update spawn in commit 9f29131d.
|
|
509
|
+
const child = spawn(cmd, args, {
|
|
510
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
511
|
+
shell: isWindows(),
|
|
512
|
+
windowsHide: true,
|
|
513
|
+
});
|
|
514
|
+
let output = "";
|
|
515
|
+
const collect = (chunk, stream) => {
|
|
516
|
+
const text = chunk.toString();
|
|
517
|
+
output += text;
|
|
518
|
+
if (opts.echo)
|
|
519
|
+
stream.write(chunk);
|
|
520
|
+
};
|
|
521
|
+
child.stdout?.on("data", (chunk) => collect(chunk, process.stdout));
|
|
522
|
+
child.stderr?.on("data", (chunk) => collect(chunk, process.stderr));
|
|
523
|
+
child.on("error", (error) => {
|
|
524
|
+
output += `${error.name}: ${error.message}`;
|
|
525
|
+
resolveExit({ exitCode: 1, output });
|
|
526
|
+
});
|
|
527
|
+
child.on("exit", (code, signal) => {
|
|
528
|
+
if (signal) {
|
|
529
|
+
resolveExit({ exitCode: 1, output: `${output}\nTerminated by signal ${signal}` });
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
resolveExit({ exitCode: code ?? 1, output });
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
async function verifyInstalledVersion(expectedVersion, previousVersion) {
|
|
537
|
+
const result = await runCommandCapture("athene", ["--version"]);
|
|
538
|
+
const output = result.output.trim();
|
|
539
|
+
const actualVersion = parseAoVersion(output);
|
|
540
|
+
if (result.exitCode !== 0) {
|
|
541
|
+
return {
|
|
542
|
+
ok: false,
|
|
543
|
+
output,
|
|
544
|
+
message: `\`athene --version\` failed with exit ${result.exitCode}.`,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (!actualVersion) {
|
|
548
|
+
return {
|
|
549
|
+
ok: false,
|
|
550
|
+
output,
|
|
551
|
+
message: `Could not parse \`athene --version\` output: ${output || "<empty>"}`,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
if (actualVersion !== expectedVersion) {
|
|
555
|
+
return {
|
|
556
|
+
ok: false,
|
|
557
|
+
actualVersion,
|
|
558
|
+
output,
|
|
559
|
+
message: actualVersion === previousVersion
|
|
560
|
+
? `The install command exited successfully, but AO is still on ${previousVersion}.`
|
|
561
|
+
: `The install command exited successfully, but AO reports ${actualVersion}.`,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return { ok: true, actualVersion, output, message: "verified" };
|
|
565
|
+
}
|
|
566
|
+
function parseAoVersion(output) {
|
|
567
|
+
const match = output.match(/\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/);
|
|
568
|
+
return match?.[1];
|
|
569
|
+
}
|
|
570
|
+
function classifyInstallFailure(output) {
|
|
571
|
+
if (/ERR_PNPM_UNEXPECTED_VIRTUAL_STORE/i.test(output)) {
|
|
572
|
+
return {
|
|
573
|
+
kind: "pnpm_virtual_store",
|
|
574
|
+
guidance: "pnpm's global store metadata is inconsistent. Try `pnpm store prune`, then retry `athene update`. " +
|
|
575
|
+
"If pnpm remains stuck, use the npm fallback below.",
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
if (/(?:EACCES|EPERM|permission denied|access denied)/i.test(output)) {
|
|
579
|
+
return {
|
|
580
|
+
kind: "permission",
|
|
581
|
+
guidance: "The package manager could not write to the global install location. Fix your npm/pnpm global prefix permissions, or retry from a shell with access to that directory.",
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
if (/(?:ENETUNREACH|ECONNRESET|ETIMEDOUT|EAI_AGAIN|network|socket hang up)/i.test(output)) {
|
|
585
|
+
return {
|
|
586
|
+
kind: "network",
|
|
587
|
+
guidance: "The registry request failed due to a network error. Check connectivity/VPN/proxy settings and retry `athene update`.",
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
if (/(?:lockfile|ERR_PNPM_LOCKFILE|ERR_PNPM_OUTDATED_LOCKFILE|ERR_PNPM_BROKEN_LOCKFILE)/i.test(output)) {
|
|
591
|
+
return {
|
|
592
|
+
kind: "lockfile",
|
|
593
|
+
guidance: "pnpm reported lockfile state problems. Clear the affected global install metadata or retry with the npm fallback below.",
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (/(?:registry|ERR_PNPM_FETCH|ERR_PNPM_META_FETCH_FAIL|E401|E403|E404|404 Not Found|401 Unauthorized|403 Forbidden)/i.test(output)) {
|
|
597
|
+
return {
|
|
598
|
+
kind: "registry",
|
|
599
|
+
guidance: "The npm registry rejected or failed the package request. Check registry configuration, auth tokens, and the selected AO update channel.",
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
kind: "unknown",
|
|
604
|
+
guidance: "The package manager failed before AO could verify the new version. Retry `athene update` after addressing the package-manager error below.",
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function printInstallFailure(opts) {
|
|
608
|
+
const classification = classifyInstallFailure(opts.output);
|
|
609
|
+
const fallbackCommand = getUpdateCommand("npm-global", opts.channel);
|
|
610
|
+
console.error(chalk.red(`\nAO was not updated. You are still on version ${opts.currentVersion}.`));
|
|
611
|
+
console.error(chalk.yellow(`The package manager (${opts.method.replace("-global", "")}) failed with exit ${opts.exitCode}.`));
|
|
612
|
+
console.error(chalk.yellow(classification.guidance));
|
|
613
|
+
console.error(chalk.dim(`\nTo retry: athene update`));
|
|
614
|
+
if (opts.command !== fallbackCommand) {
|
|
615
|
+
console.error(chalk.dim(`You can also try: ${fallbackCommand}`));
|
|
616
|
+
}
|
|
617
|
+
console.error(chalk.dim("\nPackage manager output:"));
|
|
618
|
+
console.error(opts.output.trim() || "<no output>");
|
|
619
|
+
}
|
|
620
|
+
// ---------------------------------------------------------------------------
|
|
621
|
+
// homebrew install (notice only)
|
|
622
|
+
// ---------------------------------------------------------------------------
|
|
623
|
+
async function handleHomebrewUpdate() {
|
|
624
|
+
const channel = resolveUpdateChannel();
|
|
625
|
+
const info = await checkForUpdate({ force: true, channel });
|
|
626
|
+
console.log(`Installed via: ${chalk.yellow("Homebrew")}`);
|
|
627
|
+
console.log(`Current version: ${chalk.dim(info.currentVersion)}`);
|
|
628
|
+
if (info.latestVersion) {
|
|
629
|
+
console.log(`Latest version: ${chalk.green(info.latestVersion)}`);
|
|
630
|
+
}
|
|
631
|
+
console.log();
|
|
632
|
+
console.log(`Homebrew installs are managed by brew. Run:\n ${chalk.cyan("brew upgrade ao")}`);
|
|
633
|
+
console.log(chalk.dim(" (AO does not auto-install for brew installs because it would clobber brew's symlinks.)"));
|
|
634
|
+
}
|
|
635
|
+
// ---------------------------------------------------------------------------
|
|
636
|
+
// unknown install
|
|
637
|
+
// ---------------------------------------------------------------------------
|
|
638
|
+
async function handleUnknownUpdate() {
|
|
639
|
+
const version = getCurrentVersion();
|
|
640
|
+
const channel = resolveUpdateChannel();
|
|
641
|
+
const info = await checkForUpdate({ force: true, channel });
|
|
642
|
+
console.log(`Installed version: ${chalk.dim(version)}`);
|
|
643
|
+
if (info.latestVersion) {
|
|
644
|
+
console.log(`Latest version: ${chalk.green(info.latestVersion)}`);
|
|
645
|
+
}
|
|
646
|
+
console.log(`Install method: ${chalk.yellow("unknown")}`);
|
|
647
|
+
console.log(`Channel: ${chalk.cyan(channel)}`);
|
|
648
|
+
console.log();
|
|
649
|
+
console.log(`Could not detect install method. If you installed via npm, run:\n ${chalk.cyan(getUpdateCommand("npm-global", channel))}`);
|
|
650
|
+
console.log(chalk.dim(` Override detection in ~/.agent-orchestrator/config.yaml:\n installMethod: pnpm-global # or bun-global, npm-global, homebrew, git`));
|
|
651
|
+
}
|
|
652
|
+
//# sourceMappingURL=update.js.map
|