@letta-ai/letta-code 0.33.6 → 0.33.7
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/mcp-client.js +52 -11
- package/dist/mcp-client.js.map +3 -3
- package/dist/mcp-oauth.js +18531 -0
- package/dist/mcp-oauth.js.map +155 -0
- package/dist/types/backend/backend.d.ts +2 -0
- package/dist/types/backend/backend.d.ts.map +1 -1
- package/dist/types/backend/dev/fake-headless-backend.d.ts +2 -0
- package/dist/types/backend/dev/fake-headless-backend.d.ts.map +1 -1
- package/dist/types/mcp-client.d.ts +2 -0
- package/dist/types/mcp-client.d.ts.map +1 -1
- package/dist/types/mcp-oauth-callback.d.ts +11 -0
- package/dist/types/mcp-oauth-callback.d.ts.map +1 -0
- package/dist/types/mcp-oauth-public.d.ts +46 -0
- package/dist/types/mcp-oauth-public.d.ts.map +1 -0
- package/dist/types/mcp-oauth.d.ts +56 -0
- package/dist/types/mcp-oauth.d.ts.map +1 -0
- package/dist/types/utils/secrets.d.ts.map +1 -1
- package/letta.js +767 -201
- package/package.json +11 -1
package/letta.js
CHANGED
|
@@ -4358,7 +4358,7 @@ function isDuplicateKeychainItemError(error) {
|
|
|
4358
4358
|
return message.includes("already exists in the keychain") || message.includes("code: -25299");
|
|
4359
4359
|
}
|
|
4360
4360
|
async function getSecretValue(name, label) {
|
|
4361
|
-
if (!
|
|
4361
|
+
if (!secrets && !secretGetOverrideForTests) {
|
|
4362
4362
|
return null;
|
|
4363
4363
|
}
|
|
4364
4364
|
try {
|
|
@@ -4381,7 +4381,7 @@ async function getSecretValue(name, label) {
|
|
|
4381
4381
|
}
|
|
4382
4382
|
}
|
|
4383
4383
|
async function setSecretValue(name, value) {
|
|
4384
|
-
if (!
|
|
4384
|
+
if (!secrets) {
|
|
4385
4385
|
throw new Error("Secrets API unavailable");
|
|
4386
4386
|
}
|
|
4387
4387
|
try {
|
|
@@ -4409,7 +4409,7 @@ async function setSecretValue(name, value) {
|
|
|
4409
4409
|
});
|
|
4410
4410
|
}
|
|
4411
4411
|
async function deleteSecretValue(name) {
|
|
4412
|
-
if (!
|
|
4412
|
+
if (!secrets) {
|
|
4413
4413
|
return false;
|
|
4414
4414
|
}
|
|
4415
4415
|
try {
|
|
@@ -4423,7 +4423,7 @@ async function deleteSecretValue(name) {
|
|
|
4423
4423
|
}
|
|
4424
4424
|
}
|
|
4425
4425
|
async function setApiKey(apiKey) {
|
|
4426
|
-
if (!
|
|
4426
|
+
if (!secrets) {
|
|
4427
4427
|
throw new Error("Secrets API unavailable");
|
|
4428
4428
|
}
|
|
4429
4429
|
await setSecretValue(API_KEY_NAME, apiKey);
|
|
@@ -4432,7 +4432,7 @@ async function getApiKey() {
|
|
|
4432
4432
|
return getSecretValue(API_KEY_NAME, "API key");
|
|
4433
4433
|
}
|
|
4434
4434
|
async function setRefreshToken(refreshToken2) {
|
|
4435
|
-
if (!
|
|
4435
|
+
if (!secrets) {
|
|
4436
4436
|
throw new Error("Secrets API unavailable");
|
|
4437
4437
|
}
|
|
4438
4438
|
await setSecretValue(REFRESH_TOKEN_NAME, refreshToken2);
|
|
@@ -4463,7 +4463,7 @@ async function setSecureTokens(tokens) {
|
|
|
4463
4463
|
}
|
|
4464
4464
|
}
|
|
4465
4465
|
async function deleteApiKey() {
|
|
4466
|
-
if (
|
|
4466
|
+
if (secrets) {
|
|
4467
4467
|
try {
|
|
4468
4468
|
await secrets.delete({
|
|
4469
4469
|
service: SERVICE_NAME,
|
|
@@ -4476,7 +4476,7 @@ async function deleteApiKey() {
|
|
|
4476
4476
|
}
|
|
4477
4477
|
}
|
|
4478
4478
|
async function deleteRefreshToken() {
|
|
4479
|
-
if (
|
|
4479
|
+
if (secrets) {
|
|
4480
4480
|
try {
|
|
4481
4481
|
await secrets.delete({
|
|
4482
4482
|
service: SERVICE_NAME,
|
|
@@ -4498,7 +4498,7 @@ async function isKeychainAvailable() {
|
|
|
4498
4498
|
if (process.platform === "linux" && !process.env.DBUS_SESSION_BUS_ADDRESS?.trim()) {
|
|
4499
4499
|
return false;
|
|
4500
4500
|
}
|
|
4501
|
-
if (!
|
|
4501
|
+
if (!secrets) {
|
|
4502
4502
|
return false;
|
|
4503
4503
|
}
|
|
4504
4504
|
try {
|
|
@@ -4511,15 +4511,10 @@ async function isKeychainAvailable() {
|
|
|
4511
4511
|
return false;
|
|
4512
4512
|
}
|
|
4513
4513
|
}
|
|
4514
|
-
var secrets,
|
|
4514
|
+
var secrets, SERVICE_NAME, API_KEY_NAME = "letta-api-key", REFRESH_TOKEN_NAME = "letta-refresh-token", warnedSecretReadFailures, secretGetOverrideForTests = null;
|
|
4515
4515
|
var init_secrets = __esm(() => {
|
|
4516
4516
|
init_debug();
|
|
4517
|
-
|
|
4518
|
-
secrets = globalThis.Bun.secrets;
|
|
4519
|
-
secretsAvailable = true;
|
|
4520
|
-
} catch {
|
|
4521
|
-
secretsAvailable = false;
|
|
4522
|
-
}
|
|
4517
|
+
secrets = globalThis.Bun?.secrets;
|
|
4523
4518
|
SERVICE_NAME = scopeServiceName("letta-code");
|
|
4524
4519
|
warnedSecretReadFailures = new Set;
|
|
4525
4520
|
});
|
|
@@ -4771,8 +4766,8 @@ class SettingsManager {
|
|
|
4771
4766
|
const baseSettings = this.getSettings();
|
|
4772
4767
|
const secureTokens = { ...this.secureTokensCache };
|
|
4773
4768
|
if (!getRuntimeContext()) {
|
|
4774
|
-
const
|
|
4775
|
-
if (
|
|
4769
|
+
const secretsAvailable = await this.isKeychainAvailable();
|
|
4770
|
+
if (secretsAvailable) {
|
|
4776
4771
|
const storedTokens = await this.getSecureTokens();
|
|
4777
4772
|
secureTokens.apiKey = storedTokens.apiKey ?? secureTokens.apiKey;
|
|
4778
4773
|
secureTokens.refreshToken = storedTokens.refreshToken ?? secureTokens.refreshToken;
|
|
@@ -4861,8 +4856,8 @@ class SettingsManager {
|
|
|
4861
4856
|
this.pendingWrites.add(writePromise);
|
|
4862
4857
|
}
|
|
4863
4858
|
async persistSettingsAndTokens(secureTokens) {
|
|
4864
|
-
const
|
|
4865
|
-
if (
|
|
4859
|
+
const secretsAvailable = await this.isKeychainAvailable();
|
|
4860
|
+
if (secretsAvailable && Object.keys(secureTokens).length > 0) {
|
|
4866
4861
|
try {
|
|
4867
4862
|
await Promise.all([
|
|
4868
4863
|
this.persistSettings(),
|
|
@@ -5653,7 +5648,7 @@ var package_default;
|
|
|
5653
5648
|
var init_package = __esm(() => {
|
|
5654
5649
|
package_default = {
|
|
5655
5650
|
name: "@letta-ai/letta-code",
|
|
5656
|
-
version: "0.33.
|
|
5651
|
+
version: "0.33.7",
|
|
5657
5652
|
lettaStartupLogProtocol: 1,
|
|
5658
5653
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
5659
5654
|
type: "module",
|
|
@@ -5680,6 +5675,8 @@ var init_package = __esm(() => {
|
|
|
5680
5675
|
"dist/memory-constraints.js.map",
|
|
5681
5676
|
"dist/mcp-client.js",
|
|
5682
5677
|
"dist/mcp-client.js.map",
|
|
5678
|
+
"dist/mcp-oauth.js",
|
|
5679
|
+
"dist/mcp-oauth.js.map",
|
|
5683
5680
|
"dist/agent-presets.js",
|
|
5684
5681
|
"dist/agent-presets.js.map",
|
|
5685
5682
|
"dist/schedules.js",
|
|
@@ -5724,6 +5721,11 @@ var init_package = __esm(() => {
|
|
|
5724
5721
|
import: "./dist/mcp-client.js",
|
|
5725
5722
|
default: "./dist/mcp-client.js"
|
|
5726
5723
|
},
|
|
5724
|
+
"./mcp-oauth": {
|
|
5725
|
+
types: "./dist/types/mcp-oauth-public.d.ts",
|
|
5726
|
+
import: "./dist/mcp-oauth.js",
|
|
5727
|
+
default: "./dist/mcp-oauth.js"
|
|
5728
|
+
},
|
|
5727
5729
|
"./protocol": {
|
|
5728
5730
|
types: "./dist/types/types/protocol.d.ts"
|
|
5729
5731
|
},
|
|
@@ -5881,6 +5883,9 @@ var init_package = __esm(() => {
|
|
|
5881
5883
|
"mcp-client": [
|
|
5882
5884
|
"./dist/types/mcp-client.d.ts"
|
|
5883
5885
|
],
|
|
5886
|
+
"mcp-oauth": [
|
|
5887
|
+
"./dist/types/mcp-oauth-public.d.ts"
|
|
5888
|
+
],
|
|
5884
5889
|
channels: [
|
|
5885
5890
|
"./dist/types/channels-public.d.ts"
|
|
5886
5891
|
],
|
|
@@ -106236,10 +106241,19 @@ class HeadlessBackend {
|
|
|
106236
106241
|
if (!run || run.agent_id !== agentId || isTerminalRun(run)) {
|
|
106237
106242
|
return { [runId]: "failed" };
|
|
106238
106243
|
}
|
|
106244
|
+
return this.cancelRunById(runId);
|
|
106245
|
+
}
|
|
106246
|
+
async cancelRunById(runId) {
|
|
106247
|
+
const run = this.runs.get(runId);
|
|
106248
|
+
if (!run || isTerminalRun(run)) {
|
|
106249
|
+
return { [runId]: "failed" };
|
|
106250
|
+
}
|
|
106239
106251
|
if (run.conversation_id) {
|
|
106240
|
-
this.store.settleInterruptedToolCalls(run.conversation_id, {
|
|
106252
|
+
this.store.settleInterruptedToolCalls(run.conversation_id, {
|
|
106253
|
+
agentId: run.agent_id
|
|
106254
|
+
});
|
|
106241
106255
|
} else {
|
|
106242
|
-
this.store.settleInterruptedToolCalls(
|
|
106256
|
+
this.store.settleInterruptedToolCalls(run.agent_id);
|
|
106243
106257
|
}
|
|
106244
106258
|
const controller = this.runControllerByRunId.get(runId);
|
|
106245
106259
|
this.recordRunChunk(runId, {
|
|
@@ -106250,6 +106264,17 @@ class HeadlessBackend {
|
|
|
106250
106264
|
controller?.abort();
|
|
106251
106265
|
return { [runId]: "cancelled" };
|
|
106252
106266
|
}
|
|
106267
|
+
async cancelConversationRun(...args) {
|
|
106268
|
+
const [conversationId, requestedRunId] = args;
|
|
106269
|
+
const runId = requestedRunId ?? this.activeRunByConversation.get(conversationId);
|
|
106270
|
+
if (!runId)
|
|
106271
|
+
return {};
|
|
106272
|
+
const run = this.runs.get(runId);
|
|
106273
|
+
if (!run || run.conversation_id !== conversationId || isTerminalRun(run)) {
|
|
106274
|
+
return { [runId]: "failed" };
|
|
106275
|
+
}
|
|
106276
|
+
return this.cancelRunById(runId);
|
|
106277
|
+
}
|
|
106253
106278
|
async retrieveRun(runId) {
|
|
106254
106279
|
const run = this.runs.get(runId);
|
|
106255
106280
|
if (!run)
|
|
@@ -110931,6 +110956,9 @@ class APIBackend {
|
|
|
110931
110956
|
const client = await this.getClient();
|
|
110932
110957
|
return client.agents.messages.cancel(agentId, { run_ids: [runId] });
|
|
110933
110958
|
}
|
|
110959
|
+
async cancelConversationRun(_conversationId, _runId) {
|
|
110960
|
+
throw new Error("API backend does not support exact cancellation for conversations with agent_id:null");
|
|
110961
|
+
}
|
|
110934
110962
|
async retrieveRun(runId, options) {
|
|
110935
110963
|
const client = await this.getClient();
|
|
110936
110964
|
return client.runs.retrieve(runId, options);
|
|
@@ -213117,7 +213145,7 @@ class CodexAppServerClient {
|
|
|
213117
213145
|
await this.request("initialize", {
|
|
213118
213146
|
clientInfo: {
|
|
213119
213147
|
name: "letta-code",
|
|
213120
|
-
version: "0.33.
|
|
213148
|
+
version: "0.33.7"
|
|
213121
213149
|
},
|
|
213122
213150
|
capabilities: null
|
|
213123
213151
|
});
|
|
@@ -418452,6 +418480,15 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418452
418480
|
throw new Error(`Backend did not cancel run ${runId}`);
|
|
418453
418481
|
}
|
|
418454
418482
|
},
|
|
418483
|
+
cancelConversationRun: async (conversationId, runId) => {
|
|
418484
|
+
const result3 = await getBackend().cancelConversationRun(conversationId, runId);
|
|
418485
|
+
if (runId && result3[runId] !== "cancelled") {
|
|
418486
|
+
throw new Error(`Backend did not cancel run ${runId}`);
|
|
418487
|
+
}
|
|
418488
|
+
if (!runId && !Object.values(result3).includes("cancelled")) {
|
|
418489
|
+
throw new Error(`Backend did not cancel the active run for ${conversationId}`);
|
|
418490
|
+
}
|
|
418491
|
+
},
|
|
418455
418492
|
...deps
|
|
418456
418493
|
};
|
|
418457
418494
|
if (listener !== resolvedDeps.getActiveRuntime() || listener.intentionallyClosed) {
|
|
@@ -418471,7 +418508,7 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418471
418508
|
return false;
|
|
418472
418509
|
}
|
|
418473
418510
|
const cancellation = scopedRuntime.turnLifecycle.requestCancellation({
|
|
418474
|
-
waitForExternalSettlement: hasActiveTurn
|
|
418511
|
+
waitForExternalSettlement: hasActiveTurn
|
|
418475
418512
|
});
|
|
418476
418513
|
scopedRuntime.queueRuntime.pause();
|
|
418477
418514
|
const interruptedRunId = cancellation.runId;
|
|
@@ -418535,19 +418572,17 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418535
418572
|
}
|
|
418536
418573
|
const cancelConversationId = scopedRuntime.conversationId;
|
|
418537
418574
|
const cancelAgentId = scopedRuntime.agentId;
|
|
418538
|
-
|
|
418539
|
-
|
|
418540
|
-
|
|
418541
|
-
|
|
418542
|
-
|
|
418543
|
-
|
|
418544
|
-
|
|
418545
|
-
|
|
418546
|
-
|
|
418547
|
-
|
|
418548
|
-
|
|
418549
|
-
});
|
|
418550
|
-
}
|
|
418575
|
+
const cancelRunId = interruptedRunId ?? params.command.run_id ?? null;
|
|
418576
|
+
const backendCancellation = cancelAgentId && cancelRunId ? resolvedDeps.cancelRun(cancelAgentId, cancelRunId).catch(() => resolvedDeps.cancelConversation(cancelAgentId, cancelConversationId)) : cancelAgentId ? resolvedDeps.cancelConversation(cancelAgentId, cancelConversationId) : resolvedDeps.cancelConversationRun(cancelConversationId, cancelRunId);
|
|
418577
|
+
backendCancellation.catch(() => {}).finally(() => {
|
|
418578
|
+
if (!cancellation.lease) {
|
|
418579
|
+
return;
|
|
418580
|
+
}
|
|
418581
|
+
const settlement = scopedRuntime.turnLifecycle.settleCancellation(cancellation.lease);
|
|
418582
|
+
if (settlement.released) {
|
|
418583
|
+
resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
|
|
418584
|
+
}
|
|
418585
|
+
});
|
|
418551
418586
|
resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
|
|
418552
418587
|
return true;
|
|
418553
418588
|
}
|
|
@@ -445147,23 +445182,24 @@ var init_streamableHttp = __esm(() => {
|
|
|
445147
445182
|
async function connectMcpServer(config2, options = {}) {
|
|
445148
445183
|
let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
445149
445184
|
try {
|
|
445185
|
+
options.signal?.throwIfAborted();
|
|
445150
445186
|
const transport = createTransport2(config2, options);
|
|
445151
445187
|
try {
|
|
445152
|
-
await client.connect(transport);
|
|
445188
|
+
await client.connect(transport, options.signal ? { signal: options.signal } : undefined);
|
|
445153
445189
|
} catch (error5) {
|
|
445154
445190
|
if (!(error5 instanceof UnauthorizedError) || !options.oauth?.waitForAuthorizationCode || !supportsOAuthCompletion(transport)) {
|
|
445155
445191
|
throw error5;
|
|
445156
445192
|
}
|
|
445157
|
-
const authorizationCode = await options.oauth.waitForAuthorizationCode();
|
|
445158
|
-
await transport.finishAuth(authorizationCode);
|
|
445193
|
+
const authorizationCode = await withAbort(options.oauth.waitForAuthorizationCode(), options.signal, () => options.oauth?.close());
|
|
445194
|
+
await withAbort(transport.finishAuth(authorizationCode), options.signal, () => transport.close());
|
|
445159
445195
|
await client.close().catch(() => transport.close().catch(() => {
|
|
445160
445196
|
return;
|
|
445161
445197
|
}));
|
|
445162
445198
|
client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
445163
|
-
await client.connect(createTransport2(config2, options));
|
|
445199
|
+
await client.connect(createTransport2(config2, options), options.signal ? { signal: options.signal } : undefined);
|
|
445164
445200
|
}
|
|
445165
|
-
await options.oauth?.close();
|
|
445166
|
-
const response = await client.listTools();
|
|
445201
|
+
await (options.oauth?.closeCallback?.() ?? options.oauth?.close() ?? Promise.resolve());
|
|
445202
|
+
const response = await client.listTools(undefined, options.signal ? { signal: options.signal } : undefined);
|
|
445167
445203
|
const tools = response.tools.map((tool) => ({
|
|
445168
445204
|
...tool,
|
|
445169
445205
|
inputSchema: normalizeInputSchema(tool.inputSchema),
|
|
@@ -445186,7 +445222,13 @@ async function connectMcpServer(config2, options = {}) {
|
|
|
445186
445222
|
if (closed)
|
|
445187
445223
|
return;
|
|
445188
445224
|
closed = true;
|
|
445189
|
-
await
|
|
445225
|
+
const results = await Promise.allSettled([
|
|
445226
|
+
client.close(),
|
|
445227
|
+
options.oauth?.close()
|
|
445228
|
+
]);
|
|
445229
|
+
const failure2 = results.find((result3) => result3.status === "rejected");
|
|
445230
|
+
if (failure2)
|
|
445231
|
+
throw failure2.reason;
|
|
445190
445232
|
}
|
|
445191
445233
|
};
|
|
445192
445234
|
} catch (error5) {
|
|
@@ -445197,12 +445239,34 @@ async function connectMcpServer(config2, options = {}) {
|
|
|
445197
445239
|
throw error5;
|
|
445198
445240
|
}
|
|
445199
445241
|
}
|
|
445242
|
+
async function withAbort(operation, signal, onAbort) {
|
|
445243
|
+
if (!signal)
|
|
445244
|
+
return operation;
|
|
445245
|
+
signal.throwIfAborted();
|
|
445246
|
+
let abort;
|
|
445247
|
+
const cancellation = new Promise((_resolve, reject2) => {
|
|
445248
|
+
abort = () => {
|
|
445249
|
+
Promise.resolve(onAbort?.()).catch(() => {
|
|
445250
|
+
return;
|
|
445251
|
+
});
|
|
445252
|
+
reject2(signal.reason ?? new DOMException("Operation aborted", "AbortError"));
|
|
445253
|
+
};
|
|
445254
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
445255
|
+
});
|
|
445256
|
+
try {
|
|
445257
|
+
return await Promise.race([operation, cancellation]);
|
|
445258
|
+
} finally {
|
|
445259
|
+
if (abort)
|
|
445260
|
+
signal.removeEventListener("abort", abort);
|
|
445261
|
+
}
|
|
445262
|
+
}
|
|
445200
445263
|
function createTransport2(config2, options) {
|
|
445201
445264
|
if (config2.transport === "http") {
|
|
445202
445265
|
const headers = resolveHeaderEnvironment(config2.headers);
|
|
445203
445266
|
return new StreamableHTTPClientTransport(new URL(config2.url), {
|
|
445204
445267
|
requestInit: headersRequestInit(headers),
|
|
445205
|
-
authProvider: options.oauth?.authProvider
|
|
445268
|
+
authProvider: options.oauth?.authProvider,
|
|
445269
|
+
fetch: abortAwareFetch(options.signal)
|
|
445206
445270
|
});
|
|
445207
445271
|
}
|
|
445208
445272
|
if (config2.transport === "sse") {
|
|
@@ -445211,7 +445275,7 @@ function createTransport2(config2, options) {
|
|
|
445211
445275
|
return new SSEClientTransport(new URL(config2.url), {
|
|
445212
445276
|
requestInit,
|
|
445213
445277
|
authProvider: options.oauth?.authProvider,
|
|
445214
|
-
fetch:
|
|
445278
|
+
fetch: abortAwareFetch(options.signal, headers)
|
|
445215
445279
|
});
|
|
445216
445280
|
}
|
|
445217
445281
|
return new StdioClientTransport({
|
|
@@ -445222,6 +445286,18 @@ function createTransport2(config2, options) {
|
|
|
445222
445286
|
stderr: options.stderr ?? "inherit"
|
|
445223
445287
|
});
|
|
445224
445288
|
}
|
|
445289
|
+
function abortAwareFetch(operationSignal2, headers) {
|
|
445290
|
+
if (!operationSignal2 && !headers)
|
|
445291
|
+
return;
|
|
445292
|
+
return (url2, init) => {
|
|
445293
|
+
const requestInit = headers ? mergeHeaders4(init, headers) : { ...init };
|
|
445294
|
+
const requestSignal3 = requestInit.signal ?? undefined;
|
|
445295
|
+
return fetch(url2, {
|
|
445296
|
+
...requestInit,
|
|
445297
|
+
signal: operationSignal2 && requestSignal3 ? AbortSignal.any([operationSignal2, requestSignal3]) : operationSignal2 ?? requestSignal3
|
|
445298
|
+
});
|
|
445299
|
+
};
|
|
445300
|
+
}
|
|
445225
445301
|
function supportsOAuthCompletion(transport) {
|
|
445226
445302
|
return "finishAuth" in transport && typeof transport.finishAuth === "function";
|
|
445227
445303
|
}
|
|
@@ -445268,60 +445344,281 @@ var init_mcp_client = __esm(() => {
|
|
|
445268
445344
|
init_streamableHttp();
|
|
445269
445345
|
DEFAULT_CLIENT_INFO = {
|
|
445270
445346
|
name: "letta-code",
|
|
445271
|
-
version: "0.33.
|
|
445347
|
+
version: "0.33.7"
|
|
445272
445348
|
};
|
|
445273
445349
|
});
|
|
445274
445350
|
|
|
445351
|
+
// src/mcp-oauth-callback.ts
|
|
445352
|
+
import { createServer as createServer3 } from "node:http";
|
|
445353
|
+
async function startOAuthCallbackServer2(preferredPort) {
|
|
445354
|
+
try {
|
|
445355
|
+
return await startOAuthCallbackServerOnPort(preferredPort ?? 0);
|
|
445356
|
+
} catch (error5) {
|
|
445357
|
+
if (!preferredPort)
|
|
445358
|
+
throw error5;
|
|
445359
|
+
return startOAuthCallbackServerOnPort(0);
|
|
445360
|
+
}
|
|
445361
|
+
}
|
|
445362
|
+
function callbackPort(redirectUrl) {
|
|
445363
|
+
if (!redirectUrl)
|
|
445364
|
+
return;
|
|
445365
|
+
try {
|
|
445366
|
+
const parsed = new URL(redirectUrl);
|
|
445367
|
+
return parsed.hostname === "127.0.0.1" && parsed.port ? Number(parsed.port) : undefined;
|
|
445368
|
+
} catch {
|
|
445369
|
+
return;
|
|
445370
|
+
}
|
|
445371
|
+
}
|
|
445372
|
+
async function startOAuthCallbackServerOnPort(port) {
|
|
445373
|
+
const expectedState = {};
|
|
445374
|
+
let server2;
|
|
445375
|
+
let completed = false;
|
|
445376
|
+
let settle3;
|
|
445377
|
+
let reject2;
|
|
445378
|
+
const codePromise = new Promise((resolve39, rejectPromise) => {
|
|
445379
|
+
settle3 = (code2) => {
|
|
445380
|
+
completed = true;
|
|
445381
|
+
resolve39(code2);
|
|
445382
|
+
};
|
|
445383
|
+
reject2 = (error5) => {
|
|
445384
|
+
completed = true;
|
|
445385
|
+
rejectPromise(error5);
|
|
445386
|
+
};
|
|
445387
|
+
});
|
|
445388
|
+
codePromise.catch(() => {
|
|
445389
|
+
return;
|
|
445390
|
+
});
|
|
445391
|
+
server2 = createServer3((request, response) => {
|
|
445392
|
+
if (request.method !== "GET" || !request.url?.startsWith("/") || request.url.startsWith("//") || request.url.includes("\\")) {
|
|
445393
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445394
|
+
response.end("Invalid request target");
|
|
445395
|
+
return;
|
|
445396
|
+
}
|
|
445397
|
+
const address2 = server2.address();
|
|
445398
|
+
const expectedHost = address2 && typeof address2 !== "string" ? `127.0.0.1:${address2.port}` : undefined;
|
|
445399
|
+
if (!expectedHost || request.headers.host !== expectedHost) {
|
|
445400
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445401
|
+
response.end("Invalid callback host");
|
|
445402
|
+
return;
|
|
445403
|
+
}
|
|
445404
|
+
let url2;
|
|
445405
|
+
try {
|
|
445406
|
+
url2 = new URL(request.url, `http://${expectedHost}`);
|
|
445407
|
+
} catch {
|
|
445408
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445409
|
+
response.end("Invalid request target");
|
|
445410
|
+
return;
|
|
445411
|
+
}
|
|
445412
|
+
if (url2.origin !== `http://${expectedHost}` || url2.pathname !== "/callback") {
|
|
445413
|
+
response.writeHead(404).end("Not found");
|
|
445414
|
+
return;
|
|
445415
|
+
}
|
|
445416
|
+
const error5 = url2.searchParams.get("error");
|
|
445417
|
+
const code2 = url2.searchParams.get("code");
|
|
445418
|
+
const state = url2.searchParams.get("state");
|
|
445419
|
+
if (!state || state !== expectedState.value) {
|
|
445420
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445421
|
+
response.end(callbackPage("Authorization failed", "Invalid OAuth callback"));
|
|
445422
|
+
return;
|
|
445423
|
+
}
|
|
445424
|
+
if (error5) {
|
|
445425
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445426
|
+
reject2?.(new Error(`MCP OAuth authorization failed: ${error5}`));
|
|
445427
|
+
response.end(callbackPage("Authorization failed", error5), () => {
|
|
445428
|
+
closeServer(true);
|
|
445429
|
+
});
|
|
445430
|
+
return;
|
|
445431
|
+
}
|
|
445432
|
+
if (!code2) {
|
|
445433
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445434
|
+
response.end(callbackPage("Authorization failed", "Missing authorization code"));
|
|
445435
|
+
return;
|
|
445436
|
+
}
|
|
445437
|
+
response.writeHead(200, { "Content-Type": "text/html" });
|
|
445438
|
+
settle3?.(code2);
|
|
445439
|
+
response.end(callbackPage("Authorization complete", "You can close this tab and return to Letta Code."), () => {
|
|
445440
|
+
closeServer(true);
|
|
445441
|
+
});
|
|
445442
|
+
});
|
|
445443
|
+
server2.on("clientError", (_error, socket) => {
|
|
445444
|
+
if (!socket.writable)
|
|
445445
|
+
return;
|
|
445446
|
+
socket.end(`HTTP/1.1 400 Bad Request\r
|
|
445447
|
+
Connection: close\r
|
|
445448
|
+
Content-Length: 0\r
|
|
445449
|
+
\r
|
|
445450
|
+
`);
|
|
445451
|
+
});
|
|
445452
|
+
let serverClosing = false;
|
|
445453
|
+
const closeServer = (force = false) => {
|
|
445454
|
+
if (serverClosing)
|
|
445455
|
+
return;
|
|
445456
|
+
serverClosing = true;
|
|
445457
|
+
server2.close();
|
|
445458
|
+
if (force)
|
|
445459
|
+
server2.closeAllConnections();
|
|
445460
|
+
};
|
|
445461
|
+
await new Promise((resolve39, rejectListen) => {
|
|
445462
|
+
server2.once("error", rejectListen);
|
|
445463
|
+
server2.listen(port, "127.0.0.1", resolve39);
|
|
445464
|
+
});
|
|
445465
|
+
server2.unref();
|
|
445466
|
+
const address = server2.address();
|
|
445467
|
+
if (!address || typeof address === "string") {
|
|
445468
|
+
server2.close();
|
|
445469
|
+
throw new Error("Could not start MCP OAuth callback server");
|
|
445470
|
+
}
|
|
445471
|
+
const timeout = setTimeout(() => {
|
|
445472
|
+
reject2?.(new Error("Timed out waiting for MCP OAuth authorization"));
|
|
445473
|
+
closeServer(true);
|
|
445474
|
+
}, CALLBACK_TIMEOUT_MS);
|
|
445475
|
+
timeout.unref();
|
|
445476
|
+
return {
|
|
445477
|
+
redirectUrl: `http://127.0.0.1:${address.port}/callback`,
|
|
445478
|
+
expectedState,
|
|
445479
|
+
waitForCode: () => codePromise.finally(() => clearTimeout(timeout)),
|
|
445480
|
+
close: () => {
|
|
445481
|
+
clearTimeout(timeout);
|
|
445482
|
+
if (!completed)
|
|
445483
|
+
reject2?.(new Error("MCP OAuth flow was cancelled"));
|
|
445484
|
+
closeServer(true);
|
|
445485
|
+
}
|
|
445486
|
+
};
|
|
445487
|
+
}
|
|
445488
|
+
function callbackPage(title, message) {
|
|
445489
|
+
return `<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>${escapeHtml2(title)}</h1><p>${escapeHtml2(message)}</p></body></html>`;
|
|
445490
|
+
}
|
|
445491
|
+
function escapeHtml2(value) {
|
|
445492
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
445493
|
+
const entities = {
|
|
445494
|
+
"&": "&",
|
|
445495
|
+
"<": "<",
|
|
445496
|
+
">": ">",
|
|
445497
|
+
'"': """,
|
|
445498
|
+
"'": "'"
|
|
445499
|
+
};
|
|
445500
|
+
return entities[character] ?? character;
|
|
445501
|
+
});
|
|
445502
|
+
}
|
|
445503
|
+
var CALLBACK_TIMEOUT_MS;
|
|
445504
|
+
var init_mcp_oauth_callback = __esm(() => {
|
|
445505
|
+
CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
445506
|
+
});
|
|
445507
|
+
|
|
445275
445508
|
// src/mcp-oauth.ts
|
|
445276
445509
|
import { createHash as createHash11, randomBytes as randomBytes2 } from "node:crypto";
|
|
445277
|
-
import { createServer as createServer3 } from "node:http";
|
|
445278
445510
|
async function clearMcpOAuthCredentials(agentId, serverName, serverUrl) {
|
|
445279
|
-
|
|
445511
|
+
const credentialKey = mcpOAuthCredentialKey(agentId, serverName, serverUrl);
|
|
445512
|
+
const coordinationKey = storageCoordinationKey(bunSecretStorage, credentialKey, "bun-secrets");
|
|
445513
|
+
const { result: result3 } = await clearStoredCredentials(bunSecretStorage, credentialKey, coordinationKey);
|
|
445514
|
+
return result3 === true;
|
|
445280
445515
|
}
|
|
445281
445516
|
async function createMcpOAuthSession(agentId, serverName, serverUrl, options) {
|
|
445282
|
-
const
|
|
445283
|
-
|
|
445284
|
-
|
|
445285
|
-
|
|
445286
|
-
|
|
445287
|
-
const redirectUrl = callback?.redirectUrl ?? persisted?.redirectUrl;
|
|
445288
|
-
if (!redirectUrl)
|
|
445289
|
-
return;
|
|
445290
|
-
const provider = new PersistentMcpOAuthProvider({
|
|
445291
|
-
secretName,
|
|
445292
|
-
redirectUrl,
|
|
445293
|
-
persisted,
|
|
445517
|
+
const credentialKey = mcpOAuthCredentialKey(agentId, serverName, serverUrl);
|
|
445518
|
+
return createMcpOAuthSessionWithStorage({
|
|
445519
|
+
credentialKey,
|
|
445520
|
+
storageNamespace: "bun-secrets",
|
|
445521
|
+
storage: bunSecretStorage,
|
|
445294
445522
|
interactive: options.interactive,
|
|
445295
445523
|
onStatus: options.onStatus,
|
|
445296
|
-
openBrowser: options.openBrowser ?? openSystemBrowser
|
|
445297
|
-
expectedState: callback?.expectedState
|
|
445524
|
+
openBrowser: options.openBrowser ?? openSystemBrowser
|
|
445298
445525
|
});
|
|
445299
|
-
|
|
445300
|
-
|
|
445301
|
-
|
|
445302
|
-
|
|
445303
|
-
|
|
445526
|
+
}
|
|
445527
|
+
async function createMcpOAuthSessionWithStorage(options) {
|
|
445528
|
+
const coordinationKey = storageCoordinationKey(options.storage, options.credentialKey, options.storageNamespace);
|
|
445529
|
+
retainCredentialCoordinator(coordinationKey);
|
|
445530
|
+
let provider;
|
|
445531
|
+
let callback;
|
|
445532
|
+
try {
|
|
445533
|
+
const loaded = await loadState(options.storage, options.credentialKey, coordinationKey);
|
|
445534
|
+
const persisted = loaded.state;
|
|
445535
|
+
if (!options.interactive && !persisted) {
|
|
445536
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445537
|
+
return;
|
|
445538
|
+
}
|
|
445539
|
+
callback = options.interactive ? await startOAuthCallbackServer2(callbackPort(persisted?.redirectUrl)) : undefined;
|
|
445540
|
+
const redirectUrl = callback?.redirectUrl ?? persisted?.redirectUrl;
|
|
445541
|
+
if (!redirectUrl) {
|
|
445542
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445543
|
+
return;
|
|
445544
|
+
}
|
|
445545
|
+
provider = new PersistentMcpOAuthProvider({
|
|
445546
|
+
credentialKey: options.credentialKey,
|
|
445547
|
+
coordinationKey,
|
|
445548
|
+
storage: options.storage,
|
|
445549
|
+
redirectUrl,
|
|
445550
|
+
persisted,
|
|
445551
|
+
interactive: options.interactive,
|
|
445552
|
+
onStatus: options.onStatus,
|
|
445553
|
+
openBrowser: options.openBrowser ?? openSystemBrowser,
|
|
445554
|
+
expectedState: callback?.expectedState,
|
|
445555
|
+
generation: loaded.generation
|
|
445556
|
+
});
|
|
445557
|
+
const activeProvider = provider;
|
|
445558
|
+
const activeCallback = callback;
|
|
445559
|
+
return {
|
|
445560
|
+
authProvider: activeProvider,
|
|
445561
|
+
...activeCallback ? { waitForAuthorizationCode: () => activeCallback.waitForCode() } : {},
|
|
445562
|
+
exportCredentials: () => activeProvider.exportCredentials(),
|
|
445563
|
+
closeCallback: async () => activeCallback?.close(),
|
|
445564
|
+
close: async () => {
|
|
445565
|
+
activeCallback?.close();
|
|
445566
|
+
await activeProvider.dispose();
|
|
445567
|
+
}
|
|
445568
|
+
};
|
|
445569
|
+
} catch (error5) {
|
|
445570
|
+
callback?.close();
|
|
445571
|
+
if (provider)
|
|
445572
|
+
await provider.dispose();
|
|
445573
|
+
else
|
|
445574
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445575
|
+
throw error5;
|
|
445576
|
+
}
|
|
445304
445577
|
}
|
|
445305
445578
|
|
|
445306
445579
|
class PersistentMcpOAuthProvider {
|
|
445307
445580
|
stateData;
|
|
445308
|
-
|
|
445581
|
+
pendingClientInformation;
|
|
445582
|
+
pendingDiscoveryState;
|
|
445583
|
+
codeVerifierValue;
|
|
445584
|
+
generation;
|
|
445585
|
+
observedClientVersion;
|
|
445586
|
+
observedTokenVersion;
|
|
445587
|
+
observedDiscoveryVersion;
|
|
445588
|
+
tokenOperationGeneration;
|
|
445589
|
+
tokenOperationVersion;
|
|
445590
|
+
coordinatorValue;
|
|
445591
|
+
credentialKey;
|
|
445592
|
+
coordinationKey;
|
|
445593
|
+
storage;
|
|
445309
445594
|
interactive;
|
|
445310
445595
|
onStatus;
|
|
445311
445596
|
openBrowser;
|
|
445312
445597
|
expectedState;
|
|
445598
|
+
closed = false;
|
|
445313
445599
|
constructor(options) {
|
|
445314
|
-
this.
|
|
445600
|
+
this.credentialKey = options.credentialKey;
|
|
445601
|
+
this.coordinationKey = options.coordinationKey;
|
|
445602
|
+
this.storage = options.storage;
|
|
445315
445603
|
this.interactive = options.interactive;
|
|
445316
445604
|
this.onStatus = options.onStatus;
|
|
445317
445605
|
this.openBrowser = options.openBrowser;
|
|
445318
445606
|
this.expectedState = options.expectedState;
|
|
445607
|
+
this.generation = options.generation;
|
|
445608
|
+
const coordinator = getCredentialCoordinator(options.coordinationKey);
|
|
445609
|
+
this.coordinatorValue = coordinator;
|
|
445610
|
+
this.observedClientVersion = coordinator.clientVersion;
|
|
445611
|
+
this.observedTokenVersion = coordinator.tokenVersion;
|
|
445612
|
+
this.observedDiscoveryVersion = coordinator.discoveryVersion;
|
|
445613
|
+
this.tokenOperationGeneration = coordinator.generation;
|
|
445614
|
+
this.tokenOperationVersion = coordinator.tokenVersion;
|
|
445319
445615
|
this.stateData = {
|
|
445320
445616
|
...options.persisted,
|
|
445321
445617
|
redirectUrl: options.redirectUrl
|
|
445322
445618
|
};
|
|
445323
445619
|
}
|
|
445324
445620
|
get redirectUrl() {
|
|
445621
|
+
this.assertOpen();
|
|
445325
445622
|
return this.stateData.redirectUrl;
|
|
445326
445623
|
}
|
|
445327
445624
|
get clientMetadata() {
|
|
@@ -445334,27 +445631,127 @@ class PersistentMcpOAuthProvider {
|
|
|
445334
445631
|
};
|
|
445335
445632
|
}
|
|
445336
445633
|
state() {
|
|
445634
|
+
this.assertOpen();
|
|
445337
445635
|
const state = randomBytes2(24).toString("base64url");
|
|
445338
445636
|
if (this.expectedState)
|
|
445339
445637
|
this.expectedState.value = state;
|
|
445340
445638
|
return state;
|
|
445341
445639
|
}
|
|
445342
445640
|
clientInformation() {
|
|
445343
|
-
|
|
445344
|
-
|
|
445641
|
+
this.assertOpen();
|
|
445642
|
+
if (this.generation !== this.coordinator.generation)
|
|
445643
|
+
return;
|
|
445644
|
+
if (this.pendingClientInformation && this.pendingClientInformation.baseVersion !== this.coordinator.clientVersion) {
|
|
445645
|
+
this.pendingClientInformation = undefined;
|
|
445646
|
+
}
|
|
445647
|
+
const client = this.pendingClientInformation?.value ?? this.coordinator.state?.clientInformation;
|
|
445648
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445649
|
+
return client && "redirect_uris" in client && Array.isArray(client.redirect_uris) && client.redirect_uris.includes(this.redirectUrl) ? client : undefined;
|
|
445345
445650
|
}
|
|
445346
445651
|
saveClientInformation(clientInformation) {
|
|
445652
|
+
this.assertOpen();
|
|
445653
|
+
assertCurrentGeneration(this.coordinationKey, this.generation);
|
|
445347
445654
|
this.stateData.clientInformation = clientInformation;
|
|
445655
|
+
this.pendingClientInformation = {
|
|
445656
|
+
value: clientInformation,
|
|
445657
|
+
baseVersion: this.coordinator.clientVersion
|
|
445658
|
+
};
|
|
445659
|
+
if (!this.coordinator.state?.tokens) {
|
|
445660
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445661
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445662
|
+
}
|
|
445348
445663
|
}
|
|
445349
445664
|
tokens() {
|
|
445350
|
-
|
|
445665
|
+
this.assertOpen();
|
|
445666
|
+
if (this.generation !== this.coordinator.generation)
|
|
445667
|
+
return;
|
|
445668
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445669
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445670
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445671
|
+
return this.coordinator.state?.tokens;
|
|
445672
|
+
}
|
|
445673
|
+
async exportCredentials() {
|
|
445674
|
+
this.assertOpen();
|
|
445675
|
+
const expectedGeneration = this.generation;
|
|
445676
|
+
return enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445677
|
+
this.assertOpen();
|
|
445678
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445679
|
+
const latest = await readStoredState(this.storage, this.credentialKey);
|
|
445680
|
+
if (!latest) {
|
|
445681
|
+
throw new Error("MCP OAuth authorization is not complete");
|
|
445682
|
+
}
|
|
445683
|
+
this.assertOpen();
|
|
445684
|
+
this.coordinator.state = latest;
|
|
445685
|
+
this.stateData = latest;
|
|
445686
|
+
return exportCredentialSnapshot(latest);
|
|
445687
|
+
});
|
|
445351
445688
|
}
|
|
445352
445689
|
async saveTokens(tokens) {
|
|
445353
|
-
this.
|
|
445354
|
-
|
|
445355
|
-
|
|
445690
|
+
this.assertOpen();
|
|
445691
|
+
const expectedGeneration = this.tokenOperationGeneration;
|
|
445692
|
+
const expectedTokenVersion = this.tokenOperationVersion;
|
|
445693
|
+
const tokenExpiresAt = tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;
|
|
445694
|
+
const pendingClientInformation = this.pendingClientInformation;
|
|
445695
|
+
const pendingDiscoveryState = this.pendingDiscoveryState;
|
|
445696
|
+
const result3 = await enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445697
|
+
this.assertOpen();
|
|
445698
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445699
|
+
if (this.coordinator.tokenVersion !== expectedTokenVersion) {
|
|
445700
|
+
throw new Error("MCP OAuth token response is stale because credentials changed");
|
|
445701
|
+
}
|
|
445702
|
+
if (pendingClientInformation && this.coordinator.clientVersion !== pendingClientInformation.baseVersion || pendingDiscoveryState && this.coordinator.discoveryVersion !== pendingDiscoveryState.baseVersion) {
|
|
445703
|
+
throw new Error("MCP OAuth token response is stale because protocol state changed");
|
|
445704
|
+
}
|
|
445705
|
+
const state = await readStoredState(this.storage, this.credentialKey) ?? { redirectUrl: this.redirectUrl };
|
|
445706
|
+
let clientMerged = false;
|
|
445707
|
+
let discoveryMerged = false;
|
|
445708
|
+
if (pendingClientInformation && this.coordinator.clientVersion === pendingClientInformation.baseVersion) {
|
|
445709
|
+
state.redirectUrl = this.redirectUrl;
|
|
445710
|
+
state.clientInformation = pendingClientInformation.value;
|
|
445711
|
+
clientMerged = true;
|
|
445712
|
+
}
|
|
445713
|
+
if (pendingDiscoveryState && this.coordinator.discoveryVersion === pendingDiscoveryState.baseVersion) {
|
|
445714
|
+
state.discoveryState = pendingDiscoveryState.value;
|
|
445715
|
+
discoveryMerged = true;
|
|
445716
|
+
}
|
|
445717
|
+
state.tokens = {
|
|
445718
|
+
...tokens,
|
|
445719
|
+
refresh_token: tokens.refresh_token ?? state.tokens?.refresh_token
|
|
445720
|
+
};
|
|
445721
|
+
state.tokenExpiresAt = tokenExpiresAt;
|
|
445722
|
+
await this.storage.set(this.credentialKey, JSON.stringify(state));
|
|
445723
|
+
this.coordinator.state = state;
|
|
445724
|
+
this.coordinator.tokenVersion += 1;
|
|
445725
|
+
if (clientMerged)
|
|
445726
|
+
this.coordinator.clientVersion += 1;
|
|
445727
|
+
if (discoveryMerged)
|
|
445728
|
+
this.coordinator.discoveryVersion += 1;
|
|
445729
|
+
return { state, clientMerged, discoveryMerged };
|
|
445730
|
+
});
|
|
445731
|
+
this.codeVerifierValue = undefined;
|
|
445732
|
+
if (this.closed) {
|
|
445733
|
+
this.scrubLocalState();
|
|
445734
|
+
return;
|
|
445735
|
+
}
|
|
445736
|
+
this.stateData = result3.state;
|
|
445737
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445738
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445739
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445740
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445741
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445742
|
+
if (this.pendingClientInformation === pendingClientInformation) {
|
|
445743
|
+
this.pendingClientInformation = undefined;
|
|
445744
|
+
} else if (this.pendingClientInformation) {
|
|
445745
|
+
this.pendingClientInformation.baseVersion = this.coordinator.clientVersion;
|
|
445746
|
+
}
|
|
445747
|
+
if (this.pendingDiscoveryState === pendingDiscoveryState) {
|
|
445748
|
+
this.pendingDiscoveryState = undefined;
|
|
445749
|
+
} else if (this.pendingDiscoveryState) {
|
|
445750
|
+
this.pendingDiscoveryState.baseVersion = this.coordinator.discoveryVersion;
|
|
445751
|
+
}
|
|
445356
445752
|
}
|
|
445357
445753
|
async redirectToAuthorization(authorizationUrl) {
|
|
445754
|
+
this.assertOpen();
|
|
445358
445755
|
if (!this.interactive) {
|
|
445359
445756
|
throw new Error("MCP authentication requires user authorization. Open /mcp and press R to sign in.");
|
|
445360
445757
|
}
|
|
@@ -445364,147 +445761,268 @@ ${authorizationUrl.toString()}`);
|
|
|
445364
445761
|
await this.openBrowser(authorizationUrl.toString());
|
|
445365
445762
|
}
|
|
445366
445763
|
saveCodeVerifier(codeVerifier) {
|
|
445367
|
-
this.
|
|
445764
|
+
this.assertOpen();
|
|
445765
|
+
this.codeVerifierValue = codeVerifier;
|
|
445368
445766
|
}
|
|
445369
445767
|
codeVerifier() {
|
|
445370
|
-
|
|
445768
|
+
this.assertOpen();
|
|
445769
|
+
if (!this.codeVerifierValue) {
|
|
445371
445770
|
throw new Error("No MCP OAuth PKCE verifier is available");
|
|
445372
445771
|
}
|
|
445373
|
-
return this.
|
|
445772
|
+
return this.codeVerifierValue;
|
|
445374
445773
|
}
|
|
445375
445774
|
saveDiscoveryState(state) {
|
|
445775
|
+
this.assertOpen();
|
|
445776
|
+
assertCurrentGeneration(this.coordinationKey, this.generation);
|
|
445376
445777
|
this.stateData.discoveryState = state;
|
|
445778
|
+
this.pendingDiscoveryState = {
|
|
445779
|
+
value: state,
|
|
445780
|
+
baseVersion: this.coordinator.discoveryVersion
|
|
445781
|
+
};
|
|
445782
|
+
if (!this.coordinator.state?.tokens) {
|
|
445783
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445784
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445785
|
+
}
|
|
445377
445786
|
}
|
|
445378
445787
|
discoveryState() {
|
|
445379
|
-
|
|
445380
|
-
|
|
445381
|
-
async invalidateCredentials(scope) {
|
|
445382
|
-
if (scope === "all") {
|
|
445383
|
-
this.stateData = { redirectUrl: this.redirectUrl };
|
|
445384
|
-
await deleteSecretValue(this.secretName);
|
|
445788
|
+
this.assertOpen();
|
|
445789
|
+
if (this.generation !== this.coordinator.generation)
|
|
445385
445790
|
return;
|
|
445791
|
+
if (this.pendingDiscoveryState && this.pendingDiscoveryState.baseVersion !== this.coordinator.discoveryVersion) {
|
|
445792
|
+
this.pendingDiscoveryState = undefined;
|
|
445386
445793
|
}
|
|
445387
|
-
|
|
445388
|
-
|
|
445389
|
-
if (scope === "tokens")
|
|
445390
|
-
delete this.stateData.tokens;
|
|
445391
|
-
if (scope === "verifier")
|
|
445392
|
-
delete this.stateData.codeVerifier;
|
|
445393
|
-
if (scope === "discovery")
|
|
445394
|
-
delete this.stateData.discoveryState;
|
|
445395
|
-
await this.persist();
|
|
445396
|
-
}
|
|
445397
|
-
async persist() {
|
|
445398
|
-
await setSecretValue(this.secretName, JSON.stringify(this.stateData));
|
|
445399
|
-
}
|
|
445400
|
-
}
|
|
445401
|
-
async function startOAuthCallbackServer2(preferredPort) {
|
|
445402
|
-
try {
|
|
445403
|
-
return await startOAuthCallbackServerOnPort(preferredPort ?? 0);
|
|
445404
|
-
} catch (error5) {
|
|
445405
|
-
if (!preferredPort)
|
|
445406
|
-
throw error5;
|
|
445407
|
-
return startOAuthCallbackServerOnPort(0);
|
|
445794
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445795
|
+
return this.pendingDiscoveryState?.value ?? this.coordinator.state?.discoveryState;
|
|
445408
445796
|
}
|
|
445409
|
-
|
|
445410
|
-
|
|
445411
|
-
|
|
445412
|
-
|
|
445413
|
-
let completed = false;
|
|
445414
|
-
let settle3;
|
|
445415
|
-
let reject2;
|
|
445416
|
-
const codePromise = new Promise((resolve39, rejectPromise) => {
|
|
445417
|
-
settle3 = (code2) => {
|
|
445418
|
-
completed = true;
|
|
445419
|
-
resolve39(code2);
|
|
445420
|
-
};
|
|
445421
|
-
reject2 = (error5) => {
|
|
445422
|
-
completed = true;
|
|
445423
|
-
rejectPromise(error5);
|
|
445424
|
-
};
|
|
445425
|
-
});
|
|
445426
|
-
codePromise.catch(() => {
|
|
445427
|
-
return;
|
|
445428
|
-
});
|
|
445429
|
-
server2 = createServer3((request, response) => {
|
|
445430
|
-
const url2 = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
445431
|
-
if (url2.pathname !== "/callback") {
|
|
445432
|
-
response.writeHead(404).end("Not found");
|
|
445797
|
+
async invalidateCredentials(scope) {
|
|
445798
|
+
this.assertOpen();
|
|
445799
|
+
if (scope === "verifier") {
|
|
445800
|
+
this.codeVerifierValue = undefined;
|
|
445433
445801
|
return;
|
|
445434
445802
|
}
|
|
445435
|
-
|
|
445436
|
-
|
|
445437
|
-
|
|
445438
|
-
|
|
445439
|
-
|
|
445440
|
-
|
|
445441
|
-
|
|
445442
|
-
|
|
445803
|
+
if (scope === "all") {
|
|
445804
|
+
const redirectUrl = this.redirectUrl;
|
|
445805
|
+
const { generation: generation3, cleared } = await clearStoredCredentials(this.storage, this.credentialKey, this.coordinationKey, {
|
|
445806
|
+
generation: this.generation,
|
|
445807
|
+
clientVersion: this.observedClientVersion,
|
|
445808
|
+
tokenVersion: this.observedTokenVersion,
|
|
445809
|
+
discoveryVersion: this.observedDiscoveryVersion
|
|
445810
|
+
});
|
|
445811
|
+
if (this.closed)
|
|
445812
|
+
return;
|
|
445813
|
+
if (!cleared) {
|
|
445814
|
+
this.stateData = this.coordinator.state ?? { redirectUrl };
|
|
445815
|
+
this.pendingClientInformation = undefined;
|
|
445816
|
+
this.pendingDiscoveryState = undefined;
|
|
445817
|
+
this.codeVerifierValue = undefined;
|
|
445818
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445819
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445820
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445821
|
+
return;
|
|
445822
|
+
}
|
|
445823
|
+
this.generation = generation3;
|
|
445824
|
+
this.stateData = { redirectUrl };
|
|
445825
|
+
this.pendingClientInformation = undefined;
|
|
445826
|
+
this.pendingDiscoveryState = undefined;
|
|
445827
|
+
this.codeVerifierValue = undefined;
|
|
445828
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445829
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445830
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445831
|
+
return;
|
|
445832
|
+
}
|
|
445833
|
+
const expectedGeneration = this.generation;
|
|
445834
|
+
const pendingClientInformation = this.pendingClientInformation;
|
|
445835
|
+
const pendingDiscoveryState = this.pendingDiscoveryState;
|
|
445836
|
+
const expectedVersion = scope === "client" ? this.observedClientVersion : scope === "tokens" ? this.observedTokenVersion : this.observedDiscoveryVersion;
|
|
445837
|
+
const result3 = await enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445838
|
+
this.assertOpen();
|
|
445839
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445840
|
+
const currentVersion = scope === "client" ? this.coordinator.clientVersion : scope === "tokens" ? this.coordinator.tokenVersion : this.coordinator.discoveryVersion;
|
|
445841
|
+
if (currentVersion !== expectedVersion) {
|
|
445842
|
+
return this.coordinator.state;
|
|
445843
|
+
}
|
|
445844
|
+
const state = await readStoredState(this.storage, this.credentialKey);
|
|
445845
|
+
if (state) {
|
|
445846
|
+
if (scope === "client")
|
|
445847
|
+
delete state.clientInformation;
|
|
445848
|
+
if (scope === "tokens") {
|
|
445849
|
+
delete state.tokens;
|
|
445850
|
+
delete state.tokenExpiresAt;
|
|
445851
|
+
}
|
|
445852
|
+
if (scope === "discovery")
|
|
445853
|
+
delete state.discoveryState;
|
|
445854
|
+
await this.storage.set(this.credentialKey, JSON.stringify(state));
|
|
445855
|
+
}
|
|
445856
|
+
this.coordinator.state = state;
|
|
445857
|
+
if (scope === "client")
|
|
445858
|
+
this.coordinator.clientVersion += 1;
|
|
445859
|
+
if (scope === "tokens")
|
|
445860
|
+
this.coordinator.tokenVersion += 1;
|
|
445861
|
+
if (scope === "discovery")
|
|
445862
|
+
this.coordinator.discoveryVersion += 1;
|
|
445863
|
+
return state;
|
|
445864
|
+
});
|
|
445865
|
+
if (this.closed)
|
|
445443
445866
|
return;
|
|
445867
|
+
this.stateData = result3 ?? { redirectUrl: this.redirectUrl };
|
|
445868
|
+
if (scope === "client") {
|
|
445869
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445870
|
+
if (this.pendingClientInformation === pendingClientInformation) {
|
|
445871
|
+
this.pendingClientInformation = undefined;
|
|
445872
|
+
} else if (this.pendingClientInformation) {
|
|
445873
|
+
this.pendingClientInformation.baseVersion = this.coordinator.clientVersion;
|
|
445874
|
+
}
|
|
445444
445875
|
}
|
|
445445
|
-
if (
|
|
445446
|
-
|
|
445447
|
-
|
|
445448
|
-
|
|
445449
|
-
|
|
445876
|
+
if (scope === "tokens") {
|
|
445877
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445878
|
+
}
|
|
445879
|
+
if (scope === "discovery") {
|
|
445880
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445881
|
+
if (this.pendingDiscoveryState === pendingDiscoveryState) {
|
|
445882
|
+
this.pendingDiscoveryState = undefined;
|
|
445883
|
+
} else if (this.pendingDiscoveryState) {
|
|
445884
|
+
this.pendingDiscoveryState.baseVersion = this.coordinator.discoveryVersion;
|
|
445885
|
+
}
|
|
445886
|
+
}
|
|
445887
|
+
}
|
|
445888
|
+
async dispose() {
|
|
445889
|
+
if (this.closed)
|
|
445450
445890
|
return;
|
|
445891
|
+
this.closed = true;
|
|
445892
|
+
this.scrubLocalState();
|
|
445893
|
+
try {
|
|
445894
|
+
await releaseCredentialCoordinator(this.coordinationKey);
|
|
445895
|
+
} finally {
|
|
445896
|
+
this.coordinatorValue = undefined;
|
|
445897
|
+
this.scrubLocalState();
|
|
445451
445898
|
}
|
|
445452
|
-
response.writeHead(200, { "Content-Type": "text/html" });
|
|
445453
|
-
response.end(callbackPage("Authorization complete", "You can close this tab and return to Letta Code."));
|
|
445454
|
-
settle3?.(code2);
|
|
445455
|
-
server2.close();
|
|
445456
|
-
});
|
|
445457
|
-
await new Promise((resolve39, rejectListen) => {
|
|
445458
|
-
server2.once("error", rejectListen);
|
|
445459
|
-
server2.listen(port, "127.0.0.1", resolve39);
|
|
445460
|
-
});
|
|
445461
|
-
server2.unref();
|
|
445462
|
-
const address = server2.address();
|
|
445463
|
-
if (!address || typeof address === "string") {
|
|
445464
|
-
server2.close();
|
|
445465
|
-
throw new Error("Could not start MCP OAuth callback server");
|
|
445466
445899
|
}
|
|
445467
|
-
|
|
445468
|
-
|
|
445469
|
-
|
|
445470
|
-
|
|
445471
|
-
|
|
445900
|
+
get coordinator() {
|
|
445901
|
+
if (!this.coordinatorValue)
|
|
445902
|
+
throw new Error("MCP OAuth session is closed");
|
|
445903
|
+
return this.coordinatorValue;
|
|
445904
|
+
}
|
|
445905
|
+
scrubLocalState() {
|
|
445906
|
+
this.codeVerifierValue = undefined;
|
|
445907
|
+
this.pendingClientInformation = undefined;
|
|
445908
|
+
this.pendingDiscoveryState = undefined;
|
|
445909
|
+
this.stateData = { redirectUrl: this.stateData.redirectUrl };
|
|
445910
|
+
}
|
|
445911
|
+
assertOpen() {
|
|
445912
|
+
if (this.closed)
|
|
445913
|
+
throw new Error("MCP OAuth session is closed");
|
|
445914
|
+
}
|
|
445915
|
+
}
|
|
445916
|
+
function exportCredentialSnapshot(state) {
|
|
445917
|
+
const clientInformation = state.clientInformation;
|
|
445918
|
+
const tokens = state.tokens;
|
|
445919
|
+
const validClient = clientInformation && "redirect_uris" in clientInformation && clientInformation.redirect_uris.includes(state.redirectUrl) ? clientInformation : undefined;
|
|
445920
|
+
if (!validClient?.client_id || !tokens?.access_token) {
|
|
445921
|
+
throw new Error("MCP OAuth authorization is not complete");
|
|
445922
|
+
}
|
|
445472
445923
|
return {
|
|
445473
|
-
|
|
445474
|
-
|
|
445475
|
-
|
|
445476
|
-
|
|
445477
|
-
|
|
445478
|
-
|
|
445479
|
-
|
|
445480
|
-
|
|
445481
|
-
}
|
|
445924
|
+
access_token: tokens.access_token,
|
|
445925
|
+
...tokens.refresh_token ? { refresh_token: tokens.refresh_token } : {},
|
|
445926
|
+
client_id: validClient.client_id,
|
|
445927
|
+
...validClient.client_secret ? { client_secret: validClient.client_secret } : {},
|
|
445928
|
+
redirect_uri: state.redirectUrl,
|
|
445929
|
+
...tokens.token_type ? { token_type: tokens.token_type } : {},
|
|
445930
|
+
...state.tokenExpiresAt !== undefined ? {
|
|
445931
|
+
expires_in: Math.max(0, Math.ceil((state.tokenExpiresAt - Date.now()) / 1000))
|
|
445932
|
+
} : {},
|
|
445933
|
+
...tokens.scope ? { scope: tokens.scope } : {}
|
|
445482
445934
|
};
|
|
445483
445935
|
}
|
|
445484
|
-
function
|
|
445485
|
-
|
|
445936
|
+
function enqueueStorageMutation(coordinationKey, mutation) {
|
|
445937
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
445938
|
+
const pending = coordinator.queue.then(mutation, mutation);
|
|
445939
|
+
coordinator.queue = pending.then(() => {
|
|
445486
445940
|
return;
|
|
445487
|
-
|
|
445488
|
-
const parsed = new URL(redirectUrl);
|
|
445489
|
-
return parsed.hostname === "127.0.0.1" && parsed.port ? Number(parsed.port) : undefined;
|
|
445490
|
-
} catch {
|
|
445941
|
+
}, () => {
|
|
445491
445942
|
return;
|
|
445943
|
+
});
|
|
445944
|
+
return pending;
|
|
445945
|
+
}
|
|
445946
|
+
function getCredentialCoordinator(coordinationKey) {
|
|
445947
|
+
const existing = credentialCoordinators.get(coordinationKey);
|
|
445948
|
+
if (existing)
|
|
445949
|
+
return existing;
|
|
445950
|
+
const coordinator = {
|
|
445951
|
+
queue: Promise.resolve(),
|
|
445952
|
+
generation: 0,
|
|
445953
|
+
activeProviders: 0,
|
|
445954
|
+
clientVersion: 0,
|
|
445955
|
+
tokenVersion: 0,
|
|
445956
|
+
discoveryVersion: 0
|
|
445957
|
+
};
|
|
445958
|
+
credentialCoordinators.set(coordinationKey, coordinator);
|
|
445959
|
+
return coordinator;
|
|
445960
|
+
}
|
|
445961
|
+
function storageCoordinationKey(storage, credentialKey, storageNamespace) {
|
|
445962
|
+
const namespace = storageNamespace;
|
|
445963
|
+
if (namespace !== undefined && (!namespace.trim() || namespace.includes("\x00"))) {
|
|
445964
|
+
throw new Error("MCP OAuth storage namespace must be non-empty and contain no NUL bytes");
|
|
445965
|
+
}
|
|
445966
|
+
let identity2;
|
|
445967
|
+
if (!namespace) {
|
|
445968
|
+
let objectId = storageObjectIds.get(storage);
|
|
445969
|
+
if (!objectId) {
|
|
445970
|
+
objectId = nextStorageObjectId;
|
|
445971
|
+
nextStorageObjectId += 1;
|
|
445972
|
+
storageObjectIds.set(storage, objectId);
|
|
445973
|
+
}
|
|
445974
|
+
identity2 = `object:${objectId}`;
|
|
445975
|
+
} else {
|
|
445976
|
+
identity2 = `namespace:${namespace}`;
|
|
445492
445977
|
}
|
|
445978
|
+
return `${identity2}\x00${credentialKey}`;
|
|
445493
445979
|
}
|
|
445494
|
-
function
|
|
445495
|
-
|
|
445980
|
+
function retainCredentialCoordinator(coordinationKey) {
|
|
445981
|
+
getCredentialCoordinator(coordinationKey).activeProviders += 1;
|
|
445496
445982
|
}
|
|
445497
|
-
function
|
|
445498
|
-
|
|
445499
|
-
|
|
445500
|
-
|
|
445501
|
-
|
|
445502
|
-
|
|
445503
|
-
'"': """,
|
|
445504
|
-
"'": "'"
|
|
445505
|
-
};
|
|
445506
|
-
return entities[character] ?? character;
|
|
445983
|
+
async function releaseCredentialCoordinator(coordinationKey) {
|
|
445984
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
445985
|
+
await enqueueStorageMutation(coordinationKey, async () => {
|
|
445986
|
+
coordinator.activeProviders = Math.max(0, coordinator.activeProviders - 1);
|
|
445987
|
+
if (coordinator.activeProviders === 0)
|
|
445988
|
+
coordinator.state = undefined;
|
|
445507
445989
|
});
|
|
445990
|
+
await coordinator.queue;
|
|
445991
|
+
if (coordinator.activeProviders === 0 && credentialCoordinators.get(coordinationKey) === coordinator) {
|
|
445992
|
+
credentialCoordinators.delete(coordinationKey);
|
|
445993
|
+
}
|
|
445994
|
+
}
|
|
445995
|
+
function assertCurrentGeneration(coordinationKey, expectedGeneration) {
|
|
445996
|
+
if (getCredentialCoordinator(coordinationKey).generation !== expectedGeneration) {
|
|
445997
|
+
throw new Error("MCP OAuth session is stale because credentials were cleared");
|
|
445998
|
+
}
|
|
445999
|
+
}
|
|
446000
|
+
async function clearStoredCredentials(storage, credentialKey, coordinationKey, expected) {
|
|
446001
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446002
|
+
const outcome = await enqueueStorageMutation(coordinationKey, async () => {
|
|
446003
|
+
if (expected) {
|
|
446004
|
+
assertCurrentGeneration(coordinationKey, expected.generation);
|
|
446005
|
+
if (coordinator.clientVersion !== expected.clientVersion || coordinator.tokenVersion !== expected.tokenVersion || coordinator.discoveryVersion !== expected.discoveryVersion) {
|
|
446006
|
+
return {
|
|
446007
|
+
result: false,
|
|
446008
|
+
generation: coordinator.generation,
|
|
446009
|
+
cleared: false
|
|
446010
|
+
};
|
|
446011
|
+
}
|
|
446012
|
+
}
|
|
446013
|
+
const result3 = await storage.delete(credentialKey);
|
|
446014
|
+
coordinator.generation += 1;
|
|
446015
|
+
coordinator.clientVersion += 1;
|
|
446016
|
+
coordinator.tokenVersion += 1;
|
|
446017
|
+
coordinator.discoveryVersion += 1;
|
|
446018
|
+
coordinator.state = undefined;
|
|
446019
|
+
return { result: result3, generation: coordinator.generation, cleared: true };
|
|
446020
|
+
});
|
|
446021
|
+
await coordinator.queue;
|
|
446022
|
+
if (coordinator.activeProviders === 0 && credentialCoordinators.get(coordinationKey) === coordinator) {
|
|
446023
|
+
credentialCoordinators.delete(coordinationKey);
|
|
446024
|
+
}
|
|
446025
|
+
return outcome;
|
|
445508
446026
|
}
|
|
445509
446027
|
async function openSystemBrowser(url2) {
|
|
445510
446028
|
try {
|
|
@@ -445513,25 +446031,73 @@ async function openSystemBrowser(url2) {
|
|
|
445513
446031
|
subprocess.on("error", () => {});
|
|
445514
446032
|
} catch {}
|
|
445515
446033
|
}
|
|
445516
|
-
function
|
|
446034
|
+
function mcpOAuthCredentialKey(agentId, serverName, serverUrl) {
|
|
445517
446035
|
const digest = createHash11("sha256").update(`${agentId}\x00${serverName}\x00${serverUrl}`).digest("hex").slice(0, 32);
|
|
445518
446036
|
return `mcp-oauth-${digest}`;
|
|
445519
446037
|
}
|
|
445520
|
-
async function loadState(
|
|
445521
|
-
|
|
446038
|
+
async function loadState(storage, credentialKey, coordinationKey) {
|
|
446039
|
+
return enqueueStorageMutation(coordinationKey, async () => {
|
|
446040
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446041
|
+
const state = await readStoredState(storage, credentialKey);
|
|
446042
|
+
coordinator.state = state;
|
|
446043
|
+
return { state, generation: coordinator.generation };
|
|
446044
|
+
});
|
|
446045
|
+
}
|
|
446046
|
+
async function readStoredState(storage, credentialKey) {
|
|
446047
|
+
const value = await storage.get(credentialKey);
|
|
445522
446048
|
if (!value)
|
|
445523
446049
|
return;
|
|
445524
446050
|
try {
|
|
445525
|
-
|
|
446051
|
+
const parsed = JSON.parse(value);
|
|
446052
|
+
if (!isPersistedMcpOAuthState(parsed)) {
|
|
446053
|
+
await storage.delete(credentialKey);
|
|
446054
|
+
return;
|
|
446055
|
+
}
|
|
446056
|
+
return parsed;
|
|
445526
446057
|
} catch {
|
|
445527
|
-
await
|
|
446058
|
+
await storage.delete(credentialKey);
|
|
445528
446059
|
return;
|
|
445529
446060
|
}
|
|
445530
446061
|
}
|
|
445531
|
-
|
|
446062
|
+
function isPersistedMcpOAuthState(value) {
|
|
446063
|
+
if (!isRecord17(value) || !SafeUrlSchema.safeParse(value.redirectUrl).success || "codeVerifier" in value) {
|
|
446064
|
+
return false;
|
|
446065
|
+
}
|
|
446066
|
+
if (value.clientInformation !== undefined) {
|
|
446067
|
+
if (!isRecord17(value.clientInformation))
|
|
446068
|
+
return false;
|
|
446069
|
+
const schema5 = "redirect_uris" in value.clientInformation ? OAuthClientInformationFullSchema : OAuthClientInformationSchema;
|
|
446070
|
+
if (!schema5.safeParse(value.clientInformation).success)
|
|
446071
|
+
return false;
|
|
446072
|
+
}
|
|
446073
|
+
if (value.tokens !== undefined && !OAuthTokensSchema.safeParse(value.tokens).success) {
|
|
446074
|
+
return false;
|
|
446075
|
+
}
|
|
446076
|
+
if (value.tokenExpiresAt !== undefined && (typeof value.tokenExpiresAt !== "number" || !Number.isFinite(value.tokenExpiresAt))) {
|
|
446077
|
+
return false;
|
|
446078
|
+
}
|
|
446079
|
+
if (value.discoveryState !== undefined) {
|
|
446080
|
+
if (!isRecord17(value.discoveryState) || !SafeUrlSchema.safeParse(value.discoveryState.authorizationServerUrl).success || value.discoveryState.resourceMetadataUrl !== undefined && !SafeUrlSchema.safeParse(value.discoveryState.resourceMetadataUrl).success || value.discoveryState.authorizationServerMetadata !== undefined && !OAuthMetadataSchema.safeParse(value.discoveryState.authorizationServerMetadata).success && !OpenIdProviderDiscoveryMetadataSchema.safeParse(value.discoveryState.authorizationServerMetadata).success || value.discoveryState.resourceMetadata !== undefined && !OAuthProtectedResourceMetadataSchema.safeParse(value.discoveryState.resourceMetadata).success) {
|
|
446081
|
+
return false;
|
|
446082
|
+
}
|
|
446083
|
+
}
|
|
446084
|
+
return true;
|
|
446085
|
+
}
|
|
446086
|
+
function isRecord17(value) {
|
|
446087
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
446088
|
+
}
|
|
446089
|
+
var credentialCoordinators, storageObjectIds, nextStorageObjectId = 1, bunSecretStorage;
|
|
445532
446090
|
var init_mcp_oauth = __esm(() => {
|
|
446091
|
+
init_auth2();
|
|
446092
|
+
init_mcp_oauth_callback();
|
|
445533
446093
|
init_secrets();
|
|
445534
|
-
|
|
446094
|
+
credentialCoordinators = new Map;
|
|
446095
|
+
storageObjectIds = new WeakMap;
|
|
446096
|
+
bunSecretStorage = {
|
|
446097
|
+
get: (credentialKey) => getSecretValue(credentialKey, "MCP OAuth credentials"),
|
|
446098
|
+
set: setSecretValue,
|
|
446099
|
+
delete: deleteSecretValue
|
|
446100
|
+
};
|
|
445535
446101
|
});
|
|
445536
446102
|
|
|
445537
446103
|
// src/mcp-runtime.ts
|
|
@@ -447936,7 +448502,7 @@ import {
|
|
|
447936
448502
|
} from "node:fs";
|
|
447937
448503
|
import { tmpdir as tmpdir12 } from "node:os";
|
|
447938
448504
|
import path45 from "node:path";
|
|
447939
|
-
function
|
|
448505
|
+
function isRecord18(value) {
|
|
447940
448506
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
447941
448507
|
}
|
|
447942
448508
|
function isPathInsideOrEqual2(childPath, parentPath) {
|
|
@@ -447952,7 +448518,7 @@ function readPackageJson(packageJsonPath) {
|
|
|
447952
448518
|
} catch (error5) {
|
|
447953
448519
|
throw new Error(`Could not read package.json: ${error5 instanceof Error ? error5.message : String(error5)}`);
|
|
447954
448520
|
}
|
|
447955
|
-
if (!
|
|
448521
|
+
if (!isRecord18(parsed)) {
|
|
447956
448522
|
throw new Error("package.json must be an object");
|
|
447957
448523
|
}
|
|
447958
448524
|
return parsed;
|
|
@@ -447962,7 +448528,7 @@ function formatRepository(repository) {
|
|
|
447962
448528
|
const trimmed2 = repository.trim();
|
|
447963
448529
|
return trimmed2 || undefined;
|
|
447964
448530
|
}
|
|
447965
|
-
if (!
|
|
448531
|
+
if (!isRecord18(repository))
|
|
447966
448532
|
return;
|
|
447967
448533
|
const url2 = repository.url;
|
|
447968
448534
|
if (typeof url2 !== "string")
|
|
@@ -448492,7 +449058,7 @@ function hasRuntimeDependencies(packageJson) {
|
|
|
448492
449058
|
if (!packageJson)
|
|
448493
449059
|
return false;
|
|
448494
449060
|
const dependencies4 = packageJson.dependencies;
|
|
448495
|
-
return
|
|
449061
|
+
return isRecord18(dependencies4) && Object.keys(dependencies4).length > 0;
|
|
448496
449062
|
}
|
|
448497
449063
|
function readPackageJsonIfExists(packageDirectory) {
|
|
448498
449064
|
const packageJsonPath = path45.join(packageDirectory, "package.json");
|
|
@@ -512630,4 +513196,4 @@ function registerBunOAuthFlows() {
|
|
|
512630
513196
|
registerBunOAuthFlows();
|
|
512631
513197
|
await init_src5().then(() => exports_src2);
|
|
512632
513198
|
|
|
512633
|
-
//# debugId=
|
|
513199
|
+
//# debugId=3195287CCD45D3B564756E2164756E21
|