@primitive.ai/prim 0.1.0-alpha.9 → 0.1.0-alpha.91

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 (47) hide show
  1. package/README.md +303 -37
  2. package/SKILL.md +269 -0
  3. package/dist/chunk-4GLVJLEQ.js +55 -0
  4. package/dist/chunk-5AFRMOPX.js +209 -0
  5. package/dist/chunk-5GYQ4J3D.js +126 -0
  6. package/dist/chunk-67VNLLBN.js +789 -0
  7. package/dist/chunk-BTNNEH2W.js +79 -0
  8. package/dist/chunk-EWZMDD44.js +1474 -0
  9. package/dist/chunk-EZXEWFDG.js +210 -0
  10. package/dist/chunk-FBAYCS3X.js +327 -0
  11. package/dist/chunk-FKXFUUWJ.js +36 -0
  12. package/dist/chunk-FOLKHXLJ.js +1529 -0
  13. package/dist/chunk-H4UNYFUC.js +196 -0
  14. package/dist/chunk-HU4PW6I6.js +264 -0
  15. package/dist/chunk-KFXF2V5G.js +155 -0
  16. package/dist/chunk-LFFLHSQO.js +78 -0
  17. package/dist/chunk-M6RIJVRE.js +18358 -0
  18. package/dist/chunk-NAHHFSLH.js +258 -0
  19. package/dist/chunk-OAAP2MY6.js +262 -0
  20. package/dist/chunk-PCVGBE5B.js +659 -0
  21. package/dist/chunk-VW3FDYRT.js +517 -0
  22. package/dist/chunk-WX52U7Z2.js +112 -0
  23. package/dist/chunk-ZB4F75SZ.js +843 -0
  24. package/dist/daemon/server.js +21633 -0
  25. package/dist/hook-runtime/daemon/server.js +21633 -0
  26. package/dist/hook-runtime/hooks/post-commit.js +19258 -0
  27. package/dist/hook-runtime/hooks/post-rewrite.js +19273 -0
  28. package/dist/hook-runtime/hooks/post-tool-use.js +26365 -0
  29. package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
  30. package/dist/hook-runtime/hooks/pre-tool-use.js +25832 -0
  31. package/dist/hook-runtime/hooks/prim-hook.js +26386 -0
  32. package/dist/hook-runtime/hooks/session-end.js +1078 -0
  33. package/dist/hook-runtime/hooks/session-start.js +28779 -0
  34. package/dist/hook-runtime/index.js +50987 -0
  35. package/dist/hook-runtime/statusline-main.js +809 -0
  36. package/dist/hooks/post-commit.js +190 -0
  37. package/dist/hooks/post-rewrite.js +203 -0
  38. package/dist/hooks/post-tool-use.js +288 -0
  39. package/dist/hooks/pre-commit.js +16 -101
  40. package/dist/hooks/pre-tool-use.js +369 -0
  41. package/dist/hooks/prim-hook.js +229 -0
  42. package/dist/hooks/session-end.js +59 -0
  43. package/dist/hooks/session-start.js +316 -0
  44. package/dist/index.js +8875 -429
  45. package/dist/statusline-main.js +809 -0
  46. package/package.json +35 -13
  47. package/dist/chunk-3APLWTLB.js +0 -165
@@ -0,0 +1,659 @@
1
+ import {
2
+ CREDENTIAL_FAMILY_PATH,
3
+ CREDENTIAL_LOCK_PATH,
4
+ CREDENTIAL_METADATA_PATH,
5
+ CREDENTIAL_MIGRATION_PATH,
6
+ CREDENTIAL_MIGRATION_STATE,
7
+ CREDENTIAL_MIGRATION_VERSION,
8
+ REFRESH_TOKEN_PATH,
9
+ TERMINAL_REFRESH_PATH,
10
+ TOKEN_EXPIRES_PATH,
11
+ TOKEN_FILE_PATH,
12
+ WORKOS_CONNECT_RESPONSE_MAX_BYTES,
13
+ atomicWriteFile,
14
+ jwtExpiresAt,
15
+ parseWorkosConnectTokens,
16
+ readBoundedJson,
17
+ readCredentialMigrationState,
18
+ readStoredCredentialMetadata,
19
+ resolveAuthCredential,
20
+ withFileLock,
21
+ workosConnectTokenEndpoint
22
+ } from "./chunk-67VNLLBN.js";
23
+
24
+ // src/lib/terminal-safe.ts
25
+ function replaceIsolatedSurrogates(value) {
26
+ let out = "";
27
+ for (let index = 0; index < value.length; index += 1) {
28
+ const unit = value.charCodeAt(index);
29
+ if (unit >= 55296 && unit <= 56319) {
30
+ const next = value.charCodeAt(index + 1);
31
+ if (next >= 56320 && next <= 57343) {
32
+ out += value[index] + value[index + 1];
33
+ index += 1;
34
+ } else {
35
+ out += "\uFFFD";
36
+ }
37
+ } else if (unit >= 56320 && unit <= 57343) {
38
+ out += "\uFFFD";
39
+ } else {
40
+ out += value[index];
41
+ }
42
+ }
43
+ return out;
44
+ }
45
+ function isUnsafeTerminalCodePoint(point) {
46
+ const c0OrC1 = point <= 31 || point >= 127 && point <= 159;
47
+ const bidiOrZeroWidth = point === 173 || // soft hyphen
48
+ point === 1564 || // Arabic letter mark
49
+ point === 6158 || // Mongolian vowel separator
50
+ point >= 8203 && point <= 8207 || // zero-width + directional marks
51
+ point >= 8234 && point <= 8238 || // bidi embeddings/overrides
52
+ point >= 8288 && point <= 8303 || // word joiner + bidi isolates/controls
53
+ point >= 65529 && point <= 65531 || // interlinear annotation controls
54
+ point === 65279 || // zero-width no-break space / BOM
55
+ point >= 917504 && point <= 917631;
56
+ return c0OrC1 || bidiOrZeroWidth;
57
+ }
58
+ function terminalSafeText(value) {
59
+ return Array.from(replaceIsolatedSurrogates(value)).filter((character) => !isUnsafeTerminalCodePoint(character.codePointAt(0) ?? 0)).join("");
60
+ }
61
+ function terminalSafeLine(value) {
62
+ return terminalSafeText(value.replace(/\s+/gu, " ")).replace(/\s+/gu, " ").trim();
63
+ }
64
+ function isTerminalSafeText(value) {
65
+ return terminalSafeText(value) === value;
66
+ }
67
+ var stripControlChars = terminalSafeText;
68
+
69
+ // src/client.ts
70
+ import { createHash } from "crypto";
71
+ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync } from "fs";
72
+ import { dirname } from "path";
73
+ var CONFIG_DIR_MODE = 448;
74
+ var CREDENTIAL_FILE_MODE = 384;
75
+ var REFRESH_THRESHOLD_MS = 6e4;
76
+ var DEFAULT_API_URL = "https://api.getprimitive.ai";
77
+ var AUTH_EXPIRED_MESSAGE = "Authentication expired. Run `prim auth login` to re-authenticate.";
78
+ function readTrimmed(path) {
79
+ try {
80
+ const value = readFileSync(path, "utf8").trim();
81
+ return value || void 0;
82
+ } catch {
83
+ return void 0;
84
+ }
85
+ }
86
+ function getSiteUrl() {
87
+ return getSiteUrlForEnvironment(process.env.PRIM_API_URL);
88
+ }
89
+ function getSiteUrlForEnvironment(primApiUrl) {
90
+ return primApiUrl?.trim() || DEFAULT_API_URL;
91
+ }
92
+ function expiresAtFor(token, expiresIn) {
93
+ return expiresIn === void 0 ? jwtExpiresAt(token) : Date.now() + expiresIn * 1e3;
94
+ }
95
+ function ensureConfigDirectory() {
96
+ const directory = dirname(TOKEN_FILE_PATH);
97
+ mkdirSync(directory, { recursive: true, mode: CONFIG_DIR_MODE });
98
+ chmodSync(directory, CONFIG_DIR_MODE);
99
+ }
100
+ function atomicWrite(path, content) {
101
+ ensureConfigDirectory();
102
+ atomicWriteFile(path, content, { mode: CREDENTIAL_FILE_MODE });
103
+ }
104
+ function removeCredentialFile(path) {
105
+ if (!existsSync(path)) return false;
106
+ rmSync(path, { force: true });
107
+ return true;
108
+ }
109
+ function getTokenExpiresAt() {
110
+ const stored = readTrimmed(TOKEN_EXPIRES_PATH);
111
+ if (stored === void 0) return void 0;
112
+ const value = Number(stored);
113
+ return Number.isNaN(value) ? void 0 : value;
114
+ }
115
+ function isTokenExpiringSoon(credential) {
116
+ if (credential.source !== "token_file") return false;
117
+ const expiresAt = getTokenExpiresAt();
118
+ return expiresAt !== void 0 && Date.now() >= expiresAt - REFRESH_THRESHOLD_MS;
119
+ }
120
+ function refreshFingerprint(refreshToken2) {
121
+ return createHash("sha256").update(refreshToken2).digest("hex");
122
+ }
123
+ function legacyBrokerCredentialMetadata(accessToken, refreshToken2) {
124
+ return {
125
+ version: 1,
126
+ family: "legacy_broker",
127
+ accessTokenHash: refreshFingerprint(accessToken),
128
+ refreshTokenHash: refreshFingerprint(refreshToken2)
129
+ };
130
+ }
131
+ function credentialFamilyMetadata(accessToken, refreshToken2, metadata) {
132
+ const hashes = {
133
+ accessTokenHash: refreshFingerprint(accessToken),
134
+ refreshTokenHash: refreshFingerprint(refreshToken2)
135
+ };
136
+ return metadata === void 0 ? legacyBrokerCredentialMetadata(accessToken, refreshToken2) : { ...metadata, ...hashes };
137
+ }
138
+ function writeCredentialMigrationSentinel() {
139
+ atomicWrite(
140
+ CREDENTIAL_MIGRATION_PATH,
141
+ `${JSON.stringify({
142
+ version: CREDENTIAL_MIGRATION_VERSION,
143
+ state: CREDENTIAL_MIGRATION_STATE
144
+ })}
145
+ `
146
+ );
147
+ }
148
+ function bindLegacyBrokerGeneration(accessToken, refreshToken2, metadata) {
149
+ if (metadata.state !== "legacy_broker" || !accessToken || !refreshToken2) {
150
+ return { state: "invalid" };
151
+ }
152
+ const familyMetadata = legacyBrokerCredentialMetadata(accessToken, refreshToken2);
153
+ if (metadata.metadata !== void 0 && (metadata.metadata.accessTokenHash !== familyMetadata.accessTokenHash || metadata.metadata.refreshTokenHash !== familyMetadata.refreshTokenHash)) {
154
+ return { state: "invalid" };
155
+ }
156
+ if (metadata.metadata === void 0) {
157
+ atomicWrite(CREDENTIAL_FAMILY_PATH, `${JSON.stringify(familyMetadata)}
158
+ `);
159
+ }
160
+ const migrationState = readCredentialMigrationState();
161
+ if (migrationState === "invalid") return { state: "invalid" };
162
+ if (migrationState === "absent") writeCredentialMigrationSentinel();
163
+ return { state: "legacy_broker", metadata: familyMetadata };
164
+ }
165
+ function preserveMarkerlessLegacyGeneration() {
166
+ const metadata = readStoredCredentialMetadata();
167
+ if (metadata.state !== "legacy_broker" || metadata.metadata !== void 0) return;
168
+ const accessToken = readTrimmed(TOKEN_FILE_PATH);
169
+ const refreshToken2 = readTrimmed(REFRESH_TOKEN_PATH);
170
+ if (!(accessToken && refreshToken2)) return;
171
+ const familyMetadata = legacyBrokerCredentialMetadata(accessToken, refreshToken2);
172
+ atomicWrite(CREDENTIAL_FAMILY_PATH, `${JSON.stringify(familyMetadata)}
173
+ `);
174
+ }
175
+ function terminalFingerprint() {
176
+ return readTrimmed(TERMINAL_REFRESH_PATH);
177
+ }
178
+ function writeTerminalFingerprint(refreshToken2) {
179
+ atomicWrite(TERMINAL_REFRESH_PATH, `${refreshFingerprint(refreshToken2)}
180
+ `);
181
+ }
182
+ function clearTerminalFingerprint() {
183
+ removeCredentialFile(TERMINAL_REFRESH_PATH);
184
+ }
185
+ function isSessionEnded() {
186
+ if (resolveAuthCredential()?.source !== "token_file") return false;
187
+ const refreshToken2 = readTrimmed(REFRESH_TOKEN_PATH);
188
+ const ended = terminalFingerprint();
189
+ return Boolean(refreshToken2 && ended && refreshFingerprint(refreshToken2) === ended);
190
+ }
191
+ function withCredentialLock(operation, options = {}) {
192
+ return withFileLock(CREDENTIAL_LOCK_PATH, operation, options);
193
+ }
194
+ function commitCredentialsUnlocked(credentials) {
195
+ const accessToken = credentials.accessToken.trim();
196
+ const refreshToken2 = credentials.refreshToken.trim();
197
+ if (!accessToken || !refreshToken2) {
198
+ throw new Error("OAuth credentials require both access and refresh tokens");
199
+ }
200
+ const familyMetadata = credentialFamilyMetadata(accessToken, refreshToken2, credentials.metadata);
201
+ if (credentials.metadata !== void 0) {
202
+ preserveMarkerlessLegacyGeneration();
203
+ writeCredentialMigrationSentinel();
204
+ atomicWrite(CREDENTIAL_FAMILY_PATH, `${JSON.stringify(familyMetadata)}
205
+ `);
206
+ atomicWrite(CREDENTIAL_METADATA_PATH, `${JSON.stringify(familyMetadata)}
207
+ `);
208
+ atomicWrite(REFRESH_TOKEN_PATH, `${refreshToken2}
209
+ `);
210
+ } else {
211
+ atomicWrite(REFRESH_TOKEN_PATH, `${refreshToken2}
212
+ `);
213
+ atomicWrite(CREDENTIAL_FAMILY_PATH, `${JSON.stringify(familyMetadata)}
214
+ `);
215
+ removeCredentialFile(CREDENTIAL_METADATA_PATH);
216
+ }
217
+ const expiresAt = expiresAtFor(accessToken, credentials.expiresIn);
218
+ if (expiresAt === void 0) removeCredentialFile(TOKEN_EXPIRES_PATH);
219
+ else atomicWrite(TOKEN_EXPIRES_PATH, `${expiresAt}
220
+ `);
221
+ atomicWrite(TOKEN_FILE_PATH, `${accessToken}
222
+ `);
223
+ clearTerminalFingerprint();
224
+ _cachedCredential = { token: accessToken, source: "token_file" };
225
+ }
226
+ async function commitCredentials(credentials, options = {}) {
227
+ await withCredentialLock(() => commitCredentialsUnlocked(credentials), options);
228
+ }
229
+ async function setStoredToken(token, options = {}) {
230
+ const value = token.trim();
231
+ if (!value) throw new Error("Token cannot be empty");
232
+ await withCredentialLock(() => {
233
+ removeCredentialFile(REFRESH_TOKEN_PATH);
234
+ removeCredentialFile(TOKEN_EXPIRES_PATH);
235
+ removeCredentialFile(CREDENTIAL_FAMILY_PATH);
236
+ removeCredentialFile(CREDENTIAL_METADATA_PATH);
237
+ clearTerminalFingerprint();
238
+ removeCredentialFile(CREDENTIAL_MIGRATION_PATH);
239
+ atomicWrite(TOKEN_FILE_PATH, `${value}
240
+ `);
241
+ _cachedCredential = { token: value, source: "token_file" };
242
+ }, options);
243
+ }
244
+ async function clearStoredCredentials(options = {}) {
245
+ const { beforeClear, ...lockOptions } = options;
246
+ return withCredentialLock(async () => {
247
+ const accessToken = readTrimmed(TOKEN_FILE_PATH);
248
+ const refreshToken2 = readTrimmed(REFRESH_TOKEN_PATH);
249
+ const metadata = readStoredCredentialMetadata();
250
+ let protectedMetadata = metadata;
251
+ if (metadata.state === "legacy_broker") {
252
+ try {
253
+ protectedMetadata = bindLegacyBrokerGeneration(accessToken, refreshToken2, metadata);
254
+ } catch {
255
+ protectedMetadata = { state: "invalid" };
256
+ }
257
+ }
258
+ let callbackError;
259
+ try {
260
+ await beforeClear?.(refreshToken2, protectedMetadata);
261
+ } catch (error) {
262
+ callbackError = error;
263
+ }
264
+ let removed = false;
265
+ for (const path of [
266
+ REFRESH_TOKEN_PATH,
267
+ TOKEN_EXPIRES_PATH,
268
+ CREDENTIAL_FAMILY_PATH,
269
+ CREDENTIAL_METADATA_PATH,
270
+ TERMINAL_REFRESH_PATH,
271
+ TOKEN_FILE_PATH,
272
+ CREDENTIAL_MIGRATION_PATH
273
+ ]) {
274
+ removed = removeCredentialFile(path) || removed;
275
+ }
276
+ _cachedCredential = void 0;
277
+ if (callbackError) throw callbackError;
278
+ return removed;
279
+ }, lockOptions);
280
+ }
281
+ function isTerminalRefreshResponse(response, detail) {
282
+ if (detail.includes("invalid_grant") || detail.includes("Session has already ended")) return true;
283
+ try {
284
+ const error = JSON.parse(detail).error;
285
+ return error === "invalid_grant" || response.status === 401 && error === "Invalid or expired refresh token";
286
+ } catch {
287
+ return response.status === 401 && detail.includes("Invalid or expired refresh token");
288
+ }
289
+ }
290
+ function workosConnectRefreshErrorCode(value) {
291
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return "";
292
+ return value.error === "invalid_grant" ? "invalid_grant" : "";
293
+ }
294
+ function refreshDiagnostic(response, detail, quiet, provider) {
295
+ if (quiet) return;
296
+ const statusText = terminalSafeLine(response.statusText).slice(0, 120);
297
+ const safeDetail = terminalSafeLine(detail).slice(0, 200);
298
+ process.stderr.write(
299
+ `[prim] token refresh rejected by ${provider}: ${response.status}${statusText ? ` ${statusText}` : ""}${safeDetail ? ` \u2014 ${safeDetail}` : ""}
300
+ `
301
+ );
302
+ }
303
+ function metadataMatchesCredentialGeneration(expected, accessToken, refreshToken2) {
304
+ const current = readStoredCredentialMetadata();
305
+ if (expected.state === "legacy_broker") {
306
+ if (current.state !== "legacy_broker") return false;
307
+ if (!expected.metadata) return current.metadata === void 0;
308
+ return current.metadata !== void 0 && current.metadata.accessTokenHash === refreshFingerprint(accessToken) && current.metadata.accessTokenHash === expected.metadata.accessTokenHash && current.metadata.refreshTokenHash === refreshFingerprint(refreshToken2) && current.metadata.refreshTokenHash === expected.metadata.refreshTokenHash;
309
+ }
310
+ if (expected.state !== "workos_connect" || current.state !== "workos_connect") return false;
311
+ return current.metadata.issuer === expected.metadata.issuer && current.metadata.clientId === expected.metadata.clientId && current.metadata.accessTokenHash === refreshFingerprint(accessToken) && current.metadata.accessTokenHash === expected.metadata.accessTokenHash && current.metadata.refreshTokenHash === refreshFingerprint(refreshToken2) && current.metadata.refreshTokenHash === expected.metadata.refreshTokenHash;
312
+ }
313
+ function uncoordinatedLegacyWinnerAccessToken(expected, currentGeneration) {
314
+ if (!expected || readCredentialMigrationState() !== "valid") return void 0;
315
+ const stored = readStoredCredentialMetadata();
316
+ if (stored.state !== "legacy_broker" || stored.metadata === void 0 || stored.metadata.accessTokenHash !== expected.accessTokenHash || stored.metadata.refreshTokenHash !== expected.refreshTokenHash) {
317
+ return void 0;
318
+ }
319
+ const replacementRefreshToken = readTrimmed(REFRESH_TOKEN_PATH);
320
+ if (!replacementRefreshToken || replacementRefreshToken === currentGeneration) return void 0;
321
+ return readTrimmed(TOKEN_FILE_PATH);
322
+ }
323
+ function winnerAfterRefreshRace(currentGeneration, expectedLegacyGeneration) {
324
+ if (readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration) return void 0;
325
+ const winner = resolveAuthCredential();
326
+ return (winner?.source === "token_file" ? winner.token : void 0) ?? uncoordinatedLegacyWinnerAccessToken(expectedLegacyGeneration, currentGeneration);
327
+ }
328
+ async function performTokenRefresh(options = {}) {
329
+ const selected = resolveAuthCredential();
330
+ if (selected?.source !== "token_file") return void 0;
331
+ const startingGeneration = readTrimmed(REFRESH_TOKEN_PATH);
332
+ if (!startingGeneration || isSessionEnded()) return void 0;
333
+ const startingAccessToken = selected.token;
334
+ return withCredentialLock(
335
+ async () => {
336
+ const currentCredential = resolveAuthCredential();
337
+ if (currentCredential?.source !== "token_file") return void 0;
338
+ const currentGeneration = readTrimmed(REFRESH_TOKEN_PATH);
339
+ if (!currentGeneration) return void 0;
340
+ if (currentGeneration !== startingGeneration) {
341
+ return currentCredential.token;
342
+ }
343
+ if (currentCredential.token !== startingAccessToken) {
344
+ return currentCredential.token;
345
+ }
346
+ if (isSessionEnded()) return void 0;
347
+ let metadata = readStoredCredentialMetadata();
348
+ const markerlessLegacyAtStart = metadata.state === "legacy_broker" && metadata.metadata === void 0;
349
+ if (metadata.state === "invalid" || metadata.state === "workos_connect" && (metadata.metadata.accessTokenHash !== refreshFingerprint(currentCredential.token) || metadata.metadata.refreshTokenHash !== refreshFingerprint(currentGeneration))) {
350
+ return void 0;
351
+ }
352
+ if (metadata.state === "legacy_broker") {
353
+ try {
354
+ metadata = bindLegacyBrokerGeneration(
355
+ currentCredential.token,
356
+ currentGeneration,
357
+ metadata
358
+ );
359
+ } catch {
360
+ return void 0;
361
+ }
362
+ if (metadata.state !== "legacy_broker") return void 0;
363
+ }
364
+ const expectedLegacyGeneration = markerlessLegacyAtStart && metadata.state === "legacy_broker" ? metadata.metadata : void 0;
365
+ const connectContext = metadata.state === "workos_connect" ? {
366
+ version: 1,
367
+ family: "workos_connect",
368
+ issuer: metadata.metadata.issuer,
369
+ clientId: metadata.metadata.clientId
370
+ } : void 0;
371
+ const response = await fetch(
372
+ connectContext ? workosConnectTokenEndpoint(connectContext.issuer) : `${getSiteUrl()}/mcp/broker/refresh`,
373
+ connectContext ? {
374
+ method: "POST",
375
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
376
+ body: new URLSearchParams({
377
+ grant_type: "refresh_token",
378
+ refresh_token: currentGeneration,
379
+ client_id: connectContext.clientId
380
+ }),
381
+ signal: options.signal
382
+ } : {
383
+ method: "POST",
384
+ headers: { "Content-Type": "application/json" },
385
+ body: JSON.stringify({ refresh_token: currentGeneration }),
386
+ signal: options.signal
387
+ }
388
+ );
389
+ let directResponse;
390
+ if (connectContext) {
391
+ try {
392
+ directResponse = await readBoundedJson(response, WORKOS_CONNECT_RESPONSE_MAX_BYTES);
393
+ } catch {
394
+ if (response.ok && readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration && metadataMatchesCredentialGeneration(
395
+ metadata,
396
+ currentCredential.token,
397
+ currentGeneration
398
+ )) {
399
+ writeTerminalFingerprint(currentGeneration);
400
+ }
401
+ return void 0;
402
+ }
403
+ }
404
+ if (!response.ok) {
405
+ let detail = "";
406
+ if (connectContext) {
407
+ detail = workosConnectRefreshErrorCode(directResponse);
408
+ } else {
409
+ detail = (await response.text().catch(() => "")).slice(0, 200);
410
+ }
411
+ if (isTerminalRefreshResponse(response, detail) && readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration && metadataMatchesCredentialGeneration(metadata, currentCredential.token, currentGeneration)) {
412
+ writeTerminalFingerprint(currentGeneration);
413
+ }
414
+ refreshDiagnostic(
415
+ response,
416
+ detail,
417
+ options.quiet,
418
+ connectContext ? "WorkOS Connect" : "broker"
419
+ );
420
+ return winnerAfterRefreshRace(currentGeneration, expectedLegacyGeneration);
421
+ }
422
+ let accessToken;
423
+ let replacementRefreshToken;
424
+ let expiresIn;
425
+ if (connectContext) {
426
+ try {
427
+ const tokens = parseWorkosConnectTokens(directResponse, {
428
+ fallbackRefreshToken: currentGeneration
429
+ });
430
+ accessToken = tokens.accessToken;
431
+ replacementRefreshToken = tokens.refreshToken;
432
+ expiresIn = tokens.expiresIn;
433
+ } catch {
434
+ if (readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration && metadataMatchesCredentialGeneration(
435
+ metadata,
436
+ currentCredential.token,
437
+ currentGeneration
438
+ )) {
439
+ writeTerminalFingerprint(currentGeneration);
440
+ }
441
+ return void 0;
442
+ }
443
+ } else {
444
+ let data;
445
+ try {
446
+ data = await response.json();
447
+ } catch {
448
+ if (readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration) {
449
+ writeTerminalFingerprint(currentGeneration);
450
+ }
451
+ return void 0;
452
+ }
453
+ const record = typeof data === "object" && data !== null && !Array.isArray(data) ? data : void 0;
454
+ accessToken = typeof record?.access_token === "string" ? record.access_token.trim() : "";
455
+ replacementRefreshToken = typeof record?.refresh_token === "string" ? record.refresh_token.trim() : "";
456
+ expiresIn = typeof record?.expires_in === "number" && Number.isFinite(record.expires_in) ? record.expires_in : void 0;
457
+ if (!(accessToken && replacementRefreshToken)) {
458
+ if (readTrimmed(REFRESH_TOKEN_PATH) === currentGeneration) {
459
+ writeTerminalFingerprint(currentGeneration);
460
+ }
461
+ return void 0;
462
+ }
463
+ }
464
+ const latestCredential = resolveAuthCredential();
465
+ if (readTrimmed(REFRESH_TOKEN_PATH) !== currentGeneration || latestCredential?.source !== "token_file" || latestCredential.token !== currentCredential.token || !metadataMatchesCredentialGeneration(metadata, currentCredential.token, currentGeneration)) {
466
+ return latestCredential?.source === "token_file" ? latestCredential.token : winnerAfterRefreshRace(currentGeneration, expectedLegacyGeneration);
467
+ }
468
+ commitCredentialsUnlocked({
469
+ accessToken,
470
+ refreshToken: replacementRefreshToken,
471
+ expiresIn,
472
+ metadata: connectContext
473
+ });
474
+ return accessToken;
475
+ },
476
+ { signal: options.signal }
477
+ );
478
+ }
479
+ var _refreshInFlight;
480
+ function refreshToken(options = {}) {
481
+ if (_refreshInFlight) return _refreshInFlight;
482
+ const attempt = performTokenRefresh(options).then((token) => {
483
+ if (token) _cachedCredential = { token, source: "token_file" };
484
+ return token;
485
+ }).finally(() => {
486
+ if (_refreshInFlight === attempt) _refreshInFlight = void 0;
487
+ });
488
+ _refreshInFlight = attempt;
489
+ return attempt;
490
+ }
491
+ var HttpError = class extends Error {
492
+ status;
493
+ body;
494
+ constructor(status, message, body) {
495
+ super(message);
496
+ this.name = "HttpError";
497
+ this.status = status;
498
+ this.body = body;
499
+ }
500
+ };
501
+ var CliManagementResponseError = class extends Error {
502
+ constructor() {
503
+ super("Invalid API-key management response");
504
+ this.name = "CliManagementResponseError";
505
+ }
506
+ };
507
+ var CLI_MANAGEMENT_RESPONSE_MAX_BYTES = 2 * 1024 * 1024;
508
+ async function managementResponseValue(response) {
509
+ if (response.status === 401) {
510
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
511
+ }
512
+ let value;
513
+ try {
514
+ value = await readBoundedJson(response, CLI_MANAGEMENT_RESPONSE_MAX_BYTES);
515
+ } catch {
516
+ if (response.ok) throw new CliManagementResponseError();
517
+ throw new HttpError(response.status, `HTTP ${response.status}`, null);
518
+ }
519
+ if (!response.ok) {
520
+ throw new HttpError(response.status, `HTTP ${response.status}`, value);
521
+ }
522
+ return value;
523
+ }
524
+ var _cachedCredential;
525
+ function selectedCredential() {
526
+ const resolved = resolveAuthCredential();
527
+ if (!_cachedCredential || resolved?.token !== _cachedCredential.token || resolved?.source !== _cachedCredential.source) {
528
+ _cachedCredential = resolved;
529
+ }
530
+ return _cachedCredential;
531
+ }
532
+ function fetchWithToken(method, path, body, options, token, siteUrl) {
533
+ return fetch(`${siteUrl}${path}`, {
534
+ method,
535
+ headers: {
536
+ Authorization: `Bearer ${token}`,
537
+ "Content-Type": "application/json"
538
+ },
539
+ body: body === void 0 ? void 0 : JSON.stringify(body),
540
+ signal: options?.signal
541
+ });
542
+ }
543
+ async function responseValue(response) {
544
+ if (!response.ok) {
545
+ if (response.status === 401) {
546
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
547
+ }
548
+ const errorBody = await response.json().catch(() => null);
549
+ const errorRecord = typeof errorBody === "object" && errorBody !== null && !Array.isArray(errorBody) ? errorBody : void 0;
550
+ const message = typeof errorRecord?.error === "string" ? errorRecord.error : `HTTP ${response.status}`;
551
+ throw new HttpError(response.status, message, errorBody);
552
+ }
553
+ return response.json();
554
+ }
555
+ async function request(method, path, body, options) {
556
+ const siteUrl = getSiteUrl();
557
+ let credential = selectedCredential();
558
+ let refreshAttempted = false;
559
+ if (credential && isTokenExpiringSoon(credential)) {
560
+ refreshAttempted = true;
561
+ const token = await refreshToken({
562
+ signal: options?.signal,
563
+ quiet: options?.quietRefresh
564
+ });
565
+ if (token) credential = { token, source: "token_file" };
566
+ }
567
+ if (!credential) {
568
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
569
+ }
570
+ if (isTokenExpiringSoon(credential) && isSessionEnded()) {
571
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
572
+ }
573
+ const tokenUsed = credential.token;
574
+ let response = await fetchWithToken(method, path, body, options, tokenUsed, siteUrl);
575
+ if (response.status === 401) {
576
+ const latest = resolveAuthCredential();
577
+ let retryToken = latest?.token !== tokenUsed ? latest?.token : void 0;
578
+ if (!retryToken && latest?.source === "token_file" && !refreshAttempted) {
579
+ refreshAttempted = true;
580
+ retryToken = await refreshToken({ signal: options?.signal, quiet: options?.quietRefresh });
581
+ }
582
+ if (retryToken) {
583
+ _cachedCredential = {
584
+ token: retryToken,
585
+ source: latest?.source === "token_file" ? "token_file" : latest?.source ?? "token_file"
586
+ };
587
+ response = await fetchWithToken(method, path, body, options, retryToken, siteUrl);
588
+ }
589
+ }
590
+ return responseValue(response);
591
+ }
592
+ function getClient() {
593
+ return {
594
+ get: (path, options) => request("GET", path, void 0, options),
595
+ post: (path, body, options) => request("POST", path, body, options)
596
+ };
597
+ }
598
+ async function getPinnedClient(options = {}) {
599
+ const siteUrl = getSiteUrl();
600
+ let credential = selectedCredential();
601
+ if (credential && isTokenExpiringSoon(credential)) {
602
+ const token2 = await refreshToken({
603
+ signal: options.signal,
604
+ quiet: options.quietRefresh
605
+ });
606
+ if (token2) credential = { token: token2, source: "token_file" };
607
+ }
608
+ if (!credential || isTokenExpiringSoon(credential) && isSessionEnded()) {
609
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
610
+ }
611
+ const token = credential.token;
612
+ const pinnedRequest = async (method, path, body, requestOptions) => responseValue(await fetchWithToken(method, path, body, requestOptions, token, siteUrl));
613
+ return {
614
+ get: (path, requestOptions) => pinnedRequest("GET", path, void 0, requestOptions),
615
+ post: (path, body, requestOptions) => pinnedRequest("POST", path, body, requestOptions)
616
+ };
617
+ }
618
+ async function getPinnedManagementClient(options = {}) {
619
+ const siteUrl = getSiteUrl();
620
+ let credential = selectedCredential();
621
+ if (credential && isTokenExpiringSoon(credential)) {
622
+ const token2 = await refreshToken({
623
+ signal: options.signal,
624
+ quiet: options.quietRefresh
625
+ });
626
+ if (token2) credential = { token: token2, source: "token_file" };
627
+ }
628
+ if (!credential || isTokenExpiringSoon(credential) && isSessionEnded()) {
629
+ throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
630
+ }
631
+ const token = credential.token;
632
+ const pinnedRequest = async (method, path, body, requestOptions) => managementResponseValue(
633
+ await fetchWithToken(method, path, body, requestOptions, token, siteUrl)
634
+ );
635
+ return {
636
+ delete: (path, body, requestOptions) => pinnedRequest("DELETE", path, body, requestOptions),
637
+ get: (path, requestOptions) => pinnedRequest("GET", path, void 0, requestOptions),
638
+ post: (path, body, requestOptions) => pinnedRequest("POST", path, body, requestOptions)
639
+ };
640
+ }
641
+
642
+ export {
643
+ terminalSafeText,
644
+ terminalSafeLine,
645
+ isTerminalSafeText,
646
+ stripControlChars,
647
+ getSiteUrl,
648
+ getTokenExpiresAt,
649
+ isSessionEnded,
650
+ commitCredentials,
651
+ setStoredToken,
652
+ clearStoredCredentials,
653
+ refreshToken,
654
+ HttpError,
655
+ CliManagementResponseError,
656
+ getClient,
657
+ getPinnedClient,
658
+ getPinnedManagementClient
659
+ };