@datasynx/agentic-crm 1.4.0 → 1.5.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 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
- DXCRM_IMAP_HOST=outlook.office365.com DXCRM_IMAP_USER=me@org.com DXCRM_IMAP_TOKEN=ey... \
247
- dxcrm mailbox sync # auto-route the whole mailbox to customers by domain
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
@@ -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 });
@@ -4427,6 +4428,17 @@ complianceCommand.command("status", { isDefault: true }).description("Show the a
4427
4428
  });
4428
4429
  //#endregion
4429
4430
  //#region src/commands/mailbox.ts
4431
+ /** Default IMAP endpoints for OAuth providers. */
4432
+ const PROVIDER_IMAP_HOST = {
4433
+ gmail: {
4434
+ host: "imap.gmail.com",
4435
+ port: 993
4436
+ },
4437
+ microsoft: {
4438
+ host: "outlook.office365.com",
4439
+ port: 993
4440
+ }
4441
+ };
4430
4442
  /** Read IMAP mailbox connection settings from the environment. */
4431
4443
  function imapConfigFromEnv(env = process.env) {
4432
4444
  const host = env["DXCRM_IMAP_HOST"];
@@ -4448,15 +4460,54 @@ function imapConfigFromEnv(env = process.env) {
4448
4460
  }
4449
4461
  };
4450
4462
  }
4463
+ /** Parse a "provider:user" account string. */
4464
+ function parseAccount(account) {
4465
+ const idx = account.indexOf(":");
4466
+ if (idx < 0) return null;
4467
+ const provider = account.slice(0, idx);
4468
+ const user = account.slice(idx + 1);
4469
+ if (provider !== "gmail" && provider !== "microsoft" || !user) return null;
4470
+ return {
4471
+ provider,
4472
+ user
4473
+ };
4474
+ }
4475
+ /** Build an IMAP config for a stored OAuth account, refreshing the token if needed. */
4476
+ async function resolveAccountConfig(dataDir, account, env = process.env, mailbox) {
4477
+ const parsed = parseAccount(account);
4478
+ if (!parsed) throw new Error(`Invalid --account '${account}'. Use 'gmail:you@gmail.com' or 'microsoft:you@org.com'.`);
4479
+ const { getFreshAccessToken } = await import("./token-resolver-BRLOmRvF.js");
4480
+ const accessToken = await getFreshAccessToken(dataDir, parsed.provider, parsed.user, { env });
4481
+ const { host, port } = PROVIDER_IMAP_HOST[parsed.provider];
4482
+ return {
4483
+ host,
4484
+ port,
4485
+ secure: true,
4486
+ mailbox: mailbox ?? env["DXCRM_IMAP_MAILBOX"] ?? "INBOX",
4487
+ auth: {
4488
+ user: parsed.user,
4489
+ accessToken
4490
+ }
4491
+ };
4492
+ }
4451
4493
  /**
4452
4494
  * Sync an IMAP mailbox (any provider). With a slug, all mail goes to that one
4453
4495
  * customer; without, mail is auto-routed to customers by sender/recipient
4454
- * domain and unmatched mail is reported as unrouted.
4496
+ * domain and unmatched mail is reported as unrouted. Use `account` to sync a
4497
+ * stored OAuth mailbox (Gmail/Outlook) instead of env-configured IMAP.
4455
4498
  */
4456
4499
  async function runMailboxSync(opts) {
4457
- const config = imapConfigFromEnv(opts.env ?? process.env);
4500
+ const env = opts.env ?? process.env;
4501
+ let config;
4502
+ try {
4503
+ config = opts.account ? await resolveAccountConfig(opts.dataDir, opts.account, env) : imapConfigFromEnv(env);
4504
+ } catch (err) {
4505
+ const msg = err.message;
4506
+ console.error(error(msg));
4507
+ return { error: msg };
4508
+ }
4458
4509
  if (!config) {
4459
- const msg = "IMAP not configured. Set DXCRM_IMAP_HOST, DXCRM_IMAP_USER and DXCRM_IMAP_PASS (or DXCRM_IMAP_TOKEN).";
4510
+ const msg = "IMAP not configured. Set DXCRM_IMAP_HOST, DXCRM_IMAP_USER and DXCRM_IMAP_PASS (or DXCRM_IMAP_TOKEN), or use --account.";
4460
4511
  console.error(error(msg));
4461
4512
  return { error: msg };
4462
4513
  }
@@ -4473,11 +4524,67 @@ async function runMailboxSync(opts) {
4473
4524
  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
4525
  return result;
4475
4526
  }
4527
+ function ask(question) {
4528
+ const rl = readline.createInterface({
4529
+ input: process.stdin,
4530
+ output: process.stdout
4531
+ });
4532
+ return new Promise((resolve) => rl.question(question, (a) => (rl.close(), resolve(a))));
4533
+ }
4534
+ /** Interactive `dxcrm mailbox login <provider>` flow. */
4535
+ async function runLogin(provider, user) {
4536
+ const dataDir = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4537
+ const print = (l) => console.log(l);
4538
+ if (provider === "gmail") {
4539
+ const clientId = process.env["DXCRM_GOOGLE_CLIENT_ID"];
4540
+ const clientSecret = process.env["DXCRM_GOOGLE_CLIENT_SECRET"];
4541
+ if (!clientId || !clientSecret) {
4542
+ console.error(error("Set DXCRM_GOOGLE_CLIENT_ID and DXCRM_GOOGLE_CLIENT_SECRET (OAuth desktop app)."));
4543
+ return;
4544
+ }
4545
+ const account = user ?? (await ask("Gmail address: ")).trim();
4546
+ const { runGmailLogin } = await import("./login-CYgla6-A.js");
4547
+ await runGmailLogin({
4548
+ dataDir,
4549
+ clientId,
4550
+ clientSecret,
4551
+ user: account,
4552
+ prompt: ask,
4553
+ print
4554
+ });
4555
+ console.log(success(`✓ Gmail authorized for ${bold(account)}. Sync: dxcrm mailbox sync --account gmail:${account}`));
4556
+ return;
4557
+ }
4558
+ if (provider === "microsoft") {
4559
+ const clientId = process.env["DXCRM_MS_CLIENT_ID"];
4560
+ if (!clientId) {
4561
+ console.error(error("Set DXCRM_MS_CLIENT_ID (Azure app registration, public client)."));
4562
+ return;
4563
+ }
4564
+ const tenant = process.env["DXCRM_MS_TENANT"] ?? "common";
4565
+ const account = user ?? (await ask("Outlook/Microsoft address: ")).trim();
4566
+ const { runMicrosoftLogin } = await import("./login-CYgla6-A.js");
4567
+ await runMicrosoftLogin({
4568
+ dataDir,
4569
+ clientId,
4570
+ user: account,
4571
+ tenant,
4572
+ print
4573
+ });
4574
+ console.log(success(`✓ Microsoft authorized for ${bold(account)}. Sync: dxcrm mailbox sync --account microsoft:${account}`));
4575
+ return;
4576
+ }
4577
+ console.error(error(`Unknown provider '${provider}'. Use 'gmail' or 'microsoft'.`));
4578
+ }
4476
4579
  const mailboxCommand = new Command("mailbox").description("Sync any IMAP mailbox (Gmail, Outlook, custom) into the CRM");
4477
- 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("--since <date>", "Only sync messages after this date (YYYY-MM-DD)").option("--no-attachments", "Skip downloading/converting/indexing attachments").action(async (slug, options) => {
4580
+ 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) => {
4581
+ await runLogin(provider, options.user);
4582
+ });
4583
+ 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
4584
  await runMailboxSync({
4479
4585
  dataDir: process.env["DXCRM_DATA_DIR"] ?? process.cwd(),
4480
4586
  slug,
4587
+ ...options.account ? { account: options.account } : {},
4481
4588
  ...options.since ? { since: new Date(options.since) } : {},
4482
4589
  includeAttachments: options.attachments !== false
4483
4590
  });