@bastani/pi-ai 0.9.40 → 0.9.41-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +42 -2
  3. package/dist/api/anthropic-messages.d.ts.map +1 -1
  4. package/dist/api/anthropic-messages.js +7 -10
  5. package/dist/api/anthropic-messages.js.map +1 -1
  6. package/dist/api/azure-openai-responses.js +2 -2
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +12 -0
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/classifier-shared.d.ts.map +1 -1
  12. package/dist/api/classifier-shared.js +4 -3
  13. package/dist/api/classifier-shared.js.map +1 -1
  14. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  15. package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
  16. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  17. package/dist/api/google-generative-ai.js +4 -4
  18. package/dist/api/google-generative-ai.js.map +1 -1
  19. package/dist/api/google-vertex.d.ts.map +1 -1
  20. package/dist/api/google-vertex.js +9 -9
  21. package/dist/api/google-vertex.js.map +1 -1
  22. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  23. package/dist/api/llama-cpp-classify.js +2 -1
  24. package/dist/api/llama-cpp-classify.js.map +1 -1
  25. package/dist/api/mistral-conversations.js +2 -2
  26. package/dist/api/mistral-conversations.js.map +1 -1
  27. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  28. package/dist/api/openai-codex-responses.js +12 -9
  29. package/dist/api/openai-codex-responses.js.map +1 -1
  30. package/dist/api/openai-completions.d.ts.map +1 -1
  31. package/dist/api/openai-completions.js +4 -4
  32. package/dist/api/openai-completions.js.map +1 -1
  33. package/dist/api/openai-responses.d.ts.map +1 -1
  34. package/dist/api/openai-responses.js +15 -7
  35. package/dist/api/openai-responses.js.map +1 -1
  36. package/dist/api/openrouter-images.d.ts.map +1 -1
  37. package/dist/api/openrouter-images.js +4 -3
  38. package/dist/api/openrouter-images.js.map +1 -1
  39. package/dist/api/pi-messages.d.ts.map +1 -1
  40. package/dist/api/pi-messages.js +3 -3
  41. package/dist/api/pi-messages.js.map +1 -1
  42. package/dist/api/simple-options.d.ts.map +1 -1
  43. package/dist/api/simple-options.js +1 -0
  44. package/dist/api/simple-options.js.map +1 -1
  45. package/dist/api/system-one-shared.d.ts +20 -4
  46. package/dist/api/system-one-shared.d.ts.map +1 -1
  47. package/dist/api/system-one-shared.js +49 -19
  48. package/dist/api/system-one-shared.js.map +1 -1
  49. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  50. package/dist/api/typesafe-system-one.js +10 -3
  51. package/dist/api/typesafe-system-one.js.map +1 -1
  52. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  53. package/dist/auth/oauth/anthropic.js +15 -2
  54. package/dist/auth/oauth/anthropic.js.map +1 -1
  55. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  56. package/dist/auth/oauth/github-copilot.js +7 -2
  57. package/dist/auth/oauth/github-copilot.js.map +1 -1
  58. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  59. package/dist/auth/oauth/kimi-coding.js +24 -21
  60. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  61. package/dist/auth/oauth/meta.d.ts.map +1 -1
  62. package/dist/auth/oauth/meta.js +7 -2
  63. package/dist/auth/oauth/meta.js.map +1 -1
  64. package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -1
  65. package/dist/auth/oauth/openai-chatgpt.js +10 -0
  66. package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
  67. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  68. package/dist/auth/oauth/openai-codex.js +11 -2
  69. package/dist/auth/oauth/openai-codex.js.map +1 -1
  70. package/dist/auth/oauth/radius.d.ts.map +1 -1
  71. package/dist/auth/oauth/radius.js +24 -8
  72. package/dist/auth/oauth/radius.js.map +1 -1
  73. package/dist/auth/oauth/xai.d.ts.map +1 -1
  74. package/dist/auth/oauth/xai.js +11 -0
  75. package/dist/auth/oauth/xai.js.map +1 -1
  76. package/dist/auth/oauth-refresh-errors.d.ts +46 -0
  77. package/dist/auth/oauth-refresh-errors.d.ts.map +1 -0
  78. package/dist/auth/oauth-refresh-errors.js +150 -0
  79. package/dist/auth/oauth-refresh-errors.js.map +1 -0
  80. package/dist/auth/resolve.d.ts +23 -0
  81. package/dist/auth/resolve.d.ts.map +1 -1
  82. package/dist/auth/resolve.js +194 -5
  83. package/dist/auth/resolve.js.map +1 -1
  84. package/dist/index.d.ts +3 -0
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +3 -0
  87. package/dist/index.js.map +1 -1
  88. package/dist/models.d.ts +4 -1
  89. package/dist/models.d.ts.map +1 -1
  90. package/dist/models.js +15 -4
  91. package/dist/models.js.map +1 -1
  92. package/dist/providers/compat-schema.d.ts +18 -0
  93. package/dist/providers/compat-schema.d.ts.map +1 -1
  94. package/dist/providers/compat-schema.js +10 -0
  95. package/dist/providers/compat-schema.js.map +1 -1
  96. package/dist/providers/data/.manifest.json +1 -1
  97. package/dist/providers/data/amazon-bedrock.json +1 -1
  98. package/dist/providers/data/baseten.json +1 -1
  99. package/dist/providers/data/cerebras.json +1 -1
  100. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  101. package/dist/providers/data/github-copilot.json +1 -1
  102. package/dist/providers/data/groq.json +1 -1
  103. package/dist/providers/data/huggingface.json +1 -1
  104. package/dist/providers/data/nvidia.json +1 -1
  105. package/dist/providers/data/opencode.json +1 -1
  106. package/dist/providers/data/openrouter.json +1 -1
  107. package/dist/providers/data/radius.json +1 -1
  108. package/dist/providers/data/together.json +1 -1
  109. package/dist/providers/data/typesafe.json +1 -1
  110. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  111. package/dist/types.d.ts +10 -2
  112. package/dist/types.d.ts.map +1 -1
  113. package/dist/types.js.map +1 -1
  114. package/dist/utils/identity.d.ts +65 -0
  115. package/dist/utils/identity.d.ts.map +1 -0
  116. package/dist/utils/identity.js +142 -0
  117. package/dist/utils/identity.js.map +1 -0
  118. package/dist/utils/retry.d.ts.map +1 -1
  119. package/dist/utils/retry.js +9 -8
  120. package/dist/utils/retry.js.map +1 -1
  121. package/dist/utils/version.d.ts +2 -0
  122. package/dist/utils/version.d.ts.map +1 -0
  123. package/dist/utils/version.js +3 -0
  124. package/dist/utils/version.js.map +1 -0
  125. package/package.json +2 -2
  126. package/dist/utils/pi-user-agent.d.ts +0 -2
  127. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  128. package/dist/utils/pi-user-agent.js +0 -12
  129. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -0,0 +1,150 @@
1
+ /** Codes that mean the stored refresh token can never be used again (OAuth 2.0 and OpenAI's refresh_token_*). */
2
+ const PERMANENT_REFRESH_ERROR_CODES = new Set([
3
+ "invalid_grant",
4
+ "refresh_token_expired",
5
+ "refresh_token_reused",
6
+ "refresh_token_invalidated",
7
+ ]);
8
+ /** HTTP 401 from a token endpoint: the provider no longer accepts this login, whatever its error code. */
9
+ const UNAUTHORIZED_STATUS = 401;
10
+ /** A longer description is dropped rather than cut, so an echoed token cannot survive as a prefix. */
11
+ const MAX_DESCRIPTION_LENGTH = 300;
12
+ const ERROR_CODE_PATTERN = /^[A-Za-z0-9_.:-]{1,64}$/;
13
+ const URL_PATTERN = /\b[a-z][a-z0-9+.-]*:\/\//i;
14
+ /**
15
+ * Network errors raised before a request was sent: the connection was never established, so the server cannot
16
+ * have seen (or rotated) the refresh token. Node reports the code on `cause`; Bun on the error itself.
17
+ */
18
+ const UNDELIVERED_REQUEST_ERROR_CODES = new Set([
19
+ "ECONNREFUSED",
20
+ "ENOTFOUND",
21
+ "EAI_AGAIN",
22
+ "ENETUNREACH",
23
+ "EHOSTUNREACH",
24
+ "UND_ERR_CONNECT_TIMEOUT",
25
+ ]);
26
+ function textField(value) {
27
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
28
+ }
29
+ function objectField(value) {
30
+ return value !== null && typeof value === "object" && !Array.isArray(value)
31
+ ? value
32
+ : undefined;
33
+ }
34
+ /** Reads `error` / `error.code` / `code` and `error_description` / `error.message` / `message` from a JSON body. */
35
+ export function parseOAuthErrorBody(body) {
36
+ let parsed;
37
+ try {
38
+ parsed = JSON.parse(body);
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ const json = objectField(parsed);
44
+ if (!json)
45
+ return {};
46
+ const nested = objectField(json.error);
47
+ return {
48
+ code: textField(json.error) ?? textField(nested?.code) ?? textField(json.code),
49
+ description: textField(json.error_description) ?? textField(nested?.message) ?? textField(json.message),
50
+ };
51
+ }
52
+ /** Whether a refresh rejection means the stored login is revoked or expired, so only signing in again helps. */
53
+ export function isPermanentRefreshRejection(rejection) {
54
+ return (rejection.status === UNAUTHORIZED_STATUS ||
55
+ (rejection.code !== undefined && isPermanentRefreshErrorCode(rejection.code)));
56
+ }
57
+ /** Whether `code` is one of the OAuth error codes that mark a refresh token as permanently unusable. */
58
+ export function isPermanentRefreshErrorCode(code) {
59
+ return PERMANENT_REFRESH_ERROR_CODES.has(code.toLowerCase());
60
+ }
61
+ const JSON_STRING = String.raw `"((?:[^"\\]|\\.)*)"`;
62
+ const TEXT_ERROR_CODE_PATTERNS = [
63
+ new RegExp(String.raw `"error"\s*:\s*${JSON_STRING}`, "g"),
64
+ new RegExp(String.raw `"error"\s*:\s*\{[^{}]*?"code"\s*:\s*${JSON_STRING}`, "g"),
65
+ new RegExp(String.raw `"code"\s*:\s*${JSON_STRING}`, "g"),
66
+ ];
67
+ const TEXT_ERROR_DESCRIPTION_PATTERNS = [
68
+ new RegExp(String.raw `"error_description"\s*:\s*${JSON_STRING}`),
69
+ new RegExp(String.raw `"error"\s*:\s*\{[^{}]*?"message"\s*:\s*${JSON_STRING}`),
70
+ ];
71
+ const BARE_PERMANENT_REFRESH_ERROR_CODE = new RegExp(String.raw `\b(${[...PERMANENT_REFRESH_ERROR_CODES].join("|")})\b`, "i");
72
+ function jsonStringContent(escaped) {
73
+ try {
74
+ const value = JSON.parse(`"${escaped}"`);
75
+ return typeof value === "string" ? value : undefined;
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ }
81
+ /**
82
+ * Finds a permanent refresh rejection in an error message from a refresher that does not throw
83
+ * {@link OAuthRefreshRejectedError}: an embedded JSON error body (`error`, nested `error.code`, or `code`) or a bare
84
+ * permanent code, plus the body's description. Returns nothing if no permanent code appears.
85
+ */
86
+ export function findPermanentRefreshRejectionInText(text) {
87
+ const embeddedCodes = TEXT_ERROR_CODE_PATTERNS.flatMap((pattern) => [...text.matchAll(pattern)].map((match) => jsonStringContent(match[1] ?? "")));
88
+ const code = embeddedCodes.find((candidate) => candidate !== undefined && isPermanentRefreshErrorCode(candidate)) ??
89
+ BARE_PERMANENT_REFRESH_ERROR_CODE.exec(text)?.[1];
90
+ if (code === undefined)
91
+ return undefined;
92
+ const description = TEXT_ERROR_DESCRIPTION_PATTERNS.map((pattern) => pattern.exec(text)?.[1])
93
+ .map((escaped) => (escaped === undefined ? undefined : jsonStringContent(escaped)))
94
+ .find((candidate) => candidate !== undefined);
95
+ return { code, description };
96
+ }
97
+ /** The provider's error code, if it looks like one; anything else could be echoed request data. */
98
+ export function safeOAuthErrorCode(code) {
99
+ return code !== undefined && ERROR_CODE_PATTERN.test(code) ? code : undefined;
100
+ }
101
+ /** The provider's description, unless it echoes a presented secret, carries a URL, or is too long to be one. */
102
+ export function safeOAuthErrorDescription(description, presentedSecrets) {
103
+ const text = description?.replace(/\s+/g, " ").trim();
104
+ if (!text || text.length > MAX_DESCRIPTION_LENGTH || URL_PATTERN.test(text))
105
+ return undefined;
106
+ return presentedSecrets.some((secret) => secret.length > 0 && text.includes(secret)) ? undefined : text;
107
+ }
108
+ /** `: code: description` with only the parts that are safe to show, or nothing. */
109
+ export function formatOAuthErrorDetail(detail, presentedSecrets) {
110
+ const parts = [safeOAuthErrorCode(detail.code), safeOAuthErrorDescription(detail.description, presentedSecrets)];
111
+ const shown = parts.filter((part) => part !== undefined);
112
+ return shown.length > 0 ? `: ${shown.join(": ")}` : "";
113
+ }
114
+ /**
115
+ * A token endpoint rejected a refresh because the stored login is revoked or expired. Carries only the HTTP status
116
+ * and the provider's error code and description, the latter dropped if it echoes the presented token.
117
+ */
118
+ export class OAuthRefreshRejectedError extends Error {
119
+ name = "OAuthRefreshRejectedError";
120
+ status;
121
+ code;
122
+ description;
123
+ constructor(summary, rejection, presentedSecrets) {
124
+ super(`${summary}${formatOAuthErrorDetail(rejection, presentedSecrets)}`);
125
+ this.status = rejection.status;
126
+ this.code = safeOAuthErrorCode(rejection.code);
127
+ this.description = safeOAuthErrorDescription(rejection.description, presentedSecrets);
128
+ }
129
+ }
130
+ /** Recognises the rejection across duplicated module copies (source and built output), not only by identity. */
131
+ export function isOAuthRefreshRejectedError(error) {
132
+ return (error instanceof OAuthRefreshRejectedError ||
133
+ (error.name === "OAuthRefreshRejectedError" && "status" in error && typeof error.status === "number"));
134
+ }
135
+ function errorCode(error) {
136
+ const code = "code" in error ? error.code : undefined;
137
+ return typeof code === "string" ? code : undefined;
138
+ }
139
+ /**
140
+ * Whether a failed fetch provably never sent its request (no connection was made). Only then may a refresh token be
141
+ * presented again: after an abort, a timeout, or a connection lost mid-request the server may already have rotated it.
142
+ */
143
+ export function requestNeverReachedServer(error) {
144
+ if (error.name === "AbortError" || error.name === "TimeoutError")
145
+ return false;
146
+ const cause = error.cause instanceof Error ? error.cause : undefined;
147
+ const code = errorCode(error) ?? (cause ? errorCode(cause) : undefined);
148
+ return code !== undefined && UNDELIVERED_REQUEST_ERROR_CODES.has(code);
149
+ }
150
+ //# sourceMappingURL=oauth-refresh-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-refresh-errors.js","sourceRoot":"","sources":["../../src/auth/oauth-refresh-errors.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonValue } from \"../types.ts\";\n\n/** A token endpoint's error code and description, as the provider sent them. */\nexport interface OAuthErrorDetail {\n\tcode?: string;\n\tdescription?: string;\n}\n\n/** A token endpoint's answer to a refresh, by HTTP status and the provider's error code and description. */\nexport interface OAuthRefreshRejection extends OAuthErrorDetail {\n\tstatus: number;\n}\n\n/** Codes that mean the stored refresh token can never be used again (OAuth 2.0 and OpenAI's refresh_token_*). */\nconst PERMANENT_REFRESH_ERROR_CODES = new Set([\n\t\"invalid_grant\",\n\t\"refresh_token_expired\",\n\t\"refresh_token_reused\",\n\t\"refresh_token_invalidated\",\n]);\n\n/** HTTP 401 from a token endpoint: the provider no longer accepts this login, whatever its error code. */\nconst UNAUTHORIZED_STATUS = 401;\n\n/** A longer description is dropped rather than cut, so an echoed token cannot survive as a prefix. */\nconst MAX_DESCRIPTION_LENGTH = 300;\nconst ERROR_CODE_PATTERN = /^[A-Za-z0-9_.:-]{1,64}$/;\nconst URL_PATTERN = /\\b[a-z][a-z0-9+.-]*:\\/\\//i;\n\n/**\n * Network errors raised before a request was sent: the connection was never established, so the server cannot\n * have seen (or rotated) the refresh token. Node reports the code on `cause`; Bun on the error itself.\n */\nconst UNDELIVERED_REQUEST_ERROR_CODES = new Set([\n\t\"ECONNREFUSED\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"ENETUNREACH\",\n\t\"EHOSTUNREACH\",\n\t\"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\nfunction textField(value: JsonValue | undefined): string | undefined {\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction objectField(value: JsonValue | undefined): { readonly [key: string]: JsonValue } | undefined {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as { readonly [key: string]: JsonValue })\n\t\t: undefined;\n}\n\n/** Reads `error` / `error.code` / `code` and `error_description` / `error.message` / `message` from a JSON body. */\nexport function parseOAuthErrorBody(body: string): OAuthErrorDetail {\n\tlet parsed: JsonValue;\n\ttry {\n\t\tparsed = JSON.parse(body);\n\t} catch {\n\t\treturn {};\n\t}\n\tconst json = objectField(parsed);\n\tif (!json) return {};\n\tconst nested = objectField(json.error);\n\treturn {\n\t\tcode: textField(json.error) ?? textField(nested?.code) ?? textField(json.code),\n\t\tdescription: textField(json.error_description) ?? textField(nested?.message) ?? textField(json.message),\n\t};\n}\n\n/** Whether a refresh rejection means the stored login is revoked or expired, so only signing in again helps. */\nexport function isPermanentRefreshRejection(rejection: OAuthRefreshRejection): boolean {\n\treturn (\n\t\trejection.status === UNAUTHORIZED_STATUS ||\n\t\t(rejection.code !== undefined && isPermanentRefreshErrorCode(rejection.code))\n\t);\n}\n\n/** Whether `code` is one of the OAuth error codes that mark a refresh token as permanently unusable. */\nexport function isPermanentRefreshErrorCode(code: string): boolean {\n\treturn PERMANENT_REFRESH_ERROR_CODES.has(code.toLowerCase());\n}\n\nconst JSON_STRING = String.raw`\"((?:[^\"\\\\]|\\\\.)*)\"`;\nconst TEXT_ERROR_CODE_PATTERNS = [\n\tnew RegExp(String.raw`\"error\"\\s*:\\s*${JSON_STRING}`, \"g\"),\n\tnew RegExp(String.raw`\"error\"\\s*:\\s*\\{[^{}]*?\"code\"\\s*:\\s*${JSON_STRING}`, \"g\"),\n\tnew RegExp(String.raw`\"code\"\\s*:\\s*${JSON_STRING}`, \"g\"),\n];\nconst TEXT_ERROR_DESCRIPTION_PATTERNS = [\n\tnew RegExp(String.raw`\"error_description\"\\s*:\\s*${JSON_STRING}`),\n\tnew RegExp(String.raw`\"error\"\\s*:\\s*\\{[^{}]*?\"message\"\\s*:\\s*${JSON_STRING}`),\n];\nconst BARE_PERMANENT_REFRESH_ERROR_CODE = new RegExp(String.raw`\\b(${[...PERMANENT_REFRESH_ERROR_CODES].join(\"|\")})\\b`, \"i\");\n\nfunction jsonStringContent(escaped: string): string | undefined {\n\ttry {\n\t\tconst value: JsonValue = JSON.parse(`\"${escaped}\"`);\n\t\treturn typeof value === \"string\" ? value : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Finds a permanent refresh rejection in an error message from a refresher that does not throw\n * {@link OAuthRefreshRejectedError}: an embedded JSON error body (`error`, nested `error.code`, or `code`) or a bare\n * permanent code, plus the body's description. Returns nothing if no permanent code appears.\n */\nexport function findPermanentRefreshRejectionInText(text: string): OAuthErrorDetail | undefined {\n\tconst embeddedCodes = TEXT_ERROR_CODE_PATTERNS.flatMap((pattern) =>\n\t\t[...text.matchAll(pattern)].map((match) => jsonStringContent(match[1] ?? \"\")),\n\t);\n\tconst code =\n\t\tembeddedCodes.find((candidate) => candidate !== undefined && isPermanentRefreshErrorCode(candidate)) ??\n\t\tBARE_PERMANENT_REFRESH_ERROR_CODE.exec(text)?.[1];\n\tif (code === undefined) return undefined;\n\tconst description = TEXT_ERROR_DESCRIPTION_PATTERNS.map((pattern) => pattern.exec(text)?.[1])\n\t\t.map((escaped) => (escaped === undefined ? undefined : jsonStringContent(escaped)))\n\t\t.find((candidate) => candidate !== undefined);\n\treturn { code, description };\n}\n\n/** The provider's error code, if it looks like one; anything else could be echoed request data. */\nexport function safeOAuthErrorCode(code: string | undefined): string | undefined {\n\treturn code !== undefined && ERROR_CODE_PATTERN.test(code) ? code : undefined;\n}\n\n/** The provider's description, unless it echoes a presented secret, carries a URL, or is too long to be one. */\nexport function safeOAuthErrorDescription(\n\tdescription: string | undefined,\n\tpresentedSecrets: readonly string[],\n): string | undefined {\n\tconst text = description?.replace(/\\s+/g, \" \").trim();\n\tif (!text || text.length > MAX_DESCRIPTION_LENGTH || URL_PATTERN.test(text)) return undefined;\n\treturn presentedSecrets.some((secret) => secret.length > 0 && text.includes(secret)) ? undefined : text;\n}\n\n/** `: code: description` with only the parts that are safe to show, or nothing. */\nexport function formatOAuthErrorDetail(detail: OAuthErrorDetail, presentedSecrets: readonly string[]): string {\n\tconst parts = [safeOAuthErrorCode(detail.code), safeOAuthErrorDescription(detail.description, presentedSecrets)];\n\tconst shown = parts.filter((part): part is string => part !== undefined);\n\treturn shown.length > 0 ? `: ${shown.join(\": \")}` : \"\";\n}\n\n/**\n * A token endpoint rejected a refresh because the stored login is revoked or expired. Carries only the HTTP status\n * and the provider's error code and description, the latter dropped if it echoes the presented token.\n */\nexport class OAuthRefreshRejectedError extends Error {\n\toverride readonly name = \"OAuthRefreshRejectedError\";\n\treadonly status: number;\n\treadonly code: string | undefined;\n\treadonly description: string | undefined;\n\n\tconstructor(summary: string, rejection: OAuthRefreshRejection, presentedSecrets: readonly string[]) {\n\t\tsuper(`${summary}${formatOAuthErrorDetail(rejection, presentedSecrets)}`);\n\t\tthis.status = rejection.status;\n\t\tthis.code = safeOAuthErrorCode(rejection.code);\n\t\tthis.description = safeOAuthErrorDescription(rejection.description, presentedSecrets);\n\t}\n}\n\n/** Recognises the rejection across duplicated module copies (source and built output), not only by identity. */\nexport function isOAuthRefreshRejectedError(error: Error): error is OAuthRefreshRejectedError {\n\treturn (\n\t\terror instanceof OAuthRefreshRejectedError ||\n\t\t(error.name === \"OAuthRefreshRejectedError\" && \"status\" in error && typeof error.status === \"number\")\n\t);\n}\n\nfunction errorCode(error: Error): string | undefined {\n\tconst code = \"code\" in error ? error.code : undefined;\n\treturn typeof code === \"string\" ? code : undefined;\n}\n\n/**\n * Whether a failed fetch provably never sent its request (no connection was made). Only then may a refresh token be\n * presented again: after an abort, a timeout, or a connection lost mid-request the server may already have rotated it.\n */\nexport function requestNeverReachedServer(error: Error): boolean {\n\tif (error.name === \"AbortError\" || error.name === \"TimeoutError\") return false;\n\tconst cause = error.cause instanceof Error ? error.cause : undefined;\n\tconst code = errorCode(error) ?? (cause ? errorCode(cause) : undefined);\n\treturn code !== undefined && UNDELIVERED_REQUEST_ERROR_CODES.has(code);\n}\n"]}
@@ -23,6 +23,29 @@ export declare function resolveProviderAuth(provider: {
23
23
  id: string;
24
24
  auth: ProviderAuth;
25
25
  }, credentials: CredentialStore, authContext: AuthContext, overrides?: AuthResolutionOverrides): Promise<AuthResult | undefined>;
26
+ /** How long a refresh keeps its caller waiting for one token exchange. */
27
+ export declare const OAUTH_REFRESH_TIMEOUT_MS = 60000;
28
+ /**
29
+ * How long a refresh keeps the credential lock while its token exchange is still running, so no other refresh
30
+ * presents a token the exchange may already have rotated. A response after this is still persisted, lock-free.
31
+ */
32
+ export declare const OAUTH_REFRESH_LOCK_HOLD_MS: number;
33
+ /**
34
+ * How long a graceful shutdown waits for token exchanges still in flight, and for persisting their results, before
35
+ * the process exits (see {@link drainPendingOAuthRotations}).
36
+ *
37
+ * Abrupt termination (SIGKILL, a crash, power loss) cannot wait. If the provider already rotated the refresh token
38
+ * when that happens, its response never reaches the process and the stored login keeps the replaced token; the
39
+ * next refresh then fails and `/login <provider>` signs in again.
40
+ */
41
+ export declare const OAUTH_SHUTDOWN_DRAIN_MS = 3000;
42
+ /**
43
+ * Waits for every started OAuth token exchange to settle and its result to be persisted, at most `timeoutMs`.
44
+ * Callers stop waiting for an exchange after {@link OAUTH_REFRESH_TIMEOUT_MS} while it keeps running, so a process
45
+ * that exits gracefully calls this first: the provider may already have rotated the token. Resolves `true` once none
46
+ * is in flight, `false` when the bound passes first; never rejects.
47
+ */
48
+ export declare function drainPendingOAuthRotations(timeoutMs?: number): Promise<boolean>;
26
49
  /** Cancellation stops waiting for the credential lock, not an already started token rotation. */
27
50
  export declare function refreshStoredOAuthCredential(credentials: CredentialStore, providerId: string, oauth: OAuthAuth, needsRefresh: (credential: OAuthCredential) => boolean, signal: AbortSignal): Promise<OAuthCredential | undefined>;
28
51
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/auth/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE7E,OAAO,KAAK,EAGX,WAAW,EACX,UAAU,EACV,UAAU,EACV,eAAe,EACf,SAAS,EACT,eAAe,EACf,YAAY,EACZ,MAAM,YAAY,CAAC;AAEpB,6FAA6F;AAC7F,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAE1D,qHAAqH;AACrH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,EAC5C,WAAW,EAAE,eAAe,EAC5B,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,uBAAuB,GACjC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAwBjC;AA6DD,iGAAiG;AACjG,wBAAsB,4BAA4B,CACjD,WAAW,EAAE,eAAe,EAC5B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAChB,YAAY,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,OAAO,EACtD,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CA+BtC;AAiDD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC3C,WAAW,EAAE,eAAe,EAC5B,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,EAC5C,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAQjC"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/auth/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW/C,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE7E,OAAO,KAAK,EAGX,WAAW,EACX,UAAU,EACV,UAAU,EACV,eAAe,EACf,SAAS,EACT,eAAe,EACf,YAAY,EACZ,MAAM,YAAY,CAAC;AAEpB,6FAA6F;AAC7F,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAE1D,qHAAqH;AACrH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,EAC5C,WAAW,EAAE,eAAe,EAC5B,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,uBAAuB,GACjC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAwBjC;AA2DD,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAA+B,CAAC;AAQvE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAiB7C;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAAC,SAAS,GAAE,MAAgC,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9G;AA2JD,iGAAiG;AACjG,wBAAsB,4BAA4B,CACjD,WAAW,EAAE,eAAe,EAC5B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAChB,YAAY,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,OAAO,EACtD,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CA4CtC;AAiDD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC3C,WAAW,EAAE,eAAe,EAC5B,QAAQ,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,EAC5C,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAQjC"}
@@ -1,6 +1,8 @@
1
1
  import { normalizeProviderId } from "../provider-id.js";
2
2
  import { operationSignal, raceWithAbortSignal } from "../utils/abort.js";
3
+ import { formatThrownValue } from "../utils/diagnostics.js";
3
4
  import { ModelsError } from "../utils/models-error.js";
5
+ import { findPermanentRefreshRejectionInText, isOAuthRefreshRejectedError, safeOAuthErrorCode, safeOAuthErrorDescription, } from "./oauth-refresh-errors.js";
4
6
  export { ModelsError } from "../utils/models-error.js";
5
7
  /** Bound for pre-transport request-auth work: credential read, OAuth refresh, and toAuth. */
6
8
  export const REQUEST_AUTH_PREPARATION_TIMEOUT_MS = 15_000;
@@ -67,7 +69,180 @@ function overlayEnvAuthContext(base, env) {
67
69
  };
68
70
  }
69
71
  const DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;
70
- const DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 60_000;
72
+ /** How long a refresh keeps its caller waiting for one token exchange. */
73
+ export const OAUTH_REFRESH_TIMEOUT_MS = 60_000;
74
+ /**
75
+ * How long a refresh keeps the credential lock while its token exchange is still running, so no other refresh
76
+ * presents a token the exchange may already have rotated. A response after this is still persisted, lock-free.
77
+ */
78
+ export const OAUTH_REFRESH_LOCK_HOLD_MS = 2 * OAUTH_REFRESH_TIMEOUT_MS;
79
+ /**
80
+ * Signal handed to a provider's token exchange. Nothing aborts it: once sent, the provider may already have
81
+ * rotated the refresh token, so only the exchange's own response can tell us the replacement.
82
+ */
83
+ const TOKEN_EXCHANGE_SIGNAL = new AbortController().signal;
84
+ /**
85
+ * How long a graceful shutdown waits for token exchanges still in flight, and for persisting their results, before
86
+ * the process exits (see {@link drainPendingOAuthRotations}).
87
+ *
88
+ * Abrupt termination (SIGKILL, a crash, power loss) cannot wait. If the provider already rotated the refresh token
89
+ * when that happens, its response never reaches the process and the stored login keeps the replaced token; the
90
+ * next refresh then fails and `/login <provider>` signs in again.
91
+ */
92
+ export const OAUTH_SHUTDOWN_DRAIN_MS = 3_000;
93
+ /** One entry per started token exchange, settled once its result is persisted or given up on. */
94
+ const pendingTokenExchanges = new Set();
95
+ function trackTokenExchange() {
96
+ let settle;
97
+ const pending = new Promise((resolve) => {
98
+ settle = resolve;
99
+ });
100
+ pendingTokenExchanges.add(pending);
101
+ return () => {
102
+ pendingTokenExchanges.delete(pending);
103
+ settle();
104
+ };
105
+ }
106
+ /**
107
+ * Waits for every started OAuth token exchange to settle and its result to be persisted, at most `timeoutMs`.
108
+ * Callers stop waiting for an exchange after {@link OAUTH_REFRESH_TIMEOUT_MS} while it keeps running, so a process
109
+ * that exits gracefully calls this first: the provider may already have rotated the token. Resolves `true` once none
110
+ * is in flight, `false` when the bound passes first; never rejects.
111
+ */
112
+ export async function drainPendingOAuthRotations(timeoutMs = OAUTH_SHUTDOWN_DRAIN_MS) {
113
+ const deadline = Date.now() + timeoutMs;
114
+ while (pendingTokenExchanges.size > 0) {
115
+ const remainingMs = deadline - Date.now();
116
+ if (remainingMs <= 0)
117
+ return false;
118
+ let timer;
119
+ const expired = new Promise((resolve) => {
120
+ timer = setTimeout(resolve, remainingMs);
121
+ });
122
+ try {
123
+ await Promise.race([Promise.all(pendingTokenExchanges), expired]);
124
+ }
125
+ finally {
126
+ clearTimeout(timer);
127
+ }
128
+ }
129
+ return true;
130
+ }
131
+ const MAX_ERROR_CHAIN_DEPTH = 5;
132
+ /**
133
+ * The provider's error code (or HTTP status) and description for a refresh it rejected because the stored login is
134
+ * revoked or expired: an {@link OAuthRefreshRejectedError} anywhere in the cause chain, or a permanent OAuth error
135
+ * code in a custom refresher's message. Never request bodies, URLs, or tokens.
136
+ */
137
+ function describeRejectedLogin(error, presented) {
138
+ const secrets = [presented.refresh, presented.access];
139
+ const describe = (code, description) => {
140
+ const safeDescription = safeOAuthErrorDescription(description, secrets);
141
+ return safeDescription ? `${code} (${safeDescription})` : code;
142
+ };
143
+ let current = error;
144
+ for (let depth = 0; depth < MAX_ERROR_CHAIN_DEPTH && current !== undefined && current !== null; depth++) {
145
+ if (current instanceof Error && isOAuthRefreshRejectedError(current)) {
146
+ return describe(safeOAuthErrorCode(current.code) ?? `HTTP ${current.status}`, current.description);
147
+ }
148
+ current = current instanceof Error ? current.cause : undefined;
149
+ }
150
+ current = error;
151
+ for (let depth = 0; depth < MAX_ERROR_CHAIN_DEPTH && current !== undefined && current !== null; depth++) {
152
+ const rejection = findPermanentRefreshRejectionInText(formatThrownValue(current));
153
+ const code = safeOAuthErrorCode(rejection?.code);
154
+ if (code !== undefined)
155
+ return describe(code, rejection?.description);
156
+ current = current instanceof Error ? current.cause : undefined;
157
+ }
158
+ return undefined;
159
+ }
160
+ /**
161
+ * Per credential store and provider, why the last late rotation could not be saved: only the save error's `code` or
162
+ * `name`, never its message, which may contain paths or tokens. Keyed by store so one store's lost save is never
163
+ * reported against another's login. Cleared by a later save or rotation, or once a refresh failure reports it.
164
+ */
165
+ const unsavedLateRotations = new WeakMap();
166
+ function unsavedLateRotationsFor(credentials) {
167
+ let byProvider = unsavedLateRotations.get(credentials);
168
+ if (byProvider === undefined) {
169
+ byProvider = new Map();
170
+ unsavedLateRotations.set(credentials, byProvider);
171
+ }
172
+ return byProvider;
173
+ }
174
+ function safeSaveErrorReason(error) {
175
+ if (error instanceof Error) {
176
+ const code = "code" in error ? error.code : undefined;
177
+ return typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code) ? code : error.name;
178
+ }
179
+ return "unknown error";
180
+ }
181
+ /** The sentence explaining a lost late rotation for `providerId` in `credentials`, reported once. */
182
+ function takeUnsavedLateRotationNote(credentials, providerId) {
183
+ const byProvider = unsavedLateRotations.get(credentials);
184
+ const reason = byProvider?.get(providerId);
185
+ if (reason === undefined)
186
+ return "";
187
+ byProvider?.delete(providerId);
188
+ return ` An earlier refresh for ${providerId} succeeded, but its new login could not be saved (${reason}).`;
189
+ }
190
+ function oauthRefreshFailure(credentials, providerId, error, presented) {
191
+ const rejectedLogin = describeRejectedLogin(error, presented);
192
+ const unsavedNote = takeUnsavedLateRotationNote(credentials, providerId);
193
+ if (rejectedLogin === undefined) {
194
+ const message = unsavedNote
195
+ ? `OAuth refresh failed for ${providerId}.${unsavedNote}`
196
+ : `OAuth refresh failed for ${providerId}`;
197
+ return new ModelsError("oauth", message, { cause: error });
198
+ }
199
+ return new ModelsError("oauth", `OAuth refresh failed for ${providerId}: ${rejectedLogin}. The provider rejected the stored login; run /login ${providerId} to sign in again.${unsavedNote}`);
200
+ }
201
+ /** A token exchange that settles after its timeout already rotated the token server-side, so keep its result. */
202
+ function persistLateRotation(credentials, providerId, rotatedFrom, rotation) {
203
+ const settled = trackTokenExchange();
204
+ void rotation
205
+ .then((late) => credentials
206
+ .modify(providerId, async (current) => current?.type === "oauth" && current.refresh === rotatedFrom.refresh ? late : undefined)
207
+ .then(() => {
208
+ unsavedLateRotations.get(credentials)?.delete(providerId);
209
+ }, (saveError) => {
210
+ unsavedLateRotationsFor(credentials).set(providerId, safeSaveErrorReason(saveError));
211
+ }),
212
+ // The exchange itself failed: nothing was rotated that could be lost.
213
+ () => undefined)
214
+ .finally(settled);
215
+ }
216
+ /**
217
+ * Runs one token exchange under the credential lock. The caller stops waiting after {@link OAUTH_REFRESH_TIMEOUT_MS};
218
+ * the lock is kept until the exchange settles or {@link OAUTH_REFRESH_LOCK_HOLD_MS} passes, after which a late
219
+ * response is still persisted.
220
+ */
221
+ async function rotateOAuthCredential(credentials, providerId, oauth, current, stopCallerWaiting) {
222
+ const rotation = oauth.refresh(current, TOKEN_EXCHANGE_SIGNAL);
223
+ const callerWait = AbortSignal.timeout(OAUTH_REFRESH_TIMEOUT_MS);
224
+ const onCallerWaitExpired = () => {
225
+ stopCallerWaiting(new ModelsError("oauth", `OAuth refresh failed for ${providerId}`, { cause: callerWait.reason }));
226
+ };
227
+ callerWait.addEventListener("abort", onCallerWaitExpired, { once: true });
228
+ const lockHold = AbortSignal.timeout(OAUTH_REFRESH_LOCK_HOLD_MS);
229
+ try {
230
+ return await raceWithAbortSignal(rotation, lockHold);
231
+ }
232
+ catch (error) {
233
+ if (lockHold.aborted)
234
+ persistLateRotation(credentials, providerId, current, rotation);
235
+ throw error;
236
+ }
237
+ finally {
238
+ callerWait.removeEventListener("abort", onCallerWaitExpired);
239
+ }
240
+ }
241
+ /** A login another process saved after this refresh read the store, e.g. its own rotation of the same token. */
242
+ async function readNewerOAuthCredential(credentials, providerId, rejected) {
243
+ const latest = await credentials.read(providerId).catch(() => undefined);
244
+ return latest?.type === "oauth" && latest.refresh !== rejected.refresh ? latest : undefined;
245
+ }
71
246
  /** Cancellation stops waiting for the credential lock, not an already started token rotation. */
72
247
  export async function refreshStoredOAuthCredential(credentials, providerId, oauth, needsRefresh, signal) {
73
248
  let post;
@@ -76,8 +251,14 @@ export async function refreshStoredOAuthCredential(credentials, providerId, oaut
76
251
  signal.addEventListener("abort", cancelLockWait, { once: true });
77
252
  if (signal.aborted)
78
253
  cancelLockWait();
254
+ let stopCallerWaiting;
255
+ /** Set once the token exchange starts; settles its pending entry when the transaction that commits it ends. */
256
+ let exchangeSettled;
257
+ const callerWaitExpired = new Promise((_, reject) => {
258
+ stopCallerWaiting = reject;
259
+ });
79
260
  try {
80
- post = await credentials.modify(providerId, async (current) => {
261
+ const transaction = credentials.modify(providerId, async (current) => {
81
262
  signal.removeEventListener("abort", cancelLockWait);
82
263
  signal.throwIfAborted();
83
264
  if (current?.type !== "oauth")
@@ -85,13 +266,21 @@ export async function refreshStoredOAuthCredential(credentials, providerId, oaut
85
266
  if (!needsRefresh(current))
86
267
  return undefined;
87
268
  try {
88
- const refreshSignal = AbortSignal.timeout(DEFAULT_OAUTH_REFRESH_TIMEOUT_MS);
89
- return await raceWithAbortSignal(oauth.refresh(current, refreshSignal), refreshSignal);
269
+ exchangeSettled ??= trackTokenExchange();
270
+ const rotated = await rotateOAuthCredential(credentials, providerId, oauth, current, stopCallerWaiting);
271
+ unsavedLateRotations.get(credentials)?.delete(providerId);
272
+ return rotated;
90
273
  }
91
274
  catch (error) {
92
- throw new ModelsError("oauth", `OAuth refresh failed for ${providerId}`, { cause: error });
275
+ const newer = await readNewerOAuthCredential(credentials, providerId, current);
276
+ if (newer)
277
+ return newer;
278
+ throw oauthRefreshFailure(credentials, providerId, error, current);
93
279
  }
94
280
  }, { signal: lockWait.signal });
281
+ const untrackExchange = () => exchangeSettled?.();
282
+ void transaction.then(untrackExchange, untrackExchange);
283
+ post = await Promise.race([transaction, callerWaitExpired]);
95
284
  }
96
285
  catch (error) {
97
286
  if (error instanceof ModelsError)
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/auth/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeProviderId } from \"../provider-id.ts\";\nimport type { ProviderEnv } from \"../types.ts\";\nimport { operationSignal, raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { ModelsError } from \"../utils/models-error.ts\";\n\nexport { ModelsError, type ModelsErrorCode } from \"../utils/models-error.ts\";\n\nimport type {\n\tApiKeyAuth,\n\tApiKeyCredential,\n\tAuthContext,\n\tAuthResult,\n\tCredential,\n\tCredentialStore,\n\tOAuthAuth,\n\tOAuthCredential,\n\tProviderAuth,\n} from \"./types.ts\";\n\n/** Bound for pre-transport request-auth work: credential read, OAuth refresh, and toAuth. */\nexport const REQUEST_AUTH_PREPARATION_TIMEOUT_MS = 15_000;\n\n/** Source-neutral diagnostic when pre-transport request auth exceeds {@link REQUEST_AUTH_PREPARATION_TIMEOUT_MS}. */\nexport function requestAuthTimeoutMessage(providerId: string): string {\n\treturn `Request authentication timed out for ${providerId}. Check the provider's credential source and try again.`;\n}\n\nexport interface AuthResolutionOverrides {\n\tapiKey?: string;\n\tenv?: ProviderEnv;\n\t/** Require this much remaining OAuth-token validity; defaults to five minutes. */\n\tminOAuthValidityMs?: number;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Auth resolution shared by all operations in a `Models` collection.\n * A stored credential owns the provider: ambient/env is consulted only when\n * nothing is stored. A provider that borrows credentials reads only its\n * lenders' (see `readProviderCredential`). No silent env fallback after a failed\n * refresh or for a credential type without a matching handler.\n */\nexport function resolveProviderAuth(\n\tprovider: { id: string; auth: ProviderAuth },\n\tcredentials: CredentialStore,\n\tauthContext: AuthContext,\n\toverrides?: AuthResolutionOverrides,\n): Promise<AuthResult | undefined> {\n\tconst caller = operationSignal(overrides?.signal);\n\tconst timeout = new AbortController();\n\tconst timer = setTimeout(() => {\n\t\ttimeout.abort();\n\t}, REQUEST_AUTH_PREPARATION_TIMEOUT_MS);\n\tconst signal = AbortSignal.any([caller, timeout.signal]);\n\tconst operation = resolveProviderAuthWithSignal(\n\t\tprovider,\n\t\tcredentials,\n\t\tauthContext,\n\t\t{ ...overrides, signal },\n\t\tsignal,\n\t);\n\treturn raceWithAbortSignal(operation, signal)\n\t\t.catch((error: unknown) => {\n\t\t\tif (timeout.signal.aborted && !caller.aborted) {\n\t\t\t\tthrow new ModelsError(\"auth\", requestAuthTimeoutMessage(provider.id));\n\t\t\t}\n\t\t\tthrow error;\n\t\t})\n\t\t.finally(() => {\n\t\t\tclearTimeout(timer);\n\t\t});\n}\n\nasync function resolveProviderAuthWithSignal(\n\tprovider: { id: string; auth: ProviderAuth },\n\tcredentials: CredentialStore,\n\tauthContext: AuthContext,\n\toverrides: AuthResolutionOverrides | undefined,\n\tsignal: AbortSignal,\n): Promise<AuthResult | undefined> {\n\tsignal.throwIfAborted();\n\tconst requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;\n\n\tif (overrides?.apiKey !== undefined && provider.auth.apiKey) {\n\t\treturn resolveApiKey(\n\t\t\trequestAuthContext,\n\t\t\tprovider.auth.apiKey,\n\t\t\tprovider.id,\n\t\t\t{\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey: overrides.apiKey,\n\t\t\t\tenv: overrides.env,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tconst stored = await readProviderCredential(credentials, provider, signal);\n\tif (stored) {\n\t\tif (stored.type === \"oauth\" && provider.auth.oauth) {\n\t\t\treturn resolveStoredOAuth(\n\t\t\t\tcredentials,\n\t\t\t\tprovider.id,\n\t\t\t\tprovider.auth.oauth,\n\t\t\t\tstored,\n\t\t\t\tsignal,\n\t\t\t\toverrides?.minOAuthValidityMs,\n\t\t\t);\n\t\t}\n\t\tif (stored.type === \"api_key\" && provider.auth.apiKey) {\n\t\t\tconst credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;\n\t\t\treturn resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, credential, signal);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// Ambient (env vars, AWS profiles, ADC files).\n\treturn provider.auth.apiKey\n\t\t? resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, undefined, signal)\n\t\t: undefined;\n}\n\nfunction overlayEnvAuthContext(base: AuthContext, env: ProviderEnv): AuthContext {\n\treturn {\n\t\tenv: async (name) => env[name] || (await base.env(name)),\n\t\tfileExists: (path) => base.fileExists(path),\n\t};\n}\n\nconst DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;\nconst DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 60_000;\n\n/** Cancellation stops waiting for the credential lock, not an already started token rotation. */\nexport async function refreshStoredOAuthCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\toauth: OAuthAuth,\n\tneedsRefresh: (credential: OAuthCredential) => boolean,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential | undefined> {\n\tlet post: Credential | undefined;\n\tconst lockWait = new AbortController();\n\tconst cancelLockWait = () => lockWait.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", cancelLockWait, { once: true });\n\tif (signal.aborted) cancelLockWait();\n\ttry {\n\t\tpost = await credentials.modify(\n\t\t\tproviderId,\n\t\t\tasync (current) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", cancelLockWait);\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t\tif (current?.type !== \"oauth\") return undefined;\n\t\t\t\tif (!needsRefresh(current)) return undefined;\n\t\t\t\ttry {\n\t\t\t\t\tconst refreshSignal = AbortSignal.timeout(DEFAULT_OAUTH_REFRESH_TIMEOUT_MS);\n\t\t\t\t\treturn await raceWithAbortSignal(oauth.refresh(current, refreshSignal), refreshSignal);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new ModelsError(\"oauth\", `OAuth refresh failed for ${providerId}`, { cause: error });\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ signal: lockWait.signal },\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof ModelsError) throw error;\n\t\tsignal.throwIfAborted();\n\t\tthrow new ModelsError(\"auth\", `Credential store modify failed for ${providerId}`, { cause: error });\n\t} finally {\n\t\tsignal.removeEventListener(\"abort\", cancelLockWait);\n\t}\n\treturn post?.type === \"oauth\" ? post : undefined;\n}\n\n/** Refresh expiring tokens under the credential-store lock and persist rotation before releasing it. */\nasync function resolveStoredOAuth(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\toauth: OAuthAuth,\n\tstored: OAuthCredential,\n\tsignal: AbortSignal,\n\tminOAuthValidityMs?: number,\n): Promise<AuthResult | undefined> {\n\tconst minimumValidityMs = Math.max(DEFAULT_OAUTH_MINIMUM_VALIDITY_MS, minOAuthValidityMs ?? 0);\n\tconst expiresSoon = (credential: OAuthCredential) => Date.now() + minimumValidityMs >= credential.expires;\n\tlet credential = stored;\n\n\tif (expiresSoon(credential)) {\n\t\t// Optimistic check said expired; the authoritative check runs under the lock.\n\t\tconst post = await refreshStoredOAuthCredential(credentials, providerId, oauth, expiresSoon, signal);\n\t\tif (!post) return undefined;\n\t\tcredential = post;\n\t\t// The normal five-minute window triggers a refresh but does not impose a\n\t\t// provider contract. Explicit callers (such as bearer-token export) do\n\t\t// require the requested minimum after the refresh.\n\t\tif (minOAuthValidityMs !== undefined && expiresSoon(credential)) {\n\t\t\tthrow new ModelsError(\"oauth\", `OAuth refresh returned a token that expires too soon for ${providerId}`);\n\t\t}\n\t}\n\n\ttry {\n\t\treturn { auth: await oauth.toAuth(credential), source: \"OAuth\" };\n\t} catch (error) {\n\t\tthrow new ModelsError(\"oauth\", `OAuth auth derivation failed for ${providerId}`, { cause: error });\n\t}\n}\n\nasync function resolveApiKey(\n\tauthContext: AuthContext,\n\tapiKey: ApiKeyAuth,\n\tproviderId: string,\n\tcredential: ApiKeyCredential | undefined,\n\tsignal: AbortSignal,\n): Promise<AuthResult | undefined> {\n\ttry {\n\t\treturn await apiKey.resolve({ ctx: authContext, credential, signal });\n\t} catch (error) {\n\t\tthrow new ModelsError(\"auth\", `API key auth failed for provider ${providerId}`, { cause: error });\n\t}\n}\n\n/**\n * The stored credential that supplies auth for `provider`. A provider that\n * declares `apiKey.borrowCredentialsFrom` has no credential of its own: it\n * uses the first usable api-key credential among those providers. Any other\n * provider uses its own. Shared by request auth and availability checks so\n * both agree on which credential applies.\n */\nexport async function readProviderCredential(\n\tcredentials: CredentialStore,\n\tprovider: { id: string; auth: ProviderAuth },\n\tsignal: AbortSignal,\n): Promise<Credential | undefined> {\n\tconst lenders = provider.auth.apiKey?.borrowCredentialsFrom;\n\tif (!lenders) return readCredential(credentials, provider.id, signal);\n\tfor (const sourceId of lenders) {\n\t\tconst borrowed = await readCredential(credentials, normalizeProviderId(sourceId), signal);\n\t\tif (borrowed?.type === \"api_key\" && borrowed.key) return borrowed;\n\t}\n\treturn undefined;\n}\n\nasync function readCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\tsignal: AbortSignal,\n): Promise<Credential | undefined> {\n\ttry {\n\t\tconst credential = await credentials.read(providerId, { signal });\n\t\treturn (\n\t\t\tcredential ??\n\t\t\t(providerId === \"azure\" ? await credentials.read(\"azure-openai-responses\", { signal }) : undefined)\n\t\t);\n\t} catch (error) {\n\t\tthrow new ModelsError(\"auth\", `Credential store read failed for ${providerId}`, { cause: error });\n\t}\n}\n"]}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/auth/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeProviderId } from \"../provider-id.ts\";\nimport type { ProviderEnv } from \"../types.ts\";\nimport { operationSignal, raceWithAbortSignal } from \"../utils/abort.ts\";\nimport { formatThrownValue } from \"../utils/diagnostics.ts\";\nimport { ModelsError } from \"../utils/models-error.ts\";\nimport {\n\tfindPermanentRefreshRejectionInText,\n\tisOAuthRefreshRejectedError,\n\tsafeOAuthErrorCode,\n\tsafeOAuthErrorDescription,\n} from \"./oauth-refresh-errors.ts\";\n\nexport { ModelsError, type ModelsErrorCode } from \"../utils/models-error.ts\";\n\nimport type {\n\tApiKeyAuth,\n\tApiKeyCredential,\n\tAuthContext,\n\tAuthResult,\n\tCredential,\n\tCredentialStore,\n\tOAuthAuth,\n\tOAuthCredential,\n\tProviderAuth,\n} from \"./types.ts\";\n\n/** Bound for pre-transport request-auth work: credential read, OAuth refresh, and toAuth. */\nexport const REQUEST_AUTH_PREPARATION_TIMEOUT_MS = 15_000;\n\n/** Source-neutral diagnostic when pre-transport request auth exceeds {@link REQUEST_AUTH_PREPARATION_TIMEOUT_MS}. */\nexport function requestAuthTimeoutMessage(providerId: string): string {\n\treturn `Request authentication timed out for ${providerId}. Check the provider's credential source and try again.`;\n}\n\nexport interface AuthResolutionOverrides {\n\tapiKey?: string;\n\tenv?: ProviderEnv;\n\t/** Require this much remaining OAuth-token validity; defaults to five minutes. */\n\tminOAuthValidityMs?: number;\n\tsignal?: AbortSignal;\n}\n\n/**\n * Auth resolution shared by all operations in a `Models` collection.\n * A stored credential owns the provider: ambient/env is consulted only when\n * nothing is stored. A provider that borrows credentials reads only its\n * lenders' (see `readProviderCredential`). No silent env fallback after a failed\n * refresh or for a credential type without a matching handler.\n */\nexport function resolveProviderAuth(\n\tprovider: { id: string; auth: ProviderAuth },\n\tcredentials: CredentialStore,\n\tauthContext: AuthContext,\n\toverrides?: AuthResolutionOverrides,\n): Promise<AuthResult | undefined> {\n\tconst caller = operationSignal(overrides?.signal);\n\tconst timeout = new AbortController();\n\tconst timer = setTimeout(() => {\n\t\ttimeout.abort();\n\t}, REQUEST_AUTH_PREPARATION_TIMEOUT_MS);\n\tconst signal = AbortSignal.any([caller, timeout.signal]);\n\tconst operation = resolveProviderAuthWithSignal(\n\t\tprovider,\n\t\tcredentials,\n\t\tauthContext,\n\t\t{ ...overrides, signal },\n\t\tsignal,\n\t);\n\treturn raceWithAbortSignal(operation, signal)\n\t\t.catch((error: unknown) => {\n\t\t\tif (timeout.signal.aborted && !caller.aborted) {\n\t\t\t\tthrow new ModelsError(\"auth\", requestAuthTimeoutMessage(provider.id));\n\t\t\t}\n\t\t\tthrow error;\n\t\t})\n\t\t.finally(() => {\n\t\t\tclearTimeout(timer);\n\t\t});\n}\n\nasync function resolveProviderAuthWithSignal(\n\tprovider: { id: string; auth: ProviderAuth },\n\tcredentials: CredentialStore,\n\tauthContext: AuthContext,\n\toverrides: AuthResolutionOverrides | undefined,\n\tsignal: AbortSignal,\n): Promise<AuthResult | undefined> {\n\tsignal.throwIfAborted();\n\tconst requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;\n\n\tif (overrides?.apiKey !== undefined && provider.auth.apiKey) {\n\t\treturn resolveApiKey(\n\t\t\trequestAuthContext,\n\t\t\tprovider.auth.apiKey,\n\t\t\tprovider.id,\n\t\t\t{\n\t\t\t\ttype: \"api_key\",\n\t\t\t\tkey: overrides.apiKey,\n\t\t\t\tenv: overrides.env,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\tconst stored = await readProviderCredential(credentials, provider, signal);\n\tif (stored) {\n\t\tif (stored.type === \"oauth\" && provider.auth.oauth) {\n\t\t\treturn resolveStoredOAuth(\n\t\t\t\tcredentials,\n\t\t\t\tprovider.id,\n\t\t\t\tprovider.auth.oauth,\n\t\t\t\tstored,\n\t\t\t\tsignal,\n\t\t\t\toverrides?.minOAuthValidityMs,\n\t\t\t);\n\t\t}\n\t\tif (stored.type === \"api_key\" && provider.auth.apiKey) {\n\t\t\tconst credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;\n\t\t\treturn resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, credential, signal);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// Ambient (env vars, AWS profiles, ADC files).\n\treturn provider.auth.apiKey\n\t\t? resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, undefined, signal)\n\t\t: undefined;\n}\n\nfunction overlayEnvAuthContext(base: AuthContext, env: ProviderEnv): AuthContext {\n\treturn {\n\t\tenv: async (name) => env[name] || (await base.env(name)),\n\t\tfileExists: (path) => base.fileExists(path),\n\t};\n}\n\nconst DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;\n/** How long a refresh keeps its caller waiting for one token exchange. */\nexport const OAUTH_REFRESH_TIMEOUT_MS = 60_000;\n\n/**\n * How long a refresh keeps the credential lock while its token exchange is still running, so no other refresh\n * presents a token the exchange may already have rotated. A response after this is still persisted, lock-free.\n */\nexport const OAUTH_REFRESH_LOCK_HOLD_MS = 2 * OAUTH_REFRESH_TIMEOUT_MS;\n\n/**\n * Signal handed to a provider's token exchange. Nothing aborts it: once sent, the provider may already have\n * rotated the refresh token, so only the exchange's own response can tell us the replacement.\n */\nconst TOKEN_EXCHANGE_SIGNAL = new AbortController().signal;\n\n/**\n * How long a graceful shutdown waits for token exchanges still in flight, and for persisting their results, before\n * the process exits (see {@link drainPendingOAuthRotations}).\n *\n * Abrupt termination (SIGKILL, a crash, power loss) cannot wait. If the provider already rotated the refresh token\n * when that happens, its response never reaches the process and the stored login keeps the replaced token; the\n * next refresh then fails and `/login <provider>` signs in again.\n */\nexport const OAUTH_SHUTDOWN_DRAIN_MS = 3_000;\n\n/** One entry per started token exchange, settled once its result is persisted or given up on. */\nconst pendingTokenExchanges = new Set<Promise<void>>();\n\nfunction trackTokenExchange(): () => void {\n\tlet settle!: () => void;\n\tconst pending = new Promise<void>((resolve) => {\n\t\tsettle = resolve;\n\t});\n\tpendingTokenExchanges.add(pending);\n\treturn () => {\n\t\tpendingTokenExchanges.delete(pending);\n\t\tsettle();\n\t};\n}\n\n/**\n * Waits for every started OAuth token exchange to settle and its result to be persisted, at most `timeoutMs`.\n * Callers stop waiting for an exchange after {@link OAUTH_REFRESH_TIMEOUT_MS} while it keeps running, so a process\n * that exits gracefully calls this first: the provider may already have rotated the token. Resolves `true` once none\n * is in flight, `false` when the bound passes first; never rejects.\n */\nexport async function drainPendingOAuthRotations(timeoutMs: number = OAUTH_SHUTDOWN_DRAIN_MS): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (pendingTokenExchanges.size > 0) {\n\t\tconst remainingMs = deadline - Date.now();\n\t\tif (remainingMs <= 0) return false;\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst expired = new Promise<void>((resolve) => {\n\t\t\ttimer = setTimeout(resolve, remainingMs);\n\t\t});\n\t\ttry {\n\t\t\tawait Promise.race([Promise.all(pendingTokenExchanges), expired]);\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\treturn true;\n}\n\nconst MAX_ERROR_CHAIN_DEPTH = 5;\n\n/**\n * The provider's error code (or HTTP status) and description for a refresh it rejected because the stored login is\n * revoked or expired: an {@link OAuthRefreshRejectedError} anywhere in the cause chain, or a permanent OAuth error\n * code in a custom refresher's message. Never request bodies, URLs, or tokens.\n */\nfunction describeRejectedLogin(error: unknown, presented: OAuthCredential): string | undefined {\n\tconst secrets = [presented.refresh, presented.access];\n\tconst describe = (code: string, description: string | undefined) => {\n\t\tconst safeDescription = safeOAuthErrorDescription(description, secrets);\n\t\treturn safeDescription ? `${code} (${safeDescription})` : code;\n\t};\n\tlet current = error;\n\tfor (let depth = 0; depth < MAX_ERROR_CHAIN_DEPTH && current !== undefined && current !== null; depth++) {\n\t\tif (current instanceof Error && isOAuthRefreshRejectedError(current)) {\n\t\t\treturn describe(safeOAuthErrorCode(current.code) ?? `HTTP ${current.status}`, current.description);\n\t\t}\n\t\tcurrent = current instanceof Error ? current.cause : undefined;\n\t}\n\tcurrent = error;\n\tfor (let depth = 0; depth < MAX_ERROR_CHAIN_DEPTH && current !== undefined && current !== null; depth++) {\n\t\tconst rejection = findPermanentRefreshRejectionInText(formatThrownValue(current));\n\t\tconst code = safeOAuthErrorCode(rejection?.code);\n\t\tif (code !== undefined) return describe(code, rejection?.description);\n\t\tcurrent = current instanceof Error ? current.cause : undefined;\n\t}\n\treturn undefined;\n}\n\n/**\n * Per credential store and provider, why the last late rotation could not be saved: only the save error's `code` or\n * `name`, never its message, which may contain paths or tokens. Keyed by store so one store's lost save is never\n * reported against another's login. Cleared by a later save or rotation, or once a refresh failure reports it.\n */\nconst unsavedLateRotations = new WeakMap<CredentialStore, Map<string, string>>();\n\nfunction unsavedLateRotationsFor(credentials: CredentialStore): Map<string, string> {\n\tlet byProvider = unsavedLateRotations.get(credentials);\n\tif (byProvider === undefined) {\n\t\tbyProvider = new Map();\n\t\tunsavedLateRotations.set(credentials, byProvider);\n\t}\n\treturn byProvider;\n}\n\nfunction safeSaveErrorReason(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst code = \"code\" in error ? error.code : undefined;\n\t\treturn typeof code === \"string\" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code) ? code : error.name;\n\t}\n\treturn \"unknown error\";\n}\n\n/** The sentence explaining a lost late rotation for `providerId` in `credentials`, reported once. */\nfunction takeUnsavedLateRotationNote(credentials: CredentialStore, providerId: string): string {\n\tconst byProvider = unsavedLateRotations.get(credentials);\n\tconst reason = byProvider?.get(providerId);\n\tif (reason === undefined) return \"\";\n\tbyProvider?.delete(providerId);\n\treturn ` An earlier refresh for ${providerId} succeeded, but its new login could not be saved (${reason}).`;\n}\n\nfunction oauthRefreshFailure(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\terror: unknown,\n\tpresented: OAuthCredential,\n): ModelsError {\n\tconst rejectedLogin = describeRejectedLogin(error, presented);\n\tconst unsavedNote = takeUnsavedLateRotationNote(credentials, providerId);\n\tif (rejectedLogin === undefined) {\n\t\tconst message = unsavedNote\n\t\t\t? `OAuth refresh failed for ${providerId}.${unsavedNote}`\n\t\t\t: `OAuth refresh failed for ${providerId}`;\n\t\treturn new ModelsError(\"oauth\", message, { cause: error });\n\t}\n\treturn new ModelsError(\n\t\t\"oauth\",\n\t\t`OAuth refresh failed for ${providerId}: ${rejectedLogin}. The provider rejected the stored login; run /login ${providerId} to sign in again.${unsavedNote}`,\n\t);\n}\n\n/** A token exchange that settles after its timeout already rotated the token server-side, so keep its result. */\nfunction persistLateRotation(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\trotatedFrom: OAuthCredential,\n\trotation: Promise<OAuthCredential>,\n): void {\n\tconst settled = trackTokenExchange();\n\tvoid rotation\n\t\t.then(\n\t\t\t(late) =>\n\t\t\t\tcredentials\n\t\t\t\t\t.modify(providerId, async (current) =>\n\t\t\t\t\t\tcurrent?.type === \"oauth\" && current.refresh === rotatedFrom.refresh ? late : undefined,\n\t\t\t\t\t)\n\t\t\t\t\t.then(\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tunsavedLateRotations.get(credentials)?.delete(providerId);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(saveError: unknown) => {\n\t\t\t\t\t\t\tunsavedLateRotationsFor(credentials).set(providerId, safeSaveErrorReason(saveError));\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t// The exchange itself failed: nothing was rotated that could be lost.\n\t\t\t() => undefined,\n\t\t)\n\t\t.finally(settled);\n}\n\n/**\n * Runs one token exchange under the credential lock. The caller stops waiting after {@link OAUTH_REFRESH_TIMEOUT_MS};\n * the lock is kept until the exchange settles or {@link OAUTH_REFRESH_LOCK_HOLD_MS} passes, after which a late\n * response is still persisted.\n */\nasync function rotateOAuthCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\toauth: OAuthAuth,\n\tcurrent: OAuthCredential,\n\tstopCallerWaiting: (reason: Error) => void,\n): Promise<OAuthCredential> {\n\tconst rotation = oauth.refresh(current, TOKEN_EXCHANGE_SIGNAL);\n\tconst callerWait = AbortSignal.timeout(OAUTH_REFRESH_TIMEOUT_MS);\n\tconst onCallerWaitExpired = () => {\n\t\tstopCallerWaiting(\n\t\t\tnew ModelsError(\"oauth\", `OAuth refresh failed for ${providerId}`, { cause: callerWait.reason }),\n\t\t);\n\t};\n\tcallerWait.addEventListener(\"abort\", onCallerWaitExpired, { once: true });\n\tconst lockHold = AbortSignal.timeout(OAUTH_REFRESH_LOCK_HOLD_MS);\n\ttry {\n\t\treturn await raceWithAbortSignal(rotation, lockHold);\n\t} catch (error) {\n\t\tif (lockHold.aborted) persistLateRotation(credentials, providerId, current, rotation);\n\t\tthrow error;\n\t} finally {\n\t\tcallerWait.removeEventListener(\"abort\", onCallerWaitExpired);\n\t}\n}\n\n/** A login another process saved after this refresh read the store, e.g. its own rotation of the same token. */\nasync function readNewerOAuthCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\trejected: OAuthCredential,\n): Promise<OAuthCredential | undefined> {\n\tconst latest = await credentials.read(providerId).catch(() => undefined);\n\treturn latest?.type === \"oauth\" && latest.refresh !== rejected.refresh ? latest : undefined;\n}\n\n/** Cancellation stops waiting for the credential lock, not an already started token rotation. */\nexport async function refreshStoredOAuthCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\toauth: OAuthAuth,\n\tneedsRefresh: (credential: OAuthCredential) => boolean,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential | undefined> {\n\tlet post: Credential | undefined;\n\tconst lockWait = new AbortController();\n\tconst cancelLockWait = () => lockWait.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", cancelLockWait, { once: true });\n\tif (signal.aborted) cancelLockWait();\n\tlet stopCallerWaiting!: (reason: Error) => void;\n\t/** Set once the token exchange starts; settles its pending entry when the transaction that commits it ends. */\n\tlet exchangeSettled: (() => void) | undefined;\n\tconst callerWaitExpired = new Promise<never>((_, reject) => {\n\t\tstopCallerWaiting = reject;\n\t});\n\ttry {\n\t\tconst transaction = credentials.modify(\n\t\t\tproviderId,\n\t\t\tasync (current) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", cancelLockWait);\n\t\t\t\tsignal.throwIfAborted();\n\t\t\t\tif (current?.type !== \"oauth\") return undefined;\n\t\t\t\tif (!needsRefresh(current)) return undefined;\n\t\t\t\ttry {\n\t\t\t\t\texchangeSettled ??= trackTokenExchange();\n\t\t\t\t\tconst rotated = await rotateOAuthCredential(credentials, providerId, oauth, current, stopCallerWaiting);\n\t\t\t\t\tunsavedLateRotations.get(credentials)?.delete(providerId);\n\t\t\t\t\treturn rotated;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst newer = await readNewerOAuthCredential(credentials, providerId, current);\n\t\t\t\t\tif (newer) return newer;\n\t\t\t\t\tthrow oauthRefreshFailure(credentials, providerId, error, current);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ signal: lockWait.signal },\n\t\t);\n\t\tconst untrackExchange = () => exchangeSettled?.();\n\t\tvoid transaction.then(untrackExchange, untrackExchange);\n\t\tpost = await Promise.race([transaction, callerWaitExpired]);\n\t} catch (error) {\n\t\tif (error instanceof ModelsError) throw error;\n\t\tsignal.throwIfAborted();\n\t\tthrow new ModelsError(\"auth\", `Credential store modify failed for ${providerId}`, { cause: error });\n\t} finally {\n\t\tsignal.removeEventListener(\"abort\", cancelLockWait);\n\t}\n\treturn post?.type === \"oauth\" ? post : undefined;\n}\n\n/** Refresh expiring tokens under the credential-store lock and persist rotation before releasing it. */\nasync function resolveStoredOAuth(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\toauth: OAuthAuth,\n\tstored: OAuthCredential,\n\tsignal: AbortSignal,\n\tminOAuthValidityMs?: number,\n): Promise<AuthResult | undefined> {\n\tconst minimumValidityMs = Math.max(DEFAULT_OAUTH_MINIMUM_VALIDITY_MS, minOAuthValidityMs ?? 0);\n\tconst expiresSoon = (credential: OAuthCredential) => Date.now() + minimumValidityMs >= credential.expires;\n\tlet credential = stored;\n\n\tif (expiresSoon(credential)) {\n\t\t// Optimistic check said expired; the authoritative check runs under the lock.\n\t\tconst post = await refreshStoredOAuthCredential(credentials, providerId, oauth, expiresSoon, signal);\n\t\tif (!post) return undefined;\n\t\tcredential = post;\n\t\t// The normal five-minute window triggers a refresh but does not impose a\n\t\t// provider contract. Explicit callers (such as bearer-token export) do\n\t\t// require the requested minimum after the refresh.\n\t\tif (minOAuthValidityMs !== undefined && expiresSoon(credential)) {\n\t\t\tthrow new ModelsError(\"oauth\", `OAuth refresh returned a token that expires too soon for ${providerId}`);\n\t\t}\n\t}\n\n\ttry {\n\t\treturn { auth: await oauth.toAuth(credential), source: \"OAuth\" };\n\t} catch (error) {\n\t\tthrow new ModelsError(\"oauth\", `OAuth auth derivation failed for ${providerId}`, { cause: error });\n\t}\n}\n\nasync function resolveApiKey(\n\tauthContext: AuthContext,\n\tapiKey: ApiKeyAuth,\n\tproviderId: string,\n\tcredential: ApiKeyCredential | undefined,\n\tsignal: AbortSignal,\n): Promise<AuthResult | undefined> {\n\ttry {\n\t\treturn await apiKey.resolve({ ctx: authContext, credential, signal });\n\t} catch (error) {\n\t\tthrow new ModelsError(\"auth\", `API key auth failed for provider ${providerId}`, { cause: error });\n\t}\n}\n\n/**\n * The stored credential that supplies auth for `provider`. A provider that\n * declares `apiKey.borrowCredentialsFrom` has no credential of its own: it\n * uses the first usable api-key credential among those providers. Any other\n * provider uses its own. Shared by request auth and availability checks so\n * both agree on which credential applies.\n */\nexport async function readProviderCredential(\n\tcredentials: CredentialStore,\n\tprovider: { id: string; auth: ProviderAuth },\n\tsignal: AbortSignal,\n): Promise<Credential | undefined> {\n\tconst lenders = provider.auth.apiKey?.borrowCredentialsFrom;\n\tif (!lenders) return readCredential(credentials, provider.id, signal);\n\tfor (const sourceId of lenders) {\n\t\tconst borrowed = await readCredential(credentials, normalizeProviderId(sourceId), signal);\n\t\tif (borrowed?.type === \"api_key\" && borrowed.key) return borrowed;\n\t}\n\treturn undefined;\n}\n\nasync function readCredential(\n\tcredentials: CredentialStore,\n\tproviderId: string,\n\tsignal: AbortSignal,\n): Promise<Credential | undefined> {\n\ttry {\n\t\tconst credential = await credentials.read(providerId, { signal });\n\t\treturn (\n\t\t\tcredential ??\n\t\t\t(providerId === \"azure\" ? await credentials.read(\"azure-openai-responses\", { signal }) : undefined)\n\t\t);\n\t} catch (error) {\n\t\tthrow new ModelsError(\"auth\", `Credential store read failed for ${providerId}`, { cause: error });\n\t}\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ export type { PiMessagesEvent, PiMessagesOptions, PiMessagesRewriteImpact } from
16
16
  export * from "./auth/context.ts";
17
17
  export * from "./auth/credential-store.ts";
18
18
  export * from "./auth/helpers.ts";
19
+ export { isOAuthRefreshRejectedError, isPermanentRefreshRejection, type OAuthErrorDetail, OAuthRefreshRejectedError, type OAuthRefreshRejection, parseOAuthErrorBody, } from "./auth/oauth-refresh-errors.ts";
19
20
  export * from "./auth/types.ts";
20
21
  export type { OAuthAuthInfo, OAuthDeviceCodeInfo, OAuthLoginCallbacks, OAuthPrompt, OAuthSelectOption, OAuthSelectPrompt, } from "./compat/extension-oauth-types.ts";
21
22
  export * from "./decision-models.ts";
@@ -29,6 +30,7 @@ export * from "./types.ts";
29
30
  export * from "./utils/diagnostics.ts";
30
31
  export * from "./utils/event-stream.ts";
31
32
  export { AssistantMessageEventStream } from "./utils/event-stream.ts";
33
+ export * from "./utils/identity.ts";
32
34
  export * from "./utils/json-parse.ts";
33
35
  export * from "./utils/overflow.ts";
34
36
  export * from "./utils/retry.ts";
@@ -38,4 +40,5 @@ export * from "./utils/transcript.ts";
38
40
  export * from "./utils/typebox-helpers.ts";
39
41
  export { uuidv7 } from "./utils/uuid.ts";
40
42
  export * from "./utils/validation.ts";
43
+ export { PI_AI_VERSION } from "./utils/version.ts";
41
44
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAO/B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC/G,YAAY,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AACnF,YAAY,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/F,YAAY,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAClG,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,YAAY,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACnH,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACxG,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,YAAY,EACX,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,mCAAmC,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC/F,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAO/B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC/G,YAAY,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AACnF,YAAY,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/F,YAAY,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAClG,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,YAAY,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACnH,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACxG,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACN,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,gBAAgB,EACrB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,mBAAmB,GACnB,MAAM,gCAAgC,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,YAAY,EACX,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,mCAAmC,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC/F,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export * from "./api/lazy.js";
3
3
  export * from "./auth/context.js";
4
4
  export * from "./auth/credential-store.js";
5
5
  export * from "./auth/helpers.js";
6
+ export { isOAuthRefreshRejectedError, isPermanentRefreshRejection, OAuthRefreshRejectedError, parseOAuthErrorBody, } from "./auth/oauth-refresh-errors.js";
6
7
  export * from "./auth/types.js";
7
8
  export * from "./decision-models.js";
8
9
  export { containsKnownEnvCredential } from "./env-api-keys.js";
@@ -15,6 +16,7 @@ export * from "./types.js";
15
16
  export * from "./utils/diagnostics.js";
16
17
  export * from "./utils/event-stream.js";
17
18
  export { AssistantMessageEventStream } from "./utils/event-stream.js";
19
+ export * from "./utils/identity.js";
18
20
  export * from "./utils/json-parse.js";
19
21
  export * from "./utils/overflow.js";
20
22
  export * from "./utils/retry.js";
@@ -24,4 +26,5 @@ export * from "./utils/transcript.js";
24
26
  export * from "./utils/typebox-helpers.js";
25
27
  export { uuidv7 } from "./utils/uuid.js";
26
28
  export * from "./utils/validation.js";
29
+ export { PI_AI_VERSION } from "./utils/version.js";
27
30
  //# sourceMappingURL=index.js.map