@duckmind/dm-windows-x64 0.63.4 → 0.63.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +236 -13
- package/extensions/dm-context/package.json +1 -1
- package/extensions/dm-context/skills/context-management/SKILL.md +173 -223
- package/extensions/dm-context/skills/context-management/references/development-and-troubleshooting.md +75 -0
- package/extensions/dm-context/skills/context-management/references/interleaved-async-work.md +143 -0
- package/extensions/dm-context/skills/context-management/references/planning-and-execution.md +81 -0
- package/extensions/dm-context/skills/context-management/references/repeated-items-and-batch-work.md +69 -0
- package/extensions/dm-context/skills/context-management/references/retry-branch-and-pivot.md +80 -0
- package/extensions/dm-context/skills/context-management/references/search-research-and-reading.md +103 -0
- package/extensions/dm-context/skills/context-management/references/task-switching-and-cleanup.md +73 -0
- package/extensions/dm-context/src/context.js +3 -2
- package/extensions/dm-context/src/index.js +168 -84
- package/extensions/dm-mcp-adapter/OAUTH.md +3 -0
- package/extensions/dm-mcp-adapter/THIRD_PARTY_NOTICES.md +4 -0
- package/extensions/dm-mcp-adapter/abort.js +35 -0
- package/extensions/dm-mcp-adapter/agent-dir.js +45 -0
- package/extensions/dm-mcp-adapter/agent-plugin-loader.js +349 -0
- package/extensions/dm-mcp-adapter/app-bridge.bundle.js +5843 -0
- package/extensions/dm-mcp-adapter/cli.js +354 -0
- package/extensions/dm-mcp-adapter/commands.js +619 -0
- package/extensions/dm-mcp-adapter/config.js +1060 -0
- package/extensions/dm-mcp-adapter/consent-manager.js +60 -0
- package/extensions/dm-mcp-adapter/direct-tools.js +544 -0
- package/extensions/dm-mcp-adapter/dist/agent-dir.js +45 -0
- package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +349 -0
- package/extensions/dm-mcp-adapter/dist/config.js +1060 -0
- package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +311 -0
- package/extensions/dm-mcp-adapter/dist/metadata-cache.js +285 -0
- package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +144 -0
- package/extensions/dm-mcp-adapter/dist/resource-tools.js +7 -0
- package/extensions/dm-mcp-adapter/dist/types.js +247 -0
- package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +36 -0
- package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +58 -0
- package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +26 -0
- package/extensions/dm-mcp-adapter/dist/utils.js +307 -0
- package/extensions/dm-mcp-adapter/elicitation-handler.js +306 -0
- package/extensions/dm-mcp-adapter/error-signal.js +16 -0
- package/extensions/dm-mcp-adapter/errors.js +225 -0
- package/extensions/dm-mcp-adapter/failure-backoff.js +19 -0
- package/extensions/dm-mcp-adapter/glimpse-ui.js +61 -0
- package/extensions/dm-mcp-adapter/host-html-template.js +464 -0
- package/extensions/dm-mcp-adapter/index.js +1079 -0
- package/extensions/dm-mcp-adapter/init.js +624 -0
- package/extensions/dm-mcp-adapter/json-schema-validator.js +53 -0
- package/extensions/dm-mcp-adapter/lifecycle.js +385 -0
- package/extensions/dm-mcp-adapter/logger.js +115 -0
- package/extensions/dm-mcp-adapter/mcp-auth-flow.js +824 -0
- package/extensions/dm-mcp-adapter/mcp-auth.js +732 -0
- package/extensions/dm-mcp-adapter/mcp-bearer-store.js +311 -0
- package/extensions/dm-mcp-adapter/mcp-callback-server.js +388 -0
- package/extensions/dm-mcp-adapter/mcp-code.js +291 -0
- package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +88 -0
- package/extensions/dm-mcp-adapter/mcp-oauth-provider.js +490 -0
- package/extensions/dm-mcp-adapter/mcp-output-guard.js +386 -0
- package/extensions/dm-mcp-adapter/mcp-panel.js +951 -0
- package/extensions/dm-mcp-adapter/mcp-probe.js +151 -0
- package/extensions/dm-mcp-adapter/mcp-references.js +263 -0
- package/extensions/dm-mcp-adapter/mcp-script-worker.mjs +126 -0
- package/extensions/dm-mcp-adapter/mcp-setup-panel.js +578 -0
- package/extensions/dm-mcp-adapter/mcp-status.js +80 -0
- package/extensions/dm-mcp-adapter/mcp-trace.js +215 -0
- package/extensions/dm-mcp-adapter/metadata-cache.js +292 -0
- package/extensions/dm-mcp-adapter/namespace-tools.js +155 -0
- package/extensions/dm-mcp-adapter/npx-resolver.js +465 -0
- package/extensions/dm-mcp-adapter/oauth-handler.js +17 -0
- package/extensions/dm-mcp-adapter/oauth.js +17 -0
- package/extensions/dm-mcp-adapter/onboarding-state.js +62 -0
- package/extensions/dm-mcp-adapter/package-mcp-loader.js +144 -0
- package/extensions/dm-mcp-adapter/package.json +68 -0
- package/extensions/dm-mcp-adapter/panel-keys.js +26 -0
- package/extensions/dm-mcp-adapter/prompts.js +258 -0
- package/extensions/dm-mcp-adapter/proxy-modes.js +1336 -0
- package/extensions/dm-mcp-adapter/request-headers-command.js +293 -0
- package/extensions/dm-mcp-adapter/resource-tools.js +7 -0
- package/extensions/dm-mcp-adapter/runtime-owner.js +79 -0
- package/extensions/dm-mcp-adapter/sampling-handler.js +222 -0
- package/extensions/dm-mcp-adapter/sandbox-proxy-template.js +190 -0
- package/extensions/dm-mcp-adapter/search-ranking.js +198 -0
- package/extensions/dm-mcp-adapter/server-manager.js +1258 -0
- package/extensions/dm-mcp-adapter/session-recovery.js +82 -0
- package/extensions/dm-mcp-adapter/skills/mcp-scripting/SKILL.md +39 -0
- package/extensions/dm-mcp-adapter/state.js +0 -0
- package/extensions/dm-mcp-adapter/test-runner.mjs +18 -0
- package/extensions/dm-mcp-adapter/tool-approval.js +151 -0
- package/extensions/dm-mcp-adapter/tool-metadata.js +247 -0
- package/extensions/dm-mcp-adapter/tool-registrar.js +212 -0
- package/extensions/dm-mcp-adapter/tool-result-renderer.js +355 -0
- package/extensions/dm-mcp-adapter/ts-shape.js +157 -0
- package/extensions/dm-mcp-adapter/types.js +264 -0
- package/extensions/dm-mcp-adapter/ui-app-bridge-helpers.js +36 -0
- package/extensions/dm-mcp-adapter/ui-resource-handler.js +209 -0
- package/extensions/dm-mcp-adapter/ui-server.js +815 -0
- package/extensions/dm-mcp-adapter/ui-session.js +455 -0
- package/extensions/dm-mcp-adapter/ui-stream-types.js +58 -0
- package/extensions/dm-mcp-adapter/ui-tool-visibility.js +26 -0
- package/extensions/dm-mcp-adapter/unix-socket-transport.js +83 -0
- package/extensions/dm-mcp-adapter/utils.js +307 -0
- package/extensions/dm-skills-manager/THIRD_PARTY_NOTICES.md +27 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/components.js +265 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/constants.js +31 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/creation-fallback.js +20 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/creation.js +145 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/dialog.js +738 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/dm-ai-compat.js +15 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/format.js +125 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/glyphs.js +142 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/layout.js +93 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/paths.js +76 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/registry.js +95 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/settings.js +93 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/startup.js +32 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/toggle.js +54 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/types.js +14 -0
- package/extensions/dm-skills-manager/extensions/skills-manager/ui.js +121 -0
- package/extensions/dm-skills-manager/extensions/skills-manager.js +111 -0
- package/extensions/dm-skills-manager/package.json +121 -0
- package/package.json +13 -1
- package/extensions/dm-localllm-provider/detect.test.js +0 -1051
- package/extensions/dm-localllm-provider/index.test.js +0 -149
- package/extensions/dm-localllm-provider/keychain.test.js +0 -61
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { readFileSync, existsSync, rmSync } from "fs";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { getAgentPath } from "./agent-dir.js";
|
|
8
|
+
import { resolveConfiguredOAuthDir } from "./config.js";
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const AUTH_SECRET_SERVICE = "dm-mcp-adapter.oauth";
|
|
11
|
+
const TEST_AUTH_STORE_ENV = "DM_MCP_ADAPTER_TEST_AUTH_STORE";
|
|
12
|
+
const AUTH_SECRET_CHUNK_SIZE = 1000;
|
|
13
|
+
const AUTH_SECRET_VALUE_LIMIT = 1280;
|
|
14
|
+
const KEYRING_RECOVERY_DISABLED_ENV = "DM_MCP_ADAPTER_DISABLE_KEYRING_RECOVERY";
|
|
15
|
+
const KEYRING_RECOVERY_KEYCTL_ENV = "DM_MCP_ADAPTER_KEYRING_RECOVERY_KEYCTL";
|
|
16
|
+
const KEYRING_RECOVERY_NODE_ENV = "DM_MCP_ADAPTER_KEYRING_RECOVERY_NODE";
|
|
17
|
+
const KEYRING_RECOVERY_HELPER_ENV = "DM_MCP_ADAPTER_KEYRING_RECOVERY_HELPER";
|
|
18
|
+
const TEST_LINUX_KEYRING_RECOVERY_ENV = "DM_MCP_ADAPTER_TEST_LINUX_KEYRING_RECOVERY";
|
|
19
|
+
const AUTH_CACHE_DISABLED_ENV = "DM_MCP_ADAPTER_DISABLE_AUTH_CACHE";
|
|
20
|
+
const KEYRING_RECOVERY_TIMEOUT_MS = 1e4;
|
|
21
|
+
const AUTH_CHUNK_MANIFEST_KEY = "__piMcpAdapterOAuthChunked";
|
|
22
|
+
|
|
23
|
+
export class OAuthCredentialStoreError extends Error {
|
|
24
|
+
operation;
|
|
25
|
+
code = "OAUTH_CREDENTIAL_STORE_UNAVAILABLE";
|
|
26
|
+
constructor(message, operation, cause) {
|
|
27
|
+
super(message, { cause });
|
|
28
|
+
this.operation = operation;
|
|
29
|
+
this.name = "OAuthCredentialStoreError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function causeChainContains(error, pattern) {
|
|
33
|
+
const seen = new Set;
|
|
34
|
+
let current = error;
|
|
35
|
+
while (typeof current === "object" && current !== null || typeof current === "function") {
|
|
36
|
+
if (seen.has(current))
|
|
37
|
+
break;
|
|
38
|
+
seen.add(current);
|
|
39
|
+
const candidate = current;
|
|
40
|
+
if ([candidate.name, candidate.message, candidate.code].some((value) => typeof value === "string" && pattern.test(value))) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
current = candidate.cause;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
export function formatOAuthCredentialStoreUnavailable(error) {
|
|
48
|
+
if (process.platform === "linux" && causeChainContains(error, /key\s*(?:has been\s*)?revoked|keyrevoked/i)) {
|
|
49
|
+
return "OAuth credential store unavailable: the Linux session keyring may be revoked. Start DM from a fresh login/keyring session and retry.";
|
|
50
|
+
}
|
|
51
|
+
return "OAuth credential store unavailable. Configure or unlock the OS credential store and retry.";
|
|
52
|
+
}
|
|
53
|
+
let KeyringEntryClass;
|
|
54
|
+
const memoryAuthEntries = new Map;
|
|
55
|
+
let testAuthSecretStoreReadCount = 0;
|
|
56
|
+
const authEntryCache = new Map;
|
|
57
|
+
function isAuthEntryCacheEnabled() {
|
|
58
|
+
return process.env[AUTH_CACHE_DISABLED_ENV] !== "1";
|
|
59
|
+
}
|
|
60
|
+
function cloneAuthEntry(entry) {
|
|
61
|
+
return entry === undefined ? undefined : structuredClone(entry);
|
|
62
|
+
}
|
|
63
|
+
const memoryAuthSecretStore = {
|
|
64
|
+
read(account) {
|
|
65
|
+
testAuthSecretStoreReadCount++;
|
|
66
|
+
return memoryAuthEntries.get(account);
|
|
67
|
+
},
|
|
68
|
+
write(account, payload) {
|
|
69
|
+
memoryAuthEntries.set(account, payload);
|
|
70
|
+
},
|
|
71
|
+
remove(account) {
|
|
72
|
+
memoryAuthEntries.delete(account);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const keyringAuthSecretStore = {
|
|
76
|
+
read(account) {
|
|
77
|
+
return getKeyringEntry(account).getPassword() ?? undefined;
|
|
78
|
+
},
|
|
79
|
+
write(account, payload) {
|
|
80
|
+
getKeyringEntry(account).setPassword(payload);
|
|
81
|
+
},
|
|
82
|
+
remove(account) {
|
|
83
|
+
getKeyringEntry(account).deleteCredential();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const sizeLimitedAuthSecretStore = {
|
|
87
|
+
read(account) {
|
|
88
|
+
testAuthSecretStoreReadCount++;
|
|
89
|
+
return memoryAuthEntries.get(account);
|
|
90
|
+
},
|
|
91
|
+
write(account, payload) {
|
|
92
|
+
if (payload.length > AUTH_SECRET_VALUE_LIMIT) {
|
|
93
|
+
throw new Error(`Value of 'password encoded as UTF-16' is longer than the platform limit of ${AUTH_SECRET_VALUE_LIMIT * 2} chars`);
|
|
94
|
+
}
|
|
95
|
+
memoryAuthEntries.set(account, payload);
|
|
96
|
+
},
|
|
97
|
+
remove(account) {
|
|
98
|
+
memoryAuthEntries.delete(account);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const unavailableAuthSecretStore = {
|
|
102
|
+
read() {
|
|
103
|
+
testAuthSecretStoreReadCount++;
|
|
104
|
+
throw new Error("simulated secure credential store unavailable");
|
|
105
|
+
},
|
|
106
|
+
write() {
|
|
107
|
+
throw new Error("simulated secure credential store unavailable");
|
|
108
|
+
},
|
|
109
|
+
remove() {
|
|
110
|
+
throw new Error("simulated secure credential store unavailable");
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function createKeyRevokedTestError() {
|
|
114
|
+
return new Error("Couldn't access platform storage: KeyRevoked", { cause: new Error("KeyRevoked") });
|
|
115
|
+
}
|
|
116
|
+
const keyRevokedAuthSecretStore = {
|
|
117
|
+
read() {
|
|
118
|
+
testAuthSecretStoreReadCount++;
|
|
119
|
+
throw createKeyRevokedTestError();
|
|
120
|
+
},
|
|
121
|
+
write() {
|
|
122
|
+
throw createKeyRevokedTestError();
|
|
123
|
+
},
|
|
124
|
+
remove() {
|
|
125
|
+
throw createKeyRevokedTestError();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
export function resetTestAuthSecretStore() {
|
|
129
|
+
memoryAuthEntries.clear();
|
|
130
|
+
authEntryCache.clear();
|
|
131
|
+
testAuthSecretStoreReadCount = 0;
|
|
132
|
+
}
|
|
133
|
+
export function resetAuthEntryCache() {
|
|
134
|
+
authEntryCache.clear();
|
|
135
|
+
}
|
|
136
|
+
export function getTestAuthSecretStoreReadCount() {
|
|
137
|
+
return testAuthSecretStoreReadCount;
|
|
138
|
+
}
|
|
139
|
+
export function getTestAuthSecretStoreEntries() {
|
|
140
|
+
return [...memoryAuthEntries.entries()];
|
|
141
|
+
}
|
|
142
|
+
export function removeTestAuthSecretStoreEntry(account) {
|
|
143
|
+
memoryAuthEntries.delete(account);
|
|
144
|
+
}
|
|
145
|
+
function getAuthSecretStore() {
|
|
146
|
+
if (process.env[TEST_AUTH_STORE_ENV] === "memory")
|
|
147
|
+
return memoryAuthSecretStore;
|
|
148
|
+
if (process.env[TEST_AUTH_STORE_ENV] === "sizelimited")
|
|
149
|
+
return sizeLimitedAuthSecretStore;
|
|
150
|
+
if (process.env[TEST_AUTH_STORE_ENV] === "unavailable")
|
|
151
|
+
return unavailableAuthSecretStore;
|
|
152
|
+
if (process.env[TEST_AUTH_STORE_ENV] === "keyrevoked")
|
|
153
|
+
return keyRevokedAuthSecretStore;
|
|
154
|
+
return keyringAuthSecretStore;
|
|
155
|
+
}
|
|
156
|
+
function getKeyringEntry(account) {
|
|
157
|
+
try {
|
|
158
|
+
KeyringEntryClass ??= loadKeyringEntryClass();
|
|
159
|
+
return new KeyringEntryClass(AUTH_SECRET_SERVICE, account);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new Error("OAuth secure credential storage is unavailable. Configure the OS credential store and retry authentication.", { cause: error });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function loadKeyringEntryClass(keyringRequire = require, platform = process.platform, arch = process.arch) {
|
|
165
|
+
try {
|
|
166
|
+
return keyringRequire("@napi-rs/keyring").Entry;
|
|
167
|
+
} catch (loaderError) {
|
|
168
|
+
try {
|
|
169
|
+
return loadKeyringNativeBindingFallback(keyringRequire, platform, arch).Entry;
|
|
170
|
+
} catch (fallbackError) {
|
|
171
|
+
throw new Error(`Failed to load @napi-rs/keyring; absolute-path native binding fallback also failed: ${formatErrorMessage(fallbackError)}`, {
|
|
172
|
+
cause: loaderError
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function loadKeyringNativeBindingFallback(keyringRequire, platform, arch) {
|
|
178
|
+
const targets = getKeyringNativeBindingTargets(platform, arch);
|
|
179
|
+
if (targets.length === 0) {
|
|
180
|
+
throw new Error(`Unsupported @napi-rs/keyring native binding target: ${platform}-${arch}`);
|
|
181
|
+
}
|
|
182
|
+
let lastError;
|
|
183
|
+
for (const target of targets) {
|
|
184
|
+
try {
|
|
185
|
+
const packageJsonPath = keyringRequire.resolve(`${target.packageName}/package.json`);
|
|
186
|
+
return keyringRequire(join(dirname(packageJsonPath), target.bindingFile));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
lastError = error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
192
|
+
}
|
|
193
|
+
function getKeyringNativeBindingTargets(platform, arch) {
|
|
194
|
+
return getKeyringNativeBindingSuffixes(platform, arch).map((suffix) => ({
|
|
195
|
+
packageName: `@napi-rs/keyring-${suffix}`,
|
|
196
|
+
bindingFile: `keyring.${suffix}.node`
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
function getKeyringNativeBindingSuffixes(platform, arch) {
|
|
200
|
+
if (platform === "darwin") {
|
|
201
|
+
if (arch === "arm64")
|
|
202
|
+
return ["darwin-arm64"];
|
|
203
|
+
if (arch === "x64")
|
|
204
|
+
return ["darwin-x64"];
|
|
205
|
+
}
|
|
206
|
+
if (platform === "win32") {
|
|
207
|
+
if (arch === "arm64")
|
|
208
|
+
return ["win32-arm64-msvc"];
|
|
209
|
+
if (arch === "x64")
|
|
210
|
+
return ["win32-x64-msvc"];
|
|
211
|
+
if (arch === "ia32")
|
|
212
|
+
return ["win32-ia32-msvc"];
|
|
213
|
+
}
|
|
214
|
+
if (platform === "linux") {
|
|
215
|
+
if (arch === "arm64")
|
|
216
|
+
return ["linux-arm64-gnu", "linux-arm64-musl"];
|
|
217
|
+
if (arch === "arm")
|
|
218
|
+
return ["linux-arm-gnueabihf"];
|
|
219
|
+
if (arch === "riscv64")
|
|
220
|
+
return ["linux-riscv64-gnu"];
|
|
221
|
+
if (arch === "x64")
|
|
222
|
+
return ["linux-x64-gnu", "linux-x64-musl"];
|
|
223
|
+
}
|
|
224
|
+
if (platform === "freebsd" && arch === "x64")
|
|
225
|
+
return ["freebsd-x64"];
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
function formatErrorMessage(error) {
|
|
229
|
+
return error instanceof Error ? error.message : String(error);
|
|
230
|
+
}
|
|
231
|
+
function isLinuxKeyringRecoveryEnabled() {
|
|
232
|
+
if (process.env[KEYRING_RECOVERY_DISABLED_ENV] === "1")
|
|
233
|
+
return false;
|
|
234
|
+
return process.platform === "linux" || process.env[TEST_LINUX_KEYRING_RECOVERY_ENV] === "1";
|
|
235
|
+
}
|
|
236
|
+
function shouldAttemptLinuxKeyringRecovery(error) {
|
|
237
|
+
return isLinuxKeyringRecoveryEnabled() && causeChainContains(error, /key\s*(?:has been\s*)?revoked|keyrevoked/i);
|
|
238
|
+
}
|
|
239
|
+
function runLinuxKeyringRecoveryOperation(operation, account, payload) {
|
|
240
|
+
const keyctl = process.env[KEYRING_RECOVERY_KEYCTL_ENV]?.trim() || "keyctl";
|
|
241
|
+
const node = process.env[KEYRING_RECOVERY_NODE_ENV]?.trim() || "node";
|
|
242
|
+
const helper = process.env[KEYRING_RECOVERY_HELPER_ENV]?.trim() || fileURLToPath(new URL("./mcp-keyring-helper.cjs", import.meta.url));
|
|
243
|
+
const request = JSON.stringify({ operation, service: AUTH_SECRET_SERVICE, account, payload });
|
|
244
|
+
const result = spawnSync(keyctl, ["session", "-", node, helper], {
|
|
245
|
+
input: `${request}
|
|
246
|
+
`,
|
|
247
|
+
encoding: "utf8",
|
|
248
|
+
maxBuffer: 1024 * 1024,
|
|
249
|
+
timeout: KEYRING_RECOVERY_TIMEOUT_MS,
|
|
250
|
+
windowsHide: true
|
|
251
|
+
});
|
|
252
|
+
if (result.error) {
|
|
253
|
+
throw new Error(`Linux keyring recovery helper could not start: ${result.error.message}`, { cause: result.error });
|
|
254
|
+
}
|
|
255
|
+
if (result.status !== 0) {
|
|
256
|
+
throw new Error(`Linux keyring recovery helper failed with exit code ${result.status ?? "unknown"}`);
|
|
257
|
+
}
|
|
258
|
+
let response;
|
|
259
|
+
try {
|
|
260
|
+
response = JSON.parse(result.stdout.trim());
|
|
261
|
+
} catch (error) {
|
|
262
|
+
throw new Error("Linux keyring recovery helper returned invalid JSON", { cause: error });
|
|
263
|
+
}
|
|
264
|
+
if (typeof response !== "object" || response === null || typeof response.ok !== "boolean") {
|
|
265
|
+
throw new Error("Linux keyring recovery helper returned an invalid response");
|
|
266
|
+
}
|
|
267
|
+
const typedResponse = response;
|
|
268
|
+
if (typedResponse.ok === false) {
|
|
269
|
+
throw new Error(typedResponse.error || "Linux keyring recovery helper failed");
|
|
270
|
+
}
|
|
271
|
+
if (operation === "read" && typedResponse.found === true && typeof typedResponse.value !== "string") {
|
|
272
|
+
throw new Error("Linux keyring recovery helper returned an invalid read response");
|
|
273
|
+
}
|
|
274
|
+
return typedResponse;
|
|
275
|
+
}
|
|
276
|
+
const linuxKeyringRecoveryAuthSecretStore = {
|
|
277
|
+
read(account) {
|
|
278
|
+
const response = runLinuxKeyringRecoveryOperation("read", account);
|
|
279
|
+
return response.ok && response.found === true ? response.value : undefined;
|
|
280
|
+
},
|
|
281
|
+
write(account, payload) {
|
|
282
|
+
runLinuxKeyringRecoveryOperation("write", account, payload);
|
|
283
|
+
},
|
|
284
|
+
remove(account) {
|
|
285
|
+
runLinuxKeyringRecoveryOperation("remove", account);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
export function loadTestKeyringEntryClass(keyringRequire, platform, arch) {
|
|
289
|
+
return loadKeyringEntryClass(keyringRequire, platform, arch);
|
|
290
|
+
}
|
|
291
|
+
export function getAuthStorageOptions(oauthDir, cwd = process.cwd()) {
|
|
292
|
+
const baseDir = resolveConfiguredOAuthDir(oauthDir, cwd);
|
|
293
|
+
return baseDir ? { baseDir } : {};
|
|
294
|
+
}
|
|
295
|
+
export function getAuthBaseDir(options = {}) {
|
|
296
|
+
const override = process.env.MCP_OAUTH_DIR?.trim();
|
|
297
|
+
if (override)
|
|
298
|
+
return override;
|
|
299
|
+
return options.baseDir ?? getAgentPath("mcp-oauth");
|
|
300
|
+
}
|
|
301
|
+
function getServerDir(serverName, options) {
|
|
302
|
+
if (typeof serverName !== "string") {
|
|
303
|
+
throw new Error(`Invalid MCP server name: ${JSON.stringify(serverName)}`);
|
|
304
|
+
}
|
|
305
|
+
const storageKey = getAuthEntryAccount(serverName);
|
|
306
|
+
return join(getAuthBaseDir(options), storageKey);
|
|
307
|
+
}
|
|
308
|
+
function getAuthEntryAccount(serverName) {
|
|
309
|
+
if (typeof serverName !== "string") {
|
|
310
|
+
throw new Error(`Invalid MCP server name: ${JSON.stringify(serverName)}`);
|
|
311
|
+
}
|
|
312
|
+
return `sha256-${createHash("sha256").update(serverName, "utf8").digest("hex")}`;
|
|
313
|
+
}
|
|
314
|
+
export function getAuthEntryFilePath(serverName, options) {
|
|
315
|
+
return join(getServerDir(serverName, options), "tokens.json");
|
|
316
|
+
}
|
|
317
|
+
function parseJsonPayload(serverName, payload, source) {
|
|
318
|
+
try {
|
|
319
|
+
return JSON.parse(payload);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
throw new Error(`Failed to parse OAuth credentials for ${serverName} from ${source}`, { cause: error });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function parseAuthEntryPayload(serverName, payload, source) {
|
|
325
|
+
const parsed = parseJsonPayload(serverName, payload, source);
|
|
326
|
+
const entry = toAuthEntry(parsed);
|
|
327
|
+
if (!entry) {
|
|
328
|
+
throw new Error(`Failed to parse OAuth credentials for ${serverName} from ${source}: invalid credential shape`);
|
|
329
|
+
}
|
|
330
|
+
return entry;
|
|
331
|
+
}
|
|
332
|
+
function toAuthEntry(value) {
|
|
333
|
+
const entry = toRecord(value);
|
|
334
|
+
if (!entry)
|
|
335
|
+
return;
|
|
336
|
+
const codeVerifier = optionalString(entry.codeVerifier);
|
|
337
|
+
const oauthState = optionalString(entry.oauthState);
|
|
338
|
+
const serverUrl = optionalString(entry.serverUrl);
|
|
339
|
+
if (codeVerifier === null || oauthState === null || serverUrl === null)
|
|
340
|
+
return;
|
|
341
|
+
const tokens = entry.tokens === undefined ? undefined : toStoredTokens(entry.tokens);
|
|
342
|
+
const clientInfo = entry.clientInfo === undefined ? undefined : toStoredClientInfo(entry.clientInfo);
|
|
343
|
+
if (entry.tokens !== undefined && !tokens || entry.clientInfo !== undefined && !clientInfo)
|
|
344
|
+
return;
|
|
345
|
+
const authEntry = {};
|
|
346
|
+
if (tokens)
|
|
347
|
+
authEntry.tokens = tokens;
|
|
348
|
+
if (clientInfo)
|
|
349
|
+
authEntry.clientInfo = clientInfo;
|
|
350
|
+
if (codeVerifier !== undefined)
|
|
351
|
+
authEntry.codeVerifier = codeVerifier;
|
|
352
|
+
if (oauthState !== undefined)
|
|
353
|
+
authEntry.oauthState = oauthState;
|
|
354
|
+
if (serverUrl !== undefined)
|
|
355
|
+
authEntry.serverUrl = serverUrl;
|
|
356
|
+
return authEntry;
|
|
357
|
+
}
|
|
358
|
+
function toStoredTokens(value) {
|
|
359
|
+
const tokens = toRecord(value);
|
|
360
|
+
if (!tokens || typeof tokens.accessToken !== "string")
|
|
361
|
+
return;
|
|
362
|
+
const refreshToken = optionalString(tokens.refreshToken);
|
|
363
|
+
const scope = optionalString(tokens.scope);
|
|
364
|
+
const issuer = optionalString(tokens.issuer);
|
|
365
|
+
const expiresAt = optionalNumber(tokens.expiresAt);
|
|
366
|
+
if (refreshToken === null || scope === null || issuer === null || expiresAt === null)
|
|
367
|
+
return;
|
|
368
|
+
const storedTokens = { accessToken: tokens.accessToken };
|
|
369
|
+
if (refreshToken !== undefined)
|
|
370
|
+
storedTokens.refreshToken = refreshToken;
|
|
371
|
+
if (expiresAt !== undefined)
|
|
372
|
+
storedTokens.expiresAt = expiresAt;
|
|
373
|
+
if (scope !== undefined)
|
|
374
|
+
storedTokens.scope = scope;
|
|
375
|
+
if (issuer !== undefined)
|
|
376
|
+
storedTokens.issuer = issuer;
|
|
377
|
+
return storedTokens;
|
|
378
|
+
}
|
|
379
|
+
function toStoredClientInfo(value) {
|
|
380
|
+
const clientInfo = toRecord(value);
|
|
381
|
+
if (!clientInfo || typeof clientInfo.clientId !== "string")
|
|
382
|
+
return;
|
|
383
|
+
const clientSecret = optionalString(clientInfo.clientSecret);
|
|
384
|
+
const issuer = optionalString(clientInfo.issuer);
|
|
385
|
+
const clientIdIssuedAt = optionalNumber(clientInfo.clientIdIssuedAt);
|
|
386
|
+
const clientSecretExpiresAt = optionalNumber(clientInfo.clientSecretExpiresAt);
|
|
387
|
+
const configPreRegistered = optionalBoolean(clientInfo.configPreRegistered);
|
|
388
|
+
if (clientSecret === null || issuer === null || clientIdIssuedAt === null || clientSecretExpiresAt === null || configPreRegistered === null)
|
|
389
|
+
return;
|
|
390
|
+
const storedClient = { clientId: clientInfo.clientId };
|
|
391
|
+
const redirectUris = stringArray(clientInfo.redirectUris);
|
|
392
|
+
if (clientSecret !== undefined)
|
|
393
|
+
storedClient.clientSecret = clientSecret;
|
|
394
|
+
if (clientIdIssuedAt !== undefined)
|
|
395
|
+
storedClient.clientIdIssuedAt = clientIdIssuedAt;
|
|
396
|
+
if (clientSecretExpiresAt !== undefined)
|
|
397
|
+
storedClient.clientSecretExpiresAt = clientSecretExpiresAt;
|
|
398
|
+
if (redirectUris !== undefined)
|
|
399
|
+
storedClient.redirectUris = redirectUris;
|
|
400
|
+
if (issuer !== undefined)
|
|
401
|
+
storedClient.issuer = issuer;
|
|
402
|
+
if (configPreRegistered !== undefined)
|
|
403
|
+
storedClient.configPreRegistered = configPreRegistered;
|
|
404
|
+
return storedClient;
|
|
405
|
+
}
|
|
406
|
+
function toRecord(value) {
|
|
407
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
408
|
+
}
|
|
409
|
+
function optionalString(value) {
|
|
410
|
+
if (value === undefined)
|
|
411
|
+
return;
|
|
412
|
+
return typeof value === "string" ? value : null;
|
|
413
|
+
}
|
|
414
|
+
function optionalNumber(value) {
|
|
415
|
+
if (value === undefined)
|
|
416
|
+
return;
|
|
417
|
+
return typeof value === "number" ? value : null;
|
|
418
|
+
}
|
|
419
|
+
function optionalBoolean(value) {
|
|
420
|
+
if (value === undefined)
|
|
421
|
+
return;
|
|
422
|
+
return typeof value === "boolean" ? value : null;
|
|
423
|
+
}
|
|
424
|
+
function stringArray(value) {
|
|
425
|
+
return Array.isArray(value) && value.every((uri) => typeof uri === "string") ? value : undefined;
|
|
426
|
+
}
|
|
427
|
+
function isAuthEntryChunkManifest(value) {
|
|
428
|
+
if (typeof value !== "object" || value === null)
|
|
429
|
+
return false;
|
|
430
|
+
const manifest = value;
|
|
431
|
+
return manifest[AUTH_CHUNK_MANIFEST_KEY] === 1 && typeof manifest.chunkCount === "number" && Number.isInteger(manifest.chunkCount) && manifest.chunkCount > 0 && typeof manifest.chunkDigest === "string" && /^[a-f0-9]{16}$/.test(manifest.chunkDigest);
|
|
432
|
+
}
|
|
433
|
+
function getAuthEntryChunkAccount(account, manifest, index) {
|
|
434
|
+
return `${account}.chunk.${manifest.chunkDigest}.${index}`;
|
|
435
|
+
}
|
|
436
|
+
function getAuthEntryChunkAccounts(account, manifest) {
|
|
437
|
+
return Array.from({ length: manifest.chunkCount }, (_, index) => getAuthEntryChunkAccount(account, manifest, index));
|
|
438
|
+
}
|
|
439
|
+
function readChunkManifestFromPayload(serverName, payload, source) {
|
|
440
|
+
const parsed = parseJsonPayload(serverName, payload, source);
|
|
441
|
+
return isAuthEntryChunkManifest(parsed) ? parsed : undefined;
|
|
442
|
+
}
|
|
443
|
+
function readExistingChunkManifest(store, serverName, account) {
|
|
444
|
+
try {
|
|
445
|
+
const payload = store.read(account);
|
|
446
|
+
return payload === undefined ? undefined : readChunkManifestFromPayload(serverName, payload, "OS secure credential store");
|
|
447
|
+
} catch {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function removeChunkPayloads(store, account, manifest) {
|
|
452
|
+
for (const chunkAccount of getAuthEntryChunkAccounts(account, manifest)) {
|
|
453
|
+
store.remove(chunkAccount);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function tryRemoveChunkPayloads(store, account, manifest) {
|
|
457
|
+
if (!manifest)
|
|
458
|
+
return;
|
|
459
|
+
try {
|
|
460
|
+
removeChunkPayloads(store, account, manifest);
|
|
461
|
+
} catch {}
|
|
462
|
+
}
|
|
463
|
+
function createChunkManifest(payload) {
|
|
464
|
+
return {
|
|
465
|
+
[AUTH_CHUNK_MANIFEST_KEY]: 1,
|
|
466
|
+
chunkCount: Math.ceil(payload.length / AUTH_SECRET_CHUNK_SIZE),
|
|
467
|
+
chunkDigest: createHash("sha256").update(payload, "utf8").digest("hex").slice(0, 16)
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function readChunkedAuthEntry(store, serverName, account, manifest) {
|
|
471
|
+
const chunks = getAuthEntryChunkAccounts(account, manifest).map((chunkAccount) => {
|
|
472
|
+
try {
|
|
473
|
+
const chunk = store.read(chunkAccount);
|
|
474
|
+
if (chunk === undefined) {
|
|
475
|
+
throw new Error(`Missing OAuth credential chunk ${chunkAccount} for ${serverName}`);
|
|
476
|
+
}
|
|
477
|
+
return chunk;
|
|
478
|
+
} catch (error) {
|
|
479
|
+
throw new OAuthCredentialStoreError(`Failed to read OAuth credentials for ${serverName} from the OS secure credential store`, "read", error);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
return parseAuthEntryPayload(serverName, chunks.join(""), "OS secure credential store chunks");
|
|
483
|
+
}
|
|
484
|
+
function readLegacyAuthEntry(serverName, options) {
|
|
485
|
+
const filePath = getAuthEntryFilePath(serverName, options);
|
|
486
|
+
if (!existsSync(filePath))
|
|
487
|
+
return;
|
|
488
|
+
const data = readFileSync(filePath, "utf-8");
|
|
489
|
+
return parseAuthEntryPayload(serverName, data, filePath);
|
|
490
|
+
}
|
|
491
|
+
function removeLegacyAuthEntry(serverName, options) {
|
|
492
|
+
const filePath = getAuthEntryFilePath(serverName, options);
|
|
493
|
+
if (!existsSync(filePath))
|
|
494
|
+
return;
|
|
495
|
+
try {
|
|
496
|
+
rmSync(filePath, { force: true });
|
|
497
|
+
} catch (error) {
|
|
498
|
+
throw new Error(`Failed to remove legacy plaintext OAuth credentials for ${serverName} at ${filePath}`, { cause: error });
|
|
499
|
+
}
|
|
500
|
+
const dir = getServerDir(serverName, options);
|
|
501
|
+
try {
|
|
502
|
+
rmSync(dir, { recursive: true });
|
|
503
|
+
} catch {}
|
|
504
|
+
}
|
|
505
|
+
function writeSecureAuthEntryToStore(store, serverName, entry) {
|
|
506
|
+
const account = getAuthEntryAccount(serverName);
|
|
507
|
+
const payload = JSON.stringify(entry);
|
|
508
|
+
const previousManifest = readExistingChunkManifest(store, serverName, account);
|
|
509
|
+
const manifest = payload.length > AUTH_SECRET_CHUNK_SIZE ? createChunkManifest(payload) : undefined;
|
|
510
|
+
try {
|
|
511
|
+
if (manifest) {
|
|
512
|
+
for (let index = 0;index < manifest.chunkCount; index++) {
|
|
513
|
+
const chunk = payload.slice(index * AUTH_SECRET_CHUNK_SIZE, (index + 1) * AUTH_SECRET_CHUNK_SIZE);
|
|
514
|
+
store.write(getAuthEntryChunkAccount(account, manifest, index), chunk);
|
|
515
|
+
}
|
|
516
|
+
store.write(account, JSON.stringify(manifest));
|
|
517
|
+
} else {
|
|
518
|
+
store.write(account, payload);
|
|
519
|
+
}
|
|
520
|
+
if (previousManifest?.chunkDigest !== manifest?.chunkDigest) {
|
|
521
|
+
tryRemoveChunkPayloads(store, account, previousManifest);
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
tryRemoveChunkPayloads(store, account, manifest);
|
|
525
|
+
throw new OAuthCredentialStoreError(`Failed to write OAuth credentials for ${serverName} to the OS secure credential store`, "write", error);
|
|
526
|
+
}
|
|
527
|
+
publishAuthEntryToCache(serverName, payload);
|
|
528
|
+
}
|
|
529
|
+
function publishAuthEntryToCache(serverName, payload) {
|
|
530
|
+
if (!isAuthEntryCacheEnabled())
|
|
531
|
+
return;
|
|
532
|
+
const normalized = toAuthEntry(JSON.parse(payload));
|
|
533
|
+
if (!normalized) {
|
|
534
|
+
authEntryCache.delete(serverName);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
authEntryCache.set(serverName, cloneAuthEntry(normalized));
|
|
538
|
+
}
|
|
539
|
+
function writeSecureAuthEntry(serverName, entry) {
|
|
540
|
+
try {
|
|
541
|
+
writeSecureAuthEntryToStore(getAuthSecretStore(), serverName, entry);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
if (!shouldAttemptLinuxKeyringRecovery(error))
|
|
544
|
+
throw error;
|
|
545
|
+
writeSecureAuthEntryToStore(linuxKeyringRecoveryAuthSecretStore, serverName, entry);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function readAuthEntryFromStore(store, serverName, options, behavior = {}) {
|
|
549
|
+
const account = getAuthEntryAccount(serverName);
|
|
550
|
+
let payload;
|
|
551
|
+
try {
|
|
552
|
+
payload = store.read(account);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
throw new OAuthCredentialStoreError(`Failed to read OAuth credentials for ${serverName} from the OS secure credential store`, "read", error);
|
|
555
|
+
}
|
|
556
|
+
if (payload !== undefined) {
|
|
557
|
+
const manifest = readChunkManifestFromPayload(serverName, payload, "OS secure credential store");
|
|
558
|
+
const entry = manifest ? readChunkedAuthEntry(store, serverName, account, manifest) : parseAuthEntryPayload(serverName, payload, "OS secure credential store");
|
|
559
|
+
removeLegacyAuthEntry(serverName, options);
|
|
560
|
+
return entry;
|
|
561
|
+
}
|
|
562
|
+
const legacyEntry = readLegacyAuthEntry(serverName, options);
|
|
563
|
+
if (!legacyEntry)
|
|
564
|
+
return;
|
|
565
|
+
if (behavior.migrateLegacy === false)
|
|
566
|
+
return legacyEntry;
|
|
567
|
+
writeSecureAuthEntryToStore(store, serverName, legacyEntry);
|
|
568
|
+
removeLegacyAuthEntry(serverName, options);
|
|
569
|
+
return legacyEntry;
|
|
570
|
+
}
|
|
571
|
+
function readAuthEntry(serverName, options, behavior = {}) {
|
|
572
|
+
const cacheable = behavior.migrateLegacy !== false && isAuthEntryCacheEnabled();
|
|
573
|
+
if (cacheable && authEntryCache.has(serverName)) {
|
|
574
|
+
return cloneAuthEntry(authEntryCache.get(serverName));
|
|
575
|
+
}
|
|
576
|
+
let entry;
|
|
577
|
+
try {
|
|
578
|
+
entry = readAuthEntryFromStore(getAuthSecretStore(), serverName, options, behavior);
|
|
579
|
+
} catch (error) {
|
|
580
|
+
if (!shouldAttemptLinuxKeyringRecovery(error))
|
|
581
|
+
throw error;
|
|
582
|
+
entry = readAuthEntryFromStore(linuxKeyringRecoveryAuthSecretStore, serverName, options, behavior);
|
|
583
|
+
}
|
|
584
|
+
if (cacheable)
|
|
585
|
+
authEntryCache.set(serverName, cloneAuthEntry(entry));
|
|
586
|
+
return entry;
|
|
587
|
+
}
|
|
588
|
+
export function getAuthEntry(serverName, options) {
|
|
589
|
+
return readAuthEntry(serverName, options);
|
|
590
|
+
}
|
|
591
|
+
export function getAuthForUrl(serverName, serverUrl, options) {
|
|
592
|
+
const entry = getAuthEntry(serverName, options);
|
|
593
|
+
if (!entry)
|
|
594
|
+
return;
|
|
595
|
+
if (!entry.serverUrl)
|
|
596
|
+
return;
|
|
597
|
+
if (entry.serverUrl !== serverUrl)
|
|
598
|
+
return;
|
|
599
|
+
return entry;
|
|
600
|
+
}
|
|
601
|
+
export function inspectAuthForUrl(serverName, serverUrl, options) {
|
|
602
|
+
try {
|
|
603
|
+
const entry = readAuthEntry(serverName, options, { migrateLegacy: false });
|
|
604
|
+
if (!entry?.serverUrl || entry.serverUrl !== serverUrl)
|
|
605
|
+
return { status: "absent" };
|
|
606
|
+
return { status: "present", entry };
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (!(error instanceof OAuthCredentialStoreError))
|
|
609
|
+
throw error;
|
|
610
|
+
return { status: "unavailable", message: formatOAuthCredentialStoreUnavailable(error) };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
export function saveAuthEntry(serverName, entry, serverUrl, options) {
|
|
614
|
+
if (serverUrl) {
|
|
615
|
+
entry.serverUrl = serverUrl;
|
|
616
|
+
}
|
|
617
|
+
writeSecureAuthEntry(serverName, entry);
|
|
618
|
+
removeLegacyAuthEntry(serverName, options);
|
|
619
|
+
}
|
|
620
|
+
function removeAuthEntryFromStore(store, serverName) {
|
|
621
|
+
const account = getAuthEntryAccount(serverName);
|
|
622
|
+
try {
|
|
623
|
+
const payload = store.read(account);
|
|
624
|
+
const manifest = payload === undefined ? undefined : readChunkManifestFromPayload(serverName, payload, "OS secure credential store");
|
|
625
|
+
if (manifest)
|
|
626
|
+
removeChunkPayloads(store, account, manifest);
|
|
627
|
+
store.remove(account);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
throw new OAuthCredentialStoreError(`Failed to remove OAuth credentials for ${serverName} from the OS secure credential store`, "remove", error);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
export function removeAuthEntry(serverName, options) {
|
|
633
|
+
try {
|
|
634
|
+
removeAuthEntryFromStore(getAuthSecretStore(), serverName);
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (!shouldAttemptLinuxKeyringRecovery(error))
|
|
637
|
+
throw error;
|
|
638
|
+
removeAuthEntryFromStore(linuxKeyringRecoveryAuthSecretStore, serverName);
|
|
639
|
+
}
|
|
640
|
+
authEntryCache.delete(serverName);
|
|
641
|
+
removeLegacyAuthEntry(serverName, options);
|
|
642
|
+
}
|
|
643
|
+
export function invalidateAuthEntryCache(serverName) {
|
|
644
|
+
authEntryCache.delete(serverName);
|
|
645
|
+
}
|
|
646
|
+
export function updateTokens(serverName, tokens, serverUrl, options) {
|
|
647
|
+
const entry = getAuthEntry(serverName, options) ?? {};
|
|
648
|
+
if (serverUrl && entry.serverUrl !== serverUrl) {
|
|
649
|
+
delete entry.clientInfo;
|
|
650
|
+
delete entry.codeVerifier;
|
|
651
|
+
delete entry.oauthState;
|
|
652
|
+
}
|
|
653
|
+
entry.tokens = tokens;
|
|
654
|
+
saveAuthEntry(serverName, entry, serverUrl, options);
|
|
655
|
+
}
|
|
656
|
+
export function updateClientInfo(serverName, clientInfo, serverUrl, options) {
|
|
657
|
+
const entry = getAuthEntry(serverName, options) ?? {};
|
|
658
|
+
if (serverUrl && entry.serverUrl !== serverUrl) {
|
|
659
|
+
delete entry.tokens;
|
|
660
|
+
delete entry.codeVerifier;
|
|
661
|
+
delete entry.oauthState;
|
|
662
|
+
}
|
|
663
|
+
entry.clientInfo = clientInfo;
|
|
664
|
+
saveAuthEntry(serverName, entry, serverUrl, options);
|
|
665
|
+
}
|
|
666
|
+
export function updateCodeVerifier(serverName, codeVerifier, serverUrl, options) {
|
|
667
|
+
const entry = getAuthEntry(serverName, options) ?? {};
|
|
668
|
+
if (serverUrl && entry.serverUrl !== serverUrl) {
|
|
669
|
+
delete entry.tokens;
|
|
670
|
+
delete entry.clientInfo;
|
|
671
|
+
delete entry.oauthState;
|
|
672
|
+
}
|
|
673
|
+
entry.codeVerifier = codeVerifier;
|
|
674
|
+
saveAuthEntry(serverName, entry, serverUrl, options);
|
|
675
|
+
}
|
|
676
|
+
export function clearCodeVerifier(serverName, options) {
|
|
677
|
+
const entry = getAuthEntry(serverName, options);
|
|
678
|
+
if (entry) {
|
|
679
|
+
delete entry.codeVerifier;
|
|
680
|
+
saveAuthEntry(serverName, entry, undefined, options);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
export function updateOAuthState(serverName, state, serverUrl, options) {
|
|
684
|
+
const entry = getAuthEntry(serverName, options) ?? {};
|
|
685
|
+
if (serverUrl && entry.serverUrl !== serverUrl) {
|
|
686
|
+
delete entry.tokens;
|
|
687
|
+
delete entry.clientInfo;
|
|
688
|
+
delete entry.codeVerifier;
|
|
689
|
+
}
|
|
690
|
+
entry.oauthState = state;
|
|
691
|
+
saveAuthEntry(serverName, entry, serverUrl, options);
|
|
692
|
+
}
|
|
693
|
+
export function getOAuthState(serverName, options) {
|
|
694
|
+
const entry = getAuthEntry(serverName, options);
|
|
695
|
+
return entry?.oauthState;
|
|
696
|
+
}
|
|
697
|
+
export function clearOAuthState(serverName, options) {
|
|
698
|
+
const entry = getAuthEntry(serverName, options);
|
|
699
|
+
if (entry) {
|
|
700
|
+
delete entry.oauthState;
|
|
701
|
+
saveAuthEntry(serverName, entry, undefined, options);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
export function isTokenExpired(serverName, options) {
|
|
705
|
+
const entry = getAuthEntry(serverName, options);
|
|
706
|
+
if (!entry?.tokens)
|
|
707
|
+
return null;
|
|
708
|
+
if (!entry.tokens.expiresAt)
|
|
709
|
+
return false;
|
|
710
|
+
return entry.tokens.expiresAt < Date.now() / 1000;
|
|
711
|
+
}
|
|
712
|
+
export function hasStoredTokens(serverName, options) {
|
|
713
|
+
const entry = getAuthEntry(serverName, options);
|
|
714
|
+
return !!entry?.tokens;
|
|
715
|
+
}
|
|
716
|
+
export function clearAllCredentials(serverName, options) {
|
|
717
|
+
removeAuthEntry(serverName, options);
|
|
718
|
+
}
|
|
719
|
+
export function clearClientInfo(serverName, options) {
|
|
720
|
+
const entry = getAuthEntry(serverName, options);
|
|
721
|
+
if (entry) {
|
|
722
|
+
delete entry.clientInfo;
|
|
723
|
+
saveAuthEntry(serverName, entry, undefined, options);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
export function clearTokens(serverName, options) {
|
|
727
|
+
const entry = getAuthEntry(serverName, options);
|
|
728
|
+
if (entry) {
|
|
729
|
+
delete entry.tokens;
|
|
730
|
+
saveAuthEntry(serverName, entry, undefined, options);
|
|
731
|
+
}
|
|
732
|
+
}
|