@agentage/cli 0.37.2 → 0.38.1

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 (109) hide show
  1. package/README.md +53 -83
  2. package/dist/cli.js +5 -8
  3. package/dist/commands/auth/setup.d.ts +6 -16
  4. package/dist/commands/auth/setup.js +9 -139
  5. package/dist/commands/status/status.d.ts +4 -3
  6. package/dist/commands/status/status.js +4 -27
  7. package/dist/commands/update/update.d.ts +3 -10
  8. package/dist/commands/update/update.js +24 -41
  9. package/dist/core/flag.d.ts +1 -0
  10. package/dist/core/flag.js +1 -0
  11. package/dist/lib/auth/api.d.ts +3 -2
  12. package/dist/lib/auth/api.js +50 -56
  13. package/dist/lib/auth/env-match.js +1 -0
  14. package/dist/lib/auth/introspect.js +10 -11
  15. package/dist/lib/auth/oauth.d.ts +2 -0
  16. package/dist/lib/auth/oauth.js +30 -28
  17. package/dist/lib/auth/sign-in.d.ts +20 -0
  18. package/dist/lib/auth/sign-in.js +95 -0
  19. package/dist/lib/fs/config.d.ts +1 -0
  20. package/dist/lib/fs/config.js +14 -21
  21. package/dist/lib/fs/file-lock.d.ts +7 -2
  22. package/dist/lib/fs/file-lock.js +64 -74
  23. package/dist/lib/fs/json-file.d.ts +3 -0
  24. package/dist/lib/fs/json-file.js +43 -0
  25. package/dist/lib/net/fetch.d.ts +10 -0
  26. package/dist/lib/net/fetch.js +13 -0
  27. package/dist/lib/net/http.js +51 -23
  28. package/dist/lib/net/origins.d.ts +0 -2
  29. package/dist/lib/net/origins.js +1 -5
  30. package/dist/lib/net/user-agent.d.ts +1 -6
  31. package/dist/lib/net/user-agent.js +3 -4
  32. package/dist/lib/npm/npm-cli.d.ts +17 -0
  33. package/dist/lib/npm/npm-cli.js +77 -0
  34. package/dist/lib/status/status-info.d.ts +0 -18
  35. package/dist/lib/status/status-info.js +2 -42
  36. package/dist/lib/update/update-cache.d.ts +1 -0
  37. package/dist/lib/update/update-cache.js +7 -11
  38. package/dist/lib/update/update-check.js +1 -4
  39. package/dist/plugin.d.ts +1 -0
  40. package/dist/plugin.js +2 -0
  41. package/dist/program.d.ts +18 -1
  42. package/dist/program.js +75 -44
  43. package/package.json +17 -12
  44. package/dist/commands/daemon/daemon-cmd.d.ts +0 -2
  45. package/dist/commands/daemon/daemon-cmd.js +0 -107
  46. package/dist/commands/memory/memory-verbs.d.ts +0 -21
  47. package/dist/commands/memory/memory-verbs.js +0 -98
  48. package/dist/commands/memory/memory.d.ts +0 -2
  49. package/dist/commands/memory/memory.js +0 -54
  50. package/dist/commands/vault/vault-sync.d.ts +0 -13
  51. package/dist/commands/vault/vault-sync.js +0 -90
  52. package/dist/commands/vault/vault.d.ts +0 -22
  53. package/dist/commands/vault/vault.js +0 -129
  54. package/dist/daemon/actions.d.ts +0 -5
  55. package/dist/daemon/actions.js +0 -22
  56. package/dist/daemon/client-provider.d.ts +0 -2
  57. package/dist/daemon/client-provider.js +0 -16
  58. package/dist/daemon/entry.d.ts +0 -8
  59. package/dist/daemon/entry.js +0 -113
  60. package/dist/daemon/guards.d.ts +0 -3
  61. package/dist/daemon/guards.js +0 -13
  62. package/dist/daemon/lifecycle.d.ts +0 -17
  63. package/dist/daemon/lifecycle.js +0 -151
  64. package/dist/daemon/mcp-http.d.ts +0 -3
  65. package/dist/daemon/mcp-http.js +0 -34
  66. package/dist/daemon/server.d.ts +0 -23
  67. package/dist/daemon/server.js +0 -135
  68. package/dist/daemon-entry.d.ts +0 -1
  69. package/dist/daemon-entry.js +0 -9
  70. package/dist/lib/auth/provision.d.ts +0 -14
  71. package/dist/lib/auth/provision.js +0 -49
  72. package/dist/lib/daemon/daemon-client.d.ts +0 -37
  73. package/dist/lib/daemon/daemon-client.js +0 -136
  74. package/dist/lib/daemon/daemon-pref.d.ts +0 -2
  75. package/dist/lib/daemon/daemon-pref.js +0 -10
  76. package/dist/lib/memory/memory-client.d.ts +0 -30
  77. package/dist/lib/memory/memory-client.js +0 -98
  78. package/dist/lib/status/format.d.ts +0 -1
  79. package/dist/lib/status/format.js +0 -12
  80. package/dist/lib/status/vaults-format.d.ts +0 -2
  81. package/dist/lib/status/vaults-format.js +0 -49
  82. package/dist/lib/status/vaults-status.d.ts +0 -12
  83. package/dist/lib/status/vaults-status.js +0 -54
  84. package/dist/lib/update/update-lock.d.ts +0 -2
  85. package/dist/lib/update/update-lock.js +0 -109
  86. package/dist/lib/vault/vault-registry.d.ts +0 -12
  87. package/dist/lib/vault/vault-registry.js +0 -81
  88. package/dist/lib/vault/vaults.d.ts +0 -10
  89. package/dist/lib/vault/vaults.js +0 -66
  90. package/dist/lib/vault/vaults.schema.d.ts +0 -2
  91. package/dist/lib/vault/vaults.schema.js +0 -10
  92. package/dist/mcp/local-server.d.ts +0 -4
  93. package/dist/mcp/local-server.js +0 -26
  94. package/dist/observability.d.ts +0 -4
  95. package/dist/observability.js +0 -35
  96. package/dist/sync/discover/watcher.d.ts +0 -26
  97. package/dist/sync/discover/watcher.js +0 -159
  98. package/dist/sync/git/conflict.d.ts +0 -1
  99. package/dist/sync/git/conflict.js +0 -16
  100. package/dist/sync/git/cycle.d.ts +0 -18
  101. package/dist/sync/git/cycle.js +0 -158
  102. package/dist/sync/git/git-exec.d.ts +0 -22
  103. package/dist/sync/git/git-exec.js +0 -56
  104. package/dist/sync/git/manager.d.ts +0 -28
  105. package/dist/sync/git/manager.js +0 -114
  106. package/dist/sync/git/planner.d.ts +0 -15
  107. package/dist/sync/git/planner.js +0 -46
  108. package/dist/sync/git/remote-url.d.ts +0 -3
  109. package/dist/sync/git/remote-url.js +0 -30
package/README.md CHANGED
@@ -1,122 +1,92 @@
1
1
  # @agentage/cli
2
2
 
3
- [![CI](https://github.com/agentage/cli/actions/workflows/ci.yml/badge.svg)](https://github.com/agentage/cli/actions/workflows/ci.yml)
4
- [![npm version](https://badge.fury.io/js/%40agentage%2Fcli.svg)](https://badge.fury.io/js/%40agentage%2Fcli)
3
+ ![Connect this machine to agentage from the terminal](https://github.com/agentage/cli/raw/master/docs/banner.svg)
5
4
 
6
- Give every AI on your machine one shared memory - plain markdown you own, synced across your devices.
5
+ Connect this machine to your [agentage](https://agentage.io) account from the terminal. One
6
+ command signs you in through your browser, and from then on the machine is known to your account.
7
7
 
8
- Claude, Cursor, and your other AI tools each keep their own separate memory today. This
9
- CLI gives them a single shared one. It lives on your disk as ordinary markdown files
10
- you can open, edit, and back up like any other folder. Your AI tools read and write
11
- those same files, so they all remember the same things. Connect a free account and your
12
- memory follows you to every device - and nothing is locked in, because the files are
13
- always just markdown you can export.
8
+ ## What is this?
14
9
 
15
- ## How it fits together
10
+ agentage gives your AI tools a shared, account-backed home. Every machine you use needs to be
11
+ connected to that account once: this package does that connection. It keeps your sign-in on
12
+ the machine (OAuth 2.1 with PKCE, nothing typed into the terminal), tells you whether the
13
+ machine is connected, and keeps itself up to date.
16
14
 
17
- ![Architecture](https://github.com/agentage/cli/raw/master/docs/architecture.svg)
15
+ It is intentionally **small**: sign-in, status and update. Everything else arrives as plugins
16
+ that the CLI installs for you.
18
17
 
19
- ## Quickstart
18
+ ## Get started
20
19
 
21
- Three steps from nothing to a shared memory your AI can use.
22
-
23
- **1. Install it.** Needs Node.js 22 or newer.
20
+ Needs Node.js 22 or newer.
24
21
 
25
22
  ```bash
26
23
  npm install -g @agentage/cli
24
+ agentage setup
27
25
  ```
28
26
 
29
- **2. Create a memory.** This makes a plain folder of markdown files at `~/vaults/notes`.
30
- No sign-in needed.
31
-
32
- ```bash
33
- agentage vault add notes --local
34
- ```
35
-
36
- **3. Connect your AI.** This example wires up Claude Code over MCP, the open protocol AI
37
- tools use to call tools. Any AI tool that speaks MCP connects the same way.
27
+ `setup` opens your browser, you sign in, and the session is stored in `~/.agentage`. Then:
38
28
 
39
29
  ```bash
40
- claude mcp add --transport http agentage http://127.0.0.1:4243/mcp
30
+ agentage status
41
31
  ```
42
32
 
43
- That's it. Ask your AI to remember something, and it lands in your `~/vaults/notes`
44
- folder as a markdown file you own.
45
-
46
- ## What your AI can do
47
-
48
- Once connected, your AI can:
49
-
50
- - Remember things you tell it, saved as a note.
51
- - Find them again later by keyword.
52
- - Read back a specific note in full.
53
- - Update a note in place, or add to it.
54
- - Browse the folders your notes are organized into.
55
- - Remove a note it no longer needs (deletes are recoverable).
56
-
57
- You can do all of this from the terminal too - run `agentage memory --help` to see how.
58
-
59
- ## Sync across devices
60
-
61
- Want the same memory on your laptop, desktop, and phone? Connect a free account:
33
+ ### On a server or in CI (no browser)
62
34
 
63
35
  ```bash
64
- agentage setup
36
+ agentage setup --no-browser # prints the sign-in URL to open elsewhere
37
+ AGENTAGE_TOKEN=aga_... agentage status # or use a personal access token
65
38
  ```
66
39
 
67
- This opens your browser for a one-time sign-in - no API key to copy or passwords in the
68
- terminal. After that, sync runs quietly in the background and the same memory follows
69
- you everywhere. Your files always stay yours: they remain plain markdown on your disk,
70
- and you can export them any time.
71
-
72
- Prefer to host it yourself? You can point a memory at your own git remote instead with
73
- `agentage vault add <name> --git <remote>` - see [`docs/reference.md`](docs/reference.md)
74
- for details.
40
+ ### Then
75
41
 
76
- ## Token auth (CI / headless)
42
+ - `agentage status --json` in scripts to check the machine is connected.
43
+ - `agentage update` whenever a newer version is published; `status` tells you when.
77
44
 
78
- For CI or non-interactive machines, skip the browser sign-in and authenticate with a
79
- personal access token. Mint one in the dashboard under **Settings -> API tokens**
80
- (scopes `memory:read` / `memory:write`), then set it in the environment:
45
+ ## Commands
81
46
 
82
- ```bash
83
- export AGENTAGE_TOKEN=aga_...
84
- agentage status
85
- ```
47
+ | Command | What it does |
48
+ | ----------------------------- | ----------------------------------------------------------------------- |
49
+ | `agentage setup` | Sign in through your browser, then show status |
50
+ | `agentage setup --no-browser` | Print the sign-in URL instead of opening a browser |
51
+ | `agentage setup --reauth` | Force a fresh sign-in |
52
+ | `agentage setup --disconnect` | Sign out and remove local credentials |
53
+ | `agentage status` | Version, update, target, sign-in state, endpoint (`--json` for scripts) |
54
+ | `agentage update` | Install the latest published version (`--check` only reports) |
86
55
 
87
- The token is used as the bearer for memory (MCP) calls; `--token aga_...` works per command
88
- too. Account-channel provisioning still needs an interactive `agentage setup` session.
56
+ Flags, exit codes and token auth are in [`docs/reference.md`](docs/reference.md).
89
57
 
90
- ## Going deeper
58
+ ## How it works
91
59
 
92
- - [`docs/architecture.md`](docs/architecture.md) - how the CLI, the local helper, your
93
- files, and sync fit together.
94
- - [`docs/reference.md`](docs/reference.md) - every command and flag, the full sync
95
- options, MCP details, and environment variables.
60
+ This is the **terminal client for an agentage account**. `setup` registers a one-off OAuth
61
+ client, opens the authorization page in your browser and waits on a localhost callback; the
62
+ tokens land in `~/.agentage/auth.json` with owner-only permissions and are refreshed
63
+ automatically. `status` validates the session against the account's introspection endpoint
64
+ and reports version, update, target host and connectivity. Nothing runs in the background and
65
+ nothing is installed beyond this package.
96
66
 
97
- ## Telemetry (off by default)
67
+ ## Configuration
98
68
 
99
- Nothing leaves your machine unless you ask for it. The CLI and its local helper ship with
100
- `@agentage/observability`, which stays inert until `OTEL_EXPORTER_OTLP_ENDPOINT` points at a
101
- collector you choose - then the CLI reports as `agentage-cli` and the helper as `agentage-daemon`
102
- (override with `OTEL_SERVICE_NAME`). Every line it writes goes to stderr, so `--json` output and the
103
- helper's MCP wire stay clean.
69
+ | Env | Default | Purpose |
70
+ | --------------------- | ------------- | -------------------------------------------------- |
71
+ | `AGENTAGE_SITE_FQDN` | `agentage.io` | Target host |
72
+ | `AGENTAGE_CONFIG_DIR` | `~/.agentage` | Config and credentials directory |
73
+ | `AGENTAGE_TOKEN` | - | Personal access token for CI and headless machines |
74
+ | `NO_COLOR` | - | Disable colored output |
104
75
 
105
- ## Development
76
+ ## Develop
106
77
 
107
78
  ```bash
108
79
  npm ci
109
- npm run verify # type-check + lint + format:check + unit tests + build
80
+ npm run verify
81
+ npm run build && npm run test:e2e -- --grep @offline
110
82
  ```
111
83
 
112
- End-to-end tests (Playwright) run in tiers - the offline tiers need no network or
113
- account. See [`docs/reference.md`](docs/reference.md) for more.
84
+ ## Release
114
85
 
115
- ```bash
116
- npm run build && npm run test:e2e # all e2e tiers
117
- npm run build && npm run test:e2e -- --grep @offline # offline tiers only
118
- ```
86
+ Releases publish to npm via GitHub Actions when a `chore(release): X.Y.Z` commit that touches
87
+ `package.json` lands on `master` (the squash-merge subject of a release PR). The workflow skips
88
+ any version already on npm.
119
89
 
120
90
  ## License
121
91
 
122
- MIT
92
+ MIT - see [LICENSE](./LICENSE).
package/dist/cli.js CHANGED
@@ -1,15 +1,12 @@
1
1
  #!/usr/bin/env node
2
- // The bin is a loader, nothing else. @agentage/observability instruments node:http, fetch and MCP
3
- // tool registration through module hooks, and a hook only sees a module imported after it is
4
- // registered - so the bootstrap runs first and the program is imported dynamically behind it.
5
2
  // Dependency-free guard so the message survives even if deps fail to parse on old Node.
6
3
  const nodeMajor = Number(process.versions.node.split('.')[0]);
7
4
  if (nodeMajor < 22) {
8
5
  process.stderr.write(`agentage requires Node.js >= 22 (you have v${process.versions.node})\n`);
9
- process.exit(1);
6
+ process.exitCode = 1;
7
+ }
8
+ else {
9
+ const { run } = await import('./program.js');
10
+ process.exitCode = await run();
10
11
  }
11
- const { CLI_SERVICE, startObservability } = await import('./observability.js');
12
- await startObservability(CLI_SERVICE);
13
- const { run } = await import('./program.js');
14
- await run();
15
12
  export {};
@@ -1,22 +1,12 @@
1
1
  import { type Command } from 'commander';
2
- import { startCallbackServer } from '../../lib/auth/callback-server.js';
3
- import { ensureDaemon } from '../../lib/daemon/daemon-client.js';
4
- import { introspectToken } from '../../lib/auth/api.js';
5
- import { exchangeCode, registerClient, revokeToken } from '../../lib/auth/oauth.js';
6
- export interface SetupOptions {
2
+ import { type SignInOptions, type SignInResult } from '../../lib/auth/sign-in.js';
3
+ export interface SetupOptions extends SignInOptions {
7
4
  disconnect?: boolean;
8
- reauth?: boolean;
9
- browser?: boolean;
10
5
  }
11
6
  export interface SetupDeps {
12
- register: typeof registerClient;
13
- exchange: typeof exchangeCode;
14
- revoke: typeof revokeToken;
15
- startServer: typeof startCallbackServer;
16
- openBrowser: (url: string) => Promise<void>;
7
+ ensureSignedIn: (opts: SignInOptions) => Promise<SignInResult>;
8
+ signOut: () => Promise<void>;
17
9
  printStatus: () => Promise<void>;
18
- ensureDaemon: typeof ensureDaemon;
19
- introspect: typeof introspectToken;
20
10
  }
21
- export declare const runSetup: (opts: SetupOptions, deps?: SetupDeps) => Promise<void>;
22
- export declare const registerSetup: (program: Command) => void;
11
+ export declare const runSetup: (opts: SetupOptions, deps: SetupDeps) => Promise<void>;
12
+ export declare const registerSetup: (program: Command, action: (opts: SetupOptions) => Promise<void>) => void;
@@ -1,147 +1,17 @@
1
- import chalk from 'chalk';
2
- import { startCallbackServer } from '../../lib/auth/callback-server.js';
3
- import { ensureDaemon } from '../../lib/daemon/daemon-client.js';
4
- import { daemonDisabled } from '../../lib/daemon/daemon-pref.js';
5
- import { deleteAuth, ensureConfigDir, mutateAuth, readAuth, } from '../../lib/fs/config.js';
6
- import { AuthRequiredError, introspectToken } from '../../lib/auth/api.js';
7
- import { detectEnvMismatch } from '../../lib/auth/env-match.js';
8
- import { buildAuthorizeUrl, exchangeCode, pkcePair, randomState, registerClient, revokeToken, } from '../../lib/auth/oauth.js';
9
- import { links, siteFqdn } from '../../lib/net/origins.js';
10
- import { ensureVaultsConfig } from '../../lib/vault/vaults.js';
11
- import { runStatus } from '../status/status.js';
12
- const defaultDeps = {
13
- register: registerClient,
14
- exchange: exchangeCode,
15
- revoke: revokeToken,
16
- startServer: startCallbackServer,
17
- openBrowser: async (url) => {
18
- const { default: open } = await import('open');
19
- await open(url);
20
- },
21
- printStatus: () => runStatus({}),
22
- ensureDaemon,
23
- introspect: introspectToken,
1
+ import {} from 'commander';
2
+ import {} from '../../lib/auth/sign-in.js';
3
+ export const runSetup = async (opts, deps) => {
4
+ if (opts.disconnect)
5
+ return deps.signOut();
6
+ await deps.ensureSignedIn({ reauth: opts.reauth, browser: opts.browser });
7
+ await deps.printStatus();
24
8
  };
25
- const toAuthState = (fqdn, clientId, tokens) => ({
26
- siteFqdn: fqdn,
27
- clientId,
28
- tokens: {
29
- accessToken: tokens.access_token,
30
- refreshToken: tokens.refresh_token,
31
- expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
32
- },
33
- });
34
- // After sign-in, bring the daemon up so account sync starts immediately; ensureDaemon reuses a
35
- // live daemon and starts a stopped one (idempotent). Honor the --no-daemon / AGENTAGE_NO_DAEMON
36
- // disable switches.
37
- const startDaemon = async (deps) => {
38
- if (daemonDisabled()) {
39
- console.log('Daemon disabled - account sync will run in-process on demand.');
40
- return;
41
- }
42
- const client = await deps.ensureDaemon();
43
- const fail = 'Could not start the daemon - run: agentage daemon start';
44
- console.log(client ? 'Daemon running.' : fail);
45
- };
46
- const disconnect = async (deps) => {
47
- const auth = readAuth();
48
- if (!auth) {
49
- console.log('Nothing to disconnect.');
50
- return;
51
- }
52
- await deps.revoke(links(auth.siteFqdn).auth, auth.tokens.accessToken);
53
- await deleteAuth();
54
- console.log('Disconnected - local credentials removed.');
55
- };
56
- // Validate the stored session instead of trusting mere token presence. Returns true to short-circuit
57
- // (valid or a transient blip - creds intact), false to proceed into a fresh sign-in (truly expired or
58
- // the credential belongs to a different target than the current run).
59
- const alreadySignedIn = async (auth, deps) => {
60
- const mismatch = detectEnvMismatch(auth, siteFqdn());
61
- if (mismatch) {
62
- console.log(`Signed in to ${mismatch.credentialFqdn} (${mismatch.credentialEnv}), but this run targets ` +
63
- `${mismatch.targetFqdn} (${mismatch.targetEnv}) - signing you into ${mismatch.targetEnv}.\n`);
64
- return false; // proceed to a fresh sign-in against the current target
65
- }
66
- try {
67
- await deps.introspect(auth, links(auth.siteFqdn));
68
- console.log(`Already signed in. Run ${chalk.white('agentage setup --reauth')} to sign in again.\n`);
69
- await deps.printStatus();
70
- return true;
71
- }
72
- catch (err) {
73
- if (err instanceof AuthRequiredError) {
74
- console.log('Session expired - signing you in again.\n');
75
- return false; // fall through to the sign-in flow, no --reauth needed
76
- }
77
- // Transient: do not force reauth on a blip; keep credentials and show status.
78
- console.log('You appear signed in (could not fully verify - temporary).\n');
79
- await deps.printStatus();
80
- return true;
81
- }
82
- };
83
- export const runSetup = async (opts, deps = defaultDeps) => {
84
- if (opts.disconnect) {
85
- await disconnect(deps);
86
- return;
87
- }
88
- const stored = readAuth();
89
- if (stored && !opts.reauth && (await alreadySignedIn(stored, deps)))
90
- return;
91
- const fqdn = siteFqdn();
92
- const target = links(fqdn);
93
- ensureConfigDir();
94
- ensureVaultsConfig();
95
- const { verifier, challenge } = pkcePair();
96
- const state = randomState();
97
- const server = await deps.startServer(state);
98
- try {
99
- const clientId = await deps.register(target.auth, server.redirectUri);
100
- const url = buildAuthorizeUrl(target.auth, {
101
- clientId,
102
- redirectUri: server.redirectUri,
103
- challenge,
104
- state,
105
- });
106
- console.log(`\nSign in to agentage (${fqdn}):\n\n ${url}\n`);
107
- if (opts.browser === false)
108
- console.log('Open the URL above in a browser to continue.');
109
- else
110
- await deps
111
- .openBrowser(url)
112
- .catch(() => console.log('Could not open a browser - use the URL above.'));
113
- console.log('Waiting for sign-in to complete...');
114
- const code = await server.waitForCode();
115
- const tokens = await deps.exchange(target.auth, {
116
- clientId,
117
- code,
118
- redirectUri: server.redirectUri,
119
- verifier,
120
- });
121
- await mutateAuth(() => toAuthState(fqdn, clientId, tokens));
122
- console.log(chalk.green('\nSigned in.\n'));
123
- await startDaemon(deps);
124
- await deps.printStatus();
125
- }
126
- finally {
127
- server.close();
128
- }
129
- };
130
- export const registerSetup = (program) => {
9
+ export const registerSetup = (program, action) => {
131
10
  program
132
11
  .command('setup')
133
12
  .description('Sign in and connect this machine to your agentage account')
134
13
  .option('--disconnect', 'sign out and remove local credentials')
135
14
  .option('--reauth', 'force a fresh sign-in')
136
15
  .option('--no-browser', 'print the sign-in URL instead of opening a browser')
137
- .action(async (opts) => {
138
- try {
139
- await runSetup(opts);
140
- }
141
- catch (err) {
142
- const message = err instanceof Error ? err.message : String(err);
143
- console.error(chalk.red(`Setup failed: ${message}`));
144
- process.exitCode = 1;
145
- }
146
- });
16
+ .action(action);
147
17
  };
@@ -1,8 +1,9 @@
1
1
  import { type Command } from 'commander';
2
2
  import { type StatusReport } from '../../lib/status/status-info.js';
3
3
  export declare const printStatus: (report: StatusReport) => void;
4
- export declare const runStatus: (opts?: {
4
+ export interface StatusOptions {
5
5
  json?: boolean;
6
6
  token?: string;
7
- }) => Promise<void>;
8
- export declare const registerStatus: (program: Command) => void;
7
+ }
8
+ export declare const runStatus: (opts?: StatusOptions) => Promise<void>;
9
+ export declare const registerStatus: (program: Command, action: (opts: StatusOptions) => Promise<void>) => void;
@@ -1,9 +1,8 @@
1
1
  import chalk from 'chalk';
2
+ import {} from 'commander';
2
3
  import { resolveAuth } from '../../lib/auth/credentials.js';
3
4
  import { siteFqdn } from '../../lib/net/origins.js';
4
- import { formatUptime } from '../../lib/status/format.js';
5
- import { gatherStatus, } from '../../lib/status/status-info.js';
6
- import { vaultLines } from '../../lib/status/vaults-format.js';
5
+ import { gatherStatus } from '../../lib/status/status-info.js';
7
6
  import { INSTALL_HINT } from '../../lib/update/update-check.js';
8
7
  const mark = (good) => (good ? chalk.green('✓') : chalk.red('✗'));
9
8
  const row = (label, value) => {
@@ -36,24 +35,6 @@ const updateLine = (update) => {
36
35
  return chalk.dim('- update check unavailable');
37
36
  }
38
37
  };
39
- const daemonLine = (d, cliVersion) => {
40
- if (!d.running)
41
- return `${mark(false)} stopped - run: agentage daemon start`;
42
- const up = d.uptimeSeconds !== undefined ? `, up ${formatUptime(d.uptimeSeconds)}` : '';
43
- const stale = d.daemonVersion && d.daemonVersion !== cliVersion
44
- ? chalk.yellow(` (version ${d.daemonVersion} != cli ${cliVersion})`)
45
- : '';
46
- return `${mark(true)} running (pid ${d.pid ?? '?'}, port ${d.port}${up})${stale}`;
47
- };
48
- const mcpLine = (d) => d.mcp ? `${mark(true)} serving at http://127.0.0.1:${d.port}/mcp` : `${mark(false)} off`;
49
- // Daemon rows honor version-mismatch inline (appended to the daemon row) and print mcp only when
50
- // running; the per-vault sync breakdown now lives in its own `vaults` block (printStatus).
51
- const printDaemon = (d, cliVersion) => {
52
- row('daemon', daemonLine(d, cliVersion));
53
- if (!d.running)
54
- return;
55
- row('mcp', mcpLine(d));
56
- };
57
38
  export const printStatus = (report) => {
58
39
  row('version', report.version);
59
40
  row('update', updateLine(report.update));
@@ -63,10 +44,6 @@ export const printStatus = (report) => {
63
44
  row('auth', authLine(report.auth));
64
45
  row('endpoint', `${mark(report.endpoint.reachable)} ${report.endpoint.url} ` +
65
46
  (report.endpoint.reachable ? 'reachable' : 'unreachable'));
66
- if (report.daemon)
67
- printDaemon(report.daemon, report.version);
68
- for (const line of vaultLines(report.vaults))
69
- console.log(line);
70
47
  if (report.update.message)
71
48
  console.log(chalk.yellow(`\n${report.update.message}`));
72
49
  };
@@ -78,11 +55,11 @@ export const runStatus = async (opts = {}) => {
78
55
  }
79
56
  printStatus(report);
80
57
  };
81
- export const registerStatus = (program) => {
58
+ export const registerStatus = (program, action) => {
82
59
  program
83
60
  .command('status')
84
61
  .description('Show CLI, account, and endpoint status')
85
62
  .option('--json', 'machine-readable output')
86
63
  .option('--token <token>', 'authenticate with a personal access token (aga_...); or set AGENTAGE_TOKEN')
87
- .action(runStatus);
64
+ .action(action);
88
65
  };
@@ -1,17 +1,10 @@
1
1
  import { type Command } from 'commander';
2
- import { type SpawnOutcome } from '../../lib/daemon/daemon-client.js';
2
+ import { requireNpm10, runNpm } from '../../lib/npm/npm-cli.js';
3
3
  import { type UpdateInfo } from '../../lib/update/update-check.js';
4
- export type RestartOutcome = 'restarted' | 'failed' | 'not-running';
5
- export interface RestartDeps {
6
- running?: () => boolean;
7
- stop?: () => Promise<boolean>;
8
- start?: (port: number) => Promise<SpawnOutcome>;
9
- }
10
- export declare const restartDaemonIfRunning: (deps?: RestartDeps) => Promise<RestartOutcome>;
4
+ export declare const installLatest: (run?: typeof runNpm, check?: typeof requireNpm10) => Promise<void>;
11
5
  export interface UpdateDeps {
12
6
  check: (installed: string) => Promise<UpdateInfo>;
13
7
  install: () => Promise<void>;
14
- restartDaemon: () => Promise<RestartOutcome>;
15
8
  acquireLock: () => boolean;
16
9
  releaseLock: () => void;
17
10
  log: (msg: string) => void;
@@ -20,4 +13,4 @@ export interface UpdateOptions {
20
13
  check?: boolean;
21
14
  }
22
15
  export declare const runUpdate: (opts: UpdateOptions, deps?: UpdateDeps) => Promise<void>;
23
- export declare const registerUpdate: (program: Command) => void;
16
+ export declare const registerUpdate: (program: Command, action: (opts: UpdateOptions) => Promise<void>) => void;
@@ -1,31 +1,30 @@
1
- import { execFile } from 'node:child_process';
2
- import { promisify } from 'node:util';
1
+ import { join } from 'node:path';
3
2
  import chalk from 'chalk';
4
- import { isDaemonRunning, resolvePort, stopDaemonAndWait } from '../../daemon/lifecycle.js';
5
- import { spawnDaemon } from '../../lib/daemon/daemon-client.js';
3
+ import {} from 'commander';
4
+ import { getConfigDir } from '../../lib/fs/config.js';
5
+ import { acquireFileLock, releaseFileLock } from '../../lib/fs/file-lock.js';
6
+ import { requireNpm10, runNpm } from '../../lib/npm/npm-cli.js';
6
7
  import { checkForUpdate, INSTALL_HINT } from '../../lib/update/update-check.js';
7
- import { acquireUpdateLock, releaseUpdateLock } from '../../lib/update/update-lock.js';
8
8
  import { VERSION } from '../../utils/version.js';
9
- const pexec = promisify(execFile);
10
- // Restart a running daemon so it picks up the freshly installed binary; a stopped daemon is left
11
- // stopped. Waits for the old process to exit (port free) before spawning, and reports honestly
12
- // when the new daemon did not come up.
13
- export const restartDaemonIfRunning = async (deps = {}) => {
14
- const running = deps.running ?? isDaemonRunning;
15
- if (!running())
16
- return 'not-running';
17
- await (deps.stop ?? stopDaemonAndWait)();
18
- const up = await (deps.start ?? spawnDaemon)(resolvePort());
19
- return up.ok ? 'restarted' : 'failed';
9
+ const INSTALL_TIMEOUT_MS = 5 * 60_000;
10
+ const NPM_TAIL_LINES = 10;
11
+ // An install takes minutes, so the crashed-holder ages scale up from the config-write defaults.
12
+ const UPDATE_LOCK = { ttlMs: 10 * 60_000, hardStaleMs: 30 * 60_000 };
13
+ const updateLockTarget = () => join(getConfigDir(), 'update');
14
+ const tail = (text) => text.trimEnd().split('\n').slice(-NPM_TAIL_LINES).join('\n');
15
+ export const installLatest = async (run = runNpm, check = requireNpm10) => {
16
+ await check(run);
17
+ const res = await run(['install', '-g', '@agentage/cli@latest'], {
18
+ timeoutMs: INSTALL_TIMEOUT_MS,
19
+ });
20
+ if (res.code !== 0)
21
+ throw new Error(`npm install failed (exit ${res.code}):\n${tail(res.stderr || res.stdout)}`);
20
22
  };
21
23
  const defaultDeps = {
22
24
  check: checkForUpdate,
23
- install: async () => {
24
- await pexec('npm', ['install', '-g', '@agentage/cli@latest']);
25
- },
26
- restartDaemon: () => restartDaemonIfRunning(),
27
- acquireLock: () => acquireUpdateLock(),
28
- releaseLock: releaseUpdateLock,
25
+ install: () => installLatest(),
26
+ acquireLock: () => acquireFileLock(updateLockTarget(), Date.now(), UPDATE_LOCK),
27
+ releaseLock: () => releaseFileLock(updateLockTarget()),
29
28
  log: (msg) => console.log(msg),
30
29
  };
31
30
  const describe = (info) => {
@@ -41,20 +40,12 @@ const describe = (info) => {
41
40
  return chalk.yellow(`Couldn't reach the registry - try again later. ${INSTALL_HINT}`);
42
41
  }
43
42
  };
44
- // Install, then restart a running daemon so its in-process engine runs the new version.
45
43
  const install = async (deps) => {
46
44
  deps.log('Installing the latest @agentage/cli...');
47
45
  await deps.install();
48
46
  deps.log(chalk.green('Updated. Run `agentage status` to confirm.'));
49
- const outcome = await deps.restartDaemon();
50
- if (outcome === 'restarted')
51
- deps.log(chalk.green('Restarted the daemon on the new version.'));
52
- if (outcome === 'failed')
53
- deps.log(chalk.yellow('Daemon did not restart cleanly - run `agentage daemon start`.'));
54
47
  };
55
- // --check reports the verdict and never installs. Otherwise, install only when the registry says
56
- // we're behind (available/unsupported), guarded by a single-writer lock; current/unreachable just
57
- // report.
48
+ // --check never installs; otherwise install only when behind, under a single-writer lock.
58
49
  export const runUpdate = async (opts, deps = defaultDeps) => {
59
50
  const info = await deps.check(VERSION);
60
51
  deps.log(describe(info));
@@ -72,18 +63,10 @@ export const runUpdate = async (opts, deps = defaultDeps) => {
72
63
  deps.releaseLock();
73
64
  }
74
65
  };
75
- export const registerUpdate = (program) => {
66
+ export const registerUpdate = (program, action) => {
76
67
  program
77
68
  .command('update')
78
69
  .description('Update @agentage/cli to the latest published version')
79
70
  .option('--check', 'report whether an update is available, without installing')
80
- .action(async (opts) => {
81
- try {
82
- await runUpdate(opts);
83
- }
84
- catch (err) {
85
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
86
- process.exitCode = 1;
87
- }
88
- });
71
+ .action(action);
89
72
  };
@@ -0,0 +1 @@
1
+ export declare const pluginsEnabled: () => boolean;
@@ -0,0 +1 @@
1
+ export const pluginsEnabled = () => process.env['AGENTAGE_PLUGINS'] === '1';
@@ -1,7 +1,8 @@
1
1
  import { type AuthState } from '../fs/config.js';
2
+ import { type CliRequestInit } from '../net/fetch.js';
2
3
  import { type Links } from '../net/origins.js';
3
4
  export { AuthRequiredError, TransientAuthError } from './auth-errors.js';
4
5
  export declare const refreshOrThrow: (auth: AuthState, links: Links) => Promise<void>;
5
- export declare const authedGet: <T>(auth: AuthState, links: Links, url: string) => Promise<T>;
6
- export declare const authedPost: (auth: AuthState, links: Links, url: string, body: unknown) => Promise<Response>;
6
+ export declare const authedFetch: (auth: AuthState, links: Links, url: string, init?: CliRequestInit) => Promise<Response>;
7
+ export declare const authedJson: <T>(auth: AuthState, links: Links, url: string, init?: CliRequestInit) => Promise<T>;
7
8
  export { introspectToken, type TokenSession } from './introspect.js';