@byoky/core 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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[];
@@ -155,6 +158,7 @@ interface PendingApproval {
155
158
  interface TrustedSite {
156
159
  origin: string;
157
160
  trustedAt: number;
161
+ allowedProviders?: string[];
158
162
  }
159
163
  interface TokenAllowance {
160
164
  origin: string;
@@ -267,7 +271,17 @@ interface RelayPong {
267
271
  type: 'relay:pong';
268
272
  ts: number;
269
273
  }
270
- type RelayMessage = RelayHello | RelayRequest | RelayResponseMeta | RelayResponseChunk | RelayResponseDone | RelayResponseError | RelayPing | RelayPong;
274
+ interface RelayPairHello {
275
+ type: 'relay:pair:hello';
276
+ providers: Record<string, {
277
+ available: boolean;
278
+ authMethod: AuthMethod;
279
+ }>;
280
+ }
281
+ interface RelayPairAck {
282
+ type: 'relay:pair:ack';
283
+ }
284
+ type RelayMessage = RelayHello | RelayRequest | RelayResponseMeta | RelayResponseChunk | RelayResponseDone | RelayResponseError | RelayPing | RelayPong | RelayPairHello | RelayPairAck;
271
285
  declare function parseRelayMessage(data: unknown): RelayMessage | null;
272
286
  declare function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void;
273
287
 
@@ -289,9 +303,6 @@ declare function validateProxyUrl(providerId: string, url: string): boolean;
289
303
  * Strips any fake session-key headers and injects the real API key.
290
304
  */
291
305
  declare function buildHeaders(providerId: string, requestHeaders: Record<string, string>, apiKey: string, authMethod?: string): Record<string, string>;
292
- /**
293
- * Parse the model name from a request body (JSON).
294
- */
295
306
  declare function parseModel(body?: string): string | undefined;
296
307
  /**
297
308
  * Parse token usage from a provider API response body.
@@ -317,4 +328,99 @@ declare function computeAllowanceCheck(allowance: TokenAllowance | undefined, en
317
328
  reason?: string;
318
329
  };
319
330
 
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 };
331
+ interface Gift {
332
+ id: string;
333
+ credentialId: string;
334
+ providerId: string;
335
+ label: string;
336
+ authToken: string;
337
+ maxTokens: number;
338
+ usedTokens: number;
339
+ expiresAt: number;
340
+ createdAt: number;
341
+ active: boolean;
342
+ relayUrl: string;
343
+ }
344
+ interface GiftLink {
345
+ v: 1;
346
+ id: string;
347
+ p: string;
348
+ n: string;
349
+ s: string;
350
+ t: string;
351
+ m: number;
352
+ e: number;
353
+ r: string;
354
+ }
355
+ interface GiftedCredential {
356
+ id: string;
357
+ giftId: string;
358
+ providerId: string;
359
+ providerName: string;
360
+ senderLabel: string;
361
+ authToken: string;
362
+ maxTokens: number;
363
+ usedTokens: number;
364
+ expiresAt: number;
365
+ relayUrl: string;
366
+ createdAt: number;
367
+ }
368
+ interface RelayAuth {
369
+ type: 'relay:auth';
370
+ roomId: string;
371
+ authToken: string;
372
+ role: 'sender' | 'recipient';
373
+ }
374
+ interface RelayAuthResult {
375
+ type: 'relay:auth:result';
376
+ success: boolean;
377
+ error?: string;
378
+ peerOnline?: boolean;
379
+ }
380
+ interface RelayPeerStatus {
381
+ type: 'relay:peer:status';
382
+ online: boolean;
383
+ }
384
+ interface RelayUsageUpdate {
385
+ type: 'relay:usage';
386
+ roomId: string;
387
+ usedTokens: number;
388
+ }
389
+ type RelayProtocolMessage = RelayAuth | RelayAuthResult | RelayPeerStatus | RelayUsageUpdate;
390
+ declare function encodeGiftLink(link: GiftLink): string;
391
+ declare function decodeGiftLink(encoded: string): GiftLink | null;
392
+ declare function giftLinkToUrl(encoded: string): string;
393
+ declare function validateGiftLink(link: GiftLink): {
394
+ valid: boolean;
395
+ reason?: string;
396
+ };
397
+ declare function isGiftExpired(gift: {
398
+ expiresAt: number;
399
+ }): boolean;
400
+ declare function isGiftBudgetExhausted(gift: {
401
+ usedTokens: number;
402
+ maxTokens: number;
403
+ }): boolean;
404
+ declare function giftBudgetRemaining(gift: {
405
+ usedTokens: number;
406
+ maxTokens: number;
407
+ }): number;
408
+ declare function giftBudgetPercent(gift: {
409
+ usedTokens: number;
410
+ maxTokens: number;
411
+ }): number;
412
+ declare function createGiftLink(gift: Gift): {
413
+ encoded: string;
414
+ link: GiftLink;
415
+ };
416
+ interface PairPayload {
417
+ v: 1;
418
+ r: string;
419
+ id: string;
420
+ t: string;
421
+ o: string;
422
+ }
423
+ declare function encodePairPayload(payload: PairPayload): string;
424
+ declare function decodePairPayload(encoded: string): PairPayload | null;
425
+
426
+ 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 GiftedCredential, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PairPayload, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayAuth, type RelayAuthResult, type RelayHello, type RelayMessage, type RelayPairAck, type RelayPairHello, type RelayPeerStatus, type RelayPing, type RelayPong, type RelayProtocolMessage, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RelayUsageUpdate, 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, decodePairPayload, decrypt, deriveKey, encodeGiftLink, encodePairPayload, 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[];
@@ -155,6 +158,7 @@ interface PendingApproval {
155
158
  interface TrustedSite {
156
159
  origin: string;
157
160
  trustedAt: number;
161
+ allowedProviders?: string[];
158
162
  }
159
163
  interface TokenAllowance {
160
164
  origin: string;
@@ -267,7 +271,17 @@ interface RelayPong {
267
271
  type: 'relay:pong';
268
272
  ts: number;
269
273
  }
270
- type RelayMessage = RelayHello | RelayRequest | RelayResponseMeta | RelayResponseChunk | RelayResponseDone | RelayResponseError | RelayPing | RelayPong;
274
+ interface RelayPairHello {
275
+ type: 'relay:pair:hello';
276
+ providers: Record<string, {
277
+ available: boolean;
278
+ authMethod: AuthMethod;
279
+ }>;
280
+ }
281
+ interface RelayPairAck {
282
+ type: 'relay:pair:ack';
283
+ }
284
+ type RelayMessage = RelayHello | RelayRequest | RelayResponseMeta | RelayResponseChunk | RelayResponseDone | RelayResponseError | RelayPing | RelayPong | RelayPairHello | RelayPairAck;
271
285
  declare function parseRelayMessage(data: unknown): RelayMessage | null;
272
286
  declare function sendRelayMessage(ws: WebSocketLike, msg: RelayMessage): void;
273
287
 
@@ -289,9 +303,6 @@ declare function validateProxyUrl(providerId: string, url: string): boolean;
289
303
  * Strips any fake session-key headers and injects the real API key.
290
304
  */
291
305
  declare function buildHeaders(providerId: string, requestHeaders: Record<string, string>, apiKey: string, authMethod?: string): Record<string, string>;
292
- /**
293
- * Parse the model name from a request body (JSON).
294
- */
295
306
  declare function parseModel(body?: string): string | undefined;
296
307
  /**
297
308
  * Parse token usage from a provider API response body.
@@ -317,4 +328,99 @@ declare function computeAllowanceCheck(allowance: TokenAllowance | undefined, en
317
328
  reason?: string;
318
329
  };
319
330
 
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 };
331
+ interface Gift {
332
+ id: string;
333
+ credentialId: string;
334
+ providerId: string;
335
+ label: string;
336
+ authToken: string;
337
+ maxTokens: number;
338
+ usedTokens: number;
339
+ expiresAt: number;
340
+ createdAt: number;
341
+ active: boolean;
342
+ relayUrl: string;
343
+ }
344
+ interface GiftLink {
345
+ v: 1;
346
+ id: string;
347
+ p: string;
348
+ n: string;
349
+ s: string;
350
+ t: string;
351
+ m: number;
352
+ e: number;
353
+ r: string;
354
+ }
355
+ interface GiftedCredential {
356
+ id: string;
357
+ giftId: string;
358
+ providerId: string;
359
+ providerName: string;
360
+ senderLabel: string;
361
+ authToken: string;
362
+ maxTokens: number;
363
+ usedTokens: number;
364
+ expiresAt: number;
365
+ relayUrl: string;
366
+ createdAt: number;
367
+ }
368
+ interface RelayAuth {
369
+ type: 'relay:auth';
370
+ roomId: string;
371
+ authToken: string;
372
+ role: 'sender' | 'recipient';
373
+ }
374
+ interface RelayAuthResult {
375
+ type: 'relay:auth:result';
376
+ success: boolean;
377
+ error?: string;
378
+ peerOnline?: boolean;
379
+ }
380
+ interface RelayPeerStatus {
381
+ type: 'relay:peer:status';
382
+ online: boolean;
383
+ }
384
+ interface RelayUsageUpdate {
385
+ type: 'relay:usage';
386
+ roomId: string;
387
+ usedTokens: number;
388
+ }
389
+ type RelayProtocolMessage = RelayAuth | RelayAuthResult | RelayPeerStatus | RelayUsageUpdate;
390
+ declare function encodeGiftLink(link: GiftLink): string;
391
+ declare function decodeGiftLink(encoded: string): GiftLink | null;
392
+ declare function giftLinkToUrl(encoded: string): string;
393
+ declare function validateGiftLink(link: GiftLink): {
394
+ valid: boolean;
395
+ reason?: string;
396
+ };
397
+ declare function isGiftExpired(gift: {
398
+ expiresAt: number;
399
+ }): boolean;
400
+ declare function isGiftBudgetExhausted(gift: {
401
+ usedTokens: number;
402
+ maxTokens: number;
403
+ }): boolean;
404
+ declare function giftBudgetRemaining(gift: {
405
+ usedTokens: number;
406
+ maxTokens: number;
407
+ }): number;
408
+ declare function giftBudgetPercent(gift: {
409
+ usedTokens: number;
410
+ maxTokens: number;
411
+ }): number;
412
+ declare function createGiftLink(gift: Gift): {
413
+ encoded: string;
414
+ link: GiftLink;
415
+ };
416
+ interface PairPayload {
417
+ v: 1;
418
+ r: string;
419
+ id: string;
420
+ t: string;
421
+ o: string;
422
+ }
423
+ declare function encodePairPayload(payload: PairPayload): string;
424
+ declare function decodePairPayload(encoded: string): PairPayload | null;
425
+
426
+ 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 GiftedCredential, MIN_PASSWORD_LENGTH, type MessageType, type OAuthConfig, type OAuthCredential, PROVIDERS, type PairPayload, type PasswordStrength, type PendingApproval, type ProviderConfig, type ProviderId, type ProviderRequirement, type ProxyRequest, type ProxyResponseChunk, type ProxyResponseError, type ProxyResponseMeta, type RelayAuth, type RelayAuthResult, type RelayHello, type RelayMessage, type RelayPairAck, type RelayPairHello, type RelayPeerStatus, type RelayPing, type RelayPong, type RelayProtocolMessage, type RelayRequest, type RelayResponseChunk, type RelayResponseDone, type RelayResponseError, type RelayResponseMeta, type RelayUsageUpdate, 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, decodePairPayload, decrypt, deriveKey, encodeGiftLink, encodePairPayload, encrypt, extractUsageFromParsed, getProvider, getProviderIds, giftBudgetPercent, giftBudgetRemaining, giftLinkToUrl, hashPassword, isByokyMessage, isGiftBudgetExhausted, isGiftExpired, maskKey, parseModel, parseRelayMessage, parseUsage, sendRelayMessage, validateGiftLink, validateProxyUrl, verifyPassword };
package/dist/index.js CHANGED
@@ -325,8 +325,10 @@ var WS_READY_STATE = {
325
325
  CLOSING: 2,
326
326
  CLOSED: 3
327
327
  };
328
+ var MAX_RELAY_MESSAGE_SIZE = 1048576;
328
329
  function parseRelayMessage(data) {
329
330
  try {
331
+ if (typeof data === "string" && data.length > MAX_RELAY_MESSAGE_SIZE) return null;
330
332
  const raw = typeof data === "string" ? JSON.parse(data) : data;
331
333
  if (!raw || typeof raw !== "object" || typeof raw.type !== "string" || !raw.type.startsWith("relay:")) {
332
334
  return null;
@@ -345,13 +347,21 @@ function parseRelayMessage(data) {
345
347
  if (typeof raw.requestId !== "string" || typeof raw.chunk !== "string") return null;
346
348
  break;
347
349
  case "relay:response:done":
350
+ if (typeof raw.requestId !== "string") return null;
351
+ break;
348
352
  case "relay:response:error":
349
353
  if (typeof raw.requestId !== "string") return null;
354
+ if (raw.error && (typeof raw.error !== "object" || typeof raw.error.code !== "string" || typeof raw.error.message !== "string")) return null;
350
355
  break;
351
356
  case "relay:ping":
352
357
  case "relay:pong":
353
358
  if (typeof raw.ts !== "number") return null;
354
359
  break;
360
+ case "relay:pair:hello":
361
+ if (!raw.providers || typeof raw.providers !== "object") return null;
362
+ break;
363
+ case "relay:pair:ack":
364
+ break;
355
365
  default:
356
366
  return null;
357
367
  }
@@ -387,16 +397,59 @@ var COMMON_PASSWORDS = /* @__PURE__ */ new Set([
387
397
  "shadow123",
388
398
  "michael1",
389
399
  "jordan123",
390
- "superman1"
400
+ "superman1",
401
+ "password123",
402
+ "admin12345",
403
+ "letmein123",
404
+ "p@ssw0rd",
405
+ "p@ssw0rd1",
406
+ "qwerty1234",
407
+ "changeme12",
408
+ "welcome123",
409
+ "1234567890",
410
+ "baseball1",
411
+ "starwars12",
412
+ "whatever1",
413
+ "passw0rd1",
414
+ "mustang12",
415
+ "access1234",
416
+ "charlie123",
417
+ "donald1234",
418
+ "maggie1234",
419
+ "master1234",
420
+ "michael123",
421
+ "jennifer1",
422
+ "hunter1234",
423
+ "thomas1234",
424
+ "corvette12",
425
+ "robert1234",
426
+ "summer1234",
427
+ "george1234",
428
+ "harley1234",
429
+ "cheese1234",
430
+ "computer1",
431
+ "internet1",
432
+ "secret1234",
433
+ "diamond1",
434
+ "chicken123",
435
+ "pepper1234",
436
+ "jessica123",
437
+ "hannah1234",
438
+ "ginger1234",
439
+ "joshua1234",
440
+ "abcdefgh1",
441
+ "qwertyuiop",
442
+ "asdfghjkl1",
443
+ "zxcvbnm123",
444
+ "1q2w3e4r5t",
445
+ "passpass1"
391
446
  ]);
392
447
  function checkPasswordStrength(password) {
393
448
  const feedback = [];
394
449
  let score = 0;
395
- if (password.length < 12) {
450
+ if (password.length < MIN_PASSWORD_LENGTH) {
396
451
  feedback.push("Use at least 12 characters");
397
- if (password.length < 8) {
398
- return { score: 0, label: "Too weak", feedback };
399
- }
452
+ return { score: 0, label: "Too weak", feedback };
400
453
  } else {
401
454
  score++;
402
455
  if (password.length >= 16) score++;
@@ -462,12 +515,23 @@ function buildHeaders(providerId, requestHeaders, apiKey, authMethod = "api_key"
462
515
  delete headers["authorization"];
463
516
  delete headers["x-api-key"];
464
517
  delete headers["api-key"];
518
+ delete headers["origin"];
519
+ delete headers["referer"];
520
+ for (const key of Object.keys(headers)) {
521
+ if (key.startsWith("x-stainless-")) delete headers[key];
522
+ }
523
+ delete headers["sec-fetch-mode"];
524
+ delete headers["accept-language"];
525
+ delete headers["accept-encoding"];
526
+ delete headers["content-length"];
465
527
  if (providerId === "anthropic") {
466
528
  if (authMethod === "oauth") {
467
529
  headers["authorization"] = `Bearer ${apiKey}`;
468
- headers["user-agent"] = "claude-cli/2.1.75";
530
+ headers["user-agent"] = "claude-cli/2.1.76";
469
531
  headers["x-app"] = "cli";
470
- headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20";
532
+ headers["accept"] = "application/json";
533
+ headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
534
+ headers["anthropic-dangerous-direct-browser-access"] = "true";
471
535
  } else {
472
536
  headers["x-api-key"] = apiKey;
473
537
  }
@@ -479,8 +543,9 @@ function buildHeaders(providerId, requestHeaders, apiKey, authMethod = "api_key"
479
543
  }
480
544
  return headers;
481
545
  }
546
+ var MAX_BODY_PARSE_SIZE = 10485760;
482
547
  function parseModel(body) {
483
- if (!body) return void 0;
548
+ if (!body || body.length > MAX_BODY_PARSE_SIZE) return void 0;
484
549
  try {
485
550
  const parsed = JSON.parse(body);
486
551
  return parsed.model ?? void 0;
@@ -510,25 +575,28 @@ function parseUsage(providerId, body) {
510
575
  return void 0;
511
576
  }
512
577
  }
578
+ function sanitizeTokenCounts(input, output) {
579
+ const i = Math.max(0, Math.floor(input));
580
+ const o = Math.max(0, Math.floor(output));
581
+ if (!Number.isFinite(i) || !Number.isFinite(o)) return void 0;
582
+ return { inputTokens: i, outputTokens: o };
583
+ }
513
584
  function extractUsageFromParsed(providerId, parsed) {
514
585
  if (providerId === "anthropic") {
515
586
  const usage2 = parsed.usage;
516
587
  if (usage2?.input_tokens != null && usage2?.output_tokens != null) {
517
- return { inputTokens: usage2.input_tokens, outputTokens: usage2.output_tokens };
588
+ return sanitizeTokenCounts(usage2.input_tokens, usage2.output_tokens);
518
589
  }
519
590
  }
520
591
  if (providerId === "gemini") {
521
592
  const meta = parsed.usageMetadata;
522
593
  if (meta?.promptTokenCount != null) {
523
- return {
524
- inputTokens: meta.promptTokenCount,
525
- outputTokens: meta.candidatesTokenCount ?? 0
526
- };
594
+ return sanitizeTokenCounts(meta.promptTokenCount, meta.candidatesTokenCount ?? 0);
527
595
  }
528
596
  }
529
597
  const usage = parsed.usage;
530
598
  if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {
531
- return { inputTokens: usage.prompt_tokens, outputTokens: usage.completion_tokens };
599
+ return sanitizeTokenCounts(usage.prompt_tokens, usage.completion_tokens);
532
600
  }
533
601
  return void 0;
534
602
  }
@@ -550,6 +618,105 @@ function computeAllowanceCheck(allowance, entries, providerId) {
550
618
  }
551
619
  return { allowed: true };
552
620
  }
621
+
622
+ // src/gift.ts
623
+ function encodeGiftLink(link) {
624
+ const json = JSON.stringify(link);
625
+ const bytes = new TextEncoder().encode(json);
626
+ return base64UrlEncode(bytes);
627
+ }
628
+ var MAX_GIFT_LINK_SIZE = 8192;
629
+ function decodeGiftLink(encoded) {
630
+ try {
631
+ if (encoded.length > MAX_GIFT_LINK_SIZE) return null;
632
+ const clean = encoded.replace(/^byoky:\/\/gift\//, "");
633
+ const bytes = base64UrlDecode(clean);
634
+ const json = new TextDecoder().decode(bytes);
635
+ const parsed = JSON.parse(json);
636
+ if (parsed.v !== 1) return null;
637
+ return parsed;
638
+ } catch {
639
+ return null;
640
+ }
641
+ }
642
+ function giftLinkToUrl(encoded) {
643
+ return `byoky://gift/${encoded}`;
644
+ }
645
+ function validateGiftLink(link) {
646
+ if (link.v !== 1) return { valid: false, reason: "Unsupported gift version" };
647
+ if (!link.id || typeof link.id !== "string") return { valid: false, reason: "Missing gift ID" };
648
+ if (!link.p || typeof link.p !== "string") return { valid: false, reason: "Missing provider" };
649
+ if (!link.t || typeof link.t !== "string") return { valid: false, reason: "Missing auth token" };
650
+ if (!link.r || typeof link.r !== "string") return { valid: false, reason: "Missing relay URL" };
651
+ if (typeof link.m !== "number" || link.m <= 0) return { valid: false, reason: "Invalid token budget" };
652
+ if (typeof link.e !== "number" || link.e <= Date.now()) return { valid: false, reason: "Gift has expired" };
653
+ try {
654
+ const url = new URL(link.r);
655
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") {
656
+ return { valid: false, reason: "Relay URL must use ws:// or wss://" };
657
+ }
658
+ } catch {
659
+ return { valid: false, reason: "Invalid relay URL" };
660
+ }
661
+ return { valid: true };
662
+ }
663
+ function isGiftExpired(gift) {
664
+ return gift.expiresAt <= Date.now();
665
+ }
666
+ function isGiftBudgetExhausted(gift) {
667
+ return gift.usedTokens >= gift.maxTokens;
668
+ }
669
+ function giftBudgetRemaining(gift) {
670
+ return Math.max(0, gift.maxTokens - gift.usedTokens);
671
+ }
672
+ function giftBudgetPercent(gift) {
673
+ if (gift.maxTokens === 0) return 100;
674
+ return Math.min(100, Math.round(gift.usedTokens / gift.maxTokens * 100));
675
+ }
676
+ function createGiftLink(gift) {
677
+ const provider = PROVIDERS[gift.providerId];
678
+ const link = {
679
+ v: 1,
680
+ id: gift.id,
681
+ p: gift.providerId,
682
+ n: provider?.name ?? gift.providerId,
683
+ s: gift.label,
684
+ t: gift.authToken,
685
+ m: gift.maxTokens,
686
+ e: gift.expiresAt,
687
+ r: gift.relayUrl
688
+ };
689
+ return { encoded: encodeGiftLink(link), link };
690
+ }
691
+ function encodePairPayload(payload) {
692
+ const json = JSON.stringify(payload);
693
+ const bytes = new TextEncoder().encode(json);
694
+ return base64UrlEncode(bytes);
695
+ }
696
+ function decodePairPayload(encoded) {
697
+ try {
698
+ if (encoded.length > 2048) return null;
699
+ const bytes = base64UrlDecode(encoded);
700
+ const json = new TextDecoder().decode(bytes);
701
+ const parsed = JSON.parse(json);
702
+ if (parsed.v !== 1) return null;
703
+ return parsed;
704
+ } catch {
705
+ return null;
706
+ }
707
+ }
708
+ function base64UrlEncode(bytes) {
709
+ let binary = "";
710
+ for (const byte of bytes) binary += String.fromCharCode(byte);
711
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
712
+ }
713
+ function base64UrlDecode(str) {
714
+ const padded = str.replace(/-/g, "+").replace(/_/g, "/");
715
+ const binary = atob(padded);
716
+ const bytes = new Uint8Array(binary.length);
717
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
718
+ return bytes;
719
+ }
553
720
  export {
554
721
  BYOKY_MESSAGE_PREFIX,
555
722
  BYOKY_PROVIDER_KEY,
@@ -564,20 +731,31 @@ export {
564
731
  createConnectRequest,
565
732
  createConnectResponse,
566
733
  createErrorMessage,
734
+ createGiftLink,
567
735
  createMessage,
736
+ decodeGiftLink,
737
+ decodePairPayload,
568
738
  decrypt,
569
739
  deriveKey,
740
+ encodeGiftLink,
741
+ encodePairPayload,
570
742
  encrypt,
571
743
  extractUsageFromParsed,
572
744
  getProvider,
573
745
  getProviderIds,
746
+ giftBudgetPercent,
747
+ giftBudgetRemaining,
748
+ giftLinkToUrl,
574
749
  hashPassword,
575
750
  isByokyMessage,
751
+ isGiftBudgetExhausted,
752
+ isGiftExpired,
576
753
  maskKey,
577
754
  parseModel,
578
755
  parseRelayMessage,
579
756
  parseUsage,
580
757
  sendRelayMessage,
758
+ validateGiftLink,
581
759
  validateProxyUrl,
582
760
  verifyPassword
583
761
  };