integral-mind 0.1.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.
@@ -0,0 +1,215 @@
1
+ import { computeDelta, emptySyncState, loadSyncState, readConnectorConfig, saveSyncState, sha256Hex, writeConnectorConfig, SyncUsageError, } from "./state.js";
2
+ export const MEMPALACE_CONNECTOR = "mempalace";
3
+ const DRAWER_BATCH = 25;
4
+ const KG_TRIPLE_BATCH = 200;
5
+ const TUNNEL_BATCH = 100;
6
+ export async function readMempalaceConfig(env) {
7
+ return (await readConnectorConfig(MEMPALACE_CONNECTOR, env)) ?? { palaces: [] };
8
+ }
9
+ export async function writeMempalaceConfig(config, env) {
10
+ return writeConnectorConfig(MEMPALACE_CONNECTOR, config, env);
11
+ }
12
+ export async function syncMempalace(opts, deps, stdio) {
13
+ const config = await readMempalaceConfig(deps.env);
14
+ const palaces = opts.label
15
+ ? config.palaces.filter((p) => p.label === opts.label)
16
+ : config.palaces;
17
+ if (palaces.length === 0) {
18
+ throw new SyncUsageError(opts.label
19
+ ? `No palace labeled "${opts.label}". Run \`im sync status\` to list connections.`
20
+ : "No palaces connected. Run `im sync connect mempalace --label <name> --path <palace>` first.");
21
+ }
22
+ const summary = { synced: 0, failed: 0 };
23
+ for (const palace of palaces) {
24
+ const ok = await syncPalace(palace, opts.full ?? false, deps, stdio);
25
+ if (ok)
26
+ summary.synced += 1;
27
+ else
28
+ summary.failed += 1;
29
+ }
30
+ return summary;
31
+ }
32
+ async function syncPalace(palace, full, deps, stdio) {
33
+ stdio.out(`[${palace.label}] starting ${full ? "full " : ""}sync (${palace.palace_path})`);
34
+ const source = await deps.openPalace(palace.palace_path);
35
+ let runId;
36
+ try {
37
+ ({ run_id: runId } = await deps.client.request("POST", "/api/mempalace/sync/start", { palace_id: palace.palace_id, bridge_version: "im-cli" }));
38
+ }
39
+ catch (err) {
40
+ // No run was started server-side, so there is nothing to complete —
41
+ // but the palace MCP child must not leak.
42
+ await closeQuietly(source, palace, stdio);
43
+ stdio.err(`[${palace.label}] sync error: ${err instanceof Error ? err.message : String(err)}`);
44
+ stdio.out(`[${palace.label}] sync FAILED`);
45
+ return false;
46
+ }
47
+ let runError = null;
48
+ try {
49
+ const drawers = await source.enumerateDrawers();
50
+ const kg = await source.enumerateKg();
51
+ const tunnels = await source.enumerateTunnels();
52
+ stdio.out(`[${palace.label}] enumerated ${drawers.length} drawers, ${kg.entities.length} entities, ${kg.triples.length} triples, ${tunnels.length} tunnels`);
53
+ const state = full
54
+ ? emptySyncState()
55
+ : await loadSyncState(MEMPALACE_CONNECTOR, palace.label, deps.env);
56
+ await pushDrawers(palace, runId, state, drawers, deps, stdio);
57
+ await pushKg(palace, runId, kg.entities, kg.triples, state, deps);
58
+ await pushTunnels(palace, runId, tunnels, state, deps);
59
+ state.last_synced_at = new Date().toISOString();
60
+ await saveSyncState(MEMPALACE_CONNECTOR, palace.label, state, deps.env);
61
+ }
62
+ catch (err) {
63
+ runError = err instanceof Error ? err.message : String(err);
64
+ stdio.err(`[${palace.label}] sync error: ${runError}`);
65
+ }
66
+ finally {
67
+ await closeQuietly(source, palace, stdio);
68
+ await deps.client.request("POST", "/api/mempalace/sync/complete", {
69
+ run_id: runId,
70
+ palace_id: palace.palace_id,
71
+ status: runError ? "failed" : "complete",
72
+ error: runError,
73
+ });
74
+ }
75
+ stdio.out(`[${palace.label}] sync ${runError ? "FAILED" : "complete"}`);
76
+ return runError === null;
77
+ }
78
+ /** Best-effort close: a dying MCP child must never mask the sync outcome. */
79
+ async function closeQuietly(source, palace, stdio) {
80
+ try {
81
+ await source.close();
82
+ }
83
+ catch (err) {
84
+ stdio.err(`[${palace.label}] palace close failed (ignored): ${err instanceof Error ? err.message : String(err)}`);
85
+ }
86
+ }
87
+ async function pushDrawers(palace, runId, state, drawers, deps, stdio) {
88
+ const byId = new Map(drawers.map((d) => [d.id, d]));
89
+ const delta = computeDelta(state.items, drawers.map((d) => ({ id: d.id, hash: sha256Hex(d.content) })));
90
+ if (delta.upsertIds.length === 0 && delta.deleteIds.length === 0) {
91
+ stdio.out(`[${palace.label}] drawers unchanged`);
92
+ state.items = delta.nextHashes;
93
+ return;
94
+ }
95
+ for (let i = 0; i < delta.upsertIds.length; i += DRAWER_BATCH) {
96
+ const slice = delta.upsertIds.slice(i, i + DRAWER_BATCH);
97
+ const result = await deps.client.request("POST", "/api/mempalace/sync/drawers", {
98
+ run_id: runId,
99
+ palace_id: palace.palace_id,
100
+ upserts: slice.map((id) => {
101
+ const d = byId.get(id);
102
+ return {
103
+ mempalace_drawer_id: d.id,
104
+ wing: d.wing,
105
+ room: d.room,
106
+ source_file: d.source_file ?? null,
107
+ agent_name: d.agent_name ?? null,
108
+ content: d.content,
109
+ };
110
+ }),
111
+ deletes: [],
112
+ });
113
+ stdio.out(`[${palace.label}] drawer batch: +${result.added} ~${result.updated}`);
114
+ }
115
+ for (let i = 0; i < delta.deleteIds.length; i += DRAWER_BATCH) {
116
+ const slice = delta.deleteIds.slice(i, i + DRAWER_BATCH);
117
+ const result = await deps.client.request("POST", "/api/mempalace/sync/drawers", { run_id: runId, palace_id: palace.palace_id, upserts: [], deletes: slice });
118
+ stdio.out(`[${palace.label}] drawer delete batch: -${result.removed}`);
119
+ }
120
+ state.items = delta.nextHashes;
121
+ }
122
+ async function pushKg(palace, runId, entities, triples, state, deps) {
123
+ const previous = Array.isArray(state.extra.triple_ids)
124
+ ? state.extra.triple_ids
125
+ : [];
126
+ const seen = new Set(triples.map((t) => t.id));
127
+ const invalidate = previous.filter((id) => !seen.has(id));
128
+ // Delta-cheap repeat runs: the server upserts, so an unchanged KG with
129
+ // nothing to invalidate needs no traffic at all.
130
+ const kgHash = sha256Hex(JSON.stringify({ entities, triples }));
131
+ if (kgHash === state.extra.kg_hash && invalidate.length === 0)
132
+ return;
133
+ for (let i = 0; i < triples.length; i += KG_TRIPLE_BATCH) {
134
+ const slice = triples.slice(i, i + KG_TRIPLE_BATCH);
135
+ const sliceEntityIds = new Set();
136
+ for (const t of slice) {
137
+ sliceEntityIds.add(t.subject_id);
138
+ sliceEntityIds.add(t.object_id);
139
+ }
140
+ await deps.client.request("POST", "/api/mempalace/sync/kg", {
141
+ run_id: runId,
142
+ palace_id: palace.palace_id,
143
+ entities: entities
144
+ .filter((e) => sliceEntityIds.has(e.id))
145
+ .map((e) => ({
146
+ mempalace_entity_id: e.id,
147
+ name: e.name,
148
+ entity_type: e.type,
149
+ properties: e.properties ?? {},
150
+ })),
151
+ triples: slice.map((t) => ({
152
+ mempalace_triple_id: t.id,
153
+ subject_mempalace_entity_id: t.subject_id,
154
+ predicate: t.predicate,
155
+ object_mempalace_entity_id: t.object_id,
156
+ valid_from: t.valid_from ?? null,
157
+ valid_to: t.valid_to ?? null,
158
+ confidence: t.confidence ?? 1,
159
+ source_drawer_mempalace_id: t.source_drawer_id ?? null,
160
+ source_file: t.source_file ?? null,
161
+ })),
162
+ invalidate_triple_ids: i === 0 ? invalidate : [],
163
+ });
164
+ }
165
+ if (triples.length === 0 && invalidate.length > 0) {
166
+ await deps.client.request("POST", "/api/mempalace/sync/kg", {
167
+ run_id: runId,
168
+ palace_id: palace.palace_id,
169
+ entities: [],
170
+ triples: [],
171
+ invalidate_triple_ids: invalidate,
172
+ });
173
+ }
174
+ state.extra.triple_ids = Array.from(seen);
175
+ state.extra.kg_hash = kgHash;
176
+ }
177
+ async function pushTunnels(palace, runId, tunnels, state, deps) {
178
+ const previous = Array.isArray(state.extra.tunnel_ids)
179
+ ? state.extra.tunnel_ids
180
+ : [];
181
+ const seen = new Set(tunnels.map((t) => t.id));
182
+ const deletes = previous.filter((id) => !seen.has(id));
183
+ const tunnelsHash = sha256Hex(JSON.stringify(tunnels));
184
+ if (tunnelsHash === state.extra.tunnels_hash && deletes.length === 0)
185
+ return;
186
+ for (let i = 0; i < tunnels.length; i += TUNNEL_BATCH) {
187
+ const slice = tunnels.slice(i, i + TUNNEL_BATCH);
188
+ await deps.client.request("POST", "/api/mempalace/sync/tunnels", {
189
+ run_id: runId,
190
+ palace_id: palace.palace_id,
191
+ upserts: slice.map((t) => ({
192
+ mempalace_tunnel_id: t.id,
193
+ source_wing: t.source_wing,
194
+ source_room: t.source_room,
195
+ target_wing: t.target_wing,
196
+ target_room: t.target_room,
197
+ label: t.label ?? null,
198
+ source_drawer_mempalace_id: t.source_drawer_id ?? null,
199
+ target_drawer_mempalace_id: t.target_drawer_id ?? null,
200
+ })),
201
+ deletes: i === 0 ? deletes : [],
202
+ });
203
+ }
204
+ if (tunnels.length === 0 && deletes.length > 0) {
205
+ await deps.client.request("POST", "/api/mempalace/sync/tunnels", {
206
+ run_id: runId,
207
+ palace_id: palace.palace_id,
208
+ upserts: [],
209
+ deletes,
210
+ });
211
+ }
212
+ state.extra.tunnel_ids = Array.from(seen);
213
+ state.extra.tunnels_hash = tunnelsHash;
214
+ }
215
+ //# sourceMappingURL=mempalace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mempalace.js","sourceRoot":"","sources":["../../src/sync/mempalace.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,cAAc,GAEf,MAAM,YAAY,CAAC;AA2EpB,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAC/C,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAsB;IAC9D,OAAO,CAAC,MAAM,mBAAmB,CAAkB,mBAAmB,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAuB,EACvB,GAAsB;IAEtB,OAAO,oBAAoB,CAAC,mBAAmB,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;AAChE,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAwC,EACxC,IAAmB,EACnB,KAAY;IAEZ,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK;QACxB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC;QACtD,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IACnB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,cAAc,CACtB,IAAI,CAAC,KAAK;YACR,CAAC,CAAC,sBAAsB,IAAI,CAAC,KAAK,gDAAgD;YAClF,CAAC,CAAC,6FAA6F,CAClG,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAgB,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACrE,IAAI,EAAE;YAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;;YACvB,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,MAAoB,EACpB,IAAa,EACb,IAAmB,EACnB,KAAY;IAEZ,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,cAAc,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAEzD,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAC5C,MAAM,EACN,2BAA2B,EAC3B,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,CAC1D,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,oEAAoE;QACpE,0CAA0C;QAC1C,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1C,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,iBAAiB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/F,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,eAAe,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAChD,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAChD,KAAK,CAAC,GAAG,CACP,IAAI,MAAM,CAAC,KAAK,gBAAgB,OAAO,CAAC,MAAM,aAAa,EAAE,CAAC,QAAQ,CAAC,MAAM,cAAc,EAAE,CAAC,OAAO,CAAC,MAAM,aAAa,OAAO,CAAC,MAAM,UAAU,CAClJ,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI;YAChB,CAAC,CAAC,cAAc,EAAE;YAClB,CAAC,CAAC,MAAM,aAAa,CAAC,mBAAmB,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAErE,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAClE,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAEvD,KAAK,CAAC,cAAc,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,aAAa,CAAC,mBAAmB,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5D,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,iBAAiB,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACT,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,8BAA8B,EAAE;YAChE,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;YACxC,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACxE,OAAO,QAAQ,KAAK,IAAI,CAAC;AAC3B,CAAC;AAED,6EAA6E;AAC7E,KAAK,UAAU,YAAY,CACzB,MAAoB,EACpB,MAAoB,EACpB,KAAY;IAEZ,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,CACP,IAAI,MAAM,CAAC,KAAK,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACvG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,MAAoB,EACpB,KAAa,EACb,KAAgB,EAChB,OAAmB,EACnB,IAAmB,EACnB,KAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,YAAY,CACxB,KAAK,CAAC,KAAK,EACX,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC/D,CAAC;IAEF,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjE,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,qBAAqB,CAAC,CAAC;QACjD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CACtC,MAAM,EACN,6BAA6B,EAC7B;YACE,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;gBACxB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;gBACxB,OAAO;oBACL,mBAAmB,EAAE,CAAC,CAAC,EAAE;oBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,IAAI;oBAClC,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI;oBAChC,OAAO,EAAE,CAAC,CAAC,OAAO;iBACnB,CAAC;YACJ,CAAC,CAAC;YACF,OAAO,EAAE,EAAE;SACZ,CACF,CAAC;QACF,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,oBAAoB,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CACtC,MAAM,EACN,6BAA6B,EAC7B,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAC5E,CAAC;QACF,KAAK,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,2BAA2B,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;AACjC,CAAC;AAED,KAAK,UAAU,MAAM,CACnB,MAAoB,EACpB,KAAa,EACb,QAAoB,EACpB,OAAmB,EACnB,KAAgB,EAChB,IAAmB;IAEnB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;QACpD,CAAC,CAAE,KAAK,CAAC,KAAK,CAAC,UAAuB;QACtC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1D,uEAAuE;IACvE,iDAAiD;IACjD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAChE,IAAI,MAAM,KAAK,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,eAAe,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC;QACpD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;YACjC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE;YAC1D,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,QAAQ;iBACf,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;iBACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACX,mBAAmB,EAAE,CAAC,CAAC,EAAE;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,WAAW,EAAE,CAAC,CAAC,IAAI;gBACnB,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,EAAE;aAC/B,CAAC,CAAC;YACL,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzB,mBAAmB,EAAE,CAAC,CAAC,EAAE;gBACzB,2BAA2B,EAAE,CAAC,CAAC,UAAU;gBACzC,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,0BAA0B,EAAE,CAAC,CAAC,SAAS;gBACvC,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI;gBAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;gBAC5B,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,CAAC;gBAC7B,0BAA0B,EAAE,CAAC,CAAC,gBAAgB,IAAI,IAAI;gBACtD,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,IAAI;aACnC,CAAC,CAAC;YACH,qBAAqB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;SACjD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE;YAC1D,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,EAAE;YACZ,OAAO,EAAE,EAAE;YACX,qBAAqB,EAAE,UAAU;SAClC,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,MAAoB,EACpB,KAAa,EACb,OAAmB,EACnB,KAAgB,EAChB,IAAmB;IAEnB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;QACpD,CAAC,CAAE,KAAK,CAAC,KAAK,CAAC,UAAuB;QACtC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEvD,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,IAAI,WAAW,KAAK,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE7E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;QACjD,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,6BAA6B,EAAE;YAC/D,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzB,mBAAmB,EAAE,CAAC,CAAC,EAAE;gBACzB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI;gBACtB,0BAA0B,EAAE,CAAC,CAAC,gBAAgB,IAAI,IAAI;gBACtD,0BAA0B,EAAE,CAAC,CAAC,gBAAgB,IAAI,IAAI;aACvD,CAAC,CAAC;YACH,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;SAChC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,6BAA6B,EAAE;YAC/D,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,OAAO,EAAE,EAAE;YACX,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,WAAW,CAAC;AACzC,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /**
4
+ * Default vault reader: every .md file under the vault, as vault-relative
5
+ * posix paths. Dot-directories (.obsidian, .trash, .git) and dotfiles are
6
+ * skipped — they hold app state, not notes.
7
+ */
8
+ export async function listVaultNotes(vaultPath) {
9
+ const notes = [];
10
+ await walk(vaultPath, [], notes);
11
+ return notes.sort((a, b) => a.path.localeCompare(b.path));
12
+ }
13
+ async function walk(dir, segments, out) {
14
+ const entries = await readdir(dir, { withFileTypes: true });
15
+ for (const entry of entries) {
16
+ if (entry.name.startsWith("."))
17
+ continue;
18
+ const absolute = join(dir, entry.name);
19
+ if (entry.isDirectory()) {
20
+ await walk(absolute, [...segments, entry.name], out);
21
+ }
22
+ else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
23
+ out.push({
24
+ path: [...segments, entry.name].join("/"),
25
+ content: await readFile(absolute, "utf8"),
26
+ });
27
+ }
28
+ }
29
+ }
30
+ //# sourceMappingURL=obsidian-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian-source.js","sourceRoot":"","sources":["../../src/sync/obsidian-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB;IACpD,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,GAAW,EAAE,QAAkB,EAAE,GAAgB;IACnE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QACvD,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACzC,OAAO,EAAE,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { readConnectorConfig, writeConnectorConfig, SyncUsageError } from "./state.js";
2
+ import { syncTreeRoot } from "./tree-sync.js";
3
+ export const OBSIDIAN_CONNECTOR = "obsidian";
4
+ export async function readObsidianConfig(env) {
5
+ return (await readConnectorConfig(OBSIDIAN_CONNECTOR, env)) ?? { vaults: [] };
6
+ }
7
+ export async function writeObsidianConfig(config, env) {
8
+ return writeConnectorConfig(OBSIDIAN_CONNECTOR, config, env);
9
+ }
10
+ export async function syncObsidian(opts, deps, stdio) {
11
+ const config = await readObsidianConfig(deps.env);
12
+ const vaults = opts.label ? config.vaults.filter((v) => v.label === opts.label) : config.vaults;
13
+ if (vaults.length === 0) {
14
+ throw new SyncUsageError(opts.label
15
+ ? `No vault labeled "${opts.label}". Run \`im sync status\` to list connections.`
16
+ : "No vaults connected. Run `im sync connect obsidian --vault <path>` first.");
17
+ }
18
+ const summary = { synced: 0, failed: 0 };
19
+ for (const vault of vaults) {
20
+ const ok = await syncVault(vault, opts.full ?? false, deps, stdio);
21
+ if (ok)
22
+ summary.synced += 1;
23
+ else
24
+ summary.failed += 1;
25
+ }
26
+ return summary;
27
+ }
28
+ async function syncVault(vault, full, deps, stdio) {
29
+ stdio.out(`[${vault.label}] starting ${full ? "full " : ""}sync (${vault.vault_path})`);
30
+ try {
31
+ const notes = await deps.listNotes(vault.vault_path);
32
+ await syncTreeRoot(OBSIDIAN_CONNECTOR, { label: vault.label, root_id: vault.root_id }, notes, { full, titleFor: noteTitle }, { client: deps.client, env: deps.env }, stdio);
33
+ stdio.out(`[${vault.label}] sync complete`);
34
+ return true;
35
+ }
36
+ catch (err) {
37
+ if (err instanceof SyncUsageError)
38
+ throw err;
39
+ stdio.err(`[${vault.label}] sync error: ${err instanceof Error ? err.message : String(err)}`);
40
+ stdio.out(`[${vault.label}] sync FAILED`);
41
+ return false;
42
+ }
43
+ }
44
+ /** "daily/2026-07-21.md" → "2026-07-21". */
45
+ function noteTitle(path) {
46
+ const base = path
47
+ .split("/")
48
+ .filter((s) => s.length > 0)
49
+ .pop() ?? path;
50
+ return base.replace(/\.md$/i, "");
51
+ }
52
+ //# sourceMappingURL=obsidian.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../../src/sync/obsidian.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { promisify } from "node:util";
5
+ const execFileAsync = promisify(execFile);
6
+ /** Files larger than this are almost never useful search targets. */
7
+ const MAX_FILE_BYTES = 512 * 1024;
8
+ /**
9
+ * Default repo reader: every git-tracked file (`git ls-files -z`, so the
10
+ * mirror follows commits and respects .gitignore), skipping binaries
11
+ * (NUL-byte sniff on the first 8 KB) and oversized files.
12
+ */
13
+ export async function listRepoFiles(repoPath) {
14
+ const { stdout } = await execFileAsync("git", ["-C", repoPath, "ls-files", "-z"], {
15
+ maxBuffer: 64 * 1024 * 1024,
16
+ });
17
+ const paths = stdout.split("\0").filter((p) => p.length > 0);
18
+ const files = [];
19
+ for (const path of paths) {
20
+ const absolute = join(repoPath, path);
21
+ let size;
22
+ let bytes;
23
+ try {
24
+ size = (await stat(absolute)).size;
25
+ if (size > MAX_FILE_BYTES)
26
+ continue;
27
+ bytes = await readFile(absolute);
28
+ }
29
+ catch (err) {
30
+ // Tracked but locally gone (deletion not yet committed) is a genuine
31
+ // deletion. Any other failure (EACCES, EIO) must abort the listing —
32
+ // silently dropping the file would delete a valid server source.
33
+ if (err.code === "ENOENT")
34
+ continue;
35
+ throw err;
36
+ }
37
+ if (bytes.subarray(0, 8192).includes(0))
38
+ continue; // binary
39
+ files.push({ path, content: bytes.toString("utf8") });
40
+ }
41
+ return files.sort((a, b) => a.path.localeCompare(b.path));
42
+ }
43
+ //# sourceMappingURL=repo-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-source.js","sourceRoot":"","sources":["../../src/sync/repo-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,qEAAqE;AACrE,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE;QAChF,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtC,IAAI,IAAY,CAAC;QACjB,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACnC,IAAI,IAAI,GAAG,cAAc;gBAAE,SAAS;YACpC,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,qEAAqE;YACrE,iEAAiE;YACjE,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS;YAC/D,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS,CAAC,SAAS;QAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { readConnectorConfig, writeConnectorConfig, SyncUsageError } from "./state.js";
2
+ import { syncTreeRoot } from "./tree-sync.js";
3
+ export const REPO_CONNECTOR = "repo";
4
+ export async function readRepoConfig(env) {
5
+ return (await readConnectorConfig(REPO_CONNECTOR, env)) ?? { repos: [] };
6
+ }
7
+ export async function writeRepoConfig(config, env) {
8
+ return writeConnectorConfig(REPO_CONNECTOR, config, env);
9
+ }
10
+ export async function syncRepo(opts, deps, stdio) {
11
+ const config = await readRepoConfig(deps.env);
12
+ const repos = opts.label ? config.repos.filter((r) => r.label === opts.label) : config.repos;
13
+ if (repos.length === 0) {
14
+ throw new SyncUsageError(opts.label
15
+ ? `No repo labeled "${opts.label}". Run \`im sync status\` to list connections.`
16
+ : "No repos connected. Run `im sync connect repo --path <repo-dir>` first.");
17
+ }
18
+ const summary = { synced: 0, failed: 0 };
19
+ for (const repo of repos) {
20
+ const ok = await syncOneRepo(repo, opts.full ?? false, deps, stdio);
21
+ if (ok)
22
+ summary.synced += 1;
23
+ else
24
+ summary.failed += 1;
25
+ }
26
+ return summary;
27
+ }
28
+ async function syncOneRepo(repo, full, deps, stdio) {
29
+ stdio.out(`[${repo.label}] starting ${full ? "full " : ""}sync (${repo.repo_path})`);
30
+ try {
31
+ const files = await deps.listFiles(repo.repo_path);
32
+ await syncTreeRoot(REPO_CONNECTOR, { label: repo.label, root_id: repo.root_id }, files, { full, titleFor: fileTitle }, { client: deps.client, env: deps.env }, stdio);
33
+ stdio.out(`[${repo.label}] sync complete`);
34
+ return true;
35
+ }
36
+ catch (err) {
37
+ if (err instanceof SyncUsageError)
38
+ throw err;
39
+ stdio.err(`[${repo.label}] sync error: ${err instanceof Error ? err.message : String(err)}`);
40
+ stdio.out(`[${repo.label}] sync FAILED`);
41
+ return false;
42
+ }
43
+ }
44
+ /** "src/lib/auth.ts" → "auth.ts" — extensions stay (file identity). */
45
+ function fileTitle(path) {
46
+ return (path
47
+ .split("/")
48
+ .filter((s) => s.length > 0)
49
+ .pop() ?? path);
50
+ }
51
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/sync/repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { createHash } from "node:crypto";
2
+ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { configPath } from "../config.js";
5
+ /** Fresh state (a factory, so callers can never mutate a shared constant). */
6
+ export function emptySyncState() {
7
+ return { items: {}, extra: {}, last_synced_at: null };
8
+ }
9
+ /** Misconfiguration the user can fix (maps to the CLI usage exit code). */
10
+ export class SyncUsageError extends Error {
11
+ }
12
+ /**
13
+ * Pure delta computation: which items to push (new or changed hash),
14
+ * which to delete (previously synced, no longer enumerated), and the
15
+ * hash map to persist once the push succeeds.
16
+ */
17
+ export function computeDelta(previousHashes, items) {
18
+ const nextHashes = {};
19
+ const upsertIds = [];
20
+ for (const item of items) {
21
+ nextHashes[item.id] = item.hash;
22
+ if (previousHashes[item.id] !== item.hash)
23
+ upsertIds.push(item.id);
24
+ }
25
+ const deleteIds = Object.keys(previousHashes).filter((id) => !(id in nextHashes));
26
+ return { upsertIds, deleteIds, nextHashes };
27
+ }
28
+ export function sha256Hex(content) {
29
+ return createHash("sha256").update(content, "utf8").digest("hex");
30
+ }
31
+ function syncStateDir(env) {
32
+ return join(dirname(configPath(env)), "sync", "state");
33
+ }
34
+ function sanitizeLabel(label) {
35
+ return label.replace(/[^A-Za-z0-9._-]/g, "_");
36
+ }
37
+ function syncStatePath(connector, label, env) {
38
+ // Sanitizing alone is lossy ("a/b" and "a?b" both become "a_b"); the
39
+ // label-hash suffix keeps distinct labels on distinct state files.
40
+ const suffix = sha256Hex(label).slice(0, 8);
41
+ return join(syncStateDir(env), `${sanitizeLabel(connector)}-${sanitizeLabel(label)}-${suffix}.json`);
42
+ }
43
+ export async function loadSyncState(connector, label, env = process.env) {
44
+ try {
45
+ const raw = await readFile(syncStatePath(connector, label, env), "utf8");
46
+ const parsed = JSON.parse(raw);
47
+ return {
48
+ items: parsed.items ?? {},
49
+ extra: parsed.extra ?? {},
50
+ last_synced_at: parsed.last_synced_at ?? null,
51
+ };
52
+ }
53
+ catch {
54
+ return emptySyncState();
55
+ }
56
+ }
57
+ function connectorConfigPath(connector, env) {
58
+ return join(dirname(configPath(env)), "sync", `${sanitizeLabel(connector)}.json`);
59
+ }
60
+ /** Connector-specific config (e.g. the list of connected palaces/vaults). */
61
+ export async function readConnectorConfig(connector, env = process.env) {
62
+ try {
63
+ const raw = await readFile(connectorConfigPath(connector, env), "utf8");
64
+ return JSON.parse(raw);
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ export async function writeConnectorConfig(connector, config, env = process.env) {
71
+ const path = connectorConfigPath(connector, env);
72
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
73
+ await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
74
+ await chmod(path, 0o600);
75
+ return path;
76
+ }
77
+ export async function saveSyncState(connector, label, state, env = process.env) {
78
+ const path = syncStatePath(connector, label, env);
79
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
80
+ await writeFile(path, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
81
+ // writeFile's mode only applies on creation; enforce on rewrite too.
82
+ await chmod(path, 0o600);
83
+ }
84
+ export async function deleteSyncState(connector, label, env = process.env) {
85
+ await rm(syncStatePath(connector, label, env), { force: true });
86
+ }
87
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/sync/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { computeDelta, emptySyncState, loadSyncState, saveSyncState, sha256Hex } from "./state.js";
2
+ const UPSERT_BATCH = 50;
3
+ /** Server-side BatchSchema caps deletes per request at 1000. */
4
+ const DELETE_BATCH = 1000;
5
+ export async function syncTreeRoot(connector, root, files, opts, deps, stdio) {
6
+ const byPath = new Map(files.map((f) => [f.path, f]));
7
+ // --full re-pushes everything (empty upsert baseline) but must still
8
+ // delete files that vanished since the previous sync — deletions are
9
+ // always computed against the previously synced state.
10
+ const previous = await loadSyncState(connector, root.label, deps.env);
11
+ const state = opts.full ? emptySyncState() : previous;
12
+ const delta = computeDelta(state.items, files.map((f) => ({ id: f.path, hash: sha256Hex(f.content) })));
13
+ if (opts.full) {
14
+ delta.deleteIds = Object.keys(previous.items).filter((id) => !(id in delta.nextHashes));
15
+ }
16
+ if (delta.upsertIds.length === 0 && delta.deleteIds.length === 0) {
17
+ stdio.out(`[${root.label}] ${files.length} files, nothing changed`);
18
+ }
19
+ else {
20
+ let added = 0;
21
+ let updated = 0;
22
+ let removed = 0;
23
+ const deleteChunks = [];
24
+ for (let i = 0; i < delta.deleteIds.length; i += DELETE_BATCH) {
25
+ deleteChunks.push(delta.deleteIds.slice(i, i + DELETE_BATCH));
26
+ }
27
+ const batches = [];
28
+ for (let i = 0; i < delta.upsertIds.length; i += UPSERT_BATCH) {
29
+ batches.push({
30
+ upserts: delta.upsertIds.slice(i, i + UPSERT_BATCH),
31
+ deletes: batches.length < deleteChunks.length ? deleteChunks[batches.length] : [],
32
+ });
33
+ }
34
+ for (let i = batches.length; i < deleteChunks.length; i += 1) {
35
+ batches.push({ upserts: [], deletes: deleteChunks[i] });
36
+ }
37
+ for (const batch of batches) {
38
+ const result = await deps.client.request("POST", "/api/sync/batch", {
39
+ root_id: root.root_id,
40
+ upserts: batch.upserts.map((path) => {
41
+ const file = byPath.get(path);
42
+ return { external_id: file.path, content: file.content, title: opts.titleFor(file.path) };
43
+ }),
44
+ deletes: batch.deletes,
45
+ });
46
+ added += result.added;
47
+ updated += result.updated;
48
+ removed += result.removed;
49
+ }
50
+ stdio.out(`[${root.label}] +${added} ~${updated} -${removed}`);
51
+ }
52
+ state.items = delta.nextHashes;
53
+ state.last_synced_at = new Date().toISOString();
54
+ await saveSyncState(connector, root.label, state, deps.env);
55
+ }
56
+ //# sourceMappingURL=tree-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-sync.js","sourceRoot":"","sources":["../../src/sync/tree-sync.ts"],"names":[],"mappings":"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"}