@remnic/core 9.3.768 → 9.3.769
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/access-cli.js +5 -5
- package/dist/access-http.d.ts +51 -1
- package/dist/access-http.js +2 -2
- package/dist/access-mcp.d.ts +7 -1
- package/dist/access-mcp.js +5 -3
- package/dist/access-operations.d.ts +4 -4
- package/dist/access-schema.d.ts +68 -68
- package/dist/{chunk-TVLN5EZZ.js → chunk-CNXMWYLA.js} +78 -3
- package/dist/chunk-CNXMWYLA.js.map +1 -0
- package/dist/{chunk-UG274TNV.js → chunk-E2SPGGUI.js} +13 -8
- package/dist/chunk-E2SPGGUI.js.map +1 -0
- package/dist/{chunk-HBOPSFQQ.js → chunk-EVXI2I6G.js} +3 -3
- package/dist/{chunk-4DLFJJOQ.js → chunk-QMN3CIFS.js} +2 -2
- package/dist/{chunk-J3UJJZKI.js → chunk-SIPZ5UMK.js} +85 -5
- package/dist/chunk-SIPZ5UMK.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/connectors/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -5
- package/dist/orchestrator.js +5 -5
- package/dist/schemas.d.ts +84 -84
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/tokens.d.ts +3 -1
- package/dist/tokens.js +3 -1
- package/dist/transfer/types.d.ts +66 -66
- package/package.json +2 -2
- package/src/access-http.test.ts +350 -0
- package/src/access-http.ts +160 -6
- package/src/access-mcp.ts +120 -3
- package/src/index.ts +3 -0
- package/src/tokens.test.ts +58 -0
- package/src/tokens.ts +19 -8
- package/dist/chunk-J3UJJZKI.js.map +0 -1
- package/dist/chunk-TVLN5EZZ.js.map +0 -1
- package/dist/chunk-UG274TNV.js.map +0 -1
- /package/dist/{chunk-HBOPSFQQ.js.map → chunk-EVXI2I6G.js.map} +0 -0
- /package/dist/{chunk-4DLFJJOQ.js.map → chunk-QMN3CIFS.js.map} +0 -0
|
@@ -21,7 +21,8 @@ var TOKEN_PREFIXES = {
|
|
|
21
21
|
"roo-code": "remnic_rc_",
|
|
22
22
|
"windsurf": "remnic_ws_",
|
|
23
23
|
"amp": "remnic_am_",
|
|
24
|
-
"generic-mcp": "remnic_gm_"
|
|
24
|
+
"generic-mcp": "remnic_gm_",
|
|
25
|
+
"chatgpt": "remnic_cg_"
|
|
25
26
|
};
|
|
26
27
|
function defaultTokensPath() {
|
|
27
28
|
return path.join(resolveHomeDir(), ".remnic", "tokens.json");
|
|
@@ -209,21 +210,24 @@ function getAllValidTokens(tokensPath) {
|
|
|
209
210
|
return loadTokenStore(tokensPath).tokens.map((t) => t.token);
|
|
210
211
|
}
|
|
211
212
|
var TOKEN_CACHE_TTL_MS = 5e3;
|
|
212
|
-
var
|
|
213
|
+
var _cachedEntries = [];
|
|
213
214
|
var _cachedAt = 0;
|
|
214
215
|
var _cachedPath;
|
|
215
216
|
function invalidateTokenCache() {
|
|
216
|
-
|
|
217
|
+
_cachedEntries = [];
|
|
217
218
|
_cachedAt = 0;
|
|
218
219
|
_cachedPath = void 0;
|
|
219
220
|
}
|
|
220
|
-
function
|
|
221
|
+
function getAllValidTokenEntriesCached(tokensPath) {
|
|
221
222
|
const now = Date.now();
|
|
222
|
-
if (now - _cachedAt < TOKEN_CACHE_TTL_MS && tokensPath === _cachedPath) return
|
|
223
|
-
|
|
223
|
+
if (now - _cachedAt < TOKEN_CACHE_TTL_MS && tokensPath === _cachedPath) return _cachedEntries;
|
|
224
|
+
_cachedEntries = loadTokenStore(tokensPath).tokens;
|
|
224
225
|
_cachedAt = now;
|
|
225
226
|
_cachedPath = tokensPath;
|
|
226
|
-
return
|
|
227
|
+
return _cachedEntries;
|
|
228
|
+
}
|
|
229
|
+
function getAllValidTokensCached(tokensPath) {
|
|
230
|
+
return getAllValidTokenEntriesCached(tokensPath).map((entry) => entry.token);
|
|
227
231
|
}
|
|
228
232
|
function resolveConnectorFromToken(token, tokensPath) {
|
|
229
233
|
return loadTokenStore(tokensPath).tokens.find((t) => t.token === token)?.connector;
|
|
@@ -238,7 +242,8 @@ export {
|
|
|
238
242
|
listTokens,
|
|
239
243
|
revokeToken,
|
|
240
244
|
getAllValidTokens,
|
|
245
|
+
getAllValidTokenEntriesCached,
|
|
241
246
|
getAllValidTokensCached,
|
|
242
247
|
resolveConnectorFromToken
|
|
243
248
|
};
|
|
244
|
-
//# sourceMappingURL=chunk-
|
|
249
|
+
//# sourceMappingURL=chunk-E2SPGGUI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Token management for Remnic multi-connector auth.\n *\n * Manages per-connector tokens in ~/.remnic/tokens.json.\n * Each connector gets a unique token with a recognizable prefix.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\nimport { resolveHomeDir } from \"./runtime/env.js\";\n\nexport interface TokenEntry {\n token: string;\n connector: string;\n createdAt: string;\n}\n\nexport interface TokenStore {\n tokens: TokenEntry[];\n}\n\nconst TOKEN_PREFIXES: Record<string, string> = {\n \"openclaw\": \"remnic_oc_\",\n \"claude-code\": \"remnic_cc_\",\n \"codex-cli\": \"remnic_cx_\",\n \"codex\": \"remnic_cx_\",\n \"hermes\": \"remnic_hm_\",\n \"pi\": \"remnic_pi_\",\n \"omp\": \"remnic_op_\",\n \"replit\": \"remnic_rl_\",\n \"cursor\": \"remnic_cu_\",\n \"cline\": \"remnic_cl_\",\n \"github-copilot\": \"remnic_gh_\",\n \"roo-code\": \"remnic_rc_\",\n \"windsurf\": \"remnic_ws_\",\n \"amp\": \"remnic_am_\",\n \"generic-mcp\": \"remnic_gm_\",\n \"chatgpt\": \"remnic_cg_\",\n};\n\nfunction defaultTokensPath(): string {\n return path.join(resolveHomeDir(), \".remnic\", \"tokens.json\");\n}\n\nfunction legacyTokensPath(): string {\n return path.join(resolveHomeDir(), \".engram\", \"tokens.json\");\n}\n\nfunction resolveReadPath(tokensPath?: string): string {\n const primary = tokensPath ?? defaultTokensPath();\n if (tokensPath) return primary;\n if (fs.existsSync(primary)) return primary;\n const legacy = legacyTokensPath();\n return fs.existsSync(legacy) ? legacy : primary;\n}\n\nfunction ensureDir(filePath: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n}\n\nfunction isEnoent(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && (error as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n\nfunction validateTokenEntry(raw: unknown, index: number): TokenEntry {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new Error(`invalid token entry at index ${index}: expected object`);\n }\n const entry = raw as Record<string, unknown>;\n if (typeof entry.token !== \"string\" || entry.token.length === 0) {\n throw new Error(`invalid token entry at index ${index}: token must be a non-empty string`);\n }\n if (typeof entry.connector !== \"string\" || entry.connector.length === 0) {\n throw new Error(`invalid token entry at index ${index}: connector must be a non-empty string`);\n }\n return {\n token: entry.token,\n connector: entry.connector,\n createdAt:\n typeof entry.createdAt === \"string\" && entry.createdAt.length > 0\n ? entry.createdAt\n : new Date(0).toISOString(),\n };\n}\n\nfunction parseTokenStore(rawText: string, filePath: string): { store: TokenStore; migratedLegacy: boolean } {\n let raw: unknown;\n try {\n raw = JSON.parse(rawText);\n } catch (error) {\n throw new Error(\n `failed to parse token store at ${filePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n if (typeof raw === \"object\" && raw !== null && !Array.isArray(raw)) {\n const record = raw as Record<string, unknown>;\n if (record.tokens !== undefined) {\n if (!Array.isArray(record.tokens)) {\n throw new Error(`invalid token store at ${filePath}: tokens must be an array`);\n }\n return {\n store: {\n tokens: record.tokens.map((entry, index) => validateTokenEntry(entry, index)),\n },\n migratedLegacy: false,\n };\n }\n\n // Migrate legacy flat-map format: { \"connector\": \"token_value\", ... }\n const migrated: TokenEntry[] = [];\n for (const [key, value] of Object.entries(record)) {\n if (typeof value === \"string\" && value.length > 0) {\n migrated.push(\n validateTokenEntry(\n { token: value, connector: key, createdAt: new Date().toISOString() },\n migrated.length,\n ),\n );\n }\n }\n if (migrated.length > 0) {\n return { store: { tokens: migrated }, migratedLegacy: true };\n }\n }\n\n throw new Error(`invalid token store at ${filePath}: expected token array or legacy connector map`);\n}\n\nexport function loadTokenStore(tokensPath?: string): TokenStore {\n const p = resolveReadPath(tokensPath);\n try {\n const rawText = fs.readFileSync(p, \"utf8\");\n const { store, migratedLegacy } = parseTokenStore(rawText, p);\n if (migratedLegacy) {\n // Auto-migrate legacy flat-map stores in new format. This is best-effort:\n // a migration write failure must not hide the successfully parsed tokens.\n try {\n saveTokenStore(store, tokensPath);\n } catch {\n // Migration write failed (e.g., read-only fs) — still return parsed tokens.\n }\n }\n return store;\n } catch (error) {\n if (isEnoent(error)) {\n return { tokens: [] };\n }\n throw error;\n }\n}\n\nfunction fsyncDirectoryBestEffort(dirPath: string): void {\n let dirFd: number | null = null;\n try {\n dirFd = fs.openSync(dirPath, \"r\");\n fs.fsyncSync(dirFd);\n } catch {\n // Directory fsync is not supported on every platform/filesystem.\n } finally {\n if (dirFd !== null) {\n try { fs.closeSync(dirFd); } catch { /* ignore */ }\n }\n }\n}\n\nexport function saveTokenStore(store: TokenStore, tokensPath?: string): void {\n const p = tokensPath ?? defaultTokensPath();\n ensureDir(p);\n const validated: TokenStore = {\n tokens: store.tokens.map((entry, index) => validateTokenEntry(entry, index)),\n };\n const dir = path.dirname(p);\n const tmpPath = path.join(dir, `.${path.basename(p)}.${process.pid}.${randomUUID()}.tmp`);\n let fd: number | null = null;\n try {\n fd = fs.openSync(tmpPath, \"w\", 0o600);\n fs.writeFileSync(fd, JSON.stringify(validated, null, 2) + \"\\n\", \"utf8\");\n fs.fsyncSync(fd);\n fs.closeSync(fd);\n fd = null;\n fs.renameSync(tmpPath, p);\n // Tighten permissions on pre-existing files after atomic replacement.\n try { fs.chmodSync(p, 0o600); } catch { /* ignore on platforms without chmod */ }\n fsyncDirectoryBestEffort(dir);\n invalidateTokenCache();\n } catch (error) {\n if (fd !== null) {\n try { fs.closeSync(fd); } catch { /* ignore */ }\n }\n try { fs.rmSync(tmpPath, { force: true }); } catch { /* ignore cleanup failure */ }\n throw error;\n }\n}\n\n/**\n * Build a TokenEntry candidate WITHOUT saving it to the store.\n * Callers use this when they need to defer the save until after a\n * dependent write (e.g. Hermes config.yaml) succeeds — see\n * commitTokenEntry() to persist the candidate.\n */\nexport function buildTokenEntry(connector: string): TokenEntry {\n const prefix = TOKEN_PREFIXES[connector] ?? \"remnic_xx_\";\n const token = prefix + randomBytes(24).toString(\"hex\");\n return {\n token,\n connector,\n createdAt: new Date().toISOString(),\n };\n}\n\n/**\n * Persist a pre-built TokenEntry into the store, replacing any existing\n * entry for the same connector. Used together with buildTokenEntry() when\n * the caller wants to defer the save until after a dependent write succeeds.\n *\n * For transactional rollback, callers should snapshot the full store via\n * loadTokenStore() BEFORE calling commitTokenEntry() and restore it with\n * saveTokenStore() on failure. A full-store snapshot handles partial writes\n * of tokens.json atomically — single-entry restore via the return value is\n * insufficient because if this function throws during saveTokenStore, the\n * return statement never executes (UXJI/UXJT fix).\n */\nexport function commitTokenEntry(entry: TokenEntry, tokensPath?: string): void {\n const store = loadTokenStore(tokensPath);\n store.tokens = store.tokens.filter((t) => t.connector !== entry.connector);\n store.tokens.push(entry);\n saveTokenStore(store, tokensPath);\n}\n\nexport function generateToken(connector: string, tokensPath?: string): TokenEntry {\n const store = loadTokenStore(tokensPath);\n\n // Remove existing token for this connector\n store.tokens = store.tokens.filter((t) => t.connector !== connector);\n\n const entry = buildTokenEntry(connector);\n store.tokens.push(entry);\n saveTokenStore(store, tokensPath);\n return entry;\n}\n\nexport function listTokens(tokensPath?: string): TokenEntry[] {\n return loadTokenStore(tokensPath).tokens;\n}\n\nexport function revokeToken(connector: string, tokensPath?: string): boolean {\n const store = loadTokenStore(tokensPath);\n const before = store.tokens.length;\n store.tokens = store.tokens.filter((t) => t.connector !== connector);\n if (store.tokens.length < before) {\n saveTokenStore(store, tokensPath);\n return true;\n }\n return false;\n}\n\nexport function getAllValidTokens(tokensPath?: string): string[] {\n return loadTokenStore(tokensPath).tokens.map((t) => t.token);\n}\n\n// Cached token-entry snapshot to avoid synchronous disk I/O on every HTTP\n// request. Re-reads tokens.json at most once per TTL interval (default 5s).\n// There is deliberately exactly ONE cache: validation (token strings) and\n// identity (connector ids) are derived from the SAME snapshot, so a token\n// can never validate against a fresher snapshot than the one that resolves\n// its connector. saveTokenStore() invalidates on every mutation, so a\n// freshly minted or revoked token is coherent immediately.\nconst TOKEN_CACHE_TTL_MS = 5_000;\nlet _cachedEntries: TokenEntry[] = [];\nlet _cachedAt = 0;\nlet _cachedPath: string | undefined;\n\nfunction invalidateTokenCache(): void {\n _cachedEntries = [];\n _cachedAt = 0;\n _cachedPath = undefined;\n}\n\n/** Cached token-entry snapshot ({token, connector} pairs). */\nexport function getAllValidTokenEntriesCached(tokensPath?: string): readonly TokenEntry[] {\n const now = Date.now();\n if (now - _cachedAt < TOKEN_CACHE_TTL_MS && tokensPath === _cachedPath) return _cachedEntries;\n _cachedEntries = loadTokenStore(tokensPath).tokens;\n _cachedAt = now;\n _cachedPath = tokensPath;\n return _cachedEntries;\n}\n\nexport function getAllValidTokensCached(tokensPath?: string): string[] {\n return getAllValidTokenEntriesCached(tokensPath).map((entry) => entry.token);\n}\n\nexport function resolveConnectorFromToken(token: string, tokensPath?: string): string | undefined {\n return loadTokenStore(tokensPath).tokens.find((t) => t.token === token)?.connector;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -213,7 +213,7 @@ import {
|
|
|
213
213
|
} from "./chunk-OADWQ5CR.js";
|
|
214
214
|
import {
|
|
215
215
|
EngramAccessHttpServer
|
|
216
|
-
} from "./chunk-
|
|
216
|
+
} from "./chunk-SIPZ5UMK.js";
|
|
217
217
|
import {
|
|
218
218
|
WearablesInputError
|
|
219
219
|
} from "./chunk-7WV3F5DQ.js";
|
|
@@ -226,7 +226,7 @@ import {
|
|
|
226
226
|
createProductionChatLlmAdapter,
|
|
227
227
|
loadChatSession,
|
|
228
228
|
sessionBelongsToPrincipal
|
|
229
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-CNXMWYLA.js";
|
|
230
230
|
import {
|
|
231
231
|
EngramAccessService
|
|
232
232
|
} from "./chunk-OAREUC7N.js";
|
|
@@ -7520,4 +7520,4 @@ export {
|
|
|
7520
7520
|
listMemoryMarkdownFilePaths,
|
|
7521
7521
|
registerCli
|
|
7522
7522
|
};
|
|
7523
|
-
//# sourceMappingURL=chunk-
|
|
7523
|
+
//# sourceMappingURL=chunk-EVXI2I6G.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadTokenStore,
|
|
6
6
|
revokeToken,
|
|
7
7
|
saveTokenStore
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-E2SPGGUI.js";
|
|
9
9
|
import {
|
|
10
10
|
getConnectorsDir,
|
|
11
11
|
getRegistryPath
|
|
@@ -2257,4 +2257,4 @@ export {
|
|
|
2257
2257
|
resolveWeCloneProxyConfigPath,
|
|
2258
2258
|
buildWeCloneProxyConfig
|
|
2259
2259
|
};
|
|
2260
|
-
//# sourceMappingURL=chunk-
|
|
2260
|
+
//# sourceMappingURL=chunk-QMN3CIFS.js.map
|
|
@@ -6,12 +6,13 @@ import {
|
|
|
6
6
|
} from "./chunk-JBPKEARU.js";
|
|
7
7
|
import {
|
|
8
8
|
EngramMcpServer,
|
|
9
|
+
MCP_SUPPORTED_PROTOCOL_VERSIONS,
|
|
9
10
|
cleanupExpiredChatSessions,
|
|
10
11
|
loadChatSession,
|
|
11
12
|
processChatMessage,
|
|
12
13
|
sessionBelongsToPrincipal,
|
|
13
14
|
subscribeChatTranscript
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CNXMWYLA.js";
|
|
15
16
|
import {
|
|
16
17
|
validateRequest
|
|
17
18
|
} from "./chunk-2MR3MFQB.js";
|
|
@@ -272,6 +273,23 @@ function parseHttpServerPort(port) {
|
|
|
272
273
|
}
|
|
273
274
|
return port;
|
|
274
275
|
}
|
|
276
|
+
function assertResourceMetadataUrl(value) {
|
|
277
|
+
if (value === void 0) return void 0;
|
|
278
|
+
let parsed;
|
|
279
|
+
try {
|
|
280
|
+
parsed = new URL(value);
|
|
281
|
+
} catch {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`access HTTP resourceMetadataUrl must be an absolute http(s) URL, got: ${value}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`access HTTP resourceMetadataUrl must use http or https, got: ${parsed.protocol}`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
275
293
|
function parseTrustZoneKindFilter(raw) {
|
|
276
294
|
if (raw === null) return void 0;
|
|
277
295
|
if (TRUST_ZONE_RECORD_KINDS.includes(raw)) {
|
|
@@ -343,6 +361,8 @@ var EngramAccessHttpServer = class {
|
|
|
343
361
|
authToken;
|
|
344
362
|
authTokens;
|
|
345
363
|
authTokensGetter;
|
|
364
|
+
authTokenEntriesGetter;
|
|
365
|
+
tokenPathPolicy;
|
|
346
366
|
authenticatedPrincipal;
|
|
347
367
|
maxBodyBytes;
|
|
348
368
|
adminConsoleEnabled;
|
|
@@ -352,6 +372,8 @@ var EngramAccessHttpServer = class {
|
|
|
352
372
|
trustPrincipalHeader;
|
|
353
373
|
adapterRegistry;
|
|
354
374
|
readiness;
|
|
375
|
+
resourceMetadataUrl;
|
|
376
|
+
externalRequestHandler;
|
|
355
377
|
writeRequestTimestamps = [];
|
|
356
378
|
mcpServer;
|
|
357
379
|
server = null;
|
|
@@ -381,6 +403,8 @@ var EngramAccessHttpServer = class {
|
|
|
381
403
|
this.authToken = options.authToken?.trim() || void 0;
|
|
382
404
|
this.authTokens = (options.authTokens ?? []).map((t) => t.trim()).filter(Boolean);
|
|
383
405
|
this.authTokensGetter = options.authTokensGetter;
|
|
406
|
+
this.authTokenEntriesGetter = options.authTokenEntriesGetter;
|
|
407
|
+
this.tokenPathPolicy = options.tokenPathPolicy;
|
|
384
408
|
this.authenticatedPrincipal = options.principal?.trim() || void 0;
|
|
385
409
|
this.maxBodyBytes = Number.isFinite(options.maxBodyBytes) ? Math.max(1, Math.floor(options.maxBodyBytes ?? 131072)) : 131072;
|
|
386
410
|
this.adminConsoleEnabled = options.adminConsoleEnabled !== false;
|
|
@@ -389,6 +413,8 @@ var EngramAccessHttpServer = class {
|
|
|
389
413
|
this.adminControls = options.adminControls;
|
|
390
414
|
this.trustPrincipalHeader = options.trustPrincipalHeader === true;
|
|
391
415
|
this.readiness = options.readiness ?? (() => ({ ready: true, warmupAttempts: 0 }));
|
|
416
|
+
this.resourceMetadataUrl = assertResourceMetadataUrl(options.resourceMetadataUrl);
|
|
417
|
+
this.externalRequestHandler = options.externalRequestHandler;
|
|
392
418
|
this.adapterRegistry = options.enableAdapters !== false ? options.adapterRegistry ?? new AdapterRegistry() : null;
|
|
393
419
|
this.mcpServer = new EngramMcpServer(this.service, {
|
|
394
420
|
principal: options.principal,
|
|
@@ -404,7 +430,7 @@ var EngramAccessHttpServer = class {
|
|
|
404
430
|
});
|
|
405
431
|
}
|
|
406
432
|
async start() {
|
|
407
|
-
if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter) {
|
|
433
|
+
if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter && !this.authTokenEntriesGetter) {
|
|
408
434
|
throw new Error("engram access HTTP requires authToken or authTokens");
|
|
409
435
|
}
|
|
410
436
|
if (this.server) return this.status();
|
|
@@ -647,11 +673,27 @@ var EngramAccessHttpServer = class {
|
|
|
647
673
|
return;
|
|
648
674
|
}
|
|
649
675
|
}
|
|
676
|
+
if (this.externalRequestHandler) {
|
|
677
|
+
const authorized = this.isAuthorized(req, pathname);
|
|
678
|
+
if (await this.externalRequestHandler(req, res, { authorized })) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
650
682
|
if (!this.isAuthorized(req, pathname)) {
|
|
651
683
|
const body = JSON.stringify({ error: "unauthorized", code: "unauthorized" });
|
|
652
684
|
res.writeHead(401, {
|
|
653
685
|
"content-type": "application/json; charset=utf-8",
|
|
654
|
-
"www-authenticate":
|
|
686
|
+
"www-authenticate": this.bearerChallenge(),
|
|
687
|
+
"x-request-id": correlationId
|
|
688
|
+
});
|
|
689
|
+
res.end(body);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (pathname === "/mcp" && (req.method === "GET" || req.method === "DELETE")) {
|
|
693
|
+
const body = JSON.stringify({ error: "method_not_allowed", code: "method_not_allowed" });
|
|
694
|
+
res.writeHead(405, {
|
|
695
|
+
"content-type": "application/json; charset=utf-8",
|
|
696
|
+
allow: "POST",
|
|
655
697
|
"x-request-id": correlationId
|
|
656
698
|
});
|
|
657
699
|
res.end(body);
|
|
@@ -2253,6 +2295,20 @@ var EngramAccessHttpServer = class {
|
|
|
2253
2295
|
req.once("error", cleanup);
|
|
2254
2296
|
}
|
|
2255
2297
|
async handleMcpRequest(req, res) {
|
|
2298
|
+
const headerVersion = req.headers["mcp-protocol-version"];
|
|
2299
|
+
if (typeof headerVersion === "string" && headerVersion.length > 0) {
|
|
2300
|
+
if (!MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(headerVersion)) {
|
|
2301
|
+
this.respondJson(res, 400, {
|
|
2302
|
+
jsonrpc: "2.0",
|
|
2303
|
+
id: null,
|
|
2304
|
+
error: {
|
|
2305
|
+
code: -32e3,
|
|
2306
|
+
message: `unsupported MCP-Protocol-Version: ${headerVersion}; supported: ${MCP_SUPPORTED_PROTOCOL_VERSIONS.join(", ")}`
|
|
2307
|
+
}
|
|
2308
|
+
});
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2256
2312
|
const body = await this.readJsonBody(req);
|
|
2257
2313
|
const request = body;
|
|
2258
2314
|
const toolName = typeof request.params?.name === "string" ? request.params.name : "";
|
|
@@ -2552,8 +2608,23 @@ var EngramAccessHttpServer = class {
|
|
|
2552
2608
|
}
|
|
2553
2609
|
return result.data;
|
|
2554
2610
|
}
|
|
2611
|
+
/**
|
|
2612
|
+
* Build the WWW-Authenticate challenge string for 401 responses.
|
|
2613
|
+
* When `resourceMetadataUrl` is configured, includes the RFC 9728
|
|
2614
|
+
* `resource_metadata` parameter so MCP clients (e.g. ChatGPT) can
|
|
2615
|
+
* discover the OAuth 2.0 protected-resource metadata document.
|
|
2616
|
+
* Otherwise the bare `Bearer` challenge is returned (unchanged).
|
|
2617
|
+
*/
|
|
2618
|
+
bearerChallenge() {
|
|
2619
|
+
if (this.resourceMetadataUrl) {
|
|
2620
|
+
return `Bearer resource_metadata="${this.resourceMetadataUrl}"`;
|
|
2621
|
+
}
|
|
2622
|
+
return "Bearer";
|
|
2623
|
+
}
|
|
2555
2624
|
isAuthorized(req, pathname) {
|
|
2556
|
-
if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter)
|
|
2625
|
+
if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter && !this.authTokenEntriesGetter) {
|
|
2626
|
+
return false;
|
|
2627
|
+
}
|
|
2557
2628
|
const raw = req.headers.authorization;
|
|
2558
2629
|
let candidate = null;
|
|
2559
2630
|
if (raw) {
|
|
@@ -2581,6 +2652,15 @@ var EngramAccessHttpServer = class {
|
|
|
2581
2652
|
for (const valid of this.authTokens) {
|
|
2582
2653
|
if (this.timingSafeStringEqual(token, valid)) return true;
|
|
2583
2654
|
}
|
|
2655
|
+
if (this.authTokenEntriesGetter) {
|
|
2656
|
+
for (const entry of this.authTokenEntriesGetter()) {
|
|
2657
|
+
if (!this.timingSafeStringEqual(token, entry.token)) continue;
|
|
2658
|
+
if (!this.tokenPathPolicy) return true;
|
|
2659
|
+
if (typeof entry.connector !== "string" || entry.connector.length === 0) return false;
|
|
2660
|
+
return this.tokenPathPolicy(entry.connector, pathname);
|
|
2661
|
+
}
|
|
2662
|
+
return false;
|
|
2663
|
+
}
|
|
2584
2664
|
if (this.authTokensGetter) {
|
|
2585
2665
|
for (const valid of this.authTokensGetter()) {
|
|
2586
2666
|
if (this.timingSafeStringEqual(token, valid)) return true;
|
|
@@ -2664,4 +2744,4 @@ function positiveIntQueryParam(value, label) {
|
|
|
2664
2744
|
export {
|
|
2665
2745
|
EngramAccessHttpServer
|
|
2666
2746
|
};
|
|
2667
|
-
//# sourceMappingURL=chunk-
|
|
2747
|
+
//# sourceMappingURL=chunk-SIPZ5UMK.js.map
|