@nick3/copilot-api 1.6.5 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-Db80RUHX.js → account-B4EBsn8H.js} +2 -2
- package/dist/{account-Db80RUHX.js.map → account-B4EBsn8H.js.map} +1 -1
- package/dist/{accounts-manager-B4eOuKSK.js → accounts-manager-BNVadA3o.js} +9 -6
- package/dist/accounts-manager-BNVadA3o.js.map +1 -0
- package/dist/admin/assets/{index-CH72K8cb.js → index-8eGib92I.js} +20 -20
- package/dist/admin/index.html +1 -1
- package/dist/{auth-Ba8K-1aX.js → auth-xiepl3lk.js} +3 -3
- package/dist/{auth-Ba8K-1aX.js.map → auth-xiepl3lk.js.map} +1 -1
- package/dist/{check-usage-ChbkyMqB.js → check-usage-3sFXqP0F.js} +3 -3
- package/dist/{check-usage-ChbkyMqB.js.map → check-usage-3sFXqP0F.js.map} +1 -1
- package/dist/{get-copilot-token-Buo3CA1x.js → get-copilot-token-COIPGosP.js} +2 -2
- package/dist/{get-copilot-token-Buo3CA1x.js.map → get-copilot-token-COIPGosP.js.map} +1 -1
- package/dist/main.js +3 -3
- package/dist/{poll-access-token-tKPQmH_6.js → poll-access-token-c2IYJJHM.js} +6 -2
- package/dist/poll-access-token-c2IYJJHM.js.map +1 -0
- package/dist/{request-outbound-IimJTlvY.js → request-outbound-DhI9-SrV.js} +1 -1
- package/dist/{request-outbound-C0g49Ab6.js → request-outbound-qyTeXbzy.js} +11 -5
- package/dist/request-outbound-qyTeXbzy.js.map +1 -0
- package/dist/{server-kG7N_-Aj.js → server-BhOhCgni.js} +71 -7
- package/dist/server-BhOhCgni.js.map +1 -0
- package/dist/{start-CXd89Ylu.js → start-DRl19gqI.js} +8 -7
- package/dist/start-DRl19gqI.js.map +1 -0
- package/package.json +1 -1
- package/dist/accounts-manager-B4eOuKSK.js.map +0 -1
- package/dist/poll-access-token-tKPQmH_6.js.map +0 -1
- package/dist/request-outbound-C0g49Ab6.js.map +0 -1
- package/dist/server-kG7N_-Aj.js.map +0 -1
- package/dist/start-CXd89Ylu.js.map +0 -1
package/dist/admin/index.html
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
})()
|
|
20
20
|
</script>
|
|
21
|
-
<script type="module" crossorigin src="/admin/assets/index-
|
|
21
|
+
<script type="module" crossorigin src="/admin/assets/index-8eGib92I.js"></script>
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/admin/assets/index-B2qj1asn.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { g as getCopilotUsage, h as getDeviceCode, k as state, m as getGitHubUser, t as pollAccessToken } from "./poll-access-token-
|
|
2
|
-
import { d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, n as parseAccountType, p as removeAccountFromRegistry, r as addAccountToRegistry, u as loadAccountToken } from "./account-
|
|
1
|
+
import { g as getCopilotUsage, h as getDeviceCode, k as state, m as getGitHubUser, t as pollAccessToken } from "./poll-access-token-c2IYJJHM.js";
|
|
2
|
+
import { d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, n as parseAccountType, p as removeAccountFromRegistry, r as addAccountToRegistry, u as loadAccountToken } from "./account-B4EBsn8H.js";
|
|
3
3
|
import { r as ensurePaths } from "./paths-DGlr310R.js";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import consola from "consola";
|
|
@@ -232,4 +232,4 @@ const auth = defineCommand({
|
|
|
232
232
|
|
|
233
233
|
//#endregion
|
|
234
234
|
export { auth };
|
|
235
|
-
//# sourceMappingURL=auth-
|
|
235
|
+
//# sourceMappingURL=auth-xiepl3lk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-Ba8K-1aX.js","names":["accountType: AccountType","accountToRemove: { id: string; index: number } | undefined"],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport {\n addAccountToRegistry,\n listAccountsFromRegistry,\n loadAccountToken,\n loadRegistry,\n removeAccountFromRegistry,\n removeAccountToken,\n saveAccountToken,\n saveRegistry,\n} from \"./lib/accounts-registry\"\nimport { ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport {\n parseAccountType,\n type AccountMeta,\n type AccountType,\n} from \"./lib/types/account\"\nimport { getCopilotUsage } from \"./services/github/get-copilot-usage\"\nimport { getDeviceCode } from \"./services/github/get-device-code\"\nimport { getGitHubUser } from \"./services/github/get-user\"\nimport { pollAccessToken } from \"./services/github/poll-access-token\"\n\n/**\n * Fetch quota info for an account (used by auth ls -q)\n */\nasync function fetchQuotaInfo(account: AccountMeta): Promise<string> {\n try {\n const token = await loadAccountToken(account.id)\n if (!token) {\n return \" | Quota: (no token)\"\n }\n\n const usage = await getCopilotUsage({\n githubToken: token,\n accountType: account.accountType,\n })\n const premium = usage.quota_snapshots.premium_interactions\n\n return premium.unlimited ?\n \" | Quota: unlimited\"\n : ` | Quota: ${premium.remaining}/${premium.entitlement}`\n } catch (error) {\n consola.debug(`Failed to fetch quota for ${account.id}:`, error)\n return \" | Quota: (failed to fetch)\"\n }\n}\n\n/**\n * auth add - Add a new GitHub Copilot account\n */\nconst authAdd = defineCommand({\n meta: {\n name: \"add\",\n description: \"Add a new GitHub Copilot account\",\n },\n args: {\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type (individual, business, enterprise)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token after auth\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = args[\"show-token\"]\n\n let accountType: AccountType\n try {\n accountType = parseAccountType(args[\"account-type\"])\n } catch (error) {\n consola.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n\n await ensurePaths()\n\n // Start device code flow\n consola.info(\"Starting GitHub device code authentication...\")\n const deviceResponse = await getDeviceCode()\n consola.debug(\"Device code response:\", deviceResponse)\n\n consola.info(\n `Please enter the code \"${deviceResponse.user_code}\" at ${deviceResponse.verification_uri}`,\n )\n\n // Poll for access token\n const token = await pollAccessToken(deviceResponse)\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n\n // Get user info to determine account ID\n const user = await getGitHubUser({ githubToken: token, accountType })\n const accountId = user.login\n\n // Save token and check if account already exists\n await saveAccountToken(accountId, token)\n const existingAccounts = await listAccountsFromRegistry()\n const alreadyExists = existingAccounts.some((acc) => acc.id === accountId)\n\n if (alreadyExists) {\n // Touch registry file so a running server can hot-reload updated tokens.\n await saveRegistry(await loadRegistry())\n\n consola.success(\n `Account \"${accountId}\" already exists. Token has been updated.`,\n )\n } else {\n await addAccountToRegistry({\n id: accountId,\n accountType,\n addedAt: Date.now(),\n })\n consola.success(`Account \"${accountId}\" added successfully!`)\n }\n\n consola.info(`Account type: ${accountType}`)\n },\n})\n\n/**\n * auth ls - List all registered accounts\n */\nconst authLs = defineCommand({\n meta: {\n name: \"ls\",\n description: \"List all registered accounts\",\n },\n args: {\n \"show-quota\": {\n alias: \"q\",\n type: \"boolean\",\n default: false,\n description: \"Show quota information (requires API call)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n }\n\n await ensurePaths()\n\n const accounts = await listAccountsFromRegistry()\n\n if (accounts.length === 0) {\n consola.info(\"No accounts registered. Use 'auth add' to add an account.\")\n return\n }\n\n consola.info(`Found ${accounts.length} account(s):\\n`)\n\n for (const [i, account] of accounts.entries()) {\n const addedDate = new Date(account.addedAt).toLocaleString()\n\n const quotaInfo = args[\"show-quota\"] ? await fetchQuotaInfo(account) : \"\"\n\n console.log(\n ` ${i + 1}. ${account.id} (${account.accountType})${quotaInfo}`,\n )\n console.log(` Added: ${addedDate}\\n`)\n }\n },\n})\n\n/**\n * auth rm - Remove an account\n */\nconst authRm = defineCommand({\n meta: {\n name: \"rm\",\n description: \"Remove an account\",\n },\n args: {\n target: {\n type: \"positional\",\n description: \"Account ID or index (1-based)\",\n required: true,\n },\n force: {\n alias: \"f\",\n type: \"boolean\",\n default: false,\n description: \"Skip confirmation prompt\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n }\n\n await ensurePaths()\n\n const target = args.target\n const accounts = await listAccountsFromRegistry()\n\n if (accounts.length === 0) {\n consola.error(\"No accounts to remove.\")\n return\n }\n\n // Determine account to remove (by ID or index)\n let accountToRemove: { id: string; index: number } | undefined\n\n // Try parsing as index (1-based)\n const index = Number.parseInt(target, 10)\n if (!Number.isNaN(index) && index >= 1 && index <= accounts.length) {\n accountToRemove = { id: accounts[index - 1].id, index: index - 1 }\n } else {\n // Try finding by ID\n const foundIndex = accounts.findIndex((acc) => acc.id === target)\n if (foundIndex !== -1) {\n accountToRemove = { id: accounts[foundIndex].id, index: foundIndex }\n }\n }\n\n if (!accountToRemove) {\n consola.error(`Account \"${target}\" not found.`)\n consola.info(\"Use 'auth ls' to see available accounts.\")\n return\n }\n\n // Confirmation\n if (!args.force) {\n const confirmed = await consola.prompt(\n `Are you sure you want to remove account \"${accountToRemove.id}\"?`,\n { type: \"confirm\" },\n )\n if (!confirmed) {\n consola.info(\"Cancelled.\")\n return\n }\n }\n\n // Remove token file and registry entry\n await removeAccountToken(accountToRemove.id)\n await removeAccountFromRegistry(accountToRemove.id)\n\n consola.success(`Account \"${accountToRemove.id}\" removed.`)\n },\n})\n\n/**\n * Main auth command with subcommands\n */\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Manage GitHub Copilot accounts\",\n },\n subCommands: {\n add: authAdd,\n ls: authLs,\n rm: authRm,\n },\n args: {\n // Legacy args for backward compatibility (when no subcommand)\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type (individual, business, enterprise)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token after auth\",\n },\n },\n async run(ctx) {\n // Check if a subcommand was specified in rawArgs.\n // Only treat the *first* raw arg as a subcommand to avoid false positives\n // when flags accept values like \"add\"/\"ls\"/\"rm\".\n const firstArg = ctx.rawArgs[0]\n const hasSubCommand =\n firstArg === \"add\" || firstArg === \"ls\" || firstArg === \"rm\"\n\n // Backward compatibility: if no subcommand, run 'add'\n if (!hasSubCommand && authAdd.run) {\n await authAdd.run(ctx)\n }\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth-xiepl3lk.js","names":["accountType: AccountType","accountToRemove: { id: string; index: number } | undefined"],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport {\n addAccountToRegistry,\n listAccountsFromRegistry,\n loadAccountToken,\n loadRegistry,\n removeAccountFromRegistry,\n removeAccountToken,\n saveAccountToken,\n saveRegistry,\n} from \"./lib/accounts-registry\"\nimport { ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport {\n parseAccountType,\n type AccountMeta,\n type AccountType,\n} from \"./lib/types/account\"\nimport { getCopilotUsage } from \"./services/github/get-copilot-usage\"\nimport { getDeviceCode } from \"./services/github/get-device-code\"\nimport { getGitHubUser } from \"./services/github/get-user\"\nimport { pollAccessToken } from \"./services/github/poll-access-token\"\n\n/**\n * Fetch quota info for an account (used by auth ls -q)\n */\nasync function fetchQuotaInfo(account: AccountMeta): Promise<string> {\n try {\n const token = await loadAccountToken(account.id)\n if (!token) {\n return \" | Quota: (no token)\"\n }\n\n const usage = await getCopilotUsage({\n githubToken: token,\n accountType: account.accountType,\n })\n const premium = usage.quota_snapshots.premium_interactions\n\n return premium.unlimited ?\n \" | Quota: unlimited\"\n : ` | Quota: ${premium.remaining}/${premium.entitlement}`\n } catch (error) {\n consola.debug(`Failed to fetch quota for ${account.id}:`, error)\n return \" | Quota: (failed to fetch)\"\n }\n}\n\n/**\n * auth add - Add a new GitHub Copilot account\n */\nconst authAdd = defineCommand({\n meta: {\n name: \"add\",\n description: \"Add a new GitHub Copilot account\",\n },\n args: {\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type (individual, business, enterprise)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token after auth\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = args[\"show-token\"]\n\n let accountType: AccountType\n try {\n accountType = parseAccountType(args[\"account-type\"])\n } catch (error) {\n consola.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n\n await ensurePaths()\n\n // Start device code flow\n consola.info(\"Starting GitHub device code authentication...\")\n const deviceResponse = await getDeviceCode()\n consola.debug(\"Device code response:\", deviceResponse)\n\n consola.info(\n `Please enter the code \"${deviceResponse.user_code}\" at ${deviceResponse.verification_uri}`,\n )\n\n // Poll for access token\n const token = await pollAccessToken(deviceResponse)\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n\n // Get user info to determine account ID\n const user = await getGitHubUser({ githubToken: token, accountType })\n const accountId = user.login\n\n // Save token and check if account already exists\n await saveAccountToken(accountId, token)\n const existingAccounts = await listAccountsFromRegistry()\n const alreadyExists = existingAccounts.some((acc) => acc.id === accountId)\n\n if (alreadyExists) {\n // Touch registry file so a running server can hot-reload updated tokens.\n await saveRegistry(await loadRegistry())\n\n consola.success(\n `Account \"${accountId}\" already exists. Token has been updated.`,\n )\n } else {\n await addAccountToRegistry({\n id: accountId,\n accountType,\n addedAt: Date.now(),\n })\n consola.success(`Account \"${accountId}\" added successfully!`)\n }\n\n consola.info(`Account type: ${accountType}`)\n },\n})\n\n/**\n * auth ls - List all registered accounts\n */\nconst authLs = defineCommand({\n meta: {\n name: \"ls\",\n description: \"List all registered accounts\",\n },\n args: {\n \"show-quota\": {\n alias: \"q\",\n type: \"boolean\",\n default: false,\n description: \"Show quota information (requires API call)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n }\n\n await ensurePaths()\n\n const accounts = await listAccountsFromRegistry()\n\n if (accounts.length === 0) {\n consola.info(\"No accounts registered. Use 'auth add' to add an account.\")\n return\n }\n\n consola.info(`Found ${accounts.length} account(s):\\n`)\n\n for (const [i, account] of accounts.entries()) {\n const addedDate = new Date(account.addedAt).toLocaleString()\n\n const quotaInfo = args[\"show-quota\"] ? await fetchQuotaInfo(account) : \"\"\n\n console.log(\n ` ${i + 1}. ${account.id} (${account.accountType})${quotaInfo}`,\n )\n console.log(` Added: ${addedDate}\\n`)\n }\n },\n})\n\n/**\n * auth rm - Remove an account\n */\nconst authRm = defineCommand({\n meta: {\n name: \"rm\",\n description: \"Remove an account\",\n },\n args: {\n target: {\n type: \"positional\",\n description: \"Account ID or index (1-based)\",\n required: true,\n },\n force: {\n alias: \"f\",\n type: \"boolean\",\n default: false,\n description: \"Skip confirmation prompt\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n },\n async run({ args }) {\n if (args.verbose) {\n consola.level = 5\n }\n\n await ensurePaths()\n\n const target = args.target\n const accounts = await listAccountsFromRegistry()\n\n if (accounts.length === 0) {\n consola.error(\"No accounts to remove.\")\n return\n }\n\n // Determine account to remove (by ID or index)\n let accountToRemove: { id: string; index: number } | undefined\n\n // Try parsing as index (1-based)\n const index = Number.parseInt(target, 10)\n if (!Number.isNaN(index) && index >= 1 && index <= accounts.length) {\n accountToRemove = { id: accounts[index - 1].id, index: index - 1 }\n } else {\n // Try finding by ID\n const foundIndex = accounts.findIndex((acc) => acc.id === target)\n if (foundIndex !== -1) {\n accountToRemove = { id: accounts[foundIndex].id, index: foundIndex }\n }\n }\n\n if (!accountToRemove) {\n consola.error(`Account \"${target}\" not found.`)\n consola.info(\"Use 'auth ls' to see available accounts.\")\n return\n }\n\n // Confirmation\n if (!args.force) {\n const confirmed = await consola.prompt(\n `Are you sure you want to remove account \"${accountToRemove.id}\"?`,\n { type: \"confirm\" },\n )\n if (!confirmed) {\n consola.info(\"Cancelled.\")\n return\n }\n }\n\n // Remove token file and registry entry\n await removeAccountToken(accountToRemove.id)\n await removeAccountFromRegistry(accountToRemove.id)\n\n consola.success(`Account \"${accountToRemove.id}\" removed.`)\n },\n})\n\n/**\n * Main auth command with subcommands\n */\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Manage GitHub Copilot accounts\",\n },\n subCommands: {\n add: authAdd,\n ls: authLs,\n rm: authRm,\n },\n args: {\n // Legacy args for backward compatibility (when no subcommand)\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type (individual, business, enterprise)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token after auth\",\n },\n },\n async run(ctx) {\n // Check if a subcommand was specified in rawArgs.\n // Only treat the *first* raw arg as a subcommand to avoid false positives\n // when flags accept values like \"add\"/\"ls\"/\"rm\".\n const firstArg = ctx.rawArgs[0]\n const hasSubCommand =\n firstArg === \"add\" || firstArg === \"ls\" || firstArg === \"rm\"\n\n // Backward compatibility: if no subcommand, run 'add'\n if (!hasSubCommand && authAdd.run) {\n await authAdd.run(ctx)\n }\n },\n})\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _ as HTTPError, g as getCopilotUsage, h as getDeviceCode, k as state, m as getGitHubUser, t as pollAccessToken } from "./poll-access-token-
|
|
1
|
+
import { _ as HTTPError, g as getCopilotUsage, h as getDeviceCode, k as state, m as getGitHubUser, t as pollAccessToken } from "./poll-access-token-c2IYJJHM.js";
|
|
2
2
|
import { r as ensurePaths, t as PATHS } from "./paths-DGlr310R.js";
|
|
3
|
-
import "./get-copilot-token-
|
|
3
|
+
import "./get-copilot-token-COIPGosP.js";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
@@ -79,4 +79,4 @@ const checkUsage = defineCommand({
|
|
|
79
79
|
|
|
80
80
|
//#endregion
|
|
81
81
|
export { checkUsage };
|
|
82
|
-
//# sourceMappingURL=check-usage-
|
|
82
|
+
//# sourceMappingURL=check-usage-3sFXqP0F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-usage-
|
|
1
|
+
{"version":3,"file":"check-usage-3sFXqP0F.js","names":[],"sources":["../src/lib/token.ts","../src/check-usage.ts"],"sourcesContent":["import consola from \"consola\"\nimport fs from \"node:fs/promises\"\nimport { setTimeout as delay } from \"node:timers/promises\"\n\nimport { isOpencodeOauthApp } from \"~/lib/api-config\"\nimport { PATHS } from \"~/lib/paths\"\nimport { getCopilotToken } from \"~/services/github/get-copilot-token\"\nimport { getCopilotUsage } from \"~/services/github/get-copilot-usage\"\nimport { getDeviceCode } from \"~/services/github/get-device-code\"\nimport { getGitHubUser } from \"~/services/github/get-user\"\nimport { pollAccessToken } from \"~/services/github/poll-access-token\"\n\nimport { HTTPError } from \"./error\"\nimport { state } from \"./state\"\n\nlet copilotRefreshLoopController: AbortController | null = null\n\nexport const stopCopilotRefreshLoop = () => {\n if (!copilotRefreshLoopController) {\n return\n }\n\n copilotRefreshLoopController.abort()\n copilotRefreshLoopController = null\n}\n\nconst readGithubToken = () => fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n\nconst writeGithubToken = (token: string) =>\n fs.writeFile(PATHS.GITHUB_TOKEN_PATH, token)\n\nexport const setupCopilotToken = async () => {\n if (isOpencodeOauthApp()) {\n if (!state.githubToken) throw new Error(`opencode token not found`)\n\n state.copilotToken = state.githubToken\n\n consola.debug(\"GitHub Copilot token set from opencode auth token\")\n if (state.showToken) {\n consola.info(\"Copilot token:\", state.copilotToken)\n }\n\n stopCopilotRefreshLoop()\n return\n }\n\n const { token, refresh_in } = await getCopilotToken()\n state.copilotToken = token\n\n // Display the Copilot token to the screen\n consola.debug(\"GitHub Copilot Token fetched successfully!\")\n if (state.showToken) {\n consola.info(\"Copilot token:\", token)\n }\n\n stopCopilotRefreshLoop()\n\n const controller = new AbortController()\n copilotRefreshLoopController = controller\n\n runCopilotRefreshLoop(refresh_in, controller.signal)\n .catch(() => {\n consola.warn(\"Copilot token refresh loop stopped\")\n })\n .finally(() => {\n if (copilotRefreshLoopController === controller) {\n copilotRefreshLoopController = null\n }\n })\n}\n\nconst REFRESH_POLL_INTERVAL_MS = 15_000\nconst EARLY_REFRESH_BUFFER_MS = 60_000\nconst RETRY_REFRESH_DELAY_MS = 15_000\nconst MIN_REFRESH_DELAY_MS = 1_000\n\nexport const getRefreshDeadlineMs = (\n refreshIn: number,\n nowMs: number = Date.now(),\n) =>\n nowMs\n + Math.max(refreshIn * 1000 - EARLY_REFRESH_BUFFER_MS, MIN_REFRESH_DELAY_MS)\n\n// Use short wall-clock chunks so the next wake after sleep notices elapsed time\n// quickly, without relying on the server's absolute expires_at matching local time.\nexport const getRefreshPollDelayMs = (\n refreshAtMs: number,\n nowMs: number = Date.now(),\n) => Math.min(Math.max(refreshAtMs - nowMs, 0), REFRESH_POLL_INTERVAL_MS)\n\nconst runCopilotRefreshLoop = async (\n refreshIn: number,\n signal: AbortSignal,\n) => {\n let refreshAtMs = getRefreshDeadlineMs(refreshIn)\n\n while (!signal.aborted) {\n const nextDelayMs = getRefreshPollDelayMs(refreshAtMs)\n if (nextDelayMs > 0) {\n await delay(nextDelayMs, undefined, { signal })\n continue\n }\n\n consola.debug(\"Refreshing Copilot token\")\n\n try {\n const { token, refresh_in } = await getCopilotToken()\n state.copilotToken = token\n refreshAtMs = getRefreshDeadlineMs(refresh_in)\n consola.debug(\"Copilot token refreshed\")\n if (state.showToken) {\n consola.info(\"Refreshed Copilot token:\", token)\n }\n } catch (error) {\n consola.error(\"Failed to refresh Copilot token:\", error)\n refreshAtMs = Date.now() + RETRY_REFRESH_DELAY_MS\n consola.warn(\n `Retrying Copilot token refresh in ${RETRY_REFRESH_DELAY_MS / 1000}s`,\n )\n }\n }\n}\n\ninterface SetupGitHubTokenOptions {\n force?: boolean\n}\n\nexport async function setupGitHubToken(\n options?: SetupGitHubTokenOptions,\n): Promise<void> {\n try {\n const githubToken = await readGithubToken()\n\n if (githubToken && !options?.force) {\n state.githubToken = githubToken\n if (state.showToken) {\n consola.info(\"GitHub token:\", githubToken)\n }\n await logUser()\n\n return\n }\n\n consola.info(\"Not logged in, getting new access token\")\n const response = await getDeviceCode()\n consola.debug(\"Device code response:\", response)\n\n consola.info(\n `Please enter the code \"${response.user_code}\" in ${response.verification_uri}`,\n )\n\n const token = await pollAccessToken(response)\n await writeGithubToken(token)\n state.githubToken = token\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n await logUser()\n } catch (error) {\n if (error instanceof HTTPError) {\n consola.error(\"Failed to get GitHub token:\", await error.response.json())\n throw error\n }\n\n consola.error(\"Failed to get GitHub token:\", error)\n throw error\n }\n}\n\nexport async function logUser() {\n const user = await getGitHubUser()\n consola.info(`Logged in as ${user.login}`)\n\n const copilotUser = await getCopilotUsage()\n state.copilotApiUrl = copilotUser.endpoints.api\n}\n","import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;;;;AA0BA,MAAM,wBAAwB,GAAG,SAAS,MAAM,mBAAmB,OAAO;AAE1E,MAAM,oBAAoB,UACxB,GAAG,UAAU,MAAM,mBAAmB,MAAM;AAkG9C,eAAsB,iBACpB,SACe;AACf,KAAI;EACF,MAAM,cAAc,MAAM,iBAAiB;AAE3C,MAAI,eAAe,CAAC,SAAS,OAAO;AAClC,SAAM,cAAc;AACpB,OAAI,MAAM,UACR,SAAQ,KAAK,iBAAiB,YAAY;AAE5C,SAAM,SAAS;AAEf;;AAGF,UAAQ,KAAK,0CAA0C;EACvD,MAAM,WAAW,MAAM,eAAe;AACtC,UAAQ,MAAM,yBAAyB,SAAS;AAEhD,UAAQ,KACN,0BAA0B,SAAS,UAAU,OAAO,SAAS,mBAC9D;EAED,MAAM,QAAQ,MAAM,gBAAgB,SAAS;AAC7C,QAAM,iBAAiB,MAAM;AAC7B,QAAM,cAAc;AAEpB,MAAI,MAAM,UACR,SAAQ,KAAK,iBAAiB,MAAM;AAEtC,QAAM,SAAS;UACR,OAAO;AACd,MAAI,iBAAiB,WAAW;AAC9B,WAAQ,MAAM,+BAA+B,MAAM,MAAM,SAAS,MAAM,CAAC;AACzE,SAAM;;AAGR,UAAQ,MAAM,+BAA+B,MAAM;AACnD,QAAM;;;AAIV,eAAsB,UAAU;CAC9B,MAAM,OAAO,MAAM,eAAe;AAClC,SAAQ,KAAK,gBAAgB,KAAK,QAAQ;AAG1C,OAAM,iBADc,MAAM,iBAAiB,EACT,UAAU;;;;;ACrK9C,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;AACV,QAAM,aAAa;AACnB,QAAM,kBAAkB;AACxB,MAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;AACnE,QAAI,CAAC,KAAM,QAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;AAC9B,WAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;AAED,WAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;AACZ,WAAQ,MAAM,kCAAkC,IAAI;AACpD,WAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as githubHeaders, O as accountFromState, S as getGitHubApiBaseUrl, _ as HTTPError } from "./poll-access-token-
|
|
1
|
+
import { C as githubHeaders, O as accountFromState, S as getGitHubApiBaseUrl, _ as HTTPError } from "./poll-access-token-c2IYJJHM.js";
|
|
2
2
|
|
|
3
3
|
//#region src/services/github/get-copilot-token.ts
|
|
4
4
|
const getCopilotToken = async (account) => {
|
|
@@ -10,4 +10,4 @@ const getCopilotToken = async (account) => {
|
|
|
10
10
|
|
|
11
11
|
//#endregion
|
|
12
12
|
export { getCopilotToken as t };
|
|
13
|
-
//# sourceMappingURL=get-copilot-token-
|
|
13
|
+
//# sourceMappingURL=get-copilot-token-COIPGosP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-copilot-token-
|
|
1
|
+
{"version":3,"file":"get-copilot-token-COIPGosP.js","names":[],"sources":["../src/services/github/get-copilot-token.ts"],"sourcesContent":["import type { AccountContext } from \"~/lib/types/account\"\n\nimport { getGitHubApiBaseUrl, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { accountFromState } from \"~/lib/state\"\n\nexport const getCopilotToken = async (account?: AccountContext) => {\n const ctx = account ?? accountFromState()\n const response = await fetch(\n `${getGitHubApiBaseUrl()}/copilot_internal/v2/token`,\n {\n headers: githubHeaders(ctx),\n },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get Copilot token\", response)\n\n return (await response.json()) as GetCopilotTokenResponse\n}\n\n// Trimmed for the sake of simplicity\nexport interface GetCopilotTokenResponse {\n expires_at: number\n refresh_in: number\n token: string\n}\n"],"mappings":";;;AAMA,MAAa,kBAAkB,OAAO,YAA6B;CACjE,MAAM,MAAM,WAAW,kBAAkB;CACzC,MAAM,WAAW,MAAM,MACrB,GAAG,qBAAqB,CAAC,6BACzB,EACE,SAAS,cAAc,IAAI,EAC5B,CACF;AAED,KAAI,CAAC,SAAS,GAAI,OAAM,IAAI,UAAU,+BAA+B,SAAS;AAE9E,QAAQ,MAAM,SAAS,MAAM"}
|
package/dist/main.js
CHANGED
|
@@ -20,10 +20,10 @@ const args = parseArgs(process.argv, cliArgs);
|
|
|
20
20
|
if (typeof args["api-home"] === "string") process.env.COPILOT_API_HOME = args["api-home"];
|
|
21
21
|
if (typeof args["oauth-app"] === "string") process.env.COPILOT_API_OAUTH_APP = args["oauth-app"];
|
|
22
22
|
if (typeof args["enterprise-url"] === "string") process.env.COPILOT_API_ENTERPRISE_URL = args["enterprise-url"];
|
|
23
|
-
const { auth } = await import("./auth-
|
|
24
|
-
const { checkUsage } = await import("./check-usage-
|
|
23
|
+
const { auth } = await import("./auth-xiepl3lk.js");
|
|
24
|
+
const { checkUsage } = await import("./check-usage-3sFXqP0F.js");
|
|
25
25
|
const { debug } = await import("./debug-BJfZVBB7.js");
|
|
26
|
-
const { start } = await import("./start-
|
|
26
|
+
const { start } = await import("./start-DRl19gqI.js");
|
|
27
27
|
await runMain(defineCommand({
|
|
28
28
|
meta: {
|
|
29
29
|
name: "copilot-api",
|
|
@@ -56,6 +56,7 @@ function buildOutboundHeadersSnapshot(headers) {
|
|
|
56
56
|
const snapshot = {
|
|
57
57
|
xRequestId: void 0,
|
|
58
58
|
xAgentTaskId: void 0,
|
|
59
|
+
xInteractionId: void 0,
|
|
59
60
|
xInteractionType: void 0,
|
|
60
61
|
openaiIntent: void 0,
|
|
61
62
|
userAgent: void 0
|
|
@@ -67,6 +68,9 @@ function buildOutboundHeadersSnapshot(headers) {
|
|
|
67
68
|
case "x-agent-task-id":
|
|
68
69
|
snapshot.xAgentTaskId = value;
|
|
69
70
|
break;
|
|
71
|
+
case "x-interaction-id":
|
|
72
|
+
snapshot.xInteractionId = value;
|
|
73
|
+
break;
|
|
70
74
|
case "x-interaction-type":
|
|
71
75
|
snapshot.xInteractionType = value;
|
|
72
76
|
break;
|
|
@@ -208,7 +212,7 @@ const getOpencodeVersion = () => {
|
|
|
208
212
|
};
|
|
209
213
|
const OPENCODE_VERSION = "opencode/1.3.15";
|
|
210
214
|
const OPENCODE_LLM_USER_AGENT = "opencode/1.3.15 ai-sdk/provider-utils/4.0.21 runtime/bun/1.3.11, opencode/1.3.15";
|
|
211
|
-
const COPILOT_VERSION = "0.44.
|
|
215
|
+
const COPILOT_VERSION = "0.44.2";
|
|
212
216
|
const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`;
|
|
213
217
|
const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`;
|
|
214
218
|
const CLAUDE_AGENT_USER_AGENT = "vscode_claude_code/2.1.98 (external, sdk-ts, agent-sdk/0.2.98)";
|
|
@@ -758,4 +762,4 @@ async function pollAccessToken(deviceCode, options) {
|
|
|
758
762
|
|
|
759
763
|
//#endregion
|
|
760
764
|
export { captureOutboundHeadersSnapshot as A, githubHeaders as C, prepareMessageProxyHeaders as D, prepareInteractionHeaders as E, requestContext as M, resolveTraceId as N, accountFromState as O, initOpencodeVersion as P, getGitHubApiBaseUrl as S, prepareForCompact as T, HTTPError as _, cacheVsCodeSessionId as a, copilotHeaders as b, getUUID as c, parseUserIdMetadata as d, resolveAffinityKey as f, getCopilotUsage as g, getDeviceCode as h, cacheVsCodeDeviceId as i, consumeOutboundHeadersSnapshot as j, state as k, isNullish as l, getGitHubUser as m, cacheMacMachineId as n, generateRequestIdFromPayload as o, sleep as p, cacheVSCodeVersion as r, getRootSessionId as s, pollAccessToken as t, normalizeStableSessionId as u, forwardError as v, normalizeDomain as w, copilotModelsHeaders as x, copilotBaseUrl as y };
|
|
761
|
-
//# sourceMappingURL=poll-access-token-
|
|
765
|
+
//# sourceMappingURL=poll-access-token-c2IYJJHM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll-access-token-c2IYJJHM.js","names":["opencodeVersionCache: string | undefined","snapshot: OutboundHeadersSnapshot","state: State","headers: Record<string, string>","errorJson: unknown","folder: string","readFile","deviceId: string | undefined","vsCodeSessionRefreshTimer: ReturnType<typeof setTimeout> | null","parsed: unknown"],"sources":["../src/lib/opencode.ts","../src/lib/request-context.ts","../src/lib/state.ts","../src/lib/api-config.ts","../src/lib/error.ts","../src/services/github/get-copilot-usage.ts","../src/services/github/get-device-code.ts","../src/services/github/get-user.ts","../src/services/get-vscode-version.ts","../src/lib/deviceid.ts","../src/lib/utils.ts","../src/services/github/poll-access-token.ts"],"sourcesContent":["import consola from \"consola\"\nimport { exec } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport path from \"node:path\"\n\nconst execAsync = (command: string): Promise<string> => {\n return new Promise((resolve, reject) => {\n exec(command, (error, stdout) => {\n if (error) {\n reject(error)\n return\n }\n\n resolve(stdout)\n })\n })\n}\n\nlet opencodeVersionCache: string | undefined\n\nconst getGlobalNpmRoot = async (): Promise<string> => {\n const stdout = await execAsync(\"npm root -g\")\n return stdout.trim()\n}\n\nasync function resolveOpencodeVersion(): Promise<void> {\n try {\n const npmRootPath = await getGlobalNpmRoot()\n const opencodePackagePath = path.join(\n npmRootPath,\n \"opencode-ai\",\n \"package.json\",\n )\n const packageJson = await readFile(opencodePackagePath, \"utf8\")\n const { version } = JSON.parse(packageJson) as { version: string }\n opencodeVersionCache = version\n } catch (error) {\n consola.warn(`Failed to resolve opencode version`, error)\n }\n}\n\nexport const initOpencodeVersion = (): Promise<void> => {\n if (process.env.COPILOT_API_OAUTH_APP?.trim() !== \"opencode\") {\n return Promise.resolve()\n }\n return resolveOpencodeVersion()\n}\n\nexport const getCachedOpencodeVersion = (): string | undefined => {\n return opencodeVersionCache\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\"\n\nexport interface OutboundHeadersSnapshot {\n xRequestId?: string\n xAgentTaskId?: string\n xInteractionId?: string\n xInteractionType?: string\n openaiIntent?: string\n userAgent?: string\n}\n\nexport interface RequestContext {\n traceId: string\n startTime: number\n userAgent: string\n sessionAffinity: string | undefined\n parentSessionId: string | undefined\n outboundHeaders?: OutboundHeadersSnapshot\n}\n\nconst TRACE_ID_MAX_LENGTH = 64\nconst TRACE_ID_PATTERN = /^\\w[\\w.-]*$/\n\nconst asyncLocalStorage = new AsyncLocalStorage<RequestContext>()\n\nexport const requestContext = {\n getStore: () => asyncLocalStorage.getStore(),\n run: <T>(context: RequestContext, callback: () => T) =>\n asyncLocalStorage.run(context, callback),\n}\n\nexport function generateTraceId(): string {\n const timestamp = Date.now().toString(36)\n const random = Math.random().toString(36).slice(2, 8)\n return `${timestamp}-${random}`\n}\n\nfunction buildOutboundHeadersSnapshot(\n headers: Record<string, string>,\n): OutboundHeadersSnapshot {\n const snapshot: OutboundHeadersSnapshot = {\n xRequestId: undefined,\n xAgentTaskId: undefined,\n xInteractionId: undefined,\n xInteractionType: undefined,\n openaiIntent: undefined,\n userAgent: undefined,\n }\n\n for (const [name, value] of Object.entries(headers)) {\n switch (name.toLowerCase()) {\n case \"x-request-id\": {\n snapshot.xRequestId = value\n break\n }\n case \"x-agent-task-id\": {\n snapshot.xAgentTaskId = value\n break\n }\n case \"x-interaction-id\": {\n snapshot.xInteractionId = value\n break\n }\n case \"x-interaction-type\": {\n snapshot.xInteractionType = value\n break\n }\n case \"openai-intent\": {\n snapshot.openaiIntent = value\n break\n }\n case \"user-agent\": {\n snapshot.userAgent = value\n break\n }\n default: {\n break\n }\n }\n }\n\n return snapshot\n}\n\nexport function captureOutboundHeadersSnapshot(\n headers: Record<string, string>,\n): void {\n const store = asyncLocalStorage.getStore()\n if (!store) return\n\n store.outboundHeaders = buildOutboundHeadersSnapshot(headers)\n}\n\nexport function getOutboundHeadersSnapshot():\n | OutboundHeadersSnapshot\n | undefined {\n return asyncLocalStorage.getStore()?.outboundHeaders\n}\n\nexport function consumeOutboundHeadersSnapshot():\n | OutboundHeadersSnapshot\n | undefined {\n const store = asyncLocalStorage.getStore()\n const snapshot = store?.outboundHeaders\n\n if (store) {\n store.outboundHeaders = undefined\n }\n\n return snapshot\n}\n\nexport function resolveTraceId(traceId: string | null | undefined): string {\n const candidate = traceId?.trim()\n\n if (\n !candidate\n || candidate.length > TRACE_ID_MAX_LENGTH\n || !TRACE_ID_PATTERN.test(candidate)\n ) {\n return generateTraceId()\n }\n\n return candidate\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { AccountContext, AccountType } from \"./types/account\"\n\nexport interface State {\n githubToken?: string\n copilotToken?: string\n\n accountType: AccountType\n vsCodeVersion?: string\n\n macMachineId?: string\n vsCodeSessionId?: string\n vsCodeDeviceId: string\n\n manualApprove: boolean\n rateLimitWait: boolean\n showToken: boolean\n\n // Rate limiting configuration\n rateLimitSeconds?: number\n lastRequestTimestamp?: number\n verbose: boolean\n\n copilotApiUrl?: string\n}\n\nexport const state: State = {\n accountType: \"individual\",\n manualApprove: false,\n rateLimitWait: false,\n showToken: false,\n verbose: false,\n vsCodeDeviceId: randomUUID(),\n}\n\n/**\n * Create an AccountContext from the current global state.\n * This is a compatibility layer for transitioning to multi-account support.\n * @throws Error if githubToken is not set in state\n */\nexport function accountFromState(): AccountContext {\n if (!state.githubToken) {\n throw new Error(\"GitHub token not set in state\")\n }\n return {\n githubToken: state.githubToken,\n copilotToken: state.copilotToken,\n ...(state.copilotApiUrl !== undefined ?\n { copilotApiUrl: state.copilotApiUrl }\n : {}),\n accountType: state.accountType,\n vsCodeVersion: state.vsCodeVersion,\n clientDeviceId: state.vsCodeDeviceId,\n clientMachineId: state.macMachineId,\n clientSessionId: state.vsCodeSessionId,\n }\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { CompactType } from \"./compact\"\nimport type { AccountContext } from \"./types/account\"\n\nimport { getCachedOpencodeVersion } from \"./opencode\"\nimport { requestContext } from \"./request-context\"\nimport { state } from \"./state\"\n\nexport const isOpencodeOauthApp = (): boolean => {\n return process.env.COPILOT_API_OAUTH_APP?.trim() === \"opencode\"\n}\n\nexport const normalizeDomain = (input: string): string => {\n return input\n .trim()\n .replace(/^https?:\\/\\//u, \"\")\n .replace(/\\/+$/u, \"\")\n}\n\nexport const getEnterpriseDomain = (): string | null => {\n const raw = (process.env.COPILOT_API_ENTERPRISE_URL ?? \"\").trim()\n if (!raw) return null\n const normalized = normalizeDomain(raw)\n return normalized || null\n}\n\nexport const getGitHubBaseUrl = (): string => {\n const resolvedDomain = getEnterpriseDomain()\n return resolvedDomain ? `https://${resolvedDomain}` : GITHUB_BASE_URL\n}\n\nexport const getGitHubApiBaseUrl = (): string => {\n const resolvedDomain = getEnterpriseDomain()\n return resolvedDomain ? `https://api.${resolvedDomain}` : GITHUB_API_BASE_URL\n}\n\nconst getOpencodeOauthHeaders = (): Record<string, string> => {\n return {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"User-Agent\": getOpencodeVersion(),\n }\n}\n\nconst getOpencodeLLMHeaders = (): Record<string, string> => {\n return {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"User-Agent\": OPENCODE_LLM_USER_AGENT,\n }\n}\n\nconst normalizeOpencodeUserAgent = (userAgent: string): string => {\n const candidate = userAgent.trim()\n const opencodeProduct = candidate.match(/^opencode\\/[^\\s,]+/u)?.[0]\n\n if (!opencodeProduct || candidate.includes(`, ${opencodeProduct}`)) {\n return candidate\n }\n\n return `${candidate}, ${opencodeProduct}`\n}\n\nexport const getOauthUrls = (): {\n deviceCodeUrl: string\n accessTokenUrl: string\n} => {\n const githubBaseUrl = getGitHubBaseUrl()\n\n return {\n deviceCodeUrl: `${githubBaseUrl}/login/device/code`,\n accessTokenUrl: `${githubBaseUrl}/login/oauth/access_token`,\n }\n}\n\ninterface OauthAppConfig {\n clientId: string\n headers: Record<string, string>\n scope: string\n}\n\nexport const getOauthAppConfig = (): OauthAppConfig => {\n if (isOpencodeOauthApp()) {\n return {\n clientId: OPENCODE_GITHUB_CLIENT_ID,\n headers: getOpencodeOauthHeaders(),\n scope: GITHUB_APP_SCOPES,\n }\n }\n\n return {\n clientId: GITHUB_CLIENT_ID,\n headers: standardHeaders(),\n scope: GITHUB_APP_SCOPES,\n }\n}\n\nexport const prepareForCompact = (\n headers: Record<string, string>,\n compactType?: CompactType,\n) => {\n if (compactType) {\n headers[\"x-initiator\"] = \"agent\"\n }\n}\n\nexport const prepareInteractionHeaders = (\n sessionId: string | undefined,\n isSubagent: boolean,\n headers: Record<string, string>,\n) => {\n const sendInteractionHeaders = !isOpencodeOauthApp()\n\n if (isSubagent) {\n headers[\"x-initiator\"] = \"agent\"\n if (sendInteractionHeaders) {\n headers[\"x-interaction-type\"] = \"conversation-subagent\"\n }\n }\n\n if (sessionId && sendInteractionHeaders) {\n headers[\"x-interaction-id\"] = sessionId\n }\n}\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nexport const getOpencodeVersion = () => {\n const version = getCachedOpencodeVersion()\n if (version) {\n return \"opencode/\" + version\n }\n return OPENCODE_VERSION\n}\n\nconst OPENCODE_VERSION = \"opencode/1.3.15\"\nconst OPENCODE_LLM_USER_AGENT =\n \"opencode/1.3.15 ai-sdk/provider-utils/4.0.21 runtime/bun/1.3.11, opencode/1.3.15\"\n\nconst COPILOT_VERSION = \"0.44.2\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\nconst CLAUDE_AGENT_USER_AGENT =\n \"vscode_claude_code/2.1.98 (external, sdk-ts, agent-sdk/0.2.98)\"\n\nconst API_VERSION = \"2025-10-01\"\n\nexport const copilotBaseUrl = (account: AccountContext): string => {\n const enterpriseDomain = getEnterpriseDomain()\n if (enterpriseDomain) {\n return `https://copilot-api.${enterpriseDomain}`\n }\n\n if (isOpencodeOauthApp()) {\n return \"https://api.githubcopilot.com\"\n }\n\n if (account.copilotApiUrl) {\n return account.copilotApiUrl\n }\n\n return account.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${account.accountType}.githubcopilot.com`\n}\n\nexport const prepareMessageProxyHeaders = (headers: Record<string, string>) => {\n if (isOpencodeOauthApp()) {\n return\n }\n\n // vscode copilot claude agent regenerates request id for\n // each request, keeping it consistent\n const requestIdValue = randomUUID()\n headers[\"x-agent-task-id\"] = requestIdValue\n headers[\"x-request-id\"] = requestIdValue\n\n // Consistent with vscode copilot claude agent\n headers[\"x-interaction-type\"] = \"messages-proxy\"\n headers[\"openai-intent\"] = \"messages-proxy\"\n headers[\"user-agent\"] = CLAUDE_AGENT_USER_AGENT\n\n delete headers[\"copilot-integration-id\"]\n}\n\nexport const githubUserHeaders = (\n account: AccountContext,\n): Record<string, string> => {\n if (isOpencodeOauthApp()) {\n return {\n Authorization: `Bearer ${account.githubToken}`,\n \"User-Agent\": getOpencodeVersion(),\n }\n }\n\n return {\n accept: \"application/vnd.github+json\",\n authorization: `token ${account.githubToken}`,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": \"2022-11-28\",\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n}\n\nexport const copilotModelsHeaders = (\n account: AccountContext,\n): Record<string, string> => {\n if (isOpencodeOauthApp()) {\n const token = account.copilotToken ?? account.githubToken\n return {\n Authorization: `Bearer ${token}`,\n \"User-Agent\": getOpencodeVersion(),\n }\n }\n const headers = githubCopilotHeaders(account)\n headers[\"x-interaction-type\"] = \"model-access\"\n headers[\"openai-intent\"] = \"model-access\"\n delete headers[\"x-interaction-id\"]\n delete headers[\"content-type\"]\n return headers\n}\n\nexport const copilotHeaders = (\n account: AccountContext,\n vision: boolean = false,\n requestId?: string,\n): Record<string, string> => {\n if (isOpencodeOauthApp()) {\n const token = account.copilotToken ?? account.githubToken\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n ...getOpencodeLLMHeaders(),\n \"Openai-Intent\": \"conversation-edits\",\n }\n\n const store = requestContext.getStore()\n const userAgent = store?.userAgent.trim()\n // Real opencode traffic already carries a versioned opencode/* UA,\n // so prefer the inbound header to keep upstream behavior aligned.\n if (userAgent?.startsWith(\"opencode/\")) {\n headers[\"User-Agent\"] = normalizeOpencodeUserAgent(userAgent)\n }\n\n if (store?.sessionAffinity) {\n headers[\"x-session-affinity\"] = store.sessionAffinity\n }\n\n if (store?.parentSessionId) {\n headers[\"x-parent-session-id\"] = store.parentSessionId\n }\n\n if (vision) headers[\"Copilot-Vision-Request\"] = \"true\"\n\n return headers\n }\n\n return githubCopilotHeaders(account, vision, requestId)\n}\n\nconst githubCopilotHeaders = (\n account: AccountContext,\n vision: boolean = false,\n requestId?: string,\n): Record<string, string> => {\n const resolvedRequestId = requestId ?? randomUUID()\n const resolvedDeviceId = account.clientDeviceId ?? state.vsCodeDeviceId\n const resolvedMachineId = account.clientMachineId ?? state.macMachineId\n const resolvedSessionId = account.clientSessionId ?? state.vsCodeSessionId\n const headers: Record<string, string> = {\n Authorization: `Bearer ${account.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": \"vscode-chat\",\n \"editor-version\": `vscode/${account.vsCodeVersion}`,\n \"editor-device-id\": resolvedDeviceId,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"openai-intent\": \"conversation-agent\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": resolvedRequestId,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n \"x-agent-task-id\": resolvedRequestId,\n \"x-interaction-type\": \"conversation-agent\",\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n if (resolvedMachineId) {\n headers[\"vscode-machineid\"] = resolvedMachineId\n }\n\n if (resolvedSessionId) {\n headers[\"vscode-sessionid\"] = resolvedSessionId\n }\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL = \"https://api.github.com\"\nexport const githubHeaders = (\n account: AccountContext,\n): Record<string, string> => {\n if (isOpencodeOauthApp()) {\n return {\n Authorization: `Bearer ${account.githubToken}`,\n ...getOpencodeOauthHeaders(),\n }\n }\n\n return {\n authorization: `token ${account.githubToken}`,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n}\n\nexport const GITHUB_BASE_URL = \"https://github.com\"\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\nexport const OPENCODE_GITHUB_CLIENT_ID = \"Ov23li8tweQw6odWQebz\"\n","import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n response: Response\n\n constructor(message: string, response: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport class CancelledError extends Error {}\n\nfunction getFallbackHttpErrorMessage(error: HTTPError): string {\n return error.message || `HTTP ${error.response.status}`\n}\n\nasync function readHttpErrorText(error: HTTPError): Promise<string> {\n try {\n const text = await error.response.text()\n return text || getFallbackHttpErrorMessage(error)\n } catch (readError) {\n consola.warn(\"Failed to read HTTP error response body:\", readError)\n return getFallbackHttpErrorMessage(error)\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message || \"Unknown error\"\n }\n\n if (typeof error === \"string\") {\n return error\n }\n\n if (\n typeof error === \"number\"\n || typeof error === \"boolean\"\n || typeof error === \"bigint\"\n ) {\n return `${error}`\n }\n\n return \"Unknown error\"\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n consola.error(\"Error occurred:\", error)\n\n if (error instanceof HTTPError) {\n if (error.response.status === 429) {\n for (const [name, value] of error.response.headers) {\n const lowerName = name.toLowerCase()\n if (lowerName === \"retry-after\" || lowerName.startsWith(\"x-\")) {\n c.header(name, value)\n }\n }\n }\n\n const errorText = await readHttpErrorText(error)\n let errorJson: unknown\n try {\n errorJson = JSON.parse(errorText)\n } catch {\n errorJson = errorText\n }\n consola.error(\"HTTP error:\", errorJson)\n return c.json(\n {\n error: {\n message: errorText,\n type: \"error\",\n },\n },\n error.response.status as ContentfulStatusCode,\n )\n }\n\n return c.json(\n {\n error: {\n message: getErrorMessage(error),\n type: \"error\",\n },\n },\n 500,\n )\n}\n","import type { AccountContext } from \"~/lib/types/account\"\n\nimport { getGitHubApiBaseUrl, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { accountFromState } from \"~/lib/state\"\n\nexport const getCopilotUsage = async (\n account?: AccountContext,\n): Promise<CopilotUsageResponse> => {\n const ctx = account ?? accountFromState()\n const response = await fetch(\n `${getGitHubApiBaseUrl()}/copilot_internal/user`,\n {\n headers: githubHeaders(ctx),\n },\n )\n\n if (!response.ok) {\n throw new HTTPError(\"Failed to get Copilot usage\", response)\n }\n\n return (await response.json()) as CopilotUsageResponse\n}\n\nexport interface QuotaDetail {\n entitlement: number\n overage_count: number\n overage_permitted: boolean\n percent_remaining: number\n quota_id: string\n quota_remaining: number\n remaining: number\n unlimited: boolean\n}\n\ninterface QuotaSnapshots {\n chat: QuotaDetail\n completions: QuotaDetail\n premium_interactions: QuotaDetail\n}\n\ninterface CopilotUsageResponse {\n login: string\n access_type_sku: string\n analytics_tracking_id: string\n assigned_date: string\n can_signup_for_limited: boolean\n chat_enabled: boolean\n copilot_plan: string\n organization_login_list: Array<unknown>\n organization_list: Array<unknown>\n quota_reset_date: string\n quota_snapshots: QuotaSnapshots\n endpoints: {\n api: string\n telemetry: string\n }\n}\n","import { getOauthAppConfig, getOauthUrls } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\n\nexport interface DeviceCodeOptions {\n overrideUrls?: {\n deviceCodeUrl: string\n accessTokenUrl: string\n }\n}\n\nexport async function getDeviceCode(\n options?: DeviceCodeOptions,\n): Promise<DeviceCodeResponse> {\n const { clientId, headers, scope } = getOauthAppConfig()\n const { deviceCodeUrl } = options?.overrideUrls ?? getOauthUrls()\n\n const response = await fetch(deviceCodeUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n client_id: clientId,\n scope,\n }),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get device code\", response)\n\n return (await response.json()) as DeviceCodeResponse\n}\n\nexport interface DeviceCodeResponse {\n device_code: string\n user_code: string\n verification_uri: string\n expires_in: number\n interval: number\n}\n","import type { AccountContext } from \"~/lib/types/account\"\n\nimport { getGitHubApiBaseUrl, githubUserHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { accountFromState, state } from \"~/lib/state\"\n\nconst resolveGitHubUserAccount = (account?: AccountContext): AccountContext => {\n if (account) {\n return account\n }\n\n if (!state.githubToken) {\n throw new Error(\"GitHub token not set\")\n }\n\n return accountFromState()\n}\n\nexport async function getGitHubUser(account?: AccountContext) {\n const resolvedAccount = resolveGitHubUserAccount(account)\n\n const response = await fetch(`${getGitHubApiBaseUrl()}/user`, {\n headers: githubUserHeaders(resolvedAccount),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get GitHub user\", response)\n\n return (await response.json()) as GithubUserResponse\n}\n\n// Trimmed for the sake of simplicity\nexport interface GithubUserResponse {\n login: string\n}\n","const FALLBACK = \"1.116.0\"\n\nexport async function getVSCodeVersion() {\n await Promise.resolve()\n return FALLBACK\n}\n","import consola from \"consola\"\nimport { randomUUID } from \"node:crypto\"\nimport path from \"node:path\"\n\nconst WINDOWS_DEVICE_ID_KEY = String.raw`\\SOFTWARE\\Microsoft\\DeveloperTools`\nconst WINDOWS_DEVICE_ID_NAME = \"deviceid\"\n\ntype RegistryArch = \"x86\" | \"x64\"\n\ninterface WinregConstructor {\n new (options: {\n hive: string\n key: string\n arch?: RegistryArch\n }): WinregRegistry\n HKCU: string\n REG_SZ: string\n}\n\ninterface WinregRegistry {\n get(\n name: string,\n callback: (error: RegistryError | null, item: RegistryItem | null) => void,\n ): void\n set(\n name: string,\n type: string,\n value: string,\n callback: (error: RegistryError | null) => void,\n ): void\n}\n\ninterface RegistryItem {\n value?: string\n}\n\ninterface RegistryError extends Error {\n code?: number | string\n}\n\nconst windows64Architectures = new Set([\"AMD64\", \"ARM64\", \"IA64\"])\n\nconst getPosixHomeDir = (): string => {\n if (!process.env.HOME) {\n throw new Error(\"Home directory not found\")\n }\n\n return process.env.HOME\n}\n\nconst getDeviceIdFilePath = (): string => {\n let folder: string\n\n switch (process.platform) {\n case \"darwin\": {\n folder = path.posix.join(\n getPosixHomeDir(),\n \"Library\",\n \"Application Support\",\n )\n break\n }\n case \"linux\": {\n folder =\n process.env.XDG_CACHE_HOME\n ?? path.posix.join(getPosixHomeDir(), \".cache\")\n break\n }\n default: {\n throw new Error(\"Unsupported platform\")\n }\n }\n\n return path.posix.join(folder, \"Microsoft\", \"DeveloperTools\", \"deviceid\")\n}\n\nconst isMissingFileError = (error: unknown): error is NodeJS.ErrnoException => {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\"\n}\n\nconst readStoredDeviceIdFile = async (\n filePath: string,\n): Promise<string | undefined> => {\n const { readFile } = await import(\"node:fs/promises\")\n\n try {\n return await readFile(filePath, \"utf8\")\n } catch (error) {\n if (isMissingFileError(error)) {\n return undefined\n }\n\n throw error\n }\n}\n\nconst writeStoredDeviceIdFile = async (\n filePath: string,\n deviceId: string,\n): Promise<void> => {\n const { mkdir, writeFile } = await import(\"node:fs/promises\")\n\n await mkdir(path.posix.dirname(filePath), { recursive: true })\n await writeFile(filePath, deviceId, \"utf8\")\n}\n\nconst getWindowsRegistryArch = (): RegistryArch | undefined => {\n const architecture = (\n process.env.PROCESSOR_ARCHITEW6432 ?? process.env.PROCESSOR_ARCHITECTURE\n )?.toUpperCase()\n\n return architecture && windows64Architectures.has(architecture) ?\n \"x64\"\n : undefined\n}\n\nconst loadWinreg = async (): Promise<WinregConstructor> => {\n const module = await import(\"winreg\")\n const winreg =\n \"default\" in module ? (module.default as unknown) : (module as unknown)\n\n return winreg as WinregConstructor\n}\n\nconst isMissingRegistryError = (error: RegistryError | null): boolean => {\n if (!error) {\n return false\n }\n\n const errorCode = Number(error.code)\n\n return Number.isFinite(errorCode) && errorCode === 1\n}\n\nconst createWindowsRegistry = async (): Promise<{\n registry: WinregRegistry\n regSz: string\n}> => {\n const Winreg = await loadWinreg()\n\n return {\n registry: new Winreg({\n hive: Winreg.HKCU,\n key: WINDOWS_DEVICE_ID_KEY,\n arch: getWindowsRegistryArch(),\n }),\n regSz: Winreg.REG_SZ,\n }\n}\n\nconst readRegistryString = async (\n registry: WinregRegistry,\n name: string,\n): Promise<string | undefined> => {\n return new Promise((resolve, reject) => {\n registry.get(name, (error, item) => {\n if (isMissingRegistryError(error)) {\n resolve(undefined)\n return\n }\n\n if (error) {\n reject(\n error instanceof Error ? error : new Error(\"Unknown registry error\"),\n )\n return\n }\n\n resolve(item?.value)\n })\n })\n}\n\nconst writeRegistryString = async ({\n registry,\n regSz,\n name,\n value,\n}: {\n registry: WinregRegistry\n regSz: string\n name: string\n value: string\n}): Promise<void> => {\n return new Promise((resolve, reject) => {\n registry.set(name, regSz, value, (error) => {\n if (error) {\n reject(\n error instanceof Error ? error : new Error(\"Unknown registry error\"),\n )\n return\n }\n\n resolve()\n })\n })\n}\n\nexport const getStoredVSCodeDeviceId = async (): Promise<\n string | undefined\n> => {\n switch (process.platform) {\n case \"win32\": {\n const { registry } = await createWindowsRegistry()\n\n return readRegistryString(registry, WINDOWS_DEVICE_ID_NAME)\n }\n case \"darwin\":\n case \"linux\": {\n return readStoredDeviceIdFile(getDeviceIdFilePath())\n }\n default: {\n throw new Error(\"Unsupported platform\")\n }\n }\n}\n\nconst setStoredVSCodeDeviceId = async (deviceId: string): Promise<void> => {\n switch (process.platform) {\n case \"win32\": {\n const { registry, regSz } = await createWindowsRegistry()\n\n await writeRegistryString({\n registry,\n regSz,\n name: WINDOWS_DEVICE_ID_NAME,\n value: deviceId,\n })\n return\n }\n case \"darwin\":\n case \"linux\": {\n await writeStoredDeviceIdFile(getDeviceIdFilePath(), deviceId)\n return\n }\n default: {\n throw new Error(\"Unsupported platform\")\n }\n }\n}\n\nconst createVSCodeDeviceId = (): string => randomUUID().toLowerCase()\n\nexport async function getVSCodeDeviceId(): Promise<string> {\n let deviceId: string | undefined\n\n try {\n deviceId = await getStoredVSCodeDeviceId()\n } catch (error) {\n consola.debug(\"Failed to read VSCode device id\", error)\n }\n\n if (deviceId) {\n return deviceId\n }\n\n const newDeviceId = createVSCodeDeviceId()\n\n try {\n await setStoredVSCodeDeviceId(newDeviceId)\n } catch (error) {\n consola.warn(\n \"Failed to persist VSCode device id, using ephemeral id\",\n error,\n )\n }\n\n return newDeviceId\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { createHash, randomUUID } from \"node:crypto\"\nimport { networkInterfaces } from \"node:os\"\n\nimport type { AnthropicMessagesPayload } from \"~/routes/messages/anthropic-types\"\n\nimport { getVSCodeVersion } from \"~/services/get-vscode-version\"\n\nimport { getVSCodeDeviceId } from \"./deviceid\"\nimport { state } from \"./state\"\n\nexport const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined =>\n value === null || value === undefined\n\nexport const cacheVSCodeVersion = async () => {\n const response = await getVSCodeVersion()\n state.vsCodeVersion = response\n\n consola.info(`Using VSCode version: ${response}`)\n}\n\nconst invalidMacAddresses = new Set([\n \"00:00:00:00:00:00\",\n \"ff:ff:ff:ff:ff:ff\",\n \"ac:de:48:00:11:22\",\n])\n\nfunction validateMacAddress(candidate: string): boolean {\n const tempCandidate = candidate.replaceAll(\"-\", \":\").toLowerCase()\n return !invalidMacAddresses.has(tempCandidate)\n}\n\nexport function getMac(): string | null {\n const ifaces = networkInterfaces()\n // eslint-disable-next-line guard-for-in\n for (const name in ifaces) {\n const networkInterface = ifaces[name]\n if (networkInterface) {\n for (const { mac } of networkInterface) {\n if (validateMacAddress(mac)) {\n return mac\n }\n }\n }\n }\n return null\n}\n\nexport const cacheMacMachineId = () => {\n const macAddress = getMac() ?? randomUUID()\n state.macMachineId = createHash(\"sha256\")\n .update(macAddress, \"utf8\")\n .digest(\"hex\")\n consola.debug(`Using machine ID: ${state.macMachineId}`)\n}\n\nexport const cacheVsCodeDeviceId = async () => {\n state.vsCodeDeviceId = await getVSCodeDeviceId()\n consola.debug(`Using VSCode device ID: ${state.vsCodeDeviceId}`)\n}\n\nconst SESSION_REFRESH_BASE_MS = 60 * 60 * 1000\nconst SESSION_REFRESH_JITTER_MS = 20 * 60 * 1000\nlet vsCodeSessionRefreshTimer: ReturnType<typeof setTimeout> | null = null\n\nconst generateSessionId = () => {\n state.vsCodeSessionId = randomUUID() + Date.now().toString()\n consola.debug(`Generated VSCode session ID: ${state.vsCodeSessionId}`)\n}\n\nexport const stopVsCodeSessionRefreshLoop = () => {\n if (vsCodeSessionRefreshTimer) {\n clearTimeout(vsCodeSessionRefreshTimer)\n vsCodeSessionRefreshTimer = null\n }\n}\n\nconst scheduleSessionIdRefresh = () => {\n const randomDelay = Math.floor(Math.random() * SESSION_REFRESH_JITTER_MS)\n const delay = SESSION_REFRESH_BASE_MS + randomDelay\n consola.debug(\n `Scheduling next VSCode session ID refresh in ${Math.round(\n delay / 1000,\n )} seconds`,\n )\n\n stopVsCodeSessionRefreshLoop()\n vsCodeSessionRefreshTimer = setTimeout(() => {\n try {\n generateSessionId()\n } catch (error) {\n consola.error(\"Failed to refresh session ID, rescheduling...\", error)\n } finally {\n scheduleSessionIdRefresh()\n }\n }, delay)\n}\n\nexport const cacheVsCodeSessionId = () => {\n stopVsCodeSessionRefreshLoop()\n generateSessionId()\n scheduleSessionIdRefresh()\n}\n\ninterface PayloadMessage {\n role?: string\n content?: string | Array<{ type?: string; text?: string }> | null\n type?: string\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null\n\nconst getUserIdJsonField = (\n userIdPayload: Record<string, unknown> | null,\n field: string,\n): string | null => {\n const value = userIdPayload?.[field]\n return typeof value === \"string\" && value.length > 0 ? value : null\n}\n\nconst parseJsonUserId = (userId: string): Record<string, unknown> | null => {\n try {\n const parsed: unknown = JSON.parse(userId)\n return isRecord(parsed) ? parsed : null\n } catch {\n return null\n }\n}\n\nexport const parseUserIdMetadata = (\n userId: string | undefined,\n): { safetyIdentifier: string | null; sessionId: string | null } => {\n if (!userId || typeof userId !== \"string\") {\n return { safetyIdentifier: null, sessionId: null }\n }\n\n const legacySafetyIdentifier =\n userId.match(/user_([^_]+)_account/)?.[1] ?? null\n const legacySessionId = userId.match(/_session_(.+)$/)?.[1] ?? null\n\n const parsedUserId =\n legacySafetyIdentifier && legacySessionId ? null : parseJsonUserId(userId)\n\n const safetyIdentifier =\n legacySafetyIdentifier\n ?? getUserIdJsonField(parsedUserId, \"device_id\")\n ?? getUserIdJsonField(parsedUserId, \"account_uuid\")\n const sessionId =\n legacySessionId ?? getUserIdJsonField(parsedUserId, \"session_id\")\n\n return { safetyIdentifier, sessionId }\n}\n\nconst findLastUserContent = (\n messages: Array<PayloadMessage>,\n): string | null => {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role === \"user\" && msg.content) {\n if (typeof msg.content === \"string\") {\n return msg.content\n } else if (Array.isArray(msg.content)) {\n const array = msg.content\n .filter((n) => n.type !== \"tool_result\")\n .map((n) => ({ ...n, cache_control: undefined }))\n if (array.length > 0) {\n return JSON.stringify(array)\n }\n }\n }\n }\n return null\n}\n\nexport const generateRequestIdFromPayload = (\n payload: {\n messages: string | Array<PayloadMessage> | undefined\n },\n sessionId?: string,\n): string => {\n const messages = payload.messages\n if (messages) {\n const lastUserContent =\n typeof messages === \"string\" ? messages : findLastUserContent(messages)\n\n if (lastUserContent) {\n return getUUID((sessionId ?? \"\") + lastUserContent)\n }\n }\n\n return randomUUID()\n}\n\nexport const normalizeStableSessionId = (\n sessionId?: string | null,\n): string | undefined => {\n const trimmedSessionId = sessionId?.trim()\n return trimmedSessionId ? getUUID(trimmedSessionId) : undefined\n}\n\nexport const getRootSessionId = (\n anthropicPayload: AnthropicMessagesPayload,\n c: Context,\n): string | undefined => {\n const userId = anthropicPayload.metadata?.user_id\n const sessionId =\n userId ?\n parseUserIdMetadata(userId).sessionId || undefined\n : c.req.header(\"x-session-id\")\n\n return normalizeStableSessionId(sessionId)\n}\n\nexport const getUUID = (content: string): string => {\n const uuidBytes = createHash(\"sha256\")\n .update(content)\n .digest()\n .subarray(0, 16)\n\n uuidBytes[6] = (uuidBytes[6] & 0x0f) | 0x40\n uuidBytes[8] = (uuidBytes[8] & 0x3f) | 0x80\n\n const uuidHex = uuidBytes.toString(\"hex\")\n\n return `${uuidHex.slice(0, 8)}-${uuidHex.slice(8, 12)}-${uuidHex.slice(12, 16)}-${uuidHex.slice(16, 20)}-${uuidHex.slice(20)}`\n}\n\nexport type AffinityKeySource =\n | \"prompt_cache_key\"\n | \"metadata_session_id\"\n | \"x_session_id\"\n | \"upstream_request_id_fallback\"\n\nexport interface ResolvedAffinityKey {\n requestId: string\n affinityKeyUsed: string\n affinityKeySource: AffinityKeySource\n}\n\n/**\n * Unified affinity key resolution with fixed priority:\n * 1. payload.prompt_cache_key (used as-is)\n * 2. metadata.user_id.session_id (normalized via getUUID)\n * 3. x-session-id header (normalized via getUUID)\n * 4. generateRequestIdFromPayload fallback (used as-is)\n */\nexport const resolveAffinityKey = (params: {\n promptCacheKey?: string | null\n metadataSessionId?: string | null\n headerSessionId?: string | null\n upstreamRequestId: string\n}): ResolvedAffinityKey => {\n const {\n promptCacheKey,\n metadataSessionId,\n headerSessionId,\n upstreamRequestId,\n } = params\n\n if (promptCacheKey) {\n return {\n requestId: promptCacheKey,\n affinityKeyUsed: promptCacheKey,\n affinityKeySource: \"prompt_cache_key\",\n }\n }\n\n if (metadataSessionId) {\n return {\n requestId: getUUID(metadataSessionId),\n affinityKeyUsed: metadataSessionId,\n affinityKeySource: \"metadata_session_id\",\n }\n }\n\n if (headerSessionId) {\n return {\n requestId: getUUID(headerSessionId),\n affinityKeyUsed: headerSessionId,\n affinityKeySource: \"x_session_id\",\n }\n }\n\n return {\n requestId: upstreamRequestId,\n affinityKeyUsed: upstreamRequestId,\n affinityKeySource: \"upstream_request_id_fallback\",\n }\n}\n","import consola from \"consola\"\n\nimport { getOauthAppConfig, getOauthUrls } from \"~/lib/api-config\"\nimport { CancelledError } from \"~/lib/error\"\nimport { sleep } from \"~/lib/utils\"\n\nimport type { DeviceCodeResponse } from \"./get-device-code\"\n\nexport interface PollAccessTokenOptions {\n overrideUrls?: {\n deviceCodeUrl: string\n accessTokenUrl: string\n }\n signal?: AbortSignal\n}\n\nexport async function pollAccessToken(\n deviceCode: DeviceCodeResponse,\n options?: PollAccessTokenOptions,\n): Promise<string> {\n const { clientId, headers } = getOauthAppConfig()\n const { accessTokenUrl } = options?.overrideUrls ?? getOauthUrls()\n\n // Interval is in seconds, we need to multiply by 1000 to get milliseconds\n // I'm also adding another second, just to be safe\n const sleepDuration = (deviceCode.interval + 1) * 1000\n consola.debug(`Polling access token with interval of ${sleepDuration}ms`)\n\n while (true) {\n if (options?.signal?.aborted) {\n throw new CancelledError(\"Authentication cancelled\")\n }\n\n const response = await fetch(accessTokenUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n client_id: clientId,\n device_code: deviceCode.device_code,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }),\n signal: options?.signal,\n })\n\n if (!response.ok) {\n await sleep(sleepDuration)\n consola.error(\"Failed to poll access token:\", await response.text())\n\n continue\n }\n\n const json = await response.json()\n consola.debug(\"Polling access token response:\", json)\n\n const { access_token } = json as AccessTokenResponse\n\n if (access_token) {\n return access_token\n } else {\n await sleep(sleepDuration)\n }\n }\n}\n\ninterface AccessTokenResponse {\n access_token: string\n token_type: string\n scope: string\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./paths-DGlr310R.js";
|
|
2
|
-
import { a as redactHeaders, i as getRequestOutboundStore, n as createRequestOutboundStore, r as getRedactedHeaderKeys, t as SENSITIVE_HEADER_PATTERNS } from "./request-outbound-
|
|
2
|
+
import { a as redactHeaders, i as getRequestOutboundStore, n as createRequestOutboundStore, r as getRedactedHeaderKeys, t as SENSITIVE_HEADER_PATTERNS } from "./request-outbound-qyTeXbzy.js";
|
|
3
3
|
|
|
4
4
|
export { SENSITIVE_HEADER_PATTERNS, createRequestOutboundStore, getRedactedHeaderKeys, getRequestOutboundStore, redactHeaders };
|
|
@@ -245,18 +245,24 @@ function migrateV11(db) {
|
|
|
245
245
|
PRAGMA user_version = 11;
|
|
246
246
|
`);
|
|
247
247
|
}
|
|
248
|
-
function
|
|
248
|
+
function migrateV12(db) {
|
|
249
|
+
if (!hasRequestLogColumn(db, "outbound_x_interaction_id")) db.run("ALTER TABLE request_log ADD COLUMN outbound_x_interaction_id TEXT;");
|
|
250
|
+
db.run("PRAGMA user_version = 12;");
|
|
251
|
+
}
|
|
252
|
+
function migrateV8ToV12(db, current) {
|
|
249
253
|
if (current < 8) migrateV8(db);
|
|
250
254
|
if (current < 9) migrateV9(db);
|
|
251
255
|
if (current < 10) migrateV10(db);
|
|
252
256
|
if (current < 11) migrateV11(db);
|
|
257
|
+
if (current < 12) migrateV12(db);
|
|
253
258
|
}
|
|
254
259
|
function migrateAdminDb(db) {
|
|
255
260
|
const current = db.query("PRAGMA user_version;").get()?.user_version ?? 0;
|
|
256
|
-
if (current >=
|
|
257
|
-
if (current ===
|
|
261
|
+
if (current >= 12) {
|
|
262
|
+
if (current === 12) migrateV12(db);
|
|
258
263
|
return;
|
|
259
264
|
}
|
|
265
|
+
if (current === 11) migrateV11(db);
|
|
260
266
|
if (current < 1) migrateV1(db);
|
|
261
267
|
if (current < 2) db.run(`
|
|
262
268
|
ALTER TABLE request_log ADD COLUMN user_id TEXT;
|
|
@@ -309,7 +315,7 @@ function migrateAdminDb(db) {
|
|
|
309
315
|
if (!hasRequestLogColumn(db, "upstream_error_message_raw")) db.run("ALTER TABLE request_log ADD COLUMN upstream_error_message_raw TEXT;");
|
|
310
316
|
db.run("PRAGMA user_version = 7;");
|
|
311
317
|
}
|
|
312
|
-
|
|
318
|
+
migrateV8ToV12(db, current);
|
|
313
319
|
}
|
|
314
320
|
|
|
315
321
|
//#endregion
|
|
@@ -502,4 +508,4 @@ function getRequestOutboundStore() {
|
|
|
502
508
|
|
|
503
509
|
//#endregion
|
|
504
510
|
export { redactHeaders as a, getAdminDbUserVersion as c, getRequestOutboundStore as i, createRequestOutboundStore as n, getAdminDb as o, getRedactedHeaderKeys as r, getAdminDbPath as s, SENSITIVE_HEADER_PATTERNS as t };
|
|
505
|
-
//# sourceMappingURL=request-outbound-
|
|
511
|
+
//# sourceMappingURL=request-outbound-qyTeXbzy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-outbound-qyTeXbzy.js","names":["sharedDb: Database | null","redacted: HeaderMap","disabledStore: RequestOutboundStoreApi","sharedStore: RequestOutboundStoreApi | null"],"sources":["../src/lib/admin-db.ts","../src/lib/request-outbound.ts"],"sourcesContent":["import { Database } from \"bun:sqlite\"\nimport consola from \"consola\"\nimport path from \"node:path\"\n\nimport { PATHS } from \"./paths\"\n\nconst ADMIN_DB_FILENAME = \"admin.sqlite\"\nconst DEFAULT_DB_PATH = path.join(PATHS.APP_DIR, ADMIN_DB_FILENAME)\n\nlet sharedDb: Database | null = null\nlet initialized = false\n\nconst INIT_WARN_THROTTLE_MS = 30_000\n\nlet lastInitWarnAtMs = 0\nlet suppressedInitWarnCount = 0\n\nfunction warnAdminDbInitFailure(error: unknown): void {\n const now = Date.now()\n\n if (now - lastInitWarnAtMs < INIT_WARN_THROTTLE_MS) {\n suppressedInitWarnCount++\n return\n }\n\n const suppressed = suppressedInitWarnCount\n suppressedInitWarnCount = 0\n lastInitWarnAtMs = now\n\n const suffix =\n suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : \"\"\n consola.warn(\n `Failed to initialize admin DB; admin features disabled${suffix}`,\n error,\n )\n}\n\nexport function getAdminDbPath(): string {\n return DEFAULT_DB_PATH\n}\n\nexport function openAdminDb(filePath: string = DEFAULT_DB_PATH): Database {\n return new Database(filePath)\n}\n\nexport function initAdminDb(db: Database): void {\n // Pragmas: prefer WAL for concurrent reads, keep writes fast.\n // Note: journal_mode=WAL is per-database and persists in the DB file.\n db.run(\"PRAGMA journal_mode = WAL;\")\n db.run(\"PRAGMA synchronous = NORMAL;\")\n db.run(\"PRAGMA busy_timeout = 3000;\")\n db.run(\"PRAGMA foreign_keys = ON;\")\n\n migrateAdminDb(db)\n}\n\nexport function getAdminDb(): Database {\n if (!sharedDb) {\n sharedDb = openAdminDb()\n }\n if (!initialized) {\n try {\n initAdminDb(sharedDb)\n initialized = true\n } catch (error) {\n // Admin DB is a best-effort feature; server should continue to run.\n warnAdminDbInitFailure(error)\n }\n }\n return sharedDb\n}\n\nexport function getAdminDbUserVersion(db: Database = getAdminDb()): number {\n try {\n const row = db.query(\"PRAGMA user_version;\").get() as {\n user_version?: number\n } | null\n return row?.user_version ?? 0\n } catch {\n return 0\n }\n}\n\nfunction hasRequestLogColumn(db: Database, columnName: string): boolean {\n return db\n .query(\"PRAGMA table_info(request_log);\")\n .all()\n .some((row) => (row as { name?: string }).name === columnName)\n}\n\nfunction migrateV1(db: Database): void {\n db.run(`\n CREATE TABLE IF NOT EXISTS request_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n request_id TEXT NOT NULL UNIQUE,\n\n started_at_ms INTEGER NOT NULL,\n finished_at_ms INTEGER,\n duration_ms INTEGER,\n ttfb_ms INTEGER,\n\n method TEXT NOT NULL,\n path TEXT NOT NULL,\n upstream_endpoint TEXT,\n stream INTEGER NOT NULL DEFAULT 0,\n\n account_id TEXT,\n account_type TEXT,\n cost_units REAL,\n client_model TEXT,\n upstream_model TEXT,\n\n client_ip TEXT,\n client_ip_source TEXT,\n user_agent TEXT,\n\n tokens_input INTEGER,\n tokens_output INTEGER,\n tokens_total INTEGER,\n tokens_cached_input INTEGER,\n usage_json TEXT,\n\n premium_remaining_before REAL,\n premium_remaining_after REAL,\n premium_remaining_diff REAL,\n premium_unlimited_before INTEGER,\n premium_unlimited_after INTEGER,\n\n http_status INTEGER,\n error_name TEXT,\n error_status INTEGER,\n error_message TEXT,\n selection_failure_reason TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_request_log_started_at\n ON request_log(started_at_ms DESC);\n CREATE INDEX IF NOT EXISTS idx_request_log_account_started_at\n ON request_log(account_id, started_at_ms DESC);\n CREATE INDEX IF NOT EXISTS idx_request_log_model_started_at\n ON request_log(upstream_model, started_at_ms DESC);\n CREATE INDEX IF NOT EXISTS idx_request_log_endpoint_started_at\n ON request_log(upstream_endpoint, started_at_ms DESC);\n CREATE INDEX IF NOT EXISTS idx_request_log_status_started_at\n ON request_log(http_status, started_at_ms DESC);\n\n PRAGMA user_version = 1;\n `)\n}\n\nfunction migrateV9(db: Database): void {\n db.run(`\n CREATE TABLE IF NOT EXISTS daily_premium_stats (\n date TEXT NOT NULL,\n account_id TEXT NOT NULL,\n request_count INTEGER NOT NULL DEFAULT 0,\n cost_units_sum REAL NOT NULL DEFAULT 0,\n tokens_total INTEGER NOT NULL DEFAULT 0,\n error_count INTEGER NOT NULL DEFAULT 0,\n updated_at_ms INTEGER NOT NULL,\n PRIMARY KEY (date, account_id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_daily_premium_stats_date\n ON daily_premium_stats(date);\n `)\n\n // Backfill from existing request_log (guard: table may not exist in edge cases)\n const hasRequestLog = db\n .query(\n \"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'request_log' LIMIT 1;\",\n )\n .get()\n\n if (hasRequestLog) {\n const nowMs = Date.now()\n db.run(\n `INSERT INTO daily_premium_stats\n (date, account_id, request_count, cost_units_sum, tokens_total, error_count, updated_at_ms)\n SELECT\n date(started_at_ms / 1000, 'unixepoch', 'localtime') AS date,\n account_id,\n COUNT(*) AS request_count,\n SUM(cost_units) AS cost_units_sum,\n COALESCE(SUM(tokens_total), 0) AS tokens_total,\n SUM(CASE WHEN error_name IS NOT NULL THEN 1 ELSE 0 END) AS error_count,\n ? AS updated_at_ms\n FROM request_log\n WHERE cost_units > 0\n AND account_id IS NOT NULL\n GROUP BY 1, 2\n ON CONFLICT(date, account_id) DO UPDATE SET\n request_count = excluded.request_count,\n cost_units_sum = excluded.cost_units_sum,\n tokens_total = excluded.tokens_total,\n error_count = excluded.error_count,\n updated_at_ms = excluded.updated_at_ms;`,\n [nowMs],\n )\n }\n\n db.run(\"PRAGMA user_version = 9;\")\n}\n\nfunction migrateV8(db: Database): void {\n db.run(`\n CREATE TABLE IF NOT EXISTS session_affinity (\n cache_key TEXT PRIMARY KEY,\n account_id TEXT NOT NULL,\n created_at_ms INTEGER NOT NULL,\n last_confirmed_at_ms INTEGER NOT NULL,\n last_used_at_ms INTEGER NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_session_affinity_last_used\n ON session_affinity(last_used_at_ms);\n\n CREATE INDEX IF NOT EXISTS idx_session_affinity_account\n ON session_affinity(account_id);\n\n PRAGMA user_version = 8;\n `)\n}\n\nfunction migrateV10(db: Database): void {\n db.run(`\n CREATE TABLE IF NOT EXISTS quota_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n account_id TEXT NOT NULL,\n snapshot_at_ms INTEGER NOT NULL,\n remaining INTEGER NOT NULL,\n entitlement INTEGER NOT NULL,\n unlimited INTEGER NOT NULL DEFAULT 0,\n source TEXT NOT NULL DEFAULT 'refresh'\n );\n\n CREATE INDEX IF NOT EXISTS idx_quota_snapshots_account_time\n ON quota_snapshots(account_id, snapshot_at_ms);\n\n CREATE INDEX IF NOT EXISTS idx_quota_snapshots_time\n ON quota_snapshots(snapshot_at_ms);\n `)\n\n const hasRequestLog = db\n .query(\n \"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'request_log' LIMIT 1;\",\n )\n .get()\n\n if (hasRequestLog) {\n db.run(`\n INSERT INTO quota_snapshots\n (account_id, snapshot_at_ms, remaining, entitlement, unlimited, source)\n SELECT\n account_id,\n finished_at_ms,\n CAST(premium_remaining_after AS INTEGER),\n 0,\n COALESCE(premium_unlimited_after, 0),\n 'backfill'\n FROM request_log\n WHERE premium_remaining_after IS NOT NULL\n AND account_id IS NOT NULL\n AND finished_at_ms IS NOT NULL\n ORDER BY finished_at_ms;\n `)\n }\n\n db.run(\"PRAGMA user_version = 10;\")\n}\n\nfunction migrateV11(db: Database): void {\n const outboundHeaderColumns = [\n \"outbound_x_request_id\",\n \"outbound_x_agent_task_id\",\n \"outbound_x_interaction_type\",\n \"outbound_openai_intent\",\n \"outbound_user_agent\",\n ] as const\n\n for (const columnName of outboundHeaderColumns) {\n if (!hasRequestLogColumn(db, columnName)) {\n db.run(`ALTER TABLE request_log ADD COLUMN ${columnName} TEXT;`)\n }\n }\n\n db.run(`\n CREATE TABLE IF NOT EXISTS request_outbound (\n request_id TEXT PRIMARY KEY,\n captured_at_ms INTEGER NOT NULL,\n http_status INTEGER NOT NULL,\n\n upstream_url TEXT NOT NULL,\n upstream_method TEXT NOT NULL,\n\n request_headers TEXT NOT NULL,\n request_body TEXT,\n request_body_kind TEXT NOT NULL,\n\n response_status INTEGER NOT NULL,\n response_headers TEXT NOT NULL,\n response_body TEXT,\n response_body_kind TEXT NOT NULL,\n\n FOREIGN KEY (request_id) REFERENCES request_log(request_id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_request_outbound_captured_at\n ON request_outbound(captured_at_ms DESC);\n\n PRAGMA user_version = 11;\n `)\n}\n\nfunction migrateV12(db: Database): void {\n if (!hasRequestLogColumn(db, \"outbound_x_interaction_id\")) {\n db.run(\"ALTER TABLE request_log ADD COLUMN outbound_x_interaction_id TEXT;\")\n }\n\n db.run(\"PRAGMA user_version = 12;\")\n}\n\nfunction migrateV8ToV12(db: Database, current: number): void {\n if (current < 8) {\n migrateV8(db)\n }\n\n if (current < 9) {\n migrateV9(db)\n }\n\n if (current < 10) {\n migrateV10(db)\n }\n\n if (current < 11) {\n migrateV11(db)\n }\n\n if (current < 12) {\n migrateV12(db)\n }\n}\n\n// eslint-disable-next-line complexity -- migration chain grows with each version\nfunction migrateAdminDb(db: Database): void {\n const row = db.query(\"PRAGMA user_version;\").get() as {\n user_version?: number\n } | null\n const current = row?.user_version ?? 0\n\n if (current >= 12) {\n if (current === 12) migrateV12(db)\n return\n }\n\n if (current === 11) {\n migrateV11(db)\n }\n\n if (current < 1) {\n migrateV1(db)\n }\n\n if (current < 2) {\n // v2: request_log session correlation fields\n db.run(`\n ALTER TABLE request_log ADD COLUMN user_id TEXT;\n ALTER TABLE request_log ADD COLUMN safety_identifier TEXT;\n ALTER TABLE request_log ADD COLUMN prompt_cache_key TEXT;\n ALTER TABLE request_log ADD COLUMN initiator TEXT;\n ALTER TABLE request_log ADD COLUMN upstream_request_id TEXT;\n\n PRAGMA user_version = 2;\n `)\n }\n\n if (current < 3) {\n // v3: index for session lookup\n db.run(`\n CREATE INDEX IF NOT EXISTS idx_request_log_session_finished\n ON request_log(\n prompt_cache_key,\n safety_identifier,\n finished_at_ms DESC\n )\n WHERE finished_at_ms IS NOT NULL\n AND tokens_input IS NOT NULL;\n\n PRAGMA user_version = 3;\n `)\n }\n\n if (current < 4) {\n // v4: index for session lookup by client model\n db.run(`\n CREATE INDEX IF NOT EXISTS idx_request_log_session_finished_by_client_model\n ON request_log(\n prompt_cache_key,\n safety_identifier,\n client_model,\n finished_at_ms DESC\n )\n WHERE finished_at_ms IS NOT NULL\n AND tokens_input IS NOT NULL;\n\n PRAGMA user_version = 4;\n `)\n }\n\n if (current < 5) {\n // v5: account affinity tracking columns\n db.run(`\n ALTER TABLE request_log ADD COLUMN affinity_hit INTEGER;\n ALTER TABLE request_log ADD COLUMN affinity_cache_key TEXT;\n\n PRAGMA user_version = 5;\n `)\n }\n\n if (current < 6) {\n // v6: explicit subagent request tracking column\n if (!hasRequestLogColumn(db, \"is_subagent\")) {\n db.run(\"ALTER TABLE request_log ADD COLUMN is_subagent INTEGER;\")\n }\n\n db.run(\"PRAGMA user_version = 6;\")\n }\n\n if (current < 7) {\n // v7: request-level affinity and upstream error observability columns\n if (!hasRequestLogColumn(db, \"affinity_key_used\")) {\n db.run(\"ALTER TABLE request_log ADD COLUMN affinity_key_used TEXT;\")\n }\n\n if (!hasRequestLogColumn(db, \"affinity_key_source\")) {\n db.run(\"ALTER TABLE request_log ADD COLUMN affinity_key_source TEXT;\")\n }\n\n if (!hasRequestLogColumn(db, \"selection_reason\")) {\n db.run(\"ALTER TABLE request_log ADD COLUMN selection_reason TEXT;\")\n }\n\n if (!hasRequestLogColumn(db, \"upstream_error_message_raw\")) {\n db.run(\n \"ALTER TABLE request_log ADD COLUMN upstream_error_message_raw TEXT;\",\n )\n }\n\n db.run(\"PRAGMA user_version = 7;\")\n }\n\n migrateV8ToV12(db, current)\n}\n","import type { Database } from \"bun:sqlite\"\n\nimport consola from \"consola\"\n\nimport { getAdminDb, getAdminDbPath, getAdminDbUserVersion } from \"./admin-db\"\n\nconst INSERT_WARN_THROTTLE_MS = 30_000\nconst STORE_INIT_WARN_THROTTLE_MS = 30_000\nconst STORE_INIT_RETRY_DELAY_MS = 30_000\n\nlet lastInsertWarnAtMs = 0\nlet suppressedInsertWarnCount = 0\nlet lastStoreInitWarnAtMs = 0\nlet suppressedStoreInitWarnCount = 0\nlet nextStoreRetryAtMs = 0\n\nexport const SENSITIVE_HEADER_PATTERNS = [\n /^authorization$/i,\n /^x-github-token$/i,\n /^cookie$/i,\n /^set-cookie$/i,\n /^proxy-authorization$/i,\n /^x-api-key$/i,\n /-token$/i,\n /-secret$/i,\n] as const\n\nfunction warnInsertFailure(error: unknown): void {\n const now = Date.now()\n\n if (now - lastInsertWarnAtMs < INSERT_WARN_THROTTLE_MS) {\n suppressedInsertWarnCount++\n return\n }\n\n const suppressed = suppressedInsertWarnCount\n suppressedInsertWarnCount = 0\n lastInsertWarnAtMs = now\n\n const suffix =\n suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : \"\"\n consola.warn(`Failed to insert request outbound${suffix}`, error)\n}\n\nfunction warnStoreInitFailure(error: unknown): void {\n const now = Date.now()\n\n if (now - lastStoreInitWarnAtMs < STORE_INIT_WARN_THROTTLE_MS) {\n suppressedStoreInitWarnCount++\n return\n }\n\n const suppressed = suppressedStoreInitWarnCount\n suppressedStoreInitWarnCount = 0\n lastStoreInitWarnAtMs = now\n\n const suffix =\n suppressed > 0 ? ` (suppressed ${suppressed} similar errors)` : \"\"\n consola.warn(`Request outbound store is disabled${suffix}`, error)\n}\n\ntype HeaderMap = Record<string, string>\n\nfunction shouldRedactHeader(key: string): boolean {\n return SENSITIVE_HEADER_PATTERNS.some((pattern) => pattern.test(key))\n}\n\nexport function getRedactedHeaderKeys(headers: HeaderMap): Array<string> {\n return Object.keys(headers).filter((key) => shouldRedactHeader(key))\n}\n\nexport function redactHeaders(headers: HeaderMap): HeaderMap {\n const redacted: HeaderMap = {}\n\n for (const [key, value] of Object.entries(headers)) {\n redacted[key] = shouldRedactHeader(key) ? \"***\" : value\n }\n\n return redacted\n}\n\nexport type OutboundCaptureInput = {\n requestId: string\n httpStatus: number\n upstreamUrl: string\n upstreamMethod: string\n requestHeaders: HeaderMap\n requestBody: string | null\n requestBodyKind: string\n responseStatus: number\n responseHeaders: HeaderMap\n responseBody: string | null\n responseBodyKind: string\n}\n\nexport type OutboundCaptureRow = OutboundCaptureInput & {\n capturedAtMs: number\n}\n\nexport type RequestOutboundStoreApi = {\n insert(input: OutboundCaptureInput): void\n getByRequestId(requestId: string): OutboundCaptureRow | null\n hasOutboundForIds(requestIds: Array<string>): Set<string>\n cleanupOrphans(): void\n meta(): {\n dbPath: string\n userVersion: number\n }\n}\n\nclass RequestOutboundStore implements RequestOutboundStoreApi {\n private readonly db: Database\n private readonly insertStmt: ReturnType<Database[\"query\"]>\n private readonly getByRequestIdStmt: ReturnType<Database[\"query\"]>\n private readonly cleanupOrphansStmt: ReturnType<Database[\"query\"]>\n\n constructor(db: Database) {\n this.db = db\n this.insertStmt = db.query(`\n INSERT OR REPLACE INTO request_outbound (\n request_id,\n captured_at_ms,\n http_status,\n upstream_url,\n upstream_method,\n request_headers,\n request_body,\n request_body_kind,\n response_status,\n response_headers,\n response_body,\n response_body_kind\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);\n `)\n this.getByRequestIdStmt = db.query(`\n SELECT\n request_id,\n captured_at_ms,\n http_status,\n upstream_url,\n upstream_method,\n request_headers,\n request_body,\n request_body_kind,\n response_status,\n response_headers,\n response_body,\n response_body_kind\n FROM request_outbound\n WHERE request_id = ?\n LIMIT 1;\n `)\n this.cleanupOrphansStmt = db.query(`\n DELETE FROM request_outbound\n WHERE NOT EXISTS (\n SELECT 1\n FROM request_log\n WHERE request_log.request_id = request_outbound.request_id\n );\n `)\n }\n\n insert(input: OutboundCaptureInput): void {\n try {\n this.insertStmt.run(\n input.requestId,\n Date.now(),\n input.httpStatus,\n input.upstreamUrl,\n input.upstreamMethod,\n JSON.stringify(redactHeaders(input.requestHeaders)),\n input.requestBody,\n input.requestBodyKind,\n input.responseStatus,\n JSON.stringify(input.responseHeaders),\n input.responseBody,\n input.responseBodyKind,\n )\n } catch (error) {\n warnInsertFailure(error)\n }\n }\n\n getByRequestId(requestId: string): OutboundCaptureRow | null {\n try {\n const row = this.getByRequestIdStmt.get(requestId) as\n | {\n request_id: string\n captured_at_ms: number\n http_status: number\n upstream_url: string\n upstream_method: string\n request_headers: string\n request_body: string | null\n request_body_kind: string\n response_status: number\n response_headers: string\n response_body: string | null\n response_body_kind: string\n }\n | null\n | undefined\n\n if (!row) {\n return null\n }\n\n return {\n requestId: row.request_id,\n capturedAtMs: row.captured_at_ms,\n httpStatus: row.http_status,\n upstreamUrl: row.upstream_url,\n upstreamMethod: row.upstream_method,\n requestHeaders: JSON.parse(row.request_headers) as HeaderMap,\n requestBody: row.request_body,\n requestBodyKind: row.request_body_kind,\n responseStatus: row.response_status,\n responseHeaders: JSON.parse(row.response_headers) as HeaderMap,\n responseBody: row.response_body,\n responseBodyKind: row.response_body_kind,\n }\n } catch (error) {\n consola.debug(\"Failed to fetch request outbound by request_id\", error)\n return null\n }\n }\n\n hasOutboundForIds(requestIds: Array<string>): Set<string> {\n if (requestIds.length === 0) return new Set()\n try {\n const placeholders = requestIds.map(() => \"?\").join(\", \")\n const stmt = this.db.query(\n `SELECT request_id FROM request_outbound WHERE request_id IN (${placeholders})`,\n )\n const rows = stmt.all(...requestIds) as Array<{ request_id: string }>\n return new Set(rows.map((r) => r.request_id))\n } catch (error) {\n consola.debug(\"Failed to batch-check request outbound ids\", error)\n return new Set()\n }\n }\n\n cleanupOrphans(): void {\n try {\n this.cleanupOrphansStmt.run()\n } catch (error) {\n consola.debug(\"Failed to cleanup request_outbound orphans\", error)\n }\n }\n\n meta(): { dbPath: string; userVersion: number } {\n return {\n dbPath: getAdminDbPath(),\n userVersion: getAdminDbUserVersion(this.db),\n }\n }\n}\n\nexport function createRequestOutboundStore(\n db: Database,\n): RequestOutboundStoreApi {\n return new RequestOutboundStore(db)\n}\n\nconst disabledStore: RequestOutboundStoreApi = {\n insert: () => {},\n getByRequestId: () => null,\n hasOutboundForIds: () => new Set(),\n cleanupOrphans: () => {},\n meta: () => ({\n dbPath: getAdminDbPath(),\n userVersion: 0,\n }),\n}\n\nlet sharedStore: RequestOutboundStoreApi | null = null\n\nexport function getRequestOutboundStore(): RequestOutboundStoreApi {\n if (sharedStore) {\n return sharedStore\n }\n\n const now = Date.now()\n if (now < nextStoreRetryAtMs) {\n return disabledStore\n }\n\n try {\n sharedStore = createRequestOutboundStore(getAdminDb())\n return sharedStore\n } catch (error) {\n nextStoreRetryAtMs = now + STORE_INIT_RETRY_DELAY_MS\n warnStoreInitFailure(error)\n return disabledStore\n }\n}\n"],"mappings":"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"}
|