@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.
@@ -0,0 +1,159 @@
1
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import { OAuth2Client } from "google-auth-library";
5
+ //#region src/sync/oauth/token-store.ts
6
+ function tokensPath(dataDir) {
7
+ return path.join(dataDir, ".agentic", "mailbox-tokens.json");
8
+ }
9
+ function keyOf(provider, user) {
10
+ return `${provider}:${user.toLowerCase()}`;
11
+ }
12
+ function readAll(dataDir) {
13
+ const file = tokensPath(dataDir);
14
+ if (!fs.existsSync(file)) return {};
15
+ try {
16
+ return JSON.parse(fs.readFileSync(file, "utf-8"));
17
+ } catch {
18
+ return {};
19
+ }
20
+ }
21
+ /** Persist (upsert) a mailbox OAuth token. */
22
+ function saveMailboxToken(dataDir, token) {
23
+ const all = readAll(dataDir);
24
+ all[keyOf(token.provider, token.user)] = token;
25
+ const file = tokensPath(dataDir);
26
+ fs.mkdirSync(path.dirname(file), { recursive: true });
27
+ writeFileAtomic(file, JSON.stringify(all, null, 2));
28
+ }
29
+ /** Load a stored token for a provider+user, or undefined. */
30
+ function loadMailboxToken(dataDir, provider, user) {
31
+ return readAll(dataDir)[keyOf(provider, user)];
32
+ }
33
+ /** True when the access token is missing or expires within `skewMs` (default 60s). */
34
+ function isTokenExpired(token, skewMs = 6e4, now = Date.now()) {
35
+ return !token.accessToken || token.expiresAt <= now + skewMs;
36
+ }
37
+ //#endregion
38
+ //#region src/sync/oauth/google.ts
39
+ const GMAIL_IMAP_SCOPE = "https://mail.google.com/";
40
+ /** Loopback redirect for the desktop/installed-app flow. */
41
+ const DEFAULT_REDIRECT = "http://127.0.0.1";
42
+ /** Build a real Google OAuth2 client for an installed/desktop app. */
43
+ function createOAuthClient(clientId, clientSecret, redirectUri = DEFAULT_REDIRECT) {
44
+ return new OAuth2Client(clientId, clientSecret, redirectUri);
45
+ }
46
+ /** The consent URL — offline access + forced consent so a refresh token is issued. */
47
+ function buildAuthUrl(client, redirectUri) {
48
+ return client.generateAuthUrl({
49
+ access_type: "offline",
50
+ prompt: "consent",
51
+ scope: GMAIL_IMAP_SCOPE,
52
+ ...redirectUri ? { redirect_uri: redirectUri } : {}
53
+ });
54
+ }
55
+ /** Exchange an authorization code for tokens. */
56
+ async function exchangeCodeForTokens(client, code, now = Date.now) {
57
+ const { tokens } = await client.getToken(code);
58
+ if (!tokens.access_token) throw new Error("Google did not return an access token");
59
+ return {
60
+ accessToken: tokens.access_token,
61
+ ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : {},
62
+ expiresAt: tokens.expiry_date ?? now() + 36e5
63
+ };
64
+ }
65
+ /** Mint a fresh access token from a stored refresh token. */
66
+ async function refreshGoogleToken(clientId, clientSecret, refreshToken, clientFactory = createOAuthClient, now = Date.now) {
67
+ const client = clientFactory(clientId, clientSecret);
68
+ client.setCredentials({ refresh_token: refreshToken });
69
+ const { credentials } = await client.refreshAccessToken();
70
+ if (!credentials.access_token) throw new Error("Google refresh did not return an access token");
71
+ return {
72
+ accessToken: credentials.access_token,
73
+ refreshToken,
74
+ expiresAt: credentials.expiry_date ?? now() + 36e5
75
+ };
76
+ }
77
+ //#endregion
78
+ //#region src/sync/oauth/microsoft.ts
79
+ const MS_IMAP_SCOPE = "offline_access https://outlook.office365.com/IMAP.AccessAsUser.All";
80
+ function endpoint(tenant, kind) {
81
+ return `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/${kind}`;
82
+ }
83
+ /** Start the device-code flow; returns the user code + verification URL to show. */
84
+ async function requestDeviceCode(clientId, tenant = "common", fetchFn = fetch) {
85
+ const res = await fetchFn(endpoint(tenant, "devicecode"), {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
88
+ body: new URLSearchParams({
89
+ client_id: clientId,
90
+ scope: MS_IMAP_SCOPE
91
+ }).toString()
92
+ });
93
+ if (!res.ok) throw new Error(`device code request failed: ${res.status} ${await res.text()}`);
94
+ return await res.json();
95
+ }
96
+ /**
97
+ * Poll the token endpoint until the user authorizes (or the code expires).
98
+ * Honors `authorization_pending` (keep waiting) and `slow_down` (back off).
99
+ */
100
+ async function pollForToken(opts) {
101
+ const tenant = opts.tenant ?? "common";
102
+ const fetchFn = opts.fetchFn ?? fetch;
103
+ const sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
104
+ const now = opts.now ?? Date.now;
105
+ let intervalMs = (opts.interval ?? 5) * 1e3;
106
+ const deadline = now() + (opts.expiresIn ?? 900) * 1e3;
107
+ for (;;) {
108
+ if (now() >= deadline) throw new Error("device code expired before authorization");
109
+ await sleepFn(intervalMs);
110
+ const res = await fetchFn(endpoint(tenant, "token"), {
111
+ method: "POST",
112
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
113
+ body: new URLSearchParams({
114
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
115
+ client_id: opts.clientId,
116
+ device_code: opts.deviceCode
117
+ }).toString()
118
+ });
119
+ const data = await res.json();
120
+ if (res.ok && "access_token" in data) return tokensFromSuccess(data, now());
121
+ const err = data.error;
122
+ if (err === "authorization_pending") continue;
123
+ if (err === "slow_down") {
124
+ intervalMs += 5e3;
125
+ continue;
126
+ }
127
+ throw new Error(`device flow failed: ${err}${describe(data)}`);
128
+ }
129
+ }
130
+ /** Exchange a refresh token for a fresh access token. */
131
+ async function refreshMicrosoftToken(clientId, refreshToken, tenant = "common", fetchFn = fetch, now = Date.now) {
132
+ const res = await fetchFn(endpoint(tenant, "token"), {
133
+ method: "POST",
134
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
135
+ body: new URLSearchParams({
136
+ grant_type: "refresh_token",
137
+ client_id: clientId,
138
+ refresh_token: refreshToken,
139
+ scope: MS_IMAP_SCOPE
140
+ }).toString()
141
+ });
142
+ const data = await res.json();
143
+ if (!res.ok || !("access_token" in data)) throw new Error(`token refresh failed: ${data.error ?? res.status}`);
144
+ return tokensFromSuccess(data, now());
145
+ }
146
+ function tokensFromSuccess(data, nowMs) {
147
+ return {
148
+ accessToken: data.access_token,
149
+ ...data.refresh_token ? { refreshToken: data.refresh_token } : {},
150
+ expiresAt: nowMs + data.expires_in * 1e3
151
+ };
152
+ }
153
+ function describe(e) {
154
+ return e.error_description ? ` (${e.error_description.split("\n")[0]})` : "";
155
+ }
156
+ //#endregion
157
+ export { buildAuthUrl as a, refreshGoogleToken as c, saveMailboxToken as d, DEFAULT_REDIRECT as i, isTokenExpired as l, refreshMicrosoftToken as n, createOAuthClient as o, requestDeviceCode as r, exchangeCodeForTokens as s, pollForToken as t, loadMailboxToken as u };
158
+
159
+ //# sourceMappingURL=microsoft-DgbVlHdT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-DgbVlHdT.js","names":[],"sources":["../src/sync/oauth/token-store.ts","../src/sync/oauth/google.ts","../src/sync/oauth/microsoft.ts"],"sourcesContent":["// src/sync/oauth/token-store.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"../../fs/atomic-write.js\";\n\nexport type MailboxProvider = \"gmail\" | \"microsoft\" | \"imap\";\n\nexport interface MailboxToken {\n provider: MailboxProvider;\n /** Mailbox login (email address). */\n user: string;\n accessToken: string;\n /** Long-lived refresh token used to mint new access tokens. */\n refreshToken?: string;\n /** Access-token expiry as epoch milliseconds. */\n expiresAt: number;\n scope?: string;\n}\n\nfunction tokensPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"mailbox-tokens.json\");\n}\n\nfunction keyOf(provider: MailboxProvider, user: string): string {\n return `${provider}:${user.toLowerCase()}`;\n}\n\nfunction readAll(dataDir: string): Record<string, MailboxToken> {\n const file = tokensPath(dataDir);\n if (!fs.existsSync(file)) return {};\n try {\n return JSON.parse(fs.readFileSync(file, \"utf-8\") as string) as Record<string, MailboxToken>;\n } catch {\n return {};\n }\n}\n\n/** Persist (upsert) a mailbox OAuth token. */\nexport function saveMailboxToken(dataDir: string, token: MailboxToken): void {\n const all = readAll(dataDir);\n all[keyOf(token.provider, token.user)] = token;\n const file = tokensPath(dataDir);\n fs.mkdirSync(path.dirname(file), { recursive: true });\n writeFileAtomic(file, JSON.stringify(all, null, 2));\n}\n\n/** Load a stored token for a provider+user, or undefined. */\nexport function loadMailboxToken(\n dataDir: string,\n provider: MailboxProvider,\n user: string\n): MailboxToken | undefined {\n return readAll(dataDir)[keyOf(provider, user)];\n}\n\n/** List all stored mailbox tokens. */\nexport function listMailboxTokens(dataDir: string): MailboxToken[] {\n return Object.values(readAll(dataDir));\n}\n\n/** True when the access token is missing or expires within `skewMs` (default 60s). */\nexport function isTokenExpired(token: MailboxToken, skewMs = 60_000, now = Date.now()): boolean {\n return !token.accessToken || token.expiresAt <= now + skewMs;\n}\n","// src/sync/oauth/google.ts\n//\n// Google OAuth2 for Gmail IMAP (XOAUTH2). IMAP requires the FULL mail scope\n// `https://mail.google.com/` — the gmail.readonly scope does NOT grant IMAP.\nimport { OAuth2Client } from \"google-auth-library\";\n\nexport const GMAIL_IMAP_SCOPE = \"https://mail.google.com/\";\n/** Loopback redirect for the desktop/installed-app flow. */\nexport const DEFAULT_REDIRECT = \"http://127.0.0.1\";\n\nexport interface GoogleTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt: number;\n}\n\n/** Minimal slice of OAuth2Client we use (lets tests inject a fake). */\nexport interface GoogleOAuthClient {\n generateAuthUrl(opts: {\n access_type?: string;\n scope?: string | string[];\n prompt?: string;\n redirect_uri?: string;\n }): string;\n getToken(code: string): Promise<{\n tokens: {\n access_token?: string | null;\n refresh_token?: string | null;\n expiry_date?: number | null;\n };\n }>;\n setCredentials(creds: { refresh_token?: string }): void;\n refreshAccessToken(): Promise<{\n credentials: { access_token?: string | null; expiry_date?: number | null };\n }>;\n}\n\n/** Build a real Google OAuth2 client for an installed/desktop app. */\nexport function createOAuthClient(\n clientId: string,\n clientSecret: string,\n redirectUri: string = DEFAULT_REDIRECT\n): GoogleOAuthClient {\n return new OAuth2Client(clientId, clientSecret, redirectUri) as unknown as GoogleOAuthClient;\n}\n\n/** The consent URL — offline access + forced consent so a refresh token is issued. */\nexport function buildAuthUrl(client: GoogleOAuthClient, redirectUri?: string): string {\n return client.generateAuthUrl({\n access_type: \"offline\",\n prompt: \"consent\",\n scope: GMAIL_IMAP_SCOPE,\n ...(redirectUri ? { redirect_uri: redirectUri } : {}),\n });\n}\n\n/** Exchange an authorization code for tokens. */\nexport async function exchangeCodeForTokens(\n client: GoogleOAuthClient,\n code: string,\n now: () => number = Date.now\n): Promise<GoogleTokens> {\n const { tokens } = await client.getToken(code);\n if (!tokens.access_token) throw new Error(\"Google did not return an access token\");\n return {\n accessToken: tokens.access_token,\n ...(tokens.refresh_token ? { refreshToken: tokens.refresh_token } : {}),\n expiresAt: tokens.expiry_date ?? now() + 3600_000,\n };\n}\n\n/** Mint a fresh access token from a stored refresh token. */\nexport async function refreshGoogleToken(\n clientId: string,\n clientSecret: string,\n refreshToken: string,\n clientFactory: (id: string, secret: string) => GoogleOAuthClient = createOAuthClient,\n now: () => number = Date.now\n): Promise<GoogleTokens> {\n const client = clientFactory(clientId, clientSecret);\n client.setCredentials({ refresh_token: refreshToken });\n const { credentials } = await client.refreshAccessToken();\n if (!credentials.access_token) throw new Error(\"Google refresh did not return an access token\");\n return {\n accessToken: credentials.access_token,\n refreshToken,\n expiresAt: credentials.expiry_date ?? now() + 3600_000,\n };\n}\n","// src/sync/oauth/microsoft.ts\n//\n// Microsoft OAuth2 *device code* flow for IMAP access (outlook.office365.com).\n// Device code suits a CLI: the user opens a URL on any device and enters a\n// short code — no local redirect server or client secret (public client).\n\nexport const MS_IMAP_SCOPE = \"offline_access https://outlook.office365.com/IMAP.AccessAsUser.All\";\n\ntype FetchFn = typeof fetch;\n\nfunction endpoint(tenant: string, kind: \"devicecode\" | \"token\"): string {\n return `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/${kind}`;\n}\n\nexport interface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n message: string;\n}\n\nexport interface MicrosoftTokens {\n accessToken: string;\n refreshToken?: string;\n expiresAt: number;\n}\n\n/** Start the device-code flow; returns the user code + verification URL to show. */\nexport async function requestDeviceCode(\n clientId: string,\n tenant = \"common\",\n fetchFn: FetchFn = fetch\n): Promise<DeviceCodeResponse> {\n const res = await fetchFn(endpoint(tenant, \"devicecode\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({ client_id: clientId, scope: MS_IMAP_SCOPE }).toString(),\n });\n if (!res.ok) throw new Error(`device code request failed: ${res.status} ${await res.text()}`);\n return (await res.json()) as DeviceCodeResponse;\n}\n\ninterface TokenSuccess {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n}\ninterface TokenError {\n error: string;\n error_description?: string;\n}\n\n/**\n * Poll the token endpoint until the user authorizes (or the code expires).\n * Honors `authorization_pending` (keep waiting) and `slow_down` (back off).\n */\nexport async function pollForToken(opts: {\n clientId: string;\n deviceCode: string;\n tenant?: string;\n interval?: number;\n expiresIn?: number;\n fetchFn?: FetchFn;\n sleepFn?: (ms: number) => Promise<void>;\n now?: () => number;\n}): Promise<MicrosoftTokens> {\n const tenant = opts.tenant ?? \"common\";\n const fetchFn = opts.fetchFn ?? fetch;\n const sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n const now = opts.now ?? Date.now;\n let intervalMs = (opts.interval ?? 5) * 1000;\n const deadline = now() + (opts.expiresIn ?? 900) * 1000;\n\n for (;;) {\n if (now() >= deadline) throw new Error(\"device code expired before authorization\");\n await sleepFn(intervalMs);\n\n const res = await fetchFn(endpoint(tenant, \"token\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n client_id: opts.clientId,\n device_code: opts.deviceCode,\n }).toString(),\n });\n\n const data = (await res.json()) as TokenSuccess | TokenError;\n if (res.ok && \"access_token\" in data) {\n return tokensFromSuccess(data, now());\n }\n const err = (data as TokenError).error;\n if (err === \"authorization_pending\") continue;\n if (err === \"slow_down\") {\n intervalMs += 5000;\n continue;\n }\n throw new Error(`device flow failed: ${err}${describe(data as TokenError)}`);\n }\n}\n\n/** Exchange a refresh token for a fresh access token. */\nexport async function refreshMicrosoftToken(\n clientId: string,\n refreshToken: string,\n tenant = \"common\",\n fetchFn: FetchFn = fetch,\n now: () => number = Date.now\n): Promise<MicrosoftTokens> {\n const res = await fetchFn(endpoint(tenant, \"token\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n client_id: clientId,\n refresh_token: refreshToken,\n scope: MS_IMAP_SCOPE,\n }).toString(),\n });\n const data = (await res.json()) as TokenSuccess | TokenError;\n if (!res.ok || !(\"access_token\" in data)) {\n throw new Error(`token refresh failed: ${(data as TokenError).error ?? res.status}`);\n }\n return tokensFromSuccess(data, now());\n}\n\nfunction tokensFromSuccess(data: TokenSuccess, nowMs: number): MicrosoftTokens {\n return {\n accessToken: data.access_token,\n ...(data.refresh_token ? { refreshToken: data.refresh_token } : {}),\n expiresAt: nowMs + data.expires_in * 1000,\n };\n}\n\nfunction describe(e: TokenError): string {\n return e.error_description ? ` (${e.error_description.split(\"\\n\")[0]})` : \"\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { n as logger } from "./logger-Dyl4VcLO.js";
2
+ import { c as refreshGoogleToken, d as saveMailboxToken, l as isTokenExpired, n as refreshMicrosoftToken, u as loadMailboxToken } from "./microsoft-DgbVlHdT.js";
3
+ //#region src/sync/oauth/token-resolver.ts
4
+ /**
5
+ * Return a valid access token for a stored mailbox account, transparently
6
+ * refreshing it (and persisting the new token) when it has expired. Throws a
7
+ * clear error when no token is stored or a re-login is required.
8
+ */
9
+ async function getFreshAccessToken(dataDir, provider, user, deps = {}) {
10
+ const env = deps.env ?? process.env;
11
+ const now = deps.now ?? Date.now;
12
+ const token = loadMailboxToken(dataDir, provider, user);
13
+ if (!token) throw new Error(`No stored ${provider} token for ${user}. Run 'dxcrm mailbox login ${provider}' first.`);
14
+ if (!isTokenExpired(token, 6e4, now())) return token.accessToken;
15
+ if (!token.refreshToken) throw new Error(`${provider} token for ${user} expired and has no refresh token — re-login.`);
16
+ logger.info("oauth", "refreshing mailbox access token", {
17
+ provider,
18
+ user
19
+ });
20
+ if (provider === "gmail") {
21
+ const clientId = env["DXCRM_GOOGLE_CLIENT_ID"];
22
+ const clientSecret = env["DXCRM_GOOGLE_CLIENT_SECRET"];
23
+ if (!clientId || !clientSecret) throw new Error("DXCRM_GOOGLE_CLIENT_ID / DXCRM_GOOGLE_CLIENT_SECRET are required to refresh.");
24
+ const fresh = await (deps.refreshGoogle ?? refreshGoogleToken)(clientId, clientSecret, token.refreshToken);
25
+ saveMailboxToken(dataDir, {
26
+ ...token,
27
+ accessToken: fresh.accessToken,
28
+ expiresAt: fresh.expiresAt
29
+ });
30
+ return fresh.accessToken;
31
+ }
32
+ if (provider === "microsoft") {
33
+ const clientId = env["DXCRM_MS_CLIENT_ID"];
34
+ if (!clientId) throw new Error("DXCRM_MS_CLIENT_ID is required to refresh.");
35
+ const tenant = env["DXCRM_MS_TENANT"] ?? "common";
36
+ const fresh = await (deps.refreshMicrosoft ?? refreshMicrosoftToken)(clientId, token.refreshToken, tenant);
37
+ saveMailboxToken(dataDir, {
38
+ ...token,
39
+ accessToken: fresh.accessToken,
40
+ ...fresh.refreshToken ? { refreshToken: fresh.refreshToken } : {},
41
+ expiresAt: fresh.expiresAt
42
+ });
43
+ return fresh.accessToken;
44
+ }
45
+ return token.accessToken;
46
+ }
47
+ //#endregion
48
+ export { getFreshAccessToken };
49
+
50
+ //# sourceMappingURL=token-resolver-BRLOmRvF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-resolver-BRLOmRvF.js","names":[],"sources":["../src/sync/oauth/token-resolver.ts"],"sourcesContent":["// src/sync/oauth/token-resolver.ts\nimport {\n loadMailboxToken,\n saveMailboxToken,\n isTokenExpired,\n type MailboxProvider,\n} from \"./token-store.js\";\nimport { refreshGoogleToken } from \"./google.js\";\nimport { refreshMicrosoftToken } from \"./microsoft.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport interface ResolverDeps {\n env?: NodeJS.ProcessEnv;\n now?: () => number;\n refreshGoogle?: typeof refreshGoogleToken;\n refreshMicrosoft?: typeof refreshMicrosoftToken;\n}\n\n/**\n * Return a valid access token for a stored mailbox account, transparently\n * refreshing it (and persisting the new token) when it has expired. Throws a\n * clear error when no token is stored or a re-login is required.\n */\nexport async function getFreshAccessToken(\n dataDir: string,\n provider: MailboxProvider,\n user: string,\n deps: ResolverDeps = {}\n): Promise<string> {\n const env = deps.env ?? process.env;\n const now = deps.now ?? Date.now;\n\n const token = loadMailboxToken(dataDir, provider, user);\n if (!token) {\n throw new Error(\n `No stored ${provider} token for ${user}. Run 'dxcrm mailbox login ${provider}' first.`\n );\n }\n if (!isTokenExpired(token, 60_000, now())) return token.accessToken;\n if (!token.refreshToken) {\n throw new Error(`${provider} token for ${user} expired and has no refresh token — re-login.`);\n }\n\n logger.info(\"oauth\", \"refreshing mailbox access token\", { provider, user });\n\n if (provider === \"gmail\") {\n const clientId = env[\"DXCRM_GOOGLE_CLIENT_ID\"];\n const clientSecret = env[\"DXCRM_GOOGLE_CLIENT_SECRET\"];\n if (!clientId || !clientSecret) {\n throw new Error(\n \"DXCRM_GOOGLE_CLIENT_ID / DXCRM_GOOGLE_CLIENT_SECRET are required to refresh.\"\n );\n }\n const refresh = deps.refreshGoogle ?? refreshGoogleToken;\n const fresh = await refresh(clientId, clientSecret, token.refreshToken);\n saveMailboxToken(dataDir, {\n ...token,\n accessToken: fresh.accessToken,\n expiresAt: fresh.expiresAt,\n });\n return fresh.accessToken;\n }\n\n if (provider === \"microsoft\") {\n const clientId = env[\"DXCRM_MS_CLIENT_ID\"];\n if (!clientId) throw new Error(\"DXCRM_MS_CLIENT_ID is required to refresh.\");\n const tenant = env[\"DXCRM_MS_TENANT\"] ?? \"common\";\n const refresh = deps.refreshMicrosoft ?? refreshMicrosoftToken;\n const fresh = await refresh(clientId, token.refreshToken, tenant);\n saveMailboxToken(dataDir, {\n ...token,\n accessToken: fresh.accessToken,\n ...(fresh.refreshToken ? { refreshToken: fresh.refreshToken } : {}),\n expiresAt: fresh.expiresAt,\n });\n return fresh.accessToken;\n }\n\n // \"imap\" provider uses a static password/token; nothing to refresh.\n return token.accessToken;\n}\n"],"mappings":";;;;;;;;AAuBA,eAAsB,oBACpB,SACA,UACA,MACA,OAAqB,CAAC,GACL;CACjB,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,MAAM,KAAK,OAAO,KAAK;CAE7B,MAAM,QAAQ,iBAAiB,SAAS,UAAU,IAAI;CACtD,IAAI,CAAC,OACH,MAAM,IAAI,MACR,aAAa,SAAS,aAAa,KAAK,6BAA6B,SAAS,SAChF;CAEF,IAAI,CAAC,eAAe,OAAO,KAAQ,IAAI,CAAC,GAAG,OAAO,MAAM;CACxD,IAAI,CAAC,MAAM,cACT,MAAM,IAAI,MAAM,GAAG,SAAS,aAAa,KAAK,8CAA8C;CAG9F,OAAO,KAAK,SAAS,mCAAmC;EAAE;EAAU;CAAK,CAAC;CAE1E,IAAI,aAAa,SAAS;EACxB,MAAM,WAAW,IAAI;EACrB,MAAM,eAAe,IAAI;EACzB,IAAI,CAAC,YAAY,CAAC,cAChB,MAAM,IAAI,MACR,8EACF;EAGF,MAAM,QAAQ,OADE,KAAK,iBAAiB,oBACV,UAAU,cAAc,MAAM,YAAY;EACtE,iBAAiB,SAAS;GACxB,GAAG;GACH,aAAa,MAAM;GACnB,WAAW,MAAM;EACnB,CAAC;EACD,OAAO,MAAM;CACf;CAEA,IAAI,aAAa,aAAa;EAC5B,MAAM,WAAW,IAAI;EACrB,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,4CAA4C;EAC3E,MAAM,SAAS,IAAI,sBAAsB;EAEzC,MAAM,QAAQ,OADE,KAAK,oBAAoB,uBACb,UAAU,MAAM,cAAc,MAAM;EAChE,iBAAiB,SAAS;GACxB,GAAG;GACH,aAAa,MAAM;GACnB,GAAI,MAAM,eAAe,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;GACjE,WAAW,MAAM;EACnB,CAAC;EACD,OAAO,MAAM;CACf;CAGA,OAAO,MAAM;AACf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datasynx/agentic-crm",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "The CRM your AI agents actually run — local-first, MCP-native, one autonomous agent per customer. 58 MCP tools for Claude Code, Codex & Cursor.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-Dspvybo0.d.cts","names":[],"sources":["../src/schemas/sources.ts","../src/schemas/main-facts.ts","../src/schemas/interaction.ts","../src/schemas/pipeline.ts","../src/schemas/ticket.ts","../src/schemas/quote.ts","../src/schemas/kb-article.ts","../src/schemas/survey.ts","../src/commands/create.ts","../src/commands/backup.ts","../src/commands/audit.ts","../src/commands/validate.ts","../src/fs/customer-dir.ts","../src/fs/audit-log.ts","../src/core/rbac.ts","../src/core/session-store.ts","../src/version.ts"],"mappings":"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"}