@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.
Files changed (85) hide show
  1. package/README.md +53 -83
  2. package/dist/cli.js +0 -5
  3. package/dist/commands/auth/setup.d.ts +0 -2
  4. package/dist/commands/auth/setup.js +0 -18
  5. package/dist/commands/status/status.js +1 -25
  6. package/dist/commands/update/update.d.ts +0 -9
  7. package/dist/commands/update/update.js +0 -20
  8. package/dist/core/flag.d.ts +1 -0
  9. package/dist/core/flag.js +1 -0
  10. package/dist/lib/auth/api.js +1 -1
  11. package/dist/lib/auth/oauth.js +1 -1
  12. package/dist/lib/net/origins.d.ts +0 -2
  13. package/dist/lib/net/origins.js +1 -5
  14. package/dist/lib/net/user-agent.d.ts +1 -6
  15. package/dist/lib/net/user-agent.js +3 -4
  16. package/dist/lib/status/status-info.d.ts +0 -18
  17. package/dist/lib/status/status-info.js +1 -42
  18. package/dist/plugin.d.ts +1 -0
  19. package/dist/plugin.js +2 -0
  20. package/dist/program.js +4 -16
  21. package/package.json +17 -12
  22. package/dist/commands/daemon/daemon-cmd.d.ts +0 -2
  23. package/dist/commands/daemon/daemon-cmd.js +0 -107
  24. package/dist/commands/memory/memory-verbs.d.ts +0 -21
  25. package/dist/commands/memory/memory-verbs.js +0 -98
  26. package/dist/commands/memory/memory.d.ts +0 -2
  27. package/dist/commands/memory/memory.js +0 -54
  28. package/dist/commands/vault/vault-sync.d.ts +0 -13
  29. package/dist/commands/vault/vault-sync.js +0 -90
  30. package/dist/commands/vault/vault.d.ts +0 -22
  31. package/dist/commands/vault/vault.js +0 -129
  32. package/dist/daemon/actions.d.ts +0 -5
  33. package/dist/daemon/actions.js +0 -22
  34. package/dist/daemon/client-provider.d.ts +0 -2
  35. package/dist/daemon/client-provider.js +0 -16
  36. package/dist/daemon/entry.d.ts +0 -8
  37. package/dist/daemon/entry.js +0 -113
  38. package/dist/daemon/guards.d.ts +0 -3
  39. package/dist/daemon/guards.js +0 -13
  40. package/dist/daemon/lifecycle.d.ts +0 -17
  41. package/dist/daemon/lifecycle.js +0 -151
  42. package/dist/daemon/mcp-http.d.ts +0 -3
  43. package/dist/daemon/mcp-http.js +0 -34
  44. package/dist/daemon/server.d.ts +0 -23
  45. package/dist/daemon/server.js +0 -135
  46. package/dist/daemon-entry.d.ts +0 -1
  47. package/dist/daemon-entry.js +0 -9
  48. package/dist/lib/auth/provision.d.ts +0 -14
  49. package/dist/lib/auth/provision.js +0 -49
  50. package/dist/lib/daemon/daemon-client.d.ts +0 -37
  51. package/dist/lib/daemon/daemon-client.js +0 -136
  52. package/dist/lib/daemon/daemon-pref.d.ts +0 -2
  53. package/dist/lib/daemon/daemon-pref.js +0 -10
  54. package/dist/lib/memory/memory-client.d.ts +0 -30
  55. package/dist/lib/memory/memory-client.js +0 -98
  56. package/dist/lib/status/format.d.ts +0 -1
  57. package/dist/lib/status/format.js +0 -12
  58. package/dist/lib/status/vaults-format.d.ts +0 -2
  59. package/dist/lib/status/vaults-format.js +0 -49
  60. package/dist/lib/status/vaults-status.d.ts +0 -12
  61. package/dist/lib/status/vaults-status.js +0 -54
  62. package/dist/lib/vault/vault-registry.d.ts +0 -12
  63. package/dist/lib/vault/vault-registry.js +0 -81
  64. package/dist/lib/vault/vaults.d.ts +0 -10
  65. package/dist/lib/vault/vaults.js +0 -66
  66. package/dist/lib/vault/vaults.schema.d.ts +0 -2
  67. package/dist/lib/vault/vaults.schema.js +0 -10
  68. package/dist/mcp/local-server.d.ts +0 -4
  69. package/dist/mcp/local-server.js +0 -26
  70. package/dist/observability.d.ts +0 -4
  71. package/dist/observability.js +0 -35
  72. package/dist/sync/discover/watcher.d.ts +0 -26
  73. package/dist/sync/discover/watcher.js +0 -159
  74. package/dist/sync/git/conflict.d.ts +0 -1
  75. package/dist/sync/git/conflict.js +0 -16
  76. package/dist/sync/git/cycle.d.ts +0 -18
  77. package/dist/sync/git/cycle.js +0 -158
  78. package/dist/sync/git/git-exec.d.ts +0 -22
  79. package/dist/sync/git/git-exec.js +0 -56
  80. package/dist/sync/git/manager.d.ts +0 -28
  81. package/dist/sync/git/manager.js +0 -114
  82. package/dist/sync/git/planner.d.ts +0 -15
  83. package/dist/sync/git/planner.js +0 -46
  84. package/dist/sync/git/remote-url.d.ts +0 -3
  85. package/dist/sync/git/remote-url.js +0 -30
package/package.json CHANGED
@@ -1,10 +1,21 @@
1
1
  {
2
2
  "name": "@agentage/cli",
3
- "version": "0.35.0",
3
+ "version": "0.38.0",
4
4
  "description": "The agentage CLI - connect this machine to agentage from the terminal",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./plugin": {
14
+ "types": "./dist/plugin.d.ts",
15
+ "default": "./dist/plugin.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
8
19
  "bin": {
9
20
  "agentage": "./dist/cli.js"
10
21
  },
@@ -25,11 +36,11 @@
25
36
  },
26
37
  "scripts": {
27
38
  "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
28
- "type-check": "tsc --noEmit",
29
- "lint": "eslint src",
30
- "lint:fix": "eslint src --fix",
31
- "format": "prettier --write \"{src,e2e}/**/*.ts\"",
32
- "format:check": "prettier --check \"{src,e2e}/**/*.ts\"",
39
+ "type-check": "tsc --noEmit && tsc -p fixtures",
40
+ "lint": "eslint src e2e fixtures",
41
+ "lint:fix": "eslint src e2e fixtures --fix",
42
+ "format": "prettier --write \"{src,e2e}/**/*.ts\" \"fixtures/**/*.{js,json}\"",
43
+ "format:check": "prettier --check \"{src,e2e}/**/*.ts\" \"fixtures/**/*.{js,json}\"",
33
44
  "test": "vitest run",
34
45
  "test:watch": "vitest",
35
46
  "test:coverage": "vitest run --coverage",
@@ -40,17 +51,11 @@
40
51
  "prepublishOnly": "npm run verify"
41
52
  },
42
53
  "dependencies": {
43
- "@agentage/memory-core": "^0.5.0",
44
- "@agentage/observability": "^1.4.0",
45
- "@agentage/server-memory": "^0.11.0",
46
- "@modelcontextprotocol/sdk": "^1.29.0",
47
- "@opentelemetry/api": "^1.9.0",
48
54
  "chalk": "^6.0.0",
49
55
  "commander": "^15.0.0",
50
56
  "open": "^11.0.0"
51
57
  },
52
58
  "devDependencies": {
53
- "@anthropic-ai/sdk": "0.131.0",
54
59
  "@playwright/test": "latest",
55
60
  "@types/node": "latest",
56
61
  "@typescript-eslint/eslint-plugin": "latest",
@@ -1,2 +0,0 @@
1
- import { type Command } from 'commander';
2
- export declare const registerDaemon: (program: Command) => void;
@@ -1,107 +0,0 @@
1
- import chalk from 'chalk';
2
- import { isDaemonRunning, resolvePort, signalPid, stopDaemonSafely, } from '../../daemon/lifecycle.js';
3
- import { health, mismatchNotice, spawnDaemon, syncStatus } from '../../lib/daemon/daemon-client.js';
4
- const startAction = async (opts = {}) => {
5
- const port = resolvePort();
6
- const existing = await health(port);
7
- if (existing) {
8
- console.log(chalk.gray(`Daemon already running (pid ${existing.pid}, port ${port}).`));
9
- return;
10
- }
11
- const noMcp = opts.mcp === false;
12
- const outcome = await spawnDaemon(port, { noMcp });
13
- if (!outcome.ok) {
14
- if (outcome.reason === 'port-in-use') {
15
- console.error(chalk.red(`Port ${port} is in use by another process - set AGENTAGE_DAEMON_PORT to use a different port.`));
16
- }
17
- else {
18
- console.error(chalk.red('Daemon failed to start.'));
19
- }
20
- process.exitCode = 1;
21
- return;
22
- }
23
- const h = await health(port);
24
- const mcp = noMcp ? 'off' : 'on';
25
- console.log(chalk.green(`Daemon started (pid ${h?.pid ?? '?'}, port ${port}, mcp ${mcp}).`));
26
- };
27
- // Detect the daemon the same way `start` does (tokenless /health), so a legacy daemon that never
28
- // wrote this config dir's pidfile is not misreported as "not running". With a pidfile we own, use
29
- // the safe pid-confirming stop; otherwise fall back to signalling the pid /health reports, or - if
30
- // none is available - report the truth rather than a false "not running".
31
- const stopAction = async () => {
32
- if (isDaemonRunning()) {
33
- if (await stopDaemonSafely())
34
- console.log(chalk.green('Daemon stopped.'));
35
- return;
36
- }
37
- const port = resolvePort();
38
- const h = await health(port);
39
- if (!h) {
40
- console.log(chalk.gray('Daemon is not running.'));
41
- return;
42
- }
43
- if (typeof h.pid === 'number' && signalPid(h.pid)) {
44
- console.log(chalk.green(`Daemon stopped (pid ${h.pid}).`));
45
- return;
46
- }
47
- const who = typeof h.pid === 'number' ? `pid ${h.pid}` : `port ${port}`;
48
- console.error(chalk.yellow(`Daemon is running (${who}) but has no pidfile to stop it safely; stop it with: kill ${h.pid ?? '<pid>'}`));
49
- process.exitCode = 1;
50
- };
51
- const statusAction = async () => {
52
- const port = resolvePort();
53
- const h = await health(port);
54
- if (!h) {
55
- console.log(isDaemonRunning()
56
- ? chalk.yellow(`Daemon pid file present but unreachable on port ${port}.`)
57
- : chalk.gray('Daemon is not running.'));
58
- return;
59
- }
60
- console.log(`pid ${h.pid ?? '?'}`);
61
- console.log(`port ${port}`);
62
- if (typeof h.uptime === 'number')
63
- console.log(`uptime ${h.uptime}s`);
64
- if (typeof h.served === 'number')
65
- console.log(`served ${h.served}`);
66
- console.log(`mcp ${h.mcp === false ? 'off' : 'on'}`);
67
- console.log(`version ${h.version}`);
68
- const notice = mismatchNotice(h.version);
69
- if (notice)
70
- console.log(chalk.yellow(notice));
71
- const sync = await syncStatus(port);
72
- if (sync && sync.vaults.length > 0) {
73
- console.log('sync');
74
- for (const v of sync.vaults) {
75
- const cadence = v.intervalSeconds > 0 ? `every ${v.intervalSeconds}s` : 'manual';
76
- const state = v.running
77
- ? 'running'
78
- : v.lastError
79
- ? `error: ${v.lastError}`
80
- : v.lastRun
81
- ? `ok ${v.lastRun}`
82
- : 'scheduled';
83
- console.log(` ${v.vault.padEnd(16)} ${cadence.padEnd(12)} ${state}`);
84
- }
85
- }
86
- if (sync?.discover && sync.discover.roots.length > 0) {
87
- console.log(`discover roots (${sync.discover.roots.length})`);
88
- for (const root of sync.discover.roots)
89
- console.log(` ${root}`);
90
- }
91
- };
92
- export const registerDaemon = (program) => {
93
- const daemon = program.command('daemon').description('Manage the local engine daemon');
94
- daemon
95
- .command('start')
96
- .description('Start the daemon (idempotent)')
97
- .option('--no-mcp', 'do not serve the local MCP endpoint at /mcp')
98
- .action((opts) => startAction(opts));
99
- daemon
100
- .command('stop')
101
- .description('Stop the daemon')
102
- .action(() => stopAction());
103
- daemon
104
- .command('status')
105
- .description('Show the daemon pid, uptime, and version')
106
- .action(() => statusAction());
107
- };
@@ -1,21 +0,0 @@
1
- import { type MemoryClient } from '../../lib/memory/memory-client.js';
2
- export interface CommonOpts {
3
- vault?: string;
4
- json?: boolean;
5
- }
6
- export declare const runSearch: (query: string, opts: CommonOpts & {
7
- limit?: string;
8
- }, client?: MemoryClient) => Promise<void>;
9
- export declare const runRead: (ref: string, opts: CommonOpts, client?: MemoryClient) => Promise<void>;
10
- export declare const runWrite: (ref: string, opts: CommonOpts & {
11
- body?: string;
12
- frontmatter?: string;
13
- }, client?: MemoryClient) => Promise<void>;
14
- export declare const runEdit: (ref: string, opts: CommonOpts & {
15
- old?: string;
16
- new?: string;
17
- body?: string;
18
- append?: boolean;
19
- }, client?: MemoryClient) => Promise<void>;
20
- export declare const runList: (folder: string | undefined, opts: CommonOpts, client?: MemoryClient) => Promise<void>;
21
- export declare const runDelete: (ref: string, opts: CommonOpts, client?: MemoryClient) => Promise<void>;
@@ -1,98 +0,0 @@
1
- import chalk from 'chalk';
2
- import { ensureDaemon } from '../../lib/daemon/daemon-client.js';
3
- import { daemonDisabled } from '../../lib/daemon/daemon-pref.js';
4
- import { createDirectClient } from '../../lib/memory/memory-client.js';
5
- import { loadVaultsConfig } from '../../lib/vault/vaults.js';
6
- // Default engine path (DO3/DO4): the daemon - single writer, autostarted - when reachable; the
7
- // in-process DirectClient as a seamless fallback (--no-daemon, sandbox/CI, or fork blocked).
8
- const resolveClient = async () => {
9
- const direct = () => createDirectClient(loadVaultsConfig().config);
10
- if (daemonDisabled())
11
- return direct();
12
- const daemon = await ensureDaemon();
13
- return daemon ?? direct();
14
- };
15
- const readStdin = async () => {
16
- const chunks = [];
17
- for await (const chunk of process.stdin)
18
- chunks.push(chunk);
19
- return Buffer.concat(chunks).toString('utf-8');
20
- };
21
- // --frontmatter must be a JSON object; a raw parser error or a non-object value is unfriendly.
22
- const parseFrontmatter = (raw) => {
23
- if (!raw)
24
- return undefined;
25
- let parsed;
26
- try {
27
- parsed = JSON.parse(raw);
28
- }
29
- catch (err) {
30
- const detail = err instanceof Error ? err.message : String(err);
31
- throw new Error(`--frontmatter must be a JSON object, e.g. '{"tags":["x"]}': ${detail}`);
32
- }
33
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
34
- throw new Error(`--frontmatter must be a JSON object, e.g. '{"tags":["x"]}'`);
35
- return parsed;
36
- };
37
- const emit = (json, data, human) => {
38
- if (json)
39
- console.log(JSON.stringify(data, null, 2));
40
- else
41
- human();
42
- };
43
- // Flatten a folder-tree to the file paths it contains, in tree order.
44
- const treeFiles = (entries) => entries.flatMap((e) => (e.type === 'file' ? [e.path] : e.entries ? treeFiles(e.entries) : []));
45
- export const runSearch = async (query, opts, client) => {
46
- const c = client ?? (await resolveClient());
47
- const out = await c.search(query, {
48
- vault: opts.vault,
49
- limit: opts.limit ? Number.parseInt(opts.limit, 10) : undefined,
50
- });
51
- emit(opts.json ?? false, out, () => {
52
- if (out.results.length === 0)
53
- console.log('No matches.');
54
- for (const h of out.results)
55
- console.log(`${h.path}\n ${chalk.gray(h.snippet)}`);
56
- });
57
- };
58
- export const runRead = async (ref, opts, client) => {
59
- const c = client ?? (await resolveClient());
60
- const doc = await c.read(ref, { vault: opts.vault });
61
- emit(opts.json ?? false, doc, () => console.log(doc.body));
62
- };
63
- export const runWrite = async (ref, opts, client) => {
64
- const c = client ?? (await resolveClient());
65
- const inlineBody = opts.body !== undefined && opts.body !== '-';
66
- if (!inlineBody && process.stdin.isTTY)
67
- throw new Error('provide --body, or pipe content on stdin');
68
- const body = inlineBody ? opts.body : await readStdin();
69
- const frontmatter = parseFrontmatter(opts.frontmatter);
70
- const out = await c.write(ref, body, { vault: opts.vault, frontmatter });
71
- emit(opts.json ?? false, out, () => console.log(chalk.green(`Wrote ${out.path}`)));
72
- };
73
- export const runEdit = async (ref, opts, client) => {
74
- const c = client ?? (await resolveClient());
75
- if (opts.old === undefined && opts.body === undefined)
76
- throw new Error('specify --old/--new for a replacement or --body to overwrite');
77
- const op = opts.old !== undefined
78
- ? { mode: 'str_replace', old_str: opts.old, new_str: opts.new ?? '' }
79
- : { mode: opts.append ? 'append' : 'replace', body: opts.body };
80
- const out = await c.edit(ref, op, { vault: opts.vault });
81
- emit(opts.json ?? false, out, () => console.log(chalk.green(`Edited ${out.path}`)));
82
- };
83
- export const runList = async (folder, opts, client) => {
84
- const c = client ?? (await resolveClient());
85
- const out = await c.list(folder, { vault: opts.vault });
86
- emit(opts.json ?? false, out, () => {
87
- const files = treeFiles(out.entries);
88
- if (files.length === 0)
89
- console.log('No documents.');
90
- for (const p of files)
91
- console.log(p);
92
- });
93
- };
94
- export const runDelete = async (ref, opts, client) => {
95
- const c = client ?? (await resolveClient());
96
- const out = await c.delete(ref, { vault: opts.vault });
97
- emit(opts.json ?? false, out, () => console.log(`Deleted ${out.path} (recoverable from git history)`));
98
- };
@@ -1,2 +0,0 @@
1
- import { type Command } from 'commander';
2
- export declare const registerMemory: (program: Command) => void;
@@ -1,54 +0,0 @@
1
- import chalk from 'chalk';
2
- import { translateEngineMessage } from '../../lib/memory/memory-client.js';
3
- import { runDelete, runEdit, runList, runRead, runSearch, runWrite, } from './memory-verbs.js';
4
- const guard = (fn) => fn().catch((err) => {
5
- const message = translateEngineMessage(err instanceof Error ? err.message : String(err));
6
- console.error(chalk.red(message));
7
- process.exitCode = 1;
8
- });
9
- export const registerMemory = (program) => {
10
- const memory = program.command('memory').description('Read + write memory, offline');
11
- memory
12
- .command('search <query...>')
13
- .description('Search a vault (git grep)')
14
- .option('--vault <name>', 'target vault')
15
- .option('--limit <n>', 'max hits (default 20)')
16
- .option('--json', 'machine-readable output')
17
- .action((query, opts) => guard(() => runSearch(query.join(' '), opts)));
18
- memory
19
- .command('read <ref>')
20
- .description('Print a document (@<vault>/<path> or <path> --vault)')
21
- .option('--vault <name>', 'target vault')
22
- .option('--json', 'machine-readable output')
23
- .action((ref, opts) => guard(() => runRead(ref, opts)));
24
- memory
25
- .command('write <ref>')
26
- .description('Create or overwrite a document (body from --body or stdin)')
27
- .option('--vault <name>', 'target vault')
28
- .option('--body <text>', 'document body (omit or "-" to read stdin)')
29
- .option('--frontmatter <json>', 'frontmatter as a JSON object')
30
- .option('--json', 'machine-readable output')
31
- .action((ref, opts) => guard(() => runWrite(ref, opts)));
32
- memory
33
- .command('edit <ref>')
34
- .description('Edit a document: str_replace (--old/--new) or --body (--append)')
35
- .option('--vault <name>', 'target vault')
36
- .option('--old <str>', 'exact, unique substring to replace')
37
- .option('--new <str>', 'replacement (omit to delete the match)')
38
- .option('--body <text>', 'replace the whole body')
39
- .option('--append', 'append --body instead of replacing')
40
- .option('--json', 'machine-readable output')
41
- .action((ref, opts) => guard(() => runEdit(ref, opts)));
42
- memory
43
- .command('list [folder]')
44
- .description('List documents in a vault (optionally under a folder)')
45
- .option('--vault <name>', 'target vault')
46
- .option('--json', 'machine-readable output')
47
- .action((folder, opts) => guard(() => runList(folder, opts)));
48
- memory
49
- .command('delete <ref>')
50
- .description('Delete a document (recoverable from git history)')
51
- .option('--vault <name>', 'target vault')
52
- .option('--json', 'machine-readable output')
53
- .action((ref, opts) => guard(() => runDelete(ref, opts)));
54
- };
@@ -1,13 +0,0 @@
1
- import { type VaultsConfig } from '@agentage/memory-core';
2
- import { type SyncResult } from '../../sync/git/cycle.js';
3
- import { type SyncTarget } from '../../sync/git/planner.js';
4
- export interface VaultSyncDeps {
5
- loadConfig: () => VaultsConfig;
6
- daemonPort: () => Promise<number | null>;
7
- runViaDaemon: (port: number, vault: string) => Promise<SyncResult>;
8
- runGitInProcess: (target: SyncTarget) => Promise<SyncResult>;
9
- log: (msg: string) => void;
10
- }
11
- export declare const ACCOUNT_NO_CHANNEL = "not synced - account vaults have no sync channel";
12
- export declare const runVaultSync: (name: string | undefined, deps: VaultSyncDeps) => Promise<void>;
13
- export declare const defaultVaultSyncDeps: () => VaultSyncDeps;
@@ -1,90 +0,0 @@
1
- import chalk from 'chalk';
2
- import { isAccountVault } from '@agentage/memory-core';
3
- import { health, syncRun } from '../../lib/daemon/daemon-client.js';
4
- import { daemonDisabled } from '../../lib/daemon/daemon-pref.js';
5
- import { loadVaultsConfig } from '../../lib/vault/vaults.js';
6
- import { resolvePort } from '../../daemon/lifecycle.js';
7
- import { runSyncCycle } from '../../sync/git/cycle.js';
8
- import { syncTargets } from '../../sync/git/planner.js';
9
- import { redactRemoteUrl } from '../../sync/git/remote-url.js';
10
- export const ACCOUNT_NO_CHANNEL = 'not synced - account vaults have no sync channel';
11
- const describeGit = (r) => {
12
- if (!r.ok)
13
- return chalk.red(`failed (${r.reason ?? 'error'})${r.error ? `: ${r.error}` : ''}`);
14
- if (r.skipped)
15
- return chalk.yellow(`skipped (${r.skipped})`);
16
- const bits = [];
17
- if (r.committed)
18
- bits.push('committed');
19
- if (r.conflicts.length)
20
- bits.push(`${r.conflicts.length} conflict copy(ies)`);
21
- if (r.pushed)
22
- bits.push('pushed');
23
- return chalk.green(bits.length ? bits.join(', ') : 'up to date');
24
- };
25
- const report = (log, r) => {
26
- log(`${r.vault} -> ${redactRemoteUrl(r.remote)}: ${describeGit(r)}`);
27
- for (const c of r.conflicts)
28
- log(` kept remote copy: ${c}`);
29
- };
30
- // Account vaults whose only origin is the reserved `agentage` remote: nothing syncs them. They are
31
- // named, never silently skipped, so `vault sync` cannot read as "everything is up to date".
32
- const unsyncableAccountVaults = (config, gitTargets) => {
33
- const git = new Set(gitTargets.map((t) => t.vault));
34
- return Object.entries(config.vaults ?? {})
35
- .filter(([vault, entry]) => isAccountVault(entry) && !git.has(vault))
36
- .map(([vault]) => vault);
37
- };
38
- const reportAccounts = (log, vaults) => {
39
- for (const vault of vaults)
40
- log(`${vault} (account): ${chalk.yellow(ACCOUNT_NO_CHANNEL)}`);
41
- };
42
- // `agentage vault sync [name]`: sync one vault (or every syncable vault). Git-origin vaults commit
43
- // + push + pull-rebase; account (agentage) vaults have no sync channel and are reported as such.
44
- // Prefers a running daemon (single writer), else runs the cycle in-process. Works for interval-0
45
- // (manual-only) vaults and with the daemon down. Failures are surfaced, not thrown (V6: never a crash).
46
- export const runVaultSync = async (name, deps) => {
47
- const config = deps.loadConfig();
48
- // A named vault that is not registered is an error (mirrors `vault remove`), not a silent no-op.
49
- if (name && !(config.vaults ?? {})[name])
50
- throw new Error(`vault '${name}' not found`);
51
- const gitTargets = syncTargets(config).filter((t) => !name || t.vault === name);
52
- const accounts = unsyncableAccountVaults(config, gitTargets).filter((v) => !name || v === name);
53
- if (gitTargets.length === 0) {
54
- reportAccounts(deps.log, accounts);
55
- if (accounts.length === 0)
56
- deps.log(name
57
- ? `No syncable origin configured for vault '${name}'.`
58
- : 'No syncable vaults. Add one with `agentage vault add <name>`.');
59
- return;
60
- }
61
- const vaults = [...new Set(gitTargets.map((t) => t.vault))];
62
- deps.log(`Syncing ${vaults.length} vault(s)...`);
63
- const port = await deps.daemonPort();
64
- if (port !== null) {
65
- for (const vault of vaults) {
66
- deps.log(`${vault}...`);
67
- report(deps.log, await deps.runViaDaemon(port, vault));
68
- }
69
- }
70
- else {
71
- for (const target of gitTargets) {
72
- deps.log(`${target.vault}...`);
73
- report(deps.log, await deps.runGitInProcess(target));
74
- }
75
- }
76
- reportAccounts(deps.log, accounts);
77
- };
78
- const resolveDaemonPort = async () => {
79
- if (daemonDisabled())
80
- return null;
81
- const port = resolvePort();
82
- return (await health(port)) ? port : null;
83
- };
84
- export const defaultVaultSyncDeps = () => ({
85
- loadConfig: () => loadVaultsConfig().config,
86
- daemonPort: resolveDaemonPort,
87
- runViaDaemon: syncRun,
88
- runGitInProcess: runSyncCycle,
89
- log: (msg) => console.log(msg),
90
- });
@@ -1,22 +0,0 @@
1
- import { type Command } from 'commander';
2
- import { type VaultsConfig } from '@agentage/memory-core';
3
- import { type ProvisionResult } from '../../lib/auth/provision.js';
4
- import { type LoadedVaults } from '../../lib/vault/vaults.js';
5
- export interface VaultDeps {
6
- load: () => LoadedVaults;
7
- mutate: (fn: (config: VaultsConfig) => VaultsConfig | null | void) => Promise<VaultsConfig>;
8
- ensureDir: (path: string) => void;
9
- provision: (name: string) => Promise<ProvisionResult>;
10
- log: (msg: string) => void;
11
- }
12
- export interface VaultAddOptions {
13
- local?: string | boolean;
14
- git?: string;
15
- path?: string;
16
- }
17
- export declare const runVaultAdd: (name: string, opts: VaultAddOptions, deps?: VaultDeps) => Promise<void>;
18
- export declare const runVaultRemove: (name: string, deps?: VaultDeps) => Promise<void>;
19
- export declare const runVaultList: (opts: {
20
- json?: boolean;
21
- }, deps?: VaultDeps) => void;
22
- export declare const registerVault: (program: Command) => void;
@@ -1,129 +0,0 @@
1
- import chalk from 'chalk';
2
- import { isAccountVault } from '@agentage/memory-core';
3
- import { addVault, appendDiscoverIgnore, ensureVaultDir, formatVaultLine, redactEntry, removeVault, vaultType, } from '../../lib/vault/vault-registry.js';
4
- import { defaultProvisionDeps, provisionAccountVault, } from '../../lib/auth/provision.js';
5
- import { loadVaultsConfig, mutateVaultsConfig } from '../../lib/vault/vaults.js';
6
- import { assertSafeRemoteUrl, redactRemoteUrl } from '../../sync/git/remote-url.js';
7
- import { defaultVaultSyncDeps, runVaultSync } from './vault-sync.js';
8
- const defaultDeps = {
9
- load: loadVaultsConfig,
10
- mutate: mutateVaultsConfig,
11
- ensureDir: ensureVaultDir,
12
- provision: (name) => provisionAccountVault(name, defaultProvisionDeps()),
13
- log: (msg) => console.log(msg),
14
- };
15
- const buildEntry = (name, opts) => {
16
- const hasLocal = opts.local !== undefined;
17
- if (hasLocal && opts.git)
18
- throw new Error('choose one of --local or --git, not both');
19
- if (opts.path !== undefined && (hasLocal || opts.git))
20
- throw new Error('--path applies only to an account vault (drop --local and --git)');
21
- // A --git vault is a local working copy that syncs to an external git remote (path + origin);
22
- // the daemon commits/pushes and pulls it per its interval.
23
- if (opts.git) {
24
- assertSafeRemoteUrl(opts.git);
25
- return { path: `~/vaults/${name}`, origin: [{ remote: opts.git }], mcp: ['local'] };
26
- }
27
- if (hasLocal) {
28
- const path = typeof opts.local === 'string' ? opts.local : `~/vaults/${name}`;
29
- return { path, mcp: ['local'] };
30
- }
31
- // No --local/--git: an account vault - a local folder plus a memory in your account; this CLI
32
- // has no channel that syncs the two.
33
- return { path: opts.path ?? `~/vaults/${name}`, origin: [{ remote: 'agentage' }] };
34
- };
35
- export const runVaultAdd = async (name, opts, deps = defaultDeps) => {
36
- const entry = buildEntry(name, opts);
37
- // The dup check + save run together under the lock, against the FRESH on-disk config.
38
- await deps.mutate((config) => addVault(config, name, entry));
39
- if (entry.path)
40
- deps.ensureDir(entry.path);
41
- const kind = vaultType(entry);
42
- const where = entry.path ?? entry.origin?.[0]?.remote ?? '';
43
- const via = kind === 'git' && entry.origin?.length ? ` <- ${redactRemoteUrl(entry.origin[0].remote)}` : '';
44
- deps.log(chalk.green(`Added vault '${name}' (${kind}) -> ${where}${via}`));
45
- // Offline-first: the local entry is saved first; provisioning the account channel is never fatal.
46
- if (isAccountVault(entry))
47
- deps.log((await deps.provision(name)).message);
48
- };
49
- export const runVaultRemove = async (name, deps = defaultDeps) => {
50
- let wasDefault = false;
51
- let ignoredRoot = null;
52
- // The removal and the discover-ignore append run as one locked read-modify-write, against the
53
- // FRESH on-disk config, else a concurrent writer's update is lost or the next scan re-adds the folder.
54
- const next = await deps.mutate((config) => {
55
- wasDefault = config.default === name;
56
- const path = config.vaults?.[name]?.path;
57
- let updated = removeVault(config, name);
58
- const ignored = path ? appendDiscoverIgnore(updated, name, path) : null;
59
- if (ignored) {
60
- updated = ignored.config;
61
- ignoredRoot = ignored.root;
62
- }
63
- return updated;
64
- });
65
- deps.log(`Removed vault '${name}' (files left on disk).`);
66
- if (ignoredRoot)
67
- deps.log(`Added '${name}' to ${ignoredRoot} ignore so it is not re-discovered.`);
68
- if (wasDefault && next.default)
69
- deps.log(`Default vault is now '${next.default}'.`);
70
- };
71
- export const runVaultList = (opts, deps = defaultDeps) => {
72
- const { config } = deps.load();
73
- const vaults = config.vaults ?? {};
74
- const names = Object.keys(vaults);
75
- if (opts.json) {
76
- // Backward-compatible: still the name-keyed map, each entry annotated with its honest type.
77
- // Origin remotes are redacted so a credentialed URL never leaks into machine output.
78
- const out = Object.fromEntries(names.map((n) => [n, { ...redactEntry(vaults[n]), type: vaultType(vaults[n]) }]));
79
- deps.log(JSON.stringify(out, null, 2));
80
- return;
81
- }
82
- if (names.length === 0) {
83
- deps.log('No vaults registered. Add one with `agentage vault add <name> --local`.');
84
- return;
85
- }
86
- for (const name of names) {
87
- const suffix = name === config.default ? ' (default)' : '';
88
- deps.log(formatVaultLine(name, vaults[name]) + suffix);
89
- }
90
- };
91
- const guard = (fn) => {
92
- try {
93
- fn();
94
- }
95
- catch (err) {
96
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
97
- process.exitCode = 1;
98
- }
99
- };
100
- const guardAsync = (fn) => fn().catch((err) => {
101
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
102
- process.exitCode = 1;
103
- });
104
- export const registerVault = (program) => {
105
- const vault = program.command('vault').description('Manage local memory vaults');
106
- vault
107
- .command('list')
108
- .description('List registered vaults')
109
- .option('--json', 'machine-readable output')
110
- .action((opts) => guard(() => runVaultList(opts)));
111
- vault
112
- .command('add <name>')
113
- .description('Register a vault (account by default; --local or --git for a self-hosted one)')
114
- .option('--local [path]', 'a local folder (default ~/vaults/<name>)')
115
- .option('--git <remote>', 'a vault synced to an external git remote')
116
- .option('--path <dir>', 'account vault local mirror dir (default ~/vaults/<name>)')
117
- .action((name, opts) => guardAsync(() => runVaultAdd(name, opts)));
118
- vault
119
- .command('remove <name>')
120
- .description('Unregister a vault (files stay on disk)')
121
- .action((name) => guardAsync(() => runVaultRemove(name)));
122
- vault
123
- .command('sync [name]')
124
- .description('Sync vaults now (git commit/push/pull)')
125
- .action((name) => runVaultSync(name, defaultVaultSyncDeps()).catch((err) => {
126
- console.error(chalk.red(err instanceof Error ? err.message : String(err)));
127
- process.exitCode = 1;
128
- }));
129
- };
@@ -1,5 +0,0 @@
1
- import { type MemoryClient } from '../lib/memory/memory-client.js';
2
- export declare const MEMORY_VERBS: readonly ["search", "read", "write", "edit", "list", "delete"];
3
- export type MemoryVerb = (typeof MEMORY_VERBS)[number];
4
- export declare const isMemoryVerb: (v: string) => v is MemoryVerb;
5
- export declare const dispatchMemory: (client: MemoryClient, verb: MemoryVerb, body: unknown) => Promise<unknown>;
@@ -1,22 +0,0 @@
1
- export const MEMORY_VERBS = ['search', 'read', 'write', 'edit', 'list', 'delete'];
2
- export const isMemoryVerb = (v) => MEMORY_VERBS.includes(v);
3
- // Map one wire payload to the matching MemoryClient call. The daemon and the CLI share this verb
4
- // set so a daemon request runs the exact method a DirectClient would run in-process (DO2).
5
- export const dispatchMemory = (client, verb, body) => {
6
- const p = (body ?? {});
7
- switch (verb) {
8
- case 'search':
9
- return client.search(p['query'], p['opts']);
10
- case 'read':
11
- return client.read(p['ref'], p['opts']);
12
- case 'write':
13
- return client.write(p['ref'], p['body'], p['opts']);
14
- case 'edit':
15
- return client.edit(p['ref'], p['op'], p['opts']);
16
- case 'list':
17
- return client.list(p['folder'], p['opts']);
18
- case 'delete':
19
- return client.delete(p['ref'], p['opts']);
20
- }
21
- throw new Error(`unknown verb: ${verb}`);
22
- };
@@ -1,2 +0,0 @@
1
- import { type MemoryClient } from '../lib/memory/memory-client.js';
2
- export declare const createClientProvider: () => (() => MemoryClient);
@@ -1,16 +0,0 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import { createDirectClient } from '../lib/memory/memory-client.js';
3
- import { loadVaultsConfig, vaultsJsonPath } from '../lib/vault/vaults.js';
4
- // One in-process engine per daemon, rebuilt only when vaults.json changes on disk, so a
5
- // `vault add` between requests is picked up without a daemon restart.
6
- export const createClientProvider = () => {
7
- let cached;
8
- return () => {
9
- const path = vaultsJsonPath();
10
- const mtime = existsSync(path) ? statSync(path).mtimeMs : 0;
11
- if (!cached || cached.mtime !== mtime) {
12
- cached = { client: createDirectClient(loadVaultsConfig().config), mtime };
13
- }
14
- return cached.client;
15
- };
16
- };