@symerian/symi 2.8.10 → 2.8.12
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/dist/{agents-BbVxJTp2.js → agents-OAonMxYV.js} +4 -4
- package/dist/{agents.config-LE2JxgiS.js → agents.config-BIhaDwT2.js} +1 -1
- package/dist/{agents.config-DckCExUX.js → agents.config-DGu_K5xz.js} +1 -1
- package/dist/{audio-preflight-NdNUAZ0y.js → audio-preflight-C6vpFnOG.js} +4 -4
- package/dist/{audio-preflight-rjCWpcfE.js → audio-preflight-Cb-T0r6e.js} +4 -4
- package/dist/{audit-CNU0I_UT.js → audit-Bi9Je9FZ.js} +1 -1
- package/dist/{audit-F5zQ_Wk9.js → audit-Oa5dsn5p.js} +1 -1
- package/dist/{auth-choice-j1FEYnxv.js → auth-choice-BvPX7B4c.js} +1 -1
- package/dist/{auth-choice-DnKOB8Gs.js → auth-choice-DDzWns1k.js} +1 -1
- package/dist/{banner-BFr8n0of.js → banner-BFdiq-O5.js} +1 -1
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +7 -7
- package/dist/bundled/session-memory/handler.js +7 -7
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{channel-options-DrBxsMlw.js → channel-options-DTqUB0OP.js} +1 -1
- package/dist/{channel-options-BtmFCQOB.js → channel-options-tfnOGSNF.js} +1 -1
- package/dist/{channel-web-YgdajAXX.js → channel-web-CcUXobcu.js} +1 -1
- package/dist/{channels-cli-fYbw-4gz.js → channels-cli-BcE5tHIr.js} +7 -7
- package/dist/{channels-cli-ChS_nQcP.js → channels-cli-CmShA-wJ.js} +7 -7
- package/dist/{chrome-BUT--ob3.js → chrome-B14NNyfm.js} +7 -7
- package/dist/{chrome-C08Z0XAa.js → chrome-ROtrXlNs.js} +7 -7
- package/dist/{cli-CA4qcSFY.js → cli-BxoD2wzk.js} +4 -4
- package/dist/{cli-NfoZJEPh.js → cli-CKG2iAr3.js} +4 -4
- package/dist/{command-registry-B6spVZMd.js → command-registry-D1Le-Fmh.js} +10 -10
- package/dist/{completion-cli-VQrV_JN9.js → completion-cli-D9pC-ttw.js} +2 -2
- package/dist/{completion-cli-DKoZNVbW.js → completion-cli-DgiFjSGS.js} +1 -1
- package/dist/{config-cli-CgC3xSoL.js → config-cli-5mFA0UDP.js} +1 -1
- package/dist/{config-cli-Dm2QMcwn.js → config-cli-C_uZQsTl.js} +1 -1
- package/dist/{configure-COCCg2tV.js → configure-BJcy7wXm.js} +3 -3
- package/dist/{configure-DiDXmX3E.js → configure-DuRTFCuW.js} +3 -3
- package/dist/{deliver-qUx-eLKt.js → deliver-B0OUq6RP.js} +1 -1
- package/dist/{deliver-B_Q_nWJV.js → deliver-D0bWiRCg.js} +1 -1
- package/dist/{doctor-completion-CPFKFTc-.js → doctor-completion-DCRTHpiY.js} +1 -1
- package/dist/{doctor-completion-BR5k35Qj.js → doctor-completion-sQVhKKei.js} +1 -1
- package/dist/entry.js +1 -1
- package/dist/extensionAPI.js +7 -7
- package/dist/{gateway-cli-BJH2K8fn.js → gateway-cli-C3ujhxO0.js} +13 -13
- package/dist/{gateway-cli-BF1-DFEf.js → gateway-cli-C_MT4JFm.js} +13 -13
- package/dist/{glass-ui-ws-B7j7iTDg.js → glass-ui-ws-B7zRY2BL.js} +11 -11
- package/dist/{glass-ui-ws-DyoHQW3O.js → glass-ui-ws-ColAiWUr.js} +11 -11
- package/dist/{health-D-0xOxV8.js → health-C12hy3Ao.js} +1 -1
- package/dist/{health-BAjSATWA.js → health-vKJZ7iJR.js} +1 -1
- package/dist/{hooks-cli-ZZaM2xfC.js → hooks-cli-CdUQvj8s.js} +5 -5
- package/dist/{hooks-cli-BaoCFIbQ.js → hooks-cli-fCPl5hur.js} +5 -5
- package/dist/{image-C6rCON9L.js → image-BuVL0jHI.js} +1 -1
- package/dist/{image-tzIqIuKx.js → image-MNvheU8U.js} +1 -1
- package/dist/index.js +9 -9
- package/dist/llm-slug-generator.js +7 -7
- package/dist/{manager-BpvcDr-7.js → manager-BJvYQ7xP.js} +1 -1
- package/dist/{manager-BxJ9BhQe.js → manager-CLrJn9l-.js} +1 -1
- package/dist/{manager-B5JWZL0E.js → manager-DHRBy5oR.js} +1 -1
- package/dist/{manager-DUuPE1N1.js → manager-Df_ZdrNG.js} +1 -1
- package/dist/{memory-cli-r-ulsUBa.js → memory-cli-BgJciaHC.js} +3 -3
- package/dist/{memory-cli-BQZ0rTKC.js → memory-cli-T-a5-lqZ.js} +3 -3
- package/dist/{models-B1__62Qo.js → models-BXXV_sZU.js} +2 -2
- package/dist/{models-cli-BGpB4PAp.js → models-cli-BI-DyItH.js} +5 -5
- package/dist/{models-cli-Ch-4PBud.js → models-cli-Bs6JAGq-.js} +6 -6
- package/dist/{onboard-D2NC88Mp.js → onboard-Czi_FRdi.js} +2 -2
- package/dist/{onboard-D_2-lyyt.js → onboard-DKDCWveE.js} +2 -2
- package/dist/{onboard-channels-CYlZtWF6.js → onboard-channels-BUCYZF02.js} +1 -1
- package/dist/{onboard-channels-BgA4i9TU.js → onboard-channels-sTuhQBVu.js} +1 -1
- package/dist/{onboarding-D7N2AfyJ.js → onboarding-Bgx0qZhA.js} +3 -3
- package/dist/{onboarding-uVLsv2Sd.js → onboarding-CokRRVEi.js} +3 -3
- package/dist/{onboarding.finalize-CTDv0xwp.js → onboarding.finalize-BCv5jB0d.js} +9 -9
- package/dist/{onboarding.finalize-DEjTjgRt.js → onboarding.finalize-BzEKyraI.js} +8 -8
- package/dist/{pi-embedded-HSRJqesT.js → pi-embedded-DXY7TLac.js} +27 -20
- package/dist/{pi-embedded-helpers-P13adotN.js → pi-embedded-helpers-D1_Sab0M.js} +4 -4
- package/dist/{pi-embedded-helpers-DjSdA5BG.js → pi-embedded-helpers-TcYQOZAY.js} +4 -4
- package/dist/{pi-tools.policy-BPVIDK7o.js → pi-tools.policy-BZrM6a-w.js} +8 -1
- package/dist/{pi-tools.policy-dbCkhLDL.js → pi-tools.policy-QIVWAVVI.js} +8 -1
- package/dist/{plugin-registry-Chvg6kT0.js → plugin-registry-B2cpTmJz.js} +1 -1
- package/dist/{plugin-registry-DEKq3ti3.js → plugin-registry-CyrsJDyE.js} +1 -1
- package/dist/plugin-sdk/{channel-web-CfaamthT.js → channel-web-t6enTxNE.js} +1 -1
- package/dist/plugin-sdk/index.js +3 -3
- package/dist/plugin-sdk/{manager-B3jEviU1.js → manager-LRE7zEk2.js} +1 -1
- package/dist/plugin-sdk/{reply-DWwxgBtH.js → reply-JypoGhql.js} +13 -6
- package/dist/plugin-sdk/{synthesis-C_u94H_P.js → synthesis-_WpVyZKx.js} +2 -2
- package/dist/plugin-sdk/{web-DFvYfej5.js → web-BTVj4kiQ.js} +3 -3
- package/dist/{plugins-cli-QZlpLtva.js → plugins-cli-B-zoQyqU.js} +5 -5
- package/dist/{plugins-cli-Cd99p333.js → plugins-cli-DJwRd3EZ.js} +5 -5
- package/dist/{program-viRCc6Je.js → program-H7h6Iqrq.js} +10 -10
- package/dist/{program-context-Dun-c3nf.js → program-context-BgecQAHU.js} +19 -19
- package/dist/{prompt-select-styled-DVLibGeR.js → prompt-select-styled-BDN2o35i.js} +6 -6
- package/dist/{prompt-select-styled-W2G26g34.js → prompt-select-styled-BglmD29e.js} +6 -6
- package/dist/{provider-auth-helpers-KFQclkJT.js → provider-auth-helpers-BGIgr7xM.js} +1 -1
- package/dist/{provider-auth-helpers-DCEiDG3X.js → provider-auth-helpers-MfKmTeLY.js} +1 -1
- package/dist/{push-apns-tfhjtI57.js → push-apns-IcfSTzfr.js} +1 -1
- package/dist/{push-apns-CB_8WzQ9.js → push-apns-b6bH1EBC.js} +1 -1
- package/dist/{pw-ai-CQtaPvM8.js → pw-ai-40Jf9QIb.js} +1 -1
- package/dist/{pw-ai-CeWN4iD9.js → pw-ai-DWkC5eGA.js} +1 -1
- package/dist/{register.agent--QTUjLu9.js → register.agent-BgyETrrj.js} +8 -8
- package/dist/{register.agent-i2wi1-vo.js → register.agent-ChwMSIC4.js} +9 -9
- package/dist/{register.configure-B9JysEK8.js → register.configure-Bm4CvoLF.js} +9 -9
- package/dist/{register.configure-ByJcC9t4.js → register.configure-DH3L2tqW.js} +9 -9
- package/dist/{register.maintenance-DqJL_QWT.js → register.maintenance-BbOBw1eI.js} +10 -10
- package/dist/{register.maintenance-BGiYxRYm.js → register.maintenance-CAyHUEzP.js} +11 -11
- package/dist/{register.message-G6-UeGON.js → register.message-DQ3MOWFE.js} +5 -5
- package/dist/{register.message-DT_TqsFl.js → register.message-DheqsiR0.js} +5 -5
- package/dist/{register.onboard-CfySx27T.js → register.onboard-CxqR4S1C.js} +7 -7
- package/dist/{register.onboard-848EXgTB.js → register.onboard-DOJkUhoY.js} +7 -7
- package/dist/{register.setup-B40A19lI.js → register.setup-48YBHzMZ.js} +7 -7
- package/dist/{register.setup-C-NYSAGY.js → register.setup-zdrIJA2P.js} +7 -7
- package/dist/{register.status-health-sessions-B03EDfwZ.js → register.status-health-sessions-CeT5pVXb.js} +7 -7
- package/dist/{register.status-health-sessions-BACExyrd.js → register.status-health-sessions-CimrhScH.js} +7 -7
- package/dist/{register.subclis-kF8KnNuq.js → register.subclis-B-zh940S.js} +10 -10
- package/dist/{reply-C5VU6T-F.js → reply-CUR4xpYM.js} +5 -5
- package/dist/{run-main-BqpvNq8c.js → run-main-CW_kj5Lj.js} +18 -18
- package/dist/{runner-Dpjulwnm.js → runner-DFuAePEr.js} +1 -1
- package/dist/{runner-D633VT13.js → runner-ecX1WzDt.js} +1 -1
- package/dist/{security-cli-ChPAyhec.js → security-cli-BpnzAB5_.js} +2 -2
- package/dist/{security-cli-Cq56CAVI.js → security-cli-D9WEiEjD.js} +2 -2
- package/dist/{server-methods-DjB0hxeW.js → server-methods-C1h0A5n0.js} +39 -15
- package/dist/{server-methods-CkLzZq0Y.js → server-methods-C4id-H_X.js} +39 -15
- package/dist/{server-node-events-BM-APRHy.js → server-node-events-BPZEGV_N.js} +5 -5
- package/dist/{server-node-events-ZgCh4sCg.js → server-node-events-CD2yDaEj.js} +5 -5
- package/dist/{status-WMQ2CpbK.js → status-Cb932Vl4.js} +4 -4
- package/dist/{status-C_gwgMp4.js → status-DCeW2a07.js} +4 -4
- package/dist/{status-BhQk3JSz.js → status-DvHD1cnq.js} +1 -1
- package/dist/{status-Nf53o222.js → status-_xBnHTlx.js} +1 -1
- package/dist/{subagent-registry-BCWbFTGF.js → subagent-registry-BKuL9PdW.js} +5 -5
- package/dist/{synthesis-VfWtSYrv.js → synthesis-BUqDGjlc.js} +7 -7
- package/dist/{synthesis-CJIAYDoU.js → synthesis-C9RputIi.js} +4 -4
- package/dist/{synthesis-CWsrtigA.js → synthesis-DOiTUsXQ.js} +7 -7
- package/dist/{synthesis-DIKBPZgB.js → synthesis-Ia9egx87.js} +4 -4
- package/dist/{unified-runner-yPBTU4xt.js → unified-runner-CVItnwwP.js} +27 -20
- package/dist/{update-cli-BCFHfhUW.js → update-cli-ChEABesZ.js} +11 -11
- package/dist/{update-cli-B_Mxicbw.js → update-cli-D6ax3jSX.js} +10 -10
- package/dist/{update-runner-DG5x7t--.js → update-runner-B2EY9BWM.js} +1 -1
- package/dist/{update-runner-BQJSshFU.js → update-runner-CWG6GxKs.js} +1 -1
- package/dist/{web-TVVa5EDS.js → web-CpSE67Jp.js} +5 -5
- package/dist/{web-Dpfsnk-b.js → web-D6PPeyG7.js} +4 -4
- package/dist/{web-eJWNRwV5.js → web-DMdb2p-h.js} +7 -7
- package/dist/{web-D9_FatXM.js → web-z-yQ8riS.js} +7 -7
- package/docs/reference/templates/AGENTS.md +14 -0
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/learning-loop/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +12 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +12 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +12 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/outlook/index.ts +81 -7
- package/extensions/outlook/package.json +1 -1
- package/extensions/outlook/src/store.ts +118 -11
- package/extensions/pipeline/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +12 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +12 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +12 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +12 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { refreshAccessToken } from "./auth.js";
|
|
4
5
|
|
|
@@ -9,34 +10,105 @@ export type OutlookCredentials = {
|
|
|
9
10
|
email?: string;
|
|
10
11
|
displayName?: string;
|
|
11
12
|
updatedAt?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Unix ms of the most recent moment we KNOW the refresh token worked —
|
|
15
|
+
* set on OAuth-flow success and on every successful refreshAccessToken.
|
|
16
|
+
* Consumed by `resolveConnectionState` to tell the difference between
|
|
17
|
+
* "we hold a refresh string" (previous weak check) and "the refresh
|
|
18
|
+
* string actually produced a valid access token recently." If this
|
|
19
|
+
* field is absent on an existing credentials file (pre-2.8.11), we
|
|
20
|
+
* treat it as "never verified" and the state falls back to "stale" —
|
|
21
|
+
* a safe default that surfaces the uncertainty rather than silently
|
|
22
|
+
* claiming "connected."
|
|
23
|
+
*/
|
|
24
|
+
lastVerifiedAt?: number;
|
|
12
25
|
};
|
|
13
26
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
/**
|
|
28
|
+
* How long a successful refresh is trusted before we downgrade the
|
|
29
|
+
* reported connection state to "stale" (still usable — the next tool
|
|
30
|
+
* call will attempt a refresh — but the system prompt hedges the claim
|
|
31
|
+
* so the agent doesn't assert a connection that may have been
|
|
32
|
+
* invalidated upstream).
|
|
33
|
+
*/
|
|
34
|
+
export const REFRESH_TRUST_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Tri-state connection classification. Consumed by the plugin's system-
|
|
38
|
+
* prompt injection (extensions/outlook/index.ts) and by the gateway
|
|
39
|
+
* status RPC (src/gateway/server-methods/channels.ts) so both surface
|
|
40
|
+
* the same truth.
|
|
41
|
+
*
|
|
42
|
+
* - valid access token still inside its expiry window
|
|
43
|
+
* - trusted access expired, but refresh was verified < 24h ago
|
|
44
|
+
* - stale access expired, refresh never verified or > 24h ago
|
|
45
|
+
* (still attempt refresh on next tool call; may or may
|
|
46
|
+
* not succeed — Microsoft could have invalidated the
|
|
47
|
+
* refresh token without telling us)
|
|
48
|
+
* - not-connected no credentials on disk, or creds file is unreadable
|
|
49
|
+
*/
|
|
50
|
+
export type OutlookConnectionState = "valid" | "trusted" | "stale" | "not-connected";
|
|
51
|
+
|
|
52
|
+
function resolveHome(): string {
|
|
53
|
+
const home = os.homedir();
|
|
54
|
+
if (home && home.length > 0) {
|
|
55
|
+
return home;
|
|
56
|
+
}
|
|
57
|
+
// Defence-in-depth — os.homedir() is the canonical resolution and
|
|
58
|
+
// consults /etc/passwd / platform APIs before falling back to the env.
|
|
59
|
+
// If it returns empty, environment variables are the next-best bet.
|
|
60
|
+
// Absolute last resort is /var/symi rather than /tmp — /tmp is purged
|
|
61
|
+
// by the OS on a cadence that silently eats OAuth tokens.
|
|
62
|
+
return process.env.HOME || process.env.USERPROFILE || "/var/symi";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getStoreDir(): string {
|
|
66
|
+
return path.join(resolveHome(), ".symi", "credentials");
|
|
67
|
+
}
|
|
19
68
|
|
|
20
|
-
|
|
69
|
+
export function getStorePath(): string {
|
|
70
|
+
return path.join(getStoreDir(), "outlook.json");
|
|
71
|
+
}
|
|
21
72
|
|
|
22
73
|
export function loadCredentials(): OutlookCredentials | null {
|
|
23
74
|
try {
|
|
24
|
-
const data = fs.readFileSync(
|
|
75
|
+
const data = fs.readFileSync(getStorePath(), "utf-8");
|
|
25
76
|
return JSON.parse(data) as OutlookCredentials;
|
|
26
77
|
} catch {
|
|
27
78
|
return null;
|
|
28
79
|
}
|
|
29
80
|
}
|
|
30
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Atomic write: tmp file + fsync + rename. Prevents torn writes if the
|
|
84
|
+
* process crashes mid-save (which would otherwise leave a zero-byte
|
|
85
|
+
* file and look like a missing credential to every subsequent load).
|
|
86
|
+
*
|
|
87
|
+
* Caller contract: every save is VERIFIED — saveCredentials only runs
|
|
88
|
+
* after a successful OAuth flow or successful refreshAccessToken, so
|
|
89
|
+
* stamping `lastVerifiedAt = now` unconditionally is correct. If a
|
|
90
|
+
* future code path needs to save unverified credentials (e.g. partial
|
|
91
|
+
* OAuth in progress), introduce a separate saveUnverifiedCredentials
|
|
92
|
+
* that skips the stamp.
|
|
93
|
+
*/
|
|
31
94
|
export function saveCredentials(creds: OutlookCredentials): void {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
95
|
+
const dir = getStoreDir();
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const stamped: OutlookCredentials = {
|
|
99
|
+
...creds,
|
|
100
|
+
updatedAt: new Date(now).toISOString(),
|
|
101
|
+
lastVerifiedAt: now,
|
|
102
|
+
};
|
|
103
|
+
const finalPath = getStorePath();
|
|
104
|
+
const tmpPath = `${finalPath}.tmp-${process.pid}-${now}`;
|
|
105
|
+
fs.writeFileSync(tmpPath, JSON.stringify(stamped, null, 2), { mode: 0o600 });
|
|
106
|
+
fs.renameSync(tmpPath, finalPath);
|
|
35
107
|
}
|
|
36
108
|
|
|
37
109
|
export function deleteCredentials(): boolean {
|
|
38
110
|
try {
|
|
39
|
-
fs.unlinkSync(
|
|
111
|
+
fs.unlinkSync(getStorePath());
|
|
40
112
|
return true;
|
|
41
113
|
} catch {
|
|
42
114
|
return false;
|
|
@@ -46,6 +118,12 @@ export function deleteCredentials(): boolean {
|
|
|
46
118
|
/**
|
|
47
119
|
* Returns a valid access token, refreshing if expired.
|
|
48
120
|
* Throws if no credentials stored or refresh fails.
|
|
121
|
+
*
|
|
122
|
+
* On successful refresh, re-saves credentials — which re-stamps
|
|
123
|
+
* `lastVerifiedAt` via the saveCredentials contract. This is how
|
|
124
|
+
* `stale` transitions back to `trusted` (and the agent's system-
|
|
125
|
+
* prompt block flips back to the unqualified "Connected as X" form)
|
|
126
|
+
* as soon as a tool call succeeds.
|
|
49
127
|
*/
|
|
50
128
|
export async function getAccessToken(): Promise<string> {
|
|
51
129
|
const creds = loadCredentials();
|
|
@@ -70,3 +148,32 @@ export async function getAccessToken(): Promise<string> {
|
|
|
70
148
|
});
|
|
71
149
|
return refreshed.access;
|
|
72
150
|
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Classify the current connection based on the credential state — used
|
|
154
|
+
* by both the plugin's `before_prompt_build` hook and the gateway's
|
|
155
|
+
* `channels.status` RPC so both surfaces agree on the truth.
|
|
156
|
+
*
|
|
157
|
+
* See the OutlookConnectionState doc for the meaning of each state.
|
|
158
|
+
*/
|
|
159
|
+
export function resolveConnectionState(
|
|
160
|
+
creds: OutlookCredentials | null,
|
|
161
|
+
now: number = Date.now(),
|
|
162
|
+
): OutlookConnectionState {
|
|
163
|
+
if (!creds) {
|
|
164
|
+
return "not-connected";
|
|
165
|
+
}
|
|
166
|
+
if (typeof creds.expires === "number" && now < creds.expires) {
|
|
167
|
+
return "valid";
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
typeof creds.lastVerifiedAt === "number" &&
|
|
171
|
+
now - creds.lastVerifiedAt < REFRESH_TRUST_WINDOW_MS
|
|
172
|
+
) {
|
|
173
|
+
return "trusted";
|
|
174
|
+
}
|
|
175
|
+
if (typeof creds.refresh === "string" && creds.refresh.length > 0) {
|
|
176
|
+
return "stale";
|
|
177
|
+
}
|
|
178
|
+
return "not-connected";
|
|
179
|
+
}
|