@geminixiang/mama 0.2.0-beta.1 → 0.2.0-beta.2
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/README.md +85 -58
- package/dist/adapter.d.ts +8 -6
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +2 -2
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +20 -29
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +16 -20
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +11 -4
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +199 -73
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +27 -30
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +4 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +130 -71
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +9 -95
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/html.d.ts +3 -0
- package/dist/adapters/telegram/html.d.ts.map +1 -0
- package/dist/adapters/telegram/html.js +98 -0
- package/dist/adapters/telegram/html.js.map +1 -0
- package/dist/agent.d.ts +3 -11
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +63 -70
- package/dist/agent.js.map +1 -1
- package/dist/bindings.d.ts +1 -20
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js +1 -21
- package/dist/bindings.js.map +1 -1
- package/dist/config.d.ts +7 -27
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +77 -63
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +11 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +33 -13
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +1 -3
- package/dist/execution-resolver.js.map +1 -1
- package/dist/instrument.d.ts.map +1 -1
- package/dist/instrument.js +5 -11
- package/dist/instrument.js.map +1 -1
- package/dist/link-server.d.ts +2 -1
- package/dist/link-server.d.ts.map +1 -1
- package/dist/link-server.js +62 -2
- package/dist/link-server.js.map +1 -1
- package/dist/login.d.ts +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +1 -1
- package/dist/login.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +96 -112
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +0 -41
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +0 -45
- package/dist/provisioner.js.map +1 -1
- package/dist/sandbox/host.d.ts +0 -2
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +1 -5
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +2 -0
- package/dist/sentry.js.map +1 -1
- package/dist/session-store.d.ts +1 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +5 -9
- package/dist/session-store.js.map +1 -1
- package/dist/tools/event.d.ts +1 -0
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +6 -5
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/ui-copy.d.ts +1 -0
- package/dist/ui-copy.d.ts.map +1 -1
- package/dist/ui-copy.js +3 -0
- package/dist/ui-copy.js.map +1 -1
- package/dist/vault-routing.d.ts +1 -2
- package/dist/vault-routing.d.ts.map +1 -1
- package/dist/vault-routing.js +1 -7
- package/dist/vault-routing.js.map +1 -1
- package/package.json +1 -1
- package/dist/vault.test.d.ts +0 -2
- package/dist/vault.test.d.ts.map +0 -1
- package/dist/vault.test.js +0 -67
- package/dist/vault.test.js.map +0 -1
package/dist/link-server.js
CHANGED
|
@@ -39,6 +39,7 @@ export function startLinkServer(port, linkTokenStore, vaultManager, notify) {
|
|
|
39
39
|
: undefined;
|
|
40
40
|
const oauthServices = getOAuthServices();
|
|
41
41
|
const defaultMode = oauthServiceHint ? "oauth" : "api_key";
|
|
42
|
+
const existingSecrets = describeVaultSecrets(vaultManager, linkToken.vaultId);
|
|
42
43
|
const title = oauthServiceHint ? `${oauthServiceHint.label} OAuth` : "Store Secret";
|
|
43
44
|
const helpText = oauthServiceHint
|
|
44
45
|
? `Authorize ${oauthServiceHint.label} and store tokens in your vault.`
|
|
@@ -47,7 +48,7 @@ export function startLinkServer(port, linkTokenStore, vaultManager, notify) {
|
|
|
47
48
|
const placeholder = "sk-...";
|
|
48
49
|
const initialEnvKey = "";
|
|
49
50
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
50
|
-
res.end(renderCredentialPage(rawToken, title, defaultMode, initialEnvKey, secretLabel, placeholder, helpText, oauthServices, oauthServiceHint?.id));
|
|
51
|
+
res.end(renderCredentialPage(rawToken, title, defaultMode, initialEnvKey, secretLabel, placeholder, helpText, oauthServices, oauthServiceHint?.id, existingSecrets));
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
54
|
if (req.method === "POST" && url.pathname === "/api/link/complete") {
|
|
@@ -93,6 +94,7 @@ export function startLinkServer(port, linkTokenStore, vaultManager, notify) {
|
|
|
93
94
|
server.on("error", (err) => {
|
|
94
95
|
log.logWarning("Link server error", err.message);
|
|
95
96
|
});
|
|
97
|
+
return server;
|
|
96
98
|
}
|
|
97
99
|
/**
|
|
98
100
|
* Resolve the externally-visible base URL of this server.
|
|
@@ -394,6 +396,33 @@ const sharedPageStyles = `
|
|
|
394
396
|
color: var(--err-text);
|
|
395
397
|
}
|
|
396
398
|
|
|
399
|
+
.secrets-summary {
|
|
400
|
+
margin-top: 18px;
|
|
401
|
+
padding: 14px 16px;
|
|
402
|
+
border: 1px solid var(--panel-border);
|
|
403
|
+
border-radius: 14px;
|
|
404
|
+
background: rgba(255, 255, 255, 0.72);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.secrets-summary h2 {
|
|
408
|
+
margin: 0 0 8px;
|
|
409
|
+
font-size: 0.98rem;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.secrets-summary p {
|
|
413
|
+
font-size: 0.92rem;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.secrets-summary ul {
|
|
417
|
+
margin: 10px 0 0;
|
|
418
|
+
padding-left: 18px;
|
|
419
|
+
color: var(--text);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.secrets-summary li + li {
|
|
423
|
+
margin-top: 6px;
|
|
424
|
+
}
|
|
425
|
+
|
|
397
426
|
.close-note {
|
|
398
427
|
margin-top: 14px;
|
|
399
428
|
font-size: 0.92rem;
|
|
@@ -437,7 +466,37 @@ function renderStatusPage(title, message, tone, options) {
|
|
|
437
466
|
${closeNote}
|
|
438
467
|
</div>`);
|
|
439
468
|
}
|
|
440
|
-
function
|
|
469
|
+
function describeVaultSecrets(vaultManager, vaultId) {
|
|
470
|
+
const vault = vaultManager.resolve(vaultId);
|
|
471
|
+
if (!vault) {
|
|
472
|
+
return { envKeys: [], mountTargets: [] };
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
envKeys: Object.keys(vault.env).sort((left, right) => left.localeCompare(right)),
|
|
476
|
+
mountTargets: [...new Set(vault.mounts.map((mount) => mount.target))].sort((left, right) => left.localeCompare(right)),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function renderSecretsSummary(summary) {
|
|
480
|
+
if (summary.envKeys.length === 0 && summary.mountTargets.length === 0) {
|
|
481
|
+
return `
|
|
482
|
+
<section class="secrets-summary">
|
|
483
|
+
<h2>Currently stored</h2>
|
|
484
|
+
<p>No secrets are stored in this vault yet.</p>
|
|
485
|
+
</section>`;
|
|
486
|
+
}
|
|
487
|
+
const envItems = summary.envKeys.map((envKey) => `<li><code>${esc(envKey)}</code></li>`).join("");
|
|
488
|
+
const mountItems = summary.mountTargets
|
|
489
|
+
.map((target) => `<li><code>${esc(target)}</code></li>`)
|
|
490
|
+
.join("");
|
|
491
|
+
return `
|
|
492
|
+
<section class="secrets-summary">
|
|
493
|
+
<h2>Currently stored</h2>
|
|
494
|
+
<p>Only secret names and mounted paths are shown here. Secret values are never displayed.</p>
|
|
495
|
+
${summary.envKeys.length > 0 ? `<p><strong>Environment keys</strong></p><ul>${envItems}</ul>` : ""}
|
|
496
|
+
${summary.mountTargets.length > 0 ? `<p><strong>Mounted secret files</strong></p><ul>${mountItems}</ul>` : ""}
|
|
497
|
+
</section>`;
|
|
498
|
+
}
|
|
499
|
+
function renderCredentialPage(token, title, defaultMode, initialEnvKey, secretLabel, placeholder, helpText, oauthServices, oauthServiceIdHint, existingSecrets) {
|
|
441
500
|
const oauthOptions = oauthServices
|
|
442
501
|
.map((service) => {
|
|
443
502
|
const selected = service.id === oauthServiceIdHint ? ' selected="selected"' : "";
|
|
@@ -449,6 +508,7 @@ function renderCredentialPage(token, title, defaultMode, initialEnvKey, secretLa
|
|
|
449
508
|
<h1>${esc(title)}</h1>
|
|
450
509
|
<p>Your personal sandbox is already provisioned automatically.</p>
|
|
451
510
|
<p>${esc(helpText)}</p>
|
|
511
|
+
${renderSecretsSummary(existingSecrets)}
|
|
452
512
|
<div class="mode">
|
|
453
513
|
<label><input type="radio" name="mode" value="api_key" ${defaultMode === "api_key" ? "checked" : ""}> API key</label>
|
|
454
514
|
<label><input type="radio" name="mode" value="oauth" ${defaultMode === "oauth" ? "checked" : ""}> OAuth login</label>
|
package/dist/link-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-server.js","sourceRoot":"","sources":["../src/link-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from \"crypto\";\nimport { createServer, type IncomingMessage, type ServerResponse } from \"http\";\nimport { resolveLinkBaseUrl } from \"./config.js\";\nimport type { InMemoryLinkTokenStore } from \"./link-token.js\";\nimport {\n getOAuthServices,\n resolveOAuthService,\n type LoginCredentialKind,\n type OAuthService,\n} from \"./login.js\";\nimport * as log from \"./log.js\";\nimport { PRODUCT_NAME } from \"./ui-copy.js\";\nimport { defaultVaultTargetPath, type VaultManager } from \"./vault.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\n/** Called after a binding is written, to notify the user in chat */\nexport type NotifyFn = (platform: string, conversationId: string, message: string) => Promise<void>;\n\ninterface LinkCompleteBody {\n token: string;\n mode?: LoginCredentialKind;\n envKey?: string;\n credential?: string;\n}\n\ninterface OAuthStartBody {\n token: string;\n serviceId: string;\n}\n\ninterface PendingOAuthState {\n linkToken: string;\n serviceId: string;\n codeVerifier: string;\n expiresAt: number;\n}\n\nconst OAUTH_STATE_TTL_MS = 10 * 60 * 1000;\n\n// ── startLinkServer ────────────────────────────────────────────────────────────\n\n/**\n * Start a small HTTP server that receives credential onboarding callbacks from the web portal.\n *\n * Routes:\n * GET /health — health check\n * GET /link?token=xxx — credential onboarding page\n * POST /api/link/complete — API key completion endpoint\n * POST /api/oauth/start — creates provider OAuth redirect URL\n * GET /oauth/callback — OAuth callback endpoint\n */\nexport function startLinkServer(\n port: number,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n): void {\n const oauthStates = new Map<string, PendingOAuthState>();\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? \"/\", requestBaseUrl(req));\n\n if (req.method === \"GET\" && url.pathname === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === \"/link\") {\n const rawToken = url.searchParams.get(\"token\") ?? \"\";\n const linkToken = linkTokenStore.peek(rawToken);\n\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"This link is invalid or has expired. Ask the bot for a new /login link.\",\n ),\n );\n return;\n }\n\n const oauthServiceHint = linkToken.providerId\n ? resolveOAuthService(linkToken.providerId)\n : undefined;\n const oauthServices = getOAuthServices();\n const defaultMode: LoginCredentialKind = oauthServiceHint ? \"oauth\" : \"api_key\";\n\n const title = oauthServiceHint ? `${oauthServiceHint.label} OAuth` : \"Store Secret\";\n const helpText = oauthServiceHint\n ? `Authorize ${oauthServiceHint.label} and store tokens in your vault.`\n : \"Set any environment variable key/value pair in your vault.\";\n const secretLabel = \"Secret value\";\n const placeholder = \"sk-...\";\n const initialEnvKey = \"\";\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderCredentialPage(\n rawToken,\n title,\n defaultMode,\n initialEnvKey,\n secretLabel,\n placeholder,\n helpText,\n oauthServices,\n oauthServiceHint?.id,\n ),\n );\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/api/link/complete\") {\n if (!enforceCsrf(req, res)) return;\n void readJsonBody(req, res, async (body) => {\n await handleLinkComplete(body, linkTokenStore, vaultManager, notify, res);\n });\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/api/oauth/start\") {\n if (!enforceCsrf(req, res)) return;\n void readJsonBody(req, res, async (body) => {\n await handleOAuthStart(body, req, linkTokenStore, oauthStates, res);\n });\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === \"/oauth/callback\") {\n void handleOAuthCallback(\n url,\n req,\n linkTokenStore,\n vaultManager,\n notify,\n oauthStates,\n res,\n ).catch((err: Error) => {\n log.logWarning(\"OAuth callback failed\", err.message);\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth callback failed. Please retry /login.\"));\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n // Bind to loopback when MOM_LINK_URL is unset so the credential UI and OAuth\n // callbacks are not exposed on public interfaces by default. Production\n // deployments set MOM_LINK_URL and are expected to front this server with a\n // reverse proxy, which can still reach it via 0.0.0.0.\n const bindHost = resolveLinkBaseUrl() ? undefined : \"127.0.0.1\";\n server.listen(port, bindHost, () => {\n log.logInfo(`Link callback server listening on ${bindHost ?? \"0.0.0.0\"}:${port}`);\n if (!resolveLinkBaseUrl()) {\n log.logWarning(\n \"MOM_LINK_URL is not set — bound to 127.0.0.1 and OAuth redirect_uri will be \" +\n \"derived from request headers (Host / X-Forwarded-*). Set \" +\n \"MOM_LINK_URL=https://your-host.example.com for production.\",\n );\n }\n });\n\n server.on(\"error\", (err) => {\n log.logWarning(\"Link server error\", err.message);\n });\n}\n\n/**\n * Resolve the externally-visible base URL of this server.\n *\n * Prefers MOM_LINK_URL (see config.ts) so the OAuth `redirect_uri` is\n * deterministic and not influenced by attacker-controlled request headers.\n * Falls back to Host / X-Forwarded-* only when no base URL is configured\n * — intended for local development.\n */\nfunction requestBaseUrl(req: IncomingMessage): string {\n const configured = resolveLinkBaseUrl();\n if (configured) return configured;\n\n const protoRaw = (req.headers[\"x-forwarded-proto\"] as string | undefined)?.split(\",\")[0]?.trim();\n const proto = protoRaw || \"http\";\n const host =\n ((req.headers[\"x-forwarded-host\"] as string | undefined)?.split(\",\")[0]?.trim() ??\n req.headers.host ??\n `localhost`) ||\n `localhost`;\n return `${proto}://${host}`;\n}\n\n/**\n * Block cross-site POSTs to the credential endpoints. Two defenses:\n * 1. Require Content-Type: application/json, which forces a CORS preflight\n * for any cross-origin fetch and rules out `<form enctype=\"text/plain\">`\n * tricks that could otherwise smuggle a JSON body.\n * 2. When MOM_LINK_URL is configured, require that the Origin (or Referer,\n * as a fallback for browsers that strip Origin) matches that base URL.\n * This stops an attacker-controlled page — even one that somehow stole a\n * victim's link token — from completing the flow.\n */\nfunction enforceCsrf(req: IncomingMessage, res: ServerResponse): boolean {\n const contentType = (req.headers[\"content-type\"] as string | undefined)\n ?.split(\";\")[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== \"application/json\") {\n res.writeHead(415, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Content-Type must be application/json\" }));\n return false;\n }\n\n const configured = resolveLinkBaseUrl();\n if (!configured) {\n // No trusted origin to compare against in local/dev mode; the loopback\n // bind already prevents cross-host access.\n return true;\n }\n\n let configuredOrigin: string;\n try {\n configuredOrigin = new URL(configured).origin;\n } catch {\n // Misconfigured MOM_LINK_URL — fail closed.\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Server misconfiguration\" }));\n return false;\n }\n\n if (requestOrigin(req) !== configuredOrigin) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Cross-origin request rejected\" }));\n return false;\n }\n\n return true;\n}\n\n/** Best-effort origin of the request, derived from Origin or Referer. */\nfunction requestOrigin(req: IncomingMessage): string | undefined {\n const origin = (req.headers.origin as string | undefined)?.trim();\n if (origin && origin !== \"null\") return origin;\n\n const referer = (req.headers.referer as string | undefined)?.trim();\n if (!referer) return undefined;\n try {\n return new URL(referer).origin;\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(\n req: IncomingMessage,\n res: ServerResponse,\n onBody: (body: string) => Promise<void>,\n): Promise<void> {\n let body = \"\";\n let bodyTooLarge = false;\n\n req.on(\"data\", (chunk: Buffer) => {\n if (bodyTooLarge) return;\n body += chunk.toString();\n if (body.length > 16 * 1024) {\n bodyTooLarge = true;\n res.writeHead(413);\n res.end();\n req.destroy();\n }\n });\n\n req.on(\"end\", async () => {\n if (bodyTooLarge) return;\n await onBody(body);\n });\n}\n\n// ── HTML helpers ───────────────────────────────────────────────────────────────\n\nfunction esc(s: string): string {\n return s.replace(\n /[&<>\"']/g,\n (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[c]!,\n );\n}\n\nconst sharedPageStyles = `\n :root {\n color-scheme: light;\n --bg: #f5f1e8;\n --panel: rgba(255, 255, 255, 0.9);\n --panel-border: rgba(28, 30, 33, 0.08);\n --text: #1c1e21;\n --muted: #5d5f64;\n --button: #1c1e21;\n --button-hover: #2c3035;\n --button-disabled: #8f949b;\n --field-border: #c9cfd6;\n --field-focus: #1c1e21;\n --ok-bg: #dff4e4;\n --ok-text: #1f5b34;\n --err-bg: #fde2e2;\n --err-text: #8a2f2f;\n }\n\n * { box-sizing: border-box; }\n\n body {\n margin: 0;\n min-height: 100vh;\n padding: 32px 20px;\n display: grid;\n place-items: center;\n background:\n radial-gradient(circle at top, rgba(255, 255, 255, 0.7), transparent 45%),\n linear-gradient(180deg, #faf7f0 0%, var(--bg) 100%);\n color: var(--text);\n font-family:\n \"SF Pro Text\",\n \"Segoe UI\",\n system-ui,\n sans-serif;\n }\n\n .shell {\n width: min(100%, 560px);\n }\n\n .card {\n padding: 28px;\n border: 1px solid var(--panel-border);\n border-radius: 20px;\n background: var(--panel);\n box-shadow: 0 18px 48px rgba(28, 30, 33, 0.08);\n backdrop-filter: blur(8px);\n }\n\n .eyebrow {\n margin: 0 0 10px;\n color: var(--muted);\n font-size: 0.82rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n h1 {\n margin: 0 0 10px;\n font-size: clamp(1.5rem, 2vw, 1.8rem);\n line-height: 1.15;\n text-wrap: balance;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n font-size: 0.98rem;\n line-height: 1.5;\n }\n\n .stack > * + * {\n margin-top: 14px;\n }\n\n .form {\n margin-top: 24px;\n }\n\n .form > * + * {\n margin-top: 18px;\n }\n\n label {\n display: block;\n margin-bottom: 6px;\n font-size: 0.92rem;\n font-weight: 650;\n }\n\n input,\n select,\n button {\n font: inherit;\n }\n\n input,\n select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--field-border);\n border-radius: 12px;\n background: #fff;\n color: var(--text);\n }\n\n input:focus-visible,\n select:focus-visible,\n button:focus-visible {\n outline: 2px solid var(--field-focus);\n outline-offset: 2px;\n }\n\n button {\n width: 100%;\n margin-top: 24px;\n padding: 13px 18px;\n border: none;\n border-radius: 12px;\n background: var(--button);\n color: #fff;\n cursor: pointer;\n transition: background-color 160ms ease;\n }\n\n button:hover {\n background: var(--button-hover);\n }\n\n button:disabled {\n background: var(--button-disabled);\n cursor: default;\n }\n\n .mode {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n margin-top: 22px;\n }\n\n .mode label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 10px 12px;\n border: 1px solid var(--field-border);\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.85);\n font-weight: 500;\n }\n\n .mode input {\n width: auto;\n margin: 0;\n }\n\n .panel {\n display: none;\n }\n\n .panel.active {\n display: block;\n }\n\n .panel-note {\n margin-top: 10px;\n font-size: 0.92rem;\n }\n\n .result,\n .status {\n margin-top: 20px;\n padding: 14px 16px;\n border-radius: 14px;\n font-size: 0.95rem;\n }\n\n .result {\n display: none;\n }\n\n .result.ok,\n .status.ok {\n background: var(--ok-bg);\n color: var(--ok-text);\n }\n\n .result.err,\n .status.err {\n background: var(--err-bg);\n color: var(--err-text);\n }\n\n .close-note {\n margin-top: 14px;\n font-size: 0.92rem;\n }\n\n @media (max-width: 640px) {\n body {\n padding: 20px 14px;\n }\n\n .card {\n padding: 22px;\n border-radius: 16px;\n }\n }\n`;\n\nfunction renderPageDocument(title: string, body: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${esc(title)} — ${PRODUCT_NAME}</title>\n <style>${sharedPageStyles}</style>\n</head>\n<body>\n <main class=\"shell\">\n <section class=\"card\">\n ${body}\n </section>\n </main>\n</body>\n</html>`;\n}\n\nfunction renderStatusPage(\n title: string,\n message: string,\n tone: \"ok\" | \"err\",\n options?: { closeNote?: boolean },\n): string {\n const closeNote = options?.closeNote ? '<p class=\"close-note\">You can close this tab.</p>' : \"\";\n return renderPageDocument(\n title,\n `<div class=\"stack\">\n <p class=\"eyebrow\">${PRODUCT_NAME}</p>\n <h1>${esc(title)}</h1>\n <div class=\"status ${tone}\">${esc(message)}</div>\n ${closeNote}\n </div>`,\n );\n}\n\nfunction renderCredentialPage(\n token: string,\n title: string,\n defaultMode: LoginCredentialKind,\n initialEnvKey: string,\n secretLabel: string,\n placeholder: string,\n helpText: string,\n oauthServices: OAuthService[],\n oauthServiceIdHint?: string,\n): string {\n const oauthOptions = oauthServices\n .map((service) => {\n const selected = service.id === oauthServiceIdHint ? ' selected=\"selected\"' : \"\";\n return `<option value=\"${esc(service.id)}\"${selected}>${esc(service.label)}</option>`;\n })\n .join(\"\\n\");\n\n return renderPageDocument(\n \"Login\",\n `<div class=\"stack\">\n <p class=\"eyebrow\">${PRODUCT_NAME}</p>\n <h1>${esc(title)}</h1>\n <p>Your personal sandbox is already provisioned automatically.</p>\n <p>${esc(helpText)}</p>\n <div class=\"mode\">\n <label><input type=\"radio\" name=\"mode\" value=\"api_key\" ${defaultMode === \"api_key\" ? \"checked\" : \"\"}> API key</label>\n <label><input type=\"radio\" name=\"mode\" value=\"oauth\" ${defaultMode === \"oauth\" ? \"checked\" : \"\"}> OAuth login</label>\n </div>\n\n <div class=\"form\">\n <div id=\"api-panel\" class=\"panel\">\n <label for=\"envKey\">Environment key</label>\n <input id=\"envKey\" type=\"text\" name=\"envKey\" placeholder=\"OPENAI_API_KEY\" value=\"${esc(initialEnvKey)}\" autocomplete=\"off\">\n <label for=\"credential\">${esc(secretLabel)}</label>\n <input id=\"credential\" type=\"password\" name=\"credential\" placeholder=\"${esc(placeholder)}\" autocomplete=\"off\">\n </div>\n\n <div id=\"oauth-panel\" class=\"panel\">\n <label for=\"oauthService\">OAuth service</label>\n <select id=\"oauthService\" name=\"oauthService\">${oauthOptions}</select>\n <p class=\"panel-note\">You'll be redirected to the selected service's authorization page.</p>\n </div>\n\n <button id=\"btn\" onclick=\"connect()\">Continue</button>\n <div id=\"result\" class=\"result\" aria-live=\"polite\"></div>\n </div>\n <script>\n const envKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n function selectedMode() {\n return document.querySelector('input[name=\"mode\"]:checked').value;\n }\n\n function showResult(message, ok) {\n const result = document.getElementById('result');\n result.style.display = 'block';\n result.className = ok ? 'result ok' : 'result err';\n result.textContent = message;\n }\n\n function syncPanels() {\n const api = document.getElementById('api-panel');\n const oauth = document.getElementById('oauth-panel');\n const mode = selectedMode();\n api.className = mode === 'api_key' ? 'panel active' : 'panel';\n oauth.className = mode === 'oauth' ? 'panel active' : 'panel';\n }\n\n for (const radio of document.querySelectorAll('input[name=\"mode\"]')) {\n radio.addEventListener('change', syncPanels);\n }\n\n syncPanels();\n\n async function connect() {\n const btn = document.getElementById('btn');\n const mode = selectedMode();\n btn.disabled = true;\n btn.textContent = mode === 'oauth' ? 'Redirecting…' : 'Saving…';\n\n try {\n if (mode === 'oauth') {\n const serviceId = document.getElementById('oauthService').value;\n const r = await fetch('/api/oauth/start', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: '${esc(token)}', serviceId }),\n });\n const data = await r.json();\n if (!r.ok) {\n showResult('Error: ' + (data.error ?? r.status), false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n window.location.href = data.redirectUrl;\n return;\n }\n\n const envKey = document.getElementById('envKey').value.trim();\n const credential = document.getElementById('credential').value.trim();\n if (!envKeyPattern.test(envKey)) {\n showResult('Please enter a valid environment key.', false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n if (!credential) {\n showResult('Please enter a value.', false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n\n const r = await fetch('/api/link/complete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: '${esc(token)}', mode: 'api_key', envKey, credential }),\n });\n const data = await r.json();\n if (r.ok) {\n showResult(data.message ?? 'Credential stored. You can close this tab.', true);\n btn.style.display = 'none';\n for (const input of document.querySelectorAll('input,select')) input.disabled = true;\n } else {\n showResult('Error: ' + (data.error ?? r.status), false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n }\n } catch (err) {\n showResult('Network error: ' + err.message, false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n }\n }\n </script>\n</div>`,\n );\n}\n\nfunction renderErrorPage(message: string): string {\n return renderStatusPage(\"Login Error\", message, \"err\");\n}\n\nfunction renderSuccessPage(message: string): string {\n return renderStatusPage(\"Connected\", message, \"ok\", { closeNote: true });\n}\n\n// ── API-key completion ────────────────────────────────────────────────────────\n\nasync function handleLinkComplete(\n body: string,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n res: ServerResponse,\n): Promise<void> {\n let data: Partial<LinkCompleteBody>;\n try {\n data = JSON.parse(body) as Partial<LinkCompleteBody>;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return;\n }\n\n if (!data.token) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required field: token\" }));\n return;\n }\n\n const envKey = data.envKey?.trim() ?? \"\";\n const credential = data.credential?.trim() ?? \"\";\n\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid envKey format\" }));\n return;\n }\n\n if (!credential) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required field: credential\" }));\n return;\n }\n\n // Atomic consume prevents two concurrent requests from both passing the\n // validity check before either deletes the token.\n const linkToken = linkTokenStore.consume(data.token);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid or expired token\" }));\n return;\n }\n\n try {\n vaultManager.upsertEnv(linkToken.vaultId, { [envKey]: credential });\n } catch (error) {\n log.logWarning(\n `Failed to persist ${envKey} for ${linkToken.platform}/${linkToken.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n \"Failed to store credential on server. Please fix the server issue and run /login again.\",\n }),\n );\n return;\n }\n\n log.logInfo(\n `Stored ${envKey} for ${linkToken.platform}/${linkToken.platformUserId} in vault:${linkToken.vaultId}`,\n );\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, message: `${envKey} stored successfully in vault.` }));\n\n notify(\n linkToken.platform,\n linkToken.conversationId,\n `${envKey} stored successfully in vault \\`${linkToken.vaultId}\\`.`,\n ).catch((err: Error) => {\n log.logWarning(\"Failed to notify user after credential login\", err.message);\n });\n}\n\n// ── OAuth flow ────────────────────────────────────────────────────────────────\n\nasync function handleOAuthStart(\n body: string,\n req: IncomingMessage,\n linkTokenStore: InMemoryLinkTokenStore,\n oauthStates: Map<string, PendingOAuthState>,\n res: ServerResponse,\n): Promise<void> {\n let data: Partial<OAuthStartBody>;\n try {\n data = JSON.parse(body) as Partial<OAuthStartBody>;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return;\n }\n\n if (!data.token || !data.serviceId) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required fields: token/serviceId\" }));\n return;\n }\n\n const linkToken = linkTokenStore.peek(data.token);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid or expired token\" }));\n return;\n }\n\n const service = resolveOAuthService(data.serviceId);\n if (!service) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Unsupported OAuth service: ${data.serviceId}` }));\n return;\n }\n\n const clientId = process.env[service.clientIdEnvKey];\n const clientSecret = process.env[service.clientSecretEnvKey];\n if (!clientId || !clientSecret) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n `OAuth service ${service.label} is not configured. ` +\n `Missing ${service.clientIdEnvKey}/${service.clientSecretEnvKey}.`,\n }),\n );\n return;\n }\n\n const state = randomBytes(16).toString(\"hex\");\n const codeVerifier = randomBytes(32).toString(\"base64url\");\n oauthStates.set(state, {\n linkToken: data.token,\n serviceId: service.id,\n codeVerifier,\n expiresAt: Date.now() + OAUTH_STATE_TTL_MS,\n });\n\n for (const [k, v] of oauthStates) {\n if (Date.now() > v.expiresAt) oauthStates.delete(k);\n }\n\n const redirectUri = `${requestBaseUrl(req)}/oauth/callback`;\n const authorizeUrl = new URL(service.authorizationUrl);\n authorizeUrl.searchParams.set(\"response_type\", \"code\");\n authorizeUrl.searchParams.set(\"client_id\", clientId);\n authorizeUrl.searchParams.set(\"redirect_uri\", redirectUri);\n authorizeUrl.searchParams.set(\"state\", state);\n if (service.scopes.length > 0) {\n authorizeUrl.searchParams.set(\"scope\", service.scopes.join(\" \"));\n }\n for (const [key, value] of Object.entries(service.authorizationParams ?? {})) {\n authorizeUrl.searchParams.set(key, value);\n }\n\n const codeChallenge = createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n authorizeUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, redirectUrl: authorizeUrl.toString() }));\n}\n\nasync function handleOAuthCallback(\n url: URL,\n req: IncomingMessage,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n oauthStates: Map<string, PendingOAuthState>,\n res: ServerResponse,\n): Promise<void> {\n const state = url.searchParams.get(\"state\") ?? \"\";\n const code = url.searchParams.get(\"code\") ?? \"\";\n const error = url.searchParams.get(\"error\");\n\n // Atomic pop: whatever path we take from here, this state is spent.\n // Done before any `await` to close the TOCTOU window between the state\n // lookup and the final delete.\n const pending = oauthStates.get(state);\n if (pending) oauthStates.delete(state);\n\n if (error) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(`OAuth authorization failed: ${error}`));\n return;\n }\n\n if (!pending || Date.now() > pending.expiresAt) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth state is invalid or expired. Please run /login again.\"));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Missing OAuth authorization code.\"));\n return;\n }\n\n const service = resolveOAuthService(pending.serviceId);\n if (!service) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Unsupported OAuth service.\"));\n return;\n }\n\n const clientId = process.env[service.clientIdEnvKey];\n const clientSecret = process.env[service.clientSecretEnvKey];\n if (!clientId || !clientSecret) {\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth service is not configured on server.\"));\n return;\n }\n\n // Atomic consume: pairs with the callback being one-shot. Two concurrent\n // callbacks for the same state would previously both pass `peek` and both\n // run `exchangeOAuthCode` across the await; only one reaches `consume`.\n const linkToken = linkTokenStore.consume(pending.linkToken);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Login link is invalid or expired. Please run /login again.\"));\n return;\n }\n\n const redirectUri = `${requestBaseUrl(req)}/oauth/callback`;\n const tokenResp = await exchangeOAuthCode(\n service,\n code,\n clientId,\n clientSecret,\n redirectUri,\n pending.codeVerifier,\n );\n\n const accessToken = tokenResp.access_token?.trim();\n const refreshToken = tokenResp.refresh_token?.trim();\n\n if (!accessToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth token exchange did not return an access_token.\"));\n return;\n }\n\n const updates: Record<string, string> = {};\n if (service.accessTokenEnvKey) {\n updates[service.accessTokenEnvKey] = accessToken;\n }\n for (const key of service.additionalAccessTokenEnvKeys ?? []) {\n updates[key] = accessToken;\n }\n if (refreshToken && service.refreshTokenEnvKey) {\n updates[service.refreshTokenEnvKey] = refreshToken;\n }\n\n const fileOutput = service.fileOutput;\n let mountedPath: string | undefined;\n if (fileOutput?.type === \"authorized_user\") {\n if (!refreshToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"OAuth token exchange did not return a refresh_token. \" +\n \"Retry after revoking prior consent or ensure prompt=consent is applied.\",\n ),\n );\n return;\n }\n\n mountedPath = fileOutput.targetPath ?? defaultVaultTargetPath(fileOutput.relativePath);\n if (fileOutput.envKey) {\n updates[fileOutput.envKey] = mountedPath;\n }\n }\n\n const storedTargets: string[] = [];\n try {\n if (Object.keys(updates).length > 0) {\n vaultManager.upsertEnv(linkToken.vaultId, updates);\n storedTargets.push(...Object.keys(updates).sort());\n }\n if (fileOutput?.type === \"authorized_user\" && refreshToken) {\n vaultManager.upsertFile(\n linkToken.vaultId,\n fileOutput.relativePath,\n renderAuthorizedUserCredential(clientId, clientSecret, refreshToken),\n fileOutput.targetPath,\n );\n if (mountedPath) storedTargets.push(mountedPath);\n }\n } catch (error) {\n log.logWarning(\n `Failed to persist OAuth credentials for ${linkToken.platform}/${linkToken.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"OAuth tokens were received but could not be stored on the server. Fix the server issue and run /login again.\",\n ),\n );\n return;\n }\n\n log.logInfo(\n `Stored [${storedTargets.join(\", \")}] for ${linkToken.platform}/${linkToken.platformUserId} in vault:${linkToken.vaultId}`,\n );\n\n notify(\n linkToken.platform,\n linkToken.conversationId,\n `${service.label} OAuth stored (${storedTargets.join(\", \")}) in vault \\`${linkToken.vaultId}\\`.`,\n ).catch((err: Error) => {\n log.logWarning(\"Failed to notify user after OAuth login\", err.message);\n });\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderSuccessPage(`${service.label} OAuth connected successfully.`));\n}\n\nasync function exchangeOAuthCode(\n service: OAuthService,\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string,\n codeVerifier: string,\n): Promise<Record<string, string>> {\n const params = new URLSearchParams();\n params.set(\"grant_type\", \"authorization_code\");\n params.set(\"code\", code);\n params.set(\"client_id\", clientId);\n params.set(\"client_secret\", clientSecret);\n params.set(\"redirect_uri\", redirectUri);\n params.set(\"code_verifier\", codeVerifier);\n\n const response = await fetch(service.tokenUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n const text = await response.text();\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let parsed: Record<string, string> = {};\n\n if (contentType.includes(\"application/json\")) {\n parsed = JSON.parse(text) as Record<string, string>;\n } else {\n const form = new URLSearchParams(text);\n parsed = Object.fromEntries(form.entries());\n }\n\n if (!response.ok) {\n const message = parsed.error_description ?? parsed.error ?? `${response.status}`;\n throw new Error(`OAuth token exchange failed for ${service.id}: ${message}`);\n }\n\n return parsed;\n}\n\nfunction renderAuthorizedUserCredential(\n clientId: string,\n clientSecret: string,\n refreshToken: string,\n): string {\n return (\n JSON.stringify(\n {\n client_id: clientId,\n client_secret: clientSecret,\n refresh_token: refreshToken,\n type: \"authorized_user\",\n },\n null,\n 2,\n ) + \"\\n\"\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"link-server.js","sourceRoot":"","sources":["../src/link-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from \"crypto\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"http\";\nimport { resolveLinkBaseUrl } from \"./config.js\";\nimport type { InMemoryLinkTokenStore } from \"./link-token.js\";\nimport {\n getOAuthServices,\n resolveOAuthService,\n type LoginCredentialKind,\n type OAuthService,\n} from \"./login.js\";\nimport * as log from \"./log.js\";\nimport { PRODUCT_NAME } from \"./ui-copy.js\";\nimport { defaultVaultTargetPath, type VaultManager } from \"./vault.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\n/** Called after a binding is written, to notify the user in chat */\nexport type NotifyFn = (platform: string, conversationId: string, message: string) => Promise<void>;\n\ninterface LinkCompleteBody {\n token: string;\n mode?: LoginCredentialKind;\n envKey?: string;\n credential?: string;\n}\n\ninterface OAuthStartBody {\n token: string;\n serviceId: string;\n}\n\ninterface PendingOAuthState {\n linkToken: string;\n serviceId: string;\n codeVerifier: string;\n expiresAt: number;\n}\n\nconst OAUTH_STATE_TTL_MS = 10 * 60 * 1000;\n\n// ── startLinkServer ────────────────────────────────────────────────────────────\n\n/**\n * Start a small HTTP server that receives credential onboarding callbacks from the web portal.\n *\n * Routes:\n * GET /health — health check\n * GET /link?token=xxx — credential onboarding page\n * POST /api/link/complete — API key completion endpoint\n * POST /api/oauth/start — creates provider OAuth redirect URL\n * GET /oauth/callback — OAuth callback endpoint\n */\nexport function startLinkServer(\n port: number,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n): Server {\n const oauthStates = new Map<string, PendingOAuthState>();\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? \"/\", requestBaseUrl(req));\n\n if (req.method === \"GET\" && url.pathname === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true }));\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === \"/link\") {\n const rawToken = url.searchParams.get(\"token\") ?? \"\";\n const linkToken = linkTokenStore.peek(rawToken);\n\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"This link is invalid or has expired. Ask the bot for a new /login link.\",\n ),\n );\n return;\n }\n\n const oauthServiceHint = linkToken.providerId\n ? resolveOAuthService(linkToken.providerId)\n : undefined;\n const oauthServices = getOAuthServices();\n const defaultMode: LoginCredentialKind = oauthServiceHint ? \"oauth\" : \"api_key\";\n const existingSecrets = describeVaultSecrets(vaultManager, linkToken.vaultId);\n\n const title = oauthServiceHint ? `${oauthServiceHint.label} OAuth` : \"Store Secret\";\n const helpText = oauthServiceHint\n ? `Authorize ${oauthServiceHint.label} and store tokens in your vault.`\n : \"Set any environment variable key/value pair in your vault.\";\n const secretLabel = \"Secret value\";\n const placeholder = \"sk-...\";\n const initialEnvKey = \"\";\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderCredentialPage(\n rawToken,\n title,\n defaultMode,\n initialEnvKey,\n secretLabel,\n placeholder,\n helpText,\n oauthServices,\n oauthServiceHint?.id,\n existingSecrets,\n ),\n );\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/api/link/complete\") {\n if (!enforceCsrf(req, res)) return;\n void readJsonBody(req, res, async (body) => {\n await handleLinkComplete(body, linkTokenStore, vaultManager, notify, res);\n });\n return;\n }\n\n if (req.method === \"POST\" && url.pathname === \"/api/oauth/start\") {\n if (!enforceCsrf(req, res)) return;\n void readJsonBody(req, res, async (body) => {\n await handleOAuthStart(body, req, linkTokenStore, oauthStates, res);\n });\n return;\n }\n\n if (req.method === \"GET\" && url.pathname === \"/oauth/callback\") {\n void handleOAuthCallback(\n url,\n req,\n linkTokenStore,\n vaultManager,\n notify,\n oauthStates,\n res,\n ).catch((err: Error) => {\n log.logWarning(\"OAuth callback failed\", err.message);\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth callback failed. Please retry /login.\"));\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n // Bind to loopback when MOM_LINK_URL is unset so the credential UI and OAuth\n // callbacks are not exposed on public interfaces by default. Production\n // deployments set MOM_LINK_URL and are expected to front this server with a\n // reverse proxy, which can still reach it via 0.0.0.0.\n const bindHost = resolveLinkBaseUrl() ? undefined : \"127.0.0.1\";\n server.listen(port, bindHost, () => {\n log.logInfo(`Link callback server listening on ${bindHost ?? \"0.0.0.0\"}:${port}`);\n if (!resolveLinkBaseUrl()) {\n log.logWarning(\n \"MOM_LINK_URL is not set — bound to 127.0.0.1 and OAuth redirect_uri will be \" +\n \"derived from request headers (Host / X-Forwarded-*). Set \" +\n \"MOM_LINK_URL=https://your-host.example.com for production.\",\n );\n }\n });\n\n server.on(\"error\", (err) => {\n log.logWarning(\"Link server error\", err.message);\n });\n\n return server;\n}\n\n/**\n * Resolve the externally-visible base URL of this server.\n *\n * Prefers MOM_LINK_URL (see config.ts) so the OAuth `redirect_uri` is\n * deterministic and not influenced by attacker-controlled request headers.\n * Falls back to Host / X-Forwarded-* only when no base URL is configured\n * — intended for local development.\n */\nfunction requestBaseUrl(req: IncomingMessage): string {\n const configured = resolveLinkBaseUrl();\n if (configured) return configured;\n\n const protoRaw = (req.headers[\"x-forwarded-proto\"] as string | undefined)?.split(\",\")[0]?.trim();\n const proto = protoRaw || \"http\";\n const host =\n ((req.headers[\"x-forwarded-host\"] as string | undefined)?.split(\",\")[0]?.trim() ??\n req.headers.host ??\n `localhost`) ||\n `localhost`;\n return `${proto}://${host}`;\n}\n\n/**\n * Block cross-site POSTs to the credential endpoints. Two defenses:\n * 1. Require Content-Type: application/json, which forces a CORS preflight\n * for any cross-origin fetch and rules out `<form enctype=\"text/plain\">`\n * tricks that could otherwise smuggle a JSON body.\n * 2. When MOM_LINK_URL is configured, require that the Origin (or Referer,\n * as a fallback for browsers that strip Origin) matches that base URL.\n * This stops an attacker-controlled page — even one that somehow stole a\n * victim's link token — from completing the flow.\n */\nfunction enforceCsrf(req: IncomingMessage, res: ServerResponse): boolean {\n const contentType = (req.headers[\"content-type\"] as string | undefined)\n ?.split(\";\")[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== \"application/json\") {\n res.writeHead(415, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Content-Type must be application/json\" }));\n return false;\n }\n\n const configured = resolveLinkBaseUrl();\n if (!configured) {\n // No trusted origin to compare against in local/dev mode; the loopback\n // bind already prevents cross-host access.\n return true;\n }\n\n let configuredOrigin: string;\n try {\n configuredOrigin = new URL(configured).origin;\n } catch {\n // Misconfigured MOM_LINK_URL — fail closed.\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Server misconfiguration\" }));\n return false;\n }\n\n if (requestOrigin(req) !== configuredOrigin) {\n res.writeHead(403, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Cross-origin request rejected\" }));\n return false;\n }\n\n return true;\n}\n\n/** Best-effort origin of the request, derived from Origin or Referer. */\nfunction requestOrigin(req: IncomingMessage): string | undefined {\n const origin = (req.headers.origin as string | undefined)?.trim();\n if (origin && origin !== \"null\") return origin;\n\n const referer = (req.headers.referer as string | undefined)?.trim();\n if (!referer) return undefined;\n try {\n return new URL(referer).origin;\n } catch {\n return undefined;\n }\n}\n\nasync function readJsonBody(\n req: IncomingMessage,\n res: ServerResponse,\n onBody: (body: string) => Promise<void>,\n): Promise<void> {\n let body = \"\";\n let bodyTooLarge = false;\n\n req.on(\"data\", (chunk: Buffer) => {\n if (bodyTooLarge) return;\n body += chunk.toString();\n if (body.length > 16 * 1024) {\n bodyTooLarge = true;\n res.writeHead(413);\n res.end();\n req.destroy();\n }\n });\n\n req.on(\"end\", async () => {\n if (bodyTooLarge) return;\n await onBody(body);\n });\n}\n\n// ── HTML helpers ───────────────────────────────────────────────────────────────\n\nfunction esc(s: string): string {\n return s.replace(\n /[&<>\"']/g,\n (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[c]!,\n );\n}\n\nconst sharedPageStyles = `\n :root {\n color-scheme: light;\n --bg: #f5f1e8;\n --panel: rgba(255, 255, 255, 0.9);\n --panel-border: rgba(28, 30, 33, 0.08);\n --text: #1c1e21;\n --muted: #5d5f64;\n --button: #1c1e21;\n --button-hover: #2c3035;\n --button-disabled: #8f949b;\n --field-border: #c9cfd6;\n --field-focus: #1c1e21;\n --ok-bg: #dff4e4;\n --ok-text: #1f5b34;\n --err-bg: #fde2e2;\n --err-text: #8a2f2f;\n }\n\n * { box-sizing: border-box; }\n\n body {\n margin: 0;\n min-height: 100vh;\n padding: 32px 20px;\n display: grid;\n place-items: center;\n background:\n radial-gradient(circle at top, rgba(255, 255, 255, 0.7), transparent 45%),\n linear-gradient(180deg, #faf7f0 0%, var(--bg) 100%);\n color: var(--text);\n font-family:\n \"SF Pro Text\",\n \"Segoe UI\",\n system-ui,\n sans-serif;\n }\n\n .shell {\n width: min(100%, 560px);\n }\n\n .card {\n padding: 28px;\n border: 1px solid var(--panel-border);\n border-radius: 20px;\n background: var(--panel);\n box-shadow: 0 18px 48px rgba(28, 30, 33, 0.08);\n backdrop-filter: blur(8px);\n }\n\n .eyebrow {\n margin: 0 0 10px;\n color: var(--muted);\n font-size: 0.82rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n h1 {\n margin: 0 0 10px;\n font-size: clamp(1.5rem, 2vw, 1.8rem);\n line-height: 1.15;\n text-wrap: balance;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n font-size: 0.98rem;\n line-height: 1.5;\n }\n\n .stack > * + * {\n margin-top: 14px;\n }\n\n .form {\n margin-top: 24px;\n }\n\n .form > * + * {\n margin-top: 18px;\n }\n\n label {\n display: block;\n margin-bottom: 6px;\n font-size: 0.92rem;\n font-weight: 650;\n }\n\n input,\n select,\n button {\n font: inherit;\n }\n\n input,\n select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--field-border);\n border-radius: 12px;\n background: #fff;\n color: var(--text);\n }\n\n input:focus-visible,\n select:focus-visible,\n button:focus-visible {\n outline: 2px solid var(--field-focus);\n outline-offset: 2px;\n }\n\n button {\n width: 100%;\n margin-top: 24px;\n padding: 13px 18px;\n border: none;\n border-radius: 12px;\n background: var(--button);\n color: #fff;\n cursor: pointer;\n transition: background-color 160ms ease;\n }\n\n button:hover {\n background: var(--button-hover);\n }\n\n button:disabled {\n background: var(--button-disabled);\n cursor: default;\n }\n\n .mode {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n margin-top: 22px;\n }\n\n .mode label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 10px 12px;\n border: 1px solid var(--field-border);\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.85);\n font-weight: 500;\n }\n\n .mode input {\n width: auto;\n margin: 0;\n }\n\n .panel {\n display: none;\n }\n\n .panel.active {\n display: block;\n }\n\n .panel-note {\n margin-top: 10px;\n font-size: 0.92rem;\n }\n\n .result,\n .status {\n margin-top: 20px;\n padding: 14px 16px;\n border-radius: 14px;\n font-size: 0.95rem;\n }\n\n .result {\n display: none;\n }\n\n .result.ok,\n .status.ok {\n background: var(--ok-bg);\n color: var(--ok-text);\n }\n\n .result.err,\n .status.err {\n background: var(--err-bg);\n color: var(--err-text);\n }\n\n .secrets-summary {\n margin-top: 18px;\n padding: 14px 16px;\n border: 1px solid var(--panel-border);\n border-radius: 14px;\n background: rgba(255, 255, 255, 0.72);\n }\n\n .secrets-summary h2 {\n margin: 0 0 8px;\n font-size: 0.98rem;\n }\n\n .secrets-summary p {\n font-size: 0.92rem;\n }\n\n .secrets-summary ul {\n margin: 10px 0 0;\n padding-left: 18px;\n color: var(--text);\n }\n\n .secrets-summary li + li {\n margin-top: 6px;\n }\n\n .close-note {\n margin-top: 14px;\n font-size: 0.92rem;\n }\n\n @media (max-width: 640px) {\n body {\n padding: 20px 14px;\n }\n\n .card {\n padding: 22px;\n border-radius: 16px;\n }\n }\n`;\n\nfunction renderPageDocument(title: string, body: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${esc(title)} — ${PRODUCT_NAME}</title>\n <style>${sharedPageStyles}</style>\n</head>\n<body>\n <main class=\"shell\">\n <section class=\"card\">\n ${body}\n </section>\n </main>\n</body>\n</html>`;\n}\n\nfunction renderStatusPage(\n title: string,\n message: string,\n tone: \"ok\" | \"err\",\n options?: { closeNote?: boolean },\n): string {\n const closeNote = options?.closeNote ? '<p class=\"close-note\">You can close this tab.</p>' : \"\";\n return renderPageDocument(\n title,\n `<div class=\"stack\">\n <p class=\"eyebrow\">${PRODUCT_NAME}</p>\n <h1>${esc(title)}</h1>\n <div class=\"status ${tone}\">${esc(message)}</div>\n ${closeNote}\n </div>`,\n );\n}\n\ninterface ExistingSecretsSummary {\n envKeys: string[];\n mountTargets: string[];\n}\n\nfunction describeVaultSecrets(vaultManager: VaultManager, vaultId: string): ExistingSecretsSummary {\n const vault = vaultManager.resolve(vaultId);\n if (!vault) {\n return { envKeys: [], mountTargets: [] };\n }\n\n return {\n envKeys: Object.keys(vault.env).sort((left, right) => left.localeCompare(right)),\n mountTargets: [...new Set(vault.mounts.map((mount) => mount.target))].sort((left, right) =>\n left.localeCompare(right),\n ),\n };\n}\n\nfunction renderSecretsSummary(summary: ExistingSecretsSummary): string {\n if (summary.envKeys.length === 0 && summary.mountTargets.length === 0) {\n return `\n <section class=\"secrets-summary\">\n <h2>Currently stored</h2>\n <p>No secrets are stored in this vault yet.</p>\n </section>`;\n }\n\n const envItems = summary.envKeys.map((envKey) => `<li><code>${esc(envKey)}</code></li>`).join(\"\");\n const mountItems = summary.mountTargets\n .map((target) => `<li><code>${esc(target)}</code></li>`)\n .join(\"\");\n\n return `\n <section class=\"secrets-summary\">\n <h2>Currently stored</h2>\n <p>Only secret names and mounted paths are shown here. Secret values are never displayed.</p>\n ${summary.envKeys.length > 0 ? `<p><strong>Environment keys</strong></p><ul>${envItems}</ul>` : \"\"}\n ${summary.mountTargets.length > 0 ? `<p><strong>Mounted secret files</strong></p><ul>${mountItems}</ul>` : \"\"}\n </section>`;\n}\n\nfunction renderCredentialPage(\n token: string,\n title: string,\n defaultMode: LoginCredentialKind,\n initialEnvKey: string,\n secretLabel: string,\n placeholder: string,\n helpText: string,\n oauthServices: OAuthService[],\n oauthServiceIdHint: string | undefined,\n existingSecrets: ExistingSecretsSummary,\n): string {\n const oauthOptions = oauthServices\n .map((service) => {\n const selected = service.id === oauthServiceIdHint ? ' selected=\"selected\"' : \"\";\n return `<option value=\"${esc(service.id)}\"${selected}>${esc(service.label)}</option>`;\n })\n .join(\"\\n\");\n\n return renderPageDocument(\n \"Login\",\n `<div class=\"stack\">\n <p class=\"eyebrow\">${PRODUCT_NAME}</p>\n <h1>${esc(title)}</h1>\n <p>Your personal sandbox is already provisioned automatically.</p>\n <p>${esc(helpText)}</p>\n ${renderSecretsSummary(existingSecrets)}\n <div class=\"mode\">\n <label><input type=\"radio\" name=\"mode\" value=\"api_key\" ${defaultMode === \"api_key\" ? \"checked\" : \"\"}> API key</label>\n <label><input type=\"radio\" name=\"mode\" value=\"oauth\" ${defaultMode === \"oauth\" ? \"checked\" : \"\"}> OAuth login</label>\n </div>\n\n <div class=\"form\">\n <div id=\"api-panel\" class=\"panel\">\n <label for=\"envKey\">Environment key</label>\n <input id=\"envKey\" type=\"text\" name=\"envKey\" placeholder=\"OPENAI_API_KEY\" value=\"${esc(initialEnvKey)}\" autocomplete=\"off\">\n <label for=\"credential\">${esc(secretLabel)}</label>\n <input id=\"credential\" type=\"password\" name=\"credential\" placeholder=\"${esc(placeholder)}\" autocomplete=\"off\">\n </div>\n\n <div id=\"oauth-panel\" class=\"panel\">\n <label for=\"oauthService\">OAuth service</label>\n <select id=\"oauthService\" name=\"oauthService\">${oauthOptions}</select>\n <p class=\"panel-note\">You'll be redirected to the selected service's authorization page.</p>\n </div>\n\n <button id=\"btn\" onclick=\"connect()\">Continue</button>\n <div id=\"result\" class=\"result\" aria-live=\"polite\"></div>\n </div>\n <script>\n const envKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n function selectedMode() {\n return document.querySelector('input[name=\"mode\"]:checked').value;\n }\n\n function showResult(message, ok) {\n const result = document.getElementById('result');\n result.style.display = 'block';\n result.className = ok ? 'result ok' : 'result err';\n result.textContent = message;\n }\n\n function syncPanels() {\n const api = document.getElementById('api-panel');\n const oauth = document.getElementById('oauth-panel');\n const mode = selectedMode();\n api.className = mode === 'api_key' ? 'panel active' : 'panel';\n oauth.className = mode === 'oauth' ? 'panel active' : 'panel';\n }\n\n for (const radio of document.querySelectorAll('input[name=\"mode\"]')) {\n radio.addEventListener('change', syncPanels);\n }\n\n syncPanels();\n\n async function connect() {\n const btn = document.getElementById('btn');\n const mode = selectedMode();\n btn.disabled = true;\n btn.textContent = mode === 'oauth' ? 'Redirecting…' : 'Saving…';\n\n try {\n if (mode === 'oauth') {\n const serviceId = document.getElementById('oauthService').value;\n const r = await fetch('/api/oauth/start', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: '${esc(token)}', serviceId }),\n });\n const data = await r.json();\n if (!r.ok) {\n showResult('Error: ' + (data.error ?? r.status), false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n window.location.href = data.redirectUrl;\n return;\n }\n\n const envKey = document.getElementById('envKey').value.trim();\n const credential = document.getElementById('credential').value.trim();\n if (!envKeyPattern.test(envKey)) {\n showResult('Please enter a valid environment key.', false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n if (!credential) {\n showResult('Please enter a value.', false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n return;\n }\n\n const r = await fetch('/api/link/complete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token: '${esc(token)}', mode: 'api_key', envKey, credential }),\n });\n const data = await r.json();\n if (r.ok) {\n showResult(data.message ?? 'Credential stored. You can close this tab.', true);\n btn.style.display = 'none';\n for (const input of document.querySelectorAll('input,select')) input.disabled = true;\n } else {\n showResult('Error: ' + (data.error ?? r.status), false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n }\n } catch (err) {\n showResult('Network error: ' + err.message, false);\n btn.disabled = false;\n btn.textContent = 'Continue';\n }\n }\n </script>\n</div>`,\n );\n}\n\nfunction renderErrorPage(message: string): string {\n return renderStatusPage(\"Login Error\", message, \"err\");\n}\n\nfunction renderSuccessPage(message: string): string {\n return renderStatusPage(\"Connected\", message, \"ok\", { closeNote: true });\n}\n\n// ── API-key completion ────────────────────────────────────────────────────────\n\nasync function handleLinkComplete(\n body: string,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n res: ServerResponse,\n): Promise<void> {\n let data: Partial<LinkCompleteBody>;\n try {\n data = JSON.parse(body) as Partial<LinkCompleteBody>;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return;\n }\n\n if (!data.token) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required field: token\" }));\n return;\n }\n\n const envKey = data.envKey?.trim() ?? \"\";\n const credential = data.credential?.trim() ?? \"\";\n\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid envKey format\" }));\n return;\n }\n\n if (!credential) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required field: credential\" }));\n return;\n }\n\n // Atomic consume prevents two concurrent requests from both passing the\n // validity check before either deletes the token.\n const linkToken = linkTokenStore.consume(data.token);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid or expired token\" }));\n return;\n }\n\n try {\n vaultManager.upsertEnv(linkToken.vaultId, { [envKey]: credential });\n } catch (error) {\n log.logWarning(\n `Failed to persist ${envKey} for ${linkToken.platform}/${linkToken.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n \"Failed to store credential on server. Please fix the server issue and run /login again.\",\n }),\n );\n return;\n }\n\n log.logInfo(\n `Stored ${envKey} for ${linkToken.platform}/${linkToken.platformUserId} in vault:${linkToken.vaultId}`,\n );\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, message: `${envKey} stored successfully in vault.` }));\n\n notify(\n linkToken.platform,\n linkToken.conversationId,\n `${envKey} stored successfully in vault \\`${linkToken.vaultId}\\`.`,\n ).catch((err: Error) => {\n log.logWarning(\"Failed to notify user after credential login\", err.message);\n });\n}\n\n// ── OAuth flow ────────────────────────────────────────────────────────────────\n\nasync function handleOAuthStart(\n body: string,\n req: IncomingMessage,\n linkTokenStore: InMemoryLinkTokenStore,\n oauthStates: Map<string, PendingOAuthState>,\n res: ServerResponse,\n): Promise<void> {\n let data: Partial<OAuthStartBody>;\n try {\n data = JSON.parse(body) as Partial<OAuthStartBody>;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON\" }));\n return;\n }\n\n if (!data.token || !data.serviceId) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing required fields: token/serviceId\" }));\n return;\n }\n\n const linkToken = linkTokenStore.peek(data.token);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid or expired token\" }));\n return;\n }\n\n const service = resolveOAuthService(data.serviceId);\n if (!service) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: `Unsupported OAuth service: ${data.serviceId}` }));\n return;\n }\n\n const clientId = process.env[service.clientIdEnvKey];\n const clientSecret = process.env[service.clientSecretEnvKey];\n if (!clientId || !clientSecret) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error:\n `OAuth service ${service.label} is not configured. ` +\n `Missing ${service.clientIdEnvKey}/${service.clientSecretEnvKey}.`,\n }),\n );\n return;\n }\n\n const state = randomBytes(16).toString(\"hex\");\n const codeVerifier = randomBytes(32).toString(\"base64url\");\n oauthStates.set(state, {\n linkToken: data.token,\n serviceId: service.id,\n codeVerifier,\n expiresAt: Date.now() + OAUTH_STATE_TTL_MS,\n });\n\n for (const [k, v] of oauthStates) {\n if (Date.now() > v.expiresAt) oauthStates.delete(k);\n }\n\n const redirectUri = `${requestBaseUrl(req)}/oauth/callback`;\n const authorizeUrl = new URL(service.authorizationUrl);\n authorizeUrl.searchParams.set(\"response_type\", \"code\");\n authorizeUrl.searchParams.set(\"client_id\", clientId);\n authorizeUrl.searchParams.set(\"redirect_uri\", redirectUri);\n authorizeUrl.searchParams.set(\"state\", state);\n if (service.scopes.length > 0) {\n authorizeUrl.searchParams.set(\"scope\", service.scopes.join(\" \"));\n }\n for (const [key, value] of Object.entries(service.authorizationParams ?? {})) {\n authorizeUrl.searchParams.set(key, value);\n }\n\n const codeChallenge = createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n authorizeUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authorizeUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, redirectUrl: authorizeUrl.toString() }));\n}\n\nasync function handleOAuthCallback(\n url: URL,\n req: IncomingMessage,\n linkTokenStore: InMemoryLinkTokenStore,\n vaultManager: VaultManager,\n notify: NotifyFn,\n oauthStates: Map<string, PendingOAuthState>,\n res: ServerResponse,\n): Promise<void> {\n const state = url.searchParams.get(\"state\") ?? \"\";\n const code = url.searchParams.get(\"code\") ?? \"\";\n const error = url.searchParams.get(\"error\");\n\n // Atomic pop: whatever path we take from here, this state is spent.\n // Done before any `await` to close the TOCTOU window between the state\n // lookup and the final delete.\n const pending = oauthStates.get(state);\n if (pending) oauthStates.delete(state);\n\n if (error) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(`OAuth authorization failed: ${error}`));\n return;\n }\n\n if (!pending || Date.now() > pending.expiresAt) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth state is invalid or expired. Please run /login again.\"));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Missing OAuth authorization code.\"));\n return;\n }\n\n const service = resolveOAuthService(pending.serviceId);\n if (!service) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Unsupported OAuth service.\"));\n return;\n }\n\n const clientId = process.env[service.clientIdEnvKey];\n const clientSecret = process.env[service.clientSecretEnvKey];\n if (!clientId || !clientSecret) {\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth service is not configured on server.\"));\n return;\n }\n\n // Atomic consume: pairs with the callback being one-shot. Two concurrent\n // callbacks for the same state would previously both pass `peek` and both\n // run `exchangeOAuthCode` across the await; only one reaches `consume`.\n const linkToken = linkTokenStore.consume(pending.linkToken);\n if (!linkToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"Login link is invalid or expired. Please run /login again.\"));\n return;\n }\n\n const redirectUri = `${requestBaseUrl(req)}/oauth/callback`;\n const tokenResp = await exchangeOAuthCode(\n service,\n code,\n clientId,\n clientSecret,\n redirectUri,\n pending.codeVerifier,\n );\n\n const accessToken = tokenResp.access_token?.trim();\n const refreshToken = tokenResp.refresh_token?.trim();\n\n if (!accessToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderErrorPage(\"OAuth token exchange did not return an access_token.\"));\n return;\n }\n\n const updates: Record<string, string> = {};\n if (service.accessTokenEnvKey) {\n updates[service.accessTokenEnvKey] = accessToken;\n }\n for (const key of service.additionalAccessTokenEnvKeys ?? []) {\n updates[key] = accessToken;\n }\n if (refreshToken && service.refreshTokenEnvKey) {\n updates[service.refreshTokenEnvKey] = refreshToken;\n }\n\n const fileOutput = service.fileOutput;\n let mountedPath: string | undefined;\n if (fileOutput?.type === \"authorized_user\") {\n if (!refreshToken) {\n res.writeHead(400, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"OAuth token exchange did not return a refresh_token. \" +\n \"Retry after revoking prior consent or ensure prompt=consent is applied.\",\n ),\n );\n return;\n }\n\n mountedPath = fileOutput.targetPath ?? defaultVaultTargetPath(fileOutput.relativePath);\n if (fileOutput.envKey) {\n updates[fileOutput.envKey] = mountedPath;\n }\n }\n\n const storedTargets: string[] = [];\n try {\n if (Object.keys(updates).length > 0) {\n vaultManager.upsertEnv(linkToken.vaultId, updates);\n storedTargets.push(...Object.keys(updates).sort());\n }\n if (fileOutput?.type === \"authorized_user\" && refreshToken) {\n vaultManager.upsertFile(\n linkToken.vaultId,\n fileOutput.relativePath,\n renderAuthorizedUserCredential(clientId, clientSecret, refreshToken),\n fileOutput.targetPath,\n );\n if (mountedPath) storedTargets.push(mountedPath);\n }\n } catch (error) {\n log.logWarning(\n `Failed to persist OAuth credentials for ${linkToken.platform}/${linkToken.platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n res.writeHead(500, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(\n renderErrorPage(\n \"OAuth tokens were received but could not be stored on the server. Fix the server issue and run /login again.\",\n ),\n );\n return;\n }\n\n log.logInfo(\n `Stored [${storedTargets.join(\", \")}] for ${linkToken.platform}/${linkToken.platformUserId} in vault:${linkToken.vaultId}`,\n );\n\n notify(\n linkToken.platform,\n linkToken.conversationId,\n `${service.label} OAuth stored (${storedTargets.join(\", \")}) in vault \\`${linkToken.vaultId}\\`.`,\n ).catch((err: Error) => {\n log.logWarning(\"Failed to notify user after OAuth login\", err.message);\n });\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(renderSuccessPage(`${service.label} OAuth connected successfully.`));\n}\n\nasync function exchangeOAuthCode(\n service: OAuthService,\n code: string,\n clientId: string,\n clientSecret: string,\n redirectUri: string,\n codeVerifier: string,\n): Promise<Record<string, string>> {\n const params = new URLSearchParams();\n params.set(\"grant_type\", \"authorization_code\");\n params.set(\"code\", code);\n params.set(\"client_id\", clientId);\n params.set(\"client_secret\", clientSecret);\n params.set(\"redirect_uri\", redirectUri);\n params.set(\"code_verifier\", codeVerifier);\n\n const response = await fetch(service.tokenUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Accept: \"application/json\",\n },\n body: params.toString(),\n });\n\n const text = await response.text();\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let parsed: Record<string, string> = {};\n\n if (contentType.includes(\"application/json\")) {\n parsed = JSON.parse(text) as Record<string, string>;\n } else {\n const form = new URLSearchParams(text);\n parsed = Object.fromEntries(form.entries());\n }\n\n if (!response.ok) {\n const message = parsed.error_description ?? parsed.error ?? `${response.status}`;\n throw new Error(`OAuth token exchange failed for ${service.id}: ${message}`);\n }\n\n return parsed;\n}\n\nfunction renderAuthorizedUserCredential(\n clientId: string,\n clientSecret: string,\n refreshToken: string,\n): string {\n return (\n JSON.stringify(\n {\n client_id: clientId,\n client_secret: clientSecret,\n refresh_token: refreshToken,\n type: \"authorized_user\",\n },\n null,\n 2,\n ) + \"\\n\"\n );\n}\n"]}
|
package/dist/login.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface OAuthService {
|
|
|
21
21
|
fileOutput?: OAuthAuthorizedUserFileOutput;
|
|
22
22
|
}
|
|
23
23
|
export interface ParsedLoginCommand {
|
|
24
|
-
command: "login" | "/login";
|
|
24
|
+
command: "login" | "/login" | "/pi-login";
|
|
25
25
|
}
|
|
26
26
|
export declare function getOAuthServices(): OAuthService[];
|
|
27
27
|
export declare function resolveOAuthService(input: string): OAuthService | undefined;
|
package/dist/login.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,6BAA6B,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,6BAA6B,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;CAC3C;AA+ED,wBAAgB,gBAAgB,IAAI,YAAY,EAAE,CA6FjD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAM3E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAUzE","sourcesContent":["export type LoginCredentialKind = \"api_key\" | \"oauth\";\n\nexport interface OAuthAuthorizedUserFileOutput {\n type: \"authorized_user\";\n relativePath: string;\n targetPath?: string;\n envKey?: string;\n}\n\nexport interface OAuthService {\n id: string;\n label: string;\n aliases: string[];\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n clientIdEnvKey: string;\n clientSecretEnvKey: string;\n accessTokenEnvKey?: string;\n additionalAccessTokenEnvKeys?: string[];\n refreshTokenEnvKey?: string;\n authorizationParams?: Record<string, string>;\n fileOutput?: OAuthAuthorizedUserFileOutput;\n}\n\nexport interface ParsedLoginCommand {\n command: \"login\" | \"/login\" | \"/pi-login\";\n}\n\nconst DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES = [\n \"https://www.googleapis.com/auth/drive\",\n \"https://mail.google.com/\",\n \"https://www.googleapis.com/auth/calendar\",\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/documents\",\n \"https://www.googleapis.com/auth/chat.messages.create\",\n];\n\n// Conservative default: enough for `gh` CLI repo/user/org operations, but\n// without `workflow` (can dispatch CI), `write:packages` (can publish\n// packages), or `project`. Operators who need those can opt in via\n// MOM_GITHUB_OAUTH_SCOPES to keep the blast radius of a compromised agent\n// host explicit and configurable.\nconst DEFAULT_GITHUB_OAUTH_SCOPES = [\"repo\", \"read:user\", \"user:email\", \"read:org\", \"gist\"];\n\nfunction resolveScopesFromEnv(envKey: string, fallback: string[]): string[] {\n const raw = process.env[envKey]?.trim();\n if (!raw) return fallback;\n\n const scopes = raw\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean);\n\n return scopes.length > 0 ? scopes : fallback;\n}\n\nfunction resolveGoogleWorkspaceCliScopes(): string[] {\n return resolveScopesFromEnv(\n \"MOM_GOOGLE_WORKSPACE_CLI_OAUTH_SCOPES\",\n DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES,\n );\n}\n\nfunction resolveGitHubOAuthScopes(): string[] {\n return resolveScopesFromEnv(\"MOM_GITHUB_OAUTH_SCOPES\", DEFAULT_GITHUB_OAUTH_SCOPES);\n}\n\nfunction getBuiltinOAuthServices(): OAuthService[] {\n return [\n {\n id: \"github\",\n label: \"GitHub\",\n aliases: [\"github\", \"github_oauth\", \"gh_oauth\"],\n authorizationUrl: \"https://github.com/login/oauth/authorize\",\n tokenUrl: \"https://github.com/login/oauth/access_token\",\n scopes: resolveGitHubOAuthScopes(),\n clientIdEnvKey: \"GITHUB_OAUTH_CLIENT_ID\",\n clientSecretEnvKey: \"GITHUB_OAUTH_CLIENT_SECRET\",\n accessTokenEnvKey: \"GITHUB_OAUTH_ACCESS_TOKEN\",\n additionalAccessTokenEnvKeys: [\"GH_TOKEN\"],\n refreshTokenEnvKey: \"GITHUB_OAUTH_REFRESH_TOKEN\",\n },\n {\n id: \"google_workspace_cli\",\n label: \"Google Workspace CLI\",\n aliases: [\"google_workspace_cli\", \"gws\", \"googleworkspace\", \"google-workspace-cli\"],\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n scopes: resolveGoogleWorkspaceCliScopes(),\n clientIdEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_ID\",\n clientSecretEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_SECRET\",\n authorizationParams: {\n access_type: \"offline\",\n include_granted_scopes: \"true\",\n prompt: \"consent\",\n },\n fileOutput: {\n type: \"authorized_user\",\n relativePath: \"gws.json\",\n targetPath: \"/root/.config/gws/credentials.json\",\n },\n },\n ];\n}\n\nexport function getOAuthServices(): OAuthService[] {\n const raw = process.env.MOM_OAUTH_SERVICES_JSON?.trim();\n const builtins = getBuiltinOAuthServices();\n if (!raw) return builtins;\n\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) return builtins;\n\n const custom = parsed\n .map((entry): OAuthService | null => {\n if (!entry || typeof entry !== \"object\") return null;\n const obj = entry as Record<string, unknown>;\n const id = typeof obj.id === \"string\" ? obj.id.trim() : \"\";\n const label = typeof obj.label === \"string\" ? obj.label.trim() : \"\";\n const authorizationUrl =\n typeof obj.authorizationUrl === \"string\" ? obj.authorizationUrl.trim() : \"\";\n const tokenUrl = typeof obj.tokenUrl === \"string\" ? obj.tokenUrl.trim() : \"\";\n const clientIdEnvKey =\n typeof obj.clientIdEnvKey === \"string\" ? obj.clientIdEnvKey.trim() : \"\";\n const clientSecretEnvKey =\n typeof obj.clientSecretEnvKey === \"string\" ? obj.clientSecretEnvKey.trim() : \"\";\n const accessTokenEnvKey =\n typeof obj.accessTokenEnvKey === \"string\" ? obj.accessTokenEnvKey.trim() : undefined;\n if (\n !id ||\n !label ||\n !authorizationUrl ||\n !tokenUrl ||\n !clientIdEnvKey ||\n !clientSecretEnvKey\n ) {\n return null;\n }\n\n let fileOutput: OAuthService[\"fileOutput\"];\n if (obj.fileOutput && typeof obj.fileOutput === \"object\") {\n const fileOutputObj = obj.fileOutput as Record<string, unknown>;\n const type = typeof fileOutputObj.type === \"string\" ? fileOutputObj.type.trim() : \"\";\n const relativePath =\n typeof fileOutputObj.relativePath === \"string\" ? fileOutputObj.relativePath.trim() : \"\";\n const targetPath =\n typeof fileOutputObj.targetPath === \"string\"\n ? fileOutputObj.targetPath.trim()\n : undefined;\n const envKey =\n typeof fileOutputObj.envKey === \"string\" ? fileOutputObj.envKey.trim() : undefined;\n if (type === \"authorized_user\" && relativePath) {\n fileOutput = { type: \"authorized_user\", relativePath, targetPath, envKey };\n }\n }\n\n return {\n id: id.toLowerCase(),\n label,\n aliases: Array.isArray(obj.aliases)\n ? obj.aliases\n .filter((v): v is string => typeof v === \"string\")\n .map((v) => v.toLowerCase())\n : [id.toLowerCase()],\n authorizationUrl,\n tokenUrl,\n scopes: Array.isArray(obj.scopes)\n ? obj.scopes.filter((v): v is string => typeof v === \"string\")\n : [],\n clientIdEnvKey,\n clientSecretEnvKey,\n accessTokenEnvKey,\n additionalAccessTokenEnvKeys: Array.isArray(obj.additionalAccessTokenEnvKeys)\n ? obj.additionalAccessTokenEnvKeys.filter((v): v is string => typeof v === \"string\")\n : undefined,\n refreshTokenEnvKey:\n typeof obj.refreshTokenEnvKey === \"string\" ? obj.refreshTokenEnvKey.trim() : undefined,\n authorizationParams:\n obj.authorizationParams && typeof obj.authorizationParams === \"object\"\n ? Object.fromEntries(\n Object.entries(obj.authorizationParams as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n )\n : undefined,\n fileOutput,\n };\n })\n .filter((service): service is OAuthService => service !== null);\n\n const byId = new Map<string, OAuthService>();\n for (const service of builtins) byId.set(service.id, service);\n for (const service of custom) byId.set(service.id, service);\n return [...byId.values()];\n } catch {\n return builtins;\n }\n}\n\nexport function resolveOAuthService(input: string): OAuthService | undefined {\n const normalized = input.trim().toLowerCase();\n if (!normalized) return undefined;\n return getOAuthServices().find(\n (service) => service.id === normalized || service.aliases.includes(normalized),\n );\n}\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"login\" && command !== \"/login\" && command !== \"/pi-login\") {\n return null;\n }\n\n return { command: command as \"login\" | \"/login\" | \"/pi-login\" };\n}\n"]}
|
package/dist/login.js
CHANGED
|
@@ -156,7 +156,7 @@ export function parseLoginCommand(text) {
|
|
|
156
156
|
if (tokens.length === 0)
|
|
157
157
|
return null;
|
|
158
158
|
const command = tokens[0].toLowerCase();
|
|
159
|
-
if (command !== "login" && command !== "/login") {
|
|
159
|
+
if (command !== "login" && command !== "/login" && command !== "/pi-login") {
|
|
160
160
|
return null;
|
|
161
161
|
}
|
|
162
162
|
return { command: command };
|
package/dist/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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","sourcesContent":["export type LoginCredentialKind = \"api_key\" | \"oauth\";\n\nexport interface OAuthAuthorizedUserFileOutput {\n type: \"authorized_user\";\n relativePath: string;\n targetPath?: string;\n envKey?: string;\n}\n\nexport interface OAuthService {\n id: string;\n label: string;\n aliases: string[];\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n clientIdEnvKey: string;\n clientSecretEnvKey: string;\n accessTokenEnvKey?: string;\n additionalAccessTokenEnvKeys?: string[];\n refreshTokenEnvKey?: string;\n authorizationParams?: Record<string, string>;\n fileOutput?: OAuthAuthorizedUserFileOutput;\n}\n\nexport interface ParsedLoginCommand {\n command: \"login\" | \"/login\";\n}\n\nconst DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES = [\n \"https://www.googleapis.com/auth/drive\",\n \"https://mail.google.com/\",\n \"https://www.googleapis.com/auth/calendar\",\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/documents\",\n \"https://www.googleapis.com/auth/chat.messages.create\",\n];\n\n// Conservative default: enough for `gh` CLI repo/user/org operations, but\n// without `workflow` (can dispatch CI), `write:packages` (can publish\n// packages), or `project`. Operators who need those can opt in via\n// MOM_GITHUB_OAUTH_SCOPES to keep the blast radius of a compromised agent\n// host explicit and configurable.\nconst DEFAULT_GITHUB_OAUTH_SCOPES = [\"repo\", \"read:user\", \"user:email\", \"read:org\", \"gist\"];\n\nfunction resolveScopesFromEnv(envKey: string, fallback: string[]): string[] {\n const raw = process.env[envKey]?.trim();\n if (!raw) return fallback;\n\n const scopes = raw\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean);\n\n return scopes.length > 0 ? scopes : fallback;\n}\n\nfunction resolveGoogleWorkspaceCliScopes(): string[] {\n return resolveScopesFromEnv(\n \"MOM_GOOGLE_WORKSPACE_CLI_OAUTH_SCOPES\",\n DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES,\n );\n}\n\nfunction resolveGitHubOAuthScopes(): string[] {\n return resolveScopesFromEnv(\"MOM_GITHUB_OAUTH_SCOPES\", DEFAULT_GITHUB_OAUTH_SCOPES);\n}\n\nfunction getBuiltinOAuthServices(): OAuthService[] {\n return [\n {\n id: \"github\",\n label: \"GitHub\",\n aliases: [\"github\", \"github_oauth\", \"gh_oauth\"],\n authorizationUrl: \"https://github.com/login/oauth/authorize\",\n tokenUrl: \"https://github.com/login/oauth/access_token\",\n scopes: resolveGitHubOAuthScopes(),\n clientIdEnvKey: \"GITHUB_OAUTH_CLIENT_ID\",\n clientSecretEnvKey: \"GITHUB_OAUTH_CLIENT_SECRET\",\n accessTokenEnvKey: \"GITHUB_OAUTH_ACCESS_TOKEN\",\n additionalAccessTokenEnvKeys: [\"GH_TOKEN\"],\n refreshTokenEnvKey: \"GITHUB_OAUTH_REFRESH_TOKEN\",\n },\n {\n id: \"google_workspace_cli\",\n label: \"Google Workspace CLI\",\n aliases: [\"google_workspace_cli\", \"gws\", \"googleworkspace\", \"google-workspace-cli\"],\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n scopes: resolveGoogleWorkspaceCliScopes(),\n clientIdEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_ID\",\n clientSecretEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_SECRET\",\n authorizationParams: {\n access_type: \"offline\",\n include_granted_scopes: \"true\",\n prompt: \"consent\",\n },\n fileOutput: {\n type: \"authorized_user\",\n relativePath: \"gws.json\",\n targetPath: \"/root/.config/gws/credentials.json\",\n },\n },\n ];\n}\n\nexport function getOAuthServices(): OAuthService[] {\n const raw = process.env.MOM_OAUTH_SERVICES_JSON?.trim();\n const builtins = getBuiltinOAuthServices();\n if (!raw) return builtins;\n\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) return builtins;\n\n const custom = parsed\n .map((entry): OAuthService | null => {\n if (!entry || typeof entry !== \"object\") return null;\n const obj = entry as Record<string, unknown>;\n const id = typeof obj.id === \"string\" ? obj.id.trim() : \"\";\n const label = typeof obj.label === \"string\" ? obj.label.trim() : \"\";\n const authorizationUrl =\n typeof obj.authorizationUrl === \"string\" ? obj.authorizationUrl.trim() : \"\";\n const tokenUrl = typeof obj.tokenUrl === \"string\" ? obj.tokenUrl.trim() : \"\";\n const clientIdEnvKey =\n typeof obj.clientIdEnvKey === \"string\" ? obj.clientIdEnvKey.trim() : \"\";\n const clientSecretEnvKey =\n typeof obj.clientSecretEnvKey === \"string\" ? obj.clientSecretEnvKey.trim() : \"\";\n const accessTokenEnvKey =\n typeof obj.accessTokenEnvKey === \"string\" ? obj.accessTokenEnvKey.trim() : undefined;\n if (\n !id ||\n !label ||\n !authorizationUrl ||\n !tokenUrl ||\n !clientIdEnvKey ||\n !clientSecretEnvKey\n ) {\n return null;\n }\n\n let fileOutput: OAuthService[\"fileOutput\"];\n if (obj.fileOutput && typeof obj.fileOutput === \"object\") {\n const fileOutputObj = obj.fileOutput as Record<string, unknown>;\n const type = typeof fileOutputObj.type === \"string\" ? fileOutputObj.type.trim() : \"\";\n const relativePath =\n typeof fileOutputObj.relativePath === \"string\" ? fileOutputObj.relativePath.trim() : \"\";\n const targetPath =\n typeof fileOutputObj.targetPath === \"string\"\n ? fileOutputObj.targetPath.trim()\n : undefined;\n const envKey =\n typeof fileOutputObj.envKey === \"string\" ? fileOutputObj.envKey.trim() : undefined;\n if (type === \"authorized_user\" && relativePath) {\n fileOutput = { type: \"authorized_user\", relativePath, targetPath, envKey };\n }\n }\n\n return {\n id: id.toLowerCase(),\n label,\n aliases: Array.isArray(obj.aliases)\n ? obj.aliases\n .filter((v): v is string => typeof v === \"string\")\n .map((v) => v.toLowerCase())\n : [id.toLowerCase()],\n authorizationUrl,\n tokenUrl,\n scopes: Array.isArray(obj.scopes)\n ? obj.scopes.filter((v): v is string => typeof v === \"string\")\n : [],\n clientIdEnvKey,\n clientSecretEnvKey,\n accessTokenEnvKey,\n additionalAccessTokenEnvKeys: Array.isArray(obj.additionalAccessTokenEnvKeys)\n ? obj.additionalAccessTokenEnvKeys.filter((v): v is string => typeof v === \"string\")\n : undefined,\n refreshTokenEnvKey:\n typeof obj.refreshTokenEnvKey === \"string\" ? obj.refreshTokenEnvKey.trim() : undefined,\n authorizationParams:\n obj.authorizationParams && typeof obj.authorizationParams === \"object\"\n ? Object.fromEntries(\n Object.entries(obj.authorizationParams as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n )\n : undefined,\n fileOutput,\n };\n })\n .filter((service): service is OAuthService => service !== null);\n\n const byId = new Map<string, OAuthService>();\n for (const service of builtins) byId.set(service.id, service);\n for (const service of custom) byId.set(service.id, service);\n return [...byId.values()];\n } catch {\n return builtins;\n }\n}\n\nexport function resolveOAuthService(input: string): OAuthService | undefined {\n const normalized = input.trim().toLowerCase();\n if (!normalized) return undefined;\n return getOAuthServices().find(\n (service) => service.id === normalized || service.aliases.includes(normalized),\n );\n}\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"login\" && command !== \"/login\") {\n return null;\n }\n\n return { command: command as \"login\" | \"/login\" };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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","sourcesContent":["export type LoginCredentialKind = \"api_key\" | \"oauth\";\n\nexport interface OAuthAuthorizedUserFileOutput {\n type: \"authorized_user\";\n relativePath: string;\n targetPath?: string;\n envKey?: string;\n}\n\nexport interface OAuthService {\n id: string;\n label: string;\n aliases: string[];\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n clientIdEnvKey: string;\n clientSecretEnvKey: string;\n accessTokenEnvKey?: string;\n additionalAccessTokenEnvKeys?: string[];\n refreshTokenEnvKey?: string;\n authorizationParams?: Record<string, string>;\n fileOutput?: OAuthAuthorizedUserFileOutput;\n}\n\nexport interface ParsedLoginCommand {\n command: \"login\" | \"/login\" | \"/pi-login\";\n}\n\nconst DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES = [\n \"https://www.googleapis.com/auth/drive\",\n \"https://mail.google.com/\",\n \"https://www.googleapis.com/auth/calendar\",\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/documents\",\n \"https://www.googleapis.com/auth/chat.messages.create\",\n];\n\n// Conservative default: enough for `gh` CLI repo/user/org operations, but\n// without `workflow` (can dispatch CI), `write:packages` (can publish\n// packages), or `project`. Operators who need those can opt in via\n// MOM_GITHUB_OAUTH_SCOPES to keep the blast radius of a compromised agent\n// host explicit and configurable.\nconst DEFAULT_GITHUB_OAUTH_SCOPES = [\"repo\", \"read:user\", \"user:email\", \"read:org\", \"gist\"];\n\nfunction resolveScopesFromEnv(envKey: string, fallback: string[]): string[] {\n const raw = process.env[envKey]?.trim();\n if (!raw) return fallback;\n\n const scopes = raw\n .split(/[\\s,]+/)\n .map((scope) => scope.trim())\n .filter(Boolean);\n\n return scopes.length > 0 ? scopes : fallback;\n}\n\nfunction resolveGoogleWorkspaceCliScopes(): string[] {\n return resolveScopesFromEnv(\n \"MOM_GOOGLE_WORKSPACE_CLI_OAUTH_SCOPES\",\n DEFAULT_GOOGLE_WORKSPACE_CLI_SCOPES,\n );\n}\n\nfunction resolveGitHubOAuthScopes(): string[] {\n return resolveScopesFromEnv(\"MOM_GITHUB_OAUTH_SCOPES\", DEFAULT_GITHUB_OAUTH_SCOPES);\n}\n\nfunction getBuiltinOAuthServices(): OAuthService[] {\n return [\n {\n id: \"github\",\n label: \"GitHub\",\n aliases: [\"github\", \"github_oauth\", \"gh_oauth\"],\n authorizationUrl: \"https://github.com/login/oauth/authorize\",\n tokenUrl: \"https://github.com/login/oauth/access_token\",\n scopes: resolveGitHubOAuthScopes(),\n clientIdEnvKey: \"GITHUB_OAUTH_CLIENT_ID\",\n clientSecretEnvKey: \"GITHUB_OAUTH_CLIENT_SECRET\",\n accessTokenEnvKey: \"GITHUB_OAUTH_ACCESS_TOKEN\",\n additionalAccessTokenEnvKeys: [\"GH_TOKEN\"],\n refreshTokenEnvKey: \"GITHUB_OAUTH_REFRESH_TOKEN\",\n },\n {\n id: \"google_workspace_cli\",\n label: \"Google Workspace CLI\",\n aliases: [\"google_workspace_cli\", \"gws\", \"googleworkspace\", \"google-workspace-cli\"],\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n scopes: resolveGoogleWorkspaceCliScopes(),\n clientIdEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_ID\",\n clientSecretEnvKey: \"GOOGLE_WORKSPACE_CLI_CLIENT_SECRET\",\n authorizationParams: {\n access_type: \"offline\",\n include_granted_scopes: \"true\",\n prompt: \"consent\",\n },\n fileOutput: {\n type: \"authorized_user\",\n relativePath: \"gws.json\",\n targetPath: \"/root/.config/gws/credentials.json\",\n },\n },\n ];\n}\n\nexport function getOAuthServices(): OAuthService[] {\n const raw = process.env.MOM_OAUTH_SERVICES_JSON?.trim();\n const builtins = getBuiltinOAuthServices();\n if (!raw) return builtins;\n\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) return builtins;\n\n const custom = parsed\n .map((entry): OAuthService | null => {\n if (!entry || typeof entry !== \"object\") return null;\n const obj = entry as Record<string, unknown>;\n const id = typeof obj.id === \"string\" ? obj.id.trim() : \"\";\n const label = typeof obj.label === \"string\" ? obj.label.trim() : \"\";\n const authorizationUrl =\n typeof obj.authorizationUrl === \"string\" ? obj.authorizationUrl.trim() : \"\";\n const tokenUrl = typeof obj.tokenUrl === \"string\" ? obj.tokenUrl.trim() : \"\";\n const clientIdEnvKey =\n typeof obj.clientIdEnvKey === \"string\" ? obj.clientIdEnvKey.trim() : \"\";\n const clientSecretEnvKey =\n typeof obj.clientSecretEnvKey === \"string\" ? obj.clientSecretEnvKey.trim() : \"\";\n const accessTokenEnvKey =\n typeof obj.accessTokenEnvKey === \"string\" ? obj.accessTokenEnvKey.trim() : undefined;\n if (\n !id ||\n !label ||\n !authorizationUrl ||\n !tokenUrl ||\n !clientIdEnvKey ||\n !clientSecretEnvKey\n ) {\n return null;\n }\n\n let fileOutput: OAuthService[\"fileOutput\"];\n if (obj.fileOutput && typeof obj.fileOutput === \"object\") {\n const fileOutputObj = obj.fileOutput as Record<string, unknown>;\n const type = typeof fileOutputObj.type === \"string\" ? fileOutputObj.type.trim() : \"\";\n const relativePath =\n typeof fileOutputObj.relativePath === \"string\" ? fileOutputObj.relativePath.trim() : \"\";\n const targetPath =\n typeof fileOutputObj.targetPath === \"string\"\n ? fileOutputObj.targetPath.trim()\n : undefined;\n const envKey =\n typeof fileOutputObj.envKey === \"string\" ? fileOutputObj.envKey.trim() : undefined;\n if (type === \"authorized_user\" && relativePath) {\n fileOutput = { type: \"authorized_user\", relativePath, targetPath, envKey };\n }\n }\n\n return {\n id: id.toLowerCase(),\n label,\n aliases: Array.isArray(obj.aliases)\n ? obj.aliases\n .filter((v): v is string => typeof v === \"string\")\n .map((v) => v.toLowerCase())\n : [id.toLowerCase()],\n authorizationUrl,\n tokenUrl,\n scopes: Array.isArray(obj.scopes)\n ? obj.scopes.filter((v): v is string => typeof v === \"string\")\n : [],\n clientIdEnvKey,\n clientSecretEnvKey,\n accessTokenEnvKey,\n additionalAccessTokenEnvKeys: Array.isArray(obj.additionalAccessTokenEnvKeys)\n ? obj.additionalAccessTokenEnvKeys.filter((v): v is string => typeof v === \"string\")\n : undefined,\n refreshTokenEnvKey:\n typeof obj.refreshTokenEnvKey === \"string\" ? obj.refreshTokenEnvKey.trim() : undefined,\n authorizationParams:\n obj.authorizationParams && typeof obj.authorizationParams === \"object\"\n ? Object.fromEntries(\n Object.entries(obj.authorizationParams as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n ),\n )\n : undefined,\n fileOutput,\n };\n })\n .filter((service): service is OAuthService => service !== null);\n\n const byId = new Map<string, OAuthService>();\n for (const service of builtins) byId.set(service.id, service);\n for (const service of custom) byId.set(service.id, service);\n return [...byId.values()];\n } catch {\n return builtins;\n }\n}\n\nexport function resolveOAuthService(input: string): OAuthService | undefined {\n const normalized = input.trim().toLowerCase();\n if (!normalized) return undefined;\n return getOAuthServices().find(\n (service) => service.id === normalized || service.aliases.includes(normalized),\n );\n}\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"login\" && command !== \"/login\" && command !== \"/pi-login\") {\n return null;\n }\n\n return { command: command as \"login\" | \"/login\" | \"/pi-login\" };\n}\n"]}
|