@datasynx/agentic-crm 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/cli.js +177 -15
- package/dist/cli.js.map +1 -1
- package/dist/daemon/worker.js +16 -0
- package/dist/daemon/worker.js.map +1 -1
- package/dist/{index-Dspvybo0.d.cts → index-DNHsURo5.d.cts} +14 -14
- package/dist/index-DNHsURo5.d.cts.map +1 -0
- package/dist/index.d.cts +14 -14
- package/dist/index.d.cts.map +1 -1
- package/dist/{knowledge-base-Bx2PKQR2.js → knowledge-base-Cc0niBFf.js} +2 -1
- package/dist/knowledge-base-Cc0niBFf.js.map +1 -0
- package/dist/login-CYgla6-A.js +73 -0
- package/dist/login-CYgla6-A.js.map +1 -0
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
- package/dist/mcp.cjs +289 -130
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +289 -130
- package/dist/mcp.js.map +1 -1
- package/dist/microsoft-DgbVlHdT.js +159 -0
- package/dist/microsoft-DgbVlHdT.js.map +1 -0
- package/dist/{server-uqXUhF4H.js → server-BbInMUgp.js} +172 -130
- package/dist/server-BbInMUgp.js.map +1 -0
- package/dist/snapshots-B6aOhoXs.js +2 -0
- package/dist/snapshots-CQSOaIMs.js +161 -0
- package/dist/snapshots-CQSOaIMs.js.map +1 -0
- package/dist/token-resolver-BRLOmRvF.js +50 -0
- package/dist/token-resolver-BRLOmRvF.js.map +1 -0
- package/package.json +2 -2
- package/dist/index-Dspvybo0.d.cts.map +0 -1
- package/dist/knowledge-base-Bx2PKQR2.js.map +0 -1
- package/dist/server-uqXUhF4H.js.map +0 -1
package/README.md
CHANGED
|
@@ -243,10 +243,17 @@ right customer by sender/recipient domain** (or to one customer with a slug). On
|
|
|
243
243
|
mailbox connection, all customers populated, same attachment + search pipeline.
|
|
244
244
|
|
|
245
245
|
```bash
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
# One-time OAuth (Gmail & Microsoft 365 require it for IMAP in 2026):
|
|
247
|
+
dxcrm mailbox login gmail --user you@gmail.com
|
|
248
|
+
dxcrm mailbox login microsoft --user you@org.com
|
|
249
|
+
|
|
250
|
+
# Then auto-route the whole mailbox to customers by domain:
|
|
251
|
+
dxcrm mailbox sync --account gmail:you@gmail.com
|
|
248
252
|
```
|
|
249
253
|
|
|
254
|
+
Tokens are stored locally and auto-refreshed. A password-based IMAP server works too
|
|
255
|
+
(`DXCRM_IMAP_HOST` / `DXCRM_IMAP_USER` / `DXCRM_IMAP_PASS`).
|
|
256
|
+
|
|
250
257
|
<br/>
|
|
251
258
|
|
|
252
259
|
## What it's not
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { i as writeJsonFile } from "./json-store-WWsFzXub.js";
|
|
|
5
5
|
import { a as warning, i as success, n as error, r as info, t as bold } from "./colors-BG07TZQz.js";
|
|
6
6
|
import { n as getSession } from "./session-store-CEa39Dxs.js";
|
|
7
7
|
import { i as sessionCommand, r as readAllSessions } from "./session-BgGDyP2C.js";
|
|
8
|
-
import { a as searchKbSimple, i as listKbArticles, n as getKbArticle, o as writeKbArticle, s as CAPABILITIES_TEXT, t as deleteKbArticle } from "./knowledge-base-
|
|
8
|
+
import { a as searchKbSimple, i as listKbArticles, n as getKbArticle, o as writeKbArticle, s as CAPABILITIES_TEXT, t as deleteKbArticle } from "./knowledge-base-Cc0niBFf.js";
|
|
9
9
|
import { a as restoreCommand, t as backupCommand } from "./backup-CTlIxUdO.js";
|
|
10
10
|
import { n as readSyncState } from "./sync-state-DMZgzpez.js";
|
|
11
11
|
import { n as readUnmatched } from "./unmatched-transcripts-DC-VQ9YS.js";
|
|
@@ -32,6 +32,7 @@ import { createHash } from "crypto";
|
|
|
32
32
|
import Table from "cli-table3";
|
|
33
33
|
import os from "os";
|
|
34
34
|
import { execSync, spawn } from "child_process";
|
|
35
|
+
import readline from "readline";
|
|
35
36
|
//#region src/commands/create.ts
|
|
36
37
|
async function createCustomer(opts) {
|
|
37
38
|
const id = slugify(opts.name, { lower: true });
|
|
@@ -240,14 +241,14 @@ mcpCommand.command("start").description("Start MCP server (stdio by default)").o
|
|
|
240
241
|
if (opts.http) {
|
|
241
242
|
const port = parseInt(opts.port, 10);
|
|
242
243
|
console.error(info(`Starting MCP server in HTTP mode on port ${port}...`));
|
|
243
|
-
const { startHttp } = await import("./server-
|
|
244
|
+
const { startHttp } = await import("./server-BbInMUgp.js");
|
|
244
245
|
await startHttp(port);
|
|
245
246
|
} else {
|
|
246
|
-
const { startStdio } = await import("./server-
|
|
247
|
+
const { startStdio } = await import("./server-BbInMUgp.js");
|
|
247
248
|
await startStdio();
|
|
248
249
|
}
|
|
249
250
|
});
|
|
250
|
-
const TOOL_COUNT =
|
|
251
|
+
const TOOL_COUNT = 59;
|
|
251
252
|
/** Claude Code: CLAUDE.md in CRM dataDir */
|
|
252
253
|
function buildClaudeMd(dataDir) {
|
|
253
254
|
return `# DatasynxOpenCRM v2 — Agent Instructions (${TOOL_COUNT} MCP Tools)
|
|
@@ -375,6 +376,7 @@ It combines graph, health, revenue simulation, playbook, and org intelligence in
|
|
|
375
376
|
- \`get_audit_log({ slug?, actor?, limit? })\` — read append-only audit log of all write operations
|
|
376
377
|
- \`get_logs({ level?, component?, since?, contains?, limit?, summary? })\` — query/aggregate the structured application log
|
|
377
378
|
- \`get_diagnostics({ fix? })\` — self-diagnostic health check (data integrity, temp files, log errors, backups)
|
|
379
|
+
- \`get_pipeline_changes({ since?, days? })\` — pipeline time-travel: what changed (won/lost/moved/value) since a date
|
|
378
380
|
|
|
379
381
|
### Custom Objects (Platform / metadata)
|
|
380
382
|
- \`define_custom_object({ name, label?, fields })\` — define a runtime entity type with typed fields (no migration), admin
|
|
@@ -636,7 +638,7 @@ create_ticket · update_ticket · list_tickets · close_ticket ·
|
|
|
636
638
|
send_nps_survey · get_survey_results ·
|
|
637
639
|
search_knowledge_base · create_kb_article ·
|
|
638
640
|
backup_now · list_backups ·
|
|
639
|
-
trigger_sync · get_audit_log · get_logs · get_diagnostics ·
|
|
641
|
+
trigger_sync · get_audit_log · get_logs · get_diagnostics · get_pipeline_changes ·
|
|
640
642
|
define_custom_object · create_record · list_records · list_custom_objects
|
|
641
643
|
|
|
642
644
|
## Data: ${dataDir}`.trim();
|
|
@@ -2901,14 +2903,14 @@ function buildQuery(opts) {
|
|
|
2901
2903
|
limit: opts.limit ?? 50
|
|
2902
2904
|
};
|
|
2903
2905
|
}
|
|
2904
|
-
function dataDir$
|
|
2906
|
+
function dataDir$19() {
|
|
2905
2907
|
return process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2906
2908
|
}
|
|
2907
2909
|
const logsCommand = new Command("logs").description("View and analyze the structured application log").option("--level <level>", "Minimum level: debug | info | warn | error").option("--component <name>", "Filter by component (e.g. gmail-sync, lancedb)").option("--since <iso>", "Only entries at or after this ISO timestamp").option("--contains <text>", "Filter by message substring").option("--limit <n>", "Max entries to show (default: 50)", parseInt).option("--summary", "Show aggregated counts (by level + component) instead of entries").action(async (opts) => {
|
|
2908
2910
|
const query = buildQuery(opts);
|
|
2909
2911
|
if (opts.summary) {
|
|
2910
2912
|
const { summarizeLogs } = await import("./logger-vKQS34w9.js");
|
|
2911
|
-
const s = summarizeLogs(dataDir$
|
|
2913
|
+
const s = summarizeLogs(dataDir$19(), query);
|
|
2912
2914
|
console.log(bold(`Logs — ${s.total} entr${s.total === 1 ? "y" : "ies"}`));
|
|
2913
2915
|
if (s.firstTs) console.log(info(` ${s.firstTs} → ${s.lastTs}`));
|
|
2914
2916
|
console.log(" By level:");
|
|
@@ -2925,7 +2927,7 @@ const logsCommand = new Command("logs").description("View and analyze the struct
|
|
|
2925
2927
|
return;
|
|
2926
2928
|
}
|
|
2927
2929
|
const { queryLogs } = await import("./logger-vKQS34w9.js");
|
|
2928
|
-
const entries = queryLogs(dataDir$
|
|
2930
|
+
const entries = queryLogs(dataDir$19(), query);
|
|
2929
2931
|
if (entries.length === 0) {
|
|
2930
2932
|
console.log(success("No matching log entries."));
|
|
2931
2933
|
return;
|
|
@@ -2937,7 +2939,7 @@ const logsCommand = new Command("logs").description("View and analyze the struct
|
|
|
2937
2939
|
});
|
|
2938
2940
|
//#endregion
|
|
2939
2941
|
//#region src/commands/doctor.ts
|
|
2940
|
-
function dataDir$
|
|
2942
|
+
function dataDir$18() {
|
|
2941
2943
|
return process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2942
2944
|
}
|
|
2943
2945
|
function icon(status) {
|
|
@@ -2948,10 +2950,10 @@ function icon(status) {
|
|
|
2948
2950
|
const doctorCommand = new Command("doctor").description("Run self-diagnostics: data integrity, temp files, log errors, backup freshness").option("--fix", "Clean up safely-fixable issues (orphaned temp files)").action(async (opts) => {
|
|
2949
2951
|
const { runDiagnostics, cleanupTempFiles } = await import("./doctor-CYDaNmFn.js");
|
|
2950
2952
|
if (opts.fix) {
|
|
2951
|
-
const removed = cleanupTempFiles(dataDir$
|
|
2953
|
+
const removed = cleanupTempFiles(dataDir$18());
|
|
2952
2954
|
console.log(removed.length > 0 ? success(`Removed ${removed.length} orphaned temp file(s).`) : warning("Nothing to fix."));
|
|
2953
2955
|
}
|
|
2954
|
-
const report = await runDiagnostics(dataDir$
|
|
2956
|
+
const report = await runDiagnostics(dataDir$18());
|
|
2955
2957
|
console.log(bold("dxcrm doctor"));
|
|
2956
2958
|
for (const c of report.checks) console.log(` ${icon(c.status)} ${c.name.padEnd(16)} ${c.detail}`);
|
|
2957
2959
|
if (report.ok) {
|
|
@@ -2963,6 +2965,59 @@ const doctorCommand = new Command("doctor").description("Run self-diagnostics: d
|
|
|
2963
2965
|
}
|
|
2964
2966
|
});
|
|
2965
2967
|
//#endregion
|
|
2968
|
+
//#region src/commands/pipeline.ts
|
|
2969
|
+
function dataDir$17() {
|
|
2970
|
+
return process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2971
|
+
}
|
|
2972
|
+
function daysAgoIso(days) {
|
|
2973
|
+
return (/* @__PURE__ */ new Date(Date.now() - days * 864e5)).toISOString().slice(0, 10);
|
|
2974
|
+
}
|
|
2975
|
+
const pipelineCommand = new Command("pipeline").description("Pipeline time-travel: daily snapshots and 'what changed?' diffs");
|
|
2976
|
+
pipelineCommand.command("snapshot").description("Capture a snapshot of the current pipeline across all customers").action(async () => {
|
|
2977
|
+
const { takeSnapshot } = await import("./snapshots-B6aOhoXs.js");
|
|
2978
|
+
const snap = takeSnapshot(dataDir$17());
|
|
2979
|
+
console.log(success(`Snapshot ${snap.id} taken — ${snap.deals.length} deal(s).`));
|
|
2980
|
+
});
|
|
2981
|
+
pipelineCommand.command("list").description("List available pipeline snapshots").action(async () => {
|
|
2982
|
+
const { listSnapshots } = await import("./snapshots-B6aOhoXs.js");
|
|
2983
|
+
const snaps = listSnapshots(dataDir$17());
|
|
2984
|
+
if (snaps.length === 0) {
|
|
2985
|
+
console.log(info("No snapshots yet. Run 'dxcrm pipeline snapshot' (or let the daemon take daily ones)."));
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
for (const s of snaps) console.log(`${s.id} ${String(s.dealCount).padStart(4)} deals open €${s.openValue.toLocaleString()}`);
|
|
2989
|
+
});
|
|
2990
|
+
pipelineCommand.command("changes").description("Show what changed in the pipeline since a date (default: 7 days ago)").option("--since <YYYY-MM-DD>", "Baseline date (default: 7 days ago)").action(async (opts) => {
|
|
2991
|
+
const since = opts.since ?? daysAgoIso(7);
|
|
2992
|
+
const { diffAgainstNow } = await import("./snapshots-B6aOhoXs.js");
|
|
2993
|
+
const diff = diffAgainstNow(dataDir$17(), since);
|
|
2994
|
+
if (!diff) {
|
|
2995
|
+
console.log(warning(`No snapshot at or before ${since}. Take snapshots first (or wait for the daemon).`));
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
console.log(bold(`Pipeline changes since ${diff.fromId}`));
|
|
2999
|
+
const line = (label, n) => ` ${label.padEnd(16)} ${n}`;
|
|
3000
|
+
console.log(success(line("Won", diff.won.length)));
|
|
3001
|
+
console.log(error(line("Lost", diff.lost.length)));
|
|
3002
|
+
console.log(line("New deals", diff.added.length));
|
|
3003
|
+
console.log(line("Removed", diff.removed.length));
|
|
3004
|
+
console.log(line("Stage moves", diff.advanced.length));
|
|
3005
|
+
console.log(line("Value changes", diff.valueChanged.length));
|
|
3006
|
+
const delta = diff.openValueDelta;
|
|
3007
|
+
const deltaStr = `${delta >= 0 ? "+" : ""}€${delta.toLocaleString()}`;
|
|
3008
|
+
console.log(` ${"Open value".padEnd(16)} €${diff.openValueAfter.toLocaleString()} (${delta >= 0 ? success(deltaStr) : error(deltaStr)})`);
|
|
3009
|
+
if (diff.won.length) console.log(success(`\nWon: ${diff.won.map((d) => d.name).join(", ")}`));
|
|
3010
|
+
if (diff.lost.length) console.log(error(`Lost: ${diff.lost.map((d) => d.name).join(", ")}`));
|
|
3011
|
+
if (diff.advanced.length) {
|
|
3012
|
+
console.log(info("\nStage moves:"));
|
|
3013
|
+
for (const m of diff.advanced) console.log(` ${m.slug}/${m.name}: ${m.from} → ${m.to}`);
|
|
3014
|
+
}
|
|
3015
|
+
if (diff.added.length) {
|
|
3016
|
+
console.log(info("\nNew deals:"));
|
|
3017
|
+
for (const d of diff.added) console.log(` ${d.slug}/${d.name}`);
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
//#endregion
|
|
2966
3021
|
//#region src/commands/rbac.ts
|
|
2967
3022
|
const ROLES = [
|
|
2968
3023
|
"admin",
|
|
@@ -4427,6 +4482,17 @@ complianceCommand.command("status", { isDefault: true }).description("Show the a
|
|
|
4427
4482
|
});
|
|
4428
4483
|
//#endregion
|
|
4429
4484
|
//#region src/commands/mailbox.ts
|
|
4485
|
+
/** Default IMAP endpoints for OAuth providers. */
|
|
4486
|
+
const PROVIDER_IMAP_HOST = {
|
|
4487
|
+
gmail: {
|
|
4488
|
+
host: "imap.gmail.com",
|
|
4489
|
+
port: 993
|
|
4490
|
+
},
|
|
4491
|
+
microsoft: {
|
|
4492
|
+
host: "outlook.office365.com",
|
|
4493
|
+
port: 993
|
|
4494
|
+
}
|
|
4495
|
+
};
|
|
4430
4496
|
/** Read IMAP mailbox connection settings from the environment. */
|
|
4431
4497
|
function imapConfigFromEnv(env = process.env) {
|
|
4432
4498
|
const host = env["DXCRM_IMAP_HOST"];
|
|
@@ -4448,15 +4514,54 @@ function imapConfigFromEnv(env = process.env) {
|
|
|
4448
4514
|
}
|
|
4449
4515
|
};
|
|
4450
4516
|
}
|
|
4517
|
+
/** Parse a "provider:user" account string. */
|
|
4518
|
+
function parseAccount(account) {
|
|
4519
|
+
const idx = account.indexOf(":");
|
|
4520
|
+
if (idx < 0) return null;
|
|
4521
|
+
const provider = account.slice(0, idx);
|
|
4522
|
+
const user = account.slice(idx + 1);
|
|
4523
|
+
if (provider !== "gmail" && provider !== "microsoft" || !user) return null;
|
|
4524
|
+
return {
|
|
4525
|
+
provider,
|
|
4526
|
+
user
|
|
4527
|
+
};
|
|
4528
|
+
}
|
|
4529
|
+
/** Build an IMAP config for a stored OAuth account, refreshing the token if needed. */
|
|
4530
|
+
async function resolveAccountConfig(dataDir, account, env = process.env, mailbox) {
|
|
4531
|
+
const parsed = parseAccount(account);
|
|
4532
|
+
if (!parsed) throw new Error(`Invalid --account '${account}'. Use 'gmail:you@gmail.com' or 'microsoft:you@org.com'.`);
|
|
4533
|
+
const { getFreshAccessToken } = await import("./token-resolver-BRLOmRvF.js");
|
|
4534
|
+
const accessToken = await getFreshAccessToken(dataDir, parsed.provider, parsed.user, { env });
|
|
4535
|
+
const { host, port } = PROVIDER_IMAP_HOST[parsed.provider];
|
|
4536
|
+
return {
|
|
4537
|
+
host,
|
|
4538
|
+
port,
|
|
4539
|
+
secure: true,
|
|
4540
|
+
mailbox: mailbox ?? env["DXCRM_IMAP_MAILBOX"] ?? "INBOX",
|
|
4541
|
+
auth: {
|
|
4542
|
+
user: parsed.user,
|
|
4543
|
+
accessToken
|
|
4544
|
+
}
|
|
4545
|
+
};
|
|
4546
|
+
}
|
|
4451
4547
|
/**
|
|
4452
4548
|
* Sync an IMAP mailbox (any provider). With a slug, all mail goes to that one
|
|
4453
4549
|
* customer; without, mail is auto-routed to customers by sender/recipient
|
|
4454
|
-
* domain and unmatched mail is reported as unrouted.
|
|
4550
|
+
* domain and unmatched mail is reported as unrouted. Use `account` to sync a
|
|
4551
|
+
* stored OAuth mailbox (Gmail/Outlook) instead of env-configured IMAP.
|
|
4455
4552
|
*/
|
|
4456
4553
|
async function runMailboxSync(opts) {
|
|
4457
|
-
const
|
|
4554
|
+
const env = opts.env ?? process.env;
|
|
4555
|
+
let config;
|
|
4556
|
+
try {
|
|
4557
|
+
config = opts.account ? await resolveAccountConfig(opts.dataDir, opts.account, env) : imapConfigFromEnv(env);
|
|
4558
|
+
} catch (err) {
|
|
4559
|
+
const msg = err.message;
|
|
4560
|
+
console.error(error(msg));
|
|
4561
|
+
return { error: msg };
|
|
4562
|
+
}
|
|
4458
4563
|
if (!config) {
|
|
4459
|
-
const msg = "IMAP not configured. Set DXCRM_IMAP_HOST, DXCRM_IMAP_USER and DXCRM_IMAP_PASS (or DXCRM_IMAP_TOKEN).";
|
|
4564
|
+
const msg = "IMAP not configured. Set DXCRM_IMAP_HOST, DXCRM_IMAP_USER and DXCRM_IMAP_PASS (or DXCRM_IMAP_TOKEN), or use --account.";
|
|
4460
4565
|
console.error(error(msg));
|
|
4461
4566
|
return { error: msg };
|
|
4462
4567
|
}
|
|
@@ -4473,11 +4578,67 @@ async function runMailboxSync(opts) {
|
|
|
4473
4578
|
if (!opts.slug && result.unrouted > 0) console.log(info(` ${result.unrouted} message(s) matched no customer. Add their domains via 'dxcrm create <slug> --domain <domain>'.`));
|
|
4474
4579
|
return result;
|
|
4475
4580
|
}
|
|
4581
|
+
function ask(question) {
|
|
4582
|
+
const rl = readline.createInterface({
|
|
4583
|
+
input: process.stdin,
|
|
4584
|
+
output: process.stdout
|
|
4585
|
+
});
|
|
4586
|
+
return new Promise((resolve) => rl.question(question, (a) => (rl.close(), resolve(a))));
|
|
4587
|
+
}
|
|
4588
|
+
/** Interactive `dxcrm mailbox login <provider>` flow. */
|
|
4589
|
+
async function runLogin(provider, user) {
|
|
4590
|
+
const dataDir = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4591
|
+
const print = (l) => console.log(l);
|
|
4592
|
+
if (provider === "gmail") {
|
|
4593
|
+
const clientId = process.env["DXCRM_GOOGLE_CLIENT_ID"];
|
|
4594
|
+
const clientSecret = process.env["DXCRM_GOOGLE_CLIENT_SECRET"];
|
|
4595
|
+
if (!clientId || !clientSecret) {
|
|
4596
|
+
console.error(error("Set DXCRM_GOOGLE_CLIENT_ID and DXCRM_GOOGLE_CLIENT_SECRET (OAuth desktop app)."));
|
|
4597
|
+
return;
|
|
4598
|
+
}
|
|
4599
|
+
const account = user ?? (await ask("Gmail address: ")).trim();
|
|
4600
|
+
const { runGmailLogin } = await import("./login-CYgla6-A.js");
|
|
4601
|
+
await runGmailLogin({
|
|
4602
|
+
dataDir,
|
|
4603
|
+
clientId,
|
|
4604
|
+
clientSecret,
|
|
4605
|
+
user: account,
|
|
4606
|
+
prompt: ask,
|
|
4607
|
+
print
|
|
4608
|
+
});
|
|
4609
|
+
console.log(success(`✓ Gmail authorized for ${bold(account)}. Sync: dxcrm mailbox sync --account gmail:${account}`));
|
|
4610
|
+
return;
|
|
4611
|
+
}
|
|
4612
|
+
if (provider === "microsoft") {
|
|
4613
|
+
const clientId = process.env["DXCRM_MS_CLIENT_ID"];
|
|
4614
|
+
if (!clientId) {
|
|
4615
|
+
console.error(error("Set DXCRM_MS_CLIENT_ID (Azure app registration, public client)."));
|
|
4616
|
+
return;
|
|
4617
|
+
}
|
|
4618
|
+
const tenant = process.env["DXCRM_MS_TENANT"] ?? "common";
|
|
4619
|
+
const account = user ?? (await ask("Outlook/Microsoft address: ")).trim();
|
|
4620
|
+
const { runMicrosoftLogin } = await import("./login-CYgla6-A.js");
|
|
4621
|
+
await runMicrosoftLogin({
|
|
4622
|
+
dataDir,
|
|
4623
|
+
clientId,
|
|
4624
|
+
user: account,
|
|
4625
|
+
tenant,
|
|
4626
|
+
print
|
|
4627
|
+
});
|
|
4628
|
+
console.log(success(`✓ Microsoft authorized for ${bold(account)}. Sync: dxcrm mailbox sync --account microsoft:${account}`));
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
console.error(error(`Unknown provider '${provider}'. Use 'gmail' or 'microsoft'.`));
|
|
4632
|
+
}
|
|
4476
4633
|
const mailboxCommand = new Command("mailbox").description("Sync any IMAP mailbox (Gmail, Outlook, custom) into the CRM");
|
|
4477
|
-
mailboxCommand.command("
|
|
4634
|
+
mailboxCommand.command("login").description("Authorize a Gmail or Microsoft mailbox via OAuth (stores tokens locally)").argument("<provider>", "gmail | microsoft").option("--user <email>", "Mailbox address (otherwise prompted)").action(async (provider, options) => {
|
|
4635
|
+
await runLogin(provider, options.user);
|
|
4636
|
+
});
|
|
4637
|
+
mailboxCommand.command("sync").description("Sync an IMAP mailbox; auto-routes to customers by domain unless a slug is given").argument("[slug]", "Route all mail to this customer (omit to auto-route by domain)").option("--account <provider:user>", "Use a stored OAuth mailbox (e.g. gmail:you@gmail.com)").option("--since <date>", "Only sync messages after this date (YYYY-MM-DD)").option("--no-attachments", "Skip downloading/converting/indexing attachments").action(async (slug, options) => {
|
|
4478
4638
|
await runMailboxSync({
|
|
4479
4639
|
dataDir: process.env["DXCRM_DATA_DIR"] ?? process.cwd(),
|
|
4480
4640
|
slug,
|
|
4641
|
+
...options.account ? { account: options.account } : {},
|
|
4481
4642
|
...options.since ? { since: new Date(options.since) } : {},
|
|
4482
4643
|
includeAttachments: options.attachments !== false
|
|
4483
4644
|
});
|
|
@@ -4505,6 +4666,7 @@ const ALL_COMMANDS = [
|
|
|
4505
4666
|
auditCommand,
|
|
4506
4667
|
logsCommand,
|
|
4507
4668
|
doctorCommand,
|
|
4669
|
+
pipelineCommand,
|
|
4508
4670
|
rbacCommand,
|
|
4509
4671
|
gdprCommand,
|
|
4510
4672
|
securityReportCommand,
|