@letta-ai/letta-code 0.33.6 → 0.33.8
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/agent-presets.js.map +1 -1
- package/dist/channels-public.js.map +1 -1
- package/dist/mcp-client.js +53 -11
- package/dist/mcp-client.js.map +3 -3
- package/dist/mcp-oauth.js +18545 -0
- package/dist/mcp-oauth.js.map +155 -0
- package/dist/types/agent/client-skills.d.ts.map +1 -1
- package/dist/types/agent/skills.d.ts +5 -0
- package/dist/types/agent/skills.d.ts.map +1 -1
- 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/backend/dev/pi-ollama-provider.d.ts +7 -16
- package/dist/types/backend/dev/pi-ollama-provider.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/experiments/manager.d.ts.map +1 -1
- package/dist/types/experiments/types.d.ts +1 -1
- package/dist/types/experiments/types.d.ts.map +1 -1
- package/dist/types/mcp-client.d.ts +6 -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 +55 -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/tools/toolset-catalog.d.ts +1 -1
- package/dist/types/types/protocol_v2.d.ts +1 -1
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/utils/secrets.d.ts.map +1 -1
- package/letta.js +868 -237
- package/package.json +11 -1
- package/skills/curating-memory-palace/SKILL.md +105 -0
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.8",
|
|
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
|
],
|
|
@@ -90887,7 +90892,7 @@ var init_context = __esm(() => {
|
|
|
90887
90892
|
});
|
|
90888
90893
|
|
|
90889
90894
|
// src/constants.ts
|
|
90890
|
-
var DEFAULT_SUMMARIZATION_MODEL = "letta/auto", DEFAULT_TITLE_SUMMARIZATION_MODEL = "openai/gpt-
|
|
90895
|
+
var DEFAULT_SUMMARIZATION_MODEL = "letta/auto", DEFAULT_TITLE_SUMMARIZATION_MODEL = "openai/gpt-6-luna", DEFAULT_AGENT_NAME = "Letta Code", INTERRUPTED_BY_USER = "Interrupted by user", TURN_DID_NOT_COMPLETE = "Turn did not complete", SYSTEM_REMINDER_TAG = "system-reminder", SYSTEM_REMINDER_OPEN, SYSTEM_REMINDER_CLOSE, SYSTEM_ALERT_TAG = "system-alert", SYSTEM_ALERT_OPEN, SYSTEM_ALERT_CLOSE, COMPACTION_SUMMARY_HEADER = "(Earlier messages in this conversation have been compacted to free up context, summarized below)", TOKEN_DISPLAY_THRESHOLD = 100, ELAPSED_DISPLAY_THRESHOLD_MS;
|
|
90891
90896
|
var init_constants2 = __esm(() => {
|
|
90892
90897
|
SYSTEM_REMINDER_OPEN = `<${SYSTEM_REMINDER_TAG}>`;
|
|
90893
90898
|
SYSTEM_REMINDER_CLOSE = `</${SYSTEM_REMINDER_TAG}>`;
|
|
@@ -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)
|
|
@@ -107133,27 +107158,63 @@ function parseOllamaTags(data) {
|
|
|
107133
107158
|
};
|
|
107134
107159
|
}).filter((entry) => entry !== undefined);
|
|
107135
107160
|
}
|
|
107136
|
-
function
|
|
107137
|
-
const
|
|
107161
|
+
function ollamaIdentityKeys(id2) {
|
|
107162
|
+
const normalized = normalizeOllamaModelId(id2);
|
|
107163
|
+
return normalized === id2 ? [id2] : [id2, normalized];
|
|
107164
|
+
}
|
|
107165
|
+
function parseOllamaPsEntries(data) {
|
|
107138
107166
|
if (!data || typeof data !== "object")
|
|
107139
|
-
return
|
|
107167
|
+
return [];
|
|
107140
107168
|
const models3 = data.models;
|
|
107141
107169
|
if (!Array.isArray(models3))
|
|
107142
|
-
return
|
|
107170
|
+
return [];
|
|
107171
|
+
const entries = [];
|
|
107143
107172
|
for (const entry of models3) {
|
|
107144
107173
|
if (!entry || typeof entry !== "object")
|
|
107145
107174
|
continue;
|
|
107146
107175
|
const record4 = entry;
|
|
107147
|
-
const
|
|
107148
|
-
|
|
107149
|
-
if (typeof id2 !== "string" || id2.length === 0)
|
|
107176
|
+
const rawIds = [record4.name, record4.model].filter((id2) => typeof id2 === "string" && id2.length > 0);
|
|
107177
|
+
if (rawIds.length === 0)
|
|
107150
107178
|
continue;
|
|
107151
|
-
|
|
107179
|
+
const contextLength = record4.context_length;
|
|
107180
|
+
entries.push({
|
|
107181
|
+
ids: [...new Set(rawIds.flatMap(ollamaIdentityKeys))],
|
|
107182
|
+
displayId: rawIds[0],
|
|
107183
|
+
...typeof contextLength === "number" && contextLength > 0 ? { contextLength } : {}
|
|
107184
|
+
});
|
|
107185
|
+
}
|
|
107186
|
+
return entries;
|
|
107187
|
+
}
|
|
107188
|
+
function parseOllamaRunningContexts(data) {
|
|
107189
|
+
const contexts = new Map;
|
|
107190
|
+
for (const entry of parseOllamaPsEntries(data)) {
|
|
107191
|
+
if (entry.contextLength === undefined)
|
|
107152
107192
|
continue;
|
|
107153
|
-
|
|
107193
|
+
for (const id2 of entry.ids)
|
|
107194
|
+
contexts.set(id2, entry.contextLength);
|
|
107154
107195
|
}
|
|
107155
107196
|
return contexts;
|
|
107156
107197
|
}
|
|
107198
|
+
function readOllamaServedContextOverride(env4) {
|
|
107199
|
+
const raw = env4[OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV];
|
|
107200
|
+
if (raw === undefined || raw.trim() === "")
|
|
107201
|
+
return;
|
|
107202
|
+
const trimmed = raw.trim();
|
|
107203
|
+
if (!/^[1-9]\d*$/.test(trimmed) || !Number.isSafeInteger(Number(trimmed))) {
|
|
107204
|
+
throw new Error(`${OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV}=${JSON.stringify(trimmed)} is not a positive integer token count. ` + `Set it to the context window your Ollama daemon actually serves.`);
|
|
107205
|
+
}
|
|
107206
|
+
return Number(trimmed);
|
|
107207
|
+
}
|
|
107208
|
+
function missingServedContextError(modelId, data) {
|
|
107209
|
+
const entries = parseOllamaPsEntries(data);
|
|
107210
|
+
const match2 = entries.find((entry) => entry.ids.includes(modelId));
|
|
107211
|
+
if (match2) {
|
|
107212
|
+
return `Ollama lists "${modelId}" in /api/ps but did not report context_length. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `context_length requires Ollama 0.10.0 or newer. ` + `Upgrade Ollama, or set ${OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV} to the window your daemon actually serves.`;
|
|
107213
|
+
}
|
|
107214
|
+
const running = entries.map((entry) => entry.displayId).filter((id2) => id2 !== undefined);
|
|
107215
|
+
const runningNote = running.length > 0 ? ` Running models: ${running.join(", ")}.` : ` /api/ps lists no running models.`;
|
|
107216
|
+
return `Ollama loaded "${modelId}", but /api/ps does not list it. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `If OLLAMA_KEEP_ALIVE=0, the model unloaded before this check.` + runningNote + ` Set ${OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV} to the window your daemon actually serves.`;
|
|
107217
|
+
}
|
|
107157
107218
|
async function fetchOllamaNative(fetchImpl, url, options) {
|
|
107158
107219
|
const controller = new AbortController;
|
|
107159
107220
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
@@ -107185,42 +107246,49 @@ async function fetchOllamaNative(fetchImpl, url, options) {
|
|
|
107185
107246
|
}
|
|
107186
107247
|
}
|
|
107187
107248
|
async function resolveOllamaServedContext(options) {
|
|
107249
|
+
const override = readOllamaServedContextOverride(options.env ?? process.env);
|
|
107250
|
+
if (override !== undefined)
|
|
107251
|
+
return override;
|
|
107188
107252
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
107189
107253
|
const modelId = normalizeOllamaModelId(options.modelId);
|
|
107190
107254
|
const nativeBaseURL = localEndpointNativeBaseURL(options.baseURL);
|
|
107191
107255
|
const psURL = `${nativeBaseURL}/api/ps`;
|
|
107192
|
-
const
|
|
107193
|
-
const data = await fetchOllamaNative(fetchImpl, psURL, {
|
|
107194
|
-
...options.apiKey ? { apiKey: options.apiKey } : {},
|
|
107195
|
-
...options.signal ? { signal: options.signal } : {},
|
|
107196
|
-
timeoutMs: OLLAMA_STATUS_TIMEOUT_MS,
|
|
107197
|
-
consume: (response) => response.json()
|
|
107198
|
-
});
|
|
107199
|
-
return parseOllamaRunningContexts(data).get(modelId);
|
|
107200
|
-
};
|
|
107256
|
+
const overrideHint = `or set ${OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV} to the window your daemon actually serves`;
|
|
107201
107257
|
try {
|
|
107202
107258
|
await fetchOllamaNative(fetchImpl, `${nativeBaseURL}/api/generate`, {
|
|
107203
107259
|
...options.apiKey ? { apiKey: options.apiKey } : {},
|
|
107204
107260
|
...options.signal ? { signal: options.signal } : {},
|
|
107205
|
-
body: {
|
|
107261
|
+
body: {
|
|
107262
|
+
model: modelId,
|
|
107263
|
+
prompt: "",
|
|
107264
|
+
stream: false,
|
|
107265
|
+
keep_alive: OLLAMA_PREFLIGHT_KEEP_ALIVE
|
|
107266
|
+
},
|
|
107206
107267
|
timeoutMs: OLLAMA_MODEL_LOAD_TIMEOUT_MS,
|
|
107207
107268
|
consume: (response) => response.text()
|
|
107208
107269
|
});
|
|
107209
107270
|
} catch (error4) {
|
|
107210
107271
|
if (options.signal?.aborted)
|
|
107211
107272
|
throw options.signal.reason ?? error4;
|
|
107212
|
-
throw new Error(`Unable to load Ollama model "${modelId}" to determine its served context window. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `Check the Ollama endpoint, model installation, and available memory. (${error4 instanceof Error ? error4.message : String(error4)})`);
|
|
107273
|
+
throw new Error(`Unable to load Ollama model "${modelId}" to determine its served context window. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `Check the Ollama endpoint, model installation, and available memory, ${overrideHint}. (${error4 instanceof Error ? error4.message : String(error4)})`);
|
|
107213
107274
|
}
|
|
107275
|
+
let running;
|
|
107214
107276
|
try {
|
|
107215
|
-
|
|
107216
|
-
|
|
107217
|
-
|
|
107277
|
+
running = await fetchOllamaNative(fetchImpl, psURL, {
|
|
107278
|
+
...options.apiKey ? { apiKey: options.apiKey } : {},
|
|
107279
|
+
...options.signal ? { signal: options.signal } : {},
|
|
107280
|
+
timeoutMs: OLLAMA_STATUS_TIMEOUT_MS,
|
|
107281
|
+
consume: (response) => response.json()
|
|
107282
|
+
});
|
|
107218
107283
|
} catch (error4) {
|
|
107219
107284
|
if (options.signal?.aborted)
|
|
107220
107285
|
throw options.signal.reason ?? error4;
|
|
107221
|
-
throw new Error(`Ollama loaded model "${modelId}", but /api/ps could not verify its served context window. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `Check that the endpoint supports /api/ps. (${error4 instanceof Error ? error4.message : String(error4)})`);
|
|
107286
|
+
throw new Error(`Ollama loaded model "${modelId}", but /api/ps could not verify its served context window. ` + `Refusing to send the prompt because Ollama may silently truncate it. ` + `Check that the endpoint supports /api/ps, ${overrideHint}. (${error4 instanceof Error ? error4.message : String(error4)})`);
|
|
107222
107287
|
}
|
|
107223
|
-
|
|
107288
|
+
const loadedContext = parseOllamaRunningContexts(running).get(modelId);
|
|
107289
|
+
if (loadedContext !== undefined)
|
|
107290
|
+
return loadedContext;
|
|
107291
|
+
throw new Error(missingServedContextError(modelId, running));
|
|
107224
107292
|
}
|
|
107225
107293
|
function parseOllamaShow(modelId, data, runtimeContextLength) {
|
|
107226
107294
|
if (!data || typeof data !== "object")
|
|
@@ -107250,7 +107318,7 @@ function createOllamaPiProvider(options) {
|
|
|
107250
107318
|
discover: ollamaDiscover
|
|
107251
107319
|
});
|
|
107252
107320
|
}
|
|
107253
|
-
var OLLAMA_PI_PROVIDER_ID = "ollama", OLLAMA_CLOUD_PI_PROVIDER_ID = "ollama-cloud", OLLAMA_STATUS_TIMEOUT_MS = 2000, OLLAMA_MODEL_LOAD_TIMEOUT_MS = 120000, ollamaDiscover = async (context3) => {
|
|
107321
|
+
var OLLAMA_PI_PROVIDER_ID = "ollama", OLLAMA_CLOUD_PI_PROVIDER_ID = "ollama-cloud", OLLAMA_STATUS_TIMEOUT_MS = 2000, OLLAMA_MODEL_LOAD_TIMEOUT_MS = 120000, OLLAMA_PREFLIGHT_KEEP_ALIVE = "5m", OLLAMA_SERVED_CONTEXT_OVERRIDE_ENV = "LETTA_OLLAMA_CONTEXT_LENGTH", ollamaDiscover = async (context3) => {
|
|
107254
107322
|
const tags = await context3.fetchJson(`${context3.nativeBaseURL}/api/tags`);
|
|
107255
107323
|
const entries = parseOllamaTags(tags);
|
|
107256
107324
|
const runtimeContexts = await context3.fetchJson(`${context3.nativeBaseURL}/api/ps`).then(parseOllamaRunningContexts).catch(() => new Map);
|
|
@@ -110931,6 +110999,9 @@ class APIBackend {
|
|
|
110931
110999
|
const client = await this.getClient();
|
|
110932
111000
|
return client.agents.messages.cancel(agentId, { run_ids: [runId] });
|
|
110933
111001
|
}
|
|
111002
|
+
async cancelConversationRun(_conversationId, _runId) {
|
|
111003
|
+
throw new Error("API backend does not support exact cancellation for conversations with agent_id:null");
|
|
111004
|
+
}
|
|
110934
111005
|
async retrieveRun(runId, options) {
|
|
110935
111006
|
const client = await this.getClient();
|
|
110936
111007
|
return client.runs.retrieve(runId, options);
|
|
@@ -188489,6 +188560,12 @@ var init_manager = __esm(() => {
|
|
|
188489
188560
|
label: "diffs",
|
|
188490
188561
|
description: "Open browser-based worktree diff previews powered by Diffs from Pierre."
|
|
188491
188562
|
},
|
|
188563
|
+
{
|
|
188564
|
+
id: "memory_palace",
|
|
188565
|
+
label: "memory palace",
|
|
188566
|
+
description: "Opt-in memory palace experiment flag for disposable local listener runs. Disabled by default; no behavior changes unless consumers gate on it.",
|
|
188567
|
+
envVar: "LETTA_MEMORY_PALACE"
|
|
188568
|
+
},
|
|
188492
188569
|
{
|
|
188493
188570
|
id: "reflection_arena",
|
|
188494
188571
|
label: "reflection arena",
|
|
@@ -198022,6 +198099,7 @@ var init_protocol_inbound = __esm(() => {
|
|
|
198022
198099
|
EXPERIMENT_IDS = new Set([
|
|
198023
198100
|
"conversation_titles",
|
|
198024
198101
|
"desktop_conversation_bootstrap",
|
|
198102
|
+
"memory_palace",
|
|
198025
198103
|
"tui_cron"
|
|
198026
198104
|
]);
|
|
198027
198105
|
});
|
|
@@ -213117,7 +213195,7 @@ class CodexAppServerClient {
|
|
|
213117
213195
|
await this.request("initialize", {
|
|
213118
213196
|
clientInfo: {
|
|
213119
213197
|
name: "letta-code",
|
|
213120
|
-
version: "0.33.
|
|
213198
|
+
version: "0.33.8"
|
|
213121
213199
|
},
|
|
213122
213200
|
capabilities: null
|
|
213123
213201
|
});
|
|
@@ -218276,6 +218354,7 @@ __export(exports_skills2, {
|
|
|
218276
218354
|
getBundledSkills: () => getBundledSkills,
|
|
218277
218355
|
getAgentSkillsDir: () => getAgentSkillsDir,
|
|
218278
218356
|
formatSkillsAsSystemReminder: () => formatSkillsAsSystemReminder,
|
|
218357
|
+
enabledSkillExperiments: () => enabledSkillExperiments,
|
|
218279
218358
|
discoverSkills: () => discoverSkills,
|
|
218280
218359
|
compareSkills: () => compareSkills,
|
|
218281
218360
|
SKILLS_DIR: () => SKILLS_DIR,
|
|
@@ -218331,7 +218410,14 @@ function isModelInvocableSkill(skill) {
|
|
|
218331
218410
|
function isUserInvocableSkill(skill) {
|
|
218332
218411
|
return skill.userInvocable !== false;
|
|
218333
218412
|
}
|
|
218413
|
+
function enabledSkillExperiments() {
|
|
218414
|
+
return [...new Set(EXPERIMENT_BUNDLED_SKILLS.values())].filter((id2) => experimentManager.isEnabled(id2)).sort().join(",");
|
|
218415
|
+
}
|
|
218334
218416
|
function isSkillAvailableForAgent(skill, agentId) {
|
|
218417
|
+
const experiment = skill.source === "bundled" ? EXPERIMENT_BUNDLED_SKILLS.get(skill.id) : undefined;
|
|
218418
|
+
if (experiment && !experimentManager.isEnabled(experiment)) {
|
|
218419
|
+
return false;
|
|
218420
|
+
}
|
|
218335
218421
|
if (skill.source === "bundled" && agentId && isLocalAgentId(agentId) && LOCAL_AGENT_EXCLUDED_BUNDLED_SKILLS.has(skill.id)) {
|
|
218336
218422
|
return false;
|
|
218337
218423
|
}
|
|
@@ -218395,7 +218481,7 @@ async function discoverSkills(projectSkillsPath = join61(process.cwd(), SKILLS_D
|
|
|
218395
218481
|
}
|
|
218396
218482
|
}
|
|
218397
218483
|
return {
|
|
218398
|
-
skills: Array.from(skillsById.values()).sort(compareSkills),
|
|
218484
|
+
skills: Array.from(skillsById.values()).filter((skill) => isSkillAvailableForAgent(skill, agentId)).sort(compareSkills),
|
|
218399
218485
|
errors: allErrors
|
|
218400
218486
|
};
|
|
218401
218487
|
}
|
|
@@ -218503,14 +218589,18 @@ ${lines.join(`
|
|
|
218503
218589
|
`)}
|
|
218504
218590
|
</system-reminder>`;
|
|
218505
218591
|
}
|
|
218506
|
-
var LOCAL_AGENT_EXCLUDED_BUNDLED_SKILLS, PROJECT_SKILLS_DIR, SKILLS_DIR = ".skills", GLOBAL_SKILLS_DIR;
|
|
218592
|
+
var LOCAL_AGENT_EXCLUDED_BUNDLED_SKILLS, EXPERIMENT_BUNDLED_SKILLS, PROJECT_SKILLS_DIR, SKILLS_DIR = ".skills", GLOBAL_SKILLS_DIR;
|
|
218507
218593
|
var init_skills5 = __esm(() => {
|
|
218594
|
+
init_manager();
|
|
218508
218595
|
init_skill_sources();
|
|
218509
218596
|
LOCAL_AGENT_EXCLUDED_BUNDLED_SKILLS = new Set([
|
|
218510
218597
|
"image-generation",
|
|
218511
218598
|
"managing-shared-memory",
|
|
218512
218599
|
"working-across-computers"
|
|
218513
218600
|
]);
|
|
218601
|
+
EXPERIMENT_BUNDLED_SKILLS = new Map([
|
|
218602
|
+
["curating-memory-palace", "memory_palace"]
|
|
218603
|
+
]);
|
|
218514
218604
|
PROJECT_SKILLS_DIR = join61(".agents", "skills");
|
|
218515
218605
|
GLOBAL_SKILLS_DIR = join61(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
|
|
218516
218606
|
});
|
|
@@ -228020,7 +228110,8 @@ function computeCacheKey(components) {
|
|
|
228020
228110
|
components.primaryProjectSkillsDirectory,
|
|
228021
228111
|
[...components.memorySkillsDirs].sort().join(","),
|
|
228022
228112
|
[...components.sharedMemorySkillsDirs].sort().join(","),
|
|
228023
|
-
components.sharedMemoryErrors.map((error4) => `${error4.path}:${error4.message}`).sort().join(",")
|
|
228113
|
+
components.sharedMemoryErrors.map((error4) => `${error4.path}:${error4.message}`).sort().join(","),
|
|
228114
|
+
components.skillExperiments
|
|
228024
228115
|
].join("|");
|
|
228025
228116
|
}
|
|
228026
228117
|
function getSkillRoots(components) {
|
|
@@ -228313,7 +228404,8 @@ async function buildClientSkillsPayload(options = {}) {
|
|
|
228313
228404
|
primaryProjectSkillsDirectory,
|
|
228314
228405
|
memorySkillsDirs,
|
|
228315
228406
|
sharedMemorySkillsDirs: sharedMemoryContext.skillsDirs,
|
|
228316
|
-
sharedMemoryErrors: sharedMemoryContext.errors
|
|
228407
|
+
sharedMemoryErrors: sharedMemoryContext.errors,
|
|
228408
|
+
skillExperiments: enabledSkillExperiments()
|
|
228317
228409
|
};
|
|
228318
228410
|
const cacheKey = computeCacheKey(cacheComponents);
|
|
228319
228411
|
if (useCache) {
|
|
@@ -412450,12 +412542,12 @@ function rowToolCalls(row, index, diagnostics2) {
|
|
|
412450
412542
|
}
|
|
412451
412543
|
return calls;
|
|
412452
412544
|
}
|
|
412453
|
-
function
|
|
412545
|
+
function contentText2(content) {
|
|
412454
412546
|
if (typeof content === "string") {
|
|
412455
412547
|
if (content.startsWith(CONTENT_JSON_PREFIX)) {
|
|
412456
412548
|
const encoded = content.slice(CONTENT_JSON_PREFIX.length);
|
|
412457
412549
|
try {
|
|
412458
|
-
return
|
|
412550
|
+
return contentText2(JSON.parse(encoded));
|
|
412459
412551
|
} catch {
|
|
412460
412552
|
return encoded;
|
|
412461
412553
|
}
|
|
@@ -412541,7 +412633,7 @@ var init_hermes = __esm(() => {
|
|
|
412541
412633
|
});
|
|
412542
412634
|
};
|
|
412543
412635
|
if (row.role === "user") {
|
|
412544
|
-
const content =
|
|
412636
|
+
const content = contentText2(row.content);
|
|
412545
412637
|
if (content) {
|
|
412546
412638
|
emit({
|
|
412547
412639
|
type: "message",
|
|
@@ -412561,7 +412653,7 @@ var init_hermes = __esm(() => {
|
|
|
412561
412653
|
...timestamp ? { timestamp } : {}
|
|
412562
412654
|
});
|
|
412563
412655
|
}
|
|
412564
|
-
const content =
|
|
412656
|
+
const content = contentText2(row.content);
|
|
412565
412657
|
if (content) {
|
|
412566
412658
|
emit({
|
|
412567
412659
|
type: "message",
|
|
@@ -412584,7 +412676,7 @@ var init_hermes = __esm(() => {
|
|
|
412584
412676
|
if (row.role === "tool") {
|
|
412585
412677
|
emit({
|
|
412586
412678
|
type: "tool_result",
|
|
412587
|
-
content:
|
|
412679
|
+
content: contentText2(row.content),
|
|
412588
412680
|
...typeof row.tool_call_id === "string" && row.tool_call_id ? { callId: row.tool_call_id } : {},
|
|
412589
412681
|
...timestamp ? { timestamp } : {}
|
|
412590
412682
|
});
|
|
@@ -418452,6 +418544,15 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418452
418544
|
throw new Error(`Backend did not cancel run ${runId}`);
|
|
418453
418545
|
}
|
|
418454
418546
|
},
|
|
418547
|
+
cancelConversationRun: async (conversationId, runId) => {
|
|
418548
|
+
const result3 = await getBackend().cancelConversationRun(conversationId, runId);
|
|
418549
|
+
if (runId && result3[runId] !== "cancelled") {
|
|
418550
|
+
throw new Error(`Backend did not cancel run ${runId}`);
|
|
418551
|
+
}
|
|
418552
|
+
if (!runId && !Object.values(result3).includes("cancelled")) {
|
|
418553
|
+
throw new Error(`Backend did not cancel the active run for ${conversationId}`);
|
|
418554
|
+
}
|
|
418555
|
+
},
|
|
418455
418556
|
...deps
|
|
418456
418557
|
};
|
|
418457
418558
|
if (listener !== resolvedDeps.getActiveRuntime() || listener.intentionallyClosed) {
|
|
@@ -418471,7 +418572,7 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418471
418572
|
return false;
|
|
418472
418573
|
}
|
|
418473
418574
|
const cancellation = scopedRuntime.turnLifecycle.requestCancellation({
|
|
418474
|
-
waitForExternalSettlement: hasActiveTurn
|
|
418575
|
+
waitForExternalSettlement: hasActiveTurn
|
|
418475
418576
|
});
|
|
418476
418577
|
scopedRuntime.queueRuntime.pause();
|
|
418477
418578
|
const interruptedRunId = cancellation.runId;
|
|
@@ -418535,19 +418636,17 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
|
|
|
418535
418636
|
}
|
|
418536
418637
|
const cancelConversationId = scopedRuntime.conversationId;
|
|
418537
418638
|
const cancelAgentId = scopedRuntime.agentId;
|
|
418538
|
-
|
|
418539
|
-
|
|
418540
|
-
|
|
418541
|
-
|
|
418542
|
-
|
|
418543
|
-
|
|
418544
|
-
|
|
418545
|
-
|
|
418546
|
-
|
|
418547
|
-
|
|
418548
|
-
|
|
418549
|
-
});
|
|
418550
|
-
}
|
|
418639
|
+
const cancelRunId = interruptedRunId ?? params.command.run_id ?? null;
|
|
418640
|
+
const backendCancellation = cancelAgentId && cancelRunId ? resolvedDeps.cancelRun(cancelAgentId, cancelRunId).catch(() => resolvedDeps.cancelConversation(cancelAgentId, cancelConversationId)) : cancelAgentId ? resolvedDeps.cancelConversation(cancelAgentId, cancelConversationId) : resolvedDeps.cancelConversationRun(cancelConversationId, cancelRunId);
|
|
418641
|
+
backendCancellation.catch(() => {}).finally(() => {
|
|
418642
|
+
if (!cancellation.lease) {
|
|
418643
|
+
return;
|
|
418644
|
+
}
|
|
418645
|
+
const settlement = scopedRuntime.turnLifecycle.settleCancellation(cancellation.lease);
|
|
418646
|
+
if (settlement.released) {
|
|
418647
|
+
resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
|
|
418648
|
+
}
|
|
418649
|
+
});
|
|
418551
418650
|
resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
|
|
418552
418651
|
return true;
|
|
418553
418652
|
}
|
|
@@ -445147,23 +445246,24 @@ var init_streamableHttp = __esm(() => {
|
|
|
445147
445246
|
async function connectMcpServer(config2, options = {}) {
|
|
445148
445247
|
let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
445149
445248
|
try {
|
|
445249
|
+
options.signal?.throwIfAborted();
|
|
445150
445250
|
const transport = createTransport2(config2, options);
|
|
445151
445251
|
try {
|
|
445152
|
-
await client.connect(transport);
|
|
445252
|
+
await client.connect(transport, options.signal ? { signal: options.signal } : undefined);
|
|
445153
445253
|
} catch (error5) {
|
|
445154
445254
|
if (!(error5 instanceof UnauthorizedError) || !options.oauth?.waitForAuthorizationCode || !supportsOAuthCompletion(transport)) {
|
|
445155
445255
|
throw error5;
|
|
445156
445256
|
}
|
|
445157
|
-
const authorizationCode = await options.oauth.waitForAuthorizationCode();
|
|
445158
|
-
await transport.finishAuth(authorizationCode);
|
|
445257
|
+
const authorizationCode = await withAbort(options.oauth.waitForAuthorizationCode(), options.signal, () => options.oauth?.close());
|
|
445258
|
+
await withAbort(transport.finishAuth(authorizationCode), options.signal, () => transport.close());
|
|
445159
445259
|
await client.close().catch(() => transport.close().catch(() => {
|
|
445160
445260
|
return;
|
|
445161
445261
|
}));
|
|
445162
445262
|
client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
445163
|
-
await client.connect(createTransport2(config2, options));
|
|
445263
|
+
await client.connect(createTransport2(config2, options), options.signal ? { signal: options.signal } : undefined);
|
|
445164
445264
|
}
|
|
445165
|
-
await options.oauth?.close();
|
|
445166
|
-
const response = await client.listTools();
|
|
445265
|
+
await (options.oauth?.closeCallback?.() ?? options.oauth?.close() ?? Promise.resolve());
|
|
445266
|
+
const response = await client.listTools(undefined, options.signal ? { signal: options.signal } : undefined);
|
|
445167
445267
|
const tools = response.tools.map((tool) => ({
|
|
445168
445268
|
...tool,
|
|
445169
445269
|
inputSchema: normalizeInputSchema(tool.inputSchema),
|
|
@@ -445186,7 +445286,13 @@ async function connectMcpServer(config2, options = {}) {
|
|
|
445186
445286
|
if (closed)
|
|
445187
445287
|
return;
|
|
445188
445288
|
closed = true;
|
|
445189
|
-
await
|
|
445289
|
+
const results = await Promise.allSettled([
|
|
445290
|
+
client.close(),
|
|
445291
|
+
options.oauth?.close()
|
|
445292
|
+
]);
|
|
445293
|
+
const failure2 = results.find((result3) => result3.status === "rejected");
|
|
445294
|
+
if (failure2)
|
|
445295
|
+
throw failure2.reason;
|
|
445190
445296
|
}
|
|
445191
445297
|
};
|
|
445192
445298
|
} catch (error5) {
|
|
@@ -445197,12 +445303,34 @@ async function connectMcpServer(config2, options = {}) {
|
|
|
445197
445303
|
throw error5;
|
|
445198
445304
|
}
|
|
445199
445305
|
}
|
|
445306
|
+
async function withAbort(operation, signal, onAbort) {
|
|
445307
|
+
if (!signal)
|
|
445308
|
+
return operation;
|
|
445309
|
+
signal.throwIfAborted();
|
|
445310
|
+
let abort;
|
|
445311
|
+
const cancellation = new Promise((_resolve, reject2) => {
|
|
445312
|
+
abort = () => {
|
|
445313
|
+
Promise.resolve(onAbort?.()).catch(() => {
|
|
445314
|
+
return;
|
|
445315
|
+
});
|
|
445316
|
+
reject2(signal.reason ?? new DOMException("Operation aborted", "AbortError"));
|
|
445317
|
+
};
|
|
445318
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
445319
|
+
});
|
|
445320
|
+
try {
|
|
445321
|
+
return await Promise.race([operation, cancellation]);
|
|
445322
|
+
} finally {
|
|
445323
|
+
if (abort)
|
|
445324
|
+
signal.removeEventListener("abort", abort);
|
|
445325
|
+
}
|
|
445326
|
+
}
|
|
445200
445327
|
function createTransport2(config2, options) {
|
|
445201
445328
|
if (config2.transport === "http") {
|
|
445202
445329
|
const headers = resolveHeaderEnvironment(config2.headers);
|
|
445203
445330
|
return new StreamableHTTPClientTransport(new URL(config2.url), {
|
|
445204
445331
|
requestInit: headersRequestInit(headers),
|
|
445205
|
-
authProvider: options.oauth?.authProvider
|
|
445332
|
+
authProvider: options.oauth?.authProvider,
|
|
445333
|
+
fetch: abortAwareFetch(options.signal, undefined, options.fetch)
|
|
445206
445334
|
});
|
|
445207
445335
|
}
|
|
445208
445336
|
if (config2.transport === "sse") {
|
|
@@ -445211,7 +445339,7 @@ function createTransport2(config2, options) {
|
|
|
445211
445339
|
return new SSEClientTransport(new URL(config2.url), {
|
|
445212
445340
|
requestInit,
|
|
445213
445341
|
authProvider: options.oauth?.authProvider,
|
|
445214
|
-
fetch:
|
|
445342
|
+
fetch: abortAwareFetch(options.signal, headers, options.fetch)
|
|
445215
445343
|
});
|
|
445216
445344
|
}
|
|
445217
445345
|
return new StdioClientTransport({
|
|
@@ -445222,6 +445350,19 @@ function createTransport2(config2, options) {
|
|
|
445222
445350
|
stderr: options.stderr ?? "inherit"
|
|
445223
445351
|
});
|
|
445224
445352
|
}
|
|
445353
|
+
function abortAwareFetch(operationSignal2, headers, fetchFn) {
|
|
445354
|
+
if (!operationSignal2 && !headers)
|
|
445355
|
+
return fetchFn;
|
|
445356
|
+
const request = fetchFn ?? fetch;
|
|
445357
|
+
return (url2, init) => {
|
|
445358
|
+
const requestInit = headers ? mergeHeaders4(init, headers) : { ...init };
|
|
445359
|
+
const requestSignal3 = requestInit.signal ?? undefined;
|
|
445360
|
+
return request(url2, {
|
|
445361
|
+
...requestInit,
|
|
445362
|
+
signal: operationSignal2 && requestSignal3 ? AbortSignal.any([operationSignal2, requestSignal3]) : operationSignal2 ?? requestSignal3
|
|
445363
|
+
});
|
|
445364
|
+
};
|
|
445365
|
+
}
|
|
445225
445366
|
function supportsOAuthCompletion(transport) {
|
|
445226
445367
|
return "finishAuth" in transport && typeof transport.finishAuth === "function";
|
|
445227
445368
|
}
|
|
@@ -445268,60 +445409,281 @@ var init_mcp_client = __esm(() => {
|
|
|
445268
445409
|
init_streamableHttp();
|
|
445269
445410
|
DEFAULT_CLIENT_INFO = {
|
|
445270
445411
|
name: "letta-code",
|
|
445271
|
-
version: "0.33.
|
|
445412
|
+
version: "0.33.8"
|
|
445413
|
+
};
|
|
445414
|
+
});
|
|
445415
|
+
|
|
445416
|
+
// src/mcp-oauth-callback.ts
|
|
445417
|
+
import { createServer as createServer3 } from "node:http";
|
|
445418
|
+
async function startOAuthCallbackServer2(preferredPort) {
|
|
445419
|
+
try {
|
|
445420
|
+
return await startOAuthCallbackServerOnPort(preferredPort ?? 0);
|
|
445421
|
+
} catch (error5) {
|
|
445422
|
+
if (!preferredPort)
|
|
445423
|
+
throw error5;
|
|
445424
|
+
return startOAuthCallbackServerOnPort(0);
|
|
445425
|
+
}
|
|
445426
|
+
}
|
|
445427
|
+
function callbackPort(redirectUrl) {
|
|
445428
|
+
if (!redirectUrl)
|
|
445429
|
+
return;
|
|
445430
|
+
try {
|
|
445431
|
+
const parsed = new URL(redirectUrl);
|
|
445432
|
+
return parsed.hostname === "127.0.0.1" && parsed.port ? Number(parsed.port) : undefined;
|
|
445433
|
+
} catch {
|
|
445434
|
+
return;
|
|
445435
|
+
}
|
|
445436
|
+
}
|
|
445437
|
+
async function startOAuthCallbackServerOnPort(port) {
|
|
445438
|
+
const expectedState = {};
|
|
445439
|
+
let server2;
|
|
445440
|
+
let completed = false;
|
|
445441
|
+
let settle3;
|
|
445442
|
+
let reject2;
|
|
445443
|
+
const codePromise = new Promise((resolve39, rejectPromise) => {
|
|
445444
|
+
settle3 = (code2) => {
|
|
445445
|
+
completed = true;
|
|
445446
|
+
resolve39(code2);
|
|
445447
|
+
};
|
|
445448
|
+
reject2 = (error5) => {
|
|
445449
|
+
completed = true;
|
|
445450
|
+
rejectPromise(error5);
|
|
445451
|
+
};
|
|
445452
|
+
});
|
|
445453
|
+
codePromise.catch(() => {
|
|
445454
|
+
return;
|
|
445455
|
+
});
|
|
445456
|
+
server2 = createServer3((request, response) => {
|
|
445457
|
+
if (request.method !== "GET" || !request.url?.startsWith("/") || request.url.startsWith("//") || request.url.includes("\\")) {
|
|
445458
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445459
|
+
response.end("Invalid request target");
|
|
445460
|
+
return;
|
|
445461
|
+
}
|
|
445462
|
+
const address2 = server2.address();
|
|
445463
|
+
const expectedHost = address2 && typeof address2 !== "string" ? `127.0.0.1:${address2.port}` : undefined;
|
|
445464
|
+
if (!expectedHost || request.headers.host !== expectedHost) {
|
|
445465
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445466
|
+
response.end("Invalid callback host");
|
|
445467
|
+
return;
|
|
445468
|
+
}
|
|
445469
|
+
let url2;
|
|
445470
|
+
try {
|
|
445471
|
+
url2 = new URL(request.url, `http://${expectedHost}`);
|
|
445472
|
+
} catch {
|
|
445473
|
+
response.writeHead(400, { "Content-Type": "text/plain" });
|
|
445474
|
+
response.end("Invalid request target");
|
|
445475
|
+
return;
|
|
445476
|
+
}
|
|
445477
|
+
if (url2.origin !== `http://${expectedHost}` || url2.pathname !== "/callback") {
|
|
445478
|
+
response.writeHead(404).end("Not found");
|
|
445479
|
+
return;
|
|
445480
|
+
}
|
|
445481
|
+
const error5 = url2.searchParams.get("error");
|
|
445482
|
+
const code2 = url2.searchParams.get("code");
|
|
445483
|
+
const state = url2.searchParams.get("state");
|
|
445484
|
+
if (!state || state !== expectedState.value) {
|
|
445485
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445486
|
+
response.end(callbackPage("Authorization failed", "Invalid OAuth callback"));
|
|
445487
|
+
return;
|
|
445488
|
+
}
|
|
445489
|
+
if (error5) {
|
|
445490
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445491
|
+
reject2?.(new Error(`MCP OAuth authorization failed: ${error5}`));
|
|
445492
|
+
response.end(callbackPage("Authorization failed", error5), () => {
|
|
445493
|
+
closeServer(true);
|
|
445494
|
+
});
|
|
445495
|
+
return;
|
|
445496
|
+
}
|
|
445497
|
+
if (!code2) {
|
|
445498
|
+
response.writeHead(400, { "Content-Type": "text/html" });
|
|
445499
|
+
response.end(callbackPage("Authorization failed", "Missing authorization code"));
|
|
445500
|
+
return;
|
|
445501
|
+
}
|
|
445502
|
+
response.writeHead(200, { "Content-Type": "text/html" });
|
|
445503
|
+
settle3?.(code2);
|
|
445504
|
+
response.end(callbackPage("Authorization complete", "You can close this tab and return to Letta Code."), () => {
|
|
445505
|
+
closeServer(true);
|
|
445506
|
+
});
|
|
445507
|
+
});
|
|
445508
|
+
server2.on("clientError", (_error, socket) => {
|
|
445509
|
+
if (!socket.writable)
|
|
445510
|
+
return;
|
|
445511
|
+
socket.end(`HTTP/1.1 400 Bad Request\r
|
|
445512
|
+
Connection: close\r
|
|
445513
|
+
Content-Length: 0\r
|
|
445514
|
+
\r
|
|
445515
|
+
`);
|
|
445516
|
+
});
|
|
445517
|
+
let serverClosing = false;
|
|
445518
|
+
const closeServer = (force = false) => {
|
|
445519
|
+
if (serverClosing)
|
|
445520
|
+
return;
|
|
445521
|
+
serverClosing = true;
|
|
445522
|
+
server2.close();
|
|
445523
|
+
if (force)
|
|
445524
|
+
server2.closeAllConnections();
|
|
445525
|
+
};
|
|
445526
|
+
await new Promise((resolve39, rejectListen) => {
|
|
445527
|
+
server2.once("error", rejectListen);
|
|
445528
|
+
server2.listen(port, "127.0.0.1", resolve39);
|
|
445529
|
+
});
|
|
445530
|
+
server2.unref();
|
|
445531
|
+
const address = server2.address();
|
|
445532
|
+
if (!address || typeof address === "string") {
|
|
445533
|
+
server2.close();
|
|
445534
|
+
throw new Error("Could not start MCP OAuth callback server");
|
|
445535
|
+
}
|
|
445536
|
+
const timeout = setTimeout(() => {
|
|
445537
|
+
reject2?.(new Error("Timed out waiting for MCP OAuth authorization"));
|
|
445538
|
+
closeServer(true);
|
|
445539
|
+
}, CALLBACK_TIMEOUT_MS);
|
|
445540
|
+
timeout.unref();
|
|
445541
|
+
return {
|
|
445542
|
+
redirectUrl: `http://127.0.0.1:${address.port}/callback`,
|
|
445543
|
+
expectedState,
|
|
445544
|
+
waitForCode: () => codePromise.finally(() => clearTimeout(timeout)),
|
|
445545
|
+
close: () => {
|
|
445546
|
+
clearTimeout(timeout);
|
|
445547
|
+
if (!completed)
|
|
445548
|
+
reject2?.(new Error("MCP OAuth flow was cancelled"));
|
|
445549
|
+
closeServer(true);
|
|
445550
|
+
}
|
|
445272
445551
|
};
|
|
445552
|
+
}
|
|
445553
|
+
function callbackPage(title, message) {
|
|
445554
|
+
return `<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>${escapeHtml2(title)}</h1><p>${escapeHtml2(message)}</p></body></html>`;
|
|
445555
|
+
}
|
|
445556
|
+
function escapeHtml2(value) {
|
|
445557
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
445558
|
+
const entities = {
|
|
445559
|
+
"&": "&",
|
|
445560
|
+
"<": "<",
|
|
445561
|
+
">": ">",
|
|
445562
|
+
'"': """,
|
|
445563
|
+
"'": "'"
|
|
445564
|
+
};
|
|
445565
|
+
return entities[character] ?? character;
|
|
445566
|
+
});
|
|
445567
|
+
}
|
|
445568
|
+
var CALLBACK_TIMEOUT_MS;
|
|
445569
|
+
var init_mcp_oauth_callback = __esm(() => {
|
|
445570
|
+
CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
445273
445571
|
});
|
|
445274
445572
|
|
|
445275
445573
|
// src/mcp-oauth.ts
|
|
445276
445574
|
import { createHash as createHash11, randomBytes as randomBytes2 } from "node:crypto";
|
|
445277
|
-
import { createServer as createServer3 } from "node:http";
|
|
445278
445575
|
async function clearMcpOAuthCredentials(agentId, serverName, serverUrl) {
|
|
445279
|
-
|
|
445576
|
+
const credentialKey = mcpOAuthCredentialKey(agentId, serverName, serverUrl);
|
|
445577
|
+
const coordinationKey = storageCoordinationKey(bunSecretStorage, credentialKey, "bun-secrets");
|
|
445578
|
+
const { result: result3 } = await clearStoredCredentials(bunSecretStorage, credentialKey, coordinationKey);
|
|
445579
|
+
return result3 === true;
|
|
445280
445580
|
}
|
|
445281
445581
|
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,
|
|
445582
|
+
const credentialKey = mcpOAuthCredentialKey(agentId, serverName, serverUrl);
|
|
445583
|
+
return createMcpOAuthSessionWithStorage({
|
|
445584
|
+
credentialKey,
|
|
445585
|
+
storageNamespace: "bun-secrets",
|
|
445586
|
+
storage: bunSecretStorage,
|
|
445294
445587
|
interactive: options.interactive,
|
|
445295
445588
|
onStatus: options.onStatus,
|
|
445296
|
-
openBrowser: options.openBrowser ?? openSystemBrowser
|
|
445297
|
-
expectedState: callback?.expectedState
|
|
445589
|
+
openBrowser: options.openBrowser ?? openSystemBrowser
|
|
445298
445590
|
});
|
|
445299
|
-
|
|
445300
|
-
|
|
445301
|
-
|
|
445302
|
-
|
|
445303
|
-
|
|
445591
|
+
}
|
|
445592
|
+
async function createMcpOAuthSessionWithStorage(options) {
|
|
445593
|
+
const coordinationKey = storageCoordinationKey(options.storage, options.credentialKey, options.storageNamespace);
|
|
445594
|
+
retainCredentialCoordinator(coordinationKey);
|
|
445595
|
+
let provider;
|
|
445596
|
+
let callback;
|
|
445597
|
+
try {
|
|
445598
|
+
const loaded = await loadState(options.storage, options.credentialKey, coordinationKey);
|
|
445599
|
+
const persisted = loaded.state;
|
|
445600
|
+
if (!options.interactive && !persisted) {
|
|
445601
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445602
|
+
return;
|
|
445603
|
+
}
|
|
445604
|
+
callback = options.interactive ? await startOAuthCallbackServer2(callbackPort(persisted?.redirectUrl)) : undefined;
|
|
445605
|
+
const redirectUrl = callback?.redirectUrl ?? persisted?.redirectUrl;
|
|
445606
|
+
if (!redirectUrl) {
|
|
445607
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445608
|
+
return;
|
|
445609
|
+
}
|
|
445610
|
+
provider = new PersistentMcpOAuthProvider({
|
|
445611
|
+
credentialKey: options.credentialKey,
|
|
445612
|
+
coordinationKey,
|
|
445613
|
+
storage: options.storage,
|
|
445614
|
+
redirectUrl,
|
|
445615
|
+
persisted,
|
|
445616
|
+
interactive: options.interactive,
|
|
445617
|
+
onStatus: options.onStatus,
|
|
445618
|
+
openBrowser: options.openBrowser ?? openSystemBrowser,
|
|
445619
|
+
expectedState: callback?.expectedState,
|
|
445620
|
+
generation: loaded.generation
|
|
445621
|
+
});
|
|
445622
|
+
const activeProvider = provider;
|
|
445623
|
+
const activeCallback = callback;
|
|
445624
|
+
return {
|
|
445625
|
+
authProvider: activeProvider,
|
|
445626
|
+
...activeCallback ? { waitForAuthorizationCode: () => activeCallback.waitForCode() } : {},
|
|
445627
|
+
exportCredentials: () => activeProvider.exportCredentials(),
|
|
445628
|
+
closeCallback: async () => activeCallback?.close(),
|
|
445629
|
+
close: async () => {
|
|
445630
|
+
activeCallback?.close();
|
|
445631
|
+
await activeProvider.dispose();
|
|
445632
|
+
}
|
|
445633
|
+
};
|
|
445634
|
+
} catch (error5) {
|
|
445635
|
+
callback?.close();
|
|
445636
|
+
if (provider)
|
|
445637
|
+
await provider.dispose();
|
|
445638
|
+
else
|
|
445639
|
+
await releaseCredentialCoordinator(coordinationKey);
|
|
445640
|
+
throw error5;
|
|
445641
|
+
}
|
|
445304
445642
|
}
|
|
445305
445643
|
|
|
445306
445644
|
class PersistentMcpOAuthProvider {
|
|
445307
445645
|
stateData;
|
|
445308
|
-
|
|
445646
|
+
pendingClientInformation;
|
|
445647
|
+
pendingDiscoveryState;
|
|
445648
|
+
codeVerifierValue;
|
|
445649
|
+
generation;
|
|
445650
|
+
observedClientVersion;
|
|
445651
|
+
observedTokenVersion;
|
|
445652
|
+
observedDiscoveryVersion;
|
|
445653
|
+
tokenOperationGeneration;
|
|
445654
|
+
tokenOperationVersion;
|
|
445655
|
+
coordinatorValue;
|
|
445656
|
+
credentialKey;
|
|
445657
|
+
coordinationKey;
|
|
445658
|
+
storage;
|
|
445309
445659
|
interactive;
|
|
445310
445660
|
onStatus;
|
|
445311
445661
|
openBrowser;
|
|
445312
445662
|
expectedState;
|
|
445663
|
+
closed = false;
|
|
445313
445664
|
constructor(options) {
|
|
445314
|
-
this.
|
|
445665
|
+
this.credentialKey = options.credentialKey;
|
|
445666
|
+
this.coordinationKey = options.coordinationKey;
|
|
445667
|
+
this.storage = options.storage;
|
|
445315
445668
|
this.interactive = options.interactive;
|
|
445316
445669
|
this.onStatus = options.onStatus;
|
|
445317
445670
|
this.openBrowser = options.openBrowser;
|
|
445318
445671
|
this.expectedState = options.expectedState;
|
|
445672
|
+
this.generation = options.generation;
|
|
445673
|
+
const coordinator = getCredentialCoordinator(options.coordinationKey);
|
|
445674
|
+
this.coordinatorValue = coordinator;
|
|
445675
|
+
this.observedClientVersion = coordinator.clientVersion;
|
|
445676
|
+
this.observedTokenVersion = coordinator.tokenVersion;
|
|
445677
|
+
this.observedDiscoveryVersion = coordinator.discoveryVersion;
|
|
445678
|
+
this.tokenOperationGeneration = coordinator.generation;
|
|
445679
|
+
this.tokenOperationVersion = coordinator.tokenVersion;
|
|
445319
445680
|
this.stateData = {
|
|
445320
445681
|
...options.persisted,
|
|
445321
445682
|
redirectUrl: options.redirectUrl
|
|
445322
445683
|
};
|
|
445323
445684
|
}
|
|
445324
445685
|
get redirectUrl() {
|
|
445686
|
+
this.assertOpen();
|
|
445325
445687
|
return this.stateData.redirectUrl;
|
|
445326
445688
|
}
|
|
445327
445689
|
get clientMetadata() {
|
|
@@ -445334,27 +445696,127 @@ class PersistentMcpOAuthProvider {
|
|
|
445334
445696
|
};
|
|
445335
445697
|
}
|
|
445336
445698
|
state() {
|
|
445699
|
+
this.assertOpen();
|
|
445337
445700
|
const state = randomBytes2(24).toString("base64url");
|
|
445338
445701
|
if (this.expectedState)
|
|
445339
445702
|
this.expectedState.value = state;
|
|
445340
445703
|
return state;
|
|
445341
445704
|
}
|
|
445342
445705
|
clientInformation() {
|
|
445343
|
-
|
|
445344
|
-
|
|
445706
|
+
this.assertOpen();
|
|
445707
|
+
if (this.generation !== this.coordinator.generation)
|
|
445708
|
+
return;
|
|
445709
|
+
if (this.pendingClientInformation && this.pendingClientInformation.baseVersion !== this.coordinator.clientVersion) {
|
|
445710
|
+
this.pendingClientInformation = undefined;
|
|
445711
|
+
}
|
|
445712
|
+
const client = this.pendingClientInformation?.value ?? this.coordinator.state?.clientInformation;
|
|
445713
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445714
|
+
return client && "redirect_uris" in client && Array.isArray(client.redirect_uris) && client.redirect_uris.includes(this.redirectUrl) ? client : undefined;
|
|
445345
445715
|
}
|
|
445346
445716
|
saveClientInformation(clientInformation) {
|
|
445717
|
+
this.assertOpen();
|
|
445718
|
+
assertCurrentGeneration(this.coordinationKey, this.generation);
|
|
445347
445719
|
this.stateData.clientInformation = clientInformation;
|
|
445720
|
+
this.pendingClientInformation = {
|
|
445721
|
+
value: clientInformation,
|
|
445722
|
+
baseVersion: this.coordinator.clientVersion
|
|
445723
|
+
};
|
|
445724
|
+
if (!this.coordinator.state?.tokens) {
|
|
445725
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445726
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445727
|
+
}
|
|
445348
445728
|
}
|
|
445349
445729
|
tokens() {
|
|
445350
|
-
|
|
445730
|
+
this.assertOpen();
|
|
445731
|
+
if (this.generation !== this.coordinator.generation)
|
|
445732
|
+
return;
|
|
445733
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445734
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445735
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445736
|
+
return this.coordinator.state?.tokens;
|
|
445737
|
+
}
|
|
445738
|
+
async exportCredentials() {
|
|
445739
|
+
this.assertOpen();
|
|
445740
|
+
const expectedGeneration = this.generation;
|
|
445741
|
+
return enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445742
|
+
this.assertOpen();
|
|
445743
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445744
|
+
const latest = await readStoredState(this.storage, this.credentialKey);
|
|
445745
|
+
if (!latest) {
|
|
445746
|
+
throw new Error("MCP OAuth authorization is not complete");
|
|
445747
|
+
}
|
|
445748
|
+
this.assertOpen();
|
|
445749
|
+
this.coordinator.state = latest;
|
|
445750
|
+
this.stateData = latest;
|
|
445751
|
+
return exportCredentialSnapshot(latest);
|
|
445752
|
+
});
|
|
445351
445753
|
}
|
|
445352
445754
|
async saveTokens(tokens) {
|
|
445353
|
-
this.
|
|
445354
|
-
|
|
445355
|
-
|
|
445755
|
+
this.assertOpen();
|
|
445756
|
+
const expectedGeneration = this.tokenOperationGeneration;
|
|
445757
|
+
const expectedTokenVersion = this.tokenOperationVersion;
|
|
445758
|
+
const tokenExpiresAt = tokens.expires_in === undefined ? undefined : Date.now() + tokens.expires_in * 1000;
|
|
445759
|
+
const pendingClientInformation = this.pendingClientInformation;
|
|
445760
|
+
const pendingDiscoveryState = this.pendingDiscoveryState;
|
|
445761
|
+
const result3 = await enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445762
|
+
this.assertOpen();
|
|
445763
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445764
|
+
if (this.coordinator.tokenVersion !== expectedTokenVersion) {
|
|
445765
|
+
throw new Error("MCP OAuth token response is stale because credentials changed");
|
|
445766
|
+
}
|
|
445767
|
+
if (pendingClientInformation && this.coordinator.clientVersion !== pendingClientInformation.baseVersion || pendingDiscoveryState && this.coordinator.discoveryVersion !== pendingDiscoveryState.baseVersion) {
|
|
445768
|
+
throw new Error("MCP OAuth token response is stale because protocol state changed");
|
|
445769
|
+
}
|
|
445770
|
+
const state = await readStoredState(this.storage, this.credentialKey) ?? { redirectUrl: this.redirectUrl };
|
|
445771
|
+
let clientMerged = false;
|
|
445772
|
+
let discoveryMerged = false;
|
|
445773
|
+
if (pendingClientInformation && this.coordinator.clientVersion === pendingClientInformation.baseVersion) {
|
|
445774
|
+
state.redirectUrl = this.redirectUrl;
|
|
445775
|
+
state.clientInformation = pendingClientInformation.value;
|
|
445776
|
+
clientMerged = true;
|
|
445777
|
+
}
|
|
445778
|
+
if (pendingDiscoveryState && this.coordinator.discoveryVersion === pendingDiscoveryState.baseVersion) {
|
|
445779
|
+
state.discoveryState = pendingDiscoveryState.value;
|
|
445780
|
+
discoveryMerged = true;
|
|
445781
|
+
}
|
|
445782
|
+
state.tokens = {
|
|
445783
|
+
...tokens,
|
|
445784
|
+
refresh_token: tokens.refresh_token ?? state.tokens?.refresh_token
|
|
445785
|
+
};
|
|
445786
|
+
state.tokenExpiresAt = tokenExpiresAt;
|
|
445787
|
+
await this.storage.set(this.credentialKey, JSON.stringify(state));
|
|
445788
|
+
this.coordinator.state = state;
|
|
445789
|
+
this.coordinator.tokenVersion += 1;
|
|
445790
|
+
if (clientMerged)
|
|
445791
|
+
this.coordinator.clientVersion += 1;
|
|
445792
|
+
if (discoveryMerged)
|
|
445793
|
+
this.coordinator.discoveryVersion += 1;
|
|
445794
|
+
return { state, clientMerged, discoveryMerged };
|
|
445795
|
+
});
|
|
445796
|
+
this.codeVerifierValue = undefined;
|
|
445797
|
+
if (this.closed) {
|
|
445798
|
+
this.scrubLocalState();
|
|
445799
|
+
return;
|
|
445800
|
+
}
|
|
445801
|
+
this.stateData = result3.state;
|
|
445802
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445803
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445804
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445805
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445806
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445807
|
+
if (this.pendingClientInformation === pendingClientInformation) {
|
|
445808
|
+
this.pendingClientInformation = undefined;
|
|
445809
|
+
} else if (this.pendingClientInformation) {
|
|
445810
|
+
this.pendingClientInformation.baseVersion = this.coordinator.clientVersion;
|
|
445811
|
+
}
|
|
445812
|
+
if (this.pendingDiscoveryState === pendingDiscoveryState) {
|
|
445813
|
+
this.pendingDiscoveryState = undefined;
|
|
445814
|
+
} else if (this.pendingDiscoveryState) {
|
|
445815
|
+
this.pendingDiscoveryState.baseVersion = this.coordinator.discoveryVersion;
|
|
445816
|
+
}
|
|
445356
445817
|
}
|
|
445357
445818
|
async redirectToAuthorization(authorizationUrl) {
|
|
445819
|
+
this.assertOpen();
|
|
445358
445820
|
if (!this.interactive) {
|
|
445359
445821
|
throw new Error("MCP authentication requires user authorization. Open /mcp and press R to sign in.");
|
|
445360
445822
|
}
|
|
@@ -445364,147 +445826,268 @@ ${authorizationUrl.toString()}`);
|
|
|
445364
445826
|
await this.openBrowser(authorizationUrl.toString());
|
|
445365
445827
|
}
|
|
445366
445828
|
saveCodeVerifier(codeVerifier) {
|
|
445367
|
-
this.
|
|
445829
|
+
this.assertOpen();
|
|
445830
|
+
this.codeVerifierValue = codeVerifier;
|
|
445368
445831
|
}
|
|
445369
445832
|
codeVerifier() {
|
|
445370
|
-
|
|
445833
|
+
this.assertOpen();
|
|
445834
|
+
if (!this.codeVerifierValue) {
|
|
445371
445835
|
throw new Error("No MCP OAuth PKCE verifier is available");
|
|
445372
445836
|
}
|
|
445373
|
-
return this.
|
|
445837
|
+
return this.codeVerifierValue;
|
|
445374
445838
|
}
|
|
445375
445839
|
saveDiscoveryState(state) {
|
|
445840
|
+
this.assertOpen();
|
|
445841
|
+
assertCurrentGeneration(this.coordinationKey, this.generation);
|
|
445376
445842
|
this.stateData.discoveryState = state;
|
|
445843
|
+
this.pendingDiscoveryState = {
|
|
445844
|
+
value: state,
|
|
445845
|
+
baseVersion: this.coordinator.discoveryVersion
|
|
445846
|
+
};
|
|
445847
|
+
if (!this.coordinator.state?.tokens) {
|
|
445848
|
+
this.tokenOperationGeneration = this.coordinator.generation;
|
|
445849
|
+
this.tokenOperationVersion = this.coordinator.tokenVersion;
|
|
445850
|
+
}
|
|
445377
445851
|
}
|
|
445378
445852
|
discoveryState() {
|
|
445379
|
-
|
|
445380
|
-
|
|
445381
|
-
async invalidateCredentials(scope) {
|
|
445382
|
-
if (scope === "all") {
|
|
445383
|
-
this.stateData = { redirectUrl: this.redirectUrl };
|
|
445384
|
-
await deleteSecretValue(this.secretName);
|
|
445853
|
+
this.assertOpen();
|
|
445854
|
+
if (this.generation !== this.coordinator.generation)
|
|
445385
445855
|
return;
|
|
445856
|
+
if (this.pendingDiscoveryState && this.pendingDiscoveryState.baseVersion !== this.coordinator.discoveryVersion) {
|
|
445857
|
+
this.pendingDiscoveryState = undefined;
|
|
445386
445858
|
}
|
|
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);
|
|
445859
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445860
|
+
return this.pendingDiscoveryState?.value ?? this.coordinator.state?.discoveryState;
|
|
445408
445861
|
}
|
|
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");
|
|
445862
|
+
async invalidateCredentials(scope) {
|
|
445863
|
+
this.assertOpen();
|
|
445864
|
+
if (scope === "verifier") {
|
|
445865
|
+
this.codeVerifierValue = undefined;
|
|
445433
445866
|
return;
|
|
445434
445867
|
}
|
|
445435
|
-
|
|
445436
|
-
|
|
445437
|
-
|
|
445438
|
-
|
|
445439
|
-
|
|
445440
|
-
|
|
445441
|
-
|
|
445442
|
-
|
|
445868
|
+
if (scope === "all") {
|
|
445869
|
+
const redirectUrl = this.redirectUrl;
|
|
445870
|
+
const { generation: generation3, cleared } = await clearStoredCredentials(this.storage, this.credentialKey, this.coordinationKey, {
|
|
445871
|
+
generation: this.generation,
|
|
445872
|
+
clientVersion: this.observedClientVersion,
|
|
445873
|
+
tokenVersion: this.observedTokenVersion,
|
|
445874
|
+
discoveryVersion: this.observedDiscoveryVersion
|
|
445875
|
+
});
|
|
445876
|
+
if (this.closed)
|
|
445877
|
+
return;
|
|
445878
|
+
if (!cleared) {
|
|
445879
|
+
this.stateData = this.coordinator.state ?? { redirectUrl };
|
|
445880
|
+
this.pendingClientInformation = undefined;
|
|
445881
|
+
this.pendingDiscoveryState = undefined;
|
|
445882
|
+
this.codeVerifierValue = undefined;
|
|
445883
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445884
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445885
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445886
|
+
return;
|
|
445887
|
+
}
|
|
445888
|
+
this.generation = generation3;
|
|
445889
|
+
this.stateData = { redirectUrl };
|
|
445890
|
+
this.pendingClientInformation = undefined;
|
|
445891
|
+
this.pendingDiscoveryState = undefined;
|
|
445892
|
+
this.codeVerifierValue = undefined;
|
|
445893
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445894
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445895
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445896
|
+
return;
|
|
445897
|
+
}
|
|
445898
|
+
const expectedGeneration = this.generation;
|
|
445899
|
+
const pendingClientInformation = this.pendingClientInformation;
|
|
445900
|
+
const pendingDiscoveryState = this.pendingDiscoveryState;
|
|
445901
|
+
const expectedVersion = scope === "client" ? this.observedClientVersion : scope === "tokens" ? this.observedTokenVersion : this.observedDiscoveryVersion;
|
|
445902
|
+
const result3 = await enqueueStorageMutation(this.coordinationKey, async () => {
|
|
445903
|
+
this.assertOpen();
|
|
445904
|
+
assertCurrentGeneration(this.coordinationKey, expectedGeneration);
|
|
445905
|
+
const currentVersion = scope === "client" ? this.coordinator.clientVersion : scope === "tokens" ? this.coordinator.tokenVersion : this.coordinator.discoveryVersion;
|
|
445906
|
+
if (currentVersion !== expectedVersion) {
|
|
445907
|
+
return this.coordinator.state;
|
|
445908
|
+
}
|
|
445909
|
+
const state = await readStoredState(this.storage, this.credentialKey);
|
|
445910
|
+
if (state) {
|
|
445911
|
+
if (scope === "client")
|
|
445912
|
+
delete state.clientInformation;
|
|
445913
|
+
if (scope === "tokens") {
|
|
445914
|
+
delete state.tokens;
|
|
445915
|
+
delete state.tokenExpiresAt;
|
|
445916
|
+
}
|
|
445917
|
+
if (scope === "discovery")
|
|
445918
|
+
delete state.discoveryState;
|
|
445919
|
+
await this.storage.set(this.credentialKey, JSON.stringify(state));
|
|
445920
|
+
}
|
|
445921
|
+
this.coordinator.state = state;
|
|
445922
|
+
if (scope === "client")
|
|
445923
|
+
this.coordinator.clientVersion += 1;
|
|
445924
|
+
if (scope === "tokens")
|
|
445925
|
+
this.coordinator.tokenVersion += 1;
|
|
445926
|
+
if (scope === "discovery")
|
|
445927
|
+
this.coordinator.discoveryVersion += 1;
|
|
445928
|
+
return state;
|
|
445929
|
+
});
|
|
445930
|
+
if (this.closed)
|
|
445443
445931
|
return;
|
|
445932
|
+
this.stateData = result3 ?? { redirectUrl: this.redirectUrl };
|
|
445933
|
+
if (scope === "client") {
|
|
445934
|
+
this.observedClientVersion = this.coordinator.clientVersion;
|
|
445935
|
+
if (this.pendingClientInformation === pendingClientInformation) {
|
|
445936
|
+
this.pendingClientInformation = undefined;
|
|
445937
|
+
} else if (this.pendingClientInformation) {
|
|
445938
|
+
this.pendingClientInformation.baseVersion = this.coordinator.clientVersion;
|
|
445939
|
+
}
|
|
445444
445940
|
}
|
|
445445
|
-
if (
|
|
445446
|
-
|
|
445447
|
-
|
|
445448
|
-
|
|
445449
|
-
|
|
445941
|
+
if (scope === "tokens") {
|
|
445942
|
+
this.observedTokenVersion = this.coordinator.tokenVersion;
|
|
445943
|
+
}
|
|
445944
|
+
if (scope === "discovery") {
|
|
445945
|
+
this.observedDiscoveryVersion = this.coordinator.discoveryVersion;
|
|
445946
|
+
if (this.pendingDiscoveryState === pendingDiscoveryState) {
|
|
445947
|
+
this.pendingDiscoveryState = undefined;
|
|
445948
|
+
} else if (this.pendingDiscoveryState) {
|
|
445949
|
+
this.pendingDiscoveryState.baseVersion = this.coordinator.discoveryVersion;
|
|
445950
|
+
}
|
|
445951
|
+
}
|
|
445952
|
+
}
|
|
445953
|
+
async dispose() {
|
|
445954
|
+
if (this.closed)
|
|
445450
445955
|
return;
|
|
445956
|
+
this.closed = true;
|
|
445957
|
+
this.scrubLocalState();
|
|
445958
|
+
try {
|
|
445959
|
+
await releaseCredentialCoordinator(this.coordinationKey);
|
|
445960
|
+
} finally {
|
|
445961
|
+
this.coordinatorValue = undefined;
|
|
445962
|
+
this.scrubLocalState();
|
|
445451
445963
|
}
|
|
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
445964
|
}
|
|
445467
|
-
|
|
445468
|
-
|
|
445469
|
-
|
|
445470
|
-
|
|
445471
|
-
|
|
445965
|
+
get coordinator() {
|
|
445966
|
+
if (!this.coordinatorValue)
|
|
445967
|
+
throw new Error("MCP OAuth session is closed");
|
|
445968
|
+
return this.coordinatorValue;
|
|
445969
|
+
}
|
|
445970
|
+
scrubLocalState() {
|
|
445971
|
+
this.codeVerifierValue = undefined;
|
|
445972
|
+
this.pendingClientInformation = undefined;
|
|
445973
|
+
this.pendingDiscoveryState = undefined;
|
|
445974
|
+
this.stateData = { redirectUrl: this.stateData.redirectUrl };
|
|
445975
|
+
}
|
|
445976
|
+
assertOpen() {
|
|
445977
|
+
if (this.closed)
|
|
445978
|
+
throw new Error("MCP OAuth session is closed");
|
|
445979
|
+
}
|
|
445980
|
+
}
|
|
445981
|
+
function exportCredentialSnapshot(state) {
|
|
445982
|
+
const clientInformation = state.clientInformation;
|
|
445983
|
+
const tokens = state.tokens;
|
|
445984
|
+
const validClient = clientInformation && "redirect_uris" in clientInformation && clientInformation.redirect_uris.includes(state.redirectUrl) ? clientInformation : undefined;
|
|
445985
|
+
if (!validClient?.client_id || !tokens?.access_token) {
|
|
445986
|
+
throw new Error("MCP OAuth authorization is not complete");
|
|
445987
|
+
}
|
|
445472
445988
|
return {
|
|
445473
|
-
|
|
445474
|
-
|
|
445475
|
-
|
|
445476
|
-
|
|
445477
|
-
|
|
445478
|
-
|
|
445479
|
-
|
|
445480
|
-
|
|
445481
|
-
}
|
|
445989
|
+
access_token: tokens.access_token,
|
|
445990
|
+
...tokens.refresh_token ? { refresh_token: tokens.refresh_token } : {},
|
|
445991
|
+
client_id: validClient.client_id,
|
|
445992
|
+
...validClient.client_secret ? { client_secret: validClient.client_secret } : {},
|
|
445993
|
+
redirect_uri: state.redirectUrl,
|
|
445994
|
+
...tokens.token_type ? { token_type: tokens.token_type } : {},
|
|
445995
|
+
...state.tokenExpiresAt !== undefined ? {
|
|
445996
|
+
expires_in: Math.max(0, Math.ceil((state.tokenExpiresAt - Date.now()) / 1000))
|
|
445997
|
+
} : {},
|
|
445998
|
+
...tokens.scope ? { scope: tokens.scope } : {}
|
|
445482
445999
|
};
|
|
445483
446000
|
}
|
|
445484
|
-
function
|
|
445485
|
-
|
|
446001
|
+
function enqueueStorageMutation(coordinationKey, mutation) {
|
|
446002
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446003
|
+
const pending = coordinator.queue.then(mutation, mutation);
|
|
446004
|
+
coordinator.queue = pending.then(() => {
|
|
445486
446005
|
return;
|
|
445487
|
-
|
|
445488
|
-
const parsed = new URL(redirectUrl);
|
|
445489
|
-
return parsed.hostname === "127.0.0.1" && parsed.port ? Number(parsed.port) : undefined;
|
|
445490
|
-
} catch {
|
|
446006
|
+
}, () => {
|
|
445491
446007
|
return;
|
|
446008
|
+
});
|
|
446009
|
+
return pending;
|
|
446010
|
+
}
|
|
446011
|
+
function getCredentialCoordinator(coordinationKey) {
|
|
446012
|
+
const existing = credentialCoordinators.get(coordinationKey);
|
|
446013
|
+
if (existing)
|
|
446014
|
+
return existing;
|
|
446015
|
+
const coordinator = {
|
|
446016
|
+
queue: Promise.resolve(),
|
|
446017
|
+
generation: 0,
|
|
446018
|
+
activeProviders: 0,
|
|
446019
|
+
clientVersion: 0,
|
|
446020
|
+
tokenVersion: 0,
|
|
446021
|
+
discoveryVersion: 0
|
|
446022
|
+
};
|
|
446023
|
+
credentialCoordinators.set(coordinationKey, coordinator);
|
|
446024
|
+
return coordinator;
|
|
446025
|
+
}
|
|
446026
|
+
function storageCoordinationKey(storage, credentialKey, storageNamespace) {
|
|
446027
|
+
const namespace = storageNamespace;
|
|
446028
|
+
if (namespace !== undefined && (!namespace.trim() || namespace.includes("\x00"))) {
|
|
446029
|
+
throw new Error("MCP OAuth storage namespace must be non-empty and contain no NUL bytes");
|
|
446030
|
+
}
|
|
446031
|
+
let identity2;
|
|
446032
|
+
if (!namespace) {
|
|
446033
|
+
let objectId = storageObjectIds.get(storage);
|
|
446034
|
+
if (!objectId) {
|
|
446035
|
+
objectId = nextStorageObjectId;
|
|
446036
|
+
nextStorageObjectId += 1;
|
|
446037
|
+
storageObjectIds.set(storage, objectId);
|
|
446038
|
+
}
|
|
446039
|
+
identity2 = `object:${objectId}`;
|
|
446040
|
+
} else {
|
|
446041
|
+
identity2 = `namespace:${namespace}`;
|
|
445492
446042
|
}
|
|
446043
|
+
return `${identity2}\x00${credentialKey}`;
|
|
445493
446044
|
}
|
|
445494
|
-
function
|
|
445495
|
-
|
|
446045
|
+
function retainCredentialCoordinator(coordinationKey) {
|
|
446046
|
+
getCredentialCoordinator(coordinationKey).activeProviders += 1;
|
|
445496
446047
|
}
|
|
445497
|
-
function
|
|
445498
|
-
|
|
445499
|
-
|
|
445500
|
-
|
|
445501
|
-
|
|
445502
|
-
|
|
445503
|
-
'"': """,
|
|
445504
|
-
"'": "'"
|
|
445505
|
-
};
|
|
445506
|
-
return entities[character] ?? character;
|
|
446048
|
+
async function releaseCredentialCoordinator(coordinationKey) {
|
|
446049
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446050
|
+
await enqueueStorageMutation(coordinationKey, async () => {
|
|
446051
|
+
coordinator.activeProviders = Math.max(0, coordinator.activeProviders - 1);
|
|
446052
|
+
if (coordinator.activeProviders === 0)
|
|
446053
|
+
coordinator.state = undefined;
|
|
445507
446054
|
});
|
|
446055
|
+
await coordinator.queue;
|
|
446056
|
+
if (coordinator.activeProviders === 0 && credentialCoordinators.get(coordinationKey) === coordinator) {
|
|
446057
|
+
credentialCoordinators.delete(coordinationKey);
|
|
446058
|
+
}
|
|
446059
|
+
}
|
|
446060
|
+
function assertCurrentGeneration(coordinationKey, expectedGeneration) {
|
|
446061
|
+
if (getCredentialCoordinator(coordinationKey).generation !== expectedGeneration) {
|
|
446062
|
+
throw new Error("MCP OAuth session is stale because credentials were cleared");
|
|
446063
|
+
}
|
|
446064
|
+
}
|
|
446065
|
+
async function clearStoredCredentials(storage, credentialKey, coordinationKey, expected) {
|
|
446066
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446067
|
+
const outcome = await enqueueStorageMutation(coordinationKey, async () => {
|
|
446068
|
+
if (expected) {
|
|
446069
|
+
assertCurrentGeneration(coordinationKey, expected.generation);
|
|
446070
|
+
if (coordinator.clientVersion !== expected.clientVersion || coordinator.tokenVersion !== expected.tokenVersion || coordinator.discoveryVersion !== expected.discoveryVersion) {
|
|
446071
|
+
return {
|
|
446072
|
+
result: false,
|
|
446073
|
+
generation: coordinator.generation,
|
|
446074
|
+
cleared: false
|
|
446075
|
+
};
|
|
446076
|
+
}
|
|
446077
|
+
}
|
|
446078
|
+
const result3 = await storage.delete(credentialKey);
|
|
446079
|
+
coordinator.generation += 1;
|
|
446080
|
+
coordinator.clientVersion += 1;
|
|
446081
|
+
coordinator.tokenVersion += 1;
|
|
446082
|
+
coordinator.discoveryVersion += 1;
|
|
446083
|
+
coordinator.state = undefined;
|
|
446084
|
+
return { result: result3, generation: coordinator.generation, cleared: true };
|
|
446085
|
+
});
|
|
446086
|
+
await coordinator.queue;
|
|
446087
|
+
if (coordinator.activeProviders === 0 && credentialCoordinators.get(coordinationKey) === coordinator) {
|
|
446088
|
+
credentialCoordinators.delete(coordinationKey);
|
|
446089
|
+
}
|
|
446090
|
+
return outcome;
|
|
445508
446091
|
}
|
|
445509
446092
|
async function openSystemBrowser(url2) {
|
|
445510
446093
|
try {
|
|
@@ -445513,25 +446096,73 @@ async function openSystemBrowser(url2) {
|
|
|
445513
446096
|
subprocess.on("error", () => {});
|
|
445514
446097
|
} catch {}
|
|
445515
446098
|
}
|
|
445516
|
-
function
|
|
446099
|
+
function mcpOAuthCredentialKey(agentId, serverName, serverUrl) {
|
|
445517
446100
|
const digest = createHash11("sha256").update(`${agentId}\x00${serverName}\x00${serverUrl}`).digest("hex").slice(0, 32);
|
|
445518
446101
|
return `mcp-oauth-${digest}`;
|
|
445519
446102
|
}
|
|
445520
|
-
async function loadState(
|
|
445521
|
-
|
|
446103
|
+
async function loadState(storage, credentialKey, coordinationKey) {
|
|
446104
|
+
return enqueueStorageMutation(coordinationKey, async () => {
|
|
446105
|
+
const coordinator = getCredentialCoordinator(coordinationKey);
|
|
446106
|
+
const state = await readStoredState(storage, credentialKey);
|
|
446107
|
+
coordinator.state = state;
|
|
446108
|
+
return { state, generation: coordinator.generation };
|
|
446109
|
+
});
|
|
446110
|
+
}
|
|
446111
|
+
async function readStoredState(storage, credentialKey) {
|
|
446112
|
+
const value = await storage.get(credentialKey);
|
|
445522
446113
|
if (!value)
|
|
445523
446114
|
return;
|
|
445524
446115
|
try {
|
|
445525
|
-
|
|
446116
|
+
const parsed = JSON.parse(value);
|
|
446117
|
+
if (!isPersistedMcpOAuthState(parsed)) {
|
|
446118
|
+
await storage.delete(credentialKey);
|
|
446119
|
+
return;
|
|
446120
|
+
}
|
|
446121
|
+
return parsed;
|
|
445526
446122
|
} catch {
|
|
445527
|
-
await
|
|
446123
|
+
await storage.delete(credentialKey);
|
|
445528
446124
|
return;
|
|
445529
446125
|
}
|
|
445530
446126
|
}
|
|
445531
|
-
|
|
446127
|
+
function isPersistedMcpOAuthState(value) {
|
|
446128
|
+
if (!isRecord17(value) || !SafeUrlSchema.safeParse(value.redirectUrl).success || "codeVerifier" in value) {
|
|
446129
|
+
return false;
|
|
446130
|
+
}
|
|
446131
|
+
if (value.clientInformation !== undefined) {
|
|
446132
|
+
if (!isRecord17(value.clientInformation))
|
|
446133
|
+
return false;
|
|
446134
|
+
const schema5 = "redirect_uris" in value.clientInformation ? OAuthClientInformationFullSchema : OAuthClientInformationSchema;
|
|
446135
|
+
if (!schema5.safeParse(value.clientInformation).success)
|
|
446136
|
+
return false;
|
|
446137
|
+
}
|
|
446138
|
+
if (value.tokens !== undefined && !OAuthTokensSchema.safeParse(value.tokens).success) {
|
|
446139
|
+
return false;
|
|
446140
|
+
}
|
|
446141
|
+
if (value.tokenExpiresAt !== undefined && (typeof value.tokenExpiresAt !== "number" || !Number.isFinite(value.tokenExpiresAt))) {
|
|
446142
|
+
return false;
|
|
446143
|
+
}
|
|
446144
|
+
if (value.discoveryState !== undefined) {
|
|
446145
|
+
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) {
|
|
446146
|
+
return false;
|
|
446147
|
+
}
|
|
446148
|
+
}
|
|
446149
|
+
return true;
|
|
446150
|
+
}
|
|
446151
|
+
function isRecord17(value) {
|
|
446152
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
446153
|
+
}
|
|
446154
|
+
var credentialCoordinators, storageObjectIds, nextStorageObjectId = 1, bunSecretStorage;
|
|
445532
446155
|
var init_mcp_oauth = __esm(() => {
|
|
446156
|
+
init_auth2();
|
|
446157
|
+
init_mcp_oauth_callback();
|
|
445533
446158
|
init_secrets();
|
|
445534
|
-
|
|
446159
|
+
credentialCoordinators = new Map;
|
|
446160
|
+
storageObjectIds = new WeakMap;
|
|
446161
|
+
bunSecretStorage = {
|
|
446162
|
+
get: (credentialKey) => getSecretValue(credentialKey, "MCP OAuth credentials"),
|
|
446163
|
+
set: setSecretValue,
|
|
446164
|
+
delete: deleteSecretValue
|
|
446165
|
+
};
|
|
445535
446166
|
});
|
|
445536
446167
|
|
|
445537
446168
|
// src/mcp-runtime.ts
|
|
@@ -447936,7 +448567,7 @@ import {
|
|
|
447936
448567
|
} from "node:fs";
|
|
447937
448568
|
import { tmpdir as tmpdir12 } from "node:os";
|
|
447938
448569
|
import path45 from "node:path";
|
|
447939
|
-
function
|
|
448570
|
+
function isRecord18(value) {
|
|
447940
448571
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
447941
448572
|
}
|
|
447942
448573
|
function isPathInsideOrEqual2(childPath, parentPath) {
|
|
@@ -447952,7 +448583,7 @@ function readPackageJson(packageJsonPath) {
|
|
|
447952
448583
|
} catch (error5) {
|
|
447953
448584
|
throw new Error(`Could not read package.json: ${error5 instanceof Error ? error5.message : String(error5)}`);
|
|
447954
448585
|
}
|
|
447955
|
-
if (!
|
|
448586
|
+
if (!isRecord18(parsed)) {
|
|
447956
448587
|
throw new Error("package.json must be an object");
|
|
447957
448588
|
}
|
|
447958
448589
|
return parsed;
|
|
@@ -447962,7 +448593,7 @@ function formatRepository(repository) {
|
|
|
447962
448593
|
const trimmed2 = repository.trim();
|
|
447963
448594
|
return trimmed2 || undefined;
|
|
447964
448595
|
}
|
|
447965
|
-
if (!
|
|
448596
|
+
if (!isRecord18(repository))
|
|
447966
448597
|
return;
|
|
447967
448598
|
const url2 = repository.url;
|
|
447968
448599
|
if (typeof url2 !== "string")
|
|
@@ -448492,7 +449123,7 @@ function hasRuntimeDependencies(packageJson) {
|
|
|
448492
449123
|
if (!packageJson)
|
|
448493
449124
|
return false;
|
|
448494
449125
|
const dependencies4 = packageJson.dependencies;
|
|
448495
|
-
return
|
|
449126
|
+
return isRecord18(dependencies4) && Object.keys(dependencies4).length > 0;
|
|
448496
449127
|
}
|
|
448497
449128
|
function readPackageJsonIfExists(packageDirectory) {
|
|
448498
449129
|
const packageJsonPath = path45.join(packageDirectory, "package.json");
|
|
@@ -512630,4 +513261,4 @@ function registerBunOAuthFlows() {
|
|
|
512630
513261
|
registerBunOAuthFlows();
|
|
512631
513262
|
await init_src5().then(() => exports_src2);
|
|
512632
513263
|
|
|
512633
|
-
//# debugId=
|
|
513264
|
+
//# debugId=7B5F416253AF257964756E2164756E21
|