@fleox-agent-crm/agentcrm 0.4.0 → 0.6.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 (50) hide show
  1. package/README.md +16 -16
  2. package/changelog.json +18 -0
  3. package/dist/admin/admin-files.json +1 -1
  4. package/dist/agent/agentcrm.mjs +188 -76
  5. package/dist/agent/chunks/{activities-Cj_SIOay.mjs → activities-be260oPg.mjs} +1 -1
  6. package/dist/agent/chunks/{automations-DwWruxmu.mjs → automations-gCDYNS88.mjs} +5 -5
  7. package/dist/agent/chunks/{backup-W3CjgkAJ.mjs → backup-DDqTRbuW.mjs} +4 -4
  8. package/dist/agent/chunks/{compact-BGHI92tm.mjs → compact-C_WX0_F5.mjs} +5 -5
  9. package/dist/agent/chunks/{dedupe-BepmsaHv.mjs → dedupe-isAmKHQV.mjs} +1 -1
  10. package/dist/agent/chunks/{exportCsv-C6e6XEmB.mjs → exportCsv-9ZMyqTdH.mjs} +3 -3
  11. package/dist/agent/chunks/{extCheck-GgHG1dYB.mjs → extCheck-BKh_9aLA.mjs} +4 -4
  12. package/dist/agent/chunks/{files-D1daDgY0.mjs → files-Be7DZd1a.mjs} +6 -6
  13. package/dist/agent/chunks/{forms-BfuWaQjA.mjs → forms-Bb0zztBM.mjs} +4 -4
  14. package/dist/agent/chunks/{importCsv-LVmMTaWU.mjs → importCsv-BuSYG4ja.mjs} +3 -3
  15. package/dist/agent/chunks/{install-BLNflFes.mjs → install-D1lgtRIH.mjs} +1 -1
  16. package/dist/agent/chunks/{loader-MPD9iAvh.mjs → loader-Bnu3GnMW.mjs} +14 -14
  17. package/dist/agent/chunks/{migrate-CsClnnN8.mjs → migrate-Cc_8MHZv.mjs} +1 -1
  18. package/dist/agent/chunks/{pipelines-BAKpPk1w.mjs → pipelines-G-c23x-s.mjs} +4 -4
  19. package/dist/agent/chunks/{records-DcHFelq5.mjs → records-BJLSmv0O.mjs} +2 -2
  20. package/dist/agent/chunks/{registry-COfJrSmg.mjs → registry-BpTyK6yU.mjs} +13 -13
  21. package/dist/agent/chunks/{relations-B9TynBmA.mjs → relations-bmUKPT0G.mjs} +4 -4
  22. package/dist/agent/chunks/{reports-BACHGccj.mjs → reports-ogUa41jr.mjs} +5 -5
  23. package/dist/agent/chunks/{runtime-C0Xf0Axi.mjs → runtime-D-hzk6s9.mjs} +20 -20
  24. package/dist/agent/chunks/{schema-C4lSslXv.mjs → schema-CYm_n5BN.mjs} +4 -4
  25. package/dist/agent/chunks/schemaApply-j5hbwPrH.mjs +169 -0
  26. package/dist/agent/chunks/{seed-6MRmQQBR.mjs → seed-Bb26Zsni.mjs} +2 -2
  27. package/dist/agent/chunks/{server-DC3t8r30.mjs → server-BAQwVNtC.mjs} +3 -3
  28. package/dist/agent/chunks/{standardObjects-CheqfDqa.mjs → standardObjects-vrmqIVLH.mjs} +1 -1
  29. package/dist/agent/chunks/{stats-BfuLWhVV.mjs → stats-bInarNLK.mjs} +4 -4
  30. package/dist/agent/chunks/syncRecords-69hcCgOK.mjs +102 -0
  31. package/dist/agent/chunks/{tools-iSH-yMpl.mjs → tools-BHmVIyP_.mjs} +12 -12
  32. package/dist/agent/chunks/{views-DH1n9Lrt.mjs → views-zRdsphZl.mjs} +5 -5
  33. package/dist/version.json +1 -1
  34. package/package.json +1 -1
  35. package/plugin/.claude-plugin/plugin.json +1 -1
  36. package/plugin/skills/agentcrm-admin/SKILL.md +67 -67
  37. package/plugin/skills/agentcrm-admin/references/records.md +5 -5
  38. package/plugin/skills/agentcrm-admin/references/schema.md +19 -19
  39. package/plugin/skills/agentcrm-extensions/SKILL.md +8 -8
  40. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +2 -2
  41. package/plugin/skills/agentcrm-extensions/references/testing.md +14 -14
  42. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +3 -3
  43. package/plugin/skills/agentcrm-install/SKILL.md +13 -13
  44. package/plugin/skills/agentcrm-install/references/firebase.md +1 -1
  45. package/plugin/skills/agentcrm-install/references/github.md +1 -1
  46. package/plugin/skills/agentcrm-install/references/mcp.md +2 -2
  47. package/plugin/skills/agentcrm-install/references/plans.md +1 -1
  48. package/plugin/skills/agentcrm-install/references/s3.md +1 -1
  49. package/plugin/skills/agentcrm-manage/SKILL.md +15 -15
  50. package/plugin/skills/agentcrm-sales/SKILL.md +18 -18
@@ -0,0 +1,169 @@
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 { OpsError, assertCan } from "./context-CxFfqS4D.mjs";
8
+ import { listPipelines, createPipeline, updatePipeline } from "./pipelines-G-c23x-s.mjs";
9
+ import { listObjects, createObject, updateObject, createField, updateField } from "./schema-CYm_n5BN.mjs";
10
+ import { listViews, createView, updateView } from "./views-zRdsphZl.mjs";
11
+ import "./pluginRegistry-DyHtlkcr.mjs";
12
+ import "./fields-B0bitgmr.mjs";
13
+ import "./ids-CvnmrMJX.mjs";
14
+ import "./standardObjects-vrmqIVLH.mjs";
15
+ import "./records-BJLSmv0O.mjs";
16
+ import "./query-C5nJB_3C.mjs";
17
+ import "./relations-bmUKPT0G.mjs";
18
+ import "./store-5gqRSKXg.mjs";
19
+ import "./utils-DmI8FtXP.mjs";
20
+ import "../agentcrm.mjs";
21
+ import "node:util";
22
+ import "node:fs";
23
+ import "node:path";
24
+ import "node:url";
25
+ import "node:crypto";
26
+ import "node:os";
27
+ import "node:child_process";
28
+ import "node:http";
29
+ function stable(v) {
30
+ if (Array.isArray(v)) return v.map(stable);
31
+ if (v && typeof v === "object") return Object.fromEntries(Object.keys(v).sort().map((k) => [k, stable(v[k])]));
32
+ return v ?? null;
33
+ }
34
+ const same = (a, b) => JSON.stringify(stable(a)) === JSON.stringify(stable(b));
35
+ function parseSchemaSpec(raw) {
36
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new OpsError("validation", "A schema is a JSON object with objects, pipelines and / or views.");
37
+ const spec = raw;
38
+ for (const o of spec.objects ?? []) {
39
+ if (!o.id || !o.labelSingular || !o.labelPlural) throw new OpsError("validation", `Every object needs id, labelSingular and labelPlural (got ${JSON.stringify(o.id ?? o)}).`);
40
+ for (const f of o.fields ?? []) if (!f.id || !f.label || !f.type) throw new OpsError("validation", `Every field of ${o.id} needs id, label and type.`);
41
+ }
42
+ for (const p of spec.pipelines ?? []) {
43
+ if (!p.id || !p.name || !p.object || !p.stages?.length) throw new OpsError("validation", `Every pipeline needs id, name, object and stages (got ${JSON.stringify(p.id ?? p)}).`);
44
+ for (const s of p.stages) if (!s.id || !s.label) throw new OpsError("validation", `Every stage of pipeline ${p.id} needs id and label.`);
45
+ }
46
+ for (const v of spec.views ?? []) if (!v.object || !v.name) throw new OpsError("validation", "Every view needs object and name.");
47
+ return spec;
48
+ }
49
+ function missingOptions(field, wanted) {
50
+ const have = new Set((field.options ?? []).map((o) => o.label.toLowerCase()));
51
+ return (wanted ?? []).map((o) => typeof o === "string" ? o : o.label ?? "").filter((l) => l && !have.has(l.toLowerCase()));
52
+ }
53
+ function fieldPatch(field, spec) {
54
+ const patch = {};
55
+ const changes = [];
56
+ if (spec.label !== field.label) patch.label = spec.label, changes.push("label");
57
+ if (spec.description !== void 0 && spec.description !== field.description) patch.description = spec.description, changes.push("description");
58
+ for (const k of ["required", "unique", "hidden"]) {
59
+ if (spec[k] !== void 0 && !!spec[k] !== !!field[k]) patch[k] = spec[k], changes.push(k);
60
+ }
61
+ if (spec.currency !== void 0 && spec.currency !== field.currency) patch.currency = spec.currency, changes.push("currency");
62
+ const add = missingOptions(field, spec.options);
63
+ if (add.length) {
64
+ patch.options = [...field.options ?? [], ...add];
65
+ changes.push(`options +${add.join(", +")}`);
66
+ }
67
+ return { patch, changes };
68
+ }
69
+ async function applySchema(ctx, spec) {
70
+ assertCan(ctx, "schema.write");
71
+ const report = { changes: [], conflicts: [], extraFields: [] };
72
+ for (const o of spec.objects ?? []) {
73
+ let current = listObjects(ctx).find((x) => x.id === o.id);
74
+ const fields = o.fields ?? [];
75
+ if (!current) {
76
+ const plain = fields.filter((f) => f.type !== "relation" && f.id !== "name");
77
+ current = await createObject(ctx, { id: o.id, labelSingular: o.labelSingular, labelPlural: o.labelPlural, icon: o.icon, color: o.color, fields: plain });
78
+ report.changes.push({ kind: "object", action: "create", target: o.id, changes: plain.map((f) => f.id) });
79
+ } else {
80
+ const patch = {};
81
+ const changes = [];
82
+ if (o.labelSingular !== current.labelSingular) patch.labelSingular = o.labelSingular, changes.push("labelSingular");
83
+ if (o.labelPlural !== current.labelPlural) patch.labelPlural = o.labelPlural, changes.push("labelPlural");
84
+ if (o.icon && o.icon !== current.icon) patch.icon = o.icon, changes.push("icon");
85
+ if (o.color && o.color !== current.color) patch.color = o.color, changes.push("color");
86
+ if (changes.length) {
87
+ current = await updateObject(ctx, o.id, patch);
88
+ report.changes.push({ kind: "object", action: "update", target: o.id, changes });
89
+ }
90
+ }
91
+ }
92
+ for (const o of spec.objects ?? []) {
93
+ for (const f of o.fields ?? []) {
94
+ const current2 = listObjects(ctx).find((x) => x.id === o.id);
95
+ const field = current2.fields.find((x) => x.id === f.id);
96
+ if (!field) {
97
+ await createField(ctx, o.id, f);
98
+ report.changes.push({ kind: "field", action: "create", target: `${o.id}.${f.id}`, changes: [f.type] });
99
+ continue;
100
+ }
101
+ if (field.type !== f.type) {
102
+ report.conflicts.push(`${o.id}.${f.id}: type is ${field.type} in the CRM, ${f.type} in the schema (not changed)`);
103
+ continue;
104
+ }
105
+ const { patch, changes } = fieldPatch(field, f);
106
+ if (changes.length) {
107
+ await updateField(ctx, o.id, f.id, patch);
108
+ report.changes.push({ kind: "field", action: "update", target: `${o.id}.${f.id}`, changes });
109
+ }
110
+ }
111
+ const current = listObjects(ctx).find((x) => x.id === o.id);
112
+ if (o.labelField && o.labelField !== current.labelField) {
113
+ await updateObject(ctx, o.id, { labelField: o.labelField });
114
+ report.changes.push({ kind: "object", action: "update", target: o.id, changes: ["labelField"] });
115
+ }
116
+ const listed = new Set((o.fields ?? []).map((f) => f.id));
117
+ for (const f of current.fields) if (!f.standard && !f.system && !listed.has(f.id)) report.extraFields.push(`${o.id}.${f.id}`);
118
+ }
119
+ for (const p of spec.pipelines ?? []) {
120
+ const current = listPipelines(ctx).find((x) => x.id === p.id);
121
+ const stages = p.stages.map((s) => ({ ...s, kind: s.kind ?? "open" }));
122
+ const taken = new Map(listPipelines(ctx).filter((x) => x.id !== p.id).flatMap((x) => x.stages.map((s) => [s.id, x.id])));
123
+ const clash = stages.filter((s) => taken.has(s.id));
124
+ if (clash.length) {
125
+ report.conflicts.push(`pipeline ${p.id}: stage id(s) ${clash.map((s) => `${s.id} (used by ${taken.get(s.id)})`).join(", ")} — stage ids are unique across pipelines, rename them (not applied)`);
126
+ continue;
127
+ }
128
+ if (!current) {
129
+ await createPipeline(ctx, { id: p.id, name: p.name, object: p.object, stageField: p.stageField, stages });
130
+ report.changes.push({ kind: "pipeline", action: "create", target: p.id, changes: stages.map((s) => s.id) });
131
+ continue;
132
+ }
133
+ const changes = [];
134
+ if (p.name !== current.name) changes.push("name");
135
+ const now = current.stages.map((s) => ({ id: s.id, label: s.label, kind: s.kind, probability: s.probability }));
136
+ const want = stages.map((s, i) => ({ id: s.id, label: s.label, kind: s.kind, probability: s.probability ?? current.stages.find((c) => c.id === s.id)?.probability ?? now[i]?.probability }));
137
+ const kept = current.stages.filter((s) => !stages.some((w) => w.id === s.id));
138
+ if (!same(now.filter((s) => stages.some((w) => w.id === s.id)), want)) changes.push("stages");
139
+ if (changes.length) {
140
+ await updatePipeline(ctx, p.id, { name: p.name, stages: [...stages.map((s, i) => ({ ...s, probability: want[i].probability })), ...kept] });
141
+ report.changes.push({ kind: "pipeline", action: "update", target: p.id, changes });
142
+ }
143
+ }
144
+ for (const v of spec.views ?? []) {
145
+ const current = listViews(ctx, v.object).find((x) => x.name === v.name);
146
+ if (!current) {
147
+ await createView(ctx, { ...v });
148
+ report.changes.push({ kind: "view", action: "create", target: `${v.object}: ${v.name}` });
149
+ continue;
150
+ }
151
+ const changes = [];
152
+ const patch = {};
153
+ for (const k of ["type", "columns", "filters", "sorts", "groupBy", "pipelineId", "shared"]) {
154
+ if (v[k] !== void 0 && !same(v[k], current[k])) {
155
+ patch[k] = v[k];
156
+ changes.push(k);
157
+ }
158
+ }
159
+ if (changes.length) {
160
+ await updateView(ctx, current.id, patch);
161
+ report.changes.push({ kind: "view", action: "update", target: `${v.object}: ${v.name}`, changes });
162
+ }
163
+ }
164
+ return report;
165
+ }
166
+ export {
167
+ applySchema,
168
+ parseSchemaSpec
169
+ };
@@ -6,8 +6,8 @@ const __filename = __acrmPath(import.meta.url);
6
6
  const __dirname = __acrmDirname(__filename);
7
7
  import { normalizeEmail } from "./fields-B0bitgmr.mjs";
8
8
  import { a as seededId } from "./ids-CvnmrMJX.mjs";
9
- import { l as localizeObjects, S as SCHEMA_MIGRATIONS, a as localizePipeline, b as localizeView } from "./migrate-CsClnnN8.mjs";
10
- import { a as standardObjects, b as defaultPipeline, c as defaultViews } from "./standardObjects-CheqfDqa.mjs";
9
+ import { l as localizeObjects, S as SCHEMA_MIGRATIONS, a as localizePipeline, b as localizeView } from "./migrate-Cc_8MHZv.mjs";
10
+ import { a as standardObjects, b as defaultPipeline, c as defaultViews } from "./standardObjects-vrmqIVLH.mjs";
11
11
  import { assertCan } from "./context-CxFfqS4D.mjs";
12
12
  import { sha256Hex } from "./files-CjsPJSR6.mjs";
13
13
  import { asDoc, objectOf, usersOf, settingsDoc } from "./store-5gqRSKXg.mjs";
@@ -97,7 +97,7 @@ function createMcpServer(opts) {
97
97
  try {
98
98
  const run = async () => {
99
99
  const ctx = await opts.backend.context();
100
- const { callTool: call } = await import("./tools-iSH-yMpl.mjs");
100
+ const { callTool: call } = await import("./tools-BHmVIyP_.mjs");
101
101
  return call(ctx, name, args);
102
102
  };
103
103
  const result = tool.mutating ? await opts.backend.withWrite(run) : await run();
@@ -112,9 +112,9 @@ function createMcpServer(opts) {
112
112
  async function readResource(id, uri) {
113
113
  const ctx = await opts.backend.context();
114
114
  let value;
115
- if (uri === "agentcrm://schema") value = (await import("./schema-C4lSslXv.mjs")).describeSchema(ctx);
115
+ if (uri === "agentcrm://schema") value = (await import("./schema-CYm_n5BN.mjs")).describeSchema(ctx);
116
116
  else if (uri === "agentcrm://pipelines") {
117
- const { listPipelines, forecast } = await import("./pipelines-BAKpPk1w.mjs");
117
+ const { listPipelines, forecast } = await import("./pipelines-G-c23x-s.mjs");
118
118
  value = listPipelines(ctx).map((p) => ({ ...p, forecast: forecast(ctx, { pipelineId: p.id }) }));
119
119
  } else if (uri === "agentcrm://modules") {
120
120
  const [{ listModules }, { listExtensions }] = await Promise.all([import("./modules-clWdE-K2.mjs"), import("./extensions-Bp72uZ9a.mjs")]);
@@ -4,7 +4,7 @@ import { dirname as __acrmDirname } from "node:path";
4
4
  const require2 = __acrmRequire(import.meta.url);
5
5
  const __filename = __acrmPath(import.meta.url);
6
6
  const __dirname = __acrmDirname(__filename);
7
- import { normalizeDomain, colorAt } from "./fields-B0bitgmr.mjs";
7
+ import { colorAt, normalizeDomain } from "./fields-B0bitgmr.mjs";
8
8
  const DEFAULT_PIPELINE_ID = "sales";
9
9
  function options(labels) {
10
10
  return labels.map((l, i) => {
@@ -6,7 +6,7 @@ const __filename = __acrmPath(import.meta.url);
6
6
  const __dirname = __acrmDirname(__filename);
7
7
  import { A as ACTIVITY_TYPES } from "../agentcrm.mjs";
8
8
  import { assertCan } from "./context-CxFfqS4D.mjs";
9
- import { forecast } from "./pipelines-BAKpPk1w.mjs";
9
+ import { forecast } from "./pipelines-G-c23x-s.mjs";
10
10
  import { activitiesOf, pipelinesOf, usersOf, objectsOf, recordsOf } from "./store-5gqRSKXg.mjs";
11
11
  import "node:util";
12
12
  import "node:fs";
@@ -19,10 +19,10 @@ import "node:http";
19
19
  import "./pluginRegistry-DyHtlkcr.mjs";
20
20
  import "./fields-B0bitgmr.mjs";
21
21
  import "./ids-CvnmrMJX.mjs";
22
- import "./standardObjects-CheqfDqa.mjs";
23
- import "./records-DcHFelq5.mjs";
22
+ import "./standardObjects-vrmqIVLH.mjs";
23
+ import "./records-BJLSmv0O.mjs";
24
24
  import "./query-C5nJB_3C.mjs";
25
- import "./relations-B9TynBmA.mjs";
25
+ import "./relations-bmUKPT0G.mjs";
26
26
  function getStats(ctx) {
27
27
  assertCan(ctx, "reports.read");
28
28
  const now = ctx.now();
@@ -0,0 +1,102 @@
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 { recordLabel } from "./fields-B0bitgmr.mjs";
8
+ import { n as newId } from "./ids-CvnmrMJX.mjs";
9
+ import { assertCan, OpsError } from "./context-CxFfqS4D.mjs";
10
+ import { prepareRecordFields, recordPatchMutation } from "./records-BJLSmv0O.mjs";
11
+ import { requireObject, recordsOf, asDoc } from "./store-5gqRSKXg.mjs";
12
+ import "./pluginRegistry-DyHtlkcr.mjs";
13
+ import "./query-C5nJB_3C.mjs";
14
+ import "./standardObjects-vrmqIVLH.mjs";
15
+ import "./relations-bmUKPT0G.mjs";
16
+ const SAMPLE = 10;
17
+ function isRef(v) {
18
+ return !!v && typeof v === "object" && !Array.isArray(v) && "$key" in v;
19
+ }
20
+ async function syncRecords(ctx, spec) {
21
+ assertCan(ctx, "records.write");
22
+ const object = requireObject(ctx.store, spec.object);
23
+ const keyField = object.fields.find((f) => f.id === spec.key);
24
+ if (!keyField) throw new OpsError("validation", `${object.labelPlural} has no field "${spec.key}" (the key).`);
25
+ const archive = spec.archiveField ? object.fields.find((f) => f.id === spec.archiveField) : void 0;
26
+ if (spec.archiveField && archive?.type !== "boolean") throw new OpsError("validation", `--archive-field must be a boolean field of ${object.labelPlural}.`);
27
+ const refKey = spec.refKey ?? spec.key;
28
+ const report = { object: object.id, rows: 0, created: 0, updated: 0, unchanged: 0, archived: 0, unresolved: 0, errors: [], samples: { created: [], updated: [], archived: [] } };
29
+ const rows = /* @__PURE__ */ new Map();
30
+ for (const r of spec.rows) {
31
+ const key = String(r?.key ?? "").trim();
32
+ if (key) rows.set(key, r.fields ?? {});
33
+ }
34
+ report.rows = rows.size;
35
+ const byKey = (objectId, field) => {
36
+ const m = /* @__PURE__ */ new Map();
37
+ for (const r of recordsOf(ctx.store, objectId)) {
38
+ const v = r.fields[field];
39
+ if (v !== void 0 && v !== null && v !== "") m.set(String(v), r.id);
40
+ }
41
+ return m;
42
+ };
43
+ const existing = /* @__PURE__ */ new Map();
44
+ for (const r of recordsOf(ctx.store, object.id)) {
45
+ const v = r.fields[spec.key];
46
+ if (v !== void 0 && v !== null && v !== "") existing.set(String(v), r);
47
+ }
48
+ const refMaps = /* @__PURE__ */ new Map();
49
+ const resolve = (field, value) => {
50
+ const target = field.relation?.object;
51
+ if (!target) throw new OpsError("validation", `${field.label} is not a relation: { "$key": … } only fits relation fields.`);
52
+ if (!refMaps.has(target)) refMaps.set(target, byKey(target, refKey));
53
+ const map = refMaps.get(target);
54
+ const keys = (Array.isArray(value.$key) ? value.$key : [value.$key]).map(String);
55
+ const ids = keys.map((k) => map.get(k)).filter((x) => !!x);
56
+ report.unresolved += keys.length - ids.length;
57
+ return field.relation?.cardinality === "many" ? ids : ids[0] ?? null;
58
+ };
59
+ const mutations = [];
60
+ const now = ctx.now();
61
+ for (const [key, raw] of rows) {
62
+ try {
63
+ const input = { [spec.key]: key };
64
+ for (const [fieldId, v] of Object.entries(raw)) {
65
+ const field = object.fields.find((f) => f.id === fieldId);
66
+ input[fieldId] = field && isRef(v) ? resolve(field, v) : v;
67
+ }
68
+ if (archive) input[archive.id] = false;
69
+ const previous = existing.get(key) ?? null;
70
+ const prepared = prepareRecordFields(ctx, object, input, previous, { unknownFields: "error" });
71
+ if (previous) {
72
+ if (!prepared.changed.length) {
73
+ report.unchanged++;
74
+ continue;
75
+ }
76
+ mutations.push(recordPatchMutation(ctx, previous, prepared.fields, prepared.changed));
77
+ report.updated++;
78
+ if (report.samples.updated.length < SAMPLE) report.samples.updated.push({ key, fields: prepared.changed });
79
+ } else {
80
+ const record = { id: newId(now), object: object.id, fields: prepared.fields, createdAt: now, updatedAt: now };
81
+ mutations.push({ op: "set", collection: "records", doc: asDoc(record) });
82
+ report.created++;
83
+ if (report.samples.created.length < SAMPLE) report.samples.created.push(`${key} ${recordLabel(object, record)}`);
84
+ }
85
+ } catch (err) {
86
+ report.errors.push({ key, message: err.message });
87
+ }
88
+ }
89
+ if (archive) {
90
+ for (const [key, r] of existing) {
91
+ if (rows.has(key) || r.fields[archive.id] === true) continue;
92
+ mutations.push(recordPatchMutation(ctx, r, { [archive.id]: true }, [archive.id]));
93
+ report.archived++;
94
+ if (report.samples.archived.length < SAMPLE) report.samples.archived.push(`${key} ${recordLabel(object, r)}`);
95
+ }
96
+ }
97
+ if (mutations.length) await ctx.store.write(mutations, { message: `Sync ${object.labelPlural}: ${report.created} new, ${report.updated} updated, ${report.archived} archived` });
98
+ return report;
99
+ }
100
+ export {
101
+ syncRecords
102
+ };
@@ -8,23 +8,23 @@ import { recordLabel, normalizeEmail, formatFieldValue } from "./fields-B0bitgmr
8
8
  import { r as resolveField } from "./query-C5nJB_3C.mjs";
9
9
  import { F as FILTER_OPS, A as ACTIVITY_TYPES, a as FIELD_TYPES } from "../agentcrm.mjs";
10
10
  import { OpsError } from "./context-CxFfqS4D.mjs";
11
- import { listActivities, addNote, addActivity, createTask, completeTask, listTasks } from "./activities-Cj_SIOay.mjs";
12
- import { mergeRecords } from "./dedupe-BepmsaHv.mjs";
13
- import { importCsv } from "./importCsv-LVmMTaWU.mjs";
14
- import { moveStage, forecast, listPipelines } from "./pipelines-BAKpPk1w.mjs";
15
- import { getRecord, searchRecords, listRecords, getRecordDetails, createRecord, updateRecord, deleteRecord, queryContext } from "./records-DcHFelq5.mjs";
16
- import { runReport, REPORTS } from "./reports-BACHGccj.mjs";
17
- import { describeSchema, createField } from "./schema-C4lSslXv.mjs";
11
+ import { listActivities, addNote, addActivity, createTask, completeTask, listTasks } from "./activities-be260oPg.mjs";
12
+ import { mergeRecords } from "./dedupe-isAmKHQV.mjs";
13
+ import { importCsv } from "./importCsv-BuSYG4ja.mjs";
14
+ import { moveStage, forecast, listPipelines } from "./pipelines-G-c23x-s.mjs";
15
+ import { getRecord, searchRecords, listRecords, getRecordDetails, createRecord, updateRecord, deleteRecord, queryContext } from "./records-BJLSmv0O.mjs";
16
+ import { runReport, REPORTS } from "./reports-ogUa41jr.mjs";
17
+ import { describeSchema, createField } from "./schema-CYm_n5BN.mjs";
18
18
  import { objectOf, recordOf, userOf, activitiesOf, fileOf } from "./store-5gqRSKXg.mjs";
19
- import { ACTION_TYPES, TRIGGER_TYPES, listAutomationRuns, AUTOMATION_TEMPLATES, listAutomations, createAutomationFromTemplate, createAutomation, setAutomationEnabled, testAutomation, runScheduled } from "./automations-DwWruxmu.mjs";
20
- import { listForms, ingestInbox } from "./forms-BfuWaQjA.mjs";
19
+ import { ACTION_TYPES, TRIGGER_TYPES, listAutomationRuns, AUTOMATION_TEMPLATES, listAutomations, createAutomationFromTemplate, createAutomation, setAutomationEnabled, testAutomation, runScheduled } from "./automations-gCDYNS88.mjs";
20
+ import { listForms, ingestInbox } from "./forms-Bb0zztBM.mjs";
21
21
  import { B as BUTTON_STYLES, C as CONTRASTS, d as COLOR_MODES, S as SIDEBAR_STYLES, e as DENSITIES, R as RADII, N as NEUTRALS, F as FONTS } from "./schema-DnLj8dUO.mjs";
22
22
  import { listPresets, getAppearance, setAppearance, applyPreset } from "./appearance-BloHbiLA.mjs";
23
23
  import { listExtensions, setExtensionEnabled } from "./extensions-Bp72uZ9a.mjs";
24
24
  import { listModules, setModuleEnabled } from "./modules-clWdE-K2.mjs";
25
25
  import { inlineFileIds, formatBytes, fileKind } from "./files-CjsPJSR6.mjs";
26
- import { listFiles, fileUsage } from "./files-D1daDgY0.mjs";
27
- import { l as labelOf } from "./relations-B9TynBmA.mjs";
26
+ import { listFiles, fileUsage } from "./files-Be7DZd1a.mjs";
27
+ import { l as labelOf } from "./relations-bmUKPT0G.mjs";
28
28
  import "node:util";
29
29
  import "node:fs";
30
30
  import "node:path";
@@ -39,7 +39,7 @@ import "./papaparse-Df7i1dHr.mjs";
39
39
  import "./_commonjsHelpers-ClciMIcB.mjs";
40
40
  import "stream";
41
41
  import "./utils-DmI8FtXP.mjs";
42
- import "./standardObjects-CheqfDqa.mjs";
42
+ import "./standardObjects-vrmqIVLH.mjs";
43
43
  import "./settings-Di8nzNEL.mjs";
44
44
  import "./runtimeConfig-CCOFg1RM.mjs";
45
45
  import "./extensionActions-D2HtpQz4.mjs";
@@ -6,9 +6,9 @@ const __filename = __acrmPath(import.meta.url);
6
6
  const __dirname = __acrmDirname(__filename);
7
7
  import { n as newId } from "./ids-CvnmrMJX.mjs";
8
8
  import { F as FILTER_OPS } from "../agentcrm.mjs";
9
- import { OpsError, assertCan, can } from "./context-CxFfqS4D.mjs";
10
- import { listRecords } from "./records-DcHFelq5.mjs";
11
- import { requireObject, asDoc, viewsOf, thaw, pipelinesOf } from "./store-5gqRSKXg.mjs";
9
+ import { assertCan, can, OpsError } from "./context-CxFfqS4D.mjs";
10
+ import { listRecords } from "./records-BJLSmv0O.mjs";
11
+ import { viewsOf, requireObject, asDoc, thaw, pipelinesOf } from "./store-5gqRSKXg.mjs";
12
12
  import "node:util";
13
13
  import "node:fs";
14
14
  import "node:path";
@@ -20,8 +20,8 @@ import "node:http";
20
20
  import "./pluginRegistry-DyHtlkcr.mjs";
21
21
  import "./fields-B0bitgmr.mjs";
22
22
  import "./query-C5nJB_3C.mjs";
23
- import "./standardObjects-CheqfDqa.mjs";
24
- import "./relations-B9TynBmA.mjs";
23
+ import "./standardObjects-vrmqIVLH.mjs";
24
+ import "./relations-bmUKPT0G.mjs";
25
25
  function listViews(ctx, objectId) {
26
26
  assertCan(ctx, "views.read");
27
27
  const uid = ctx.user?.id;
package/dist/version.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleox-agent-crm/agentcrm",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "minSkillsVersion": "0.1.0",
5
5
  "dataSchema": 1,
6
6
  "repository": "github:fleox/agentcrm"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleox-agent-crm/agentcrm",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "The static CRM your AI agent can run: a Twenty / HubSpot-style CRM that runs entirely in the browser or headless through the agentcrm CLI, with data on storage you own.",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcrm",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Skills to install AgentCRM (the static CRM your AI agent can run), manage its sites, run and customise the CRM (modules, appearance, extensions), drive sales workflows and build extension modules and themes.",
5
5
  "author": {
6
6
  "name": "fleox"