@fleox-agent-crm/agentcrm 0.0.0-stage → 0.3.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 (129) hide show
  1. package/LICENSE +143 -0
  2. package/README.md +223 -2
  3. package/changelog.json +57 -0
  4. package/dist/admin/admin-files.json +26 -0
  5. package/dist/admin/assets/chartsImpl-CE2akJat.js +91 -0
  6. package/dist/admin/assets/firebase-BHSUggRo.js +2 -0
  7. package/dist/admin/assets/gitJson-BulGh2KV.js +3 -0
  8. package/dist/admin/assets/index-DidVVK7L.js +1181 -0
  9. package/dist/admin/assets/index-hMXwHSvN.css +1 -0
  10. package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
  11. package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
  12. package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
  13. package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
  14. package/dist/admin/assets/lucide-react.min-DEDlHCPj.js +6 -0
  15. package/dist/admin/assets/sdk-CnrLnlRo.js +1717 -0
  16. package/dist/admin/config.js +3 -0
  17. package/dist/admin/favicon.svg +1 -0
  18. package/dist/admin/index.html +139 -0
  19. package/dist/admin/runtime/agentcrm-runtime.js +104 -0
  20. package/dist/admin/runtime/lucide-react.js +791 -0
  21. package/dist/admin/runtime/react-dom-client.js +17 -0
  22. package/dist/admin/runtime/react-dom.js +20 -0
  23. package/dist/admin/runtime/react-i18next.js +19 -0
  24. package/dist/admin/runtime/react-jsx-runtime.js +17 -0
  25. package/dist/admin/runtime/react.js +22 -0
  26. package/dist/agent/agentcrm.mjs +3932 -0
  27. package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
  28. package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
  29. package/dist/agent/chunks/api-B6OqNVWp.mjs +97 -0
  30. package/dist/agent/chunks/appearance-BaShelzt.mjs +174 -0
  31. package/dist/agent/chunks/automations-BWrvLqP6.mjs +698 -0
  32. package/dist/agent/chunks/backup-ClSmxmFZ.mjs +103 -0
  33. package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
  34. package/dist/agent/chunks/compact-CsFPaAAq.mjs +69 -0
  35. package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
  36. package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
  37. package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
  38. package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
  39. package/dist/agent/chunks/exportCsv-B5hRvkNm.mjs +95 -0
  40. package/dist/agent/chunks/extCheck--7lmVvl7.mjs +193 -0
  41. package/dist/agent/chunks/extPackage-BGl5Oqs8.mjs +115 -0
  42. package/dist/agent/chunks/extScaffold-DqxI7R1e.mjs +320 -0
  43. package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
  44. package/dist/agent/chunks/extensions-BP1k60md.mjs +151 -0
  45. package/dist/agent/chunks/featureRegistry-C9nbVShp.mjs +149 -0
  46. package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
  47. package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
  48. package/dist/agent/chunks/files-q8Xu5bft.mjs +367 -0
  49. package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
  50. package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
  51. package/dist/agent/chunks/forms-BKU5F9WF.mjs +847 -0
  52. package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
  53. package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
  54. package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
  55. package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
  56. package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
  57. package/dist/agent/chunks/importCsv-DmE_sBch.mjs +334 -0
  58. package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
  59. package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
  60. package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
  61. package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
  62. package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
  63. package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
  64. package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
  65. package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
  66. package/dist/agent/chunks/install-CpHoUFtL.mjs +188 -0
  67. package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
  68. package/dist/agent/chunks/loader-C9nm3iwx.mjs +184 -0
  69. package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
  70. package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
  71. package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
  72. package/dist/agent/chunks/modules-CqdVQVJb.mjs +177 -0
  73. package/dist/agent/chunks/nodeImport-BPCt5uMu.mjs +38 -0
  74. package/dist/agent/chunks/package-DWbn3t2R.mjs +194 -0
  75. package/dist/agent/chunks/papaparse-DjBYnQM2.mjs +1285 -0
  76. package/dist/agent/chunks/pipelines-DCdRBNBV.mjs +234 -0
  77. package/dist/agent/chunks/plan-C0TBrU92.mjs +254 -0
  78. package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
  79. package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
  80. package/dist/agent/chunks/records-DoqiT9xR.mjs +2191 -0
  81. package/dist/agent/chunks/registry-Ceexawxl.mjs +182 -0
  82. package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
  83. package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
  84. package/dist/agent/chunks/reports-BB59N6ke.mjs +189 -0
  85. package/dist/agent/chunks/runtime-DHo7ZEEO.mjs +14852 -0
  86. package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
  87. package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
  88. package/dist/agent/chunks/schema-7-4-qtSS.mjs +203 -0
  89. package/dist/agent/chunks/schema-DlHt2PxI.mjs +367 -0
  90. package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
  91. package/dist/agent/chunks/seed-CugPGYZL.mjs +523 -0
  92. package/dist/agent/chunks/server-ISwlDRCN.mjs +242 -0
  93. package/dist/agent/chunks/settings-BtvR_jdm.mjs +76 -0
  94. package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
  95. package/dist/agent/chunks/stats-CfeJssSS.mjs +51 -0
  96. package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
  97. package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
  98. package/dist/agent/chunks/tools-Bew8lxKx.mjs +844 -0
  99. package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
  100. package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
  101. package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
  102. package/dist/agent/chunks/views-fO_BM5U_.mjs +109 -0
  103. package/dist/version.json +7 -0
  104. package/package.json +97 -4
  105. package/plugin/.claude-plugin/plugin.json +17 -0
  106. package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
  107. package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
  108. package/plugin/skills/agentcrm-admin/references/import.md +16 -0
  109. package/plugin/skills/agentcrm-admin/references/records.md +30 -0
  110. package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
  111. package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
  112. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
  113. package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
  114. package/plugin/skills/agentcrm-extensions/references/testing.md +76 -0
  115. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
  116. package/plugin/skills/agentcrm-install/SKILL.md +116 -0
  117. package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
  118. package/plugin/skills/agentcrm-install/references/github.md +20 -0
  119. package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
  120. package/plugin/skills/agentcrm-install/references/plans.md +45 -0
  121. package/plugin/skills/agentcrm-install/references/s3.md +24 -0
  122. package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
  123. package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
  124. package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
  125. package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
  126. package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
  127. package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
  128. package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
  129. package/types/agentcrm-runtime.d.ts +894 -0
@@ -0,0 +1,535 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { isEncrypted, decryptBytes, encryptBytes, PassphraseError, decryptText, newEncryptionParams, PBKDF2_ITERATIONS, encryptText } from "./index-CWrrF1wl.mjs";
8
+ import { b as bytesToBase64, d as base64ToBytes } from "./encoding-C9MzdP95.mjs";
9
+ import { m as mapLimit } from "./http-Bk9ICb7I.mjs";
10
+ import { b as setPath, a as splitPath, n as normalizeDoc, e as emptySnapshot, c as createStoreCore } from "./core-Dy7vY9H6.mjs";
11
+ import { w as withFileStore } from "./index-RCmo-uld.mjs";
12
+ import { a as assertFileId } from "./ids-u-noYcy5.mjs";
13
+ import { COLLECTIONS } from "./types-BOy9PUl7.mjs";
14
+ import "./index-BM8qeGOb.mjs";
15
+ const MAX_COUNTER = 36 ** 4 - 1;
16
+ const MAX_DRIFT_MS = 24 * 36e5;
17
+ function formatHlc(h) {
18
+ return `${String(Math.floor(h.wall)).padStart(13, "0")}-${h.counter.toString(36).padStart(4, "0")}-${h.node}`;
19
+ }
20
+ function parseHlc(s) {
21
+ const m = /^(\d{13})-([0-9a-z]{4})-(.+)$/.exec(s);
22
+ if (!m) throw new Error(`Invalid HLC timestamp "${s}"`);
23
+ return { wall: Number(m[1]), counter: parseInt(m[2], 36), node: m[3] };
24
+ }
25
+ function maxHlc(a, b) {
26
+ if (!a) return b;
27
+ if (!b) return a;
28
+ return a > b ? a : b;
29
+ }
30
+ function createHlcClock(node, wallClock = Date.now) {
31
+ if (!/^[0-9a-z]+$/i.test(node)) throw new Error(`Invalid HLC node id "${node}"`);
32
+ let state = { wall: 0, counter: 0, node };
33
+ let issued = false;
34
+ function bump(wall, counter) {
35
+ if (counter > MAX_COUNTER) {
36
+ state = { wall: wall + 1, counter: 0, node };
37
+ } else {
38
+ state = { wall, counter, node };
39
+ }
40
+ }
41
+ return {
42
+ node,
43
+ now() {
44
+ const physical = wallClock();
45
+ if (physical > state.wall) bump(physical, 0);
46
+ else bump(state.wall, state.counter + 1);
47
+ issued = true;
48
+ return formatHlc(state);
49
+ },
50
+ receive(stamp) {
51
+ const remote = parseHlc(stamp);
52
+ const physical = wallClock();
53
+ if (remote.wall > physical + MAX_DRIFT_MS) return;
54
+ if (remote.wall > state.wall) bump(remote.wall, remote.counter + 1);
55
+ else if (remote.wall === state.wall && remote.counter >= state.counter) bump(state.wall, remote.counter + 1);
56
+ issued = true;
57
+ },
58
+ last() {
59
+ return issued ? formatHlc(state) : "";
60
+ }
61
+ };
62
+ }
63
+ function createJournalFileStore(opts) {
64
+ const { connector, folder } = opts;
65
+ const path = (id) => `${folder}${id}.bin`;
66
+ async function encode(bytes) {
67
+ const key = await opts.key();
68
+ return key ? encryptBytes(bytes, key.passphrase, key.params) : bytes;
69
+ }
70
+ const put = async (id, bytes) => {
71
+ assertFileId(id);
72
+ const data = await encode(bytes);
73
+ await connector.put({ path: path(id), content: bytesToBase64(data), encoding: "base64" });
74
+ };
75
+ return {
76
+ kind: "journal",
77
+ put,
78
+ async putMany(items) {
79
+ for (const it of items) await put(it.id, it.bytes);
80
+ },
81
+ async get(id) {
82
+ assertFileId(id);
83
+ const bytes = await connector.getBytes(path(id));
84
+ if (!bytes) return null;
85
+ return isEncrypted(bytes) ? decryptBytes(bytes, opts.passphrase()) : bytes;
86
+ },
87
+ async delete(id) {
88
+ assertFileId(id);
89
+ await connector.delete(path(id));
90
+ },
91
+ async list() {
92
+ if (!connector.listPrefix) return [];
93
+ return (await connector.listPrefix(folder)).filter((p) => p.endsWith(".bin")).map((p) => p.slice(folder.length, -".bin".length)).filter((id) => !id.includes("/"));
94
+ }
95
+ };
96
+ }
97
+ const GZIP_MAGIC = [31, 139];
98
+ function hasCompressionStream() {
99
+ return typeof CompressionStream !== "undefined" && typeof DecompressionStream !== "undefined";
100
+ }
101
+ async function nodeZlib() {
102
+ const specifier = "node:zlib";
103
+ return await import(
104
+ /* @vite-ignore */
105
+ specifier
106
+ );
107
+ }
108
+ async function pipe(bytes, transform) {
109
+ const stream = new Blob([bytes]).stream().pipeThrough(transform);
110
+ return new Uint8Array(await new Response(stream).arrayBuffer());
111
+ }
112
+ async function gzip(bytes) {
113
+ if (hasCompressionStream()) return pipe(bytes, new CompressionStream("gzip"));
114
+ return new Uint8Array((await nodeZlib()).gzipSync(bytes));
115
+ }
116
+ async function gunzip(bytes) {
117
+ if (hasCompressionStream()) return pipe(bytes, new DecompressionStream("gzip"));
118
+ return new Uint8Array((await nodeZlib()).gunzipSync(bytes));
119
+ }
120
+ function isGzip(bytes) {
121
+ return bytes.length > 2 && bytes[0] === GZIP_MAGIC[0] && bytes[1] === GZIP_MAGIC[1];
122
+ }
123
+ async function encodeJson(value, key) {
124
+ const compressed = await gzip(new TextEncoder().encode(JSON.stringify(value)));
125
+ return key ? encryptBytes(compressed, key.passphrase, key.params) : compressed;
126
+ }
127
+ async function decodeJson(bytes, passphrase) {
128
+ let data = bytes;
129
+ if (isEncrypted(data)) data = await decryptBytes(data, passphrase);
130
+ if (isGzip(data)) data = await gunzip(data);
131
+ return JSON.parse(new TextDecoder().decode(data));
132
+ }
133
+ function emptyMergeState() {
134
+ return Object.fromEntries(COLLECTIONS.map((c) => [c, /* @__PURE__ */ new Map()]));
135
+ }
136
+ function toJournalOp(m, t) {
137
+ if (m.op === "set") return { t, op: "set", c: m.collection, doc: m.doc };
138
+ if (m.op === "delete") return { t, op: "delete", c: m.collection, id: m.id };
139
+ return {
140
+ t,
141
+ op: "patch",
142
+ c: m.collection,
143
+ id: m.id,
144
+ f: Object.entries(m.fields).filter(([k]) => splitPath(k)[0] !== "id").map(([k, v]) => v === void 0 ? [k] : [k, v])
145
+ };
146
+ }
147
+ function docState(state, c, id) {
148
+ let ds = state[c].get(id);
149
+ if (!ds) {
150
+ ds = { e: {} };
151
+ state[c].set(id, ds);
152
+ }
153
+ return ds;
154
+ }
155
+ function isDescendant(path, ancestor) {
156
+ return path.length > ancestor.length && path.startsWith(ancestor) && path[ancestor.length] === ".";
157
+ }
158
+ function applyEntry(ds, path, entry) {
159
+ if (ds.reset && entry.t < ds.reset) return false;
160
+ const parts = splitPath(path);
161
+ for (let i = 1; i <= parts.length; i++) {
162
+ const existing = ds.e[parts.slice(0, i).join(".")];
163
+ if (existing && (i === parts.length ? existing.t >= entry.t : existing.t > entry.t)) return false;
164
+ }
165
+ for (const key of Object.keys(ds.e)) {
166
+ if (isDescendant(key, path) && ds.e[key].t < entry.t) delete ds.e[key];
167
+ }
168
+ ds.e[path] = entry;
169
+ return true;
170
+ }
171
+ function applySet(ds, t, doc) {
172
+ if (ds.reset && ds.reset >= t) return false;
173
+ ds.reset = t;
174
+ for (const [key, entry] of Object.entries(ds.e)) if (entry.t < t) delete ds.e[key];
175
+ for (const [key, value] of Object.entries(doc)) {
176
+ if (key === "id" || value === void 0) continue;
177
+ applyEntry(ds, key, { t, v: value });
178
+ }
179
+ return true;
180
+ }
181
+ function applyOp(state, op) {
182
+ if (!state[op.c]) return false;
183
+ const id = op.op === "set" ? op.doc.id : op.id;
184
+ if (typeof id !== "string" || !id) return false;
185
+ const ds = docState(state, op.c, id);
186
+ if (op.op === "set") return applySet(ds, op.t, op.doc);
187
+ if (op.op === "delete") {
188
+ if (ds.del && ds.del >= op.t) return false;
189
+ ds.del = op.t;
190
+ return true;
191
+ }
192
+ let changed = false;
193
+ for (const item of op.f) {
194
+ const entry = item.length === 1 ? { t: op.t, u: 1 } : { t: op.t, v: item[1] };
195
+ if (applyEntry(ds, item[0], entry)) changed = true;
196
+ }
197
+ return changed;
198
+ }
199
+ function mergeStates(into, other) {
200
+ const touched = /* @__PURE__ */ new Set();
201
+ for (const c of COLLECTIONS) {
202
+ for (const [id, src] of other[c] ?? []) {
203
+ const ds = docState(into, c, id);
204
+ let changed = false;
205
+ if (src.reset && (!ds.reset || src.reset > ds.reset)) {
206
+ ds.reset = src.reset;
207
+ for (const [key, entry] of Object.entries(ds.e)) if (entry.t < src.reset) delete ds.e[key];
208
+ changed = true;
209
+ }
210
+ if (src.del && (!ds.del || src.del > ds.del)) {
211
+ ds.del = src.del;
212
+ changed = true;
213
+ }
214
+ for (const [path, entry] of Object.entries(src.e).sort((a, b) => a[1].t < b[1].t ? -1 : 1)) {
215
+ if (applyEntry(ds, path, entry)) changed = true;
216
+ }
217
+ if (changed) touched.add(`${c}/${id}`);
218
+ }
219
+ }
220
+ return touched;
221
+ }
222
+ function isVisible(ds) {
223
+ return !!ds.reset && (!ds.del || ds.reset > ds.del);
224
+ }
225
+ function materializeDoc(id, ds) {
226
+ if (!isVisible(ds)) return null;
227
+ let out = {};
228
+ const entries = Object.entries(ds.e).sort((a, b) => a[1].t < b[1].t ? -1 : a[1].t > b[1].t ? 1 : a[0].length - b[0].length);
229
+ for (const [path, entry] of entries) out = setPath(out, splitPath(path), entry.u ? void 0 : entry.v);
230
+ out.id = id;
231
+ return normalizeDoc(out);
232
+ }
233
+ function maxStamp(state) {
234
+ let max = "";
235
+ for (const c of COLLECTIONS) {
236
+ for (const ds of state[c].values()) {
237
+ if (ds.reset && ds.reset > max) max = ds.reset;
238
+ if (ds.del && ds.del > max) max = ds.del;
239
+ for (const e of Object.values(ds.e)) if (e.t > max) max = e.t;
240
+ }
241
+ }
242
+ return max;
243
+ }
244
+ function purgeState(state) {
245
+ let purged = 0;
246
+ for (const c of COLLECTIONS) {
247
+ for (const [id, ds] of state[c]) {
248
+ if (isVisible(ds)) continue;
249
+ if (Object.keys(ds.e).length || ds.reset) purged++;
250
+ if (ds.del) state[c].set(id, { del: ds.del, e: {} });
251
+ else state[c].delete(id);
252
+ }
253
+ }
254
+ return purged;
255
+ }
256
+ function serializeState(state) {
257
+ const out = {};
258
+ for (const c of COLLECTIONS) {
259
+ if (state[c].size === 0) continue;
260
+ out[c] = Object.fromEntries(state[c]);
261
+ }
262
+ return out;
263
+ }
264
+ function deserializeState(raw) {
265
+ const state = emptyMergeState();
266
+ for (const c of COLLECTIONS) {
267
+ for (const [id, ds] of Object.entries(raw?.[c] ?? {})) {
268
+ state[c].set(id, { ...ds.reset ? { reset: ds.reset } : {}, ...ds.del ? { del: ds.del } : {}, e: { ...ds.e ?? {} } });
269
+ }
270
+ }
271
+ return state;
272
+ }
273
+ function cloneMergeState(state) {
274
+ return deserializeState(JSON.parse(JSON.stringify(serializeState(state))));
275
+ }
276
+ const JOURNAL_FORMAT = "agentcrm-journal";
277
+ const JOURNAL_VERSION = 1;
278
+ const DEFAULT_POLL_MS = 2e4;
279
+ const KEY_CHECK = "agentcrm-keycheck";
280
+ function newClientId() {
281
+ const bytes = crypto.getRandomValues(new Uint8Array(6));
282
+ return [...bytes].map((b) => (b % 36).toString(36)).join("") + Date.now().toString(36).slice(-2);
283
+ }
284
+ function join(root, path) {
285
+ const r = root.replace(/^\/+|\/+$/g, "");
286
+ return r ? `${r}/${path}` : path;
287
+ }
288
+ function createJournalStore(opts) {
289
+ const connector = opts.connector;
290
+ if (!connector.listPrefix) throw new Error(`The "${connector.id}" storage can't list files: it can't hold a journal dataset.`);
291
+ if (!connector.getBytes) throw new Error(`The "${connector.id}" storage can't read binary files: it can't hold a journal dataset.`);
292
+ const listPrefix = connector.listPrefix.bind(connector);
293
+ const getBytes = connector.getBytes.bind(connector);
294
+ const clientId = (opts.clientId ?? newClientId()).toLowerCase().replace(/[^0-9a-z]/g, "") || newClientId();
295
+ const clock = createHlcClock(clientId, opts.wallClock);
296
+ const paths = {
297
+ keyinfo: join(opts.root, "keyinfo.json"),
298
+ snapshot: join(opts.root, "snapshot.bin"),
299
+ journal: join(opts.root, "journal/"),
300
+ files: join(opts.root, "files/")
301
+ };
302
+ const segmentPath = (name) => `${paths.journal}${name}`;
303
+ let keyInfo = null;
304
+ let key = null;
305
+ let state = emptyMergeState();
306
+ const applied = /* @__PURE__ */ new Set();
307
+ let snapshotApplied = /* @__PURE__ */ new Set();
308
+ let materialized = emptySnapshot();
309
+ function passphrase() {
310
+ const p = typeof opts.passphrase === "function" ? opts.passphrase() : opts.passphrase;
311
+ return p ?? null;
312
+ }
313
+ async function readText(path) {
314
+ const bytes = await getBytes(path);
315
+ return bytes ? new TextDecoder().decode(bytes) : null;
316
+ }
317
+ async function loadKey() {
318
+ const raw = await readText(paths.keyinfo);
319
+ if (raw) {
320
+ const info = JSON.parse(raw);
321
+ if (info.format !== JOURNAL_FORMAT) throw new Error(`${paths.keyinfo} is not an AgentCRM journal key file.`);
322
+ keyInfo = info;
323
+ }
324
+ const encrypted = keyInfo ? keyInfo.encrypted : opts.encrypted !== false;
325
+ if (!encrypted) {
326
+ key = null;
327
+ return;
328
+ }
329
+ const pass = passphrase();
330
+ if (!pass) throw new PassphraseError("missing");
331
+ if (keyInfo?.kdf) {
332
+ const params = { salt: base64ToBytes(keyInfo.kdf.salt), iterations: keyInfo.kdf.iterations };
333
+ if (keyInfo.check) {
334
+ const check = await decryptText(base64ToBytes(keyInfo.check), pass);
335
+ if (check !== KEY_CHECK) throw new PassphraseError("wrong");
336
+ }
337
+ key = { passphrase: pass, params };
338
+ } else {
339
+ key = { passphrase: pass, params: newEncryptionParams(opts.iterations ?? PBKDF2_ITERATIONS) };
340
+ }
341
+ }
342
+ async function ensureKeyInfo() {
343
+ if (keyInfo) return;
344
+ const info = { format: JOURNAL_FORMAT, version: JOURNAL_VERSION, encrypted: !!key, createdAt: Date.now() };
345
+ if (key) {
346
+ info.kdf = { name: "PBKDF2-SHA256", iterations: key.params.iterations, salt: bytesToBase64(key.params.salt) };
347
+ info.check = bytesToBase64(await encryptText(KEY_CHECK, key.passphrase, key.params));
348
+ }
349
+ await connector.put({ path: paths.keyinfo, content: JSON.stringify(info, null, 2) + "\n" });
350
+ keyInfo = info;
351
+ }
352
+ async function readSnapshot() {
353
+ const bytes = await getBytes(paths.snapshot);
354
+ if (!bytes) return null;
355
+ const file = await decodeJson(bytes, passphrase());
356
+ if (file.format !== "agentcrm-snapshot") throw new Error(`${paths.snapshot} is not an AgentCRM snapshot.`);
357
+ return file;
358
+ }
359
+ function adoptSnapshot(file) {
360
+ if (!file) return /* @__PURE__ */ new Set();
361
+ if (file.hlc) clock.receive(file.hlc);
362
+ snapshotApplied = new Set(file.applied);
363
+ for (const name of file.applied) applied.add(name);
364
+ return mergeStates(state, deserializeState(file.state));
365
+ }
366
+ function applySegment(name, seg, touched) {
367
+ for (const op of seg.ops ?? []) {
368
+ if (!COLLECTIONS.includes(op.c)) continue;
369
+ clock.receive(op.t);
370
+ if (applyOp(state, op)) touched.add(`${op.c}/${op.op === "set" ? op.doc.id : op.id}`);
371
+ }
372
+ applied.add(name);
373
+ }
374
+ async function sync(withSnapshot) {
375
+ const touched = /* @__PURE__ */ new Set();
376
+ const listed = (await listPrefix(paths.journal)).filter((p) => p.endsWith(".bin")).map((p) => p.slice(paths.journal.length));
377
+ if (withSnapshot) for (const k of adoptSnapshot(await readSnapshot())) touched.add(k);
378
+ let todo = listed.filter((n) => !applied.has(n)).sort();
379
+ for (let attempt = 0; todo.length && attempt < 3; attempt++) {
380
+ const missing = [];
381
+ const read = await mapLimit(todo, 6, async (name) => {
382
+ const bytes = await getBytes(segmentPath(name));
383
+ if (!bytes) {
384
+ missing.push(name);
385
+ return null;
386
+ }
387
+ return { name, seg: await decodeJson(bytes, passphrase()) };
388
+ });
389
+ for (const r of read) if (r) applySegment(r.name, r.seg, touched);
390
+ if (!missing.length) break;
391
+ for (const k of adoptSnapshot(await readSnapshot())) touched.add(k);
392
+ todo = missing.filter((n) => !applied.has(n));
393
+ }
394
+ return touched;
395
+ }
396
+ function rematerialize(touched) {
397
+ const next = emptySnapshot();
398
+ for (const c of COLLECTIONS) {
399
+ if (touched === null) {
400
+ for (const [id, ds] of state[c]) {
401
+ const doc = materializeDoc(id, ds);
402
+ if (doc) next[c].set(id, doc);
403
+ }
404
+ } else {
405
+ for (const [id, doc] of materialized[c]) next[c].set(id, doc);
406
+ }
407
+ }
408
+ if (touched) {
409
+ for (const key2 of touched) {
410
+ const slash = key2.indexOf("/");
411
+ const c = key2.slice(0, slash);
412
+ const id = key2.slice(slash + 1);
413
+ const ds = state[c].get(id);
414
+ const doc = ds ? materializeDoc(id, ds) : null;
415
+ if (doc) next[c].set(id, doc);
416
+ else next[c].delete(id);
417
+ }
418
+ }
419
+ materialized = next;
420
+ return next;
421
+ }
422
+ const core = createStoreCore({
423
+ kind: "journal",
424
+ pollIntervalMs: opts.pollIntervalMs ?? DEFAULT_POLL_MS,
425
+ async load() {
426
+ await loadKey();
427
+ state = emptyMergeState();
428
+ applied.clear();
429
+ await sync(true);
430
+ return rematerialize(null);
431
+ },
432
+ async persist(mutations, before, _after, message) {
433
+ const effective = mutations.filter((m) => m.op !== "patch" || before[m.collection].has(m.id));
434
+ if (effective.length === 0) return;
435
+ const ops = effective.map((m) => toJournalOp(m, clock.now()));
436
+ const name = `${ops[0].t}.bin`;
437
+ const seg = { format: "agentcrm-segment", version: JOURNAL_VERSION, client: clientId, message, ops };
438
+ await ensureKeyInfo();
439
+ const bytes = await encodeJson(seg, key);
440
+ await connector.put({ path: segmentPath(name), content: bytesToBase64(bytes), encoding: "base64" });
441
+ const touched = /* @__PURE__ */ new Set();
442
+ applySegment(name, seg, touched);
443
+ rematerialize(touched);
444
+ },
445
+ async poll() {
446
+ const touched = await sync(false);
447
+ return touched.size ? rematerialize(touched) : null;
448
+ },
449
+ async identity() {
450
+ return opts.identity === void 0 ? { uid: "owner", email: "", displayName: "Owner", role: "admin" } : opts.identity;
451
+ },
452
+ async compact() {
453
+ await sync(true);
454
+ const next = cloneMergeState(state);
455
+ purgeState(next);
456
+ const merged = (await listPrefix(paths.journal)).filter((p) => p.endsWith(".bin")).map((p) => p.slice(paths.journal.length)).filter((n) => applied.has(n));
457
+ const compactionId = `${clientId}-${clock.now()}`;
458
+ const file = {
459
+ format: "agentcrm-snapshot",
460
+ version: JOURNAL_VERSION,
461
+ hlc: maxHlc(clock.last(), maxStamp(next)) ?? "",
462
+ applied: merged.sort(),
463
+ compactionId,
464
+ createdAt: Date.now(),
465
+ createdBy: clientId,
466
+ state: serializeState(next)
467
+ };
468
+ await ensureKeyInfo();
469
+ await connector.put({ path: paths.snapshot, content: bytesToBase64(await encodeJson(file, key)), encoding: "base64" });
470
+ const check = await readSnapshot();
471
+ if (check?.compactionId === compactionId) {
472
+ await mapLimit(merged, 4, (name) => connector.delete(segmentPath(name)));
473
+ }
474
+ state = next;
475
+ snapshotApplied = new Set(merged);
476
+ return rematerialize(null);
477
+ }
478
+ });
479
+ const store = core;
480
+ Object.defineProperty(store, "clientId", { value: clientId });
481
+ withFileStore(
482
+ store,
483
+ createJournalFileStore({
484
+ connector,
485
+ folder: paths.files,
486
+ async key() {
487
+ if (!keyInfo && !key) await loadKey();
488
+ await ensureKeyInfo();
489
+ return key;
490
+ },
491
+ passphrase
492
+ })
493
+ );
494
+ store.stats = async () => {
495
+ const listed = (await listPrefix(paths.journal)).filter((p) => p.endsWith(".bin"));
496
+ let documents = 0;
497
+ let tombstones = 0;
498
+ for (const c of COLLECTIONS) {
499
+ for (const ds of state[c].values()) {
500
+ if (materializeDoc("x", ds)) documents++;
501
+ else tombstones++;
502
+ }
503
+ }
504
+ return {
505
+ segments: listed.length,
506
+ pendingSegments: listed.filter((p) => !snapshotApplied.has(p.slice(paths.journal.length))).length,
507
+ snapshotSegments: snapshotApplied.size,
508
+ documents,
509
+ tombstones
510
+ };
511
+ };
512
+ return store;
513
+ }
514
+ export {
515
+ DEFAULT_POLL_MS,
516
+ JOURNAL_FORMAT,
517
+ applyEntry,
518
+ applyOp,
519
+ cloneMergeState,
520
+ createJournalStore,
521
+ decodeJson,
522
+ deserializeState,
523
+ emptyMergeState,
524
+ encodeJson,
525
+ gunzip,
526
+ gzip,
527
+ isVisible,
528
+ materializeDoc,
529
+ maxStamp,
530
+ mergeStates,
531
+ newClientId,
532
+ purgeState,
533
+ serializeState,
534
+ toJournalOp
535
+ };