@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,528 @@
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 { SECRET_SETTING_KEYS, datasetId, getSecret, getGitToken, setSecret, setSecretJson, getSecretJson } from "./runtimeConfig-CCOFg1RM.mjs";
8
+ import { setGitToken } from "./runtimeConfig-CCOFg1RM.mjs";
9
+ import { j as joinRepoPath, G as GitConflictError, c as createRemoteGitTransport } from "./s3-Dz50-NuA.mjs";
10
+ import { b as bytesToBase64, a as base64ToUtf8 } from "./encoding-C9MzdP95.mjs";
11
+ import { w as withFileStore } from "./index-RCmo-uld.mjs";
12
+ import { e as emptySnapshot, n as normalizeDoc, a as splitPath, g as getPath, b as setPath, c as createStoreCore, d as applyMutations } from "./core-Dy7vY9H6.mjs";
13
+ import { COLLECTIONS, isCollectionName, DocConflictError } from "./types-BOy9PUl7.mjs";
14
+ import { a as assertFileId } from "./ids-u-noYcy5.mjs";
15
+ import "./index-BM8qeGOb.mjs";
16
+ import { l as loadFolderHandle, e as ensureFolderPermission, c as createLocalFsTransport } from "./registry-DijAKZ24.mjs";
17
+ import "./http-Bk9ICb7I.mjs";
18
+ import "./index-w03ReRs4.mjs";
19
+ import "./_commonjsHelpers-ClciMIcB.mjs";
20
+ import "stream";
21
+ import "events";
22
+ import "buffer";
23
+ import "util";
24
+ const GIT_JSON_FORMAT = "agentcrm-git-json";
25
+ const GIT_JSON_VERSION = 1;
26
+ const MARKER_FILE = "agentcrm.json";
27
+ function docKey(collection, id) {
28
+ return `${collection}/${id}`;
29
+ }
30
+ function encodeName(id) {
31
+ return encodeURIComponent(id).replace(/\./g, "%2E");
32
+ }
33
+ function decodeName(name) {
34
+ try {
35
+ return decodeURIComponent(name);
36
+ } catch {
37
+ return name;
38
+ }
39
+ }
40
+ function docPath(collection, doc) {
41
+ if (collection === "records") {
42
+ const object = typeof doc.object === "string" && doc.object ? doc.object : "_";
43
+ return `records/${encodeName(object)}/${encodeName(doc.id)}.json`;
44
+ }
45
+ return `${collection}/${encodeName(doc.id)}.json`;
46
+ }
47
+ function parseDocPath(path) {
48
+ const parts = path.split("/");
49
+ const [head] = parts;
50
+ if (!isCollectionName(head)) return null;
51
+ const file = parts[parts.length - 1];
52
+ if (!file.endsWith(".json")) return null;
53
+ if (head === "records" ? parts.length !== 3 : parts.length !== 2) return null;
54
+ const id = decodeName(file.slice(0, -".json".length));
55
+ return id ? { collection: head, id } : null;
56
+ }
57
+ function isLayoutFile(path) {
58
+ return path === MARKER_FILE || parseDocPath(path) !== null;
59
+ }
60
+ function serializeDoc(doc) {
61
+ return JSON.stringify(doc, null, 2) + "\n";
62
+ }
63
+ function collectSecrets(value, path, out) {
64
+ if (Array.isArray(value)) return value.map((v, i) => collectSecrets(v, [...path, String(i)], out));
65
+ if (!value || typeof value !== "object") return value;
66
+ const clean = {};
67
+ for (const [k, v] of Object.entries(value)) {
68
+ if (SECRET_SETTING_KEYS.has(k) && typeof v === "string") {
69
+ if (v) out[[...path, k].join(".")] = v;
70
+ continue;
71
+ }
72
+ clean[k] = collectSecrets(v, [...path, k], out);
73
+ }
74
+ return clean;
75
+ }
76
+ function stripDocSecrets(doc) {
77
+ const secrets = {};
78
+ const clean = collectSecrets(doc, [], secrets);
79
+ return { doc: clean, secrets };
80
+ }
81
+ function mergeDocSecrets(doc, secrets) {
82
+ if (!secrets) return doc;
83
+ let out = { ...doc };
84
+ for (const [path, value] of Object.entries(secrets)) {
85
+ const p = splitPath(path);
86
+ if (getPath(out, p.slice(0, -1)) === void 0 && p.length > 1) continue;
87
+ out = setPath(out, p, value);
88
+ }
89
+ return out;
90
+ }
91
+ function filesToSnapshot(files, secrets = {}) {
92
+ const snapshot = emptySnapshot();
93
+ const index = /* @__PURE__ */ new Map();
94
+ const errors = [];
95
+ for (const [path, text] of files) {
96
+ const parsed = parseDocPath(path);
97
+ if (!parsed) continue;
98
+ let doc;
99
+ try {
100
+ const raw = JSON.parse(text);
101
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("not an object");
102
+ doc = { ...raw, id: parsed.id };
103
+ } catch (err) {
104
+ errors.push(`${path}: ${err.message}`);
105
+ continue;
106
+ }
107
+ if (parsed.collection === "config") doc = mergeDocSecrets(doc, secrets[parsed.id]);
108
+ snapshot[parsed.collection].set(parsed.id, normalizeDoc(doc));
109
+ index.set(docKey(parsed.collection, parsed.id), path);
110
+ }
111
+ return { snapshot, index, errors };
112
+ }
113
+ function planChanges(keys, target, files, index, secrets) {
114
+ const nextFiles = new Map(files);
115
+ const nextIndex = new Map(index);
116
+ const nextSecrets = { ...secrets };
117
+ const changes = /* @__PURE__ */ new Map();
118
+ const seen = /* @__PURE__ */ new Set();
119
+ for (const [collection, id] of keys) {
120
+ const key = docKey(collection, id);
121
+ if (seen.has(key)) continue;
122
+ seen.add(key);
123
+ const oldPath = nextIndex.get(key);
124
+ const doc = target[collection].get(id);
125
+ if (!doc) {
126
+ if (oldPath) {
127
+ changes.set(oldPath, { type: "delete", path: oldPath });
128
+ nextFiles.delete(oldPath);
129
+ nextIndex.delete(key);
130
+ }
131
+ if (collection === "config") delete nextSecrets[id];
132
+ continue;
133
+ }
134
+ let stored = doc;
135
+ if (collection === "config") {
136
+ const split = stripDocSecrets(doc);
137
+ stored = split.doc;
138
+ if (Object.keys(split.secrets).length) nextSecrets[id] = split.secrets;
139
+ else delete nextSecrets[id];
140
+ }
141
+ const path = docPath(collection, stored);
142
+ const content = serializeDoc(stored);
143
+ if (oldPath && oldPath !== path) {
144
+ changes.set(oldPath, { type: "delete", path: oldPath });
145
+ nextFiles.delete(oldPath);
146
+ }
147
+ if (nextFiles.get(path) !== content) changes.set(path, { type: "put", path, content });
148
+ nextFiles.set(path, content);
149
+ nextIndex.set(key, path);
150
+ }
151
+ return { changes: [...changes.values()], files: nextFiles, index: nextIndex, secrets: nextSecrets };
152
+ }
153
+ function markerContent() {
154
+ return JSON.stringify({ format: GIT_JSON_FORMAT, version: GIT_JSON_VERSION }, null, 2) + "\n";
155
+ }
156
+ function isCollectionFolder(name) {
157
+ return COLLECTIONS.includes(name);
158
+ }
159
+ const GIT_JSON_POLL_MS = 3e4;
160
+ const GIT_JSON_LOCAL_POLL_MS = 1e4;
161
+ const MAX_REBASES = 3;
162
+ function isGitJsonStore(store) {
163
+ return store.kind === "git-json" && typeof store.resetSession === "function";
164
+ }
165
+ function roleFromPermissions(p) {
166
+ if (p.isAdmin) return "admin";
167
+ if (p.canWrite) return "member";
168
+ return "viewer";
169
+ }
170
+ function defaultGitIdentity(p, t, email) {
171
+ const noreply = t.kind === "github" ? `${p.login}@users.noreply.github.com` : t.kind === "gitlab" ? `${p.login}@users.noreply.gitlab.com` : "local@localhost";
172
+ return {
173
+ uid: t.kind === "local" ? "local" : `${t.kind}:${p.login}`,
174
+ email: email?.trim().toLowerCase() || noreply,
175
+ displayName: p.login,
176
+ role: t.kind === "local" ? "admin" : roleFromPermissions(p)
177
+ };
178
+ }
179
+ const GIT_FILES_DIR = "files";
180
+ const GIT_LARGE_FILE_WARN_BYTES = 5 * 1024 * 1024;
181
+ function keyOf(m) {
182
+ return [m.collection, m.op === "set" ? m.doc.id : m.id];
183
+ }
184
+ const same = (a, b) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
185
+ function checkRebase(mutations, before, remote, message = "Someone else changed this document — reload to see their version.") {
186
+ for (const m of mutations) {
187
+ const [collection, id] = keyOf(m);
188
+ const base = before[collection].get(id);
189
+ const theirs = remote[collection].get(id);
190
+ if (same(base, theirs)) continue;
191
+ if (m.op === "patch") {
192
+ if (!theirs) throw new DocConflictError(message, collection, id);
193
+ for (const [path, value] of Object.entries(m.fields)) {
194
+ const p = splitPath(path);
195
+ const b = getPath(base, p);
196
+ const r = getPath(theirs, p);
197
+ if (!same(b, r) && !same(r, value)) throw new DocConflictError(message, collection, id);
198
+ for (let i = 1; i < p.length; i++) {
199
+ const pb = getPath(base, p.slice(0, i));
200
+ const pr = getPath(theirs, p.slice(0, i));
201
+ if (pb === void 0 !== (pr === void 0)) throw new DocConflictError(message, collection, id);
202
+ }
203
+ }
204
+ continue;
205
+ }
206
+ if (m.op === "set" && same(theirs, m.doc)) continue;
207
+ if (m.op === "delete" && !theirs) continue;
208
+ throw new DocConflictError(message, collection, id);
209
+ }
210
+ }
211
+ function createGitJsonStoreWith(opts) {
212
+ const dir = opts.dir.replace(/^\/+|\/+$/g, "");
213
+ const rel = (path) => dir ? path.slice(dir.length + 1) : path;
214
+ const inDir = (path) => !dir || path.startsWith(dir + "/");
215
+ const suffix = opts.messageSuffix ?? " — AgentCRM [skip ci]";
216
+ const secrets = opts.secrets ?? { read: () => ({}), write: () => void 0 };
217
+ let transport = null;
218
+ let identityPromise = null;
219
+ let state = null;
220
+ let stale = false;
221
+ async function openTransport() {
222
+ if (!transport) transport = await opts.openTransport();
223
+ return transport;
224
+ }
225
+ async function requireTransport() {
226
+ const t = await openTransport();
227
+ if (!t) throw new Error("Not connected to the data repository on this browser (missing token or folder access).");
228
+ return t;
229
+ }
230
+ async function loadRemote() {
231
+ const t = await requireTransport();
232
+ const tree = await t.listTree();
233
+ const entries = tree.entries.filter((e) => inDir(e.path) && isLayoutFile(rel(e.path)));
234
+ const texts = await t.readTexts(entries);
235
+ const files2 = /* @__PURE__ */ new Map();
236
+ for (const [path, text] of texts) files2.set(rel(path), text);
237
+ const { snapshot, index, errors } = filesToSnapshot(files2, secrets.read());
238
+ if (errors.length) console.warn("[git-json] unreadable files:", errors);
239
+ const blobs = /* @__PURE__ */ new Map();
240
+ for (const e of tree.entries) {
241
+ if (!inDir(e.path)) continue;
242
+ const r = rel(e.path);
243
+ if (r.startsWith(`${GIT_FILES_DIR}/`) && r.endsWith(".bin")) blobs.set(r, e);
244
+ }
245
+ return { head: tree.headSha, blobs, files: files2, index, snapshot, errors };
246
+ }
247
+ async function persist(mutations, before, _after, message) {
248
+ const t = await requireTransport();
249
+ if (!state) state = await loadRemote();
250
+ for (let attempt = 0; ; attempt++) {
251
+ const base = state;
252
+ checkRebase(mutations, before, base.snapshot);
253
+ const { next } = applyMutations(base.snapshot, mutations);
254
+ const plan = planChanges(mutations.map(keyOf), next, base.files, base.index, secrets.read());
255
+ if (!base.files.has(MARKER_FILE)) {
256
+ plan.changes.push({ type: "put", path: MARKER_FILE, content: markerContent() });
257
+ plan.files.set(MARKER_FILE, markerContent());
258
+ }
259
+ if (plan.changes.length === 0) return;
260
+ const changes = plan.changes.map(
261
+ (c) => c.type === "put" ? { type: "put", path: joinRepoPath(dir, c.path), content: c.content ?? "" } : { type: "delete", path: joinRepoPath(dir, c.path) }
262
+ );
263
+ try {
264
+ const head = await t.commit(changes, { message: `${message}${suffix}`, expectedHeadSha: base.head });
265
+ secrets.write(plan.secrets);
266
+ state = { head, blobs: base.blobs, files: plan.files, index: plan.index, snapshot: next, errors: base.errors };
267
+ if (base.snapshot !== before && attempt > 0) stale = true;
268
+ return;
269
+ } catch (err) {
270
+ if (!(err instanceof GitConflictError) || attempt >= MAX_REBASES) throw err;
271
+ }
272
+ state = await loadRemote();
273
+ stale = true;
274
+ }
275
+ }
276
+ const core = createStoreCore({
277
+ kind: "git-json",
278
+ pollIntervalMs: opts.pollIntervalMs ?? GIT_JSON_POLL_MS,
279
+ async load() {
280
+ state = await loadRemote();
281
+ stale = false;
282
+ return state.snapshot;
283
+ },
284
+ persist,
285
+ async poll() {
286
+ const t = await openTransport();
287
+ if (!t || !state) return null;
288
+ const head = await t.headSha();
289
+ if (head === state.head && !stale) return null;
290
+ state = await loadRemote();
291
+ stale = false;
292
+ return state.snapshot;
293
+ },
294
+ identity() {
295
+ if (!identityPromise) {
296
+ identityPromise = (async () => {
297
+ const t = await openTransport();
298
+ if (!t) return null;
299
+ try {
300
+ const p = await t.permissions();
301
+ return opts.identityFor ? opts.identityFor(p, t) : defaultGitIdentity(p, t);
302
+ } catch (err) {
303
+ console.warn("[git-json] permission check failed:", err);
304
+ return null;
305
+ }
306
+ })();
307
+ void identityPromise.then((id) => {
308
+ if (!id) {
309
+ identityPromise = null;
310
+ transport = null;
311
+ }
312
+ });
313
+ }
314
+ return identityPromise;
315
+ }
316
+ });
317
+ const blobPath = (id) => `${GIT_FILES_DIR}/${id}.bin`;
318
+ async function commitBlobs(changes, message) {
319
+ const t = await requireTransport();
320
+ const full = changes.map((c) => ({ ...c, path: joinRepoPath(dir, c.path) }));
321
+ const msg = `${message}${suffix}`;
322
+ if (state) {
323
+ try {
324
+ const head = await t.commit(full, { message: msg, expectedHeadSha: state.head });
325
+ const blobs = new Map(state.blobs);
326
+ for (const c of changes) {
327
+ if (c.type === "put") blobs.set(c.path, { path: joinRepoPath(dir, c.path), sha: "" });
328
+ else blobs.delete(c.path);
329
+ }
330
+ state = { ...state, head, blobs };
331
+ return;
332
+ } catch (err) {
333
+ if (!(err instanceof GitConflictError)) throw err;
334
+ }
335
+ }
336
+ await t.commit(full, { message: msg });
337
+ stale = true;
338
+ }
339
+ async function findBlob(id) {
340
+ const p = blobPath(id);
341
+ let e = state?.blobs.get(p);
342
+ if (!e || !e.sha) {
343
+ const t = await requireTransport();
344
+ const tree = await t.listTree();
345
+ const full = joinRepoPath(dir, p);
346
+ e = tree.entries.find((x) => x.path === full);
347
+ if (e && state) state.blobs.set(p, e);
348
+ }
349
+ return e ?? null;
350
+ }
351
+ const files = {
352
+ kind: "git-json",
353
+ async put(id, bytes) {
354
+ await files.putMany([{ id, bytes }]);
355
+ },
356
+ async putMany(items) {
357
+ if (!items.length) return;
358
+ for (const it of items) assertFileId(it.id);
359
+ const total = items.reduce((n, it) => n + it.bytes.length, 0);
360
+ if (total > GIT_LARGE_FILE_WARN_BYTES) console.warn(`[git-json] committing ${Math.round(total / 1024)} KB of files: git history keeps them forever and large repos get slow.`);
361
+ await commitBlobs(
362
+ items.map((it) => ({ type: "put", path: blobPath(it.id), content: bytesToBase64(it.bytes), encoding: "base64" })),
363
+ `Upload ${items.length === 1 ? items[0].meta?.name ?? "a file" : `${items.length} files`}`
364
+ );
365
+ },
366
+ async get(id) {
367
+ assertFileId(id);
368
+ const entry = await findBlob(id);
369
+ if (!entry) return null;
370
+ return (await requireTransport()).readBytes(entry);
371
+ },
372
+ async delete(id) {
373
+ assertFileId(id);
374
+ if (!await findBlob(id)) return;
375
+ await commitBlobs([{ type: "delete", path: blobPath(id) }], "Purge a file");
376
+ },
377
+ async list() {
378
+ const t = await requireTransport();
379
+ const tree = await t.listTree();
380
+ const prefix = joinRepoPath(dir, `${GIT_FILES_DIR}/`);
381
+ return tree.entries.filter((e) => e.path.startsWith(prefix) && e.path.endsWith(".bin")).map((e) => e.path.slice(prefix.length, -".bin".length)).filter((id) => !id.includes("/")).sort();
382
+ }
383
+ };
384
+ withFileStore(core, files);
385
+ return Object.assign(core, {
386
+ resetSession() {
387
+ transport = null;
388
+ identityPromise = null;
389
+ state = null;
390
+ stale = false;
391
+ },
392
+ loadErrors: () => state?.errors ?? [],
393
+ headSha: () => state?.head ?? null
394
+ });
395
+ }
396
+ async function inspectGitJsonFolder(t, dir) {
397
+ const d = dir.replace(/^\/+|\/+$/g, "");
398
+ const tree = await t.listTree();
399
+ const relPaths = tree.entries.map((e) => d ? e.path.startsWith(d + "/") ? e.path.slice(d.length + 1) : null : e.path).filter((p) => p !== null);
400
+ const docs = relPaths.filter((p) => p !== MARKER_FILE && isLayoutFile(p));
401
+ const exists = relPaths.includes(MARKER_FILE) || docs.some((p) => p.startsWith("objects/"));
402
+ let name;
403
+ const settingsEntry = tree.entries.find((e) => e.path === joinRepoPath(d, "settings/general.json"));
404
+ if (settingsEntry) {
405
+ const text = (await t.readTexts([settingsEntry])).get(settingsEntry.path);
406
+ try {
407
+ name = text ? JSON.parse(text).name : void 0;
408
+ } catch {
409
+ }
410
+ }
411
+ return { exists, documents: docs.length, hasUsers: docs.some((p) => p.startsWith("users/")), name };
412
+ }
413
+ function createMemoryRepo(files = {}) {
414
+ return { head: "c0", files: new Map(Object.entries(files)), commits: [] };
415
+ }
416
+ function shaOf(text) {
417
+ let h = 2166136261;
418
+ for (let i = 0; i < text.length; i++) {
419
+ h ^= text.charCodeAt(i);
420
+ h = Math.imul(h, 16777619);
421
+ }
422
+ return `b${(h >>> 0).toString(16)}-${text.length}`;
423
+ }
424
+ function createMemoryGitTransport(repo, opts = {}) {
425
+ const t = {
426
+ kind: opts.kind ?? "github",
427
+ label: "memory/repo@main",
428
+ reads: 0,
429
+ async headSha() {
430
+ return repo.head;
431
+ },
432
+ async listTree() {
433
+ const entries = [...repo.files.entries()].map(([path, text]) => ({ path, sha: shaOf(text), size: text.length })).sort((a, b) => a.path.localeCompare(b.path));
434
+ return { headSha: repo.head, entries };
435
+ },
436
+ async readTexts(entries) {
437
+ t.reads++;
438
+ const out = /* @__PURE__ */ new Map();
439
+ for (const e of entries) {
440
+ const text = repo.files.get(e.path);
441
+ if (text !== void 0) out.set(e.path, text);
442
+ }
443
+ return out;
444
+ },
445
+ async readBytes(entry) {
446
+ return new TextEncoder().encode(repo.files.get(entry.path) ?? "");
447
+ },
448
+ async commit(changes, o) {
449
+ opts.beforeCommit?.(repo);
450
+ if (o.expectedHeadSha && o.expectedHeadSha !== repo.head) throw new GitConflictError("Branch moved", repo.head);
451
+ for (const c of changes) {
452
+ if (c.type === "delete") repo.files.delete(c.path);
453
+ else repo.files.set(c.path, c.encoding === "base64" ? base64ToUtf8(c.content) : c.content);
454
+ }
455
+ const sha = `c${repo.commits.length + 1}`;
456
+ repo.commits.push({ sha, message: o.message, changes });
457
+ repo.head = sha;
458
+ return sha;
459
+ },
460
+ async permissions() {
461
+ return { login: "octocat", canWrite: true, isAdmin: true, visibility: "private", ...opts.permissions };
462
+ }
463
+ };
464
+ return t;
465
+ }
466
+ const LOCAL_GIT_HANDLE_KEY = "git-json-folder";
467
+ async function openGitJsonTransport(git, token) {
468
+ if (git.provider === "local") {
469
+ const handle = await loadFolderHandle(LOCAL_GIT_HANDLE_KEY);
470
+ if (!handle || !await ensureFolderPermission(handle)) return null;
471
+ return createLocalFsTransport(handle);
472
+ }
473
+ const tok = token ?? getGitToken(git);
474
+ if (!tok) return null;
475
+ return git.provider === "github" ? createRemoteGitTransport({ provider: "github", owner: git.owner ?? "", repo: git.repo ?? "", branch: git.branch, token: tok, apiBaseUrl: git.apiBaseUrl }) : createRemoteGitTransport({ provider: "gitlab", project: git.project ?? "", branch: git.branch, token: tok, apiBaseUrl: git.apiBaseUrl });
476
+ }
477
+ function getGitUserEmail(config) {
478
+ return getSecret(`gitEmail:${datasetId(config)}`);
479
+ }
480
+ function setGitUserEmail(config, email) {
481
+ setSecret(`gitEmail:${datasetId(config)}`, email?.trim().toLowerCase() || null);
482
+ }
483
+ function createGitJsonStore(config, opts = {}) {
484
+ const cfg = config.git;
485
+ const explicitToken = typeof opts.secrets?.token === "string" ? opts.secrets.token : null;
486
+ const explicitEmail = typeof opts.secrets?.email === "string" ? opts.secrets.email : null;
487
+ const bagKey = `gitSecrets:${datasetId(config)}`;
488
+ return createGitJsonStoreWith({
489
+ dir: cfg.dir,
490
+ openTransport: () => openGitJsonTransport(cfg, explicitToken),
491
+ identityFor: (p, t) => defaultGitIdentity(p, t, explicitEmail ?? getGitUserEmail(config)),
492
+ secrets: {
493
+ read: () => getSecretJson(bagKey) ?? {},
494
+ write: (bag) => setSecretJson(bagKey, bag)
495
+ },
496
+ pollIntervalMs: opts.pollIntervalMs ?? (cfg.provider === "local" ? GIT_JSON_LOCAL_POLL_MS : GIT_JSON_POLL_MS)
497
+ });
498
+ }
499
+ export {
500
+ GIT_JSON_FORMAT,
501
+ GIT_JSON_VERSION,
502
+ LOCAL_GIT_HANDLE_KEY,
503
+ MARKER_FILE,
504
+ checkRebase,
505
+ createGitJsonStore,
506
+ createGitJsonStoreWith,
507
+ createMemoryGitTransport,
508
+ createMemoryRepo,
509
+ docKey,
510
+ docPath,
511
+ filesToSnapshot,
512
+ getGitToken,
513
+ getGitUserEmail,
514
+ inspectGitJsonFolder,
515
+ isCollectionFolder,
516
+ isGitJsonStore,
517
+ isLayoutFile,
518
+ markerContent,
519
+ mergeDocSecrets,
520
+ openGitJsonTransport,
521
+ parseDocPath,
522
+ planChanges,
523
+ roleFromPermissions,
524
+ serializeDoc,
525
+ setGitToken,
526
+ setGitUserEmail,
527
+ stripDocSecrets
528
+ };
@@ -0,0 +1,144 @@
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 { statSync, rmSync, existsSync, unlinkSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from "node:fs";
8
+ import { resolve, dirname, sep, join, relative } from "node:path";
9
+ import { buildConfigJsSource } from "./runtimeConfig-CCOFg1RM.mjs";
10
+ import { a as createGitPagesConnector, b as createS3Connector } from "./s3-Dz50-NuA.mjs";
11
+ import { C as CliError, r as readVersionInfo, i as distRoot } from "../agentcrm.mjs";
12
+ import { pathToFileURL } from "node:url";
13
+ import "./index-BM8qeGOb.mjs";
14
+ import "./http-Bk9ICb7I.mjs";
15
+ import "./encoding-C9MzdP95.mjs";
16
+ import "node:util";
17
+ import "node:crypto";
18
+ import "node:os";
19
+ import "node:child_process";
20
+ import "node:http";
21
+ function createFsConnector(settings) {
22
+ const raw = typeof settings.path === "string" ? settings.path.trim() : "";
23
+ if (!raw) throw new Error("local-folder: settings.path (absolute folder) is required.");
24
+ const root = resolve(raw);
25
+ const abs = (p) => {
26
+ const file = resolve(root, p.replace(/^\/+/, ""));
27
+ if (file !== root && !file.startsWith(root + sep)) throw new Error(`Path escapes the folder: ${p}`);
28
+ return file;
29
+ };
30
+ const walk = (dir) => existsSync(dir) ? readdirSync(dir).flatMap((name) => {
31
+ const p = join(dir, name);
32
+ return statSync(p).isDirectory() ? walk(p) : [p];
33
+ }) : [];
34
+ const rel = (file) => relative(root, file).split(sep).join("/");
35
+ const connector = {
36
+ id: settings.id ?? "local-folder",
37
+ model: "files",
38
+ capabilities: { list: true, rename: false, renameFolder: false, folders: true, deletePrefix: true },
39
+ async put(input) {
40
+ const file = abs(input.path);
41
+ mkdirSync(dirname(file), { recursive: true });
42
+ writeFileSync(file, input.encoding === "base64" ? Buffer.from(input.content, "base64") : input.content);
43
+ },
44
+ async get(path) {
45
+ return readFileSync(abs(path), "utf8");
46
+ },
47
+ async getBytes(path) {
48
+ const file = abs(path);
49
+ return existsSync(file) ? new Uint8Array(readFileSync(file)) : null;
50
+ },
51
+ async delete(path) {
52
+ const file = abs(path);
53
+ if (existsSync(file)) unlinkSync(file);
54
+ },
55
+ async deletePrefix(path) {
56
+ rmSync(abs(path), { recursive: true, force: true });
57
+ },
58
+ async list(page, perPage) {
59
+ const all = walk(root).map((f) => ({ path: rel(f), type: "file", size: statSync(f).size })).sort((a, b) => a.path.localeCompare(b.path));
60
+ return { items: all.slice((page - 1) * perPage, page * perPage), page, per_page: perPage, total: all.length };
61
+ },
62
+ async listPrefix(prefix) {
63
+ const p = prefix.replace(/^\/+/, "");
64
+ return walk(root).map(rel).filter((f) => f.startsWith(p)).sort();
65
+ },
66
+ async publicUrl(path) {
67
+ return pathToFileURL(abs(path)).href;
68
+ },
69
+ async commitBatch(ops) {
70
+ for (const op of ops) {
71
+ if (op.type === "put") await connector.put(op);
72
+ else if (op.type === "delete") await connector.delete(op.path);
73
+ else await connector.deletePrefix(op.path);
74
+ }
75
+ }
76
+ };
77
+ return connector;
78
+ }
79
+ function buildConnector(target) {
80
+ switch (target.connectorId) {
81
+ case "s3":
82
+ return createS3Connector(target.settings);
83
+ case "git-pages":
84
+ return createGitPagesConnector({ token: "", ...target.settings });
85
+ case "local-folder":
86
+ return createFsConnector({ path: target.settings.path });
87
+ default:
88
+ throw new CliError("unsupported-storage", `The "${target.connectorId}" storage can't be used from the CLI.`, "Use an S3 bucket, a GitHub / GitLab repo or a local folder.");
89
+ }
90
+ }
91
+ function adminDir() {
92
+ return join(distRoot(), "admin");
93
+ }
94
+ function adminFiles() {
95
+ const root = adminDir();
96
+ if (!existsSync(join(root, "index.html"))) {
97
+ throw new CliError("admin-missing", `No admin build at ${root}.`, "Use the npm package (`npx -y @fleox-agent-crm/agentcrm …`), or run `npm run build` in a clone.");
98
+ }
99
+ const manifest = join(root, "admin-files.json");
100
+ if (existsSync(manifest)) {
101
+ try {
102
+ const files = JSON.parse(readFileSync(manifest, "utf8")).files;
103
+ if (Array.isArray(files) && files.every((f) => typeof f === "string")) {
104
+ return [.../* @__PURE__ */ new Set([...files.filter((f) => f !== "config.js" && f !== "version.json" && existsSync(join(root, f))), "admin-files.json"])];
105
+ }
106
+ } catch {
107
+ }
108
+ }
109
+ const walk = (dir) => readdirSync(dir).flatMap((name) => {
110
+ const abs = join(dir, name);
111
+ return statSync(abs).isDirectory() ? walk(abs) : [abs];
112
+ });
113
+ return walk(root).map((abs) => relative(root, abs).split(sep).join("/")).filter((f) => f !== "config.js" && f !== "version.json" && !f.endsWith(".DS_Store")).sort();
114
+ }
115
+ const putBytes = (path, bytes) => ({ type: "put", path, content: Buffer.from(bytes).toString("base64"), encoding: "base64" });
116
+ function adminOps(folder, runtime) {
117
+ const root = adminDir();
118
+ const files = adminFiles();
119
+ const ordered = [...files.filter((f) => f !== "index.html"), "index.html"];
120
+ const info = readVersionInfo();
121
+ const prefix = folder ? `${folder.replace(/^\/+|\/+$/g, "")}/` : "";
122
+ const ops = ordered.map((f) => putBytes(`${prefix}${f}`, readFileSync(join(root, f))));
123
+ ops.splice(ops.length - 1, 0, { type: "put", path: `${prefix}version.json`, content: JSON.stringify(info, null, 2) + "\n" });
124
+ ops.splice(ops.length - 1, 0, { type: "put", path: `${prefix}config.js`, content: buildConfigJsSource(runtime) });
125
+ return { ops, version: info.version };
126
+ }
127
+ async function applyOps(connector, ops) {
128
+ if (connector.commitBatch) {
129
+ await connector.commitBatch(ops);
130
+ return;
131
+ }
132
+ for (const op of ops) {
133
+ if (op.type === "put") await connector.put(op);
134
+ else if (op.type === "delete") await connector.delete(op.path);
135
+ else if (connector.deletePrefix) await connector.deletePrefix(op.path);
136
+ }
137
+ }
138
+ export {
139
+ adminDir,
140
+ adminFiles,
141
+ adminOps,
142
+ applyOps,
143
+ buildConnector
144
+ };