@byoky/core 0.3.0 → 0.4.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 byoky contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs CHANGED
@@ -33,20 +33,29 @@ __export(index_exports, {
33
33
  createConnectRequest: () => createConnectRequest,
34
34
  createConnectResponse: () => createConnectResponse,
35
35
  createErrorMessage: () => createErrorMessage,
36
+ createGiftLink: () => createGiftLink,
36
37
  createMessage: () => createMessage,
38
+ decodeGiftLink: () => decodeGiftLink,
37
39
  decrypt: () => decrypt,
38
40
  deriveKey: () => deriveKey,
41
+ encodeGiftLink: () => encodeGiftLink,
39
42
  encrypt: () => encrypt,
40
43
  extractUsageFromParsed: () => extractUsageFromParsed,
41
44
  getProvider: () => getProvider,
42
45
  getProviderIds: () => getProviderIds,
46
+ giftBudgetPercent: () => giftBudgetPercent,
47
+ giftBudgetRemaining: () => giftBudgetRemaining,
48
+ giftLinkToUrl: () => giftLinkToUrl,
43
49
  hashPassword: () => hashPassword,
44
50
  isByokyMessage: () => isByokyMessage,
51
+ isGiftBudgetExhausted: () => isGiftBudgetExhausted,
52
+ isGiftExpired: () => isGiftExpired,
45
53
  maskKey: () => maskKey,
46
54
  parseModel: () => parseModel,
47
55
  parseRelayMessage: () => parseRelayMessage,
48
56
  parseUsage: () => parseUsage,
49
57
  sendRelayMessage: () => sendRelayMessage,
58
+ validateGiftLink: () => validateGiftLink,
50
59
  validateProxyUrl: () => validateProxyUrl,
51
60
  verifyPassword: () => verifyPassword
52
61
  });
@@ -516,12 +525,23 @@ function buildHeaders(providerId, requestHeaders, apiKey, authMethod = "api_key"
516
525
  delete headers["authorization"];
517
526
  delete headers["x-api-key"];
518
527
  delete headers["api-key"];
528
+ delete headers["origin"];
529
+ delete headers["referer"];
530
+ for (const key of Object.keys(headers)) {
531
+ if (key.startsWith("x-stainless-")) delete headers[key];
532
+ }
533
+ delete headers["sec-fetch-mode"];
534
+ delete headers["accept-language"];
535
+ delete headers["accept-encoding"];
536
+ delete headers["content-length"];
519
537
  if (providerId === "anthropic") {
520
538
  if (authMethod === "oauth") {
521
539
  headers["authorization"] = `Bearer ${apiKey}`;
522
- headers["user-agent"] = "claude-cli/2.1.75";
540
+ headers["user-agent"] = "claude-cli/2.1.76";
523
541
  headers["x-app"] = "cli";
524
- headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20";
542
+ headers["accept"] = "application/json";
543
+ headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
544
+ headers["anthropic-dangerous-direct-browser-access"] = "true";
525
545
  } else {
526
546
  headers["x-api-key"] = apiKey;
527
547
  }
@@ -604,6 +624,86 @@ function computeAllowanceCheck(allowance, entries, providerId) {
604
624
  }
605
625
  return { allowed: true };
606
626
  }
627
+
628
+ // src/gift.ts
629
+ function encodeGiftLink(link) {
630
+ const json = JSON.stringify(link);
631
+ const bytes = new TextEncoder().encode(json);
632
+ return base64UrlEncode(bytes);
633
+ }
634
+ function decodeGiftLink(encoded) {
635
+ try {
636
+ const clean = encoded.replace(/^byoky:\/\/gift\//, "");
637
+ const bytes = base64UrlDecode(clean);
638
+ const json = new TextDecoder().decode(bytes);
639
+ const parsed = JSON.parse(json);
640
+ if (parsed.v !== 1) return null;
641
+ return parsed;
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+ function giftLinkToUrl(encoded) {
647
+ return `byoky://gift/${encoded}`;
648
+ }
649
+ function validateGiftLink(link) {
650
+ if (link.v !== 1) return { valid: false, reason: "Unsupported gift version" };
651
+ if (!link.id || typeof link.id !== "string") return { valid: false, reason: "Missing gift ID" };
652
+ if (!link.p || typeof link.p !== "string") return { valid: false, reason: "Missing provider" };
653
+ if (!link.t || typeof link.t !== "string") return { valid: false, reason: "Missing auth token" };
654
+ if (!link.r || typeof link.r !== "string") return { valid: false, reason: "Missing relay URL" };
655
+ if (typeof link.m !== "number" || link.m <= 0) return { valid: false, reason: "Invalid token budget" };
656
+ if (typeof link.e !== "number" || link.e <= Date.now()) return { valid: false, reason: "Gift has expired" };
657
+ try {
658
+ const url = new URL(link.r);
659
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") {
660
+ return { valid: false, reason: "Relay URL must use ws:// or wss://" };
661
+ }
662
+ } catch {
663
+ return { valid: false, reason: "Invalid relay URL" };
664
+ }
665
+ return { valid: true };
666
+ }
667
+ function isGiftExpired(gift) {
668
+ return gift.expiresAt <= Date.now();
669
+ }
670
+ function isGiftBudgetExhausted(gift) {
671
+ return gift.usedTokens >= gift.maxTokens;
672
+ }
673
+ function giftBudgetRemaining(gift) {
674
+ return Math.max(0, gift.maxTokens - gift.usedTokens);
675
+ }
676
+ function giftBudgetPercent(gift) {
677
+ if (gift.maxTokens === 0) return 100;
678
+ return Math.min(100, Math.round(gift.usedTokens / gift.maxTokens * 100));
679
+ }
680
+ function createGiftLink(gift) {
681
+ const provider = PROVIDERS[gift.providerId];
682
+ const link = {
683
+ v: 1,
684
+ id: gift.id,
685
+ p: gift.providerId,
686
+ n: provider?.name ?? gift.providerId,
687
+ s: gift.label,
688
+ t: gift.authToken,
689
+ m: gift.maxTokens,
690
+ e: gift.expiresAt,
691
+ r: gift.relayUrl
692
+ };
693
+ return { encoded: encodeGiftLink(link), link };
694
+ }
695
+ function base64UrlEncode(bytes) {
696
+ let binary = "";
697
+ for (const byte of bytes) binary += String.fromCharCode(byte);
698
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
699
+ }
700
+ function base64UrlDecode(str) {
701
+ const padded = str.replace(/-/g, "+").replace(/_/g, "/");
702
+ const binary = atob(padded);
703
+ const bytes = new Uint8Array(binary.length);
704
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
705
+ return bytes;
706
+ }
607
707
  // Annotate the CommonJS export names for ESM import in node:
608
708
  0 && (module.exports = {
609
709
  BYOKY_MESSAGE_PREFIX,
@@ -619,20 +719,29 @@ function computeAllowanceCheck(allowance, entries, providerId) {
619
719
  createConnectRequest,
620
720
  createConnectResponse,
621
721
  createErrorMessage,
722
+ createGiftLink,
622
723
  createMessage,
724
+ decodeGiftLink,
623
725
  decrypt,
624
726
  deriveKey,
727
+ encodeGiftLink,
625
728
  encrypt,
626
729
  extractUsageFromParsed,
627
730
  getProvider,
628
731
  getProviderIds,
732
+ giftBudgetPercent,
733
+ giftBudgetRemaining,
734
+ giftLinkToUrl,
629
735
  hashPassword,
630
736
  isByokyMessage,
737
+ isGiftBudgetExhausted,
738
+ isGiftExpired,
631
739
  maskKey,
632
740
  parseModel,
633
741
  parseRelayMessage,
634
742
  parseUsage,
635
743
  sendRelayMessage,
744
+ validateGiftLink,
636
745
  validateProxyUrl,
637
746
  verifyPassword
638
747
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/crypto.ts","../src/errors.ts","../src/protocol.ts","../src/providers.ts","../src/relay.ts","../src/password-strength.ts","../src/proxy-utils.ts"],"sourcesContent":["export * from './types.js';\nexport * from './crypto.js';\nexport * from './errors.js';\nexport * from './protocol.js';\nexport * from './providers.js';\nexport * from './relay.js';\nexport * from './password-strength.js';\nexport * from './proxy-utils.js';\n","export type ProviderId = 'anthropic' | 'openai' | 'gemini' | (string & {});\n\nexport type AuthMethod = 'api_key' | 'oauth';\n\nexport interface ProviderConfig {\n id: ProviderId;\n name: string;\n authMethods: AuthMethod[];\n baseUrl: string;\n oauthConfig?: OAuthConfig;\n}\n\nexport interface OAuthConfig {\n clientId: string;\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n extraAuthParams?: Record<string, string>;\n}\n\n// --- Credentials ---\n\nexport interface CredentialBase {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n}\n\nexport interface ApiKeyCredential extends CredentialBase {\n authMethod: 'api_key';\n encryptedKey: string;\n}\n\nexport interface OAuthCredential extends CredentialBase {\n authMethod: 'oauth';\n encryptedAccessToken: string;\n encryptedRefreshToken?: string;\n expiresAt?: number;\n}\n\nexport type Credential = ApiKeyCredential | OAuthCredential;\n\nexport interface CredentialMeta {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n maskedKey?: string;\n}\n\n// --- Sessions ---\n\nexport interface Session {\n id: string;\n sessionKey: string;\n appOrigin: string;\n appName?: string;\n providers: SessionProvider[];\n createdAt: number;\n expiresAt: number;\n}\n\nexport interface SessionProvider {\n providerId: ProviderId;\n credentialId: string;\n available: boolean;\n authMethod: AuthMethod;\n}\n\n// --- Connect ---\n\nexport interface ConnectRequest {\n providers?: ProviderRequirement[];\n capabilities?: string[];\n}\n\nexport interface ProviderRequirement {\n id: ProviderId;\n required: boolean;\n}\n\nexport interface ConnectResponse {\n sessionKey: string;\n proxyUrl: string;\n providers: Record<\n string,\n {\n available: boolean;\n authMethod: AuthMethod;\n }\n >;\n}\n\n// --- Proxy ---\n\nexport interface ProxyRequest {\n requestId: string;\n sessionKey: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface ProxyResponseMeta {\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface ProxyResponseChunk {\n requestId: string;\n chunk: string;\n}\n\nexport interface ProxyResponseError {\n requestId: string;\n status: number;\n error: { code: string; message: string };\n}\n\n// --- Protocol messages ---\n\nexport type MessageType =\n | 'BYOKY_CONNECT_REQUEST'\n | 'BYOKY_CONNECT_RESPONSE'\n | 'BYOKY_DISCONNECT'\n | 'BYOKY_PROXY_REQUEST'\n | 'BYOKY_PROXY_RESPONSE_META'\n | 'BYOKY_PROXY_RESPONSE_CHUNK'\n | 'BYOKY_PROXY_RESPONSE_DONE'\n | 'BYOKY_PROXY_RESPONSE_ERROR'\n | 'BYOKY_SESSION_STATUS'\n | 'BYOKY_SESSION_STATUS_RESPONSE'\n | 'BYOKY_SESSION_USAGE'\n | 'BYOKY_SESSION_USAGE_RESPONSE'\n | 'BYOKY_SESSION_REVOKED'\n | 'BYOKY_ERROR';\n\nexport interface ByokyMessage {\n type: MessageType;\n id: string;\n requestId?: string;\n payload: unknown;\n}\n\n// --- Session queries ---\n\nexport interface SessionUsage {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n byProvider: Record<string, {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n }>;\n}\n\n// --- Errors ---\n\nexport enum ByokyErrorCode {\n WALLET_NOT_INSTALLED = 'WALLET_NOT_INSTALLED',\n USER_REJECTED = 'USER_REJECTED',\n PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE',\n SESSION_EXPIRED = 'SESSION_EXPIRED',\n RATE_LIMITED = 'RATE_LIMITED',\n QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',\n INVALID_KEY = 'INVALID_KEY',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n PROXY_ERROR = 'PROXY_ERROR',\n RELAY_CONNECTION_FAILED = 'RELAY_CONNECTION_FAILED',\n RELAY_DISCONNECTED = 'RELAY_DISCONNECTED',\n UNKNOWN = 'UNKNOWN',\n}\n\n// --- Request log ---\n\nexport interface RequestLogEntry {\n id: string;\n sessionId: string;\n appOrigin: string;\n providerId: ProviderId;\n url: string;\n method: string;\n status: number;\n timestamp: number;\n error?: string;\n inputTokens?: number;\n outputTokens?: number;\n model?: string;\n}\n\n// --- Pending approval ---\n\nexport interface PendingApproval {\n id: string;\n appOrigin: string;\n appName?: string;\n providers: ProviderRequirement[];\n timestamp: number;\n}\n\n// --- Trusted sites ---\n\nexport interface TrustedSite {\n origin: string;\n trustedAt: number;\n}\n\n// --- Token allowances ---\n\nexport interface TokenAllowance {\n origin: string;\n totalLimit?: number;\n providerLimits?: Record<string, number>;\n}\n","const SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst KEY_LENGTH = 256;\nconst ITERATIONS = 600_000;\n\nexport async function deriveKey(\n password: string,\n salt: Uint8Array,\n): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveKey'],\n );\n\n return crypto.subtle.deriveKey(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n { name: 'AES-GCM', length: KEY_LENGTH },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\nexport async function encrypt(\n plaintext: string,\n password: string,\n): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n const key = await deriveKey(password, salt);\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(plaintext),\n );\n\n const combined = new Uint8Array(\n salt.length + iv.length + new Uint8Array(ciphertext).length,\n );\n combined.set(salt, 0);\n combined.set(iv, salt.length);\n combined.set(new Uint8Array(ciphertext), salt.length + iv.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function decrypt(\n encrypted: string,\n password: string,\n): Promise<string> {\n const combined = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));\n\n const salt = combined.slice(0, SALT_LENGTH);\n const iv = combined.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const ciphertext = combined.slice(SALT_LENGTH + IV_LENGTH);\n\n const key = await deriveKey(password, salt);\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n ciphertext,\n );\n\n return new TextDecoder().decode(plaintext);\n}\n\nasync function deriveRawHash(\n password: string,\n salt: Uint8Array,\n): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveBits'],\n );\n\n const bits = await crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n KEY_LENGTH,\n );\n\n return new Uint8Array(bits);\n}\n\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const hash = await deriveRawHash(password, salt);\n\n const combined = new Uint8Array(salt.length + hash.length);\n combined.set(salt, 0);\n combined.set(hash, salt.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function verifyPassword(\n password: string,\n storedHash: string,\n): Promise<boolean> {\n const combined = Uint8Array.from(atob(storedHash), (c) => c.charCodeAt(0));\n const salt = combined.slice(0, SALT_LENGTH);\n const originalHash = combined.slice(SALT_LENGTH);\n\n const newHash = await deriveRawHash(password, salt);\n\n if (originalHash.length !== newHash.length) return false;\n let result = 0;\n for (let i = 0; i < originalHash.length; i++) {\n result |= originalHash[i] ^ newHash[i];\n }\n return result === 0;\n}\n\nexport function maskKey(key: string): string {\n if (key.length <= 8) return '****';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n}\n","import { ByokyErrorCode } from './types.js';\n\nexport class ByokyError extends Error {\n constructor(\n public readonly code: ByokyErrorCode,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = 'ByokyError';\n }\n\n static walletNotInstalled() {\n return new ByokyError(\n ByokyErrorCode.WALLET_NOT_INSTALLED,\n 'byoky wallet extension is not installed',\n );\n }\n\n static userRejected() {\n return new ByokyError(\n ByokyErrorCode.USER_REJECTED,\n 'User rejected the connection request',\n );\n }\n\n static providerUnavailable(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.PROVIDER_UNAVAILABLE,\n `Provider \"${providerId}\" is not available`,\n { providerId },\n );\n }\n\n static sessionExpired() {\n return new ByokyError(\n ByokyErrorCode.SESSION_EXPIRED,\n 'Session has expired — please reconnect',\n );\n }\n\n static rateLimited(retryAfter?: number) {\n return new ByokyError(\n ByokyErrorCode.RATE_LIMITED,\n `Rate limit exceeded${retryAfter ? ` — retry after ${retryAfter}s` : ''}`,\n { retryAfter },\n );\n }\n\n static quotaExceeded(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.QUOTA_EXCEEDED,\n `Quota exceeded for ${providerId} — check your billing`,\n { providerId },\n );\n }\n\n static invalidKey(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.INVALID_KEY,\n `Invalid API key for ${providerId}`,\n { providerId },\n );\n }\n\n static tokenExpired(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.TOKEN_EXPIRED,\n `OAuth token expired for ${providerId} — re-authentication required`,\n { providerId },\n );\n }\n\n static relayConnectionFailed(reason?: string) {\n return new ByokyError(\n ByokyErrorCode.RELAY_CONNECTION_FAILED,\n `Relay connection failed${reason ? `: ${reason}` : ''}`,\n );\n }\n\n static relayDisconnected() {\n return new ByokyError(\n ByokyErrorCode.RELAY_DISCONNECTED,\n 'Relay connection was closed',\n );\n }\n}\n","import type {\n ByokyMessage,\n ConnectRequest,\n ConnectResponse,\n MessageType,\n} from './types.js';\n\nexport const BYOKY_PROVIDER_KEY = '__byoky__';\nexport const BYOKY_MESSAGE_PREFIX = 'BYOKY_';\n\nexport function createMessage(\n type: MessageType,\n payload: unknown,\n requestId?: string,\n): ByokyMessage {\n return {\n type,\n id: crypto.randomUUID(),\n requestId,\n payload,\n };\n}\n\nexport function isByokyMessage(data: unknown): data is ByokyMessage {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n typeof (data as ByokyMessage).type === 'string' &&\n (data as ByokyMessage).type.startsWith(BYOKY_MESSAGE_PREFIX)\n );\n}\n\nexport function createConnectRequest(request: ConnectRequest): ByokyMessage {\n return createMessage('BYOKY_CONNECT_REQUEST', request);\n}\n\nexport function createConnectResponse(\n response: ConnectResponse,\n requestId: string,\n): ByokyMessage {\n return createMessage('BYOKY_CONNECT_RESPONSE', response, requestId);\n}\n\nexport function createErrorMessage(\n code: string,\n message: string,\n requestId?: string,\n): ByokyMessage {\n return createMessage('BYOKY_ERROR', { code, message }, requestId);\n}\n","import type { ProviderConfig } from './types.js';\n\nexport const PROVIDERS: Record<string, ProviderConfig> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api.anthropic.com',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.openai.com',\n },\n gemini: {\n id: 'gemini',\n name: 'Google Gemini',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://generativelanguage.googleapis.com',\n oauthConfig: {\n clientId: '699663966637-gr4d994198r4g6jvip25ffg85kree6ck.apps.googleusercontent.com',\n authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n scopes: ['https://www.googleapis.com/auth/generative-language'],\n extraAuthParams: { access_type: 'offline', prompt: 'consent' },\n },\n },\n mistral: {\n id: 'mistral',\n name: 'Mistral',\n authMethods: ['api_key'],\n baseUrl: 'https://api.mistral.ai',\n },\n cohere: {\n id: 'cohere',\n name: 'Cohere',\n authMethods: ['api_key'],\n baseUrl: 'https://api.cohere.com',\n },\n xai: {\n id: 'xai',\n name: 'xAI (Grok)',\n authMethods: ['api_key'],\n baseUrl: 'https://api.x.ai',\n },\n deepseek: {\n id: 'deepseek',\n name: 'DeepSeek',\n authMethods: ['api_key'],\n baseUrl: 'https://api.deepseek.com',\n },\n perplexity: {\n id: 'perplexity',\n name: 'Perplexity',\n authMethods: ['api_key'],\n baseUrl: 'https://api.perplexity.ai',\n },\n groq: {\n id: 'groq',\n name: 'Groq',\n authMethods: ['api_key'],\n baseUrl: 'https://api.groq.com',\n },\n together: {\n id: 'together',\n name: 'Together AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.together.xyz',\n },\n fireworks: {\n id: 'fireworks',\n name: 'Fireworks AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.fireworks.ai',\n },\n replicate: {\n id: 'replicate',\n name: 'Replicate',\n authMethods: ['api_key'],\n baseUrl: 'https://api.replicate.com',\n },\n openrouter: {\n id: 'openrouter',\n name: 'OpenRouter',\n authMethods: ['api_key'],\n baseUrl: 'https://openrouter.ai/api',\n },\n huggingface: {\n id: 'huggingface',\n name: 'Hugging Face',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api-inference.huggingface.co',\n oauthConfig: {\n clientId: '031aeb11-725b-498a-93f9-d3599d84f57c',\n authorizationUrl: 'https://huggingface.co/oauth/authorize',\n tokenUrl: 'https://huggingface.co/oauth/token',\n scopes: ['inference-api'],\n },\n },\n azure_openai: {\n id: 'azure_openai',\n name: 'Azure OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://YOUR_RESOURCE.openai.azure.com',\n },\n};\n\nexport function getProvider(id: string): ProviderConfig | undefined {\n return PROVIDERS[id];\n}\n\nexport function getProviderIds(): string[] {\n return Object.keys(PROVIDERS);\n}\n","import type { AuthMethod } from './types.js';\n\n/** Minimal WebSocket interface — compatible with browser WebSocket and `ws` library. */\nexport interface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport const WS_READY_STATE = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n// --- Relay message types ---\n\nexport interface RelayHello {\n type: 'relay:hello';\n sessionId: string;\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>;\n}\n\nexport interface RelayRequest {\n type: 'relay:request';\n requestId: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface RelayResponseMeta {\n type: 'relay:response:meta';\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface RelayResponseChunk {\n type: 'relay:response:chunk';\n requestId: string;\n chunk: string;\n}\n\nexport interface RelayResponseDone {\n type: 'relay:response:done';\n requestId: string;\n}\n\nexport interface RelayResponseError {\n type: 'relay:response:error';\n requestId: string;\n error: { code: string; message: string };\n}\n\nexport interface RelayPing {\n type: 'relay:ping';\n ts: number;\n}\n\nexport interface RelayPong {\n type: 'relay:pong';\n ts: number;\n}\n\nexport type RelayMessage =\n | RelayHello\n | RelayRequest\n | RelayResponseMeta\n | RelayResponseChunk\n | RelayResponseDone\n | RelayResponseError\n | RelayPing\n | RelayPong;\n\nexport function parseRelayMessage(data: unknown): RelayMessage | null {\n try {\n const raw = typeof data === 'string' ? JSON.parse(data) : data;\n if (!raw || typeof raw !== 'object' || typeof raw.type !== 'string' || !raw.type.startsWith('relay:')) {\n return null;\n }\n\n // Validate required fields per message type\n switch (raw.type) {\n case 'relay:hello':\n if (typeof raw.sessionId !== 'string') return null;\n break;\n case 'relay:request':\n if (typeof raw.requestId !== 'string' || typeof raw.providerId !== 'string' ||\n typeof raw.url !== 'string' || typeof raw.method !== 'string') return null;\n break;\n case 'relay:response:meta':\n if (typeof raw.requestId !== 'string' || typeof raw.status !== 'number') return null;\n break;\n case 'relay:response:chunk':\n if (typeof raw.requestId !== 'string' || typeof raw.chunk !== 'string') return null;\n break;\n case 'relay:response:done':\n case 'relay:response:error':\n if (typeof raw.requestId !== 'string') return null;\n break;\n case 'relay:ping':\n case 'relay:pong':\n if (typeof raw.ts !== 'number') return null;\n break;\n default:\n return null;\n }\n\n return raw as RelayMessage;\n } catch {\n return null;\n }\n}\n\nexport function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n","export interface PasswordStrength {\n score: 0 | 1 | 2 | 3 | 4;\n label: 'Too weak' | 'Weak' | 'Fair' | 'Strong' | 'Very strong';\n feedback: string[];\n}\n\nconst COMMON_PASSWORDS = new Set([\n 'password', '12345678', 'qwerty12', 'letmein12', 'welcome1',\n 'monkey12', 'dragon12', 'master12', 'abc12345', 'password1',\n 'password12', 'iloveyou1', 'sunshine1', 'trustno1', 'princess1',\n 'football1', 'shadow123', 'michael1', 'jordan123', 'superman1',\n]);\n\nexport function checkPasswordStrength(password: string): PasswordStrength {\n const feedback: string[] = [];\n let score = 0;\n\n if (password.length < 12) {\n feedback.push('Use at least 12 characters');\n if (password.length < 8) {\n return { score: 0, label: 'Too weak', feedback };\n }\n } else {\n score++;\n if (password.length >= 16) score++;\n }\n\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n feedback.push('This is a commonly used password');\n return { score: 0, label: 'Too weak', feedback };\n }\n\n // Check character diversity\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasDigit = /\\d/.test(password);\n const hasSymbol = /[^a-zA-Z0-9]/.test(password);\n const charTypes = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;\n\n if (charTypes < 2) {\n feedback.push('Mix uppercase, lowercase, numbers, and symbols');\n } else if (charTypes >= 3) {\n score++;\n }\n if (charTypes >= 4) {\n score++;\n }\n\n // Check for repeated characters (e.g. aaaaaa)\n if (/(.)\\1{3,}/.test(password)) {\n feedback.push('Avoid repeated characters');\n score = Math.max(0, score - 1);\n }\n\n // Check for sequential patterns (e.g. 123456, abcdef)\n if (/(?:012|123|234|345|456|567|678|789|abc|bcd|cde|def)/i.test(password)) {\n feedback.push('Avoid sequential patterns');\n score = Math.max(0, score - 1);\n }\n\n const capped = Math.min(4, Math.max(0, score)) as 0 | 1 | 2 | 3 | 4;\n const labels: Record<number, PasswordStrength['label']> = {\n 0: 'Too weak',\n 1: 'Weak',\n 2: 'Fair',\n 3: 'Strong',\n 4: 'Very strong',\n };\n\n if (feedback.length === 0 && capped < 3) {\n feedback.push('Add more character variety or length');\n }\n\n return { score: capped, label: labels[capped], feedback };\n}\n\nexport const MIN_PASSWORD_LENGTH = 12;\n","import type { RequestLogEntry, TokenAllowance } from './types.js';\nimport { PROVIDERS } from './providers.js';\n\n/**\n * Validate that a proxy request URL targets the registered provider's base URL.\n * Prevents API key exfiltration by rejecting requests to arbitrary domains.\n */\nexport function validateProxyUrl(providerId: string, url: string): boolean {\n const provider = PROVIDERS[providerId];\n if (!provider) return false;\n try {\n const target = new URL(url);\n if (target.protocol !== 'https:') return false;\n const base = new URL(provider.baseUrl);\n return target.origin === base.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the real auth headers for a provider API request.\n * Strips any fake session-key headers and injects the real API key.\n */\nexport function buildHeaders(\n providerId: string,\n requestHeaders: Record<string, string>,\n apiKey: string,\n authMethod: string = 'api_key',\n): Record<string, string> {\n // Normalize header keys to lowercase to prevent case-sensitive bypass\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(requestHeaders)) {\n headers[key.toLowerCase()] = value;\n }\n\n // Remove any auth headers the SDK might have set (they're fake session keys)\n delete headers['authorization'];\n delete headers['x-api-key'];\n delete headers['api-key'];\n\n if (providerId === 'anthropic') {\n if (authMethod === 'oauth') {\n headers['authorization'] = `Bearer ${apiKey}`;\n headers['user-agent'] = 'claude-cli/2.1.75';\n headers['x-app'] = 'cli';\n headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20';\n } else {\n headers['x-api-key'] = apiKey;\n }\n headers['anthropic-version'] = headers['anthropic-version'] ?? '2023-06-01';\n } else if (providerId === 'azure_openai') {\n headers['api-key'] = apiKey;\n } else {\n headers['authorization'] = `Bearer ${apiKey}`;\n }\n\n return headers;\n}\n\n/**\n * Parse the model name from a request body (JSON).\n */\nexport function parseModel(body?: string): string | undefined {\n if (!body) return undefined;\n try {\n const parsed = JSON.parse(body);\n return parsed.model ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse token usage from a provider API response body.\n * Handles both regular JSON responses and SSE streaming responses.\n */\nexport function parseUsage(\n providerId: string,\n body: string,\n): { inputTokens: number; outputTokens: number } | undefined {\n try {\n // For streaming responses (SSE), try to find usage in the last data chunk\n if (body.includes('data: ')) {\n const lines = body.split('\\n').filter((l) => l.startsWith('data: ') && !l.includes('[DONE]'));\n // Anthropic streaming: message_stop event has usage in a preceding message_delta\n // OpenAI streaming: last chunk may include usage\n for (let i = lines.length - 1; i >= 0; i--) {\n const json = lines[i].replace('data: ', '');\n try {\n const parsed = JSON.parse(json);\n const usage = extractUsageFromParsed(providerId, parsed);\n if (usage) return usage;\n } catch {\n continue;\n }\n }\n return undefined;\n }\n\n const parsed = JSON.parse(body);\n return extractUsageFromParsed(providerId, parsed);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Extract token usage from a parsed provider response object.\n */\nexport function extractUsageFromParsed(\n providerId: string,\n parsed: Record<string, unknown>,\n): { inputTokens: number; outputTokens: number } | undefined {\n // Anthropic: { usage: { input_tokens, output_tokens } }\n if (providerId === 'anthropic') {\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };\n }\n }\n\n // Gemini: { usageMetadata: { promptTokenCount, candidatesTokenCount } }\n if (providerId === 'gemini') {\n const meta = parsed.usageMetadata as Record<string, number> | undefined;\n if (meta?.promptTokenCount != null) {\n return {\n inputTokens: meta.promptTokenCount,\n outputTokens: meta.candidatesTokenCount ?? 0,\n };\n }\n }\n\n // OpenAI-compatible (openai, groq, together, deepseek, xai, perplexity, fireworks, openrouter, mistral, azure_openai):\n // { usage: { prompt_tokens, completion_tokens } }\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n return { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens };\n }\n\n return undefined;\n}\n\n/**\n * Check whether a request is allowed given token allowances and usage history.\n * Pure computation — no storage access.\n */\nexport function computeAllowanceCheck(\n allowance: TokenAllowance | undefined,\n entries: Pick<RequestLogEntry, 'providerId' | 'inputTokens' | 'outputTokens'>[],\n providerId: string,\n): { allowed: boolean; reason?: string } {\n if (!allowance) return { allowed: true };\n\n let totalUsed = 0;\n const byProvider: Record<string, number> = {};\n for (const entry of entries) {\n const tokens = (entry.inputTokens ?? 0) + (entry.outputTokens ?? 0);\n totalUsed += tokens;\n byProvider[entry.providerId] = (byProvider[entry.providerId] ?? 0) + tokens;\n }\n\n if (allowance.totalLimit != null && totalUsed >= allowance.totalLimit) {\n return { allowed: false, reason: `Token allowance exceeded for ${allowance.origin}` };\n }\n\n const providerLimit = allowance.providerLimits?.[providerId];\n if (providerLimit != null && (byProvider[providerId] ?? 0) >= providerLimit) {\n return { allowed: false, reason: `Token allowance for ${providerId} exceeded` };\n }\n\n return { allowed: true };\n}\n"],"mappings":"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","names":["ByokyErrorCode","parsed","usage"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/crypto.ts","../src/errors.ts","../src/protocol.ts","../src/providers.ts","../src/relay.ts","../src/password-strength.ts","../src/proxy-utils.ts","../src/gift.ts"],"sourcesContent":["export * from './types.js';\nexport * from './crypto.js';\nexport * from './errors.js';\nexport * from './protocol.js';\nexport * from './providers.js';\nexport * from './relay.js';\nexport * from './password-strength.js';\nexport * from './proxy-utils.js';\nexport * from './gift.js';\n","export type ProviderId = 'anthropic' | 'openai' | 'gemini' | (string & {});\n\nexport type AuthMethod = 'api_key' | 'oauth';\n\nexport interface ProviderConfig {\n id: ProviderId;\n name: string;\n authMethods: AuthMethod[];\n baseUrl: string;\n oauthConfig?: OAuthConfig;\n}\n\nexport interface OAuthConfig {\n clientId: string;\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n extraAuthParams?: Record<string, string>;\n}\n\n// --- Credentials ---\n\nexport interface CredentialBase {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n}\n\nexport interface ApiKeyCredential extends CredentialBase {\n authMethod: 'api_key';\n encryptedKey: string;\n}\n\nexport interface OAuthCredential extends CredentialBase {\n authMethod: 'oauth';\n encryptedAccessToken: string;\n encryptedRefreshToken?: string;\n expiresAt?: number;\n}\n\nexport type Credential = ApiKeyCredential | OAuthCredential;\n\nexport interface CredentialMeta {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n maskedKey?: string;\n}\n\n// --- Sessions ---\n\nexport interface Session {\n id: string;\n sessionKey: string;\n appOrigin: string;\n appName?: string;\n providers: SessionProvider[];\n createdAt: number;\n expiresAt: number;\n}\n\nexport interface SessionProvider {\n providerId: ProviderId;\n credentialId: string;\n available: boolean;\n authMethod: AuthMethod;\n giftId?: string;\n giftRelayUrl?: string;\n giftAuthToken?: string;\n}\n\n// --- Connect ---\n\nexport interface ConnectRequest {\n providers?: ProviderRequirement[];\n capabilities?: string[];\n}\n\nexport interface ProviderRequirement {\n id: ProviderId;\n required: boolean;\n}\n\nexport interface ConnectResponse {\n sessionKey: string;\n proxyUrl: string;\n providers: Record<\n string,\n {\n available: boolean;\n authMethod: AuthMethod;\n }\n >;\n}\n\n// --- Proxy ---\n\nexport interface ProxyRequest {\n requestId: string;\n sessionKey: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface ProxyResponseMeta {\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface ProxyResponseChunk {\n requestId: string;\n chunk: string;\n}\n\nexport interface ProxyResponseError {\n requestId: string;\n status: number;\n error: { code: string; message: string };\n}\n\n// --- Protocol messages ---\n\nexport type MessageType =\n | 'BYOKY_CONNECT_REQUEST'\n | 'BYOKY_CONNECT_RESPONSE'\n | 'BYOKY_DISCONNECT'\n | 'BYOKY_PROXY_REQUEST'\n | 'BYOKY_PROXY_RESPONSE_META'\n | 'BYOKY_PROXY_RESPONSE_CHUNK'\n | 'BYOKY_PROXY_RESPONSE_DONE'\n | 'BYOKY_PROXY_RESPONSE_ERROR'\n | 'BYOKY_SESSION_STATUS'\n | 'BYOKY_SESSION_STATUS_RESPONSE'\n | 'BYOKY_SESSION_USAGE'\n | 'BYOKY_SESSION_USAGE_RESPONSE'\n | 'BYOKY_SESSION_REVOKED'\n | 'BYOKY_ERROR';\n\nexport interface ByokyMessage {\n type: MessageType;\n id: string;\n requestId?: string;\n payload: unknown;\n}\n\n// --- Session queries ---\n\nexport interface SessionUsage {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n byProvider: Record<string, {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n }>;\n}\n\n// --- Errors ---\n\nexport enum ByokyErrorCode {\n WALLET_NOT_INSTALLED = 'WALLET_NOT_INSTALLED',\n USER_REJECTED = 'USER_REJECTED',\n PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE',\n SESSION_EXPIRED = 'SESSION_EXPIRED',\n RATE_LIMITED = 'RATE_LIMITED',\n QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',\n INVALID_KEY = 'INVALID_KEY',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n PROXY_ERROR = 'PROXY_ERROR',\n RELAY_CONNECTION_FAILED = 'RELAY_CONNECTION_FAILED',\n RELAY_DISCONNECTED = 'RELAY_DISCONNECTED',\n UNKNOWN = 'UNKNOWN',\n}\n\n// --- Request log ---\n\nexport interface RequestLogEntry {\n id: string;\n sessionId: string;\n appOrigin: string;\n providerId: ProviderId;\n url: string;\n method: string;\n status: number;\n timestamp: number;\n error?: string;\n inputTokens?: number;\n outputTokens?: number;\n model?: string;\n}\n\n// --- Pending approval ---\n\nexport interface PendingApproval {\n id: string;\n appOrigin: string;\n appName?: string;\n providers: ProviderRequirement[];\n timestamp: number;\n}\n\n// --- Trusted sites ---\n\nexport interface TrustedSite {\n origin: string;\n trustedAt: number;\n}\n\n// --- Token allowances ---\n\nexport interface TokenAllowance {\n origin: string;\n totalLimit?: number;\n providerLimits?: Record<string, number>;\n}\n","const SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst KEY_LENGTH = 256;\nconst ITERATIONS = 600_000;\n\nexport async function deriveKey(\n password: string,\n salt: Uint8Array,\n): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveKey'],\n );\n\n return crypto.subtle.deriveKey(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n { name: 'AES-GCM', length: KEY_LENGTH },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\nexport async function encrypt(\n plaintext: string,\n password: string,\n): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n const key = await deriveKey(password, salt);\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(plaintext),\n );\n\n const combined = new Uint8Array(\n salt.length + iv.length + new Uint8Array(ciphertext).length,\n );\n combined.set(salt, 0);\n combined.set(iv, salt.length);\n combined.set(new Uint8Array(ciphertext), salt.length + iv.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function decrypt(\n encrypted: string,\n password: string,\n): Promise<string> {\n const combined = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));\n\n const salt = combined.slice(0, SALT_LENGTH);\n const iv = combined.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const ciphertext = combined.slice(SALT_LENGTH + IV_LENGTH);\n\n const key = await deriveKey(password, salt);\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n ciphertext,\n );\n\n return new TextDecoder().decode(plaintext);\n}\n\nasync function deriveRawHash(\n password: string,\n salt: Uint8Array,\n): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveBits'],\n );\n\n const bits = await crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n KEY_LENGTH,\n );\n\n return new Uint8Array(bits);\n}\n\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const hash = await deriveRawHash(password, salt);\n\n const combined = new Uint8Array(salt.length + hash.length);\n combined.set(salt, 0);\n combined.set(hash, salt.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function verifyPassword(\n password: string,\n storedHash: string,\n): Promise<boolean> {\n const combined = Uint8Array.from(atob(storedHash), (c) => c.charCodeAt(0));\n const salt = combined.slice(0, SALT_LENGTH);\n const originalHash = combined.slice(SALT_LENGTH);\n\n const newHash = await deriveRawHash(password, salt);\n\n if (originalHash.length !== newHash.length) return false;\n let result = 0;\n for (let i = 0; i < originalHash.length; i++) {\n result |= originalHash[i] ^ newHash[i];\n }\n return result === 0;\n}\n\nexport function maskKey(key: string): string {\n if (key.length <= 8) return '****';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n}\n","import { ByokyErrorCode } from './types.js';\n\nexport class ByokyError extends Error {\n constructor(\n public readonly code: ByokyErrorCode,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = 'ByokyError';\n }\n\n static walletNotInstalled() {\n return new ByokyError(\n ByokyErrorCode.WALLET_NOT_INSTALLED,\n 'byoky wallet extension is not installed',\n );\n }\n\n static userRejected() {\n return new ByokyError(\n ByokyErrorCode.USER_REJECTED,\n 'User rejected the connection request',\n );\n }\n\n static providerUnavailable(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.PROVIDER_UNAVAILABLE,\n `Provider \"${providerId}\" is not available`,\n { providerId },\n );\n }\n\n static sessionExpired() {\n return new ByokyError(\n ByokyErrorCode.SESSION_EXPIRED,\n 'Session has expired — please reconnect',\n );\n }\n\n static rateLimited(retryAfter?: number) {\n return new ByokyError(\n ByokyErrorCode.RATE_LIMITED,\n `Rate limit exceeded${retryAfter ? ` — retry after ${retryAfter}s` : ''}`,\n { retryAfter },\n );\n }\n\n static quotaExceeded(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.QUOTA_EXCEEDED,\n `Quota exceeded for ${providerId} — check your billing`,\n { providerId },\n );\n }\n\n static invalidKey(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.INVALID_KEY,\n `Invalid API key for ${providerId}`,\n { providerId },\n );\n }\n\n static tokenExpired(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.TOKEN_EXPIRED,\n `OAuth token expired for ${providerId} — re-authentication required`,\n { providerId },\n );\n }\n\n static relayConnectionFailed(reason?: string) {\n return new ByokyError(\n ByokyErrorCode.RELAY_CONNECTION_FAILED,\n `Relay connection failed${reason ? `: ${reason}` : ''}`,\n );\n }\n\n static relayDisconnected() {\n return new ByokyError(\n ByokyErrorCode.RELAY_DISCONNECTED,\n 'Relay connection was closed',\n );\n }\n}\n","import type {\n ByokyMessage,\n ConnectRequest,\n ConnectResponse,\n MessageType,\n} from './types.js';\n\nexport const BYOKY_PROVIDER_KEY = '__byoky__';\nexport const BYOKY_MESSAGE_PREFIX = 'BYOKY_';\n\nexport function createMessage(\n type: MessageType,\n payload: unknown,\n requestId?: string,\n): ByokyMessage {\n return {\n type,\n id: crypto.randomUUID(),\n requestId,\n payload,\n };\n}\n\nexport function isByokyMessage(data: unknown): data is ByokyMessage {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n typeof (data as ByokyMessage).type === 'string' &&\n (data as ByokyMessage).type.startsWith(BYOKY_MESSAGE_PREFIX)\n );\n}\n\nexport function createConnectRequest(request: ConnectRequest): ByokyMessage {\n return createMessage('BYOKY_CONNECT_REQUEST', request);\n}\n\nexport function createConnectResponse(\n response: ConnectResponse,\n requestId: string,\n): ByokyMessage {\n return createMessage('BYOKY_CONNECT_RESPONSE', response, requestId);\n}\n\nexport function createErrorMessage(\n code: string,\n message: string,\n requestId?: string,\n): ByokyMessage {\n return createMessage('BYOKY_ERROR', { code, message }, requestId);\n}\n","import type { ProviderConfig } from './types.js';\n\nexport const PROVIDERS: Record<string, ProviderConfig> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api.anthropic.com',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.openai.com',\n },\n gemini: {\n id: 'gemini',\n name: 'Google Gemini',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://generativelanguage.googleapis.com',\n oauthConfig: {\n clientId: '699663966637-gr4d994198r4g6jvip25ffg85kree6ck.apps.googleusercontent.com',\n authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n scopes: ['https://www.googleapis.com/auth/generative-language'],\n extraAuthParams: { access_type: 'offline', prompt: 'consent' },\n },\n },\n mistral: {\n id: 'mistral',\n name: 'Mistral',\n authMethods: ['api_key'],\n baseUrl: 'https://api.mistral.ai',\n },\n cohere: {\n id: 'cohere',\n name: 'Cohere',\n authMethods: ['api_key'],\n baseUrl: 'https://api.cohere.com',\n },\n xai: {\n id: 'xai',\n name: 'xAI (Grok)',\n authMethods: ['api_key'],\n baseUrl: 'https://api.x.ai',\n },\n deepseek: {\n id: 'deepseek',\n name: 'DeepSeek',\n authMethods: ['api_key'],\n baseUrl: 'https://api.deepseek.com',\n },\n perplexity: {\n id: 'perplexity',\n name: 'Perplexity',\n authMethods: ['api_key'],\n baseUrl: 'https://api.perplexity.ai',\n },\n groq: {\n id: 'groq',\n name: 'Groq',\n authMethods: ['api_key'],\n baseUrl: 'https://api.groq.com',\n },\n together: {\n id: 'together',\n name: 'Together AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.together.xyz',\n },\n fireworks: {\n id: 'fireworks',\n name: 'Fireworks AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.fireworks.ai',\n },\n replicate: {\n id: 'replicate',\n name: 'Replicate',\n authMethods: ['api_key'],\n baseUrl: 'https://api.replicate.com',\n },\n openrouter: {\n id: 'openrouter',\n name: 'OpenRouter',\n authMethods: ['api_key'],\n baseUrl: 'https://openrouter.ai/api',\n },\n huggingface: {\n id: 'huggingface',\n name: 'Hugging Face',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api-inference.huggingface.co',\n oauthConfig: {\n clientId: '031aeb11-725b-498a-93f9-d3599d84f57c',\n authorizationUrl: 'https://huggingface.co/oauth/authorize',\n tokenUrl: 'https://huggingface.co/oauth/token',\n scopes: ['inference-api'],\n },\n },\n azure_openai: {\n id: 'azure_openai',\n name: 'Azure OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://YOUR_RESOURCE.openai.azure.com',\n },\n};\n\nexport function getProvider(id: string): ProviderConfig | undefined {\n return PROVIDERS[id];\n}\n\nexport function getProviderIds(): string[] {\n return Object.keys(PROVIDERS);\n}\n","import type { AuthMethod } from './types.js';\n\n/** Minimal WebSocket interface — compatible with browser WebSocket and `ws` library. */\nexport interface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport const WS_READY_STATE = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n// --- Relay message types ---\n\nexport interface RelayHello {\n type: 'relay:hello';\n sessionId: string;\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>;\n}\n\nexport interface RelayRequest {\n type: 'relay:request';\n requestId: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface RelayResponseMeta {\n type: 'relay:response:meta';\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface RelayResponseChunk {\n type: 'relay:response:chunk';\n requestId: string;\n chunk: string;\n}\n\nexport interface RelayResponseDone {\n type: 'relay:response:done';\n requestId: string;\n}\n\nexport interface RelayResponseError {\n type: 'relay:response:error';\n requestId: string;\n error: { code: string; message: string };\n}\n\nexport interface RelayPing {\n type: 'relay:ping';\n ts: number;\n}\n\nexport interface RelayPong {\n type: 'relay:pong';\n ts: number;\n}\n\nexport type RelayMessage =\n | RelayHello\n | RelayRequest\n | RelayResponseMeta\n | RelayResponseChunk\n | RelayResponseDone\n | RelayResponseError\n | RelayPing\n | RelayPong;\n\nexport function parseRelayMessage(data: unknown): RelayMessage | null {\n try {\n const raw = typeof data === 'string' ? JSON.parse(data) : data;\n if (!raw || typeof raw !== 'object' || typeof raw.type !== 'string' || !raw.type.startsWith('relay:')) {\n return null;\n }\n\n // Validate required fields per message type\n switch (raw.type) {\n case 'relay:hello':\n if (typeof raw.sessionId !== 'string') return null;\n break;\n case 'relay:request':\n if (typeof raw.requestId !== 'string' || typeof raw.providerId !== 'string' ||\n typeof raw.url !== 'string' || typeof raw.method !== 'string') return null;\n break;\n case 'relay:response:meta':\n if (typeof raw.requestId !== 'string' || typeof raw.status !== 'number') return null;\n break;\n case 'relay:response:chunk':\n if (typeof raw.requestId !== 'string' || typeof raw.chunk !== 'string') return null;\n break;\n case 'relay:response:done':\n case 'relay:response:error':\n if (typeof raw.requestId !== 'string') return null;\n break;\n case 'relay:ping':\n case 'relay:pong':\n if (typeof raw.ts !== 'number') return null;\n break;\n default:\n return null;\n }\n\n return raw as RelayMessage;\n } catch {\n return null;\n }\n}\n\nexport function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n","export interface PasswordStrength {\n score: 0 | 1 | 2 | 3 | 4;\n label: 'Too weak' | 'Weak' | 'Fair' | 'Strong' | 'Very strong';\n feedback: string[];\n}\n\nconst COMMON_PASSWORDS = new Set([\n 'password', '12345678', 'qwerty12', 'letmein12', 'welcome1',\n 'monkey12', 'dragon12', 'master12', 'abc12345', 'password1',\n 'password12', 'iloveyou1', 'sunshine1', 'trustno1', 'princess1',\n 'football1', 'shadow123', 'michael1', 'jordan123', 'superman1',\n]);\n\nexport function checkPasswordStrength(password: string): PasswordStrength {\n const feedback: string[] = [];\n let score = 0;\n\n if (password.length < 12) {\n feedback.push('Use at least 12 characters');\n if (password.length < 8) {\n return { score: 0, label: 'Too weak', feedback };\n }\n } else {\n score++;\n if (password.length >= 16) score++;\n }\n\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n feedback.push('This is a commonly used password');\n return { score: 0, label: 'Too weak', feedback };\n }\n\n // Check character diversity\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasDigit = /\\d/.test(password);\n const hasSymbol = /[^a-zA-Z0-9]/.test(password);\n const charTypes = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;\n\n if (charTypes < 2) {\n feedback.push('Mix uppercase, lowercase, numbers, and symbols');\n } else if (charTypes >= 3) {\n score++;\n }\n if (charTypes >= 4) {\n score++;\n }\n\n // Check for repeated characters (e.g. aaaaaa)\n if (/(.)\\1{3,}/.test(password)) {\n feedback.push('Avoid repeated characters');\n score = Math.max(0, score - 1);\n }\n\n // Check for sequential patterns (e.g. 123456, abcdef)\n if (/(?:012|123|234|345|456|567|678|789|abc|bcd|cde|def)/i.test(password)) {\n feedback.push('Avoid sequential patterns');\n score = Math.max(0, score - 1);\n }\n\n const capped = Math.min(4, Math.max(0, score)) as 0 | 1 | 2 | 3 | 4;\n const labels: Record<number, PasswordStrength['label']> = {\n 0: 'Too weak',\n 1: 'Weak',\n 2: 'Fair',\n 3: 'Strong',\n 4: 'Very strong',\n };\n\n if (feedback.length === 0 && capped < 3) {\n feedback.push('Add more character variety or length');\n }\n\n return { score: capped, label: labels[capped], feedback };\n}\n\nexport const MIN_PASSWORD_LENGTH = 12;\n","import type { RequestLogEntry, TokenAllowance } from './types.js';\nimport { PROVIDERS } from './providers.js';\n\n/**\n * Validate that a proxy request URL targets the registered provider's base URL.\n * Prevents API key exfiltration by rejecting requests to arbitrary domains.\n */\nexport function validateProxyUrl(providerId: string, url: string): boolean {\n const provider = PROVIDERS[providerId];\n if (!provider) return false;\n try {\n const target = new URL(url);\n if (target.protocol !== 'https:') return false;\n const base = new URL(provider.baseUrl);\n return target.origin === base.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the real auth headers for a provider API request.\n * Strips any fake session-key headers and injects the real API key.\n */\nexport function buildHeaders(\n providerId: string,\n requestHeaders: Record<string, string>,\n apiKey: string,\n authMethod: string = 'api_key',\n): Record<string, string> {\n // Normalize header keys to lowercase to prevent case-sensitive bypass\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(requestHeaders)) {\n headers[key.toLowerCase()] = value;\n }\n\n // Remove any auth headers the SDK might have set (they're fake session keys)\n delete headers['authorization'];\n delete headers['x-api-key'];\n delete headers['api-key'];\n\n // Strip browser/SDK headers that can trigger rejection from provider APIs\n delete headers['origin'];\n delete headers['referer'];\n // Remove SDK telemetry headers that leak the real client environment\n for (const key of Object.keys(headers)) {\n if (key.startsWith('x-stainless-')) delete headers[key];\n }\n delete headers['sec-fetch-mode'];\n delete headers['accept-language'];\n delete headers['accept-encoding'];\n // Always strip content-length — fetch() recalculates it from the actual body,\n // and the body may have been modified (e.g. system prompt injection)\n delete headers['content-length'];\n\n if (providerId === 'anthropic') {\n if (authMethod === 'oauth') {\n headers['authorization'] = `Bearer ${apiKey}`;\n headers['user-agent'] = 'claude-cli/2.1.76';\n headers['x-app'] = 'cli';\n headers['accept'] = 'application/json';\n headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14';\n headers['anthropic-dangerous-direct-browser-access'] = 'true';\n } else {\n headers['x-api-key'] = apiKey;\n }\n headers['anthropic-version'] = headers['anthropic-version'] ?? '2023-06-01';\n } else if (providerId === 'azure_openai') {\n headers['api-key'] = apiKey;\n } else {\n headers['authorization'] = `Bearer ${apiKey}`;\n }\n\n return headers;\n}\n\n/**\n * Parse the model name from a request body (JSON).\n */\nexport function parseModel(body?: string): string | undefined {\n if (!body) return undefined;\n try {\n const parsed = JSON.parse(body);\n return parsed.model ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse token usage from a provider API response body.\n * Handles both regular JSON responses and SSE streaming responses.\n */\nexport function parseUsage(\n providerId: string,\n body: string,\n): { inputTokens: number; outputTokens: number } | undefined {\n try {\n // For streaming responses (SSE), try to find usage in the last data chunk\n if (body.includes('data: ')) {\n const lines = body.split('\\n').filter((l) => l.startsWith('data: ') && !l.includes('[DONE]'));\n // Anthropic streaming: message_stop event has usage in a preceding message_delta\n // OpenAI streaming: last chunk may include usage\n for (let i = lines.length - 1; i >= 0; i--) {\n const json = lines[i].replace('data: ', '');\n try {\n const parsed = JSON.parse(json);\n const usage = extractUsageFromParsed(providerId, parsed);\n if (usage) return usage;\n } catch {\n continue;\n }\n }\n return undefined;\n }\n\n const parsed = JSON.parse(body);\n return extractUsageFromParsed(providerId, parsed);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Extract token usage from a parsed provider response object.\n */\nexport function extractUsageFromParsed(\n providerId: string,\n parsed: Record<string, unknown>,\n): { inputTokens: number; outputTokens: number } | undefined {\n // Anthropic: { usage: { input_tokens, output_tokens } }\n if (providerId === 'anthropic') {\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };\n }\n }\n\n // Gemini: { usageMetadata: { promptTokenCount, candidatesTokenCount } }\n if (providerId === 'gemini') {\n const meta = parsed.usageMetadata as Record<string, number> | undefined;\n if (meta?.promptTokenCount != null) {\n return {\n inputTokens: meta.promptTokenCount,\n outputTokens: meta.candidatesTokenCount ?? 0,\n };\n }\n }\n\n // OpenAI-compatible (openai, groq, together, deepseek, xai, perplexity, fireworks, openrouter, mistral, azure_openai):\n // { usage: { prompt_tokens, completion_tokens } }\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n return { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens };\n }\n\n return undefined;\n}\n\n/**\n * Check whether a request is allowed given token allowances and usage history.\n * Pure computation — no storage access.\n */\nexport function computeAllowanceCheck(\n allowance: TokenAllowance | undefined,\n entries: Pick<RequestLogEntry, 'providerId' | 'inputTokens' | 'outputTokens'>[],\n providerId: string,\n): { allowed: boolean; reason?: string } {\n if (!allowance) return { allowed: true };\n\n let totalUsed = 0;\n const byProvider: Record<string, number> = {};\n for (const entry of entries) {\n const tokens = (entry.inputTokens ?? 0) + (entry.outputTokens ?? 0);\n totalUsed += tokens;\n byProvider[entry.providerId] = (byProvider[entry.providerId] ?? 0) + tokens;\n }\n\n if (allowance.totalLimit != null && totalUsed >= allowance.totalLimit) {\n return { allowed: false, reason: `Token allowance exceeded for ${allowance.origin}` };\n }\n\n const providerLimit = allowance.providerLimits?.[providerId];\n if (providerLimit != null && (byProvider[providerId] ?? 0) >= providerLimit) {\n return { allowed: false, reason: `Token allowance for ${providerId} exceeded` };\n }\n\n return { allowed: true };\n}\n","import { PROVIDERS } from './providers.js';\n\n// --- Gift (sender side) ---\n\nexport interface Gift {\n id: string;\n credentialId: string;\n providerId: string;\n label: string;\n authToken: string;\n maxTokens: number;\n usedTokens: number;\n expiresAt: number;\n createdAt: number;\n active: boolean;\n relayUrl: string;\n}\n\n// --- Gift link (shareable payload) ---\n\nexport interface GiftLink {\n v: 1;\n id: string;\n p: string; // providerId\n n: string; // provider display name\n s: string; // sender label\n t: string; // auth token\n m: number; // max tokens\n e: number; // expires at (unix ms)\n r: string; // relay URL\n}\n\n// --- Gifted credential (recipient side) ---\n\nexport interface GiftedCredential {\n id: string;\n giftId: string;\n providerId: string;\n providerName: string;\n senderLabel: string;\n authToken: string;\n maxTokens: number;\n usedTokens: number;\n expiresAt: number;\n relayUrl: string;\n createdAt: number;\n}\n\n// --- Gift relay protocol ---\n\nexport interface GiftRelayAuth {\n type: 'gift:auth';\n giftId: string;\n authToken: string;\n role: 'sender' | 'recipient';\n}\n\nexport interface GiftRelayAuthResult {\n type: 'gift:auth:result';\n success: boolean;\n error?: string;\n peerOnline?: boolean;\n}\n\nexport interface GiftRelayPeerStatus {\n type: 'gift:peer:status';\n online: boolean;\n}\n\nexport interface GiftRelayUsageUpdate {\n type: 'gift:usage';\n giftId: string;\n usedTokens: number;\n}\n\nexport type GiftRelayMessage =\n | GiftRelayAuth\n | GiftRelayAuthResult\n | GiftRelayPeerStatus\n | GiftRelayUsageUpdate;\n\n// --- Encoding / decoding ---\n\nexport function encodeGiftLink(link: GiftLink): string {\n const json = JSON.stringify(link);\n const bytes = new TextEncoder().encode(json);\n return base64UrlEncode(bytes);\n}\n\nexport function decodeGiftLink(encoded: string): GiftLink | null {\n try {\n const clean = encoded.replace(/^byoky:\\/\\/gift\\//, '');\n const bytes = base64UrlDecode(clean);\n const json = new TextDecoder().decode(bytes);\n const parsed = JSON.parse(json);\n if (parsed.v !== 1) return null;\n return parsed as GiftLink;\n } catch {\n return null;\n }\n}\n\nexport function giftLinkToUrl(encoded: string): string {\n return `byoky://gift/${encoded}`;\n}\n\n// --- Validation ---\n\nexport function validateGiftLink(link: GiftLink): { valid: boolean; reason?: string } {\n if (link.v !== 1) return { valid: false, reason: 'Unsupported gift version' };\n if (!link.id || typeof link.id !== 'string') return { valid: false, reason: 'Missing gift ID' };\n if (!link.p || typeof link.p !== 'string') return { valid: false, reason: 'Missing provider' };\n if (!link.t || typeof link.t !== 'string') return { valid: false, reason: 'Missing auth token' };\n if (!link.r || typeof link.r !== 'string') return { valid: false, reason: 'Missing relay URL' };\n if (typeof link.m !== 'number' || link.m <= 0) return { valid: false, reason: 'Invalid token budget' };\n if (typeof link.e !== 'number' || link.e <= Date.now()) return { valid: false, reason: 'Gift has expired' };\n\n try {\n const url = new URL(link.r);\n if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {\n return { valid: false, reason: 'Relay URL must use ws:// or wss://' };\n }\n } catch {\n return { valid: false, reason: 'Invalid relay URL' };\n }\n\n return { valid: true };\n}\n\nexport function isGiftExpired(gift: { expiresAt: number }): boolean {\n return gift.expiresAt <= Date.now();\n}\n\nexport function isGiftBudgetExhausted(gift: { usedTokens: number; maxTokens: number }): boolean {\n return gift.usedTokens >= gift.maxTokens;\n}\n\nexport function giftBudgetRemaining(gift: { usedTokens: number; maxTokens: number }): number {\n return Math.max(0, gift.maxTokens - gift.usedTokens);\n}\n\nexport function giftBudgetPercent(gift: { usedTokens: number; maxTokens: number }): number {\n if (gift.maxTokens === 0) return 100;\n return Math.min(100, Math.round((gift.usedTokens / gift.maxTokens) * 100));\n}\n\nexport function createGiftLink(gift: Gift): { encoded: string; link: GiftLink } {\n const provider = PROVIDERS[gift.providerId];\n const link: GiftLink = {\n v: 1,\n id: gift.id,\n p: gift.providerId,\n n: provider?.name ?? gift.providerId,\n s: gift.label,\n t: gift.authToken,\n m: gift.maxTokens,\n e: gift.expiresAt,\n r: gift.relayUrl,\n };\n return { encoded: encodeGiftLink(link), link };\n}\n\n// --- Base64url helpers ---\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction base64UrlDecode(str: string): Uint8Array {\n const padded = str.replace(/-/g, '+').replace(/_/g, '/');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n"],"mappings":"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","names":["ByokyErrorCode","parsed","usage"]}
package/dist/index.d.cts CHANGED
@@ -56,6 +56,9 @@ interface SessionProvider {
56
56
  credentialId: string;
57
57
  available: boolean;
58
58
  authMethod: AuthMethod;
59
+ giftId?: string;
60
+ giftRelayUrl?: string;
61
+ giftAuthToken?: string;
59
62
  }
60
63
  interface ConnectRequest {
61
64
  providers?: ProviderRequirement[];
@@ -317,4 +320,90 @@ declare function computeAllowanceCheck(allowance: TokenAllowance | undefined, en
317
320
  reason?: string;
318
321
  };
319
322
 
320
- export { type ApiKeyCredential, type AuthMethod, BYOKY_MESSAGE_PREFIX, BYOKY_PROVIDER_KEY, ByokyError, ByokyErrorCode, type ByokyMessage, type ConnectRequest, type ConnectResponse, type Credential, type CredentialBase, type CredentialMeta, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayHello, type RelayMessage, type RelayPing, type RelayPong, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RequestLogEntry, type Session, type SessionProvider, type SessionUsage, type TokenAllowance, type TrustedSite, WS_READY_STATE, type WebSocketLike, buildHeaders, checkPasswordStrength, computeAllowanceCheck, createConnectRequest, createConnectResponse, createErrorMessage, createMessage, decrypt, deriveKey, encrypt, extractUsageFromParsed, getProvider, getProviderIds, hashPassword, isByokyMessage, maskKey, parseModel, parseRelayMessage, parseUsage, sendRelayMessage, validateProxyUrl, verifyPassword };
323
+ interface Gift {
324
+ id: string;
325
+ credentialId: string;
326
+ providerId: string;
327
+ label: string;
328
+ authToken: string;
329
+ maxTokens: number;
330
+ usedTokens: number;
331
+ expiresAt: number;
332
+ createdAt: number;
333
+ active: boolean;
334
+ relayUrl: string;
335
+ }
336
+ interface GiftLink {
337
+ v: 1;
338
+ id: string;
339
+ p: string;
340
+ n: string;
341
+ s: string;
342
+ t: string;
343
+ m: number;
344
+ e: number;
345
+ r: string;
346
+ }
347
+ interface GiftedCredential {
348
+ id: string;
349
+ giftId: string;
350
+ providerId: string;
351
+ providerName: string;
352
+ senderLabel: string;
353
+ authToken: string;
354
+ maxTokens: number;
355
+ usedTokens: number;
356
+ expiresAt: number;
357
+ relayUrl: string;
358
+ createdAt: number;
359
+ }
360
+ interface GiftRelayAuth {
361
+ type: 'gift:auth';
362
+ giftId: string;
363
+ authToken: string;
364
+ role: 'sender' | 'recipient';
365
+ }
366
+ interface GiftRelayAuthResult {
367
+ type: 'gift:auth:result';
368
+ success: boolean;
369
+ error?: string;
370
+ peerOnline?: boolean;
371
+ }
372
+ interface GiftRelayPeerStatus {
373
+ type: 'gift:peer:status';
374
+ online: boolean;
375
+ }
376
+ interface GiftRelayUsageUpdate {
377
+ type: 'gift:usage';
378
+ giftId: string;
379
+ usedTokens: number;
380
+ }
381
+ type GiftRelayMessage = GiftRelayAuth | GiftRelayAuthResult | GiftRelayPeerStatus | GiftRelayUsageUpdate;
382
+ declare function encodeGiftLink(link: GiftLink): string;
383
+ declare function decodeGiftLink(encoded: string): GiftLink | null;
384
+ declare function giftLinkToUrl(encoded: string): string;
385
+ declare function validateGiftLink(link: GiftLink): {
386
+ valid: boolean;
387
+ reason?: string;
388
+ };
389
+ declare function isGiftExpired(gift: {
390
+ expiresAt: number;
391
+ }): boolean;
392
+ declare function isGiftBudgetExhausted(gift: {
393
+ usedTokens: number;
394
+ maxTokens: number;
395
+ }): boolean;
396
+ declare function giftBudgetRemaining(gift: {
397
+ usedTokens: number;
398
+ maxTokens: number;
399
+ }): number;
400
+ declare function giftBudgetPercent(gift: {
401
+ usedTokens: number;
402
+ maxTokens: number;
403
+ }): number;
404
+ declare function createGiftLink(gift: Gift): {
405
+ encoded: string;
406
+ link: GiftLink;
407
+ };
408
+
409
+ export { type ApiKeyCredential, type AuthMethod, BYOKY_MESSAGE_PREFIX, BYOKY_PROVIDER_KEY, ByokyError, ByokyErrorCode, type ByokyMessage, type ConnectRequest, type ConnectResponse, type Credential, type CredentialBase, type CredentialMeta, type Gift, type GiftLink, type GiftRelayAuth, type GiftRelayAuthResult, type GiftRelayMessage, type GiftRelayPeerStatus, type GiftRelayUsageUpdate, type GiftedCredential, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayHello, type RelayMessage, type RelayPing, type RelayPong, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RequestLogEntry, type Session, type SessionProvider, type SessionUsage, type TokenAllowance, type TrustedSite, WS_READY_STATE, type WebSocketLike, buildHeaders, checkPasswordStrength, computeAllowanceCheck, createConnectRequest, createConnectResponse, createErrorMessage, createGiftLink, createMessage, decodeGiftLink, decrypt, deriveKey, encodeGiftLink, encrypt, extractUsageFromParsed, getProvider, getProviderIds, giftBudgetPercent, giftBudgetRemaining, giftLinkToUrl, hashPassword, isByokyMessage, isGiftBudgetExhausted, isGiftExpired, maskKey, parseModel, parseRelayMessage, parseUsage, sendRelayMessage, validateGiftLink, validateProxyUrl, verifyPassword };
package/dist/index.d.ts CHANGED
@@ -56,6 +56,9 @@ interface SessionProvider {
56
56
  credentialId: string;
57
57
  available: boolean;
58
58
  authMethod: AuthMethod;
59
+ giftId?: string;
60
+ giftRelayUrl?: string;
61
+ giftAuthToken?: string;
59
62
  }
60
63
  interface ConnectRequest {
61
64
  providers?: ProviderRequirement[];
@@ -317,4 +320,90 @@ declare function computeAllowanceCheck(allowance: TokenAllowance | undefined, en
317
320
  reason?: string;
318
321
  };
319
322
 
320
- export { type ApiKeyCredential, type AuthMethod, BYOKY_MESSAGE_PREFIX, BYOKY_PROVIDER_KEY, ByokyError, ByokyErrorCode, type ByokyMessage, type ConnectRequest, type ConnectResponse, type Credential, type CredentialBase, type CredentialMeta, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayHello, type RelayMessage, type RelayPing, type RelayPong, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RequestLogEntry, type Session, type SessionProvider, type SessionUsage, type TokenAllowance, type TrustedSite, WS_READY_STATE, type WebSocketLike, buildHeaders, checkPasswordStrength, computeAllowanceCheck, createConnectRequest, createConnectResponse, createErrorMessage, createMessage, decrypt, deriveKey, encrypt, extractUsageFromParsed, getProvider, getProviderIds, hashPassword, isByokyMessage, maskKey, parseModel, parseRelayMessage, parseUsage, sendRelayMessage, validateProxyUrl, verifyPassword };
323
+ interface Gift {
324
+ id: string;
325
+ credentialId: string;
326
+ providerId: string;
327
+ label: string;
328
+ authToken: string;
329
+ maxTokens: number;
330
+ usedTokens: number;
331
+ expiresAt: number;
332
+ createdAt: number;
333
+ active: boolean;
334
+ relayUrl: string;
335
+ }
336
+ interface GiftLink {
337
+ v: 1;
338
+ id: string;
339
+ p: string;
340
+ n: string;
341
+ s: string;
342
+ t: string;
343
+ m: number;
344
+ e: number;
345
+ r: string;
346
+ }
347
+ interface GiftedCredential {
348
+ id: string;
349
+ giftId: string;
350
+ providerId: string;
351
+ providerName: string;
352
+ senderLabel: string;
353
+ authToken: string;
354
+ maxTokens: number;
355
+ usedTokens: number;
356
+ expiresAt: number;
357
+ relayUrl: string;
358
+ createdAt: number;
359
+ }
360
+ interface GiftRelayAuth {
361
+ type: 'gift:auth';
362
+ giftId: string;
363
+ authToken: string;
364
+ role: 'sender' | 'recipient';
365
+ }
366
+ interface GiftRelayAuthResult {
367
+ type: 'gift:auth:result';
368
+ success: boolean;
369
+ error?: string;
370
+ peerOnline?: boolean;
371
+ }
372
+ interface GiftRelayPeerStatus {
373
+ type: 'gift:peer:status';
374
+ online: boolean;
375
+ }
376
+ interface GiftRelayUsageUpdate {
377
+ type: 'gift:usage';
378
+ giftId: string;
379
+ usedTokens: number;
380
+ }
381
+ type GiftRelayMessage = GiftRelayAuth | GiftRelayAuthResult | GiftRelayPeerStatus | GiftRelayUsageUpdate;
382
+ declare function encodeGiftLink(link: GiftLink): string;
383
+ declare function decodeGiftLink(encoded: string): GiftLink | null;
384
+ declare function giftLinkToUrl(encoded: string): string;
385
+ declare function validateGiftLink(link: GiftLink): {
386
+ valid: boolean;
387
+ reason?: string;
388
+ };
389
+ declare function isGiftExpired(gift: {
390
+ expiresAt: number;
391
+ }): boolean;
392
+ declare function isGiftBudgetExhausted(gift: {
393
+ usedTokens: number;
394
+ maxTokens: number;
395
+ }): boolean;
396
+ declare function giftBudgetRemaining(gift: {
397
+ usedTokens: number;
398
+ maxTokens: number;
399
+ }): number;
400
+ declare function giftBudgetPercent(gift: {
401
+ usedTokens: number;
402
+ maxTokens: number;
403
+ }): number;
404
+ declare function createGiftLink(gift: Gift): {
405
+ encoded: string;
406
+ link: GiftLink;
407
+ };
408
+
409
+ export { type ApiKeyCredential, type AuthMethod, BYOKY_MESSAGE_PREFIX, BYOKY_PROVIDER_KEY, ByokyError, ByokyErrorCode, type ByokyMessage, type ConnectRequest, type ConnectResponse, type Credential, type CredentialBase, type CredentialMeta, type Gift, type GiftLink, type GiftRelayAuth, type GiftRelayAuthResult, type GiftRelayMessage, type GiftRelayPeerStatus, type GiftRelayUsageUpdate, type GiftedCredential, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayHello, type RelayMessage, type RelayPing, type RelayPong, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RequestLogEntry, type Session, type SessionProvider, type SessionUsage, type TokenAllowance, type TrustedSite, WS_READY_STATE, type WebSocketLike, buildHeaders, checkPasswordStrength, computeAllowanceCheck, createConnectRequest, createConnectResponse, createErrorMessage, createGiftLink, createMessage, decodeGiftLink, decrypt, deriveKey, encodeGiftLink, encrypt, extractUsageFromParsed, getProvider, getProviderIds, giftBudgetPercent, giftBudgetRemaining, giftLinkToUrl, hashPassword, isByokyMessage, isGiftBudgetExhausted, isGiftExpired, maskKey, parseModel, parseRelayMessage, parseUsage, sendRelayMessage, validateGiftLink, validateProxyUrl, verifyPassword };
package/dist/index.js CHANGED
@@ -462,12 +462,23 @@ function buildHeaders(providerId, requestHeaders, apiKey, authMethod = "api_key"
462
462
  delete headers["authorization"];
463
463
  delete headers["x-api-key"];
464
464
  delete headers["api-key"];
465
+ delete headers["origin"];
466
+ delete headers["referer"];
467
+ for (const key of Object.keys(headers)) {
468
+ if (key.startsWith("x-stainless-")) delete headers[key];
469
+ }
470
+ delete headers["sec-fetch-mode"];
471
+ delete headers["accept-language"];
472
+ delete headers["accept-encoding"];
473
+ delete headers["content-length"];
465
474
  if (providerId === "anthropic") {
466
475
  if (authMethod === "oauth") {
467
476
  headers["authorization"] = `Bearer ${apiKey}`;
468
- headers["user-agent"] = "claude-cli/2.1.75";
477
+ headers["user-agent"] = "claude-cli/2.1.76";
469
478
  headers["x-app"] = "cli";
470
- headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20";
479
+ headers["accept"] = "application/json";
480
+ headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
481
+ headers["anthropic-dangerous-direct-browser-access"] = "true";
471
482
  } else {
472
483
  headers["x-api-key"] = apiKey;
473
484
  }
@@ -550,6 +561,86 @@ function computeAllowanceCheck(allowance, entries, providerId) {
550
561
  }
551
562
  return { allowed: true };
552
563
  }
564
+
565
+ // src/gift.ts
566
+ function encodeGiftLink(link) {
567
+ const json = JSON.stringify(link);
568
+ const bytes = new TextEncoder().encode(json);
569
+ return base64UrlEncode(bytes);
570
+ }
571
+ function decodeGiftLink(encoded) {
572
+ try {
573
+ const clean = encoded.replace(/^byoky:\/\/gift\//, "");
574
+ const bytes = base64UrlDecode(clean);
575
+ const json = new TextDecoder().decode(bytes);
576
+ const parsed = JSON.parse(json);
577
+ if (parsed.v !== 1) return null;
578
+ return parsed;
579
+ } catch {
580
+ return null;
581
+ }
582
+ }
583
+ function giftLinkToUrl(encoded) {
584
+ return `byoky://gift/${encoded}`;
585
+ }
586
+ function validateGiftLink(link) {
587
+ if (link.v !== 1) return { valid: false, reason: "Unsupported gift version" };
588
+ if (!link.id || typeof link.id !== "string") return { valid: false, reason: "Missing gift ID" };
589
+ if (!link.p || typeof link.p !== "string") return { valid: false, reason: "Missing provider" };
590
+ if (!link.t || typeof link.t !== "string") return { valid: false, reason: "Missing auth token" };
591
+ if (!link.r || typeof link.r !== "string") return { valid: false, reason: "Missing relay URL" };
592
+ if (typeof link.m !== "number" || link.m <= 0) return { valid: false, reason: "Invalid token budget" };
593
+ if (typeof link.e !== "number" || link.e <= Date.now()) return { valid: false, reason: "Gift has expired" };
594
+ try {
595
+ const url = new URL(link.r);
596
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") {
597
+ return { valid: false, reason: "Relay URL must use ws:// or wss://" };
598
+ }
599
+ } catch {
600
+ return { valid: false, reason: "Invalid relay URL" };
601
+ }
602
+ return { valid: true };
603
+ }
604
+ function isGiftExpired(gift) {
605
+ return gift.expiresAt <= Date.now();
606
+ }
607
+ function isGiftBudgetExhausted(gift) {
608
+ return gift.usedTokens >= gift.maxTokens;
609
+ }
610
+ function giftBudgetRemaining(gift) {
611
+ return Math.max(0, gift.maxTokens - gift.usedTokens);
612
+ }
613
+ function giftBudgetPercent(gift) {
614
+ if (gift.maxTokens === 0) return 100;
615
+ return Math.min(100, Math.round(gift.usedTokens / gift.maxTokens * 100));
616
+ }
617
+ function createGiftLink(gift) {
618
+ const provider = PROVIDERS[gift.providerId];
619
+ const link = {
620
+ v: 1,
621
+ id: gift.id,
622
+ p: gift.providerId,
623
+ n: provider?.name ?? gift.providerId,
624
+ s: gift.label,
625
+ t: gift.authToken,
626
+ m: gift.maxTokens,
627
+ e: gift.expiresAt,
628
+ r: gift.relayUrl
629
+ };
630
+ return { encoded: encodeGiftLink(link), link };
631
+ }
632
+ function base64UrlEncode(bytes) {
633
+ let binary = "";
634
+ for (const byte of bytes) binary += String.fromCharCode(byte);
635
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
636
+ }
637
+ function base64UrlDecode(str) {
638
+ const padded = str.replace(/-/g, "+").replace(/_/g, "/");
639
+ const binary = atob(padded);
640
+ const bytes = new Uint8Array(binary.length);
641
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
642
+ return bytes;
643
+ }
553
644
  export {
554
645
  BYOKY_MESSAGE_PREFIX,
555
646
  BYOKY_PROVIDER_KEY,
@@ -564,20 +655,29 @@ export {
564
655
  createConnectRequest,
565
656
  createConnectResponse,
566
657
  createErrorMessage,
658
+ createGiftLink,
567
659
  createMessage,
660
+ decodeGiftLink,
568
661
  decrypt,
569
662
  deriveKey,
663
+ encodeGiftLink,
570
664
  encrypt,
571
665
  extractUsageFromParsed,
572
666
  getProvider,
573
667
  getProviderIds,
668
+ giftBudgetPercent,
669
+ giftBudgetRemaining,
670
+ giftLinkToUrl,
574
671
  hashPassword,
575
672
  isByokyMessage,
673
+ isGiftBudgetExhausted,
674
+ isGiftExpired,
576
675
  maskKey,
577
676
  parseModel,
578
677
  parseRelayMessage,
579
678
  parseUsage,
580
679
  sendRelayMessage,
680
+ validateGiftLink,
581
681
  validateProxyUrl,
582
682
  verifyPassword
583
683
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/crypto.ts","../src/errors.ts","../src/protocol.ts","../src/providers.ts","../src/relay.ts","../src/password-strength.ts","../src/proxy-utils.ts"],"sourcesContent":["export type ProviderId = 'anthropic' | 'openai' | 'gemini' | (string & {});\n\nexport type AuthMethod = 'api_key' | 'oauth';\n\nexport interface ProviderConfig {\n id: ProviderId;\n name: string;\n authMethods: AuthMethod[];\n baseUrl: string;\n oauthConfig?: OAuthConfig;\n}\n\nexport interface OAuthConfig {\n clientId: string;\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n extraAuthParams?: Record<string, string>;\n}\n\n// --- Credentials ---\n\nexport interface CredentialBase {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n}\n\nexport interface ApiKeyCredential extends CredentialBase {\n authMethod: 'api_key';\n encryptedKey: string;\n}\n\nexport interface OAuthCredential extends CredentialBase {\n authMethod: 'oauth';\n encryptedAccessToken: string;\n encryptedRefreshToken?: string;\n expiresAt?: number;\n}\n\nexport type Credential = ApiKeyCredential | OAuthCredential;\n\nexport interface CredentialMeta {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n maskedKey?: string;\n}\n\n// --- Sessions ---\n\nexport interface Session {\n id: string;\n sessionKey: string;\n appOrigin: string;\n appName?: string;\n providers: SessionProvider[];\n createdAt: number;\n expiresAt: number;\n}\n\nexport interface SessionProvider {\n providerId: ProviderId;\n credentialId: string;\n available: boolean;\n authMethod: AuthMethod;\n}\n\n// --- Connect ---\n\nexport interface ConnectRequest {\n providers?: ProviderRequirement[];\n capabilities?: string[];\n}\n\nexport interface ProviderRequirement {\n id: ProviderId;\n required: boolean;\n}\n\nexport interface ConnectResponse {\n sessionKey: string;\n proxyUrl: string;\n providers: Record<\n string,\n {\n available: boolean;\n authMethod: AuthMethod;\n }\n >;\n}\n\n// --- Proxy ---\n\nexport interface ProxyRequest {\n requestId: string;\n sessionKey: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface ProxyResponseMeta {\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface ProxyResponseChunk {\n requestId: string;\n chunk: string;\n}\n\nexport interface ProxyResponseError {\n requestId: string;\n status: number;\n error: { code: string; message: string };\n}\n\n// --- Protocol messages ---\n\nexport type MessageType =\n | 'BYOKY_CONNECT_REQUEST'\n | 'BYOKY_CONNECT_RESPONSE'\n | 'BYOKY_DISCONNECT'\n | 'BYOKY_PROXY_REQUEST'\n | 'BYOKY_PROXY_RESPONSE_META'\n | 'BYOKY_PROXY_RESPONSE_CHUNK'\n | 'BYOKY_PROXY_RESPONSE_DONE'\n | 'BYOKY_PROXY_RESPONSE_ERROR'\n | 'BYOKY_SESSION_STATUS'\n | 'BYOKY_SESSION_STATUS_RESPONSE'\n | 'BYOKY_SESSION_USAGE'\n | 'BYOKY_SESSION_USAGE_RESPONSE'\n | 'BYOKY_SESSION_REVOKED'\n | 'BYOKY_ERROR';\n\nexport interface ByokyMessage {\n type: MessageType;\n id: string;\n requestId?: string;\n payload: unknown;\n}\n\n// --- Session queries ---\n\nexport interface SessionUsage {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n byProvider: Record<string, {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n }>;\n}\n\n// --- Errors ---\n\nexport enum ByokyErrorCode {\n WALLET_NOT_INSTALLED = 'WALLET_NOT_INSTALLED',\n USER_REJECTED = 'USER_REJECTED',\n PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE',\n SESSION_EXPIRED = 'SESSION_EXPIRED',\n RATE_LIMITED = 'RATE_LIMITED',\n QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',\n INVALID_KEY = 'INVALID_KEY',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n PROXY_ERROR = 'PROXY_ERROR',\n RELAY_CONNECTION_FAILED = 'RELAY_CONNECTION_FAILED',\n RELAY_DISCONNECTED = 'RELAY_DISCONNECTED',\n UNKNOWN = 'UNKNOWN',\n}\n\n// --- Request log ---\n\nexport interface RequestLogEntry {\n id: string;\n sessionId: string;\n appOrigin: string;\n providerId: ProviderId;\n url: string;\n method: string;\n status: number;\n timestamp: number;\n error?: string;\n inputTokens?: number;\n outputTokens?: number;\n model?: string;\n}\n\n// --- Pending approval ---\n\nexport interface PendingApproval {\n id: string;\n appOrigin: string;\n appName?: string;\n providers: ProviderRequirement[];\n timestamp: number;\n}\n\n// --- Trusted sites ---\n\nexport interface TrustedSite {\n origin: string;\n trustedAt: number;\n}\n\n// --- Token allowances ---\n\nexport interface TokenAllowance {\n origin: string;\n totalLimit?: number;\n providerLimits?: Record<string, number>;\n}\n","const SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst KEY_LENGTH = 256;\nconst ITERATIONS = 600_000;\n\nexport async function deriveKey(\n password: string,\n salt: Uint8Array,\n): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveKey'],\n );\n\n return crypto.subtle.deriveKey(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n { name: 'AES-GCM', length: KEY_LENGTH },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\nexport async function encrypt(\n plaintext: string,\n password: string,\n): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n const key = await deriveKey(password, salt);\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(plaintext),\n );\n\n const combined = new Uint8Array(\n salt.length + iv.length + new Uint8Array(ciphertext).length,\n );\n combined.set(salt, 0);\n combined.set(iv, salt.length);\n combined.set(new Uint8Array(ciphertext), salt.length + iv.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function decrypt(\n encrypted: string,\n password: string,\n): Promise<string> {\n const combined = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));\n\n const salt = combined.slice(0, SALT_LENGTH);\n const iv = combined.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const ciphertext = combined.slice(SALT_LENGTH + IV_LENGTH);\n\n const key = await deriveKey(password, salt);\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n ciphertext,\n );\n\n return new TextDecoder().decode(plaintext);\n}\n\nasync function deriveRawHash(\n password: string,\n salt: Uint8Array,\n): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveBits'],\n );\n\n const bits = await crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n KEY_LENGTH,\n );\n\n return new Uint8Array(bits);\n}\n\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const hash = await deriveRawHash(password, salt);\n\n const combined = new Uint8Array(salt.length + hash.length);\n combined.set(salt, 0);\n combined.set(hash, salt.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function verifyPassword(\n password: string,\n storedHash: string,\n): Promise<boolean> {\n const combined = Uint8Array.from(atob(storedHash), (c) => c.charCodeAt(0));\n const salt = combined.slice(0, SALT_LENGTH);\n const originalHash = combined.slice(SALT_LENGTH);\n\n const newHash = await deriveRawHash(password, salt);\n\n if (originalHash.length !== newHash.length) return false;\n let result = 0;\n for (let i = 0; i < originalHash.length; i++) {\n result |= originalHash[i] ^ newHash[i];\n }\n return result === 0;\n}\n\nexport function maskKey(key: string): string {\n if (key.length <= 8) return '****';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n}\n","import { ByokyErrorCode } from './types.js';\n\nexport class ByokyError extends Error {\n constructor(\n public readonly code: ByokyErrorCode,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = 'ByokyError';\n }\n\n static walletNotInstalled() {\n return new ByokyError(\n ByokyErrorCode.WALLET_NOT_INSTALLED,\n 'byoky wallet extension is not installed',\n );\n }\n\n static userRejected() {\n return new ByokyError(\n ByokyErrorCode.USER_REJECTED,\n 'User rejected the connection request',\n );\n }\n\n static providerUnavailable(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.PROVIDER_UNAVAILABLE,\n `Provider \"${providerId}\" is not available`,\n { providerId },\n );\n }\n\n static sessionExpired() {\n return new ByokyError(\n ByokyErrorCode.SESSION_EXPIRED,\n 'Session has expired — please reconnect',\n );\n }\n\n static rateLimited(retryAfter?: number) {\n return new ByokyError(\n ByokyErrorCode.RATE_LIMITED,\n `Rate limit exceeded${retryAfter ? ` — retry after ${retryAfter}s` : ''}`,\n { retryAfter },\n );\n }\n\n static quotaExceeded(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.QUOTA_EXCEEDED,\n `Quota exceeded for ${providerId} — check your billing`,\n { providerId },\n );\n }\n\n static invalidKey(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.INVALID_KEY,\n `Invalid API key for ${providerId}`,\n { providerId },\n );\n }\n\n static tokenExpired(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.TOKEN_EXPIRED,\n `OAuth token expired for ${providerId} — re-authentication required`,\n { providerId },\n );\n }\n\n static relayConnectionFailed(reason?: string) {\n return new ByokyError(\n ByokyErrorCode.RELAY_CONNECTION_FAILED,\n `Relay connection failed${reason ? `: ${reason}` : ''}`,\n );\n }\n\n static relayDisconnected() {\n return new ByokyError(\n ByokyErrorCode.RELAY_DISCONNECTED,\n 'Relay connection was closed',\n );\n }\n}\n","import type {\n ByokyMessage,\n ConnectRequest,\n ConnectResponse,\n MessageType,\n} from './types.js';\n\nexport const BYOKY_PROVIDER_KEY = '__byoky__';\nexport const BYOKY_MESSAGE_PREFIX = 'BYOKY_';\n\nexport function createMessage(\n type: MessageType,\n payload: unknown,\n requestId?: string,\n): ByokyMessage {\n return {\n type,\n id: crypto.randomUUID(),\n requestId,\n payload,\n };\n}\n\nexport function isByokyMessage(data: unknown): data is ByokyMessage {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n typeof (data as ByokyMessage).type === 'string' &&\n (data as ByokyMessage).type.startsWith(BYOKY_MESSAGE_PREFIX)\n );\n}\n\nexport function createConnectRequest(request: ConnectRequest): ByokyMessage {\n return createMessage('BYOKY_CONNECT_REQUEST', request);\n}\n\nexport function createConnectResponse(\n response: ConnectResponse,\n requestId: string,\n): ByokyMessage {\n return createMessage('BYOKY_CONNECT_RESPONSE', response, requestId);\n}\n\nexport function createErrorMessage(\n code: string,\n message: string,\n requestId?: string,\n): ByokyMessage {\n return createMessage('BYOKY_ERROR', { code, message }, requestId);\n}\n","import type { ProviderConfig } from './types.js';\n\nexport const PROVIDERS: Record<string, ProviderConfig> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api.anthropic.com',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.openai.com',\n },\n gemini: {\n id: 'gemini',\n name: 'Google Gemini',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://generativelanguage.googleapis.com',\n oauthConfig: {\n clientId: '699663966637-gr4d994198r4g6jvip25ffg85kree6ck.apps.googleusercontent.com',\n authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n scopes: ['https://www.googleapis.com/auth/generative-language'],\n extraAuthParams: { access_type: 'offline', prompt: 'consent' },\n },\n },\n mistral: {\n id: 'mistral',\n name: 'Mistral',\n authMethods: ['api_key'],\n baseUrl: 'https://api.mistral.ai',\n },\n cohere: {\n id: 'cohere',\n name: 'Cohere',\n authMethods: ['api_key'],\n baseUrl: 'https://api.cohere.com',\n },\n xai: {\n id: 'xai',\n name: 'xAI (Grok)',\n authMethods: ['api_key'],\n baseUrl: 'https://api.x.ai',\n },\n deepseek: {\n id: 'deepseek',\n name: 'DeepSeek',\n authMethods: ['api_key'],\n baseUrl: 'https://api.deepseek.com',\n },\n perplexity: {\n id: 'perplexity',\n name: 'Perplexity',\n authMethods: ['api_key'],\n baseUrl: 'https://api.perplexity.ai',\n },\n groq: {\n id: 'groq',\n name: 'Groq',\n authMethods: ['api_key'],\n baseUrl: 'https://api.groq.com',\n },\n together: {\n id: 'together',\n name: 'Together AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.together.xyz',\n },\n fireworks: {\n id: 'fireworks',\n name: 'Fireworks AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.fireworks.ai',\n },\n replicate: {\n id: 'replicate',\n name: 'Replicate',\n authMethods: ['api_key'],\n baseUrl: 'https://api.replicate.com',\n },\n openrouter: {\n id: 'openrouter',\n name: 'OpenRouter',\n authMethods: ['api_key'],\n baseUrl: 'https://openrouter.ai/api',\n },\n huggingface: {\n id: 'huggingface',\n name: 'Hugging Face',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api-inference.huggingface.co',\n oauthConfig: {\n clientId: '031aeb11-725b-498a-93f9-d3599d84f57c',\n authorizationUrl: 'https://huggingface.co/oauth/authorize',\n tokenUrl: 'https://huggingface.co/oauth/token',\n scopes: ['inference-api'],\n },\n },\n azure_openai: {\n id: 'azure_openai',\n name: 'Azure OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://YOUR_RESOURCE.openai.azure.com',\n },\n};\n\nexport function getProvider(id: string): ProviderConfig | undefined {\n return PROVIDERS[id];\n}\n\nexport function getProviderIds(): string[] {\n return Object.keys(PROVIDERS);\n}\n","import type { AuthMethod } from './types.js';\n\n/** Minimal WebSocket interface — compatible with browser WebSocket and `ws` library. */\nexport interface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport const WS_READY_STATE = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n// --- Relay message types ---\n\nexport interface RelayHello {\n type: 'relay:hello';\n sessionId: string;\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>;\n}\n\nexport interface RelayRequest {\n type: 'relay:request';\n requestId: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface RelayResponseMeta {\n type: 'relay:response:meta';\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface RelayResponseChunk {\n type: 'relay:response:chunk';\n requestId: string;\n chunk: string;\n}\n\nexport interface RelayResponseDone {\n type: 'relay:response:done';\n requestId: string;\n}\n\nexport interface RelayResponseError {\n type: 'relay:response:error';\n requestId: string;\n error: { code: string; message: string };\n}\n\nexport interface RelayPing {\n type: 'relay:ping';\n ts: number;\n}\n\nexport interface RelayPong {\n type: 'relay:pong';\n ts: number;\n}\n\nexport type RelayMessage =\n | RelayHello\n | RelayRequest\n | RelayResponseMeta\n | RelayResponseChunk\n | RelayResponseDone\n | RelayResponseError\n | RelayPing\n | RelayPong;\n\nexport function parseRelayMessage(data: unknown): RelayMessage | null {\n try {\n const raw = typeof data === 'string' ? JSON.parse(data) : data;\n if (!raw || typeof raw !== 'object' || typeof raw.type !== 'string' || !raw.type.startsWith('relay:')) {\n return null;\n }\n\n // Validate required fields per message type\n switch (raw.type) {\n case 'relay:hello':\n if (typeof raw.sessionId !== 'string') return null;\n break;\n case 'relay:request':\n if (typeof raw.requestId !== 'string' || typeof raw.providerId !== 'string' ||\n typeof raw.url !== 'string' || typeof raw.method !== 'string') return null;\n break;\n case 'relay:response:meta':\n if (typeof raw.requestId !== 'string' || typeof raw.status !== 'number') return null;\n break;\n case 'relay:response:chunk':\n if (typeof raw.requestId !== 'string' || typeof raw.chunk !== 'string') return null;\n break;\n case 'relay:response:done':\n case 'relay:response:error':\n if (typeof raw.requestId !== 'string') return null;\n break;\n case 'relay:ping':\n case 'relay:pong':\n if (typeof raw.ts !== 'number') return null;\n break;\n default:\n return null;\n }\n\n return raw as RelayMessage;\n } catch {\n return null;\n }\n}\n\nexport function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n","export interface PasswordStrength {\n score: 0 | 1 | 2 | 3 | 4;\n label: 'Too weak' | 'Weak' | 'Fair' | 'Strong' | 'Very strong';\n feedback: string[];\n}\n\nconst COMMON_PASSWORDS = new Set([\n 'password', '12345678', 'qwerty12', 'letmein12', 'welcome1',\n 'monkey12', 'dragon12', 'master12', 'abc12345', 'password1',\n 'password12', 'iloveyou1', 'sunshine1', 'trustno1', 'princess1',\n 'football1', 'shadow123', 'michael1', 'jordan123', 'superman1',\n]);\n\nexport function checkPasswordStrength(password: string): PasswordStrength {\n const feedback: string[] = [];\n let score = 0;\n\n if (password.length < 12) {\n feedback.push('Use at least 12 characters');\n if (password.length < 8) {\n return { score: 0, label: 'Too weak', feedback };\n }\n } else {\n score++;\n if (password.length >= 16) score++;\n }\n\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n feedback.push('This is a commonly used password');\n return { score: 0, label: 'Too weak', feedback };\n }\n\n // Check character diversity\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasDigit = /\\d/.test(password);\n const hasSymbol = /[^a-zA-Z0-9]/.test(password);\n const charTypes = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;\n\n if (charTypes < 2) {\n feedback.push('Mix uppercase, lowercase, numbers, and symbols');\n } else if (charTypes >= 3) {\n score++;\n }\n if (charTypes >= 4) {\n score++;\n }\n\n // Check for repeated characters (e.g. aaaaaa)\n if (/(.)\\1{3,}/.test(password)) {\n feedback.push('Avoid repeated characters');\n score = Math.max(0, score - 1);\n }\n\n // Check for sequential patterns (e.g. 123456, abcdef)\n if (/(?:012|123|234|345|456|567|678|789|abc|bcd|cde|def)/i.test(password)) {\n feedback.push('Avoid sequential patterns');\n score = Math.max(0, score - 1);\n }\n\n const capped = Math.min(4, Math.max(0, score)) as 0 | 1 | 2 | 3 | 4;\n const labels: Record<number, PasswordStrength['label']> = {\n 0: 'Too weak',\n 1: 'Weak',\n 2: 'Fair',\n 3: 'Strong',\n 4: 'Very strong',\n };\n\n if (feedback.length === 0 && capped < 3) {\n feedback.push('Add more character variety or length');\n }\n\n return { score: capped, label: labels[capped], feedback };\n}\n\nexport const MIN_PASSWORD_LENGTH = 12;\n","import type { RequestLogEntry, TokenAllowance } from './types.js';\nimport { PROVIDERS } from './providers.js';\n\n/**\n * Validate that a proxy request URL targets the registered provider's base URL.\n * Prevents API key exfiltration by rejecting requests to arbitrary domains.\n */\nexport function validateProxyUrl(providerId: string, url: string): boolean {\n const provider = PROVIDERS[providerId];\n if (!provider) return false;\n try {\n const target = new URL(url);\n if (target.protocol !== 'https:') return false;\n const base = new URL(provider.baseUrl);\n return target.origin === base.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the real auth headers for a provider API request.\n * Strips any fake session-key headers and injects the real API key.\n */\nexport function buildHeaders(\n providerId: string,\n requestHeaders: Record<string, string>,\n apiKey: string,\n authMethod: string = 'api_key',\n): Record<string, string> {\n // Normalize header keys to lowercase to prevent case-sensitive bypass\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(requestHeaders)) {\n headers[key.toLowerCase()] = value;\n }\n\n // Remove any auth headers the SDK might have set (they're fake session keys)\n delete headers['authorization'];\n delete headers['x-api-key'];\n delete headers['api-key'];\n\n if (providerId === 'anthropic') {\n if (authMethod === 'oauth') {\n headers['authorization'] = `Bearer ${apiKey}`;\n headers['user-agent'] = 'claude-cli/2.1.75';\n headers['x-app'] = 'cli';\n headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20';\n } else {\n headers['x-api-key'] = apiKey;\n }\n headers['anthropic-version'] = headers['anthropic-version'] ?? '2023-06-01';\n } else if (providerId === 'azure_openai') {\n headers['api-key'] = apiKey;\n } else {\n headers['authorization'] = `Bearer ${apiKey}`;\n }\n\n return headers;\n}\n\n/**\n * Parse the model name from a request body (JSON).\n */\nexport function parseModel(body?: string): string | undefined {\n if (!body) return undefined;\n try {\n const parsed = JSON.parse(body);\n return parsed.model ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse token usage from a provider API response body.\n * Handles both regular JSON responses and SSE streaming responses.\n */\nexport function parseUsage(\n providerId: string,\n body: string,\n): { inputTokens: number; outputTokens: number } | undefined {\n try {\n // For streaming responses (SSE), try to find usage in the last data chunk\n if (body.includes('data: ')) {\n const lines = body.split('\\n').filter((l) => l.startsWith('data: ') && !l.includes('[DONE]'));\n // Anthropic streaming: message_stop event has usage in a preceding message_delta\n // OpenAI streaming: last chunk may include usage\n for (let i = lines.length - 1; i >= 0; i--) {\n const json = lines[i].replace('data: ', '');\n try {\n const parsed = JSON.parse(json);\n const usage = extractUsageFromParsed(providerId, parsed);\n if (usage) return usage;\n } catch {\n continue;\n }\n }\n return undefined;\n }\n\n const parsed = JSON.parse(body);\n return extractUsageFromParsed(providerId, parsed);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Extract token usage from a parsed provider response object.\n */\nexport function extractUsageFromParsed(\n providerId: string,\n parsed: Record<string, unknown>,\n): { inputTokens: number; outputTokens: number } | undefined {\n // Anthropic: { usage: { input_tokens, output_tokens } }\n if (providerId === 'anthropic') {\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };\n }\n }\n\n // Gemini: { usageMetadata: { promptTokenCount, candidatesTokenCount } }\n if (providerId === 'gemini') {\n const meta = parsed.usageMetadata as Record<string, number> | undefined;\n if (meta?.promptTokenCount != null) {\n return {\n inputTokens: meta.promptTokenCount,\n outputTokens: meta.candidatesTokenCount ?? 0,\n };\n }\n }\n\n // OpenAI-compatible (openai, groq, together, deepseek, xai, perplexity, fireworks, openrouter, mistral, azure_openai):\n // { usage: { prompt_tokens, completion_tokens } }\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n return { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens };\n }\n\n return undefined;\n}\n\n/**\n * Check whether a request is allowed given token allowances and usage history.\n * Pure computation — no storage access.\n */\nexport function computeAllowanceCheck(\n allowance: TokenAllowance | undefined,\n entries: Pick<RequestLogEntry, 'providerId' | 'inputTokens' | 'outputTokens'>[],\n providerId: string,\n): { allowed: boolean; reason?: string } {\n if (!allowance) return { allowed: true };\n\n let totalUsed = 0;\n const byProvider: Record<string, number> = {};\n for (const entry of entries) {\n const tokens = (entry.inputTokens ?? 0) + (entry.outputTokens ?? 0);\n totalUsed += tokens;\n byProvider[entry.providerId] = (byProvider[entry.providerId] ?? 0) + tokens;\n }\n\n if (allowance.totalLimit != null && totalUsed >= allowance.totalLimit) {\n return { allowed: false, reason: `Token allowance exceeded for ${allowance.origin}` };\n }\n\n const providerLimit = allowance.providerLimits?.[providerId];\n if (providerLimit != null && (byProvider[providerId] ?? 0) >= providerLimit) {\n return { allowed: false, reason: `Token allowance for ${providerId} exceeded` };\n }\n\n return { allowed: true };\n}\n"],"mappings":"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","names":["ByokyErrorCode","parsed","usage"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/crypto.ts","../src/errors.ts","../src/protocol.ts","../src/providers.ts","../src/relay.ts","../src/password-strength.ts","../src/proxy-utils.ts","../src/gift.ts"],"sourcesContent":["export type ProviderId = 'anthropic' | 'openai' | 'gemini' | (string & {});\n\nexport type AuthMethod = 'api_key' | 'oauth';\n\nexport interface ProviderConfig {\n id: ProviderId;\n name: string;\n authMethods: AuthMethod[];\n baseUrl: string;\n oauthConfig?: OAuthConfig;\n}\n\nexport interface OAuthConfig {\n clientId: string;\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n extraAuthParams?: Record<string, string>;\n}\n\n// --- Credentials ---\n\nexport interface CredentialBase {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n}\n\nexport interface ApiKeyCredential extends CredentialBase {\n authMethod: 'api_key';\n encryptedKey: string;\n}\n\nexport interface OAuthCredential extends CredentialBase {\n authMethod: 'oauth';\n encryptedAccessToken: string;\n encryptedRefreshToken?: string;\n expiresAt?: number;\n}\n\nexport type Credential = ApiKeyCredential | OAuthCredential;\n\nexport interface CredentialMeta {\n id: string;\n providerId: ProviderId;\n label: string;\n authMethod: AuthMethod;\n createdAt: number;\n lastUsedAt?: number;\n maskedKey?: string;\n}\n\n// --- Sessions ---\n\nexport interface Session {\n id: string;\n sessionKey: string;\n appOrigin: string;\n appName?: string;\n providers: SessionProvider[];\n createdAt: number;\n expiresAt: number;\n}\n\nexport interface SessionProvider {\n providerId: ProviderId;\n credentialId: string;\n available: boolean;\n authMethod: AuthMethod;\n giftId?: string;\n giftRelayUrl?: string;\n giftAuthToken?: string;\n}\n\n// --- Connect ---\n\nexport interface ConnectRequest {\n providers?: ProviderRequirement[];\n capabilities?: string[];\n}\n\nexport interface ProviderRequirement {\n id: ProviderId;\n required: boolean;\n}\n\nexport interface ConnectResponse {\n sessionKey: string;\n proxyUrl: string;\n providers: Record<\n string,\n {\n available: boolean;\n authMethod: AuthMethod;\n }\n >;\n}\n\n// --- Proxy ---\n\nexport interface ProxyRequest {\n requestId: string;\n sessionKey: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface ProxyResponseMeta {\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface ProxyResponseChunk {\n requestId: string;\n chunk: string;\n}\n\nexport interface ProxyResponseError {\n requestId: string;\n status: number;\n error: { code: string; message: string };\n}\n\n// --- Protocol messages ---\n\nexport type MessageType =\n | 'BYOKY_CONNECT_REQUEST'\n | 'BYOKY_CONNECT_RESPONSE'\n | 'BYOKY_DISCONNECT'\n | 'BYOKY_PROXY_REQUEST'\n | 'BYOKY_PROXY_RESPONSE_META'\n | 'BYOKY_PROXY_RESPONSE_CHUNK'\n | 'BYOKY_PROXY_RESPONSE_DONE'\n | 'BYOKY_PROXY_RESPONSE_ERROR'\n | 'BYOKY_SESSION_STATUS'\n | 'BYOKY_SESSION_STATUS_RESPONSE'\n | 'BYOKY_SESSION_USAGE'\n | 'BYOKY_SESSION_USAGE_RESPONSE'\n | 'BYOKY_SESSION_REVOKED'\n | 'BYOKY_ERROR';\n\nexport interface ByokyMessage {\n type: MessageType;\n id: string;\n requestId?: string;\n payload: unknown;\n}\n\n// --- Session queries ---\n\nexport interface SessionUsage {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n byProvider: Record<string, {\n requests: number;\n inputTokens: number;\n outputTokens: number;\n }>;\n}\n\n// --- Errors ---\n\nexport enum ByokyErrorCode {\n WALLET_NOT_INSTALLED = 'WALLET_NOT_INSTALLED',\n USER_REJECTED = 'USER_REJECTED',\n PROVIDER_UNAVAILABLE = 'PROVIDER_UNAVAILABLE',\n SESSION_EXPIRED = 'SESSION_EXPIRED',\n RATE_LIMITED = 'RATE_LIMITED',\n QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',\n INVALID_KEY = 'INVALID_KEY',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n PROXY_ERROR = 'PROXY_ERROR',\n RELAY_CONNECTION_FAILED = 'RELAY_CONNECTION_FAILED',\n RELAY_DISCONNECTED = 'RELAY_DISCONNECTED',\n UNKNOWN = 'UNKNOWN',\n}\n\n// --- Request log ---\n\nexport interface RequestLogEntry {\n id: string;\n sessionId: string;\n appOrigin: string;\n providerId: ProviderId;\n url: string;\n method: string;\n status: number;\n timestamp: number;\n error?: string;\n inputTokens?: number;\n outputTokens?: number;\n model?: string;\n}\n\n// --- Pending approval ---\n\nexport interface PendingApproval {\n id: string;\n appOrigin: string;\n appName?: string;\n providers: ProviderRequirement[];\n timestamp: number;\n}\n\n// --- Trusted sites ---\n\nexport interface TrustedSite {\n origin: string;\n trustedAt: number;\n}\n\n// --- Token allowances ---\n\nexport interface TokenAllowance {\n origin: string;\n totalLimit?: number;\n providerLimits?: Record<string, number>;\n}\n","const SALT_LENGTH = 16;\nconst IV_LENGTH = 12;\nconst KEY_LENGTH = 256;\nconst ITERATIONS = 600_000;\n\nexport async function deriveKey(\n password: string,\n salt: Uint8Array,\n): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveKey'],\n );\n\n return crypto.subtle.deriveKey(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n { name: 'AES-GCM', length: KEY_LENGTH },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\nexport async function encrypt(\n plaintext: string,\n password: string,\n): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));\n const key = await deriveKey(password, salt);\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(plaintext),\n );\n\n const combined = new Uint8Array(\n salt.length + iv.length + new Uint8Array(ciphertext).length,\n );\n combined.set(salt, 0);\n combined.set(iv, salt.length);\n combined.set(new Uint8Array(ciphertext), salt.length + iv.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function decrypt(\n encrypted: string,\n password: string,\n): Promise<string> {\n const combined = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));\n\n const salt = combined.slice(0, SALT_LENGTH);\n const iv = combined.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);\n const ciphertext = combined.slice(SALT_LENGTH + IV_LENGTH);\n\n const key = await deriveKey(password, salt);\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n ciphertext,\n );\n\n return new TextDecoder().decode(plaintext);\n}\n\nasync function deriveRawHash(\n password: string,\n salt: Uint8Array,\n): Promise<Uint8Array> {\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(password),\n 'PBKDF2',\n false,\n ['deriveBits'],\n );\n\n const bits = await crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt: salt as BufferSource, iterations: ITERATIONS, hash: 'SHA-256' },\n keyMaterial,\n KEY_LENGTH,\n );\n\n return new Uint8Array(bits);\n}\n\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));\n const hash = await deriveRawHash(password, salt);\n\n const combined = new Uint8Array(salt.length + hash.length);\n combined.set(salt, 0);\n combined.set(hash, salt.length);\n\n return btoa(String.fromCharCode(...combined));\n}\n\nexport async function verifyPassword(\n password: string,\n storedHash: string,\n): Promise<boolean> {\n const combined = Uint8Array.from(atob(storedHash), (c) => c.charCodeAt(0));\n const salt = combined.slice(0, SALT_LENGTH);\n const originalHash = combined.slice(SALT_LENGTH);\n\n const newHash = await deriveRawHash(password, salt);\n\n if (originalHash.length !== newHash.length) return false;\n let result = 0;\n for (let i = 0; i < originalHash.length; i++) {\n result |= originalHash[i] ^ newHash[i];\n }\n return result === 0;\n}\n\nexport function maskKey(key: string): string {\n if (key.length <= 8) return '****';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n}\n","import { ByokyErrorCode } from './types.js';\n\nexport class ByokyError extends Error {\n constructor(\n public readonly code: ByokyErrorCode,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message);\n this.name = 'ByokyError';\n }\n\n static walletNotInstalled() {\n return new ByokyError(\n ByokyErrorCode.WALLET_NOT_INSTALLED,\n 'byoky wallet extension is not installed',\n );\n }\n\n static userRejected() {\n return new ByokyError(\n ByokyErrorCode.USER_REJECTED,\n 'User rejected the connection request',\n );\n }\n\n static providerUnavailable(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.PROVIDER_UNAVAILABLE,\n `Provider \"${providerId}\" is not available`,\n { providerId },\n );\n }\n\n static sessionExpired() {\n return new ByokyError(\n ByokyErrorCode.SESSION_EXPIRED,\n 'Session has expired — please reconnect',\n );\n }\n\n static rateLimited(retryAfter?: number) {\n return new ByokyError(\n ByokyErrorCode.RATE_LIMITED,\n `Rate limit exceeded${retryAfter ? ` — retry after ${retryAfter}s` : ''}`,\n { retryAfter },\n );\n }\n\n static quotaExceeded(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.QUOTA_EXCEEDED,\n `Quota exceeded for ${providerId} — check your billing`,\n { providerId },\n );\n }\n\n static invalidKey(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.INVALID_KEY,\n `Invalid API key for ${providerId}`,\n { providerId },\n );\n }\n\n static tokenExpired(providerId: string) {\n return new ByokyError(\n ByokyErrorCode.TOKEN_EXPIRED,\n `OAuth token expired for ${providerId} — re-authentication required`,\n { providerId },\n );\n }\n\n static relayConnectionFailed(reason?: string) {\n return new ByokyError(\n ByokyErrorCode.RELAY_CONNECTION_FAILED,\n `Relay connection failed${reason ? `: ${reason}` : ''}`,\n );\n }\n\n static relayDisconnected() {\n return new ByokyError(\n ByokyErrorCode.RELAY_DISCONNECTED,\n 'Relay connection was closed',\n );\n }\n}\n","import type {\n ByokyMessage,\n ConnectRequest,\n ConnectResponse,\n MessageType,\n} from './types.js';\n\nexport const BYOKY_PROVIDER_KEY = '__byoky__';\nexport const BYOKY_MESSAGE_PREFIX = 'BYOKY_';\n\nexport function createMessage(\n type: MessageType,\n payload: unknown,\n requestId?: string,\n): ByokyMessage {\n return {\n type,\n id: crypto.randomUUID(),\n requestId,\n payload,\n };\n}\n\nexport function isByokyMessage(data: unknown): data is ByokyMessage {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'type' in data &&\n typeof (data as ByokyMessage).type === 'string' &&\n (data as ByokyMessage).type.startsWith(BYOKY_MESSAGE_PREFIX)\n );\n}\n\nexport function createConnectRequest(request: ConnectRequest): ByokyMessage {\n return createMessage('BYOKY_CONNECT_REQUEST', request);\n}\n\nexport function createConnectResponse(\n response: ConnectResponse,\n requestId: string,\n): ByokyMessage {\n return createMessage('BYOKY_CONNECT_RESPONSE', response, requestId);\n}\n\nexport function createErrorMessage(\n code: string,\n message: string,\n requestId?: string,\n): ByokyMessage {\n return createMessage('BYOKY_ERROR', { code, message }, requestId);\n}\n","import type { ProviderConfig } from './types.js';\n\nexport const PROVIDERS: Record<string, ProviderConfig> = {\n anthropic: {\n id: 'anthropic',\n name: 'Anthropic',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api.anthropic.com',\n },\n openai: {\n id: 'openai',\n name: 'OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.openai.com',\n },\n gemini: {\n id: 'gemini',\n name: 'Google Gemini',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://generativelanguage.googleapis.com',\n oauthConfig: {\n clientId: '699663966637-gr4d994198r4g6jvip25ffg85kree6ck.apps.googleusercontent.com',\n authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n scopes: ['https://www.googleapis.com/auth/generative-language'],\n extraAuthParams: { access_type: 'offline', prompt: 'consent' },\n },\n },\n mistral: {\n id: 'mistral',\n name: 'Mistral',\n authMethods: ['api_key'],\n baseUrl: 'https://api.mistral.ai',\n },\n cohere: {\n id: 'cohere',\n name: 'Cohere',\n authMethods: ['api_key'],\n baseUrl: 'https://api.cohere.com',\n },\n xai: {\n id: 'xai',\n name: 'xAI (Grok)',\n authMethods: ['api_key'],\n baseUrl: 'https://api.x.ai',\n },\n deepseek: {\n id: 'deepseek',\n name: 'DeepSeek',\n authMethods: ['api_key'],\n baseUrl: 'https://api.deepseek.com',\n },\n perplexity: {\n id: 'perplexity',\n name: 'Perplexity',\n authMethods: ['api_key'],\n baseUrl: 'https://api.perplexity.ai',\n },\n groq: {\n id: 'groq',\n name: 'Groq',\n authMethods: ['api_key'],\n baseUrl: 'https://api.groq.com',\n },\n together: {\n id: 'together',\n name: 'Together AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.together.xyz',\n },\n fireworks: {\n id: 'fireworks',\n name: 'Fireworks AI',\n authMethods: ['api_key'],\n baseUrl: 'https://api.fireworks.ai',\n },\n replicate: {\n id: 'replicate',\n name: 'Replicate',\n authMethods: ['api_key'],\n baseUrl: 'https://api.replicate.com',\n },\n openrouter: {\n id: 'openrouter',\n name: 'OpenRouter',\n authMethods: ['api_key'],\n baseUrl: 'https://openrouter.ai/api',\n },\n huggingface: {\n id: 'huggingface',\n name: 'Hugging Face',\n authMethods: ['api_key', 'oauth'],\n baseUrl: 'https://api-inference.huggingface.co',\n oauthConfig: {\n clientId: '031aeb11-725b-498a-93f9-d3599d84f57c',\n authorizationUrl: 'https://huggingface.co/oauth/authorize',\n tokenUrl: 'https://huggingface.co/oauth/token',\n scopes: ['inference-api'],\n },\n },\n azure_openai: {\n id: 'azure_openai',\n name: 'Azure OpenAI',\n authMethods: ['api_key'],\n baseUrl: 'https://YOUR_RESOURCE.openai.azure.com',\n },\n};\n\nexport function getProvider(id: string): ProviderConfig | undefined {\n return PROVIDERS[id];\n}\n\nexport function getProviderIds(): string[] {\n return Object.keys(PROVIDERS);\n}\n","import type { AuthMethod } from './types.js';\n\n/** Minimal WebSocket interface — compatible with browser WebSocket and `ws` library. */\nexport interface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code: number; reason: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport const WS_READY_STATE = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n// --- Relay message types ---\n\nexport interface RelayHello {\n type: 'relay:hello';\n sessionId: string;\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>;\n}\n\nexport interface RelayRequest {\n type: 'relay:request';\n requestId: string;\n providerId: string;\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string;\n}\n\nexport interface RelayResponseMeta {\n type: 'relay:response:meta';\n requestId: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport interface RelayResponseChunk {\n type: 'relay:response:chunk';\n requestId: string;\n chunk: string;\n}\n\nexport interface RelayResponseDone {\n type: 'relay:response:done';\n requestId: string;\n}\n\nexport interface RelayResponseError {\n type: 'relay:response:error';\n requestId: string;\n error: { code: string; message: string };\n}\n\nexport interface RelayPing {\n type: 'relay:ping';\n ts: number;\n}\n\nexport interface RelayPong {\n type: 'relay:pong';\n ts: number;\n}\n\nexport type RelayMessage =\n | RelayHello\n | RelayRequest\n | RelayResponseMeta\n | RelayResponseChunk\n | RelayResponseDone\n | RelayResponseError\n | RelayPing\n | RelayPong;\n\nexport function parseRelayMessage(data: unknown): RelayMessage | null {\n try {\n const raw = typeof data === 'string' ? JSON.parse(data) : data;\n if (!raw || typeof raw !== 'object' || typeof raw.type !== 'string' || !raw.type.startsWith('relay:')) {\n return null;\n }\n\n // Validate required fields per message type\n switch (raw.type) {\n case 'relay:hello':\n if (typeof raw.sessionId !== 'string') return null;\n break;\n case 'relay:request':\n if (typeof raw.requestId !== 'string' || typeof raw.providerId !== 'string' ||\n typeof raw.url !== 'string' || typeof raw.method !== 'string') return null;\n break;\n case 'relay:response:meta':\n if (typeof raw.requestId !== 'string' || typeof raw.status !== 'number') return null;\n break;\n case 'relay:response:chunk':\n if (typeof raw.requestId !== 'string' || typeof raw.chunk !== 'string') return null;\n break;\n case 'relay:response:done':\n case 'relay:response:error':\n if (typeof raw.requestId !== 'string') return null;\n break;\n case 'relay:ping':\n case 'relay:pong':\n if (typeof raw.ts !== 'number') return null;\n break;\n default:\n return null;\n }\n\n return raw as RelayMessage;\n } catch {\n return null;\n }\n}\n\nexport function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n}\n","export interface PasswordStrength {\n score: 0 | 1 | 2 | 3 | 4;\n label: 'Too weak' | 'Weak' | 'Fair' | 'Strong' | 'Very strong';\n feedback: string[];\n}\n\nconst COMMON_PASSWORDS = new Set([\n 'password', '12345678', 'qwerty12', 'letmein12', 'welcome1',\n 'monkey12', 'dragon12', 'master12', 'abc12345', 'password1',\n 'password12', 'iloveyou1', 'sunshine1', 'trustno1', 'princess1',\n 'football1', 'shadow123', 'michael1', 'jordan123', 'superman1',\n]);\n\nexport function checkPasswordStrength(password: string): PasswordStrength {\n const feedback: string[] = [];\n let score = 0;\n\n if (password.length < 12) {\n feedback.push('Use at least 12 characters');\n if (password.length < 8) {\n return { score: 0, label: 'Too weak', feedback };\n }\n } else {\n score++;\n if (password.length >= 16) score++;\n }\n\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n feedback.push('This is a commonly used password');\n return { score: 0, label: 'Too weak', feedback };\n }\n\n // Check character diversity\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasDigit = /\\d/.test(password);\n const hasSymbol = /[^a-zA-Z0-9]/.test(password);\n const charTypes = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;\n\n if (charTypes < 2) {\n feedback.push('Mix uppercase, lowercase, numbers, and symbols');\n } else if (charTypes >= 3) {\n score++;\n }\n if (charTypes >= 4) {\n score++;\n }\n\n // Check for repeated characters (e.g. aaaaaa)\n if (/(.)\\1{3,}/.test(password)) {\n feedback.push('Avoid repeated characters');\n score = Math.max(0, score - 1);\n }\n\n // Check for sequential patterns (e.g. 123456, abcdef)\n if (/(?:012|123|234|345|456|567|678|789|abc|bcd|cde|def)/i.test(password)) {\n feedback.push('Avoid sequential patterns');\n score = Math.max(0, score - 1);\n }\n\n const capped = Math.min(4, Math.max(0, score)) as 0 | 1 | 2 | 3 | 4;\n const labels: Record<number, PasswordStrength['label']> = {\n 0: 'Too weak',\n 1: 'Weak',\n 2: 'Fair',\n 3: 'Strong',\n 4: 'Very strong',\n };\n\n if (feedback.length === 0 && capped < 3) {\n feedback.push('Add more character variety or length');\n }\n\n return { score: capped, label: labels[capped], feedback };\n}\n\nexport const MIN_PASSWORD_LENGTH = 12;\n","import type { RequestLogEntry, TokenAllowance } from './types.js';\nimport { PROVIDERS } from './providers.js';\n\n/**\n * Validate that a proxy request URL targets the registered provider's base URL.\n * Prevents API key exfiltration by rejecting requests to arbitrary domains.\n */\nexport function validateProxyUrl(providerId: string, url: string): boolean {\n const provider = PROVIDERS[providerId];\n if (!provider) return false;\n try {\n const target = new URL(url);\n if (target.protocol !== 'https:') return false;\n const base = new URL(provider.baseUrl);\n return target.origin === base.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the real auth headers for a provider API request.\n * Strips any fake session-key headers and injects the real API key.\n */\nexport function buildHeaders(\n providerId: string,\n requestHeaders: Record<string, string>,\n apiKey: string,\n authMethod: string = 'api_key',\n): Record<string, string> {\n // Normalize header keys to lowercase to prevent case-sensitive bypass\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(requestHeaders)) {\n headers[key.toLowerCase()] = value;\n }\n\n // Remove any auth headers the SDK might have set (they're fake session keys)\n delete headers['authorization'];\n delete headers['x-api-key'];\n delete headers['api-key'];\n\n // Strip browser/SDK headers that can trigger rejection from provider APIs\n delete headers['origin'];\n delete headers['referer'];\n // Remove SDK telemetry headers that leak the real client environment\n for (const key of Object.keys(headers)) {\n if (key.startsWith('x-stainless-')) delete headers[key];\n }\n delete headers['sec-fetch-mode'];\n delete headers['accept-language'];\n delete headers['accept-encoding'];\n // Always strip content-length — fetch() recalculates it from the actual body,\n // and the body may have been modified (e.g. system prompt injection)\n delete headers['content-length'];\n\n if (providerId === 'anthropic') {\n if (authMethod === 'oauth') {\n headers['authorization'] = `Bearer ${apiKey}`;\n headers['user-agent'] = 'claude-cli/2.1.76';\n headers['x-app'] = 'cli';\n headers['accept'] = 'application/json';\n headers['anthropic-beta'] = 'claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14';\n headers['anthropic-dangerous-direct-browser-access'] = 'true';\n } else {\n headers['x-api-key'] = apiKey;\n }\n headers['anthropic-version'] = headers['anthropic-version'] ?? '2023-06-01';\n } else if (providerId === 'azure_openai') {\n headers['api-key'] = apiKey;\n } else {\n headers['authorization'] = `Bearer ${apiKey}`;\n }\n\n return headers;\n}\n\n/**\n * Parse the model name from a request body (JSON).\n */\nexport function parseModel(body?: string): string | undefined {\n if (!body) return undefined;\n try {\n const parsed = JSON.parse(body);\n return parsed.model ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse token usage from a provider API response body.\n * Handles both regular JSON responses and SSE streaming responses.\n */\nexport function parseUsage(\n providerId: string,\n body: string,\n): { inputTokens: number; outputTokens: number } | undefined {\n try {\n // For streaming responses (SSE), try to find usage in the last data chunk\n if (body.includes('data: ')) {\n const lines = body.split('\\n').filter((l) => l.startsWith('data: ') && !l.includes('[DONE]'));\n // Anthropic streaming: message_stop event has usage in a preceding message_delta\n // OpenAI streaming: last chunk may include usage\n for (let i = lines.length - 1; i >= 0; i--) {\n const json = lines[i].replace('data: ', '');\n try {\n const parsed = JSON.parse(json);\n const usage = extractUsageFromParsed(providerId, parsed);\n if (usage) return usage;\n } catch {\n continue;\n }\n }\n return undefined;\n }\n\n const parsed = JSON.parse(body);\n return extractUsageFromParsed(providerId, parsed);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Extract token usage from a parsed provider response object.\n */\nexport function extractUsageFromParsed(\n providerId: string,\n parsed: Record<string, unknown>,\n): { inputTokens: number; outputTokens: number } | undefined {\n // Anthropic: { usage: { input_tokens, output_tokens } }\n if (providerId === 'anthropic') {\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };\n }\n }\n\n // Gemini: { usageMetadata: { promptTokenCount, candidatesTokenCount } }\n if (providerId === 'gemini') {\n const meta = parsed.usageMetadata as Record<string, number> | undefined;\n if (meta?.promptTokenCount != null) {\n return {\n inputTokens: meta.promptTokenCount,\n outputTokens: meta.candidatesTokenCount ?? 0,\n };\n }\n }\n\n // OpenAI-compatible (openai, groq, together, deepseek, xai, perplexity, fireworks, openrouter, mistral, azure_openai):\n // { usage: { prompt_tokens, completion_tokens } }\n const usage = parsed.usage as Record<string, number> | undefined;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n return { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens };\n }\n\n return undefined;\n}\n\n/**\n * Check whether a request is allowed given token allowances and usage history.\n * Pure computation — no storage access.\n */\nexport function computeAllowanceCheck(\n allowance: TokenAllowance | undefined,\n entries: Pick<RequestLogEntry, 'providerId' | 'inputTokens' | 'outputTokens'>[],\n providerId: string,\n): { allowed: boolean; reason?: string } {\n if (!allowance) return { allowed: true };\n\n let totalUsed = 0;\n const byProvider: Record<string, number> = {};\n for (const entry of entries) {\n const tokens = (entry.inputTokens ?? 0) + (entry.outputTokens ?? 0);\n totalUsed += tokens;\n byProvider[entry.providerId] = (byProvider[entry.providerId] ?? 0) + tokens;\n }\n\n if (allowance.totalLimit != null && totalUsed >= allowance.totalLimit) {\n return { allowed: false, reason: `Token allowance exceeded for ${allowance.origin}` };\n }\n\n const providerLimit = allowance.providerLimits?.[providerId];\n if (providerLimit != null && (byProvider[providerId] ?? 0) >= providerLimit) {\n return { allowed: false, reason: `Token allowance for ${providerId} exceeded` };\n }\n\n return { allowed: true };\n}\n","import { PROVIDERS } from './providers.js';\n\n// --- Gift (sender side) ---\n\nexport interface Gift {\n id: string;\n credentialId: string;\n providerId: string;\n label: string;\n authToken: string;\n maxTokens: number;\n usedTokens: number;\n expiresAt: number;\n createdAt: number;\n active: boolean;\n relayUrl: string;\n}\n\n// --- Gift link (shareable payload) ---\n\nexport interface GiftLink {\n v: 1;\n id: string;\n p: string; // providerId\n n: string; // provider display name\n s: string; // sender label\n t: string; // auth token\n m: number; // max tokens\n e: number; // expires at (unix ms)\n r: string; // relay URL\n}\n\n// --- Gifted credential (recipient side) ---\n\nexport interface GiftedCredential {\n id: string;\n giftId: string;\n providerId: string;\n providerName: string;\n senderLabel: string;\n authToken: string;\n maxTokens: number;\n usedTokens: number;\n expiresAt: number;\n relayUrl: string;\n createdAt: number;\n}\n\n// --- Gift relay protocol ---\n\nexport interface GiftRelayAuth {\n type: 'gift:auth';\n giftId: string;\n authToken: string;\n role: 'sender' | 'recipient';\n}\n\nexport interface GiftRelayAuthResult {\n type: 'gift:auth:result';\n success: boolean;\n error?: string;\n peerOnline?: boolean;\n}\n\nexport interface GiftRelayPeerStatus {\n type: 'gift:peer:status';\n online: boolean;\n}\n\nexport interface GiftRelayUsageUpdate {\n type: 'gift:usage';\n giftId: string;\n usedTokens: number;\n}\n\nexport type GiftRelayMessage =\n | GiftRelayAuth\n | GiftRelayAuthResult\n | GiftRelayPeerStatus\n | GiftRelayUsageUpdate;\n\n// --- Encoding / decoding ---\n\nexport function encodeGiftLink(link: GiftLink): string {\n const json = JSON.stringify(link);\n const bytes = new TextEncoder().encode(json);\n return base64UrlEncode(bytes);\n}\n\nexport function decodeGiftLink(encoded: string): GiftLink | null {\n try {\n const clean = encoded.replace(/^byoky:\\/\\/gift\\//, '');\n const bytes = base64UrlDecode(clean);\n const json = new TextDecoder().decode(bytes);\n const parsed = JSON.parse(json);\n if (parsed.v !== 1) return null;\n return parsed as GiftLink;\n } catch {\n return null;\n }\n}\n\nexport function giftLinkToUrl(encoded: string): string {\n return `byoky://gift/${encoded}`;\n}\n\n// --- Validation ---\n\nexport function validateGiftLink(link: GiftLink): { valid: boolean; reason?: string } {\n if (link.v !== 1) return { valid: false, reason: 'Unsupported gift version' };\n if (!link.id || typeof link.id !== 'string') return { valid: false, reason: 'Missing gift ID' };\n if (!link.p || typeof link.p !== 'string') return { valid: false, reason: 'Missing provider' };\n if (!link.t || typeof link.t !== 'string') return { valid: false, reason: 'Missing auth token' };\n if (!link.r || typeof link.r !== 'string') return { valid: false, reason: 'Missing relay URL' };\n if (typeof link.m !== 'number' || link.m <= 0) return { valid: false, reason: 'Invalid token budget' };\n if (typeof link.e !== 'number' || link.e <= Date.now()) return { valid: false, reason: 'Gift has expired' };\n\n try {\n const url = new URL(link.r);\n if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {\n return { valid: false, reason: 'Relay URL must use ws:// or wss://' };\n }\n } catch {\n return { valid: false, reason: 'Invalid relay URL' };\n }\n\n return { valid: true };\n}\n\nexport function isGiftExpired(gift: { expiresAt: number }): boolean {\n return gift.expiresAt <= Date.now();\n}\n\nexport function isGiftBudgetExhausted(gift: { usedTokens: number; maxTokens: number }): boolean {\n return gift.usedTokens >= gift.maxTokens;\n}\n\nexport function giftBudgetRemaining(gift: { usedTokens: number; maxTokens: number }): number {\n return Math.max(0, gift.maxTokens - gift.usedTokens);\n}\n\nexport function giftBudgetPercent(gift: { usedTokens: number; maxTokens: number }): number {\n if (gift.maxTokens === 0) return 100;\n return Math.min(100, Math.round((gift.usedTokens / gift.maxTokens) * 100));\n}\n\nexport function createGiftLink(gift: Gift): { encoded: string; link: GiftLink } {\n const provider = PROVIDERS[gift.providerId];\n const link: GiftLink = {\n v: 1,\n id: gift.id,\n p: gift.providerId,\n n: provider?.name ?? gift.providerId,\n s: gift.label,\n t: gift.authToken,\n m: gift.maxTokens,\n e: gift.expiresAt,\n r: gift.relayUrl,\n };\n return { encoded: encodeGiftLink(link), link };\n}\n\n// --- Base64url helpers ---\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction base64UrlDecode(str: string): Uint8Array {\n const padded = str.replace(/-/g, '+').replace(/_/g, '/');\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n"],"mappings":"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","names":["ByokyErrorCode","parsed","usage"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byoky/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -12,12 +12,11 @@
12
12
  "require": "./dist/index.cjs"
13
13
  }
14
14
  },
15
- "files": ["dist", "README.md", "LICENSE"],
16
- "scripts": {
17
- "build": "tsup",
18
- "typecheck": "tsc --noEmit",
19
- "clean": "rm -rf dist"
20
- },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
21
20
  "license": "MIT",
22
21
  "repository": {
23
22
  "type": "git",
@@ -25,8 +24,20 @@
25
24
  "directory": "packages/core"
26
25
  },
27
26
  "homepage": "https://byoky.com",
28
- "keywords": ["byoky", "ai", "api-keys", "wallet", "encryption", "llm"],
27
+ "keywords": [
28
+ "byoky",
29
+ "ai",
30
+ "api-keys",
31
+ "wallet",
32
+ "encryption",
33
+ "llm"
34
+ ],
29
35
  "devDependencies": {
30
36
  "typescript": "^5.7.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "typecheck": "tsc --noEmit",
41
+ "clean": "rm -rf dist"
31
42
  }
32
- }
43
+ }