@agentage/cli 0.35.0 → 0.38.0
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/README.md +53 -83
- package/dist/cli.js +0 -5
- package/dist/commands/auth/setup.d.ts +0 -2
- package/dist/commands/auth/setup.js +0 -18
- package/dist/commands/status/status.js +1 -25
- package/dist/commands/update/update.d.ts +0 -9
- package/dist/commands/update/update.js +0 -20
- package/dist/core/flag.d.ts +1 -0
- package/dist/core/flag.js +1 -0
- package/dist/lib/auth/api.js +1 -1
- package/dist/lib/auth/oauth.js +1 -1
- package/dist/lib/net/origins.d.ts +0 -2
- package/dist/lib/net/origins.js +1 -5
- package/dist/lib/net/user-agent.d.ts +1 -6
- package/dist/lib/net/user-agent.js +3 -4
- package/dist/lib/status/status-info.d.ts +0 -18
- package/dist/lib/status/status-info.js +1 -42
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.js +2 -0
- package/dist/program.js +4 -16
- package/package.json +17 -12
- package/dist/commands/daemon/daemon-cmd.d.ts +0 -2
- package/dist/commands/daemon/daemon-cmd.js +0 -107
- package/dist/commands/memory/memory-verbs.d.ts +0 -21
- package/dist/commands/memory/memory-verbs.js +0 -98
- package/dist/commands/memory/memory.d.ts +0 -2
- package/dist/commands/memory/memory.js +0 -54
- package/dist/commands/vault/vault-sync.d.ts +0 -13
- package/dist/commands/vault/vault-sync.js +0 -90
- package/dist/commands/vault/vault.d.ts +0 -22
- package/dist/commands/vault/vault.js +0 -129
- package/dist/daemon/actions.d.ts +0 -5
- package/dist/daemon/actions.js +0 -22
- package/dist/daemon/client-provider.d.ts +0 -2
- package/dist/daemon/client-provider.js +0 -16
- package/dist/daemon/entry.d.ts +0 -8
- package/dist/daemon/entry.js +0 -113
- package/dist/daemon/guards.d.ts +0 -3
- package/dist/daemon/guards.js +0 -13
- package/dist/daemon/lifecycle.d.ts +0 -17
- package/dist/daemon/lifecycle.js +0 -151
- package/dist/daemon/mcp-http.d.ts +0 -3
- package/dist/daemon/mcp-http.js +0 -34
- package/dist/daemon/server.d.ts +0 -23
- package/dist/daemon/server.js +0 -135
- package/dist/daemon-entry.d.ts +0 -1
- package/dist/daemon-entry.js +0 -9
- package/dist/lib/auth/provision.d.ts +0 -14
- package/dist/lib/auth/provision.js +0 -49
- package/dist/lib/daemon/daemon-client.d.ts +0 -37
- package/dist/lib/daemon/daemon-client.js +0 -136
- package/dist/lib/daemon/daemon-pref.d.ts +0 -2
- package/dist/lib/daemon/daemon-pref.js +0 -10
- package/dist/lib/memory/memory-client.d.ts +0 -30
- package/dist/lib/memory/memory-client.js +0 -98
- package/dist/lib/status/format.d.ts +0 -1
- package/dist/lib/status/format.js +0 -12
- package/dist/lib/status/vaults-format.d.ts +0 -2
- package/dist/lib/status/vaults-format.js +0 -49
- package/dist/lib/status/vaults-status.d.ts +0 -12
- package/dist/lib/status/vaults-status.js +0 -54
- package/dist/lib/vault/vault-registry.d.ts +0 -12
- package/dist/lib/vault/vault-registry.js +0 -81
- package/dist/lib/vault/vaults.d.ts +0 -10
- package/dist/lib/vault/vaults.js +0 -66
- package/dist/lib/vault/vaults.schema.d.ts +0 -2
- package/dist/lib/vault/vaults.schema.js +0 -10
- package/dist/mcp/local-server.d.ts +0 -4
- package/dist/mcp/local-server.js +0 -26
- package/dist/observability.d.ts +0 -4
- package/dist/observability.js +0 -35
- package/dist/sync/discover/watcher.d.ts +0 -26
- package/dist/sync/discover/watcher.js +0 -159
- package/dist/sync/git/conflict.d.ts +0 -1
- package/dist/sync/git/conflict.js +0 -16
- package/dist/sync/git/cycle.d.ts +0 -18
- package/dist/sync/git/cycle.js +0 -158
- package/dist/sync/git/git-exec.d.ts +0 -22
- package/dist/sync/git/git-exec.js +0 -56
- package/dist/sync/git/manager.d.ts +0 -28
- package/dist/sync/git/manager.js +0 -114
- package/dist/sync/git/planner.d.ts +0 -15
- package/dist/sync/git/planner.js +0 -46
- package/dist/sync/git/remote-url.d.ts +0 -3
- package/dist/sync/git/remote-url.js +0 -30
package/README.md
CHANGED
|
@@ -1,122 +1,92 @@
|
|
|
1
1
|
# @agentage/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://badge.fury.io/js/%40agentage%2Fcli)
|
|
3
|
+

|
|
5
4
|
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
18
|
+
## Get started
|
|
20
19
|
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
+
agentage status
|
|
41
31
|
```
|
|
42
32
|
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
58
|
+
## How it works
|
|
91
59
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
##
|
|
67
|
+
## Configuration
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
##
|
|
76
|
+
## Develop
|
|
106
77
|
|
|
107
78
|
```bash
|
|
108
79
|
npm ci
|
|
109
|
-
npm run verify
|
|
80
|
+
npm run verify
|
|
81
|
+
npm run build && npm run test:e2e -- --grep @offline
|
|
110
82
|
```
|
|
111
83
|
|
|
112
|
-
|
|
113
|
-
account. See [`docs/reference.md`](docs/reference.md) for more.
|
|
84
|
+
## Release
|
|
114
85
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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,10 @@
|
|
|
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
6
|
process.exit(1);
|
|
10
7
|
}
|
|
11
|
-
const { CLI_SERVICE, startObservability } = await import('./observability.js');
|
|
12
|
-
await startObservability(CLI_SERVICE);
|
|
13
8
|
const { run } = await import('./program.js');
|
|
14
9
|
await run();
|
|
15
10
|
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type Command } from 'commander';
|
|
2
2
|
import { startCallbackServer } from '../../lib/auth/callback-server.js';
|
|
3
|
-
import { ensureDaemon } from '../../lib/daemon/daemon-client.js';
|
|
4
3
|
import { introspectToken } from '../../lib/auth/api.js';
|
|
5
4
|
import { exchangeCode, registerClient, revokeToken } from '../../lib/auth/oauth.js';
|
|
6
5
|
export interface SetupOptions {
|
|
@@ -15,7 +14,6 @@ export interface SetupDeps {
|
|
|
15
14
|
startServer: typeof startCallbackServer;
|
|
16
15
|
openBrowser: (url: string) => Promise<void>;
|
|
17
16
|
printStatus: () => Promise<void>;
|
|
18
|
-
ensureDaemon: typeof ensureDaemon;
|
|
19
17
|
introspect: typeof introspectToken;
|
|
20
18
|
}
|
|
21
19
|
export declare const runSetup: (opts: SetupOptions, deps?: SetupDeps) => Promise<void>;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
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
3
|
import { deleteAuth, ensureConfigDir, mutateAuth, readAuth, } from '../../lib/fs/config.js';
|
|
6
4
|
import { AuthRequiredError, introspectToken } from '../../lib/auth/api.js';
|
|
7
5
|
import { detectEnvMismatch } from '../../lib/auth/env-match.js';
|
|
8
6
|
import { buildAuthorizeUrl, exchangeCode, pkcePair, randomState, registerClient, revokeToken, } from '../../lib/auth/oauth.js';
|
|
9
7
|
import { links, siteFqdn } from '../../lib/net/origins.js';
|
|
10
|
-
import { ensureVaultsConfig } from '../../lib/vault/vaults.js';
|
|
11
8
|
import { runStatus } from '../status/status.js';
|
|
12
9
|
const defaultDeps = {
|
|
13
10
|
register: registerClient,
|
|
@@ -19,7 +16,6 @@ const defaultDeps = {
|
|
|
19
16
|
await open(url);
|
|
20
17
|
},
|
|
21
18
|
printStatus: () => runStatus({}),
|
|
22
|
-
ensureDaemon,
|
|
23
19
|
introspect: introspectToken,
|
|
24
20
|
};
|
|
25
21
|
const toAuthState = (fqdn, clientId, tokens) => ({
|
|
@@ -31,18 +27,6 @@ const toAuthState = (fqdn, clientId, tokens) => ({
|
|
|
31
27
|
expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
|
|
32
28
|
},
|
|
33
29
|
});
|
|
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
30
|
const disconnect = async (deps) => {
|
|
47
31
|
const auth = readAuth();
|
|
48
32
|
if (!auth) {
|
|
@@ -91,7 +75,6 @@ export const runSetup = async (opts, deps = defaultDeps) => {
|
|
|
91
75
|
const fqdn = siteFqdn();
|
|
92
76
|
const target = links(fqdn);
|
|
93
77
|
ensureConfigDir();
|
|
94
|
-
ensureVaultsConfig();
|
|
95
78
|
const { verifier, challenge } = pkcePair();
|
|
96
79
|
const state = randomState();
|
|
97
80
|
const server = await deps.startServer(state);
|
|
@@ -120,7 +103,6 @@ export const runSetup = async (opts, deps = defaultDeps) => {
|
|
|
120
103
|
});
|
|
121
104
|
await mutateAuth(() => toAuthState(fqdn, clientId, tokens));
|
|
122
105
|
console.log(chalk.green('\nSigned in.\n'));
|
|
123
|
-
await startDaemon(deps);
|
|
124
106
|
await deps.printStatus();
|
|
125
107
|
}
|
|
126
108
|
finally {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { resolveAuth } from '../../lib/auth/credentials.js';
|
|
3
3
|
import { siteFqdn } from '../../lib/net/origins.js';
|
|
4
|
-
import {
|
|
5
|
-
import { gatherStatus, } from '../../lib/status/status-info.js';
|
|
6
|
-
import { vaultLines } from '../../lib/status/vaults-format.js';
|
|
4
|
+
import { gatherStatus } from '../../lib/status/status-info.js';
|
|
7
5
|
import { INSTALL_HINT } from '../../lib/update/update-check.js';
|
|
8
6
|
const mark = (good) => (good ? chalk.green('✓') : chalk.red('✗'));
|
|
9
7
|
const row = (label, value) => {
|
|
@@ -36,24 +34,6 @@ const updateLine = (update) => {
|
|
|
36
34
|
return chalk.dim('- update check unavailable');
|
|
37
35
|
}
|
|
38
36
|
};
|
|
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
37
|
export const printStatus = (report) => {
|
|
58
38
|
row('version', report.version);
|
|
59
39
|
row('update', updateLine(report.update));
|
|
@@ -63,10 +43,6 @@ export const printStatus = (report) => {
|
|
|
63
43
|
row('auth', authLine(report.auth));
|
|
64
44
|
row('endpoint', `${mark(report.endpoint.reachable)} ${report.endpoint.url} ` +
|
|
65
45
|
(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
46
|
if (report.update.message)
|
|
71
47
|
console.log(chalk.yellow(`\n${report.update.message}`));
|
|
72
48
|
};
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import { type Command } from 'commander';
|
|
2
|
-
import { type SpawnOutcome } from '../../lib/daemon/daemon-client.js';
|
|
3
2
|
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>;
|
|
11
3
|
export interface UpdateDeps {
|
|
12
4
|
check: (installed: string) => Promise<UpdateInfo>;
|
|
13
5
|
install: () => Promise<void>;
|
|
14
|
-
restartDaemon: () => Promise<RestartOutcome>;
|
|
15
6
|
acquireLock: () => boolean;
|
|
16
7
|
releaseLock: () => void;
|
|
17
8
|
log: (msg: string) => void;
|
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { isDaemonRunning, resolvePort, stopDaemonAndWait } from '../../daemon/lifecycle.js';
|
|
5
|
-
import { spawnDaemon } from '../../lib/daemon/daemon-client.js';
|
|
6
4
|
import { checkForUpdate, INSTALL_HINT } from '../../lib/update/update-check.js';
|
|
7
5
|
import { acquireUpdateLock, releaseUpdateLock } from '../../lib/update/update-lock.js';
|
|
8
6
|
import { VERSION } from '../../utils/version.js';
|
|
9
7
|
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';
|
|
20
|
-
};
|
|
21
8
|
const defaultDeps = {
|
|
22
9
|
check: checkForUpdate,
|
|
23
10
|
install: async () => {
|
|
24
11
|
await pexec('npm', ['install', '-g', '@agentage/cli@latest']);
|
|
25
12
|
},
|
|
26
|
-
restartDaemon: () => restartDaemonIfRunning(),
|
|
27
13
|
acquireLock: () => acquireUpdateLock(),
|
|
28
14
|
releaseLock: releaseUpdateLock,
|
|
29
15
|
log: (msg) => console.log(msg),
|
|
@@ -41,16 +27,10 @@ const describe = (info) => {
|
|
|
41
27
|
return chalk.yellow(`Couldn't reach the registry - try again later. ${INSTALL_HINT}`);
|
|
42
28
|
}
|
|
43
29
|
};
|
|
44
|
-
// Install, then restart a running daemon so its in-process engine runs the new version.
|
|
45
30
|
const install = async (deps) => {
|
|
46
31
|
deps.log('Installing the latest @agentage/cli...');
|
|
47
32
|
await deps.install();
|
|
48
33
|
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
34
|
};
|
|
55
35
|
// --check reports the verdict and never installs. Otherwise, install only when the registry says
|
|
56
36
|
// we're behind (available/unsupported), guarded by a single-writer lock; current/unreachable just
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const pluginsEnabled: () => boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const pluginsEnabled = () => process.env['AGENTAGE_PLUGINS'] === '1';
|
package/dist/lib/auth/api.js
CHANGED
|
@@ -4,7 +4,7 @@ import { requestHeaders } from '../net/user-agent.js';
|
|
|
4
4
|
import { AuthRequiredError, isTerminalRefresh, TransientAuthError } from './auth-errors.js';
|
|
5
5
|
export { AuthRequiredError, TransientAuthError } from './auth-errors.js';
|
|
6
6
|
// Every authed call is CLI-originated; identify the caller alongside the bearer.
|
|
7
|
-
const cliHeaders = () => requestHeaders(
|
|
7
|
+
const cliHeaders = () => requestHeaders();
|
|
8
8
|
// Refresh the stored token in place. On success mutates auth + persists and returns. On failure it
|
|
9
9
|
// throws: AuthRequiredError when the grant is dead (401/invalid_grant/invalid_client), else
|
|
10
10
|
// TransientAuthError (429/5xx/network/timeout) - a blip that must not be read as a dead session.
|
package/dist/lib/auth/oauth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
2
|
import { requestHeaders } from '../net/user-agent.js';
|
|
3
3
|
// OAuth register/token/revoke are all CLI-originated; identify the caller on each.
|
|
4
|
-
const cliHeaders = () => requestHeaders(
|
|
4
|
+
const cliHeaders = () => requestHeaders();
|
|
5
5
|
const REGISTER_PATH = '/api/auth/mcp/register';
|
|
6
6
|
const AUTHORIZE_PATH = '/api/auth/mcp/authorize';
|
|
7
7
|
const TOKEN_PATH = '/api/auth/mcp/token';
|
package/dist/lib/net/origins.js
CHANGED
|
@@ -20,20 +20,16 @@ export const environment = (fqdn) => isLocal(fqdn) || fqdn.startsWith('dev.') ?
|
|
|
20
20
|
export const links = (fqdn) => {
|
|
21
21
|
const stub = stubOrigin(fqdn);
|
|
22
22
|
if (stub)
|
|
23
|
-
return { site: stub, api: `${stub}/api`, auth: stub
|
|
23
|
+
return { site: stub, api: `${stub}/api`, auth: stub };
|
|
24
24
|
return isLocal(fqdn)
|
|
25
25
|
? {
|
|
26
26
|
site: 'http://localhost:3000',
|
|
27
27
|
api: 'http://localhost:3001/api',
|
|
28
28
|
auth: 'http://localhost:3010',
|
|
29
|
-
mcp: 'http://localhost:3003/mcp',
|
|
30
|
-
sync: 'http://localhost:3011',
|
|
31
29
|
}
|
|
32
30
|
: {
|
|
33
31
|
site: `https://${fqdn}`,
|
|
34
32
|
api: `https://api.${fqdn}/api`,
|
|
35
33
|
auth: `https://auth.${fqdn}`,
|
|
36
|
-
mcp: `https://memory.${fqdn}/mcp`,
|
|
37
|
-
sync: `https://sync.${fqdn}`,
|
|
38
34
|
};
|
|
39
35
|
};
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export interface RequestHeaderOptions {
|
|
3
|
-
component: RequestComponent;
|
|
4
|
-
daemonVersion?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare const requestHeaders: (opts: RequestHeaderOptions) => Record<string, string>;
|
|
1
|
+
export declare const requestHeaders: () => Record<string, string>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { VERSION } from '../../utils/version.js';
|
|
2
|
-
// Identify the caller
|
|
3
|
-
export const requestHeaders = (
|
|
4
|
-
'User-Agent': `agentage
|
|
2
|
+
// Identify the caller and its version so the server can observe which CLI connects.
|
|
3
|
+
export const requestHeaders = () => ({
|
|
4
|
+
'User-Agent': `agentage-cli/${VERSION}`,
|
|
5
5
|
'X-Agentage-CLI-Version': VERSION,
|
|
6
|
-
'X-Agentage-Daemon-Version': opts.component === 'daemon' ? VERSION : (opts.daemonVersion ?? 'none'),
|
|
7
6
|
});
|
|
@@ -2,22 +2,6 @@ import { type AuthEnvMismatch } from '../auth/env-match.js';
|
|
|
2
2
|
import { type AuthState } from '../fs/config.js';
|
|
3
3
|
import { type Env } from '../net/origins.js';
|
|
4
4
|
import { type UpdateInfo } from '../update/update-check.js';
|
|
5
|
-
import { type VaultStatus } from './vaults-status.js';
|
|
6
|
-
export interface DaemonSyncSummary {
|
|
7
|
-
vaults: number;
|
|
8
|
-
state: 'ok' | 'error' | 'syncing';
|
|
9
|
-
lastRun?: string;
|
|
10
|
-
lastError?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface DaemonStatus {
|
|
13
|
-
running: boolean;
|
|
14
|
-
pid?: number;
|
|
15
|
-
port: number;
|
|
16
|
-
uptimeSeconds?: number;
|
|
17
|
-
mcp?: boolean;
|
|
18
|
-
daemonVersion?: string;
|
|
19
|
-
sync?: DaemonSyncSummary;
|
|
20
|
-
}
|
|
21
5
|
export interface StatusReport {
|
|
22
6
|
version: string;
|
|
23
7
|
fqdn: string;
|
|
@@ -40,7 +24,5 @@ export interface StatusReport {
|
|
|
40
24
|
reachable: boolean;
|
|
41
25
|
};
|
|
42
26
|
update: UpdateInfo;
|
|
43
|
-
daemon?: DaemonStatus;
|
|
44
|
-
vaults: VaultStatus[];
|
|
45
27
|
}
|
|
46
28
|
export declare const gatherStatus: (auth: AuthState | null, fqdn: string) => Promise<StatusReport>;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { AuthRequiredError, introspectToken } from '../auth/api.js';
|
|
2
2
|
import { detectEnvMismatch } from '../auth/env-match.js';
|
|
3
|
-
import { health, syncStatus } from '../daemon/daemon-client.js';
|
|
4
3
|
import { fetchJsonUnref } from '../net/http.js';
|
|
5
4
|
import { requestHeaders } from '../net/user-agent.js';
|
|
6
5
|
import { environment, links } from '../net/origins.js';
|
|
7
6
|
import { checkForUpdate } from '../update/update-check.js';
|
|
8
7
|
import { VERSION } from '../../utils/version.js';
|
|
9
|
-
import { isDaemonRunning, resolvePort } from '../../daemon/lifecycle.js';
|
|
10
|
-
import { buildVaultStatuses } from './vaults-status.js';
|
|
11
8
|
// node:https via fetchJsonUnref, not global fetch: undici's ref'd connect timer keeps the process
|
|
12
9
|
// alive ~10s after an aborted request on a packet-drop network, stalling `status` exit.
|
|
13
10
|
const checkEndpoint = async (apiUrl, headers) => {
|
|
@@ -20,40 +17,6 @@ const checkSite = async (siteUrl, headers) => {
|
|
|
20
17
|
const res = await fetchJsonUnref(siteUrl, 3000, headers);
|
|
21
18
|
return res !== null;
|
|
22
19
|
};
|
|
23
|
-
// Fold the per-vault git states into one summary: any error wins, then any in-flight run, else ok.
|
|
24
|
-
// lastRun is the freshest reported; lastError the first seen. Account vaults are absent by design -
|
|
25
|
-
// they have no sync channel, so they are never counted as synced here (the vaults block names them).
|
|
26
|
-
const summarizeSync = (sync) => {
|
|
27
|
-
const git = Array.isArray(sync.vaults) ? sync.vaults : [];
|
|
28
|
-
const error = git.find((v) => v.lastError)?.lastError;
|
|
29
|
-
const running = git.some((v) => v.running);
|
|
30
|
-
const state = error ? 'error' : running ? 'syncing' : 'ok';
|
|
31
|
-
const lastRun = git
|
|
32
|
-
.map((v) => v.lastRun)
|
|
33
|
-
.filter((r) => Boolean(r))
|
|
34
|
-
.sort()
|
|
35
|
-
.at(-1);
|
|
36
|
-
return { vaults: git.length, state, lastRun, lastError: error };
|
|
37
|
-
};
|
|
38
|
-
const probeDaemon = async () => {
|
|
39
|
-
const port = resolvePort();
|
|
40
|
-
const h = await health(port);
|
|
41
|
-
if (!h)
|
|
42
|
-
return { status: { running: isDaemonRunning(), port }, sync: null };
|
|
43
|
-
const sync = await syncStatus(port);
|
|
44
|
-
const summary = sync ? summarizeSync(sync) : undefined;
|
|
45
|
-
const status = {
|
|
46
|
-
running: true,
|
|
47
|
-
pid: h.pid,
|
|
48
|
-
port,
|
|
49
|
-
uptimeSeconds: h.uptime,
|
|
50
|
-
// Absent on a legacy daemon that predates the /mcp gate; treat undefined as serving (on).
|
|
51
|
-
mcp: h.mcp !== false,
|
|
52
|
-
daemonVersion: h.version,
|
|
53
|
-
sync: summary && summary.vaults > 0 ? summary : undefined,
|
|
54
|
-
};
|
|
55
|
-
return { status, sync };
|
|
56
|
-
};
|
|
57
20
|
// AuthRequiredError = truly expired (terminal). TransientAuthError (or any non-terminal) = a blip
|
|
58
21
|
// while we could not re-verify: never rendered as expired, never told to run setup, exit stays 0.
|
|
59
22
|
const classifyAuthError = (err) => err instanceof AuthRequiredError
|
|
@@ -70,9 +33,7 @@ const mismatchAuth = (m) => ({
|
|
|
70
33
|
export const gatherStatus = async (auth, fqdn) => {
|
|
71
34
|
const target = links(fqdn);
|
|
72
35
|
const env = environment(fqdn);
|
|
73
|
-
|
|
74
|
-
const probe = await probeDaemon();
|
|
75
|
-
const headers = requestHeaders({ component: 'cli', daemonVersion: probe.status.daemonVersion });
|
|
36
|
+
const headers = requestHeaders();
|
|
76
37
|
// Endpoint reachability and the (npm-registry) update check are independent - run them
|
|
77
38
|
// together so `status` stays snappy.
|
|
78
39
|
const [reachable, siteReachable, update] = await Promise.all([
|
|
@@ -88,8 +49,6 @@ export const gatherStatus = async (auth, fqdn) => {
|
|
|
88
49
|
auth: { signedIn: false, note: 'not signed in - run: agentage setup' },
|
|
89
50
|
endpoint: { url: target.api, reachable },
|
|
90
51
|
update,
|
|
91
|
-
daemon: probe.status,
|
|
92
|
-
vaults: buildVaultStatuses(probe.sync, probe.status.running),
|
|
93
52
|
};
|
|
94
53
|
if (!auth)
|
|
95
54
|
return report;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_API_VERSION = "1.0.0";
|
package/dist/plugin.js
ADDED
package/dist/program.js
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { registerDaemon } from './commands/daemon/daemon-cmd.js';
|
|
4
|
-
import { registerMemory } from './commands/memory/memory.js';
|
|
5
3
|
import { registerSetup } from './commands/auth/setup.js';
|
|
6
4
|
import { registerStatus } from './commands/status/status.js';
|
|
7
5
|
import { registerUpdate } from './commands/update/update.js';
|
|
8
|
-
import { registerVault } from './commands/vault/vault.js';
|
|
9
|
-
import { disableDaemon } from './lib/daemon/daemon-pref.js';
|
|
10
6
|
import { refreshUpdateCache, updateHint } from './lib/update/update-cache.js';
|
|
11
7
|
import { VERSION } from './utils/version.js';
|
|
12
8
|
const program = new Command();
|
|
13
9
|
program
|
|
14
10
|
.name('agentage')
|
|
15
|
-
.description('
|
|
16
|
-
.version(VERSION)
|
|
17
|
-
|
|
18
|
-
program.addHelpText('after', '\nNew here? Run: agentage vault add <name> --local, or agentage setup to connect an account.');
|
|
19
|
-
program.hook('preAction', () => {
|
|
20
|
-
if (program.opts().daemon === false)
|
|
21
|
-
disableDaemon();
|
|
22
|
-
});
|
|
11
|
+
.description('Connect this machine to agentage from the terminal')
|
|
12
|
+
.version(VERSION);
|
|
13
|
+
program.addHelpText('after', '\nNew here? Run: agentage setup to connect this machine.');
|
|
23
14
|
// After any command (except the update-aware ones, which report richer state themselves), print a
|
|
24
15
|
// one-line hint from the cache, then kick a fire-and-forget refresh. Never awaited, never throws -
|
|
25
16
|
// the command output is already done and the cache-only read keeps this instant. The hint is
|
|
@@ -37,11 +28,8 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
|
37
28
|
});
|
|
38
29
|
registerSetup(program);
|
|
39
30
|
registerStatus(program);
|
|
40
|
-
registerVault(program);
|
|
41
|
-
registerMemory(program);
|
|
42
|
-
registerDaemon(program);
|
|
43
31
|
registerUpdate(program);
|
|
44
|
-
// Called by the bin (src/cli.ts)
|
|
32
|
+
// Called by the bin (src/cli.ts).
|
|
45
33
|
export const run = async () => {
|
|
46
34
|
await program.parseAsync().catch((err) => {
|
|
47
35
|
console.error(err instanceof Error ? err.message : String(err));
|