@agentage/cli 0.26.0 → 0.27.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 (46) hide show
  1. package/dist/commands/daemon/daemon-cmd.js +0 -17
  2. package/dist/commands/vault/vault-sync.d.ts +2 -4
  3. package/dist/commands/vault/vault-sync.js +32 -42
  4. package/dist/commands/vault/vault.js +3 -2
  5. package/dist/daemon/server.d.ts +1 -4
  6. package/dist/daemon/server.js +0 -1
  7. package/dist/daemon-entry.js +6 -16
  8. package/dist/lib/auth/api.d.ts +0 -1
  9. package/dist/lib/auth/api.js +0 -12
  10. package/dist/lib/auth/provision.d.ts +1 -1
  11. package/dist/lib/auth/provision.js +5 -27
  12. package/dist/lib/daemon/daemon-client.d.ts +1 -3
  13. package/dist/lib/daemon/daemon-client.js +1 -2
  14. package/dist/lib/status/status-info.js +11 -9
  15. package/dist/lib/status/vaults-format.js +7 -2
  16. package/dist/lib/status/vaults-status.d.ts +2 -2
  17. package/dist/lib/status/vaults-status.js +13 -12
  18. package/dist/lib/vault/vault-registry.js +2 -3
  19. package/dist/sync/discover/watcher.js +1 -1
  20. package/dist/sync/git/manager.d.ts +0 -2
  21. package/dist/sync/git/planner.js +3 -3
  22. package/package.json +4 -4
  23. package/dist/sync/couch/cycle.d.ts +0 -2
  24. package/dist/sync/couch/cycle.js +0 -58
  25. package/dist/sync/couch/discovery.d.ts +0 -22
  26. package/dist/sync/couch/discovery.js +0 -34
  27. package/dist/sync/couch/file-store.d.ts +0 -2
  28. package/dist/sync/couch/file-store.js +0 -47
  29. package/dist/sync/couch/local-commit.d.ts +0 -2
  30. package/dist/sync/couch/local-commit.js +0 -24
  31. package/dist/sync/couch/manager.d.ts +0 -4
  32. package/dist/sync/couch/manager.fixtures.d.ts +0 -20
  33. package/dist/sync/couch/manager.fixtures.js +0 -56
  34. package/dist/sync/couch/manager.js +0 -126
  35. package/dist/sync/couch/manager.types.d.ts +0 -81
  36. package/dist/sync/couch/manager.types.js +0 -1
  37. package/dist/sync/couch/mutation-target.d.ts +0 -5
  38. package/dist/sync/couch/mutation-target.js +0 -33
  39. package/dist/sync/couch/push-on-write.d.ts +0 -3
  40. package/dist/sync/couch/push-on-write.js +0 -33
  41. package/dist/sync/couch/state-store.d.ts +0 -3
  42. package/dist/sync/couch/state-store.js +0 -28
  43. package/dist/sync/couch/targets.d.ts +0 -9
  44. package/dist/sync/couch/targets.js +0 -23
  45. package/dist/sync/couch/wire.d.ts +0 -6
  46. package/dist/sync/couch/wire.js +0 -16
@@ -1,58 +0,0 @@
1
- import { ensureWire, pendingCount } from './wire.js';
2
- // One couch cycle: commit dirty local truth first, drain queued pushes/deletions, then push+pull.
3
- // Every failure is caught and recorded (lastError / paused); it never throws to the caller.
4
- export const runCouchCycle = async (rt, st) => {
5
- const vault = st.target.vault;
6
- const build = (extra) => ({
7
- vault,
8
- channel: 'couch',
9
- ok: true,
10
- committed: false,
11
- pulled: false,
12
- pendingCount: pendingCount(st),
13
- ...extra,
14
- });
15
- if (st.running)
16
- return build({});
17
- st.running = true;
18
- try {
19
- const bearer = await rt.getBearer();
20
- if (!bearer) {
21
- st.paused = 'signed out';
22
- st.lastError = undefined;
23
- return build({ paused: 'signed out' });
24
- }
25
- const decision = await rt.discovery.channelFor(vault, bearer);
26
- if (decision.kind === 'paused') {
27
- st.paused = decision.reason;
28
- st.lastError = undefined;
29
- return build({ paused: decision.reason });
30
- }
31
- st.paused = undefined;
32
- const couch = await ensureWire(rt, st, decision);
33
- const pre = await rt.commitDirty(st.target.path, `sync: ${rt.nowIso()}`);
34
- await couch.flushPending(); // drain queued pushes AND queued deletions first
35
- const res = await couch.syncNow(); // pushAll + reconcile deletions, then pullOnce
36
- const post = await rt.commitDirty(st.target.path, `sync: couch ${rt.nowIso()}`);
37
- if (res.error) {
38
- st.lastError = res.error;
39
- return build({
40
- ok: false,
41
- committed: pre.committed,
42
- pulled: post.committed,
43
- error: res.error,
44
- });
45
- }
46
- st.lastSync = rt.nowIso();
47
- st.lastError = undefined;
48
- return build({ committed: pre.committed, pulled: post.committed });
49
- }
50
- catch (err) {
51
- const msg = err instanceof Error ? err.message : String(err);
52
- st.lastError = msg;
53
- return build({ ok: false, error: msg });
54
- }
55
- finally {
56
- st.running = false;
57
- }
58
- };
@@ -1,22 +0,0 @@
1
- import { type FetchJson } from '@agentage/memory-core';
2
- import { type ProvisionResult } from '../../lib/auth/provision.js';
3
- export type ChannelDecision = {
4
- kind: 'couch';
5
- endpoint: string;
6
- db: string;
7
- tokenUrl: string;
8
- } | {
9
- kind: 'paused';
10
- reason: string;
11
- };
12
- export interface DiscoveryDeps {
13
- bootstrapHost: string;
14
- fetchJson: FetchJson;
15
- provision: (vault: string) => Promise<ProvisionResult>;
16
- now?: () => number;
17
- }
18
- export interface Discovery {
19
- channelFor(vault: string, token: string): Promise<ChannelDecision>;
20
- reset(): void;
21
- }
22
- export declare const createDiscovery: (deps: DiscoveryDeps) => Discovery;
@@ -1,34 +0,0 @@
1
- import { channelForVault, HostResolver } from '@agentage/memory-core';
2
- const pausedReason = (prov) => {
3
- switch (prov.status) {
4
- case 'disabled':
5
- return 'account sync is not enabled on this server';
6
- case 'conflict':
7
- return 'name conflicts with a memory on another channel';
8
- case 'unauthenticated':
9
- return 'signed out';
10
- default:
11
- return 'provisioning - will retry';
12
- }
13
- };
14
- export const createDiscovery = (deps) => {
15
- const resolver = new HostResolver(deps.bootstrapHost, deps.fetchJson, deps.now ?? Date.now);
16
- const toCouch = (ch) => ch.channel === 'couch'
17
- ? { kind: 'couch', endpoint: ch.endpoint, db: ch.db, tokenUrl: ch.tokenUrl }
18
- : null;
19
- return {
20
- async channelFor(vault, token) {
21
- const first = toCouch(channelForVault(await resolver.resolve(token), vault));
22
- if (first)
23
- return first;
24
- // Missing from couch_vaults: provision once, refresh discovery once, re-check.
25
- const prov = await deps.provision(vault);
26
- resolver.invalidate();
27
- const second = toCouch(channelForVault(await resolver.resolve(token), vault));
28
- return second ?? { kind: 'paused', reason: pausedReason(prov) };
29
- },
30
- reset() {
31
- resolver.invalidate();
32
- },
33
- };
34
- };
@@ -1,2 +0,0 @@
1
- import { type FileStore } from '@agentage/memory-core';
2
- export declare const createFileStore: (root: string) => FileStore;
@@ -1,47 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
3
- import { dirname, join, relative, sep } from 'node:path';
4
- // The couch channel speaks vault-relative POSIX paths; on Windows the fs layer still uses `\`.
5
- const toPosix = (p) => (sep === '/' ? p : p.split(sep).join('/'));
6
- const fromPosix = (p) => (sep === '/' ? p : p.split('/').join(sep));
7
- // Recursively collect *.md under root as vault-relative POSIX paths. Dot-directories are skipped -
8
- // `.git` (the engine's own repo) must never enter the content-addressed model, and editor state
9
- // like `.obsidian/` holds no synced notes; this mirrors memory-core's own local listing.
10
- const walk = async (root, dir, acc) => {
11
- const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
12
- for (const e of entries) {
13
- if (e.name.startsWith('.'))
14
- continue;
15
- const abs = join(dir, e.name);
16
- if (e.isDirectory())
17
- await walk(root, abs, acc);
18
- else if (e.isFile() && e.name.endsWith('.md'))
19
- acc.push(toPosix(relative(root, abs)));
20
- }
21
- };
22
- // A FileStore rooted at one account vault's mirror dir: the seam CouchSync reads/writes through.
23
- export const createFileStore = (root) => ({
24
- async listMarkdown() {
25
- if (!existsSync(root))
26
- return [];
27
- const acc = [];
28
- await walk(root, root, acc);
29
- return acc.sort();
30
- },
31
- async read(path) {
32
- try {
33
- return await readFile(join(root, fromPosix(path)), 'utf8');
34
- }
35
- catch {
36
- return null; // gone from the file set
37
- }
38
- },
39
- async write(path, body) {
40
- const abs = join(root, fromPosix(path));
41
- await mkdir(dirname(abs), { recursive: true });
42
- await writeFile(abs, body, 'utf8');
43
- },
44
- async remove(path) {
45
- await rm(join(root, fromPosix(path)), { force: true });
46
- },
47
- });
@@ -1,2 +0,0 @@
1
- import { type CommitOutcome } from './manager.types.js';
2
- export declare const gitCommitDirty: (path: string, message: string) => Promise<CommitOutcome>;
@@ -1,24 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { createSyncGit, GitError } from '../git/git-exec.js';
4
- // The default local-git commit: stage everything and make one commit when the tree is dirty. An
5
- // index.lock collision (the engine mid-mutation) is a clean skip - the change stays for next cycle.
6
- export const gitCommitDirty = async (path, message) => {
7
- if (!existsSync(path))
8
- return { committed: false, skipped: false };
9
- const git = createSyncGit(path);
10
- try {
11
- if (!existsSync(join(path, '.git')))
12
- await git.run(['init', '-b', 'main']);
13
- await git.run(['add', '-A']);
14
- if ((await git.exec(['diff', '--cached', '--quiet'])).code === 0)
15
- return { committed: false, skipped: false };
16
- await git.run(['commit', '-m', message]);
17
- return { committed: true, skipped: false };
18
- }
19
- catch (err) {
20
- if (err instanceof GitError && err.kind === 'lock')
21
- return { committed: false, skipped: true };
22
- throw err;
23
- }
24
- };
@@ -1,4 +0,0 @@
1
- import { type CouchSyncManager, type CouchSyncManagerDeps } from './manager.types.js';
2
- export { type CouchSyncManager, type CouchSyncManagerDeps, type CouchSyncResult, type CouchTargetStatus, } from './manager.types.js';
3
- export { resolveMutationTarget } from './mutation-target.js';
4
- export declare const createCouchSyncManager: (deps?: CouchSyncManagerDeps) => CouchSyncManager;
@@ -1,20 +0,0 @@
1
- import { type FileStore, type VaultsConfig } from '@agentage/memory-core';
2
- import { type ChannelDecision, type Discovery } from './discovery.js';
3
- import { type CouchSyncManagerDeps } from './manager.js';
4
- export declare const config: VaultsConfig;
5
- export declare const autoConfig: VaultsConfig;
6
- export declare const noopStore: () => FileStore;
7
- export declare const couchDecision: ChannelDecision;
8
- export declare const makeManager: (over?: Partial<CouchSyncManagerDeps>) => {
9
- mgr: import("./manager.types.js").CouchSyncManager;
10
- couch: {
11
- pushFileLive: import("vitest").Mock<() => Promise<void>>;
12
- removeFile: import("vitest").Mock<() => Promise<void>>;
13
- flushPending: import("vitest").Mock<() => Promise<void>>;
14
- syncNow: import("vitest").Mock<() => Promise<{
15
- pushed: boolean;
16
- pulled: boolean;
17
- }>>;
18
- };
19
- discovery: Discovery;
20
- };
@@ -1,56 +0,0 @@
1
- import { vi } from 'vitest';
2
- import { createCouchSyncManager } from './manager.js';
3
- export const config = {
4
- version: 1,
5
- default: 'acct',
6
- vaults: {
7
- acct: { path: '/tmp/acct', origin: [{ remote: 'agentage', interval: 0 }] },
8
- git: { path: '/tmp/git', origin: [{ remote: 'git@h:g.git' }] },
9
- local: { path: '/tmp/local' },
10
- },
11
- };
12
- export const autoConfig = {
13
- version: 1,
14
- default: 'acct',
15
- vaults: {
16
- acct: { path: '/tmp/acct', origin: [{ remote: 'agentage', interval: 300 }] },
17
- two: { path: '/tmp/two', origin: [{ remote: 'agentage', interval: 300 }] },
18
- },
19
- };
20
- export const noopStore = () => ({
21
- listMarkdown: async () => [],
22
- read: async () => null,
23
- write: async () => { },
24
- remove: async () => { },
25
- });
26
- export const couchDecision = {
27
- kind: 'couch',
28
- endpoint: 'https://couch.test',
29
- db: 'mem_acct',
30
- tokenUrl: 'https://auth.test/couch-token',
31
- };
32
- export const makeManager = (over = {}) => {
33
- const couch = {
34
- pushFileLive: vi.fn(async () => { }),
35
- removeFile: vi.fn(async () => { }),
36
- flushPending: vi.fn(async () => { }),
37
- syncNow: vi.fn(async () => ({ pushed: true, pulled: true })),
38
- };
39
- const discovery = {
40
- channelFor: vi.fn(async () => couchDecision),
41
- reset: vi.fn(),
42
- };
43
- const mgr = createCouchSyncManager({
44
- getConfig: () => config,
45
- configDir: () => '/tmp/cfg',
46
- getBearer: async () => 'tok',
47
- discovery,
48
- makeCouchSync: () => couch,
49
- makeFileStore: noopStore,
50
- makeStatePersistence: () => ({ load: async () => null, save: async () => { } }),
51
- commitDirty: async () => ({ committed: false, skipped: false }),
52
- now: () => '2026-01-01T00:00:00Z',
53
- ...over,
54
- });
55
- return { mgr, couch, discovery };
56
- };
@@ -1,126 +0,0 @@
1
- import { CouchSync } from '@agentage/memory-core';
2
- import { currentBearer } from '../../lib/auth/api.js';
3
- import { getConfigDir, readAuth } from '../../lib/fs/config.js';
4
- import { links, siteFqdn } from '../../lib/net/origins.js';
5
- import { requestHeaders } from '../../lib/net/user-agent.js';
6
- import { defaultProvisionDeps, provisionAccountVault } from '../../lib/auth/provision.js';
7
- import { loadVaultsConfig } from '../../lib/vault/vaults.js';
8
- import { intervalMs } from '../git/planner.js';
9
- import { runCouchCycle } from './cycle.js';
10
- import { createDiscovery } from './discovery.js';
11
- import { createFileStore } from './file-store.js';
12
- import { gitCommitDirty } from './local-commit.js';
13
- import { resolveMutationTarget } from './mutation-target.js';
14
- import { pushOnWrite } from './push-on-write.js';
15
- import { createStatePersistence } from './state-store.js';
16
- import { autoCouchTargets, couchTargets } from './targets.js';
17
- import { getState, pendingCount } from './wire.js';
18
- export { resolveMutationTarget } from './mutation-target.js';
19
- // Couch sync runs inside the daemon; identify the caller as the daemon on every outbound request.
20
- const daemonHeaders = () => requestHeaders({ component: 'daemon' });
21
- const defaultFetch = (url, init) => {
22
- const base = init;
23
- const merged = { ...base, headers: { ...daemonHeaders(), ...base?.headers } };
24
- return globalThis.fetch(url, merged);
25
- };
26
- const defaultFetchJson = async (url, token) => {
27
- const res = await globalThis.fetch(url, {
28
- headers: { authorization: `Bearer ${token}`, ...daemonHeaders() },
29
- });
30
- const json = await res.json().catch(() => null);
31
- return { status: res.status, json };
32
- };
33
- // The daemon-side couch scheduler: per-account-vault timers + a persistent CouchSync per target for
34
- // sync-on-save. Every couch failure is caught and recorded (lastError / paused); it never crashes
35
- // the daemon and never blocks a memory API response.
36
- export const createCouchSyncManager = (deps = {}) => {
37
- const getConfig = deps.getConfig ?? (() => loadVaultsConfig().config);
38
- const getBearer = deps.getBearer ?? (() => currentBearer(readAuth, links(siteFqdn())));
39
- const makeFileStore = deps.makeFileStore ?? createFileStore;
40
- const makeCouchSync = deps.makeCouchSync ??
41
- ((files, cfg, f, authorize, onUnauthorized, state, log) => new CouchSync(files, cfg, f, authorize, onUnauthorized, state, log));
42
- const rt = {
43
- configDir: deps.configDir ?? getConfigDir,
44
- getBearer,
45
- fetch: deps.fetch ?? defaultFetch,
46
- makeCouchSync,
47
- makeStatePersistence: deps.makeStatePersistence ?? createStatePersistence,
48
- commitDirty: deps.commitDirty ?? gitCommitDirty,
49
- discovery: deps.discovery ??
50
- createDiscovery({
51
- bootstrapHost: links(siteFqdn()).sync,
52
- fetchJson: defaultFetchJson,
53
- provision: (vault) => provisionAccountVault(vault, defaultProvisionDeps()),
54
- }),
55
- nowIso: deps.now ?? (() => new Date().toISOString()),
56
- log: deps.log ?? (() => { }),
57
- };
58
- const states = new Map();
59
- const timers = new Map();
60
- const ensureTargetState = (target) => {
61
- const existing = states.get(target.vault);
62
- if (existing) {
63
- existing.target = target;
64
- return existing;
65
- }
66
- const fresh = { target, files: makeFileStore(target.path), running: false };
67
- states.set(target.vault, fresh);
68
- return fresh;
69
- };
70
- return {
71
- reschedule() {
72
- for (const timer of timers.values())
73
- clearInterval(timer);
74
- timers.clear();
75
- const targets = couchTargets(getConfig());
76
- const live = new Set(targets.map((t) => t.vault));
77
- for (const vault of [...states.keys()])
78
- if (!live.has(vault))
79
- states.delete(vault);
80
- for (const t of targets)
81
- void getState(rt, ensureTargetState(t)).catch(() => { });
82
- for (const t of autoCouchTargets(getConfig())) {
83
- const timer = setInterval(() => void runCouchCycle(rt, ensureTargetState(t)), intervalMs(t.intervalSeconds));
84
- timer.unref?.();
85
- timers.set(t.vault, timer);
86
- }
87
- },
88
- async runNow(vault) {
89
- const t = couchTargets(getConfig()).find((x) => x.vault === vault);
90
- if (!t)
91
- throw new Error(`'${vault}' is not an account vault`);
92
- return runCouchCycle(rt, ensureTargetState(t));
93
- },
94
- onWrite(verb, body) {
95
- if (verb !== 'write' && verb !== 'edit' && verb !== 'delete')
96
- return;
97
- const target = resolveMutationTarget(getConfig(), body);
98
- if (!target)
99
- return;
100
- const t = couchTargets(getConfig()).find((x) => x.vault === target.vault);
101
- if (!t)
102
- return;
103
- void pushOnWrite(rt, ensureTargetState(t), verb, target.path);
104
- },
105
- status() {
106
- return couchTargets(getConfig()).map((t) => {
107
- const st = states.get(t.vault);
108
- return {
109
- vault: t.vault,
110
- channel: 'couch',
111
- intervalSeconds: t.intervalSeconds,
112
- lastSync: st?.lastSync,
113
- lastError: st?.lastError,
114
- pendingCount: pendingCount(st),
115
- paused: st?.paused,
116
- running: st?.running ?? false,
117
- };
118
- });
119
- },
120
- stop() {
121
- for (const timer of timers.values())
122
- clearInterval(timer);
123
- timers.clear();
124
- },
125
- };
126
- };
@@ -1,81 +0,0 @@
1
- import { type CouchState, type CouchStatePersistence, type FetchLike, type FileStore, type SyncResult as CouchChannelResult, type VaultsConfig } from '@agentage/memory-core';
2
- import { type MemoryVerb } from '../../daemon/actions.js';
3
- import { type Discovery } from './discovery.js';
4
- import { type CouchTarget } from './targets.js';
5
- export interface CouchSyncResult {
6
- vault: string;
7
- channel: 'couch';
8
- ok: boolean;
9
- committed: boolean;
10
- pulled: boolean;
11
- pendingCount: number;
12
- paused?: string;
13
- error?: string;
14
- }
15
- export interface CouchTargetStatus {
16
- vault: string;
17
- channel: 'couch';
18
- intervalSeconds: number;
19
- lastSync?: string;
20
- lastError?: string;
21
- pendingCount: number;
22
- paused?: string;
23
- running: boolean;
24
- }
25
- export interface CouchLike {
26
- pushFileLive(path: string): Promise<void>;
27
- removeFile(path: string): Promise<void>;
28
- flushPending(): Promise<void>;
29
- syncNow(): Promise<CouchChannelResult>;
30
- }
31
- export type MakeCouchSync = (files: FileStore, cfg: {
32
- endpoint: string;
33
- db: string;
34
- }, fetch: FetchLike, authorize: () => Promise<string>, onUnauthorized: () => void, state: CouchState, log?: (msg: string) => void) => CouchLike;
35
- export interface CommitOutcome {
36
- committed: boolean;
37
- skipped: boolean;
38
- }
39
- export interface CouchSyncManagerDeps {
40
- getConfig?: () => VaultsConfig;
41
- configDir?: () => string;
42
- getBearer?: () => Promise<string | null>;
43
- discovery?: Discovery;
44
- fetch?: FetchLike;
45
- makeFileStore?: (path: string) => FileStore;
46
- makeStatePersistence?: (configDir: string, vault: string) => CouchStatePersistence;
47
- makeCouchSync?: MakeCouchSync;
48
- commitDirty?: (path: string, message: string) => Promise<CommitOutcome>;
49
- now?: () => string;
50
- log?: (msg: string) => void;
51
- }
52
- export interface CouchSyncManager {
53
- reschedule(): void;
54
- runNow(vault: string): Promise<CouchSyncResult>;
55
- onWrite(verb: MemoryVerb, body: unknown): void;
56
- status(): CouchTargetStatus[];
57
- stop(): void;
58
- }
59
- export interface TargetState {
60
- target: CouchTarget;
61
- files: FileStore;
62
- state?: CouchState;
63
- statePromise?: Promise<CouchState>;
64
- couch?: CouchLike;
65
- wireKey?: string;
66
- running: boolean;
67
- lastSync?: string;
68
- lastError?: string;
69
- paused?: string;
70
- }
71
- export interface CouchRuntime {
72
- configDir: () => string;
73
- getBearer: () => Promise<string | null>;
74
- fetch: FetchLike;
75
- makeCouchSync: MakeCouchSync;
76
- makeStatePersistence: (configDir: string, vault: string) => CouchStatePersistence;
77
- commitDirty: (path: string, message: string) => Promise<CommitOutcome>;
78
- discovery: Discovery;
79
- nowIso: () => string;
80
- log: (msg: string) => void;
81
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,5 +0,0 @@
1
- import { type VaultsConfig } from '@agentage/memory-core';
2
- export declare const resolveMutationTarget: (config: VaultsConfig, body: unknown) => {
3
- vault: string;
4
- path: string;
5
- } | null;
@@ -1,33 +0,0 @@
1
- import { isAccountVault } from '@agentage/memory-core';
2
- // Map one memory-verb wire payload to the account vault + vault-relative POSIX path it mutated, or
3
- // null when the target is not an account vault (git/local mutations never touch the couch channel).
4
- export const resolveMutationTarget = (config, body) => {
5
- const p = (body ?? {});
6
- const ref = typeof p.ref === 'string' ? p.ref : '';
7
- if (!ref)
8
- return null;
9
- let vault;
10
- let path;
11
- if (ref.startsWith('@')) {
12
- const m = ref.match(/^@([^/]+)\/(.+)$/);
13
- if (!m)
14
- return null; // a bare '@vault' is not a file mutation
15
- vault = m[1];
16
- path = m[2];
17
- }
18
- else {
19
- vault = (typeof p.opts?.vault === 'string' ? p.opts.vault : undefined) ?? config.default;
20
- if (!vault) {
21
- const names = Object.keys(config.vaults ?? {});
22
- if (names.length === 1)
23
- vault = names[0];
24
- }
25
- path = ref;
26
- }
27
- if (!vault)
28
- return null;
29
- const entry = config.vaults?.[vault];
30
- if (!entry || !isAccountVault(entry))
31
- return null;
32
- return { vault, path: path.replace(/^\.?\//, '') };
33
- };
@@ -1,3 +0,0 @@
1
- import { type MemoryVerb } from '../../daemon/actions.js';
2
- import { type CouchRuntime, type TargetState } from './manager.types.js';
3
- export declare const pushOnWrite: (rt: CouchRuntime, st: TargetState, verb: MemoryVerb, path: string) => Promise<void>;
@@ -1,33 +0,0 @@
1
- import { ensureWire, getState } from './wire.js';
2
- // Sync-on-save: push (or tombstone) one path right after the engine committed it. Failures queue
3
- // the path in the module's persisted pending/deletion sets (retried by the next cycle) and never
4
- // surface to the API. A delete is durable regardless of auth/network state at delete time: with
5
- // no wire it enqueues the deletion, and removeFile itself self-enqueues on transport failure.
6
- export const pushOnWrite = async (rt, st, verb, path) => {
7
- const defer = async () => {
8
- const state = await getState(rt, st);
9
- if (verb === 'delete')
10
- await state.enqueueDeletion(path);
11
- else
12
- await state.enqueue(path);
13
- };
14
- try {
15
- const bearer = await rt.getBearer();
16
- if (bearer) {
17
- const decision = await rt.discovery.channelFor(st.target.vault, bearer);
18
- if (decision.kind === 'couch') {
19
- const couch = await ensureWire(rt, st, decision);
20
- if (verb === 'delete')
21
- await couch.removeFile(path);
22
- else
23
- await couch.pushFileLive(path);
24
- return;
25
- }
26
- }
27
- await defer(); // no wire yet (signed out / paused) - queued until one exists
28
- }
29
- catch (err) {
30
- rt.log(`couch push-on-write ${path}: ${err instanceof Error ? err.message : String(err)}`);
31
- await defer().catch(() => { });
32
- }
33
- };
@@ -1,3 +0,0 @@
1
- import { type CouchStatePersistence } from '@agentage/memory-core';
2
- export declare const couchStateDir: (configDir: string) => string;
3
- export declare const createStatePersistence: (configDir: string, vault: string) => CouchStatePersistence;
@@ -1,28 +0,0 @@
1
- import { mkdirSync } from 'node:fs';
2
- import { readFile, rename, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- // The couch sync cursor + rev-cache + pending queue for one vault, at
5
- // <configDir>/couch-state/<vault>.json. Load returns null on a missing OR unparseable file so a
6
- // corrupt state degrades to a fresh from-scratch sync rather than crashing the daemon; save is
7
- // atomic (temp + rename) so a crash mid-write never truncates it.
8
- export const couchStateDir = (configDir) => join(configDir, 'couch-state');
9
- export const createStatePersistence = (configDir, vault) => {
10
- const dir = couchStateDir(configDir);
11
- const path = join(dir, `${encodeURIComponent(vault)}.json`);
12
- return {
13
- async load() {
14
- try {
15
- return JSON.parse(await readFile(path, 'utf8'));
16
- }
17
- catch {
18
- return null;
19
- }
20
- },
21
- async save(state) {
22
- mkdirSync(dir, { recursive: true });
23
- const tmp = `${path}.tmp`;
24
- await writeFile(tmp, JSON.stringify(state), 'utf8');
25
- await rename(tmp, path);
26
- },
27
- };
28
- };
@@ -1,9 +0,0 @@
1
- import { type VaultsConfig } from '@agentage/memory-core';
2
- export declare const ACCOUNT_REMOTE = "agentage";
3
- export interface CouchTarget {
4
- vault: string;
5
- path: string;
6
- intervalSeconds: number;
7
- }
8
- export declare const couchTargets: (config: VaultsConfig) => CouchTarget[];
9
- export declare const autoCouchTargets: (config: VaultsConfig) => CouchTarget[];
@@ -1,23 +0,0 @@
1
- import { expandPath, isAccountVault } from '@agentage/memory-core';
2
- import { DEFAULT_INTERVAL_SECONDS } from '../git/planner.js';
3
- // The account (agentage) channel a couch target syncs to. Unlike a git target it has no external
4
- // remote URL: the daemon resolves the per-memory CouchDB + JWT endpoints from discovery at runtime.
5
- export const ACCOUNT_REMOTE = 'agentage';
6
- // Every account vault (agentage origin) with a local mirror path is a couch target. Interval rides
7
- // on the agentage origin and matches git semantics: absent = 300s, 0 = manual-only.
8
- export const couchTargets = (config) => {
9
- const out = [];
10
- for (const [vault, entry] of Object.entries(config.vaults ?? {})) {
11
- if (!isAccountVault(entry) || !entry.path)
12
- continue;
13
- const origin = entry.origin?.find((o) => o.remote === ACCOUNT_REMOTE);
14
- out.push({
15
- vault,
16
- path: expandPath(entry.path),
17
- intervalSeconds: origin?.interval ?? DEFAULT_INTERVAL_SECONDS,
18
- });
19
- }
20
- return out;
21
- };
22
- // The couch targets the daemon auto-loop schedules: interval 0 is manual-only and excluded.
23
- export const autoCouchTargets = (config) => couchTargets(config).filter((t) => t.intervalSeconds > 0);
@@ -1,6 +0,0 @@
1
- import { type CouchState } from '@agentage/memory-core';
2
- import { type ChannelDecision } from './discovery.js';
3
- import { type CouchLike, type CouchRuntime, type TargetState } from './manager.types.js';
4
- export declare const pendingCount: (st: TargetState | undefined) => number;
5
- export declare const getState: (rt: CouchRuntime, st: TargetState) => Promise<CouchState>;
6
- export declare const ensureWire: (rt: CouchRuntime, st: TargetState, d: ChannelDecision) => Promise<CouchLike>;