@fleox-agent-crm/agentcrm 0.3.0 → 0.3.1
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.
- package/README.md +16 -16
- package/changelog.json +19 -0
- package/dist/admin/admin-files.json +8 -8
- package/dist/admin/assets/{chartsImpl-CE2akJat.js → chartsImpl-B45AAqnr.js} +30 -30
- package/dist/admin/assets/{firebase-BHSUggRo.js → firebase-C1x_l9P9.js} +2 -2
- package/dist/admin/assets/{gitJson-BulGh2KV.js → gitJson-CAUSxl8d.js} +1 -1
- package/dist/admin/assets/{index-DidVVK7L.js → index-ChFauO8y.js} +265 -265
- package/dist/admin/assets/{index-hMXwHSvN.css → index-TcjySLVg.css} +1 -1
- package/dist/admin/assets/{lucide-react.min-DEDlHCPj.js → lucide-react.min--rznIkmQ.js} +1 -1
- package/dist/admin/assets/{sdk-CnrLnlRo.js → sdk-DgzLACmL.js} +1 -1
- package/dist/admin/index.html +2 -2
- package/dist/agent/agentcrm.mjs +267 -142
- package/dist/agent/chunks/{api-B6OqNVWp.mjs → api-Cz0Ubj-B.mjs} +4 -3
- package/dist/agent/chunks/{appearance-BaShelzt.mjs → appearance-BloHbiLA.mjs} +1 -1
- package/dist/agent/chunks/{automations-BWrvLqP6.mjs → automations-DwWruxmu.mjs} +3 -3
- package/dist/agent/chunks/{backup-ClSmxmFZ.mjs → backup-W3CjgkAJ.mjs} +16 -3
- package/dist/agent/chunks/{compact-CsFPaAAq.mjs → compact-BGHI92tm.mjs} +2 -2
- package/dist/agent/chunks/{exportCsv-B5hRvkNm.mjs → exportCsv-C6e6XEmB.mjs} +38 -3
- package/dist/agent/chunks/{extCheck--7lmVvl7.mjs → extCheck-GgHG1dYB.mjs} +16 -9
- package/dist/agent/chunks/{extPackage-BGl5Oqs8.mjs → extPackage-DuZ6GZhp.mjs} +2 -2
- package/dist/agent/chunks/{extScaffold-DqxI7R1e.mjs → extScaffold-Aqyjf1nr.mjs} +3 -3
- package/dist/agent/chunks/{extensions-BP1k60md.mjs → extensions-Bp72uZ9a.mjs} +19 -6
- package/dist/agent/chunks/{featureRegistry-C9nbVShp.mjs → featureRegistry-DSsZbhrh.mjs} +3 -3
- package/dist/agent/chunks/{files-q8Xu5bft.mjs → files-D1daDgY0.mjs} +1 -1
- package/dist/agent/chunks/{forms-BKU5F9WF.mjs → forms-BfuWaQjA.mjs} +1 -1
- package/dist/agent/chunks/{importCsv-DmE_sBch.mjs → importCsv-LVmMTaWU.mjs} +2 -2
- package/dist/agent/chunks/{install-CpHoUFtL.mjs → install-CbpD_7gd.mjs} +3 -3
- package/dist/agent/chunks/{loader-C9nm3iwx.mjs → loader-MPD9iAvh.mjs} +20 -20
- package/dist/agent/chunks/{modules-CqdVQVJb.mjs → modules-clWdE-K2.mjs} +27 -2
- package/dist/agent/chunks/{nodeImport-BPCt5uMu.mjs → nodeImport-ylfRDvqt.mjs} +2 -2
- package/dist/agent/chunks/{package-DWbn3t2R.mjs → package-B7mI6HGi.mjs} +1 -1
- package/dist/agent/chunks/{papaparse-DjBYnQM2.mjs → papaparse-Df7i1dHr.mjs} +31 -7
- package/dist/agent/chunks/{pipelines-DCdRBNBV.mjs → pipelines-BAKpPk1w.mjs} +1 -1
- package/dist/agent/chunks/{plan-C0TBrU92.mjs → plan-BmTat6-T.mjs} +1 -1
- package/dist/agent/chunks/{records-DoqiT9xR.mjs → records-DcHFelq5.mjs} +5 -1
- package/dist/agent/chunks/{registry-Ceexawxl.mjs → registry-COfJrSmg.mjs} +18 -18
- package/dist/agent/chunks/{reports-BB59N6ke.mjs → reports-BACHGccj.mjs} +3 -3
- package/dist/agent/chunks/{runtime-DHo7ZEEO.mjs → runtime-C0Xf0Axi.mjs} +53 -32
- package/dist/agent/chunks/{schema-DlHt2PxI.mjs → schema-C4lSslXv.mjs} +10 -4
- package/dist/agent/chunks/{schema-7-4-qtSS.mjs → schema-DnLj8dUO.mjs} +10 -0
- package/dist/agent/chunks/{seed-CugPGYZL.mjs → seed-6MRmQQBR.mjs} +26 -8
- package/dist/agent/chunks/{server-ISwlDRCN.mjs → server-DC3t8r30.mjs} +5 -5
- package/dist/agent/chunks/{settings-BtvR_jdm.mjs → settings-Di8nzNEL.mjs} +35 -0
- package/dist/agent/chunks/{stats-CfeJssSS.mjs → stats-BfuLWhVV.mjs} +2 -2
- package/dist/agent/chunks/{tools-Bew8lxKx.mjs → tools-iSH-yMpl.mjs} +59 -19
- package/dist/agent/chunks/{views-fO_BM5U_.mjs → views-DH1n9Lrt.mjs} +1 -1
- package/dist/version.json +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/agentcrm-admin/SKILL.md +67 -67
- package/plugin/skills/agentcrm-admin/references/appearance.md +1 -1
- package/plugin/skills/agentcrm-admin/references/records.md +5 -5
- package/plugin/skills/agentcrm-admin/references/schema.md +20 -20
- package/plugin/skills/agentcrm-extensions/SKILL.md +9 -9
- package/plugin/skills/agentcrm-extensions/references/module-recipes.md +2 -2
- package/plugin/skills/agentcrm-extensions/references/testing.md +16 -14
- package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +3 -3
- package/plugin/skills/agentcrm-install/SKILL.md +13 -13
- package/plugin/skills/agentcrm-install/references/firebase.md +1 -1
- package/plugin/skills/agentcrm-install/references/github.md +1 -1
- package/plugin/skills/agentcrm-install/references/mcp.md +3 -3
- package/plugin/skills/agentcrm-install/references/plans.md +1 -1
- package/plugin/skills/agentcrm-install/references/s3.md +1 -1
- package/plugin/skills/agentcrm-manage/SKILL.md +15 -15
- package/plugin/skills/agentcrm-sales/SKILL.md +18 -18
package/dist/agent/agentcrm.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { dirname as __acrmDirname } from "node:path";
|
|
|
5
5
|
const require2 = __acrmRequire(import.meta.url);
|
|
6
6
|
const __filename = __acrmPath(import.meta.url);
|
|
7
7
|
const __dirname = __acrmDirname(__filename);
|
|
8
|
-
import { parseArgs } from "node:util";
|
|
8
|
+
import { format, parseArgs } from "node:util";
|
|
9
9
|
import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, chmodSync, renameSync, openSync, writeSync, closeSync, unlinkSync, mkdtempSync, readdirSync, createReadStream } from "node:fs";
|
|
10
10
|
import { join, resolve, dirname, basename, extname, sep, normalize, isAbsolute } from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
@@ -54,6 +54,13 @@ class CliError extends Error {
|
|
|
54
54
|
this.exitCode = exitCode;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
function routeConsoleToStderr(target = console, write = (t) => void process.stderr.write(t)) {
|
|
58
|
+
const toStderr = (...args) => write(`${format(...args)}
|
|
59
|
+
`);
|
|
60
|
+
target.log = toStderr;
|
|
61
|
+
target.info = toStderr;
|
|
62
|
+
target.debug = toStderr;
|
|
63
|
+
}
|
|
57
64
|
function createIo(json) {
|
|
58
65
|
return {
|
|
59
66
|
json,
|
|
@@ -68,10 +75,20 @@ function printResult(io, data, human) {
|
|
|
68
75
|
const OPS_HINTS = {
|
|
69
76
|
forbidden: "The CRM user the CLI acts as lacks this permission (see `site show` → user, `users list`). Ask an admin, or re-register the site with an admin user (`site add … --user <email>`).",
|
|
70
77
|
notFound: "Check the id (list commands print them: `records list --object …`, `objects list`, `views list`…).",
|
|
71
|
-
validation: "Fix the input
|
|
78
|
+
validation: "Fix the input and retry (`agentcrm help <command>` lists the options; for record fields, `objects get <object>` lists field ids, types and select options).",
|
|
72
79
|
conflict: "Something with the same key already exists — list it first, then update instead of creating.",
|
|
73
80
|
invalid: "Check the arguments with `agentcrm help <command>`."
|
|
74
81
|
};
|
|
82
|
+
function dependencyHint(details) {
|
|
83
|
+
const ids = (key) => Array.isArray(details?.[key]) ? details[key].map(String) : [];
|
|
84
|
+
const blockers = ids("blockers");
|
|
85
|
+
if (blockers.length) return `Blocked by: ${blockers.join(", ")}. Re-run with --cascade (AI tools: "cascade": true) to change ${blockers.length > 1 ? "them" : "it"} too — show the user the plan's \`changed\` list first.`;
|
|
86
|
+
const dependents = ids("dependents");
|
|
87
|
+
if (dependents.length) return `Disable or uninstall ${dependents.join(", ")} first (\`ext disable <id>\` / \`ext uninstall <id>\`), or pass --force to \`ext uninstall\`.`;
|
|
88
|
+
const missing = ids("missing");
|
|
89
|
+
if (missing.length) return `Install the missing module(s) first: ${missing.join(", ")} (\`ext install <zip>\`).`;
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
75
92
|
function knownError(error) {
|
|
76
93
|
if (!(error instanceof Error)) return null;
|
|
77
94
|
if (error.name === "PassphraseError") {
|
|
@@ -84,7 +101,8 @@ function knownError(error) {
|
|
|
84
101
|
}
|
|
85
102
|
if (error.name === "OpsError") {
|
|
86
103
|
const code = error.code ?? "invalid";
|
|
87
|
-
|
|
104
|
+
const details = error.details;
|
|
105
|
+
return new CliError(code, error.message, code === "conflict" && dependencyHint(details) || OPS_HINTS[code], code === "validation" || code === "invalid" ? 2 : 1);
|
|
88
106
|
}
|
|
89
107
|
if (error.name === "FieldValidationError") {
|
|
90
108
|
return new CliError("validation", error.message, OPS_HINTS.validation, 2);
|
|
@@ -120,10 +138,11 @@ const output = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
120
138
|
knownError,
|
|
121
139
|
printError,
|
|
122
140
|
printResult,
|
|
141
|
+
routeConsoleToStderr,
|
|
123
142
|
toCliError
|
|
124
143
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
125
144
|
const name = "@fleox-agent-crm/agentcrm";
|
|
126
|
-
const version = "0.3.
|
|
145
|
+
const version = "0.3.1";
|
|
127
146
|
const type = "module";
|
|
128
147
|
const 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.";
|
|
129
148
|
const license = "SEE LICENSE IN LICENSE";
|
|
@@ -505,8 +524,8 @@ function tmpFolder() {
|
|
|
505
524
|
return tmpBase;
|
|
506
525
|
}
|
|
507
526
|
async function nodeImportBundle() {
|
|
508
|
-
await import("./chunks/runtime-
|
|
509
|
-
const { createNodeImportBundle } = await import("./chunks/nodeImport-
|
|
527
|
+
await import("./chunks/runtime-C0Xf0Axi.mjs");
|
|
528
|
+
const { createNodeImportBundle } = await import("./chunks/nodeImport-ylfRDvqt.mjs");
|
|
510
529
|
return createNodeImportBundle({ runtimeDir: runtimeDir(), tmpDir: tmpFolder() });
|
|
511
530
|
}
|
|
512
531
|
function hasEnabledExtensions(store) {
|
|
@@ -520,15 +539,9 @@ async function loadSessionExtensions(store, opts = {}) {
|
|
|
520
539
|
if (!loadedAny && !opts.force && !hasEnabledExtensions(store)) return null;
|
|
521
540
|
try {
|
|
522
541
|
const importBundle = await nodeImportBundle();
|
|
523
|
-
const { syncExtensions } = await import("./chunks/loader-
|
|
542
|
+
const { syncExtensions } = await import("./chunks/loader-MPD9iAvh.mjs");
|
|
524
543
|
loadedAny = true;
|
|
525
|
-
|
|
526
|
-
console.error = () => void 0;
|
|
527
|
-
try {
|
|
528
|
-
return await syncExtensions(store, { importBundle });
|
|
529
|
-
} finally {
|
|
530
|
-
console.error = original;
|
|
531
|
-
}
|
|
544
|
+
return await syncExtensions(store, { importBundle, onError: () => void 0 });
|
|
532
545
|
} catch (err) {
|
|
533
546
|
return { loaded: [], unloaded: [], failed: [{ id: "*", error: err.message }], skipped: [] };
|
|
534
547
|
}
|
|
@@ -540,7 +553,7 @@ async function loadSessionExtensions(store, opts = {}) {
|
|
|
540
553
|
sync,
|
|
541
554
|
async close() {
|
|
542
555
|
if (!loadedAny) return;
|
|
543
|
-
(await import("./chunks/registry-
|
|
556
|
+
(await import("./chunks/registry-COfJrSmg.mjs")).deactivateAllExtensions();
|
|
544
557
|
}
|
|
545
558
|
};
|
|
546
559
|
}
|
|
@@ -663,7 +676,7 @@ async function openSession(alias, opts = {}) {
|
|
|
663
676
|
const [{ createOpsContext }, { createPluginRegistry, plugins: defaultPlugins }, { installAutomations }] = await Promise.all([
|
|
664
677
|
import("./chunks/context-CxFfqS4D.mjs"),
|
|
665
678
|
import("./chunks/pluginRegistry-DyHtlkcr.mjs"),
|
|
666
|
-
import("./chunks/automations-
|
|
679
|
+
import("./chunks/automations-DwWruxmu.mjs")
|
|
667
680
|
]);
|
|
668
681
|
const own = createPluginRegistry((hook, err) => process.stderr.write(`[agentcrm] ${hook} handler failed: ${String(err?.message ?? err)}
|
|
669
682
|
`));
|
|
@@ -696,7 +709,7 @@ async function openSession(alias, opts = {}) {
|
|
|
696
709
|
}
|
|
697
710
|
}
|
|
698
711
|
{
|
|
699
|
-
const forms = await import("./chunks/forms-
|
|
712
|
+
const forms = await import("./chunks/forms-BfuWaQjA.mjs");
|
|
700
713
|
const { buildConnector } = await import("./chunks/hosting-Bme7LxPc.mjs");
|
|
701
714
|
forms.setInboxSourceResolver((_ctx, form) => {
|
|
702
715
|
if (form.inbox.kind !== "s3") return null;
|
|
@@ -768,6 +781,12 @@ const session = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
|
|
|
768
781
|
openStore,
|
|
769
782
|
withSession
|
|
770
783
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
784
|
+
function writeOutFile(path, data) {
|
|
785
|
+
const abs = resolve(path);
|
|
786
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
787
|
+
writeFileSync(abs, data);
|
|
788
|
+
return abs;
|
|
789
|
+
}
|
|
771
790
|
function usage(message, hint) {
|
|
772
791
|
throw new CliError("usage", message, hint, 2);
|
|
773
792
|
}
|
|
@@ -912,7 +931,7 @@ function readLocalFile(path) {
|
|
|
912
931
|
return { name: basename(full), bytes: new Uint8Array(readFileSync(full)) };
|
|
913
932
|
}
|
|
914
933
|
async function uploadLocalFiles(ctx, paths, targets2 = []) {
|
|
915
|
-
const { uploadFile } = await import("./chunks/files-
|
|
934
|
+
const { uploadFile } = await import("./chunks/files-D1daDgY0.mjs");
|
|
916
935
|
const { guessMime, imageDimensions } = await import("./chunks/files-CjsPJSR6.mjs");
|
|
917
936
|
const out = [];
|
|
918
937
|
for (const p of paths) {
|
|
@@ -960,7 +979,7 @@ const filesCommand = {
|
|
|
960
979
|
switch (action) {
|
|
961
980
|
case "list":
|
|
962
981
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
963
|
-
const { listFiles } = await import("./chunks/files-
|
|
982
|
+
const { listFiles } = await import("./chunks/files-D1daDgY0.mjs");
|
|
964
983
|
const target = strOpt(options, "record") ?? strOpt(options, "activity") ?? null;
|
|
965
984
|
const items = listFiles(ctx, target, { includeRelated: boolOpt(options, "related"), kind: strOpt(options, "kind"), search: strOpt(options, "search") });
|
|
966
985
|
const limit = Math.min(1e3, numOpt(options, "limit") ?? 100);
|
|
@@ -975,7 +994,7 @@ ${table(r.files, ["id", "name", "mime", "size", "createdAt"])}`);
|
|
|
975
994
|
if (record && activity) usage("Give --record or --activity, not both.");
|
|
976
995
|
for (const p of args) readLocalFile(p);
|
|
977
996
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
978
|
-
const { attachFile } = await import("./chunks/files-
|
|
997
|
+
const { attachFile } = await import("./chunks/files-D1daDgY0.mjs");
|
|
979
998
|
const files2 = await uploadLocalFiles(ctx, args, record ? [record] : []);
|
|
980
999
|
if (activity) for (const f of files2) await attachFile(ctx, f.id, { kind: "activity", id: activity });
|
|
981
1000
|
const { requireFile } = await import("./chunks/store-5gqRSKXg.mjs");
|
|
@@ -988,17 +1007,17 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
|
|
|
988
1007
|
const out = strOpt(options, "out");
|
|
989
1008
|
if (!out) usage("files get needs --out <path>.");
|
|
990
1009
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
991
|
-
const { getFile, getFileBytes } = await import("./chunks/files-
|
|
1010
|
+
const { getFile, getFileBytes } = await import("./chunks/files-D1daDgY0.mjs");
|
|
992
1011
|
const meta = getFile(ctx, id);
|
|
993
1012
|
const bytes = await getFileBytes(ctx, id, { thumb: boolOpt(options, "thumb") });
|
|
994
|
-
|
|
1013
|
+
writeOutFile(out, bytes);
|
|
995
1014
|
return { id, name: meta.name, mime: meta.mime, bytes: bytes.length, path: resolve(out) };
|
|
996
1015
|
}, (r) => `Wrote ${r.bytes} bytes (${r.name}) to ${r.path}`);
|
|
997
1016
|
}
|
|
998
1017
|
case "delete": {
|
|
999
1018
|
const id = positional(args, 0, "file id", U$1.delete);
|
|
1000
1019
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
|
|
1001
|
-
const { getFile, deleteFile } = await import("./chunks/files-
|
|
1020
|
+
const { getFile, deleteFile } = await import("./chunks/files-D1daDgY0.mjs");
|
|
1002
1021
|
const meta = fileOut(getFile(ctx, id));
|
|
1003
1022
|
if (options.yes !== true) return { needsConfirmation: true, plan: { delete: meta } };
|
|
1004
1023
|
await deleteFile(ctx, id);
|
|
@@ -1006,7 +1025,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
|
|
|
1006
1025
|
}, (r) => "plan" in r && r.plan ? `Would delete ${r.plan.delete.name} (${r.plan.delete.id}). Re-run with --yes once the user agrees.` : `Deleted ${"deleted" in r ? r.deleted : id} (bytes purged by \`agentcrm compact\`).`);
|
|
1007
1026
|
}
|
|
1008
1027
|
case "usage":
|
|
1009
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-
|
|
1028
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-D1daDgY0.mjs")).fileUsage(ctx));
|
|
1010
1029
|
default:
|
|
1011
1030
|
usage(`Unknown files action "${action ?? ""}".`, "Use list, upload, get, delete or usage.");
|
|
1012
1031
|
}
|
|
@@ -1019,7 +1038,7 @@ async function recordImageAction(io, options, args) {
|
|
|
1019
1038
|
if (!remove && !path) usage("records image needs an image path (or --remove).");
|
|
1020
1039
|
const local = remove ? null : readLocalFile(path);
|
|
1021
1040
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
1022
|
-
const { setRecordImage, clearRecordImage } = await import("./chunks/files-
|
|
1041
|
+
const { setRecordImage, clearRecordImage } = await import("./chunks/files-D1daDgY0.mjs");
|
|
1023
1042
|
const field = strOpt(options, "field");
|
|
1024
1043
|
if (!local) {
|
|
1025
1044
|
const r = await clearRecordImage(ctx, recordId, field);
|
|
@@ -1307,7 +1326,7 @@ const recordsCommand = {
|
|
|
1307
1326
|
const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
|
|
1308
1327
|
const sorts = parseSorts(multiOpt(options, "sort"));
|
|
1309
1328
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
1310
|
-
const { listRecords } = await import("./chunks/records-
|
|
1329
|
+
const { listRecords } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1311
1330
|
const page = listRecords(ctx, objectId, {
|
|
1312
1331
|
filters,
|
|
1313
1332
|
sorts,
|
|
@@ -1325,7 +1344,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1325
1344
|
case "get": {
|
|
1326
1345
|
const id = positional(args, 0, "record id", U.get);
|
|
1327
1346
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
1328
|
-
const { getRecordDetails } = await import("./chunks/records-
|
|
1347
|
+
const { getRecordDetails } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1329
1348
|
const d = getRecordDetails(ctx, id);
|
|
1330
1349
|
return { ...await recordOut(ctx, d.record), relations: d.relations, related: d.inverse.filter((i) => i.records.length), activityCount: d.activityCount };
|
|
1331
1350
|
});
|
|
@@ -1336,7 +1355,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1336
1355
|
if (!Object.keys(fields).length) usage("records create needs --fields or --set.", `Usage: agentcrm ${U.create}`);
|
|
1337
1356
|
const owner = strOpt(options, "owner");
|
|
1338
1357
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
1339
|
-
const { createRecord } = await import("./chunks/records-
|
|
1358
|
+
const { createRecord } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1340
1359
|
return recordOut(ctx, await createRecord(ctx, objectId, fields, owner ? { ownerId: owner } : {}));
|
|
1341
1360
|
}, (r) => `Created ${r.object} ${r.id} (${String(r.label)})`);
|
|
1342
1361
|
}
|
|
@@ -1346,7 +1365,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1346
1365
|
const owner = strOpt(options, "owner");
|
|
1347
1366
|
if (!Object.keys(fields).length && owner === void 0) usage("records update needs --fields, --set or --owner.");
|
|
1348
1367
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
1349
|
-
const { updateRecord } = await import("./chunks/records-
|
|
1368
|
+
const { updateRecord } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1350
1369
|
return recordOut(ctx, await updateRecord(ctx, id, fields, owner !== void 0 ? { ownerId: owner || null } : {}));
|
|
1351
1370
|
}, (r) => `Updated ${r.id} (${String(r.label)})`);
|
|
1352
1371
|
}
|
|
@@ -1354,7 +1373,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1354
1373
|
if (!args.length) usage("records delete needs at least one id.", `Usage: agentcrm ${U.delete}`);
|
|
1355
1374
|
const hard = boolOpt(options, "hard");
|
|
1356
1375
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
|
|
1357
|
-
const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-
|
|
1376
|
+
const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1358
1377
|
const { recordLabel } = await import("./chunks/fields-B0bitgmr.mjs");
|
|
1359
1378
|
const { objectOf } = await import("./chunks/store-5gqRSKXg.mjs");
|
|
1360
1379
|
const targets2 = args.map((id) => getRecord(ctx, id, { includeDeleted: true }));
|
|
@@ -1367,7 +1386,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1367
1386
|
case "restore": {
|
|
1368
1387
|
const id = positional(args, 0, "record id", U.restore);
|
|
1369
1388
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
1370
|
-
const { restoreRecord } = await import("./chunks/records-
|
|
1389
|
+
const { restoreRecord } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1371
1390
|
return recordOut(ctx, await restoreRecord(ctx, id));
|
|
1372
1391
|
});
|
|
1373
1392
|
}
|
|
@@ -1375,7 +1394,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1375
1394
|
const query = args.join(" ").trim();
|
|
1376
1395
|
if (!query) usage("records search needs a query.", `Usage: agentcrm ${U.search}`);
|
|
1377
1396
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
1378
|
-
const { searchRecords } = await import("./chunks/records-
|
|
1397
|
+
const { searchRecords } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1379
1398
|
return searchRecords(ctx, query, { objects: listOpt(options, "object"), limit: Math.min(100, numOpt(options, "limit") ?? 20) });
|
|
1380
1399
|
});
|
|
1381
1400
|
}
|
|
@@ -1392,7 +1411,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
|
|
|
1392
1411
|
const [primary, ...dups] = args;
|
|
1393
1412
|
if (!primary || !dups.length) usage("records merge needs <primaryId> <duplicateId>….", `Usage: agentcrm ${U.merge}`);
|
|
1394
1413
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
|
|
1395
|
-
const { getRecord } = await import("./chunks/records-
|
|
1414
|
+
const { getRecord } = await import("./chunks/records-DcHFelq5.mjs");
|
|
1396
1415
|
const all = [primary, ...dups].map((id) => getRecord(ctx, id));
|
|
1397
1416
|
const plan = { primary: await recordOut(ctx, all[0]), duplicates: await Promise.all(all.slice(1).map((r) => recordOut(ctx, r))) };
|
|
1398
1417
|
if (options.yes !== true) return { needsConfirmation: true, plan };
|
|
@@ -1424,13 +1443,13 @@ const pipelinesCommand = {
|
|
|
1424
1443
|
io,
|
|
1425
1444
|
options,
|
|
1426
1445
|
{},
|
|
1427
|
-
async ({ ctx }) => (await import("./chunks/pipelines-
|
|
1446
|
+
async ({ ctx }) => (await import("./chunks/pipelines-BAKpPk1w.mjs")).listPipelines(ctx, strOpt(options, "object")),
|
|
1428
1447
|
(ps) => ps.map((p) => `${p.id} (${p.name}, ${p.object}): ${p.stages.map((s) => `${s.label} ${Math.round(s.probability * (s.probability <= 1 ? 100 : 1))}%`).join(" → ")}`).join("\n")
|
|
1429
1448
|
);
|
|
1430
1449
|
}
|
|
1431
1450
|
if (action === "move") {
|
|
1432
1451
|
if (!id || !stage.length) usage("pipelines move needs <recordId> <stage>.");
|
|
1433
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => recordOut(ctx, await (await import("./chunks/pipelines-
|
|
1452
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => recordOut(ctx, await (await import("./chunks/pipelines-BAKpPk1w.mjs")).moveStage(ctx, id, stage.join(" "), { lostReason: strOpt(options, "lost-reason") })));
|
|
1434
1453
|
}
|
|
1435
1454
|
usage(`Unknown pipelines action "${action}".`, "Use list or move.");
|
|
1436
1455
|
}
|
|
@@ -1462,7 +1481,7 @@ const viewsCommand = {
|
|
|
1462
1481
|
const [action = "list", id] = positionals;
|
|
1463
1482
|
switch (action) {
|
|
1464
1483
|
case "list":
|
|
1465
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/views-
|
|
1484
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/views-DH1n9Lrt.mjs")).listViews(ctx, strOpt(options, "object")), (vs) => table(vs, ["id", "object", "name", "type", "shared"]));
|
|
1466
1485
|
case "create": {
|
|
1467
1486
|
const type2 = strOpt(options, "type") ?? "table";
|
|
1468
1487
|
if (type2 !== "table" && type2 !== "kanban") usage("--type must be table or kanban.");
|
|
@@ -1477,12 +1496,12 @@ const viewsCommand = {
|
|
|
1477
1496
|
...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
|
|
1478
1497
|
shared: boolOpt(options, "shared")
|
|
1479
1498
|
};
|
|
1480
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-
|
|
1499
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-DH1n9Lrt.mjs")).createView(ctx, input));
|
|
1481
1500
|
}
|
|
1482
1501
|
case "delete":
|
|
1483
1502
|
if (!id) usage("views delete needs a view id.");
|
|
1484
1503
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
|
|
1485
|
-
const { getView, deleteView } = await import("./chunks/views-
|
|
1504
|
+
const { getView, deleteView } = await import("./chunks/views-DH1n9Lrt.mjs");
|
|
1486
1505
|
const v = getView(ctx, id);
|
|
1487
1506
|
if (options.yes !== true) return { needsConfirmation: true, plan: { delete: { id: v.id, name: v.name, object: v.object, shared: v.shared } } };
|
|
1488
1507
|
return deleteView(ctx, id);
|
|
@@ -1545,11 +1564,11 @@ const settingsCommand = {
|
|
|
1545
1564
|
options: [SITE_OPTION, AS_OPTION, { name: "set", type: "string", description: "key=value (repeatable), e.g. --set staleDealDays=21 --set defaultCurrency=EUR.", multiple: true }, { name: "data", type: "string", description: "JSON object of settings, or @file." }],
|
|
1546
1565
|
async run({ io, options, positionals }) {
|
|
1547
1566
|
const [action = "get"] = positionals;
|
|
1548
|
-
if (action === "get") return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/settings-
|
|
1567
|
+
if (action === "get") return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/settings-Di8nzNEL.mjs")).getSettings(ctx));
|
|
1549
1568
|
if (action === "set") {
|
|
1550
1569
|
const patch = fieldsInput(options, "data");
|
|
1551
1570
|
if (!Object.keys(patch).length) usage("settings set needs --set key=value or --data.");
|
|
1552
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/settings-
|
|
1571
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/settings-Di8nzNEL.mjs")).updateSettings(ctx, patch));
|
|
1553
1572
|
}
|
|
1554
1573
|
usage(`Unknown settings action "${action}".`, "Use get or set.");
|
|
1555
1574
|
}
|
|
@@ -1705,7 +1724,7 @@ const credentialsCommand = {
|
|
|
1705
1724
|
return;
|
|
1706
1725
|
}
|
|
1707
1726
|
if (!process.stdin.isTTY) {
|
|
1708
|
-
throw new CliError("needs-terminal", "verify reads the value the USER pastes in their own terminal.", `Ask the user to run it themselves (in Claude Code: \`! npx -y
|
|
1727
|
+
throw new CliError("needs-terminal", "verify reads the value the USER pastes in their own terminal.", `Ask the user to run it themselves (in Claude Code: \`! npx -y ${PACKAGE_NAME}@${readVersionInfo().version} credentials verify ${account}\`).`);
|
|
1709
1728
|
}
|
|
1710
1729
|
try {
|
|
1711
1730
|
copyToClipboard("");
|
|
@@ -1765,6 +1784,18 @@ function parseJsonValue(raw, name2) {
|
|
|
1765
1784
|
usage(`--${name2} is not valid JSON: ${err.message}`, `Example: --${name2} '{"targets":{"owner":50000}}' or --${name2} @config.json`);
|
|
1766
1785
|
}
|
|
1767
1786
|
}
|
|
1787
|
+
const MODULE_CONFIG_HINT = "Module configuration is JSON (≤ 256 KB) readable by every CRM user: no secrets (apiKey, token, secret, password…). Module ids: `agentcrm modules list`.";
|
|
1788
|
+
async function withHint(hint, p) {
|
|
1789
|
+
try {
|
|
1790
|
+
return await p;
|
|
1791
|
+
} catch (err) {
|
|
1792
|
+
const e = err;
|
|
1793
|
+
if (e?.name === "OpsError" && (e.code === "validation" || e.code === "notFound" || e.code === "invalid")) {
|
|
1794
|
+
throw new CliError(e.code, e.message, hint, e.code === "notFound" ? 1 : 2);
|
|
1795
|
+
}
|
|
1796
|
+
throw err;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1768
1799
|
const modulesCommand = {
|
|
1769
1800
|
name: "modules",
|
|
1770
1801
|
usage: "modules <list|get|enable|disable|config> …",
|
|
@@ -1782,13 +1813,13 @@ const modulesCommand = {
|
|
|
1782
1813
|
const apply = options.yes === true;
|
|
1783
1814
|
switch (action) {
|
|
1784
1815
|
case "list":
|
|
1785
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/modules-
|
|
1816
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/modules-clWdE-K2.mjs")).listModules(ctx), (rows) => table(
|
|
1786
1817
|
rows.map((m) => ({ ...m, enabled: m.enabled ? "yes" : "no", required: m.required ? "yes" : "", dependsOn: m.dependsOn.join(",") })),
|
|
1787
1818
|
["id", "name", "category", "source", "enabled", "required", "dependsOn"]
|
|
1788
1819
|
));
|
|
1789
1820
|
case "get": {
|
|
1790
1821
|
const id = positional(positionals, 1, "module id", "modules get <id>");
|
|
1791
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/modules-
|
|
1822
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/modules-clWdE-K2.mjs")).getModule(ctx, id));
|
|
1792
1823
|
}
|
|
1793
1824
|
case "enable":
|
|
1794
1825
|
case "disable": {
|
|
@@ -1796,7 +1827,7 @@ const modulesCommand = {
|
|
|
1796
1827
|
const enabled = action === "enable";
|
|
1797
1828
|
const cascade = boolOpt(options, "cascade");
|
|
1798
1829
|
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
1799
|
-
const { setModuleEnabled } = await import("./chunks/modules-
|
|
1830
|
+
const { setModuleEnabled } = await import("./chunks/modules-clWdE-K2.mjs");
|
|
1800
1831
|
if (apply) return setModuleEnabled(ctx, id, enabled, { cascade });
|
|
1801
1832
|
const { result } = await dryRun(ctx, (copy) => setModuleEnabled(copy, id, enabled, { cascade }));
|
|
1802
1833
|
return { needsConfirmation: true, plan: result };
|
|
@@ -1806,20 +1837,20 @@ Nothing changed — re-run with --yes once the user agrees.` : r.changed.length
|
|
|
1806
1837
|
case "config": {
|
|
1807
1838
|
const sub = positionals[1];
|
|
1808
1839
|
const id = positional(positionals, 2, "module id", "modules config <get|set> <id>");
|
|
1809
|
-
if (sub === "get") return withSession(io, options, {}, async ({ ctx }) => ({ id, config: (await import("./chunks/modules-
|
|
1840
|
+
if (sub === "get") return withSession(io, options, {}, async ({ ctx }) => ({ id, config: (await import("./chunks/modules-clWdE-K2.mjs")).getModuleConfig(ctx, id) ?? null }));
|
|
1810
1841
|
if (sub !== "set") usage(`Unknown modules config action "${sub ?? ""}".`, "Use modules config get <id> or modules config set <id> --value <json>.");
|
|
1811
1842
|
const clear = boolOpt(options, "clear");
|
|
1812
1843
|
const raw = strOpt(options, "value");
|
|
1813
1844
|
if (!clear && raw === void 0) usage("modules config set needs --value <json|@file|-> (or --clear).");
|
|
1814
1845
|
const value = clear ? null : parseJsonValue(raw, "value");
|
|
1815
|
-
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
1816
|
-
const mod = await import("./chunks/modules-
|
|
1846
|
+
return withHint(MODULE_CONFIG_HINT, withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
1847
|
+
const mod = await import("./chunks/modules-clWdE-K2.mjs");
|
|
1817
1848
|
mod.getModule(ctx, id);
|
|
1818
1849
|
const before = mod.getModuleConfig(ctx, id) ?? null;
|
|
1819
1850
|
if (apply) return { id, config: await mod.setModuleConfig(ctx, id, value) ?? null, before };
|
|
1820
1851
|
const { result } = await dryRun(ctx, (copy) => mod.setModuleConfig(copy, id, value));
|
|
1821
1852
|
return { needsConfirmation: true, plan: { id, before, after: result ?? null } };
|
|
1822
|
-
});
|
|
1853
|
+
}));
|
|
1823
1854
|
}
|
|
1824
1855
|
default:
|
|
1825
1856
|
usage(`Unknown modules action "${action}".`, "Use list, get, enable, disable or config.");
|
|
@@ -1845,14 +1876,27 @@ function imageDataUrl(file2) {
|
|
|
1845
1876
|
function view(a, full) {
|
|
1846
1877
|
if (full) return { ...a };
|
|
1847
1878
|
const { logo, favicon, customCss, ...rest } = a;
|
|
1848
|
-
const
|
|
1879
|
+
const image = (s) => `${s.slice(5, s.indexOf(";"))}, ${formatSize(Math.floor((s.length - s.indexOf(",") - 1) * 3 / 4))}, #${shortHash(s)}`;
|
|
1849
1880
|
return {
|
|
1850
1881
|
...rest,
|
|
1851
|
-
logo: logo ?
|
|
1852
|
-
favicon: favicon ?
|
|
1853
|
-
customCss: customCss ? `${new TextEncoder().encode(customCss).length} bytes` : null
|
|
1882
|
+
logo: logo ? image(logo) : null,
|
|
1883
|
+
favicon: favicon ? image(favicon) : null,
|
|
1884
|
+
customCss: customCss ? `${new TextEncoder().encode(customCss).length} bytes, #${shortHash(customCss)}` : null
|
|
1854
1885
|
};
|
|
1855
1886
|
}
|
|
1887
|
+
function formatSize(n) {
|
|
1888
|
+
return n < 1024 ? `${n} B` : `${(n / 1024).toFixed(1)} KB`;
|
|
1889
|
+
}
|
|
1890
|
+
function shortHash(s) {
|
|
1891
|
+
let h = 2166136261;
|
|
1892
|
+
for (let i = 0; i < s.length; i++) {
|
|
1893
|
+
h ^= s.charCodeAt(i);
|
|
1894
|
+
h = Math.imul(h, 16777619);
|
|
1895
|
+
}
|
|
1896
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
1897
|
+
}
|
|
1898
|
+
const APPEARANCE_HINT = "Allowed values: `agentcrm help appearance`; presets and installed themes: `agentcrm appearance presets`.";
|
|
1899
|
+
const withAppearanceHint = (p) => withHint(APPEARANCE_HINT, p);
|
|
1856
1900
|
function diff(before, after) {
|
|
1857
1901
|
const out = {};
|
|
1858
1902
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
|
|
@@ -1918,8 +1962,8 @@ const appearanceCommand = {
|
|
|
1918
1962
|
const [action = "get"] = positionals;
|
|
1919
1963
|
const apply = options.yes === true;
|
|
1920
1964
|
const full = boolOpt(options, "full");
|
|
1921
|
-
const appearance = () => import("./chunks/appearance-
|
|
1922
|
-
const change = (describe, op) => withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
1965
|
+
const appearance = () => import("./chunks/appearance-BloHbiLA.mjs");
|
|
1966
|
+
const change = (describe, op) => withAppearanceHint(withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
1923
1967
|
const before = view((await appearance()).getAppearance(ctx), full);
|
|
1924
1968
|
if (apply) {
|
|
1925
1969
|
const after = view(await op(ctx), full);
|
|
@@ -1931,7 +1975,7 @@ const appearanceCommand = {
|
|
|
1931
1975
|
const changes = r.plan ? r.plan.changes : r.changes ?? {};
|
|
1932
1976
|
const lines = Object.entries(changes).map(([k, c]) => ` ${k}: ${JSON.stringify(c.from)} → ${JSON.stringify(c.to)}`);
|
|
1933
1977
|
return [`${r.plan ? "Would apply" : "Applied"}: ${describe}`, ...lines.length ? lines : [" (no visible change)"], ...r.plan ? ["Nothing changed — re-run with --yes once the user agrees."] : []].join("\n");
|
|
1934
|
-
});
|
|
1978
|
+
}));
|
|
1935
1979
|
switch (action) {
|
|
1936
1980
|
case "get":
|
|
1937
1981
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
@@ -1986,7 +2030,7 @@ async function scaffold({ io, options, positionals }) {
|
|
|
1986
2030
|
if (existsSync(dir) && readdirSync(dir).length > 0 && !boolOpt(options, "force")) {
|
|
1987
2031
|
throw new CliError("dir-not-empty", `${dir} already exists and is not empty.`, "Pick another --dir (or --force to write into it).", 2);
|
|
1988
2032
|
}
|
|
1989
|
-
const { scaffoldExtension } = await import("./chunks/extScaffold-
|
|
2033
|
+
const { scaffoldExtension } = await import("./chunks/extScaffold-Aqyjf1nr.mjs");
|
|
1990
2034
|
const res = scaffoldExtension({ kind, id, dir, name: strOpt(options, "name"), description: strOpt(options, "description"), author: strOpt(options, "author") });
|
|
1991
2035
|
printResult(io, res, () => [`Created ${kind} "${id}" in ${dir}:`, ...res.files.map((f) => ` ${f}`), "", "Next:", ...res.next.map((n) => ` ${n}`)].join("\n"));
|
|
1992
2036
|
}
|
|
@@ -1994,8 +2038,8 @@ async function check({ io, options, positionals }) {
|
|
|
1994
2038
|
const target = positionals[1] ?? ".";
|
|
1995
2039
|
const kind = strOpt(options, "kind");
|
|
1996
2040
|
if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
|
|
1997
|
-
const { readRawPackage } = await import("./chunks/extPackage-
|
|
1998
|
-
const { checkExtension } = await import("./chunks/extCheck
|
|
2041
|
+
const { readRawPackage } = await import("./chunks/extPackage-DuZ6GZhp.mjs");
|
|
2042
|
+
const { checkExtension } = await import("./chunks/extCheck-GgHG1dYB.mjs");
|
|
1999
2043
|
const raw = await readRawPackage(target);
|
|
2000
2044
|
const report = await checkExtension(raw, { kind, callTools: !boolOpt(options, "no-call") });
|
|
2001
2045
|
printResult(io, report, () => {
|
|
@@ -2024,10 +2068,10 @@ async function check({ io, options, positionals }) {
|
|
|
2024
2068
|
}
|
|
2025
2069
|
async function pack({ io, options, positionals }) {
|
|
2026
2070
|
const target = positionals[1] ?? ".";
|
|
2027
|
-
const { readPackage } = await import("./chunks/extPackage-
|
|
2071
|
+
const { readPackage } = await import("./chunks/extPackage-DuZ6GZhp.mjs");
|
|
2028
2072
|
const { raw, pkg: pkg2 } = await readPackage(target);
|
|
2029
2073
|
if (raw.sourceType === "zip") usage("ext pack takes a project or an unpacked folder, not a .zip.");
|
|
2030
|
-
const { packExtension } = await import("./chunks/package-
|
|
2074
|
+
const { packExtension } = await import("./chunks/package-B7mI6HGi.mjs");
|
|
2031
2075
|
const bytes = await packExtension(pkg2);
|
|
2032
2076
|
const out = resolve(strOpt(options, "out") ?? join(raw.source, `${pkg2.manifest.id}-${pkg2.manifest.version}.zip`));
|
|
2033
2077
|
mkdirSync(dirname(out), { recursive: true });
|
|
@@ -2037,12 +2081,23 @@ async function pack({ io, options, positionals }) {
|
|
|
2037
2081
|
}
|
|
2038
2082
|
async function install({ io, options, positionals }) {
|
|
2039
2083
|
const target = positional(positionals, 1, "ZIP or folder", "ext install <zip|dir> [--site <alias>] [--yes]");
|
|
2040
|
-
const { readPackage } = await import("./chunks/extPackage-
|
|
2084
|
+
const { readPackage } = await import("./chunks/extPackage-DuZ6GZhp.mjs");
|
|
2041
2085
|
const { pkg: pkg2, raw } = await readPackage(target);
|
|
2042
2086
|
const apply = options.yes === true;
|
|
2043
2087
|
const disabled = boolOpt(options, "disabled");
|
|
2088
|
+
if (!boolOpt(options, "force")) {
|
|
2089
|
+
const { checkExtension } = await import("./chunks/extCheck-GgHG1dYB.mjs");
|
|
2090
|
+
const report = await checkExtension(raw, { callTools: false });
|
|
2091
|
+
if (!report.valid) {
|
|
2092
|
+
throw new CliError(
|
|
2093
|
+
"invalid-package",
|
|
2094
|
+
`The extension doesn't pass the check: ${report.errors.map((e) => e.message).join(" ")}`,
|
|
2095
|
+
`Nothing was installed. Run \`agentcrm ext check ${target}\` for the full report (--force installs it anyway).`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2044
2099
|
return withSession(io, options, { lock: apply }, async (s) => {
|
|
2045
|
-
const ext = await import("./chunks/extensions-
|
|
2100
|
+
const ext = await import("./chunks/extensions-Bp72uZ9a.mjs");
|
|
2046
2101
|
const existing = s.store.get("extensions", pkg2.manifest.id);
|
|
2047
2102
|
const m = pkg2.manifest;
|
|
2048
2103
|
const plan = {
|
|
@@ -2067,7 +2122,7 @@ async function install({ io, options, positionals }) {
|
|
|
2067
2122
|
}
|
|
2068
2123
|
const result = await ext.installExtension(s.ctx, pkg2, disabled ? { enabled: false } : {});
|
|
2069
2124
|
const report = await s.extensions.sync();
|
|
2070
|
-
const { getLoadedExtension } = await import("./chunks/registry-
|
|
2125
|
+
const { getLoadedExtension } = await import("./chunks/registry-COfJrSmg.mjs");
|
|
2071
2126
|
const loaded = getLoadedExtension(m.id);
|
|
2072
2127
|
const failure = report?.failed.find((f) => f.id === m.id || f.id === "*");
|
|
2073
2128
|
if (failure && !loaded) return { ...result, author: m.author ?? null, load: { status: "error", error: failure.error } };
|
|
@@ -2078,7 +2133,7 @@ async function install({ io, options, positionals }) {
|
|
|
2078
2133
|
...r.plan.warnings.map((w) => ` note: ${w}`),
|
|
2079
2134
|
` ${r.plan.warning}`,
|
|
2080
2135
|
"Nothing changed — re-run with --yes once the user agrees."
|
|
2081
|
-
].join("\n") : `${r.mode === "upgrade" ? `Upgraded ${r.previousVersion} → ` :
|
|
2136
|
+
].join("\n") : `${r.mode === "upgrade" ? `Upgraded ${r.id} ${r.previousVersion} → ${r.version}` : `Installed ${r.id} ${r.version}`} (${r.enabled ? "enabled" : "disabled"}; load: ${r.load.status}${"error" in r.load ? ` — ${r.load.error}` : ""})${r.warnings.length ? `
|
|
2082
2137
|
${r.warnings.join("\n ")}` : ""}`);
|
|
2083
2138
|
}
|
|
2084
2139
|
const extCommand = {
|
|
@@ -2089,7 +2144,7 @@ const extCommand = {
|
|
|
2089
2144
|
{ name: "new", usage: "ext new <module|theme> <id> [--dir <path>] [--name <name>] [--description <text>] [--author <name>] [--force]", summary: "Scaffold a standalone extension project (package.json, vite.config.ts, tsconfig + runtime typings, src/index.tsx, README).", writes: true },
|
|
2090
2145
|
{ name: "check", usage: "ext check [<dir|zip>] [--kind module|theme] [--no-call]", summary: "Validate manifest, ids, apiVersion, sizes, imports; load the bundle in a throw-away CRM and report routes, nav, slots, AI tools, automation actions, theme values." },
|
|
2091
2146
|
{ name: "pack", usage: "ext pack [<dir>] [--out <file.zip>]", summary: "ZIP manifest.json + bundle.js (+ style.css) for Settings → Modules → Install or `ext install`.", writes: true },
|
|
2092
|
-
{ name: "install", usage: "ext install <zip|dir> [--site <alias>] [--disabled] [--yes]", summary: "Install or upgrade an extension on a CRM. Prints the plan (id, version, author, upgrade, size) unless --yes.", writes: true, destructive: true },
|
|
2147
|
+
{ name: "install", usage: "ext install <zip|dir> [--site <alias>] [--disabled] [--force] [--yes]", summary: "Install or upgrade an extension on a CRM. The bundle is first loaded in a throw-away CRM (like ext check; --force skips it). Prints the plan (id, version, author, upgrade, size) unless --yes.", writes: true, destructive: true },
|
|
2093
2148
|
{ name: "list", usage: "ext list [--kind module|theme]", summary: "Installed extensions with version, enabled state and load status in this process." },
|
|
2094
2149
|
{ name: "enable", usage: "ext enable <id> [--cascade] [--yes]", summary: "Enable an installed extension (plan unless --yes).", writes: true },
|
|
2095
2150
|
{ name: "disable", usage: "ext disable <id> [--cascade] [--yes]", summary: "Disable it without uninstalling (plan unless --yes).", writes: true },
|
|
@@ -2104,7 +2159,7 @@ const extCommand = {
|
|
|
2104
2159
|
{ name: "name", type: "string", description: "new: display name." },
|
|
2105
2160
|
{ name: "description", type: "string", description: "new: one-sentence description." },
|
|
2106
2161
|
{ name: "author", type: "string", description: "new: author shown in the install plan and Settings → Modules." },
|
|
2107
|
-
{ name: "force", type: "boolean", description: "new: write into a non-empty folder. uninstall: even if enabled modules depend on it." },
|
|
2162
|
+
{ name: "force", type: "boolean", description: "new: write into a non-empty folder. install: skip the trial load (ext check). uninstall: even if enabled modules depend on it." },
|
|
2108
2163
|
{ name: "kind", type: "string", description: "check: expected kind. list: only modules or themes." },
|
|
2109
2164
|
{ name: "no-call", type: "boolean", description: "check: don't smoke-call the AI tools on the demo data." },
|
|
2110
2165
|
{ name: "out", type: "string", description: "pack / export: ZIP path (default: <dir>/<id>-<version>.zip, ./<id>-<version>.zip)." },
|
|
@@ -2128,8 +2183,8 @@ const extCommand = {
|
|
|
2128
2183
|
const kind = strOpt(options, "kind");
|
|
2129
2184
|
if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
|
|
2130
2185
|
return withSession(io, options, {}, async (s) => {
|
|
2131
|
-
const { listExtensions } = await import("./chunks/extensions-
|
|
2132
|
-
const { getLoadedExtension } = await import("./chunks/registry-
|
|
2186
|
+
const { listExtensions } = await import("./chunks/extensions-Bp72uZ9a.mjs");
|
|
2187
|
+
const { getLoadedExtension } = await import("./chunks/registry-COfJrSmg.mjs");
|
|
2133
2188
|
return listExtensions(s.ctx, kind ? { kind } : {}).map((e) => ({ ...e, load: s.extensions.safeMode ? "skipped" : getLoadedExtension(e.id)?.status ?? (e.enabled ? "not loaded" : "disabled"), ...getLoadedExtension(e.id)?.error ? { loadError: getLoadedExtension(e.id)?.error } : {} }));
|
|
2134
2189
|
}, (rows) => table(rows.map((r) => ({ ...r, enabled: r.enabled ? "yes" : "no" })), ["id", "kind", "name", "version", "enabled", "load", "author"]));
|
|
2135
2190
|
}
|
|
@@ -2139,15 +2194,15 @@ const extCommand = {
|
|
|
2139
2194
|
const enabled = action === "enable";
|
|
2140
2195
|
const cascade = boolOpt(options, "cascade");
|
|
2141
2196
|
return withSession(io, options, { lock: apply }, async (s) => {
|
|
2142
|
-
const ext = await import("./chunks/extensions-
|
|
2197
|
+
const ext = await import("./chunks/extensions-Bp72uZ9a.mjs");
|
|
2143
2198
|
const before = ext.getExtension(s.ctx, id);
|
|
2144
2199
|
if (!apply) {
|
|
2145
2200
|
const { result } = await dryRun(s.ctx, async (copy) => {
|
|
2146
2201
|
await ext.setExtensionEnabled(copy, id, enabled, { cascade });
|
|
2147
|
-
return (await import("./chunks/modules-
|
|
2202
|
+
return (await import("./chunks/modules-clWdE-K2.mjs")).moduleStates(copy.store);
|
|
2148
2203
|
});
|
|
2149
2204
|
const now = new Map(result.map((m) => [m.id, m.enabled]));
|
|
2150
|
-
const changed = (await import("./chunks/modules-
|
|
2205
|
+
const changed = (await import("./chunks/modules-clWdE-K2.mjs")).moduleStates(s.store).filter((m) => now.has(m.id) && now.get(m.id) !== m.enabled).map((m) => ({ id: m.id, enabled: now.get(m.id) }));
|
|
2151
2206
|
if (before.kind === "theme" && before.enabled !== enabled) changed.push({ id, enabled });
|
|
2152
2207
|
return { needsConfirmation: true, plan: { id, kind: before.kind, from: before.enabled, to: enabled, changed } };
|
|
2153
2208
|
}
|
|
@@ -2161,7 +2216,7 @@ Nothing changed — re-run with --yes once the user agrees.` : `${r.id} is now $
|
|
|
2161
2216
|
const id = positional(positionals, 1, "extension id", "ext uninstall <id> [--yes]");
|
|
2162
2217
|
const force = boolOpt(options, "force");
|
|
2163
2218
|
return withSession(io, options, { lock: apply }, async (s) => {
|
|
2164
|
-
const ext = await import("./chunks/extensions-
|
|
2219
|
+
const ext = await import("./chunks/extensions-Bp72uZ9a.mjs");
|
|
2165
2220
|
const summary = ext.getExtension(s.ctx, id);
|
|
2166
2221
|
if (!apply) {
|
|
2167
2222
|
await dryRun(s.ctx, (copy) => ext.uninstallExtension(copy, id, { force }));
|
|
@@ -2176,8 +2231,8 @@ Nothing changed — re-run with --yes once the user agrees.` : `Uninstalled ${r.
|
|
|
2176
2231
|
case "export": {
|
|
2177
2232
|
const id = positional(positionals, 1, "extension id", "ext export <id> [--out <file.zip>]");
|
|
2178
2233
|
return withSession(io, options, {}, async (s) => {
|
|
2179
|
-
const { getExtensionPackage } = await import("./chunks/extensions-
|
|
2180
|
-
const { packExtension } = await import("./chunks/package-
|
|
2234
|
+
const { getExtensionPackage } = await import("./chunks/extensions-Bp72uZ9a.mjs");
|
|
2235
|
+
const { packExtension } = await import("./chunks/package-B7mI6HGi.mjs");
|
|
2181
2236
|
const pkg2 = getExtensionPackage(s.ctx, id);
|
|
2182
2237
|
const bytes = await packExtension(pkg2);
|
|
2183
2238
|
const out = resolve(strOpt(options, "out") ?? `${id}-${pkg2.manifest.version}.zip`);
|
|
@@ -2191,9 +2246,6 @@ Nothing changed — re-run with --yes once the user agrees.` : `Uninstalled ${r.
|
|
|
2191
2246
|
}
|
|
2192
2247
|
}
|
|
2193
2248
|
};
|
|
2194
|
-
function printPlan(io, plan) {
|
|
2195
|
-
printResult(io, plan, () => `Would restore ${String(plan.plan.restore)} (${String(plan.plan.mode)}). Re-run with --yes once the user agrees.`);
|
|
2196
|
-
}
|
|
2197
2249
|
function mappingOpt(options) {
|
|
2198
2250
|
const raw = typeof options.map === "string" ? readTextValue(options.map, "map").trim() : void 0;
|
|
2199
2251
|
if (!raw) return void 0;
|
|
@@ -2242,10 +2294,22 @@ const importCommand = {
|
|
|
2242
2294
|
if (!existsSync2(path)) usage(`${path} does not exist.`);
|
|
2243
2295
|
const bytes = new Uint8Array(readFileSync2(path));
|
|
2244
2296
|
if (options.yes !== true) {
|
|
2245
|
-
|
|
2246
|
-
|
|
2297
|
+
return withSession(
|
|
2298
|
+
io,
|
|
2299
|
+
options,
|
|
2300
|
+
{},
|
|
2301
|
+
async ({ ctx }) => {
|
|
2302
|
+
const plan = await (await import("./chunks/backup-W3CjgkAJ.mjs")).planBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" });
|
|
2303
|
+
return { needsConfirmation: true, plan: { restore: resolve(path), bytes: bytes.length, ...plan } };
|
|
2304
|
+
},
|
|
2305
|
+
(r) => [
|
|
2306
|
+
`Would restore ${r.plan.restore} (${r.plan.mode}): ${r.plan.added} new, ${r.plan.overwritten} overwritten, ${r.plan.deleted} deleted document(s), ${r.plan.files} file(s).`,
|
|
2307
|
+
...r.plan.warnings.map((w) => `Warning: ${w}`),
|
|
2308
|
+
"Re-run with --yes once the user agrees."
|
|
2309
|
+
].join("\n")
|
|
2310
|
+
);
|
|
2247
2311
|
}
|
|
2248
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/backup-
|
|
2312
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/backup-W3CjgkAJ.mjs")).importBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" }), (r) => `Restored ${r.written} document(s) and ${r.files} file(s)`);
|
|
2249
2313
|
}
|
|
2250
2314
|
if (kind !== "csv") usage('Only "import csv" and "import zip" are supported.', "Usage: agentcrm import csv <file> --object <id>");
|
|
2251
2315
|
const source = positional(positionals, 1, "CSV file (or -)", "import csv <file|-> --object <id>");
|
|
@@ -2256,7 +2320,7 @@ const importCommand = {
|
|
|
2256
2320
|
const apply = options.yes === true && options["dry-run"] !== true;
|
|
2257
2321
|
const shown = numOpt(options, "rows") ?? 100;
|
|
2258
2322
|
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
2259
|
-
const { importCsv } = await import("./chunks/importCsv-
|
|
2323
|
+
const { importCsv } = await import("./chunks/importCsv-LVmMTaWU.mjs");
|
|
2260
2324
|
const report = await importCsv(ctx, {
|
|
2261
2325
|
object: objectId,
|
|
2262
2326
|
csv,
|
|
@@ -2305,16 +2369,16 @@ const exportCommand = {
|
|
|
2305
2369
|
const file2 = strOpt(options, "out");
|
|
2306
2370
|
if (!file2) usage("export zip needs --out <file.zip>.");
|
|
2307
2371
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
2308
|
-
const { exportBackupZip } = await import("./chunks/backup-
|
|
2372
|
+
const { exportBackupZip } = await import("./chunks/backup-W3CjgkAJ.mjs");
|
|
2309
2373
|
const { zip, report } = await exportBackupZip(ctx, { includeFiles: options["no-files"] !== true });
|
|
2310
|
-
|
|
2374
|
+
writeOutFile(file2, zip);
|
|
2311
2375
|
return { file: resolve(file2), zipBytes: zip.length, files: report.files, missing: report.missing, fileBytes: report.bytes };
|
|
2312
2376
|
}, (r) => `Wrote ${r.zipBytes} bytes (${r.files} file(s)${r.missing.length ? `, ${r.missing.length} missing` : ""}) to ${r.file}`);
|
|
2313
2377
|
}
|
|
2314
2378
|
const out = strOpt(options, "out");
|
|
2315
2379
|
const deliver = (text, extra) => {
|
|
2316
2380
|
if (out) {
|
|
2317
|
-
|
|
2381
|
+
writeOutFile(out, text);
|
|
2318
2382
|
return { file: resolve(out), bytes: Buffer.byteLength(text), ...extra };
|
|
2319
2383
|
}
|
|
2320
2384
|
return { ...extra, content: text };
|
|
@@ -2324,7 +2388,7 @@ const exportCommand = {
|
|
|
2324
2388
|
const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
|
|
2325
2389
|
const sorts = parseSorts(multiOpt(options, "sort"));
|
|
2326
2390
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
2327
|
-
const { exportCsv } = await import("./chunks/exportCsv-
|
|
2391
|
+
const { exportCsv } = await import("./chunks/exportCsv-C6e6XEmB.mjs");
|
|
2328
2392
|
const csv = exportCsv(ctx, objectId, {
|
|
2329
2393
|
viewId: strOpt(options, "view"),
|
|
2330
2394
|
filters,
|
|
@@ -2333,12 +2397,14 @@ const exportCommand = {
|
|
|
2333
2397
|
values: boolOpt(options, "raw") ? "raw" : "labels",
|
|
2334
2398
|
includeDeleted: boolOpt(options, "deleted")
|
|
2335
2399
|
});
|
|
2336
|
-
|
|
2400
|
+
const { default: Papa } = await import("./chunks/papaparse-Df7i1dHr.mjs").then((n) => n.p);
|
|
2401
|
+
const rows = Math.max(0, Papa.parse(csv, { skipEmptyLines: true }).data.length - 1);
|
|
2402
|
+
return deliver(csv, { object: objectId, rows });
|
|
2337
2403
|
}, (r) => "content" in r ? String(r.content) : `Wrote ${String(r.rows)} row(s) to ${String(r.file)}`);
|
|
2338
2404
|
}
|
|
2339
2405
|
if (kind === "json") {
|
|
2340
2406
|
return withSession(io, options, {}, async ({ ctx }) => {
|
|
2341
|
-
const { exportJson } = await import("./chunks/exportCsv-
|
|
2407
|
+
const { exportJson } = await import("./chunks/exportCsv-C6e6XEmB.mjs");
|
|
2342
2408
|
const data = exportJson(ctx, { collections: listOpt(options, "collections") });
|
|
2343
2409
|
const text = JSON.stringify(data, null, 2);
|
|
2344
2410
|
if (out) return deliver(text, {});
|
|
@@ -2374,6 +2440,7 @@ const reportCommand = {
|
|
|
2374
2440
|
async run({ io, options, positionals }) {
|
|
2375
2441
|
const name2 = positional(positionals, 0, "report name", "report <name>");
|
|
2376
2442
|
const internal = REPORT_ALIASES[name2] ?? name2;
|
|
2443
|
+
if (!Object.values(REPORT_ALIASES).includes(internal)) usage(`Unknown report "${name2}".`, `Reports: ${Object.keys(REPORT_ALIASES).join(", ")}.`);
|
|
2377
2444
|
const params = {
|
|
2378
2445
|
...jsonObjectOpt(options, "params") ?? {},
|
|
2379
2446
|
...numOpt(options, "months") !== void 0 ? { months: numOpt(options, "months") } : {},
|
|
@@ -2381,7 +2448,7 @@ const reportCommand = {
|
|
|
2381
2448
|
...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
|
|
2382
2449
|
...strOpt(options, "owner") ? { ownerId: strOpt(options, "owner") } : {}
|
|
2383
2450
|
};
|
|
2384
|
-
return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-
|
|
2451
|
+
return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-BACHGccj.mjs")).runReport(ctx, internal, params) }));
|
|
2385
2452
|
}
|
|
2386
2453
|
};
|
|
2387
2454
|
const statsCommand = {
|
|
@@ -2391,7 +2458,7 @@ const statsCommand = {
|
|
|
2391
2458
|
options: [SITE_OPTION, AS_OPTION],
|
|
2392
2459
|
async run({ io, options }) {
|
|
2393
2460
|
return withSession(io, options, {}, async ({ ctx, store }) => {
|
|
2394
|
-
const stats = (await import("./chunks/stats-
|
|
2461
|
+
const stats = (await import("./chunks/stats-BfuLWhVV.mjs")).getStats(ctx);
|
|
2395
2462
|
const journal = store.stats;
|
|
2396
2463
|
return { ...stats, ...journal ? { journal: await journal.call(store) } : {} };
|
|
2397
2464
|
}, (s) => [`backend: ${s.backend}`, ...s.objects.map((o) => ` ${o.label}: ${o.count}${o.deleted ? ` (+${o.deleted} deleted)` : ""}`), `open tasks: ${s.openTasks} (${s.overdueTasks} overdue)`, `users: ${s.users}`].join("\n"));
|
|
@@ -2405,7 +2472,7 @@ const compactCommand = {
|
|
|
2405
2472
|
async run({ io, options }) {
|
|
2406
2473
|
const apply = options.yes === true;
|
|
2407
2474
|
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
2408
|
-
const { compactData } = await import("./chunks/compact-
|
|
2475
|
+
const { compactData } = await import("./chunks/compact-BGHI92tm.mjs");
|
|
2409
2476
|
const report = await compactData(ctx, { olderThanDays: numOpt(options, "older-than"), keepRuns: numOpt(options, "keep-runs"), dryRun: !apply });
|
|
2410
2477
|
return apply ? report : { needsConfirmation: true, plan: report };
|
|
2411
2478
|
}, (r) => "plan" in r ? `Would purge ${r.plan.purgedRecords.length} record(s), ${r.plan.purgedActivities} activit(ies), ${r.plan.purgedRuns} run(s), ${r.plan.purgedFiles} file(s). Re-run with --yes once the user agrees.` : `Purged ${r.purgedRecords.length} record(s), ${r.purgedFiles} file(s); storage compacted: ${r.storageCompacted}`);
|
|
@@ -2426,19 +2493,19 @@ const automationsCommand = {
|
|
|
2426
2493
|
const [action = "list", id] = positionals;
|
|
2427
2494
|
switch (action) {
|
|
2428
2495
|
case "list":
|
|
2429
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/automations-
|
|
2496
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/automations-DwWruxmu.mjs")).listAutomations(ctx).map((a) => ({ id: a.id, name: a.name, enabled: a.enabled, object: a.object, trigger: a.trigger, actions: a.actions.map((x) => x.type), lastRunAt: a.lastRunAt ? new Date(a.lastRunAt).toISOString() : null })), (rows) => table(rows, ["id", "name", "enabled", "object", "lastRunAt"]));
|
|
2430
2497
|
case "enable":
|
|
2431
2498
|
case "disable": {
|
|
2432
2499
|
if (!id) usage(`automations ${action} needs an id.`);
|
|
2433
2500
|
return withSession(io, options, { lock: true }, async ({ ctx }) => {
|
|
2434
|
-
const mod = await import("./chunks/automations-
|
|
2501
|
+
const mod = await import("./chunks/automations-DwWruxmu.mjs");
|
|
2435
2502
|
return mod.setAutomationEnabled(ctx, id, action === "enable");
|
|
2436
2503
|
});
|
|
2437
2504
|
}
|
|
2438
2505
|
case "run": {
|
|
2439
2506
|
const apply = options.yes === true;
|
|
2440
2507
|
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
2441
|
-
const { runScheduled } = await import("./chunks/automations-
|
|
2508
|
+
const { runScheduled } = await import("./chunks/automations-DwWruxmu.mjs");
|
|
2442
2509
|
const report = await runScheduled(ctx, { dryRun: !apply, automationId: strOpt(options, "id"), force: boolOpt(options, "force") });
|
|
2443
2510
|
return apply ? report : { needsConfirmation: true, ...report };
|
|
2444
2511
|
}, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}ran ${r.ran}, skipped ${r.skipped}, errors ${r.errors}`);
|
|
@@ -2462,11 +2529,11 @@ const formsCommand = {
|
|
|
2462
2529
|
const [action = "list", id] = positionals;
|
|
2463
2530
|
switch (action) {
|
|
2464
2531
|
case "list":
|
|
2465
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-
|
|
2532
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-BfuWaQjA.mjs")).listForms(ctx), (fs) => table(fs, ["id", "name", "object", "publishedPath"]));
|
|
2466
2533
|
case "publish": {
|
|
2467
2534
|
if (!id) usage("forms publish needs a form id.");
|
|
2468
2535
|
return withSession(io, options, { lock: options.yes === true }, async (s) => {
|
|
2469
|
-
const forms = await import("./chunks/forms-
|
|
2536
|
+
const forms = await import("./chunks/forms-BfuWaQjA.mjs");
|
|
2470
2537
|
const built = await forms.buildFormHtml(s.ctx, id);
|
|
2471
2538
|
const target = s.hostingTarget();
|
|
2472
2539
|
if (!target) usage(`${s.site.alias} has no hosting to publish the form to.`);
|
|
@@ -2480,7 +2547,7 @@ const formsCommand = {
|
|
|
2480
2547
|
case "ingest": {
|
|
2481
2548
|
const apply = options.yes === true;
|
|
2482
2549
|
return withSession(io, options, { lock: apply }, async ({ ctx }) => {
|
|
2483
|
-
const { ingestInbox } = await import("./chunks/forms-
|
|
2550
|
+
const { ingestInbox } = await import("./chunks/forms-BfuWaQjA.mjs");
|
|
2484
2551
|
const report = await ingestInbox(ctx, strOpt(options, "form"), { dryRun: !apply, limit: numOpt(options, "limit") });
|
|
2485
2552
|
return apply ? report : { needsConfirmation: true, ...report };
|
|
2486
2553
|
}, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}processed ${r.processed}: ${r.created} created, ${r.updated} updated, ${r.failed} failed`);
|
|
@@ -2549,6 +2616,31 @@ async function fetchHostedAdmin(adminUrl, fetchImpl = fetch) {
|
|
|
2549
2616
|
return { version: null, dataSchema: null, error: "version.json is not valid JSON" };
|
|
2550
2617
|
}
|
|
2551
2618
|
}
|
|
2619
|
+
function localAdminFolder(hosting) {
|
|
2620
|
+
if (!hosting || hosting.connectorId !== "local-folder") return null;
|
|
2621
|
+
const root = typeof hosting.settings.path === "string" ? hosting.settings.path : "";
|
|
2622
|
+
if (!root) return null;
|
|
2623
|
+
return join(root, hosting.folder ?? "admin");
|
|
2624
|
+
}
|
|
2625
|
+
function readLocalAdmin(folder) {
|
|
2626
|
+
for (const name2 of ["version.json", "admin-files.json"]) {
|
|
2627
|
+
const file2 = join(folder, name2);
|
|
2628
|
+
if (!existsSync(file2)) continue;
|
|
2629
|
+
try {
|
|
2630
|
+
const m = JSON.parse(readFileSync(file2, "utf8"));
|
|
2631
|
+
return { version: m.version ?? null, dataSchema: m.dataSchema ?? null };
|
|
2632
|
+
} catch {
|
|
2633
|
+
return { version: null, dataSchema: null, error: `${name2} is not valid JSON` };
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return { version: null, dataSchema: null, error: `no version.json in ${folder}` };
|
|
2637
|
+
}
|
|
2638
|
+
async function siteAdminVersion(site) {
|
|
2639
|
+
if (site.adminUrl) return { ...await fetchHostedAdmin(site.adminUrl), source: "url" };
|
|
2640
|
+
const folder = localAdminFolder(site.hosting);
|
|
2641
|
+
if (folder) return { ...readLocalAdmin(folder), source: "folder" };
|
|
2642
|
+
return { version: null, dataSchema: null, source: "none" };
|
|
2643
|
+
}
|
|
2552
2644
|
function readChangelog() {
|
|
2553
2645
|
try {
|
|
2554
2646
|
const data = JSON.parse(readFileSync(join(packageRoot(), "changelog.json"), "utf8"));
|
|
@@ -2596,7 +2688,7 @@ const deployAdminCommand = {
|
|
|
2596
2688
|
const plans = [];
|
|
2597
2689
|
for (const site of sites) {
|
|
2598
2690
|
if (!site.hosting) throw new CliError("not-hosted", `${site.alias} has no hosting recorded.`, "Register it with `site add --admin-url …` from a CRM whose admin is hosted, or reinstall with a hosting.");
|
|
2599
|
-
const hosted =
|
|
2691
|
+
const hosted = await siteAdminVersion(site);
|
|
2600
2692
|
const plan = {
|
|
2601
2693
|
alias: site.alias,
|
|
2602
2694
|
adminUrl: site.adminUrl,
|
|
@@ -2695,7 +2787,7 @@ async function runDoctor(site, opts = {}) {
|
|
|
2695
2787
|
installBrowserEnv();
|
|
2696
2788
|
try {
|
|
2697
2789
|
if (site.runtime.backend === "journal" && site.runtime.journal.connectorId === "git-pages") {
|
|
2698
|
-
const { probeTarget } = await import("./chunks/install-
|
|
2790
|
+
const { probeTarget } = await import("./chunks/install-CbpD_7gd.mjs");
|
|
2699
2791
|
const { dataTargetOf: dataTargetOf2 } = await Promise.resolve().then(() => session);
|
|
2700
2792
|
const probe = await probeTarget(dataTargetOf2(site), { requireSiteUrl: false, requirePrivate: true });
|
|
2701
2793
|
add(probe.ok ? { id: "data-repo", status: "ok", message: probe.detail } : { id: "data-repo", status: "fail", message: probe.detail, fix: "Make the data repository private and check the token's Contents permission." });
|
|
@@ -2712,7 +2804,7 @@ async function runDoctor(site, opts = {}) {
|
|
|
2712
2804
|
if (!settingsDoc(store)) missing.push("settings");
|
|
2713
2805
|
if (!usersOf(store).some((u) => u.role === "admin" && !u.disabled)) missing.push("an active admin user");
|
|
2714
2806
|
if (missing.length && opts.repair) {
|
|
2715
|
-
const { seedCrm } = await import("./chunks/seed-
|
|
2807
|
+
const { seedCrm } = await import("./chunks/seed-6MRmQQBR.mjs");
|
|
2716
2808
|
repaired = await seedCrm(session$1.system, { admin: site.user ? { name: site.user.split("@")[0], email: site.user } : void 0 });
|
|
2717
2809
|
add({ id: "dataset", status: "ok", message: `Repaired: seeded ${missing.join(", ")}.` });
|
|
2718
2810
|
} else {
|
|
@@ -2740,26 +2832,40 @@ async function runDoctor(site, opts = {}) {
|
|
|
2740
2832
|
add({ id: "data-access", status: "fail", message: e.message, fix: e.hint });
|
|
2741
2833
|
}
|
|
2742
2834
|
const adminUrl = site.adminUrl;
|
|
2835
|
+
const adminFolder = adminUrl ? null : localAdminFolder(site.hosting);
|
|
2743
2836
|
let origin = null;
|
|
2744
|
-
if (!adminUrl) {
|
|
2837
|
+
if (!adminUrl && !adminFolder) {
|
|
2745
2838
|
add({ id: "admin", status: "skip", message: "Admin not hosted (use `agentcrm serve`)." });
|
|
2746
2839
|
} else {
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2840
|
+
if (adminUrl) {
|
|
2841
|
+
origin = new URL(adminUrl).origin;
|
|
2842
|
+
add(
|
|
2843
|
+
adminUrl.startsWith("https://") || /^http:\/\/(localhost|127\.0\.0\.1)/.test(adminUrl) ? { id: "https", status: "ok", message: "Admin served over HTTPS (or localhost)." } : { id: "https", status: "fail", message: `${adminUrl} is plain HTTP: browsers refuse WebCrypto (signing, encryption) there.`, fix: "Serve the admin over HTTPS (CDN / custom domain with TLS, the bucket's https endpoint, GitHub Pages)." }
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
const read = async (name2) => {
|
|
2847
|
+
if (adminUrl) {
|
|
2848
|
+
const res = await fetchText(`${adminUrl}${name2}`);
|
|
2849
|
+
return { ok: res.ok, text: res.text, error: res.error ?? (res.ok ? void 0 : `HTTP ${res.status}`) };
|
|
2850
|
+
}
|
|
2851
|
+
const file2 = join(adminFolder, name2);
|
|
2852
|
+
if (!existsSync(file2)) return { ok: false, text: "", error: `${file2} not found` };
|
|
2853
|
+
return { ok: true, text: readFileSync(file2, "utf8") };
|
|
2854
|
+
};
|
|
2855
|
+
const index = await read("index.html");
|
|
2752
2856
|
if (!index.ok) {
|
|
2753
|
-
add({ id: "admin", status: "fail", message: `Admin page not
|
|
2857
|
+
add({ id: "admin", status: "fail", message: `Admin page not ${adminUrl ? "served" : "found"} (${index.error}).`, fix: `\`agentcrm deploy-admin --site ${site.alias} --yes\`${adminUrl ? " (Pages hosts take a minute after a commit)" : ""}.` });
|
|
2754
2858
|
} else {
|
|
2755
|
-
add(
|
|
2756
|
-
|
|
2859
|
+
add(
|
|
2860
|
+
adminUrl ? { id: "admin", status: "ok", message: `Admin served at ${adminUrl}.` } : { id: "admin", status: "ok", message: `Admin in the folder ${adminFolder} (no public URL): open it with \`agentcrm serve --dir ${adminFolder}\`.` }
|
|
2861
|
+
);
|
|
2862
|
+
const hosted = adminUrl ? await fetchHostedAdmin(adminUrl) : readLocalAdmin(adminFolder);
|
|
2757
2863
|
add(
|
|
2758
2864
|
!hosted.version ? { id: "admin-version", status: "warn", message: "version.json missing next to the admin.", fix: `\`agentcrm deploy-admin --site ${site.alias} --yes\`.` } : compareVersions(hosted.version, info.version) >= 0 ? { id: "admin-version", status: "ok", message: `Admin ${hosted.version} is up to date.` } : { id: "admin-version", status: "warn", message: `Admin ${hosted.version} is older than ${info.version}.`, fix: `Offer \`agentcrm deploy-admin --site ${site.alias}\`.` }
|
|
2759
2865
|
);
|
|
2760
|
-
const cfg = await
|
|
2866
|
+
const cfg = await read("config.js");
|
|
2761
2867
|
try {
|
|
2762
|
-
if (!cfg.ok) throw new Error(cfg.error
|
|
2868
|
+
if (!cfg.ok) throw new Error(cfg.error);
|
|
2763
2869
|
const runtime = parseConfigJs(cfg.text);
|
|
2764
2870
|
const secrets = findSecretKeys(runtime);
|
|
2765
2871
|
if (secrets.length) add({ id: "config-js", status: "fail", message: `config.js publishes secrets: ${secrets.join(", ")}.`, fix: "Revoke those keys NOW, then redeploy the admin (config.js is regenerated without secrets)." });
|
|
@@ -2823,7 +2929,7 @@ function readPlan(source) {
|
|
|
2823
2929
|
try {
|
|
2824
2930
|
return JSON.parse(text);
|
|
2825
2931
|
} catch (err) {
|
|
2826
|
-
throw new CliError("invalid-plan", `The plan is not valid JSON: ${err.message}`,
|
|
2932
|
+
throw new CliError("invalid-plan", `The plan is not valid JSON: ${err.message}`, "Write the plan as one JSON object (`agentcrm options --json` documents every field).", 2);
|
|
2827
2933
|
}
|
|
2828
2934
|
}
|
|
2829
2935
|
const installCommand = {
|
|
@@ -2838,10 +2944,10 @@ const installCommand = {
|
|
|
2838
2944
|
const planPath = strOpt(options, "plan");
|
|
2839
2945
|
if (!planPath) usage("install needs --plan <file|->.", "Run `agentcrm options` for the format.");
|
|
2840
2946
|
installBrowserEnv();
|
|
2841
|
-
const { resolvePlan } = await import("./chunks/plan-
|
|
2947
|
+
const { resolvePlan } = await import("./chunks/plan-BmTat6-T.mjs");
|
|
2842
2948
|
const plan = resolvePlan(readPlan(planPath));
|
|
2843
2949
|
if (getSite(plan.alias)) throw new CliError("site-exists", `A site named "${plan.alias}" is already registered.`, "Use another alias, or `agentcrm site forget` it first.");
|
|
2844
|
-
const { checkPlan, runInstall } = await import("./chunks/install-
|
|
2950
|
+
const { checkPlan, runInstall } = await import("./chunks/install-CbpD_7gd.mjs");
|
|
2845
2951
|
io.err("Checking access…");
|
|
2846
2952
|
const checks = await checkPlan(plan);
|
|
2847
2953
|
const summary = {
|
|
@@ -2861,7 +2967,7 @@ const installCommand = {
|
|
|
2861
2967
|
throw new CliError(
|
|
2862
2968
|
"check-failed",
|
|
2863
2969
|
[!checks.data.ok && `Data: ${checks.data.detail}`, checks.hosting && !checks.hosting.ok && `Hosting: ${checks.hosting.detail}`].filter(Boolean).join(" — "),
|
|
2864
|
-
"Fix the access (key / token permissions, bucket / repo names, URL, CORS) and retry the dry run."
|
|
2970
|
+
checks.data.existing && !checks.data.ok ? `Reuse it: the user stores its passphrase (\`agentcrm credentials set ${plan.passphraseAccount ?? `passphrase:${plan.alias}`}\` in their own terminal, or --from-env), then add "encryption": { "credential": "${plan.passphraseAccount ?? `passphrase:${plan.alias}`}" } to the plan's data and retry the dry run. Or pick another folder / root for a new CRM.` : "Fix the access (key / token permissions, bucket / repo names, URL, CORS) and retry the dry run."
|
|
2865
2971
|
);
|
|
2866
2972
|
}
|
|
2867
2973
|
if (options["dry-run"] === true) {
|
|
@@ -2887,6 +2993,8 @@ const installCommand = {
|
|
|
2887
2993
|
});
|
|
2888
2994
|
const nextSteps = [];
|
|
2889
2995
|
if (plan.adminUrl) nextSteps.push(`Open the admin: ${plan.adminUrl}`);
|
|
2996
|
+
else if (localAdminFolder(plan.hosting)) nextSteps.push(`The admin is in ${localAdminFolder(plan.hosting)} (no public URL): open it with \`agentcrm serve --dir ${localAdminFolder(plan.hosting)}\`, or serve that folder over HTTPS.`);
|
|
2997
|
+
else if (plan.hosting) nextSteps.push(`The admin was uploaded to the ${plan.hosting.type} hosting; its public URL is unknown (add siteUrl to the plan's hosting settings next time).`);
|
|
2890
2998
|
else nextSteps.push("The admin was not uploaded: run `agentcrm serve` to use it on this computer.");
|
|
2891
2999
|
if (plan.backend === "journal") {
|
|
2892
3000
|
nextSteps.push(`Each new browser asks once for the data location keys${plan.passphraseAccount ? " and the passphrase" : ""} (they stay in that browser).`);
|
|
@@ -3136,9 +3244,10 @@ const versionCommand = {
|
|
|
3136
3244
|
}
|
|
3137
3245
|
};
|
|
3138
3246
|
async function siteStatus(site, version2, dataSchema) {
|
|
3139
|
-
const
|
|
3140
|
-
|
|
3141
|
-
const hosted = await
|
|
3247
|
+
const folder = site.adminUrl ? null : localAdminFolder(site.hosting);
|
|
3248
|
+
const base = { alias: site.alias, adminUrl: site.adminUrl, ...folder ? { adminFolder: folder } : {} };
|
|
3249
|
+
const hosted = await siteAdminVersion(site);
|
|
3250
|
+
if (hosted.source === "none") return { ...base, adminVersion: null, state: "not-hosted" };
|
|
3142
3251
|
if (!hosted.version) return { ...base, adminVersion: null, state: "unreachable", error: hosted.error };
|
|
3143
3252
|
if (hosted.version !== site.adminVersion) putSite({ ...site, adminVersion: hosted.version });
|
|
3144
3253
|
const cmp = compareVersions(hosted.version, version2);
|
|
@@ -3199,7 +3308,7 @@ const optionsCommand = {
|
|
|
3199
3308
|
printResult(io, { email: rulesEmail, rules, where: "Firebase console → Firestore Database → Rules → paste → Publish (or `firebase deploy --only firestore:rules`)." }, () => rules);
|
|
3200
3309
|
return;
|
|
3201
3310
|
}
|
|
3202
|
-
const { PLAN_EXAMPLE } = await import("./chunks/plan-
|
|
3311
|
+
const { PLAN_EXAMPLE } = await import("./chunks/plan-BmTat6-T.mjs");
|
|
3203
3312
|
const data = {
|
|
3204
3313
|
order: ["data backend", "data location", "admin hosting"],
|
|
3205
3314
|
backends: DATA_BACKENDS,
|
|
@@ -3272,13 +3381,13 @@ const objectsCommand = {
|
|
|
3272
3381
|
switch (action) {
|
|
3273
3382
|
case "list":
|
|
3274
3383
|
return withSession(io, options, {}, async ({ ctx, store }) => {
|
|
3275
|
-
const { listObjects } = await import("./chunks/schema-
|
|
3384
|
+
const { listObjects } = await import("./chunks/schema-C4lSslXv.mjs");
|
|
3276
3385
|
const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
|
|
3277
3386
|
return listObjects(ctx).map((o) => ({ id: o.id, labelSingular: o.labelSingular, labelPlural: o.labelPlural, standard: o.standard, labelField: o.labelField, fields: o.fields.length, records: recordsOf(store, o.id).length }));
|
|
3278
3387
|
}, (rows) => table(rows, ["id", "labelPlural", "fields", "records", "standard"]));
|
|
3279
3388
|
case "get": {
|
|
3280
3389
|
const objectId = positional(positionals, 1, "object id", "objects get <id>");
|
|
3281
|
-
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-
|
|
3390
|
+
return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).describeSchema(ctx, objectId)[0]);
|
|
3282
3391
|
}
|
|
3283
3392
|
case "create": {
|
|
3284
3393
|
const data = jsonObjectOpt(options, "data") ?? {};
|
|
@@ -3291,7 +3400,7 @@ const objectsCommand = {
|
|
|
3291
3400
|
...strOpt(options, "color") ? { color: strOpt(options, "color") } : {}
|
|
3292
3401
|
};
|
|
3293
3402
|
if (!input.labelSingular || !input.labelPlural) usage("objects create needs --singular and --plural (or --data).");
|
|
3294
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-
|
|
3403
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).createObject(ctx, input));
|
|
3295
3404
|
}
|
|
3296
3405
|
case "update": {
|
|
3297
3406
|
const objectId = positional(positionals, 1, "object id", "objects update <id> …");
|
|
@@ -3303,12 +3412,12 @@ const objectsCommand = {
|
|
|
3303
3412
|
...strOpt(options, "label-field") ? { labelField: strOpt(options, "label-field") } : {}
|
|
3304
3413
|
};
|
|
3305
3414
|
if (!Object.keys(patch).length) usage("objects update needs --singular, --plural, --icon, --color or --label-field.");
|
|
3306
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-
|
|
3415
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).updateObject(ctx, objectId, patch));
|
|
3307
3416
|
}
|
|
3308
3417
|
case "delete": {
|
|
3309
3418
|
if (!id) usage("objects delete needs an object id.");
|
|
3310
3419
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
|
|
3311
|
-
const { getObject, deleteObject } = await import("./chunks/schema-
|
|
3420
|
+
const { getObject, deleteObject } = await import("./chunks/schema-C4lSslXv.mjs");
|
|
3312
3421
|
const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
|
|
3313
3422
|
const o = getObject(ctx, id);
|
|
3314
3423
|
const plan = { object: o.id, label: o.labelPlural, standard: o.standard, recordsDeleted: recordsOf(store, o.id, { includeDeleted: true }).length };
|
|
@@ -3366,7 +3475,7 @@ const fieldsCommand = {
|
|
|
3366
3475
|
...boolOpt(options, "unique") ? { unique: true } : {},
|
|
3367
3476
|
...strOpt(options, "description") ? { description: strOpt(options, "description") } : {}
|
|
3368
3477
|
};
|
|
3369
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-
|
|
3478
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).createField(ctx, objectId, input));
|
|
3370
3479
|
}
|
|
3371
3480
|
case "update": {
|
|
3372
3481
|
if (!fieldId) usage("fields update needs a field id.");
|
|
@@ -3383,12 +3492,12 @@ const fieldsCommand = {
|
|
|
3383
3492
|
...strOpt(options, "description") !== void 0 ? { description: strOpt(options, "description") } : {}
|
|
3384
3493
|
};
|
|
3385
3494
|
if (!Object.keys(patch).length) usage("fields update needs at least one change.");
|
|
3386
|
-
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-
|
|
3495
|
+
return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).updateField(ctx, objectId, fieldId, patch));
|
|
3387
3496
|
}
|
|
3388
3497
|
case "delete": {
|
|
3389
3498
|
if (!fieldId) usage("fields delete needs a field id.");
|
|
3390
3499
|
return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
|
|
3391
|
-
const { getObject, deleteField } = await import("./chunks/schema-
|
|
3500
|
+
const { getObject, deleteField } = await import("./chunks/schema-C4lSslXv.mjs");
|
|
3392
3501
|
const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
|
|
3393
3502
|
const field = getObject(ctx, objectId).fields.find((f) => f.id === fieldId);
|
|
3394
3503
|
if (!field) usage(`No field "${fieldId}" on ${objectId}.`, `\`agentcrm objects get ${objectId}\` lists them.`);
|
|
@@ -3695,7 +3804,7 @@ const toolsCommand = {
|
|
|
3695
3804
|
async run({ io, options, positionals }) {
|
|
3696
3805
|
const [action = "list"] = positionals;
|
|
3697
3806
|
if (action === "list") {
|
|
3698
|
-
const { listAiTools, AI_TOOLS } = await import("./chunks/tools-
|
|
3807
|
+
const { listAiTools, AI_TOOLS } = await import("./chunks/tools-iSH-yMpl.mjs");
|
|
3699
3808
|
const builtin = new Set(AI_TOOLS.map((t) => t.name));
|
|
3700
3809
|
const shape = () => listAiTools().map((t) => ({ name: t.name, description: t.description, mutating: t.mutating, source: builtin.has(t.name) ? "builtin" : "extension", inputSchema: t.inputSchema }));
|
|
3701
3810
|
const human = (r) => table(r.tools, ["name", "mutating", "source", "description"]);
|
|
@@ -3715,7 +3824,7 @@ const toolsCommand = {
|
|
|
3715
3824
|
}
|
|
3716
3825
|
if (action === "call") {
|
|
3717
3826
|
const name2 = positional(positionals, 1, "tool name", "tools call <name> --input <json>");
|
|
3718
|
-
const { findTool, callTool, listAiTools } = await import("./chunks/tools-
|
|
3827
|
+
const { findTool, callTool, listAiTools } = await import("./chunks/tools-iSH-yMpl.mjs");
|
|
3719
3828
|
const input = jsonObjectOpt(options, "input") ?? {};
|
|
3720
3829
|
const builtin = findTool(name2);
|
|
3721
3830
|
return withSession(io, options, { lock: builtin ? builtin.mutating : true }, async ({ ctx }) => {
|
|
@@ -3737,8 +3846,8 @@ const mcpCommand = {
|
|
|
3737
3846
|
const log = (msg) => process.stderr.write(`${msg}
|
|
3738
3847
|
`);
|
|
3739
3848
|
for (const w of session2.warnings) log(`[agentcrm mcp] note: ${w}`);
|
|
3740
|
-
const { listAiTools } = await import("./chunks/tools-
|
|
3741
|
-
const { createMcpServer, serveStdio } = await import("./chunks/server-
|
|
3849
|
+
const { listAiTools } = await import("./chunks/tools-iSH-yMpl.mjs");
|
|
3850
|
+
const { createMcpServer, serveStdio } = await import("./chunks/server-DC3t8r30.mjs");
|
|
3742
3851
|
let lastRefresh = Date.now();
|
|
3743
3852
|
const server = createMcpServer({
|
|
3744
3853
|
readOnly,
|
|
@@ -3761,7 +3870,10 @@ const mcpCommand = {
|
|
|
3761
3870
|
async withWrite(fn) {
|
|
3762
3871
|
const unlock = lockSite(session2.site.alias);
|
|
3763
3872
|
try {
|
|
3764
|
-
|
|
3873
|
+
const result = await fn();
|
|
3874
|
+
const report = await session2.extensions.sync();
|
|
3875
|
+
for (const f of report?.failed ?? []) log(`[agentcrm mcp] extension ${f.id} failed to load: ${f.error}`);
|
|
3876
|
+
return result;
|
|
3765
3877
|
} finally {
|
|
3766
3878
|
unlock();
|
|
3767
3879
|
}
|
|
@@ -3866,7 +3978,11 @@ async function main(argv, io) {
|
|
|
3866
3978
|
const json = argv.includes("--json");
|
|
3867
3979
|
const out = createIo(json);
|
|
3868
3980
|
try {
|
|
3869
|
-
const
|
|
3981
|
+
const args = argv.filter((a) => a !== "--json");
|
|
3982
|
+
const leading = [];
|
|
3983
|
+
while (args[0] && LEADING_GLOBAL_FLAGS.has(args[0])) leading.push(args.shift());
|
|
3984
|
+
const [name2 = "help", ...rest] = args;
|
|
3985
|
+
rest.push(...leading);
|
|
3870
3986
|
if (name2 === "--help" || name2 === "-h") return await run("help", rest, out) ?? 0;
|
|
3871
3987
|
if (name2 === "--version" || name2 === "-v") return await run("version", rest, out) ?? 0;
|
|
3872
3988
|
return await run(name2, rest, out) ?? 0;
|
|
@@ -3874,6 +3990,7 @@ async function main(argv, io) {
|
|
|
3874
3990
|
return printError(out, error);
|
|
3875
3991
|
}
|
|
3876
3992
|
}
|
|
3993
|
+
const LEADING_GLOBAL_FLAGS = new Set(GLOBAL_OPTIONS.filter((o) => o.type === "boolean" && o.name !== "help").map((o) => `--${o.name}`));
|
|
3877
3994
|
async function run(name2, args, io) {
|
|
3878
3995
|
const command = findCommand(name2);
|
|
3879
3996
|
if (!command) {
|
|
@@ -3891,13 +4008,21 @@ async function run(name2, args, io) {
|
|
|
3891
4008
|
throw new CliError("usage", e.message, `Usage: agentcrm ${command.usage}`, 2);
|
|
3892
4009
|
}
|
|
3893
4010
|
if (parsed.values.help && command.name !== "help") return run("help", [command.name], io);
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
4011
|
+
try {
|
|
4012
|
+
return await command.run({
|
|
4013
|
+
io,
|
|
4014
|
+
options: parsed.values,
|
|
4015
|
+
positionals: parsed.positionals
|
|
4016
|
+
});
|
|
4017
|
+
} catch (e) {
|
|
4018
|
+
if (e instanceof CliError && !e.hint && e.exitCode === 2) {
|
|
4019
|
+
throw new CliError(e.code, e.message, `Usage: agentcrm ${command.usage} (details: \`agentcrm help ${command.name}\`).`, e.exitCode);
|
|
4020
|
+
}
|
|
4021
|
+
throw e;
|
|
4022
|
+
}
|
|
3899
4023
|
}
|
|
3900
4024
|
installBrowserEnv();
|
|
4025
|
+
routeConsoleToStderr();
|
|
3901
4026
|
main(process.argv.slice(2)).then(
|
|
3902
4027
|
(code) => process.exit(code),
|
|
3903
4028
|
(err) => {
|
|
@@ -3913,11 +4038,11 @@ export {
|
|
|
3913
4038
|
LOCATIONS as L,
|
|
3914
4039
|
PACKAGE_NAME as P,
|
|
3915
4040
|
USER_ROLES as U,
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
4041
|
+
FIELD_TYPES as a,
|
|
4042
|
+
parseAccount as b,
|
|
4043
|
+
requireCredential as c,
|
|
4044
|
+
findLocation as d,
|
|
4045
|
+
findHosting as e,
|
|
3921
4046
|
findBackend as f,
|
|
3922
4047
|
getCredential as g,
|
|
3923
4048
|
openStore as h,
|