@primitive.ai/prim 0.1.0-alpha.8 → 0.1.0-alpha.81

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 (45) hide show
  1. package/README.md +295 -37
  2. package/SKILL.md +269 -0
  3. package/dist/chunk-3EEFL6NJ.js +529 -0
  4. package/dist/chunk-4CZTNXIS.js +505 -0
  5. package/dist/chunk-4GLVJLEQ.js +55 -0
  6. package/dist/chunk-BQUJ4AFJ.js +771 -0
  7. package/dist/chunk-CUE2LKLR.js +155 -0
  8. package/dist/chunk-EZXEWFDG.js +210 -0
  9. package/dist/chunk-FI3LIRCB.js +262 -0
  10. package/dist/chunk-HWSS4BV5.js +1400 -0
  11. package/dist/chunk-LEMX3IOG.js +79 -0
  12. package/dist/chunk-LMRYZL3D.js +209 -0
  13. package/dist/chunk-OUXH3UEP.js +247 -0
  14. package/dist/chunk-PMRDE2DA.js +310 -0
  15. package/dist/chunk-RO3F6JYV.js +126 -0
  16. package/dist/chunk-TJTVHDJ5.js +321 -0
  17. package/dist/chunk-WB7AUZ4C.js +52 -0
  18. package/dist/chunk-XGADQPCI.js +26 -0
  19. package/dist/chunk-YWO7O6QG.js +327 -0
  20. package/dist/chunk-Z7YPFWAZ.js +917 -0
  21. package/dist/chunk-ZOSNEO5N.js +77 -0
  22. package/dist/daemon/server.js +15329 -0
  23. package/dist/hook-runtime/daemon/server.js +15329 -0
  24. package/dist/hook-runtime/hooks/post-commit.js +1128 -0
  25. package/dist/hook-runtime/hooks/post-rewrite.js +1148 -0
  26. package/dist/hook-runtime/hooks/post-tool-use.js +7586 -0
  27. package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
  28. package/dist/hook-runtime/hooks/pre-tool-use.js +7099 -0
  29. package/dist/hook-runtime/hooks/prim-hook.js +7691 -0
  30. package/dist/hook-runtime/hooks/session-end.js +495 -0
  31. package/dist/hook-runtime/hooks/session-start.js +11313 -0
  32. package/dist/hook-runtime/index.js +44696 -0
  33. package/dist/hook-runtime/statusline-main.js +765 -0
  34. package/dist/hooks/post-commit.js +179 -0
  35. package/dist/hooks/post-rewrite.js +197 -0
  36. package/dist/hooks/post-tool-use.js +250 -0
  37. package/dist/hooks/pre-commit.js +21 -74
  38. package/dist/hooks/pre-tool-use.js +348 -0
  39. package/dist/hooks/prim-hook.js +195 -0
  40. package/dist/hooks/session-end.js +70 -0
  41. package/dist/hooks/session-start.js +299 -0
  42. package/dist/index.js +20981 -519
  43. package/dist/statusline-main.js +765 -0
  44. package/package.json +35 -13
  45. package/dist/chunk-3APLWTLB.js +0 -165
@@ -0,0 +1,529 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // src/lib/paths.ts
28
+ import { homedir } from "os";
29
+ import { isAbsolute, join, normalize } from "path";
30
+ var UNSAFE_CONFIG_PATH_CHARACTERS = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
31
+ function absolutePath(value, variable) {
32
+ if (value !== void 0 && UNSAFE_CONFIG_PATH_CHARACTERS.test(value)) {
33
+ throw new Error(`${variable} contains unsafe characters`);
34
+ }
35
+ if (!value || value.trim() !== value || !isAbsolute(value)) return;
36
+ const normalized = normalize(value);
37
+ return normalized === value ? value : void 0;
38
+ }
39
+ function resolvePrimConfigDirectory(options = {}) {
40
+ const env = options.env ?? process.env;
41
+ const explicit = absolutePath(env.PRIM_CONFIG_DIR, "PRIM_CONFIG_DIR");
42
+ if (explicit) return { path: explicit, source: "explicit" };
43
+ const xdgConfigHome = absolutePath(env.XDG_CONFIG_HOME, "XDG_CONFIG_HOME");
44
+ if (xdgConfigHome) return { path: join(xdgConfigHome, "prim"), source: "xdg" };
45
+ const home = absolutePath(options.homeDir ?? homedir(), "HOME");
46
+ if (!home) {
47
+ throw new Error("cannot resolve Primitive config directory: HOME is not an absolute path");
48
+ }
49
+ return { path: join(home, ".config", "prim"), source: "default" };
50
+ }
51
+ function primConfigDirectory(options = {}) {
52
+ return resolvePrimConfigDirectory(options).path;
53
+ }
54
+
55
+ // src/lib/workos-connect.ts
56
+ var DISCOVERY_PATH = "/api/cli/auth/connect/config";
57
+ var DEVICE_AUTHORIZATION_PATH = "/oauth2/device_authorization";
58
+ var TOKEN_PATH = "/oauth2/token";
59
+ var DISCOVERY_MAX_BYTES = 16 * 1024;
60
+ var WORKOS_CONNECT_RESPONSE_MAX_BYTES = 64 * 1024;
61
+ var MAX_DEVICE_LIFETIME_SECONDS = 60 * 60;
62
+ var MAX_TOKEN_LIFETIME_SECONDS = 24 * 60 * 60;
63
+ var MAX_INTERVAL_SECONDS = 60;
64
+ var POLL_REQUEST_TIMEOUT_MS = 3e4;
65
+ var SLOW_DOWN_SECONDS = 5;
66
+ var MAX_URL_LENGTH = 2048;
67
+ var MAX_DEVICE_CODE_LENGTH = 4096;
68
+ var MAX_USER_CODE_LENGTH = 128;
69
+ var MAX_CLIENT_ID_LENGTH = 256;
70
+ var CONNECT_CLIENT_ID_PATTERN = /^client_[A-Za-z0-9_-]+$/u;
71
+ var WORKOS_CONNECT_DEFAULT_SCOPES = ["openid", "profile", "email", "offline_access"];
72
+ var WorkosConnectProtocolError = class extends Error {
73
+ code;
74
+ constructor(code, message) {
75
+ super(message);
76
+ this.name = "WorkosConnectProtocolError";
77
+ this.code = code;
78
+ }
79
+ };
80
+ function recordFrom(value, label) {
81
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
82
+ throw new WorkosConnectProtocolError("invalid_response", `${label} must be an object`);
83
+ }
84
+ return value;
85
+ }
86
+ function exactString(value, field, maxLength) {
87
+ const containsControl = typeof value === "string" && [...value].some((character) => {
88
+ const code = character.charCodeAt(0);
89
+ return code <= 31 || code === 127;
90
+ });
91
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength || value.trim() !== value || containsControl) {
92
+ throw new WorkosConnectProtocolError("invalid_response", `${field} is invalid`);
93
+ }
94
+ return value;
95
+ }
96
+ function safeInteger(value, field, minimum, maximum) {
97
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
98
+ throw new WorkosConnectProtocolError("invalid_response", `${field} is invalid`);
99
+ }
100
+ return value;
101
+ }
102
+ function canonicalIssuer(value) {
103
+ const issuer = exactString(value, "issuer", MAX_URL_LENGTH);
104
+ let parsed;
105
+ try {
106
+ parsed = new URL(issuer);
107
+ } catch {
108
+ throw new WorkosConnectProtocolError("invalid_response", "issuer must be an HTTPS origin");
109
+ }
110
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.origin !== issuer) {
111
+ throw new WorkosConnectProtocolError("invalid_response", "issuer must be an HTTPS origin");
112
+ }
113
+ return issuer;
114
+ }
115
+ function sameIssuerUrl(value, field, issuer) {
116
+ const raw = exactString(value, field, MAX_URL_LENGTH);
117
+ let parsed;
118
+ try {
119
+ parsed = new URL(raw);
120
+ } catch {
121
+ throw new WorkosConnectProtocolError("invalid_response", `${field} must be an HTTPS URL`);
122
+ }
123
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.origin !== issuer) {
124
+ throw new WorkosConnectProtocolError("invalid_response", `${field} is not trusted`);
125
+ }
126
+ return parsed.toString();
127
+ }
128
+ function parseWorkosConnectConfiguration(value) {
129
+ const record = recordFrom(value, "Connect discovery");
130
+ const expectedKeys = ["protocol_version", "issuer", "client_id", "default_scopes"];
131
+ if (record.protocol_version !== 1 || Object.keys(record).length !== expectedKeys.length || expectedKeys.some((key) => !Object.hasOwn(record, key)) || !Array.isArray(record.default_scopes) || record.default_scopes.length !== WORKOS_CONNECT_DEFAULT_SCOPES.length || record.default_scopes.some((scope, index) => scope !== WORKOS_CONNECT_DEFAULT_SCOPES[index])) {
132
+ throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
133
+ }
134
+ const issuer = canonicalIssuer(record.issuer);
135
+ const clientId = exactString(record.client_id, "client_id", MAX_CLIENT_ID_LENGTH);
136
+ if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
137
+ throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
138
+ }
139
+ return {
140
+ issuer,
141
+ clientId,
142
+ scopes: [...WORKOS_CONNECT_DEFAULT_SCOPES]
143
+ };
144
+ }
145
+ function parseWorkosConnectCredentialMetadata(value) {
146
+ const record = recordFrom(value, "credential metadata");
147
+ if (record.version !== 1 || record.family !== "workos_connect") {
148
+ throw new WorkosConnectProtocolError("invalid_metadata", "credential metadata is invalid");
149
+ }
150
+ const issuer = canonicalIssuer(record.issuer);
151
+ const clientId = exactString(record.clientId, "clientId", MAX_CLIENT_ID_LENGTH);
152
+ const accessTokenHash = exactString(record.accessTokenHash, "accessTokenHash", 64);
153
+ const refreshTokenHash = exactString(record.refreshTokenHash, "refreshTokenHash", 64);
154
+ if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
155
+ throw new WorkosConnectProtocolError("invalid_metadata", "credential clientId is invalid");
156
+ }
157
+ if (!/^[a-f0-9]{64}$/u.test(accessTokenHash)) {
158
+ throw new WorkosConnectProtocolError(
159
+ "invalid_metadata",
160
+ "credential accessTokenHash is invalid"
161
+ );
162
+ }
163
+ if (!/^[a-f0-9]{64}$/u.test(refreshTokenHash)) {
164
+ throw new WorkosConnectProtocolError(
165
+ "invalid_metadata",
166
+ "credential refreshTokenHash is invalid"
167
+ );
168
+ }
169
+ return {
170
+ version: 1,
171
+ family: "workos_connect",
172
+ issuer,
173
+ clientId,
174
+ accessTokenHash,
175
+ refreshTokenHash
176
+ };
177
+ }
178
+ async function readBoundedJson(response, maxBytes) {
179
+ const contentLength = response.headers.get("content-length");
180
+ if (contentLength !== null) {
181
+ if (!/^\d+$/u.test(contentLength) || Number(contentLength) > maxBytes) {
182
+ throw new WorkosConnectProtocolError("invalid_response", "response body is too large");
183
+ }
184
+ }
185
+ if (!response.body) {
186
+ throw new WorkosConnectProtocolError("invalid_response", "response body is missing");
187
+ }
188
+ const reader = response.body.getReader();
189
+ const chunks = [];
190
+ let total = 0;
191
+ try {
192
+ while (true) {
193
+ const result = await reader.read();
194
+ if (result.done) break;
195
+ total += result.value.byteLength;
196
+ if (total > maxBytes) {
197
+ await reader.cancel();
198
+ throw new WorkosConnectProtocolError("invalid_response", "response body is too large");
199
+ }
200
+ chunks.push(result.value);
201
+ }
202
+ } finally {
203
+ reader.releaseLock();
204
+ }
205
+ const bytes = new Uint8Array(total);
206
+ let offset = 0;
207
+ for (const chunk of chunks) {
208
+ bytes.set(chunk, offset);
209
+ offset += chunk.byteLength;
210
+ }
211
+ let text;
212
+ try {
213
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
214
+ return JSON.parse(text);
215
+ } catch {
216
+ throw new WorkosConnectProtocolError("invalid_response", "response body is not valid JSON");
217
+ }
218
+ }
219
+ async function discoverWorkosConnect(siteUrl, fetchImpl = fetch, signal = AbortSignal.timeout(1e4)) {
220
+ const response = await fetchImpl(`${siteUrl}${DISCOVERY_PATH}`, { signal });
221
+ if (response.status === 404) return { state: "legacy_server" };
222
+ if (!response.ok) {
223
+ throw new WorkosConnectProtocolError(
224
+ "discovery_unavailable",
225
+ `Connect discovery returned HTTP ${response.status}`
226
+ );
227
+ }
228
+ return {
229
+ state: "available",
230
+ configuration: parseWorkosConnectConfiguration(
231
+ await readBoundedJson(response, DISCOVERY_MAX_BYTES)
232
+ )
233
+ };
234
+ }
235
+ function workosConnectTokenEndpoint(issuer) {
236
+ return `${issuer}${TOKEN_PATH}`;
237
+ }
238
+ async function requestWorkosDeviceAuthorization(configuration, fetchImpl = fetch, signal = AbortSignal.timeout(1e4)) {
239
+ const body = new URLSearchParams({
240
+ client_id: configuration.clientId,
241
+ scope: configuration.scopes.join(" ")
242
+ });
243
+ const response = await fetchImpl(`${configuration.issuer}${DEVICE_AUTHORIZATION_PATH}`, {
244
+ method: "POST",
245
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
246
+ body,
247
+ signal
248
+ });
249
+ const value = await readBoundedJson(response, WORKOS_CONNECT_RESPONSE_MAX_BYTES);
250
+ if (!response.ok) {
251
+ const error = recordFrom(value, "device authorization error").error;
252
+ throw new WorkosConnectProtocolError(
253
+ typeof error === "string" ? error : "device_authorization_failed",
254
+ `Device authorization returned HTTP ${response.status}`
255
+ );
256
+ }
257
+ const record = recordFrom(value, "device authorization");
258
+ const verificationUri = sameIssuerUrl(
259
+ record.verification_uri,
260
+ "verification_uri",
261
+ configuration.issuer
262
+ );
263
+ return {
264
+ deviceCode: exactString(record.device_code, "device_code", MAX_DEVICE_CODE_LENGTH),
265
+ userCode: exactString(record.user_code, "user_code", MAX_USER_CODE_LENGTH),
266
+ verificationUri,
267
+ verificationUriComplete: record.verification_uri_complete === void 0 ? void 0 : sameIssuerUrl(
268
+ record.verification_uri_complete,
269
+ "verification_uri_complete",
270
+ configuration.issuer
271
+ ),
272
+ expiresIn: safeInteger(record.expires_in, "expires_in", 1, MAX_DEVICE_LIFETIME_SECONDS),
273
+ interval: safeInteger(record.interval ?? 5, "interval", 1, MAX_INTERVAL_SECONDS)
274
+ };
275
+ }
276
+ function parseWorkosConnectTokens(value, options = {}) {
277
+ const record = recordFrom(value, "token response");
278
+ const accessToken = exactString(
279
+ record.access_token,
280
+ "access_token",
281
+ WORKOS_CONNECT_RESPONSE_MAX_BYTES
282
+ );
283
+ const refreshToken = record.refresh_token === void 0 && options.fallbackRefreshToken !== void 0 ? options.fallbackRefreshToken : exactString(record.refresh_token, "refresh_token", WORKOS_CONNECT_RESPONSE_MAX_BYTES);
284
+ return {
285
+ accessToken,
286
+ refreshToken,
287
+ expiresIn: record.expires_in === void 0 ? void 0 : safeInteger(record.expires_in, "expires_in", 1, MAX_TOKEN_LIFETIME_SECONDS)
288
+ };
289
+ }
290
+ function providerError(value) {
291
+ const error = recordFrom(value, "token error").error;
292
+ return exactString(error, "error", MAX_USER_CODE_LENGTH);
293
+ }
294
+ async function defaultSleep(milliseconds, signal) {
295
+ await new Promise((resolve, reject) => {
296
+ if (signal.aborted) {
297
+ reject(signal.reason);
298
+ return;
299
+ }
300
+ const abort = () => {
301
+ clearTimeout(timer);
302
+ reject(signal.reason);
303
+ };
304
+ const timer = setTimeout(() => {
305
+ signal.removeEventListener("abort", abort);
306
+ resolve();
307
+ }, milliseconds);
308
+ signal.addEventListener("abort", abort, { once: true });
309
+ });
310
+ }
311
+ async function pollWorkosDeviceAuthorization(configuration, authorization, options = {}) {
312
+ const fetchImpl = options.fetch ?? fetch;
313
+ const now = options.now ?? Date.now;
314
+ const sleep = options.sleep ?? defaultSleep;
315
+ const signal = options.signal ?? AbortSignal.timeout(authorization.expiresIn * 1e3);
316
+ const deadline = now() + authorization.expiresIn * 1e3;
317
+ let intervalSeconds = authorization.interval;
318
+ while (now() < deadline) {
319
+ await sleep(Math.min(intervalSeconds * 1e3, Math.max(0, deadline - now())), signal);
320
+ if (now() >= deadline) break;
321
+ let response;
322
+ try {
323
+ response = await fetchImpl(workosConnectTokenEndpoint(configuration.issuer), {
324
+ method: "POST",
325
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
326
+ body: new URLSearchParams({
327
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
328
+ device_code: authorization.deviceCode,
329
+ client_id: configuration.clientId
330
+ }),
331
+ signal: AbortSignal.any([signal, AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)])
332
+ });
333
+ } catch (error2) {
334
+ if (signal.aborted) {
335
+ throw new WorkosConnectProtocolError("expired_token", "Device authorization expired");
336
+ }
337
+ continue;
338
+ }
339
+ const value = await readBoundedJson(response, WORKOS_CONNECT_RESPONSE_MAX_BYTES);
340
+ if (response.ok) return parseWorkosConnectTokens(value);
341
+ const error = providerError(value);
342
+ if (error === "authorization_pending") continue;
343
+ if (error === "slow_down") {
344
+ intervalSeconds = Math.min(MAX_INTERVAL_SECONDS, intervalSeconds + SLOW_DOWN_SECONDS);
345
+ continue;
346
+ }
347
+ if (error === "access_denied" || error === "expired_token") {
348
+ throw new WorkosConnectProtocolError(error, `Device authorization failed: ${error}`);
349
+ }
350
+ throw new WorkosConnectProtocolError(error, "Token endpoint rejected device authorization");
351
+ }
352
+ throw new WorkosConnectProtocolError("expired_token", "Device authorization expired");
353
+ }
354
+
355
+ // src/lib/credentials.ts
356
+ import { createHash } from "crypto";
357
+ import { readFileSync } from "fs";
358
+ import { join as join2 } from "path";
359
+ var CONFIG_DIRECTORY = primConfigDirectory();
360
+ var TOKEN_FILE_PATH = join2(CONFIG_DIRECTORY, "token");
361
+ var REFRESH_TOKEN_PATH = join2(CONFIG_DIRECTORY, "refresh_token");
362
+ var TOKEN_EXPIRES_PATH = join2(CONFIG_DIRECTORY, "token_expires_at");
363
+ var TERMINAL_REFRESH_PATH = join2(CONFIG_DIRECTORY, "refresh_terminal");
364
+ var CREDENTIAL_METADATA_PATH = join2(CONFIG_DIRECTORY, "credential_metadata.json");
365
+ var CREDENTIAL_FAMILY_PATH = join2(CONFIG_DIRECTORY, "credential_family.json");
366
+ var CREDENTIAL_MIGRATION_PATH = join2(CONFIG_DIRECTORY, "credential_migration.json");
367
+ var CREDENTIAL_LOCK_PATH = join2(CONFIG_DIRECTORY, "credentials.lock");
368
+ var MAX_CREDENTIAL_METADATA_BYTES = 4096;
369
+ var CREDENTIAL_MIGRATION_VERSION = 1;
370
+ var CREDENTIAL_MIGRATION_STATE = "family_bound";
371
+ function readTrimmed(path) {
372
+ try {
373
+ const value = readFileSync(path, "utf8").trim();
374
+ return value || void 0;
375
+ } catch {
376
+ return void 0;
377
+ }
378
+ }
379
+ function resolveAuthCredential(options = {}) {
380
+ const envToken = (options.env ?? process.env).PRIM_TOKEN?.trim();
381
+ if (envToken) return { token: envToken, source: "environment" };
382
+ const stored = readTrimmed(options.tokenFilePath ?? TOKEN_FILE_PATH);
383
+ if (!stored) return void 0;
384
+ const metadata = readStoredCredentialMetadata(
385
+ options.metadataPath ?? CREDENTIAL_METADATA_PATH,
386
+ options.familyPath ?? CREDENTIAL_FAMILY_PATH,
387
+ options.migrationPath ?? CREDENTIAL_MIGRATION_PATH
388
+ );
389
+ if (metadata.state === "legacy_broker") {
390
+ if (metadata.metadata) {
391
+ const refreshToken2 = readTrimmed(options.refreshTokenPath ?? REFRESH_TOKEN_PATH);
392
+ if (!refreshToken2 || metadata.metadata.accessTokenHash !== credentialFingerprint(stored) || metadata.metadata.refreshTokenHash !== credentialFingerprint(refreshToken2)) {
393
+ return void 0;
394
+ }
395
+ }
396
+ return { token: stored, source: "token_file" };
397
+ }
398
+ if (metadata.state === "invalid") return void 0;
399
+ const refreshToken = readTrimmed(options.refreshTokenPath ?? REFRESH_TOKEN_PATH);
400
+ if (!refreshToken || metadata.metadata.accessTokenHash !== credentialFingerprint(stored) || metadata.metadata.refreshTokenHash !== credentialFingerprint(refreshToken)) {
401
+ return void 0;
402
+ }
403
+ return { token: stored, source: "token_file" };
404
+ }
405
+ function credentialFingerprint(value) {
406
+ return createHash("sha256").update(value).digest("hex");
407
+ }
408
+ function parseLegacyBrokerCredentialMetadata(value) {
409
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
410
+ throw new Error("credential family is invalid");
411
+ }
412
+ const record = value;
413
+ if (record.version !== 1 || record.family !== "legacy_broker" || typeof record.accessTokenHash !== "string" || typeof record.refreshTokenHash !== "string" || !/^[a-f0-9]{64}$/u.test(record.accessTokenHash) || !/^[a-f0-9]{64}$/u.test(record.refreshTokenHash)) {
414
+ throw new Error("credential family is invalid");
415
+ }
416
+ return {
417
+ version: 1,
418
+ family: "legacy_broker",
419
+ accessTokenHash: record.accessTokenHash,
420
+ refreshTokenHash: record.refreshTokenHash
421
+ };
422
+ }
423
+ function readBoundedCredentialJson(path) {
424
+ let raw;
425
+ try {
426
+ raw = readFileSync(path, "utf8");
427
+ } catch (error) {
428
+ return error.code === "ENOENT" ? void 0 : null;
429
+ }
430
+ if (Buffer.byteLength(raw, "utf8") > MAX_CREDENTIAL_METADATA_BYTES) return null;
431
+ try {
432
+ return JSON.parse(raw);
433
+ } catch {
434
+ return null;
435
+ }
436
+ }
437
+ function readCredentialFamily(path) {
438
+ const parsed = readBoundedCredentialJson(path);
439
+ if (parsed === void 0) return void 0;
440
+ if (parsed === null) return { state: "invalid" };
441
+ try {
442
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && parsed.family === "legacy_broker") {
443
+ return { state: "legacy_broker", metadata: parseLegacyBrokerCredentialMetadata(parsed) };
444
+ }
445
+ return {
446
+ state: "workos_connect",
447
+ metadata: parseWorkosConnectCredentialMetadata(parsed)
448
+ };
449
+ } catch {
450
+ return { state: "invalid" };
451
+ }
452
+ }
453
+ function isCredentialMigrationSentinel(value) {
454
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
455
+ const record = value;
456
+ const keys = Object.keys(record).sort();
457
+ return keys.length === 2 && keys[0] === "state" && keys[1] === "version" && record.version === CREDENTIAL_MIGRATION_VERSION && record.state === CREDENTIAL_MIGRATION_STATE;
458
+ }
459
+ function readCredentialMigrationState(path = CREDENTIAL_MIGRATION_PATH) {
460
+ const parsed = readBoundedCredentialJson(path);
461
+ if (parsed === void 0) return "absent";
462
+ return parsed !== null && isCredentialMigrationSentinel(parsed) ? "valid" : "invalid";
463
+ }
464
+ function readStoredCredentialMetadata(metadataPath = CREDENTIAL_METADATA_PATH, familyPath = CREDENTIAL_FAMILY_PATH, migrationPath = CREDENTIAL_MIGRATION_PATH) {
465
+ const family = readCredentialFamily(familyPath);
466
+ if (family) return family;
467
+ const parsed = readBoundedCredentialJson(metadataPath);
468
+ if (parsed === void 0) {
469
+ return readCredentialMigrationState(migrationPath) === "absent" ? { state: "legacy_broker" } : { state: "invalid" };
470
+ }
471
+ if (parsed === null) return { state: "invalid" };
472
+ try {
473
+ return {
474
+ state: "workos_connect",
475
+ metadata: parseWorkosConnectCredentialMetadata(parsed)
476
+ };
477
+ } catch {
478
+ return { state: "invalid" };
479
+ }
480
+ }
481
+ function decodeJwtPayload(token) {
482
+ const parts = token.split(".");
483
+ if (parts.length !== 3) return void 0;
484
+ try {
485
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
486
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
487
+ return payload;
488
+ } catch {
489
+ return void 0;
490
+ }
491
+ }
492
+ function jwtExpiresAt(token) {
493
+ const exp = decodeJwtPayload(token)?.exp;
494
+ return typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : void 0;
495
+ }
496
+ function jwtOrganizationId(token) {
497
+ const orgId = decodeJwtPayload(token)?.org_id;
498
+ return typeof orgId === "string" && orgId.length > 0 ? orgId : void 0;
499
+ }
500
+
501
+ export {
502
+ __commonJS,
503
+ __toESM,
504
+ primConfigDirectory,
505
+ WORKOS_CONNECT_RESPONSE_MAX_BYTES,
506
+ WorkosConnectProtocolError,
507
+ readBoundedJson,
508
+ discoverWorkosConnect,
509
+ workosConnectTokenEndpoint,
510
+ requestWorkosDeviceAuthorization,
511
+ parseWorkosConnectTokens,
512
+ pollWorkosDeviceAuthorization,
513
+ TOKEN_FILE_PATH,
514
+ REFRESH_TOKEN_PATH,
515
+ TOKEN_EXPIRES_PATH,
516
+ TERMINAL_REFRESH_PATH,
517
+ CREDENTIAL_METADATA_PATH,
518
+ CREDENTIAL_FAMILY_PATH,
519
+ CREDENTIAL_MIGRATION_PATH,
520
+ CREDENTIAL_LOCK_PATH,
521
+ CREDENTIAL_MIGRATION_VERSION,
522
+ CREDENTIAL_MIGRATION_STATE,
523
+ resolveAuthCredential,
524
+ readCredentialMigrationState,
525
+ readStoredCredentialMetadata,
526
+ decodeJwtPayload,
527
+ jwtExpiresAt,
528
+ jwtOrganizationId
529
+ };