@fleox-agent-crm/agentcrm 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +143 -0
- package/README.md +223 -2
- package/changelog.json +57 -0
- package/dist/admin/admin-files.json +26 -0
- package/dist/admin/assets/chartsImpl-CE2akJat.js +91 -0
- package/dist/admin/assets/firebase-BHSUggRo.js +2 -0
- package/dist/admin/assets/gitJson-BulGh2KV.js +3 -0
- package/dist/admin/assets/index-DidVVK7L.js +1181 -0
- package/dist/admin/assets/index-hMXwHSvN.css +1 -0
- package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
- package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
- package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
- package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
- package/dist/admin/assets/lucide-react.min-DEDlHCPj.js +6 -0
- package/dist/admin/assets/sdk-CnrLnlRo.js +1717 -0
- package/dist/admin/config.js +3 -0
- package/dist/admin/favicon.svg +1 -0
- package/dist/admin/index.html +139 -0
- package/dist/admin/runtime/agentcrm-runtime.js +104 -0
- package/dist/admin/runtime/lucide-react.js +791 -0
- package/dist/admin/runtime/react-dom-client.js +17 -0
- package/dist/admin/runtime/react-dom.js +20 -0
- package/dist/admin/runtime/react-i18next.js +19 -0
- package/dist/admin/runtime/react-jsx-runtime.js +17 -0
- package/dist/admin/runtime/react.js +22 -0
- package/dist/agent/agentcrm.mjs +3932 -0
- package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
- package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
- package/dist/agent/chunks/api-B6OqNVWp.mjs +97 -0
- package/dist/agent/chunks/appearance-BaShelzt.mjs +174 -0
- package/dist/agent/chunks/automations-BWrvLqP6.mjs +698 -0
- package/dist/agent/chunks/backup-ClSmxmFZ.mjs +103 -0
- package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
- package/dist/agent/chunks/compact-CsFPaAAq.mjs +69 -0
- package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
- package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
- package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
- package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
- package/dist/agent/chunks/exportCsv-B5hRvkNm.mjs +95 -0
- package/dist/agent/chunks/extCheck--7lmVvl7.mjs +193 -0
- package/dist/agent/chunks/extPackage-BGl5Oqs8.mjs +115 -0
- package/dist/agent/chunks/extScaffold-DqxI7R1e.mjs +320 -0
- package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
- package/dist/agent/chunks/extensions-BP1k60md.mjs +151 -0
- package/dist/agent/chunks/featureRegistry-C9nbVShp.mjs +149 -0
- package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
- package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
- package/dist/agent/chunks/files-q8Xu5bft.mjs +367 -0
- package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
- package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
- package/dist/agent/chunks/forms-BKU5F9WF.mjs +847 -0
- package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
- package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
- package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
- package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
- package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
- package/dist/agent/chunks/importCsv-DmE_sBch.mjs +334 -0
- package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
- package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
- package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
- package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
- package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
- package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
- package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
- package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
- package/dist/agent/chunks/install-CpHoUFtL.mjs +188 -0
- package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
- package/dist/agent/chunks/loader-C9nm3iwx.mjs +184 -0
- package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
- package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
- package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
- package/dist/agent/chunks/modules-CqdVQVJb.mjs +177 -0
- package/dist/agent/chunks/nodeImport-BPCt5uMu.mjs +38 -0
- package/dist/agent/chunks/package-DWbn3t2R.mjs +194 -0
- package/dist/agent/chunks/papaparse-DjBYnQM2.mjs +1285 -0
- package/dist/agent/chunks/pipelines-DCdRBNBV.mjs +234 -0
- package/dist/agent/chunks/plan-C0TBrU92.mjs +254 -0
- package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
- package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
- package/dist/agent/chunks/records-DoqiT9xR.mjs +2191 -0
- package/dist/agent/chunks/registry-Ceexawxl.mjs +182 -0
- package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
- package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
- package/dist/agent/chunks/reports-BB59N6ke.mjs +189 -0
- package/dist/agent/chunks/runtime-DHo7ZEEO.mjs +14852 -0
- package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
- package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
- package/dist/agent/chunks/schema-7-4-qtSS.mjs +203 -0
- package/dist/agent/chunks/schema-DlHt2PxI.mjs +367 -0
- package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
- package/dist/agent/chunks/seed-CugPGYZL.mjs +523 -0
- package/dist/agent/chunks/server-ISwlDRCN.mjs +242 -0
- package/dist/agent/chunks/settings-BtvR_jdm.mjs +76 -0
- package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
- package/dist/agent/chunks/stats-CfeJssSS.mjs +51 -0
- package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
- package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
- package/dist/agent/chunks/tools-Bew8lxKx.mjs +844 -0
- package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
- package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
- package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
- package/dist/agent/chunks/views-fO_BM5U_.mjs +109 -0
- package/dist/version.json +7 -0
- package/package.json +97 -4
- package/plugin/.claude-plugin/plugin.json +17 -0
- package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
- package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
- package/plugin/skills/agentcrm-admin/references/import.md +16 -0
- package/plugin/skills/agentcrm-admin/references/records.md +30 -0
- package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
- package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
- package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
- package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
- package/plugin/skills/agentcrm-extensions/references/testing.md +76 -0
- package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
- package/plugin/skills/agentcrm-install/SKILL.md +116 -0
- package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
- package/plugin/skills/agentcrm-install/references/github.md +20 -0
- package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
- package/plugin/skills/agentcrm-install/references/plans.md +45 -0
- package/plugin/skills/agentcrm-install/references/s3.md +24 -0
- package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
- package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
- package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
- package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
- package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
- package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
- package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
- package/types/agentcrm-runtime.d.ts +894 -0
|
@@ -0,0 +1,242 @@
|
|
|
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 { t as toCliError } from "../agentcrm.mjs";
|
|
8
|
+
import "node:util";
|
|
9
|
+
import "node:fs";
|
|
10
|
+
import "node:path";
|
|
11
|
+
import "node:url";
|
|
12
|
+
import "node:crypto";
|
|
13
|
+
import "node:os";
|
|
14
|
+
import "node:child_process";
|
|
15
|
+
import "node:http";
|
|
16
|
+
const SUPPORTED_PROTOCOLS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
17
|
+
const LATEST_PROTOCOL = SUPPORTED_PROTOCOLS[0];
|
|
18
|
+
const RPC = { parse: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602, internal: -32603 };
|
|
19
|
+
const DESTRUCTIVE = /* @__PURE__ */ new Set(["delete_record", "merge_records", "import_csv", "update_record"]);
|
|
20
|
+
const IDEMPOTENT = /* @__PURE__ */ new Set(["update_record", "move_deal_stage", "complete_task"]);
|
|
21
|
+
const PROMPTS = [
|
|
22
|
+
{
|
|
23
|
+
name: "pipeline_review",
|
|
24
|
+
title: "Pipeline review",
|
|
25
|
+
description: "Review the sales pipeline: totals by stage, forecast, stale deals and the next best action per deal.",
|
|
26
|
+
arguments: [
|
|
27
|
+
{ name: "pipelineId", description: "Pipeline id (default: the main one).", required: false },
|
|
28
|
+
{ name: "ownerId", description: "Only this owner's deals.", required: false }
|
|
29
|
+
],
|
|
30
|
+
render: (a) => [
|
|
31
|
+
"Review my sales pipeline in the CRM.",
|
|
32
|
+
`1. Call pipeline_summary${a.pipelineId || a.ownerId ? ` with ${JSON.stringify({ ...a.pipelineId ? { pipelineId: a.pipelineId } : {}, ...a.ownerId ? { ownerId: a.ownerId } : {} })}` : ""} for totals per stage and the weighted forecast.`,
|
|
33
|
+
'2. Call run_report with { "name": "staleDeals" } to find open deals without recent activity.',
|
|
34
|
+
"3. For the 5 largest open deals, call get_record (and list_activities) to read their latest activity and open tasks.",
|
|
35
|
+
"4. Answer with: a short summary (open amount, weighted amount, what changed), a table of deals at risk (stale, close date passed, no next step), and ONE concrete next best action per deal (call, email, meeting, update the close date…).",
|
|
36
|
+
"Do not change anything in the CRM unless I confirm; offer to create the follow-up tasks with create_task."
|
|
37
|
+
].join("\n")
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "follow_up_email",
|
|
41
|
+
title: "Follow-up email draft",
|
|
42
|
+
description: "Draft a follow-up email for a person, company or deal from its CRM timeline.",
|
|
43
|
+
arguments: [
|
|
44
|
+
{ name: "recordId", description: "Record id (person, company or deal).", required: true },
|
|
45
|
+
{ name: "goal", description: "What the email should achieve (e.g. book a demo, send the proposal).", required: false },
|
|
46
|
+
{ name: "tone", description: "Tone (default: friendly and concise).", required: false }
|
|
47
|
+
],
|
|
48
|
+
render: (a) => [
|
|
49
|
+
`Draft a follow-up email for CRM record ${a.recordId}.`,
|
|
50
|
+
`1. Call get_record with { "id": "${a.recordId}" } and list_activities with { "recordId": "${a.recordId}", "includeRelated": true } to learn who they are, what was discussed and what is pending.`,
|
|
51
|
+
`2. Write the email${a.goal ? ` with this goal: ${a.goal}` : " that moves the relationship to its next step"}, tone: ${a.tone || "friendly, concise, specific"}. Reference concrete facts from the timeline; never invent facts, prices or commitments.`,
|
|
52
|
+
"3. Show me the subject and body. Only after I approve, offer to log it with log_activity (type email) and to create a follow-up task with create_task."
|
|
53
|
+
].join("\n")
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "lead_qualification",
|
|
57
|
+
title: "Lead qualification",
|
|
58
|
+
description: "Qualify a lead (BANT-style) from the CRM data and suggest the lifecycle stage and next step.",
|
|
59
|
+
arguments: [{ name: "recordId", description: "Person or company record id.", required: true }],
|
|
60
|
+
render: (a) => [
|
|
61
|
+
`Qualify the lead ${a.recordId}.`,
|
|
62
|
+
`1. Call describe_schema (object "people") to learn the lifecycle stages, then get_record with { "id": "${a.recordId}" } and list_activities for its history.`,
|
|
63
|
+
"2. Assess Budget, Authority, Need and Timing from what the CRM knows; list what is unknown and the questions to ask.",
|
|
64
|
+
"3. Recommend a lifecycleStage (subscriber, lead, mql, sql, opportunity…) with a one-line justification, a fit score from 1 to 5 and the next best action.",
|
|
65
|
+
"4. Only after I confirm: update_record with the new lifecycleStage and add_note with the qualification summary."
|
|
66
|
+
].join("\n")
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
const RESOURCES = [
|
|
70
|
+
{ uri: "agentcrm://schema", name: "schema", title: "CRM data model", description: "Objects, fields (ids, types, select options, relations) and pipelines.", mimeType: "application/json" },
|
|
71
|
+
{ uri: "agentcrm://pipelines", name: "pipelines", title: "Pipelines", description: "Pipelines with stages, probabilities and the current forecast.", mimeType: "application/json" },
|
|
72
|
+
{ uri: "agentcrm://modules", name: "modules", title: "Modules", description: "Workspace modules (built-in and extensions) with enabled / required state and dependencies, plus the installed extensions.", mimeType: "application/json" },
|
|
73
|
+
{ uri: "agentcrm://appearance", name: "appearance", title: "Appearance", description: "The effective workspace appearance (accent, neutral, radius, density, font, sidebar, mode, theme) and the available presets / themes.", mimeType: "application/json" }
|
|
74
|
+
];
|
|
75
|
+
function toolAnnotations(tool) {
|
|
76
|
+
return {
|
|
77
|
+
title: tool.name.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()),
|
|
78
|
+
readOnlyHint: !tool.mutating,
|
|
79
|
+
destructiveHint: tool.mutating && DESTRUCTIVE.has(tool.name),
|
|
80
|
+
idempotentHint: !tool.mutating || IDEMPOTENT.has(tool.name),
|
|
81
|
+
openWorldHint: false
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function createMcpServer(opts) {
|
|
85
|
+
const all = () => typeof opts.tools === "function" ? opts.tools() : opts.tools;
|
|
86
|
+
const visible = () => all().filter((t) => !(opts.readOnly && t.mutating));
|
|
87
|
+
let initialized = false;
|
|
88
|
+
let protocol = LATEST_PROTOCOL;
|
|
89
|
+
const ok = (id, result) => ({ jsonrpc: "2.0", id, result });
|
|
90
|
+
const fail = (id, code, message, data) => ({ jsonrpc: "2.0", id, error: { code, message, ...data !== void 0 ? { data } : {} } });
|
|
91
|
+
async function callTool(id, params) {
|
|
92
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
93
|
+
const tool = all().find((t) => t.name === name);
|
|
94
|
+
if (!tool) return fail(id, RPC.invalidParams, `Unknown tool: ${name}`);
|
|
95
|
+
if (opts.readOnly && tool.mutating) return fail(id, RPC.invalidParams, `Tool ${name} changes data and this server is read-only.`);
|
|
96
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
97
|
+
try {
|
|
98
|
+
const run = async () => {
|
|
99
|
+
const ctx = await opts.backend.context();
|
|
100
|
+
const { callTool: call } = await import("./tools-Bew8lxKx.mjs");
|
|
101
|
+
return call(ctx, name, args);
|
|
102
|
+
};
|
|
103
|
+
const result = tool.mutating ? await opts.backend.withWrite(run) : await run();
|
|
104
|
+
const structured = result && typeof result === "object" && !Array.isArray(result) ? result : { result: result ?? null };
|
|
105
|
+
return ok(id, { content: [{ type: "text", text: JSON.stringify(result ?? null, null, 2) }], structuredContent: structured, isError: false });
|
|
106
|
+
} catch (err) {
|
|
107
|
+
const e = toCliError(err);
|
|
108
|
+
const error = { code: e.code, message: e.message, ...e.hint ? { hint: e.hint } : {} };
|
|
109
|
+
return ok(id, { content: [{ type: "text", text: JSON.stringify({ error }) }], structuredContent: { error }, isError: true });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function readResource(id, uri) {
|
|
113
|
+
const ctx = await opts.backend.context();
|
|
114
|
+
let value;
|
|
115
|
+
if (uri === "agentcrm://schema") value = (await import("./schema-DlHt2PxI.mjs")).describeSchema(ctx);
|
|
116
|
+
else if (uri === "agentcrm://pipelines") {
|
|
117
|
+
const { listPipelines, forecast } = await import("./pipelines-DCdRBNBV.mjs");
|
|
118
|
+
value = listPipelines(ctx).map((p) => ({ ...p, forecast: forecast(ctx, { pipelineId: p.id }) }));
|
|
119
|
+
} else if (uri === "agentcrm://modules") {
|
|
120
|
+
const [{ listModules }, { listExtensions }] = await Promise.all([import("./modules-CqdVQVJb.mjs"), import("./extensions-BP1k60md.mjs")]);
|
|
121
|
+
value = { modules: listModules(ctx), extensions: listExtensions(ctx) };
|
|
122
|
+
} else if (uri === "agentcrm://appearance") {
|
|
123
|
+
const { getAppearance, listPresets } = await import("./appearance-BaShelzt.mjs");
|
|
124
|
+
const { logo, favicon, customCss, ...rest } = getAppearance(ctx);
|
|
125
|
+
value = { appearance: { ...rest, hasLogo: !!logo, hasFavicon: !!favicon, customCss: customCss ?? null }, presets: listPresets(ctx).map((p) => ({ id: p.id, name: p.name, source: p.source, description: p.description, ...p.enabled !== void 0 ? { enabled: p.enabled } : {} })) };
|
|
126
|
+
} else return fail(id, -32002, `Resource not found: ${uri}`, { uri });
|
|
127
|
+
return ok(id, { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(value, null, 2) }] });
|
|
128
|
+
}
|
|
129
|
+
async function handle(msg) {
|
|
130
|
+
if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0" || typeof msg.method !== "string") {
|
|
131
|
+
const id2 = msg && typeof msg === "object" && "id" in msg ? msg.id ?? null : null;
|
|
132
|
+
return fail(id2, RPC.invalidRequest, "Invalid JSON-RPC 2.0 request.");
|
|
133
|
+
}
|
|
134
|
+
const req = msg;
|
|
135
|
+
const isNotification = !("id" in req) || req.id === void 0;
|
|
136
|
+
const id = req.id ?? null;
|
|
137
|
+
const params = req.params ?? {};
|
|
138
|
+
if (isNotification) {
|
|
139
|
+
if (req.method === "notifications/initialized") initialized = true;
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
switch (req.method) {
|
|
144
|
+
case "initialize": {
|
|
145
|
+
const requested = typeof params.protocolVersion === "string" ? params.protocolVersion : LATEST_PROTOCOL;
|
|
146
|
+
protocol = SUPPORTED_PROTOCOLS.includes(requested) ? requested : LATEST_PROTOCOL;
|
|
147
|
+
return ok(id, {
|
|
148
|
+
protocolVersion: protocol,
|
|
149
|
+
capabilities: { tools: { listChanged: false }, resources: { listChanged: false, subscribe: false }, prompts: { listChanged: false }, logging: {} },
|
|
150
|
+
serverInfo: { name: opts.backend.serverName, title: "AgentCRM", version: opts.backend.version },
|
|
151
|
+
instructions: `AgentCRM (${opts.backend.siteLabel})${opts.readOnly ? " — READ-ONLY" : ""}. Call describe_schema first to learn object and field ids. Search before creating to avoid duplicates. Mutating tools change the user's CRM: confirm destructive actions (delete, merge, import with confirm: true) with the user first.`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
case "ping":
|
|
155
|
+
return ok(id, {});
|
|
156
|
+
case "tools/list":
|
|
157
|
+
return ok(id, {
|
|
158
|
+
tools: visible().map((t) => ({ name: t.name, title: toolAnnotations(t).title, description: t.description, inputSchema: t.inputSchema, annotations: toolAnnotations(t) }))
|
|
159
|
+
});
|
|
160
|
+
case "tools/call":
|
|
161
|
+
return callTool(id, params);
|
|
162
|
+
case "resources/list":
|
|
163
|
+
return ok(id, { resources: RESOURCES });
|
|
164
|
+
case "resources/templates/list":
|
|
165
|
+
return ok(id, { resourceTemplates: [] });
|
|
166
|
+
case "resources/read":
|
|
167
|
+
return readResource(id, typeof params.uri === "string" ? params.uri : "");
|
|
168
|
+
case "prompts/list":
|
|
169
|
+
return ok(id, { prompts: PROMPTS.map(({ render: _render, ...p }) => p) });
|
|
170
|
+
case "prompts/get": {
|
|
171
|
+
const prompt = PROMPTS.find((p) => p.name === params.name);
|
|
172
|
+
if (!prompt) return fail(id, RPC.invalidParams, `Unknown prompt: ${String(params.name)}`);
|
|
173
|
+
const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
|
|
174
|
+
const missing = prompt.arguments.filter((a) => a.required && !args[a.name]);
|
|
175
|
+
if (missing.length) return fail(id, RPC.invalidParams, `Missing argument(s): ${missing.map((a) => a.name).join(", ")}`);
|
|
176
|
+
return ok(id, { description: prompt.description, messages: [{ role: "user", content: { type: "text", text: prompt.render(args) } }] });
|
|
177
|
+
}
|
|
178
|
+
case "logging/setLevel":
|
|
179
|
+
return ok(id, {});
|
|
180
|
+
default:
|
|
181
|
+
return fail(id, RPC.methodNotFound, `Method not found: ${req.method}`);
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
return fail(id, RPC.internal, err.message);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
handle,
|
|
189
|
+
get initialized() {
|
|
190
|
+
return initialized;
|
|
191
|
+
},
|
|
192
|
+
get protocol() {
|
|
193
|
+
return protocol;
|
|
194
|
+
},
|
|
195
|
+
toolNames: () => visible().map((t) => t.name)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function serveStdio(server, input, write, log = () => void 0) {
|
|
199
|
+
return new Promise((resolve) => {
|
|
200
|
+
let buffer = "";
|
|
201
|
+
let chain = Promise.resolve();
|
|
202
|
+
const processLine = async (line) => {
|
|
203
|
+
if (!line.trim()) return;
|
|
204
|
+
let msg;
|
|
205
|
+
try {
|
|
206
|
+
msg = JSON.parse(line);
|
|
207
|
+
} catch {
|
|
208
|
+
write(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: RPC.parse, message: "Parse error" } }));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (Array.isArray(msg)) {
|
|
212
|
+
const out = (await Promise.all(msg.map((m) => server.handle(m)))).filter((r) => r !== null);
|
|
213
|
+
if (out.length) write(JSON.stringify(out));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const res = await server.handle(msg);
|
|
217
|
+
if (res) write(JSON.stringify(res));
|
|
218
|
+
};
|
|
219
|
+
input.setEncoding?.("utf8");
|
|
220
|
+
input.on("data", (chunk) => {
|
|
221
|
+
buffer += chunk.toString();
|
|
222
|
+
let nl;
|
|
223
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
224
|
+
const line = buffer.slice(0, nl).replace(/\r$/, "");
|
|
225
|
+
buffer = buffer.slice(nl + 1);
|
|
226
|
+
chain = chain.then(() => processLine(line)).catch((err) => log(`[mcp] ${err.message}`));
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
input.on("end", () => {
|
|
230
|
+
const rest = buffer;
|
|
231
|
+
buffer = "";
|
|
232
|
+
chain = chain.then(() => processLine(rest)).finally(() => resolve());
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
export {
|
|
237
|
+
LATEST_PROTOCOL,
|
|
238
|
+
RPC,
|
|
239
|
+
SUPPORTED_PROTOCOLS,
|
|
240
|
+
createMcpServer,
|
|
241
|
+
serveStdio
|
|
242
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
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 { settingsDoc, asDoc } from "./store-5gqRSKXg.mjs";
|
|
9
|
+
import { SECRET_SETTING_KEYS } from "./runtimeConfig-CCOFg1RM.mjs";
|
|
10
|
+
import "./pluginRegistry-DyHtlkcr.mjs";
|
|
11
|
+
const DEFAULT_SETTINGS = {
|
|
12
|
+
id: "general",
|
|
13
|
+
name: "AgentCRM",
|
|
14
|
+
defaultCurrency: "USD",
|
|
15
|
+
locale: "en-US",
|
|
16
|
+
weekStartsOn: 1,
|
|
17
|
+
staleDealDays: 14
|
|
18
|
+
};
|
|
19
|
+
function getSettings(ctx) {
|
|
20
|
+
assertCan(ctx, "settings.read");
|
|
21
|
+
return { ...DEFAULT_SETTINGS, ...settingsDoc(ctx.store) ?? {} };
|
|
22
|
+
}
|
|
23
|
+
async function updateSettings(ctx, patch) {
|
|
24
|
+
assertCan(ctx, "settings.write");
|
|
25
|
+
if (patch.defaultCurrency !== void 0 && !/^[A-Z]{3}$/.test(patch.defaultCurrency.toUpperCase())) {
|
|
26
|
+
throw new OpsError("validation", `"${patch.defaultCurrency}" is not an ISO 4217 currency code.`);
|
|
27
|
+
}
|
|
28
|
+
if (patch.staleDealDays !== void 0 && (!Number.isFinite(patch.staleDealDays) || patch.staleDealDays < 1)) {
|
|
29
|
+
throw new OpsError("validation", "Stale deal days must be a positive number.");
|
|
30
|
+
}
|
|
31
|
+
if (patch.maxUploadMb !== void 0 && (!Number.isFinite(patch.maxUploadMb) || patch.maxUploadMb < 1 || patch.maxUploadMb > 1024)) {
|
|
32
|
+
throw new OpsError("validation", "Max upload size must be between 1 and 1024 MB.");
|
|
33
|
+
}
|
|
34
|
+
const current = getSettings(ctx);
|
|
35
|
+
const next = {
|
|
36
|
+
...current,
|
|
37
|
+
...Object.fromEntries(Object.entries(patch).filter(([, v]) => v !== void 0)),
|
|
38
|
+
...patch.defaultCurrency ? { defaultCurrency: patch.defaultCurrency.toUpperCase() } : {},
|
|
39
|
+
id: "general",
|
|
40
|
+
updatedAt: ctx.now(),
|
|
41
|
+
createdAt: current.createdAt ?? ctx.now()
|
|
42
|
+
};
|
|
43
|
+
await ctx.store.write([{ op: "set", collection: "settings", doc: asDoc(next) }], { message: "Update settings" });
|
|
44
|
+
return getSettings(ctx);
|
|
45
|
+
}
|
|
46
|
+
function assertNoSecrets(value, path = "") {
|
|
47
|
+
if (!value || typeof value !== "object") return;
|
|
48
|
+
for (const [k, v] of Object.entries(value)) {
|
|
49
|
+
if (SECRET_SETTING_KEYS.has(k) && v !== "" && v != null) {
|
|
50
|
+
throw new OpsError("validation", `Refusing to store the secret "${path}${k}" in the CRM data. Keep secrets in this browser / the CLI credentials.`);
|
|
51
|
+
}
|
|
52
|
+
assertNoSecrets(v, `${path}${k}.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getConfigDoc(ctx, id) {
|
|
56
|
+
assertCan(ctx, "settings.read");
|
|
57
|
+
return ctx.store.get("config", id);
|
|
58
|
+
}
|
|
59
|
+
async function setConfigDoc(ctx, id, value) {
|
|
60
|
+
assertCan(ctx, "settings.write");
|
|
61
|
+
if (value === null) {
|
|
62
|
+
await ctx.store.write([{ op: "delete", collection: "config", id }], { message: `Remove config ${id}` });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
assertNoSecrets(value);
|
|
66
|
+
const doc = { ...value, id };
|
|
67
|
+
await ctx.store.write([{ op: "set", collection: "config", doc: asDoc(doc) }], { message: `Update config ${id}` });
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
DEFAULT_SETTINGS,
|
|
71
|
+
assertNoSecrets,
|
|
72
|
+
getConfigDoc,
|
|
73
|
+
getSettings,
|
|
74
|
+
setConfigDoc,
|
|
75
|
+
updateSettings
|
|
76
|
+
};
|
|
@@ -0,0 +1,243 @@
|
|
|
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 { normalizeDomain, colorAt } from "./fields-B0bitgmr.mjs";
|
|
8
|
+
const DEFAULT_PIPELINE_ID = "sales";
|
|
9
|
+
function options(labels) {
|
|
10
|
+
return labels.map((l, i) => {
|
|
11
|
+
const [id, label] = Array.isArray(l) ? l : [l.toLowerCase().replace(/[^a-z0-9]+/g, "-"), l];
|
|
12
|
+
return { id, label, color: colorAt(i) };
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const INDUSTRY_OPTIONS = options([
|
|
16
|
+
"Software",
|
|
17
|
+
"Finance",
|
|
18
|
+
"Healthcare",
|
|
19
|
+
"Retail",
|
|
20
|
+
"Manufacturing",
|
|
21
|
+
"Education",
|
|
22
|
+
"Media",
|
|
23
|
+
"Consulting",
|
|
24
|
+
["real-estate", "Real estate"],
|
|
25
|
+
"Other"
|
|
26
|
+
]);
|
|
27
|
+
const LIFECYCLE_OPTIONS = [
|
|
28
|
+
{ id: "subscriber", label: "Subscriber", color: "gray" },
|
|
29
|
+
{ id: "lead", label: "Lead", color: "blue" },
|
|
30
|
+
{ id: "mql", label: "MQL", color: "teal" },
|
|
31
|
+
{ id: "sql", label: "SQL", color: "indigo" },
|
|
32
|
+
{ id: "opportunity", label: "Opportunity", color: "orange" },
|
|
33
|
+
{ id: "customer", label: "Customer", color: "green" },
|
|
34
|
+
{ id: "evangelist", label: "Evangelist", color: "purple" }
|
|
35
|
+
];
|
|
36
|
+
const LEAD_SOURCE_OPTIONS = options(["Website", "Referral", "Event", "Outbound", "Partner", "Ads", "Social", "Other"]);
|
|
37
|
+
const DEFAULT_STAGES = [
|
|
38
|
+
{ id: "new", label: "New", color: "gray", probability: 10, kind: "open" },
|
|
39
|
+
{ id: "qualified", label: "Qualified", color: "blue", probability: 25, kind: "open" },
|
|
40
|
+
{ id: "proposal", label: "Proposal", color: "purple", probability: 50, kind: "open" },
|
|
41
|
+
{ id: "negotiation", label: "Negotiation", color: "orange", probability: 75, kind: "open" },
|
|
42
|
+
{ id: "won", label: "Won", color: "green", probability: 100, kind: "won" },
|
|
43
|
+
{ id: "lost", label: "Lost", color: "red", probability: 0, kind: "lost" }
|
|
44
|
+
];
|
|
45
|
+
function defaultPipeline() {
|
|
46
|
+
return {
|
|
47
|
+
id: DEFAULT_PIPELINE_ID,
|
|
48
|
+
object: "deals",
|
|
49
|
+
name: "Sales",
|
|
50
|
+
stageField: "stage",
|
|
51
|
+
stages: DEFAULT_STAGES.map((s) => ({ ...s }))
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const f = (def) => ({ standard: true, ...def });
|
|
55
|
+
function companiesObject(now) {
|
|
56
|
+
return {
|
|
57
|
+
id: "companies",
|
|
58
|
+
labelSingular: "Company",
|
|
59
|
+
labelPlural: "Companies",
|
|
60
|
+
icon: "Building2",
|
|
61
|
+
color: "blue",
|
|
62
|
+
standard: true,
|
|
63
|
+
labelField: "name",
|
|
64
|
+
imageField: "logo",
|
|
65
|
+
createdAt: now,
|
|
66
|
+
updatedAt: now,
|
|
67
|
+
fields: [
|
|
68
|
+
f({ id: "name", label: "Name", type: "text", required: true, system: true }),
|
|
69
|
+
f({ id: "domain", label: "Domain", type: "url", unique: true, description: "Main website — used to deduplicate companies." }),
|
|
70
|
+
f({ id: "industry", label: "Industry", type: "select", options: INDUSTRY_OPTIONS.map((o) => ({ ...o })) }),
|
|
71
|
+
f({ id: "employees", label: "Employees", type: "number" }),
|
|
72
|
+
f({ id: "annualRevenue", label: "Annual revenue", type: "currency", currency: "USD" }),
|
|
73
|
+
f({ id: "address", label: "Address", type: "address" }),
|
|
74
|
+
f({ id: "linkedin", label: "LinkedIn", type: "url" }),
|
|
75
|
+
f({ id: "phone", label: "Phone", type: "phone" }),
|
|
76
|
+
f({ id: "tags", label: "Tags", type: "tags" }),
|
|
77
|
+
f({ id: "logo", label: "Logo", type: "image", description: "Company logo shown next to its name." })
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function peopleObject(now) {
|
|
82
|
+
return {
|
|
83
|
+
id: "people",
|
|
84
|
+
labelSingular: "Person",
|
|
85
|
+
labelPlural: "People",
|
|
86
|
+
icon: "User",
|
|
87
|
+
color: "purple",
|
|
88
|
+
standard: true,
|
|
89
|
+
labelField: "name",
|
|
90
|
+
imageField: "avatar",
|
|
91
|
+
createdAt: now,
|
|
92
|
+
updatedAt: now,
|
|
93
|
+
fields: [
|
|
94
|
+
f({ id: "firstName", label: "First name", type: "text" }),
|
|
95
|
+
f({ id: "lastName", label: "Last name", type: "text" }),
|
|
96
|
+
f({ id: "name", label: "Name", type: "text", system: true, description: "Full name — kept in sync with first and last name." }),
|
|
97
|
+
f({ id: "email", label: "Email", type: "email", unique: true }),
|
|
98
|
+
f({ id: "phone", label: "Phone", type: "phone" }),
|
|
99
|
+
f({ id: "jobTitle", label: "Job title", type: "text" }),
|
|
100
|
+
f({ id: "company", label: "Company", type: "relation", relation: { object: "companies", cardinality: "one", inverseField: "people" } }),
|
|
101
|
+
f({ id: "city", label: "City", type: "text" }),
|
|
102
|
+
f({ id: "linkedin", label: "LinkedIn", type: "url" }),
|
|
103
|
+
f({ id: "lifecycleStage", label: "Lifecycle stage", type: "select", options: LIFECYCLE_OPTIONS.map((o) => ({ ...o })), defaultValue: "lead" }),
|
|
104
|
+
f({ id: "leadSource", label: "Lead source", type: "select", options: LEAD_SOURCE_OPTIONS.map((o) => ({ ...o })) }),
|
|
105
|
+
f({ id: "tags", label: "Tags", type: "tags" }),
|
|
106
|
+
f({ id: "avatar", label: "Photo", type: "image", description: "Profile picture shown next to the name." })
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function dealsObject(now, pipelines = [defaultPipeline()]) {
|
|
111
|
+
return {
|
|
112
|
+
id: "deals",
|
|
113
|
+
labelSingular: "Deal",
|
|
114
|
+
labelPlural: "Deals",
|
|
115
|
+
icon: "Handshake",
|
|
116
|
+
color: "green",
|
|
117
|
+
standard: true,
|
|
118
|
+
labelField: "name",
|
|
119
|
+
createdAt: now,
|
|
120
|
+
updatedAt: now,
|
|
121
|
+
fields: [
|
|
122
|
+
f({ id: "name", label: "Name", type: "text", required: true, system: true }),
|
|
123
|
+
f({ id: "amount", label: "Amount", type: "currency", currency: "USD" }),
|
|
124
|
+
f({ id: "stage", label: "Stage", type: "select", system: true, options: stageOptions(pipelines), defaultValue: pipelines[0]?.stages[0]?.id }),
|
|
125
|
+
f({ id: "pipeline", label: "Pipeline", type: "select", system: true, options: pipelineOptions(pipelines), defaultValue: pipelines[0]?.id }),
|
|
126
|
+
f({ id: "closeDate", label: "Close date", type: "date" }),
|
|
127
|
+
f({ id: "probability", label: "Probability", type: "percent" }),
|
|
128
|
+
f({ id: "company", label: "Company", type: "relation", relation: { object: "companies", cardinality: "one", inverseField: "deals" } }),
|
|
129
|
+
f({ id: "contacts", label: "Contacts", type: "relation", relation: { object: "people", cardinality: "many", inverseField: "deals" } }),
|
|
130
|
+
f({ id: "lostReason", label: "Lost reason", type: "text" })
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function stageOptions(pipelines) {
|
|
135
|
+
return pipelines.flatMap((p) => p.stages.map((s) => ({ id: s.id, label: s.label, color: s.color })));
|
|
136
|
+
}
|
|
137
|
+
function pipelineOptions(pipelines) {
|
|
138
|
+
return pipelines.map((p, i) => ({ id: p.id, label: p.name, color: colorAt(i + 1) }));
|
|
139
|
+
}
|
|
140
|
+
const STANDARD_IMAGE_FIELDS = { companies: "logo", people: "avatar" };
|
|
141
|
+
function standardObjects(now) {
|
|
142
|
+
return [companiesObject(now), peopleObject(now), dealsObject(now)];
|
|
143
|
+
}
|
|
144
|
+
function defaultViews() {
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
147
|
+
id: "companies-all",
|
|
148
|
+
object: "companies",
|
|
149
|
+
name: "All companies",
|
|
150
|
+
type: "table",
|
|
151
|
+
columns: ["name", "domain", "industry", "employees", "annualRevenue", "tags"],
|
|
152
|
+
filters: [],
|
|
153
|
+
sorts: [{ field: "name", dir: "asc" }],
|
|
154
|
+
shared: true
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "people-all",
|
|
158
|
+
object: "people",
|
|
159
|
+
name: "All people",
|
|
160
|
+
type: "table",
|
|
161
|
+
columns: ["name", "email", "company", "jobTitle", "lifecycleStage", "leadSource", "city"],
|
|
162
|
+
filters: [],
|
|
163
|
+
sorts: [{ field: "name", dir: "asc" }],
|
|
164
|
+
shared: true
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "deals-all",
|
|
168
|
+
object: "deals",
|
|
169
|
+
name: "All deals",
|
|
170
|
+
type: "table",
|
|
171
|
+
columns: ["name", "amount", "stage", "closeDate", "company", "contacts", "probability"],
|
|
172
|
+
filters: [],
|
|
173
|
+
sorts: [{ field: "closeDate", dir: "asc" }],
|
|
174
|
+
shared: true
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "deals-pipeline",
|
|
178
|
+
object: "deals",
|
|
179
|
+
name: "Pipeline",
|
|
180
|
+
type: "kanban",
|
|
181
|
+
columns: ["name", "amount", "company", "closeDate"],
|
|
182
|
+
filters: [{ field: "pipeline", op: "eq", value: DEFAULT_PIPELINE_ID }],
|
|
183
|
+
sorts: [{ field: "amount", dir: "desc" }],
|
|
184
|
+
groupBy: "stage",
|
|
185
|
+
pipelineId: DEFAULT_PIPELINE_ID,
|
|
186
|
+
shared: true
|
|
187
|
+
}
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
function defaultViewFor(object) {
|
|
191
|
+
return {
|
|
192
|
+
id: `${object.id}-all`,
|
|
193
|
+
object: object.id,
|
|
194
|
+
name: `All ${object.labelPlural.toLowerCase()}`,
|
|
195
|
+
type: "table",
|
|
196
|
+
columns: object.fields.filter((fd) => !fd.hidden && fd.type !== "image").slice(0, 7).map((fd) => fd.id),
|
|
197
|
+
filters: [],
|
|
198
|
+
sorts: [],
|
|
199
|
+
shared: true
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function normalizeStandardFields(objectId, next, previous) {
|
|
203
|
+
if (objectId === "people") {
|
|
204
|
+
const out = { ...next };
|
|
205
|
+
const first = str(out.firstName);
|
|
206
|
+
const last = str(out.lastName);
|
|
207
|
+
const name = str(out.name);
|
|
208
|
+
const prevDerived = previous ? [str(previous.firstName), str(previous.lastName)].filter(Boolean).join(" ") : "";
|
|
209
|
+
const partsChanged = !!previous && (str(previous.firstName) !== first || str(previous.lastName) !== last);
|
|
210
|
+
if (first || last) {
|
|
211
|
+
if (!name || partsChanged && name === prevDerived) out.name = [first, last].filter(Boolean).join(" ");
|
|
212
|
+
} else if (name) {
|
|
213
|
+
const parts = name.split(/\s+/);
|
|
214
|
+
out.firstName = parts[0];
|
|
215
|
+
if (parts.length > 1) out.lastName = parts.slice(1).join(" ");
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
if (objectId === "companies") {
|
|
220
|
+
const out = { ...next };
|
|
221
|
+
if (!str(out.name) && str(out.domain)) {
|
|
222
|
+
const d = normalizeDomain(out.domain);
|
|
223
|
+
const base = d.split(".")[0] ?? "";
|
|
224
|
+
if (base) out.name = base[0].toUpperCase() + base.slice(1);
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
return next;
|
|
229
|
+
}
|
|
230
|
+
function str(v) {
|
|
231
|
+
return typeof v === "string" ? v.trim() : "";
|
|
232
|
+
}
|
|
233
|
+
export {
|
|
234
|
+
LIFECYCLE_OPTIONS as L,
|
|
235
|
+
STANDARD_IMAGE_FIELDS as S,
|
|
236
|
+
standardObjects as a,
|
|
237
|
+
defaultPipeline as b,
|
|
238
|
+
defaultViews as c,
|
|
239
|
+
defaultViewFor as d,
|
|
240
|
+
normalizeStandardFields as n,
|
|
241
|
+
pipelineOptions as p,
|
|
242
|
+
stageOptions as s
|
|
243
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
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 { A as ACTIVITY_TYPES } from "../agentcrm.mjs";
|
|
8
|
+
import { assertCan } from "./context-CxFfqS4D.mjs";
|
|
9
|
+
import { forecast } from "./pipelines-DCdRBNBV.mjs";
|
|
10
|
+
import { activitiesOf, pipelinesOf, usersOf, objectsOf, recordsOf } from "./store-5gqRSKXg.mjs";
|
|
11
|
+
import "node:util";
|
|
12
|
+
import "node:fs";
|
|
13
|
+
import "node:path";
|
|
14
|
+
import "node:url";
|
|
15
|
+
import "node:crypto";
|
|
16
|
+
import "node:os";
|
|
17
|
+
import "node:child_process";
|
|
18
|
+
import "node:http";
|
|
19
|
+
import "./pluginRegistry-DyHtlkcr.mjs";
|
|
20
|
+
import "./fields-B0bitgmr.mjs";
|
|
21
|
+
import "./ids-CvnmrMJX.mjs";
|
|
22
|
+
import "./standardObjects-CheqfDqa.mjs";
|
|
23
|
+
import "./records-DoqiT9xR.mjs";
|
|
24
|
+
import "./query-C5nJB_3C.mjs";
|
|
25
|
+
import "./relations-B9TynBmA.mjs";
|
|
26
|
+
function getStats(ctx) {
|
|
27
|
+
assertCan(ctx, "reports.read");
|
|
28
|
+
const now = ctx.now();
|
|
29
|
+
const activities = activitiesOf(ctx.store);
|
|
30
|
+
const byType = Object.fromEntries(ACTIVITY_TYPES.map((t) => [t, 0]));
|
|
31
|
+
for (const a of activities) byType[a.type]++;
|
|
32
|
+
const tasks = activities.filter((a) => a.type === "task" && !a.doneAt);
|
|
33
|
+
const firstPipeline = pipelinesOf(ctx.store, "deals")[0] ?? pipelinesOf(ctx.store)[0];
|
|
34
|
+
const f = firstPipeline ? forecast(ctx, { pipelineId: firstPipeline.id }) : null;
|
|
35
|
+
return {
|
|
36
|
+
objects: objectsOf(ctx.store).map((o) => {
|
|
37
|
+
const all = recordsOf(ctx.store, o.id, { includeDeleted: true });
|
|
38
|
+
const deleted = all.filter((r) => r.deletedAt).length;
|
|
39
|
+
return { id: o.id, label: o.labelPlural, count: all.length - deleted, deleted };
|
|
40
|
+
}),
|
|
41
|
+
activities: byType,
|
|
42
|
+
openTasks: tasks.length,
|
|
43
|
+
overdueTasks: tasks.filter((t) => t.dueAt !== void 0 && t.dueAt < now).length,
|
|
44
|
+
users: usersOf(ctx.store).filter((u) => !u.disabled).length,
|
|
45
|
+
pipeline: f ? { pipelineId: f.pipelineId, openCount: f.openCount, openAmount: f.openAmount, weightedAmount: f.weightedAmount } : null,
|
|
46
|
+
backend: ctx.store.kind
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
getStats
|
|
51
|
+
};
|