@sellable/install 0.1.484 → 0.1.486-wip.phase169.20260804.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.
@@ -0,0 +1,574 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import {
3
+ chmodSync,
4
+ existsSync,
5
+ lstatSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ renameSync,
9
+ writeFileSync,
10
+ } from "node:fs";
11
+ import { dirname } from "node:path";
12
+
13
+ export const MODEL_AUTH_RUNTIME_SCHEMA = "sellable-agent-model-auth-runtime/v2";
14
+ export const MODEL_AUTH_READBACK_SCHEMA =
15
+ "sellable-agent-model-auth-readback/v2";
16
+ export const MODEL_AUTH_MINIMUM_VALIDITY_MS = 20 * 60 * 1000;
17
+ export const MODEL_AUTH_PROVIDER = "openai-codex";
18
+ export const MODEL_AUTH_DEFAULT_MODEL = "gpt-5.6-sol";
19
+ export const MODEL_AUTH_REASONING_EFFORT = "high";
20
+
21
+ const MARKER_VERSION = 2;
22
+ const SHA256 = /^[a-f0-9]{64}$/;
23
+ const FINGERPRINT = /^[a-f0-9]{16,64}$/;
24
+ const SAFE_ID = /^[A-Za-z0-9_.:-]{1,256}$/;
25
+ const MODEL_ID = /^~?[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._:-]*$/;
26
+ const FORBIDDEN_REFRESH_KEY = /^(?:refresh[_-]?token|id[_-]?token)$/i;
27
+ const EXPECTED_SLOT_IDENTITIES = Object.freeze([
28
+ Object.freeze({ slotId: "dittto-primary", priority: 1 }),
29
+ Object.freeze({ slotId: "kaigo-secondary", priority: 2 }),
30
+ ]);
31
+ const RUNTIME_KEYS = Object.freeze([
32
+ "schemaVersion",
33
+ "activationId",
34
+ "bundleGeneration",
35
+ "policyHash",
36
+ "credentialPoolStrategy",
37
+ "codex",
38
+ "openrouter",
39
+ ]);
40
+ const SLOT_KEYS = Object.freeze([
41
+ "slotId",
42
+ "priority",
43
+ "accessToken",
44
+ "accountId",
45
+ "expiresAt",
46
+ "grantFingerprint",
47
+ ]);
48
+ const OPENROUTER_KEYS = Object.freeze(["apiKey", "keyFingerprint", "models"]);
49
+ const MARKER_KEYS = Object.freeze([
50
+ "version",
51
+ "activationId",
52
+ "bundleGeneration",
53
+ "policyHash",
54
+ "sourceFingerprint",
55
+ "slots",
56
+ "openrouterKeyFingerprint",
57
+ "models",
58
+ ]);
59
+
60
+ export class ModelAuthRuntimeError extends Error {
61
+ constructor(code) {
62
+ super(code);
63
+ this.name = "ModelAuthRuntimeError";
64
+ this.code = code;
65
+ }
66
+ }
67
+
68
+ function fail(code) {
69
+ throw new ModelAuthRuntimeError(code);
70
+ }
71
+
72
+ function isRecord(value) {
73
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
74
+ }
75
+
76
+ function exactKeys(value, keys) {
77
+ return (
78
+ isRecord(value) &&
79
+ Object.keys(value).sort().join("\0") === [...keys].sort().join("\0")
80
+ );
81
+ }
82
+
83
+ function bounded(value, minimum, maximum) {
84
+ return (
85
+ typeof value === "string" &&
86
+ value.length >= minimum &&
87
+ value.length <= maximum &&
88
+ !value.includes("\0")
89
+ );
90
+ }
91
+
92
+ function stableJson(value) {
93
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
94
+ if (isRecord(value)) {
95
+ return `{${Object.keys(value)
96
+ .sort()
97
+ .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
98
+ .join(",")}}`;
99
+ }
100
+ return JSON.stringify(value);
101
+ }
102
+
103
+ const sha256 = (value) => createHash("sha256").update(value).digest("hex");
104
+
105
+ function containsRuntimeRefreshOwnership(value) {
106
+ if (Array.isArray(value)) return value.some(containsRuntimeRefreshOwnership);
107
+ if (!isRecord(value)) return false;
108
+ return Object.entries(value).some(
109
+ ([key, entry]) =>
110
+ FORBIDDEN_REFRESH_KEY.test(key) || containsRuntimeRefreshOwnership(entry)
111
+ );
112
+ }
113
+
114
+ function jwtExpiry(accessToken) {
115
+ try {
116
+ const parts = accessToken.split(".");
117
+ if (parts.length !== 3) return null;
118
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
119
+ return Number.isFinite(payload?.exp) ? Number(payload.exp) * 1000 : null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+
125
+ function assertRegularFileOrMissing(path) {
126
+ if (!existsSync(path)) return;
127
+ const entry = lstatSync(path);
128
+ if (entry.isSymbolicLink() || !entry.isFile())
129
+ fail("model_auth_store_rejected");
130
+ }
131
+
132
+ function atomicPrivateFile(path, bytes) {
133
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
134
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
135
+ writeFileSync(temporary, bytes, { flag: "wx", mode: 0o600 });
136
+ chmodSync(temporary, 0o600);
137
+ renameSync(temporary, path);
138
+ chmodSync(path, 0o600);
139
+ }
140
+
141
+ function projectionIdentity(value) {
142
+ return {
143
+ activationId: value.activationId,
144
+ bundleGeneration: value.bundleGeneration,
145
+ policyHash: value.policyHash,
146
+ slots: value.codex.map((slot) => ({
147
+ slotId: slot.slotId,
148
+ priority: slot.priority,
149
+ grantFingerprint: slot.grantFingerprint,
150
+ expiresAt: slot.expiresAt,
151
+ })),
152
+ openrouterKeyFingerprint: value.openrouter.keyFingerprint,
153
+ models: [...value.openrouter.models],
154
+ };
155
+ }
156
+
157
+ function markerFor(credential) {
158
+ return {
159
+ version: MARKER_VERSION,
160
+ ...credential.identity,
161
+ sourceFingerprint: credential.sourceFingerprint,
162
+ };
163
+ }
164
+
165
+ function parseMarker(path) {
166
+ assertRegularFileOrMissing(path);
167
+ if (!existsSync(path)) return null;
168
+ try {
169
+ const marker = JSON.parse(readFileSync(path, "utf8"));
170
+ if (
171
+ !exactKeys(marker, MARKER_KEYS) ||
172
+ marker.version !== MARKER_VERSION ||
173
+ !SAFE_ID.test(marker.activationId ?? "") ||
174
+ !Number.isSafeInteger(marker.bundleGeneration) ||
175
+ marker.bundleGeneration < 1 ||
176
+ !SHA256.test(marker.policyHash ?? "") ||
177
+ !FINGERPRINT.test(marker.sourceFingerprint ?? "") ||
178
+ !Array.isArray(marker.slots) ||
179
+ marker.slots.length !== 2 ||
180
+ !FINGERPRINT.test(marker.openrouterKeyFingerprint ?? "") ||
181
+ !Array.isArray(marker.models) ||
182
+ marker.models.length < 1
183
+ ) {
184
+ return null;
185
+ }
186
+ return marker;
187
+ } catch {
188
+ return null;
189
+ }
190
+ }
191
+
192
+ function accessOnlyStoreFor(value) {
193
+ return {
194
+ version: 1,
195
+ credential_pool: {
196
+ "openai-codex": value.codex.map((slot) => ({
197
+ id: slot.slotId,
198
+ label: slot.slotId,
199
+ auth_type: "oauth",
200
+ priority: slot.priority,
201
+ source: "manual:device_code",
202
+ access_token: slot.accessToken,
203
+ account_id: slot.accountId,
204
+ expires_at: slot.expiresAt,
205
+ request_count: 0,
206
+ secret_fingerprint: slot.grantFingerprint,
207
+ })),
208
+ openrouter: [
209
+ {
210
+ id: "sellable-openrouter",
211
+ label: "sellable-openrouter",
212
+ auth_type: "api_key",
213
+ priority: 1,
214
+ source: "manual",
215
+ access_token: value.openrouter.apiKey,
216
+ base_url: "https://openrouter.ai/api/v1",
217
+ request_count: 0,
218
+ secret_fingerprint: value.openrouter.keyFingerprint,
219
+ },
220
+ ],
221
+ },
222
+ active_provider: MODEL_AUTH_PROVIDER,
223
+ };
224
+ }
225
+
226
+ function essentialStoreMatches(path, credential) {
227
+ if (!existsSync(path)) return false;
228
+ try {
229
+ const stored = JSON.parse(readFileSync(path, "utf8"));
230
+ if (containsRuntimeRefreshOwnership(stored)) return false;
231
+ const codex = stored?.credential_pool?.[MODEL_AUTH_PROVIDER];
232
+ const openrouter = stored?.credential_pool?.openrouter;
233
+ if (
234
+ stored?.version !== 1 ||
235
+ stored?.active_provider !== MODEL_AUTH_PROVIDER ||
236
+ !Array.isArray(codex) ||
237
+ codex.length !== 2 ||
238
+ !Array.isArray(openrouter) ||
239
+ openrouter.length !== 1
240
+ ) {
241
+ return false;
242
+ }
243
+ return (
244
+ codex.every((entry, index) => {
245
+ const expected = credential.value.codex[index];
246
+ return (
247
+ entry?.id === expected.slotId &&
248
+ entry?.priority === expected.priority &&
249
+ entry?.source === "manual:device_code" &&
250
+ entry?.auth_type === "oauth" &&
251
+ entry?.access_token === expected.accessToken &&
252
+ entry?.account_id === expected.accountId &&
253
+ entry?.expires_at === expected.expiresAt &&
254
+ entry?.secret_fingerprint === expected.grantFingerprint
255
+ );
256
+ }) &&
257
+ openrouter[0]?.id === "sellable-openrouter" &&
258
+ openrouter[0]?.auth_type === "api_key" &&
259
+ openrouter[0]?.access_token === credential.value.openrouter.apiKey &&
260
+ openrouter[0]?.secret_fingerprint ===
261
+ credential.value.openrouter.keyFingerprint
262
+ );
263
+ } catch {
264
+ return false;
265
+ }
266
+ }
267
+
268
+ export function parseModelAuthRuntimeCredential(secret, options = {}) {
269
+ if (
270
+ typeof secret !== "string" ||
271
+ !secret ||
272
+ Buffer.byteLength(secret, "utf8") > 64 * 1024
273
+ ) {
274
+ fail("model_auth_projection_incomplete");
275
+ }
276
+ let value;
277
+ try {
278
+ value = JSON.parse(secret);
279
+ } catch {
280
+ fail("model_auth_projection_incomplete");
281
+ }
282
+ if (containsRuntimeRefreshOwnership(value)) {
283
+ fail("model_auth_runtime_refresh_ownership_rejected");
284
+ }
285
+ if (
286
+ !exactKeys(value, RUNTIME_KEYS) ||
287
+ value.schemaVersion !== MODEL_AUTH_RUNTIME_SCHEMA ||
288
+ value.credentialPoolStrategy !== "fill_first" ||
289
+ !SAFE_ID.test(value.activationId ?? "") ||
290
+ !Number.isSafeInteger(value.bundleGeneration) ||
291
+ value.bundleGeneration < 1 ||
292
+ !SHA256.test(value.policyHash ?? "") ||
293
+ !Array.isArray(value.codex) ||
294
+ value.codex.length !== 2 ||
295
+ !exactKeys(value.openrouter, OPENROUTER_KEYS)
296
+ ) {
297
+ fail("model_auth_projection_incomplete");
298
+ }
299
+ const nowValue = options.now ?? new Date();
300
+ const nowMs =
301
+ nowValue instanceof Date ? nowValue.getTime() : Number(nowValue);
302
+ if (!Number.isFinite(nowMs)) fail("model_auth_projection_incomplete");
303
+ for (const [index, slot] of value.codex.entries()) {
304
+ const expected = EXPECTED_SLOT_IDENTITIES[index];
305
+ if (
306
+ !exactKeys(slot, SLOT_KEYS) ||
307
+ slot.slotId !== expected.slotId ||
308
+ slot.priority !== expected.priority ||
309
+ !bounded(slot.accessToken, 32, 32 * 1024) ||
310
+ !bounded(slot.accountId, 1, 512) ||
311
+ !bounded(slot.expiresAt, 10, 128) ||
312
+ !FINGERPRINT.test(slot.grantFingerprint ?? "")
313
+ ) {
314
+ fail("model_auth_projection_incomplete");
315
+ }
316
+ const declaredExpiry = Date.parse(slot.expiresAt);
317
+ const embeddedExpiry = jwtExpiry(slot.accessToken);
318
+ if (
319
+ !Number.isFinite(declaredExpiry) ||
320
+ embeddedExpiry === null ||
321
+ Math.abs(embeddedExpiry - declaredExpiry) > 1_000 ||
322
+ Math.min(declaredExpiry, embeddedExpiry) - nowMs <
323
+ MODEL_AUTH_MINIMUM_VALIDITY_MS
324
+ ) {
325
+ fail("model_auth_projection_validity_insufficient");
326
+ }
327
+ }
328
+ if (
329
+ !bounded(value.openrouter.apiKey, 20, 4096) ||
330
+ !FINGERPRINT.test(value.openrouter.keyFingerprint ?? "") ||
331
+ !Array.isArray(value.openrouter.models) ||
332
+ value.openrouter.models.length < 1 ||
333
+ value.openrouter.models.length > 8 ||
334
+ value.openrouter.models.some(
335
+ (model) => typeof model !== "string" || !MODEL_ID.test(model)
336
+ ) ||
337
+ new Set(value.openrouter.models).size !== value.openrouter.models.length
338
+ ) {
339
+ fail("model_auth_projection_incomplete");
340
+ }
341
+ const identity = projectionIdentity(value);
342
+ const store = accessOnlyStoreFor(value);
343
+ const sourceFingerprint = sha256(stableJson(identity)).slice(0, 16);
344
+ return Object.freeze({
345
+ provider: MODEL_AUTH_PROVIDER,
346
+ model: MODEL_AUTH_DEFAULT_MODEL,
347
+ reasoningEffort: MODEL_AUTH_REASONING_EFFORT,
348
+ activationId: value.activationId,
349
+ bundleGeneration: value.bundleGeneration,
350
+ policyHash: value.policyHash,
351
+ sourceFingerprint,
352
+ identity: Object.freeze(identity),
353
+ value: Object.freeze(value),
354
+ authJson: JSON.stringify(value),
355
+ hermesRootAuthJson: `${JSON.stringify(store, null, 2)}\n`,
356
+ testAccessToken: value.codex[0].accessToken,
357
+ });
358
+ }
359
+
360
+ export function modelAuthConfigOverlay(credential) {
361
+ if (!credential?.value || !SHA256.test(credential.policyHash ?? "")) {
362
+ fail("model_auth_projection_incomplete");
363
+ }
364
+ return Object.freeze({
365
+ model: Object.freeze({
366
+ default: MODEL_AUTH_DEFAULT_MODEL,
367
+ provider: MODEL_AUTH_PROVIDER,
368
+ reasoning_effort: MODEL_AUTH_REASONING_EFFORT,
369
+ bundle_generation: credential.bundleGeneration,
370
+ policy_hash: credential.policyHash,
371
+ }),
372
+ credential_pool_strategies: Object.freeze({
373
+ "openai-codex": "fill_first",
374
+ openrouter: "fill_first",
375
+ }),
376
+ fallback_providers: Object.freeze(
377
+ credential.value.openrouter.models.map((model) =>
378
+ Object.freeze({ provider: "openrouter", model })
379
+ )
380
+ ),
381
+ });
382
+ }
383
+
384
+ export function reconcileHermesModelAuth({
385
+ authStorePath,
386
+ markerPath,
387
+ credential,
388
+ lifecycle,
389
+ }) {
390
+ if (
391
+ typeof authStorePath !== "string" ||
392
+ !authStorePath ||
393
+ typeof markerPath !== "string" ||
394
+ !markerPath ||
395
+ !credential?.value ||
396
+ !["ON", "OFF"].includes(lifecycle)
397
+ ) {
398
+ fail("model_auth_store_rejected");
399
+ }
400
+ if (lifecycle === "OFF") {
401
+ return Object.freeze({
402
+ status: "desired_converged_materialization_pending_start",
403
+ materialized: false,
404
+ activationId: credential.activationId,
405
+ bundleGeneration: credential.bundleGeneration,
406
+ policyHash: credential.policyHash,
407
+ sourceFingerprint: credential.sourceFingerprint,
408
+ reseeded: false,
409
+ });
410
+ }
411
+ assertRegularFileOrMissing(authStorePath);
412
+ assertRegularFileOrMissing(markerPath);
413
+ const expectedMarker = markerFor(credential);
414
+ const priorMarker = parseMarker(markerPath);
415
+ const markerMatches = stableJson(priorMarker) === stableJson(expectedMarker);
416
+ const storeMatches = essentialStoreMatches(authStorePath, credential);
417
+ const reseeded = !markerMatches || !storeMatches;
418
+ if (reseeded) {
419
+ atomicPrivateFile(authStorePath, credential.hermesRootAuthJson);
420
+ atomicPrivateFile(
421
+ markerPath,
422
+ `${JSON.stringify(expectedMarker, null, 2)}\n`
423
+ );
424
+ } else {
425
+ chmodSync(authStorePath, 0o600);
426
+ chmodSync(markerPath, 0o600);
427
+ }
428
+ return Object.freeze({
429
+ status: "materialized_current",
430
+ materialized: true,
431
+ activationId: credential.activationId,
432
+ bundleGeneration: credential.bundleGeneration,
433
+ policyHash: credential.policyHash,
434
+ sourceFingerprint: credential.sourceFingerprint,
435
+ reseeded,
436
+ });
437
+ }
438
+
439
+ function safeRuntimeReason(value) {
440
+ return typeof value === "string" && /^[a-z0-9_.:-]{1,128}$/.test(value)
441
+ ? value
442
+ : null;
443
+ }
444
+
445
+ function resetAt(value) {
446
+ if (typeof value === "string" && Number.isFinite(Date.parse(value))) {
447
+ return new Date(value).toISOString();
448
+ }
449
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
450
+ const milliseconds = value > 1_000_000_000_000 ? value : value * 1000;
451
+ return new Date(milliseconds).toISOString();
452
+ }
453
+ return null;
454
+ }
455
+
456
+ export function readModelAuthMaterialization({ authStorePath, markerPath }) {
457
+ assertRegularFileOrMissing(authStorePath);
458
+ const marker = parseMarker(markerPath);
459
+ if (!marker || !existsSync(authStorePath)) {
460
+ return Object.freeze({
461
+ schemaVersion: MODEL_AUTH_READBACK_SCHEMA,
462
+ activationId: marker?.activationId ?? null,
463
+ bundleGeneration: marker?.bundleGeneration ?? null,
464
+ policyHash: marker?.policyHash ?? null,
465
+ provider: MODEL_AUTH_PROVIDER,
466
+ model: MODEL_AUTH_DEFAULT_MODEL,
467
+ fallbackDepth: marker?.models?.length ?? 0,
468
+ desiredStatus: marker ? "converged" : "unknown",
469
+ materializedStatus: "pending_start",
470
+ slots: Object.freeze([]),
471
+ openrouter: null,
472
+ activeProvider: null,
473
+ activeFallbackModelId: null,
474
+ });
475
+ }
476
+ let auth;
477
+ try {
478
+ auth = JSON.parse(readFileSync(authStorePath, "utf8"));
479
+ } catch {
480
+ fail("model_auth_store_rejected");
481
+ }
482
+ if (containsRuntimeRefreshOwnership(auth)) {
483
+ fail("model_auth_runtime_refresh_ownership_rejected");
484
+ }
485
+ const codex = auth?.credential_pool?.[MODEL_AUTH_PROVIDER];
486
+ const openrouter = auth?.credential_pool?.openrouter;
487
+ if (
488
+ !Array.isArray(codex) ||
489
+ codex.length !== 2 ||
490
+ !Array.isArray(openrouter)
491
+ ) {
492
+ fail("model_auth_store_rejected");
493
+ }
494
+ const slots = codex.map((entry, index) => {
495
+ const expected = EXPECTED_SLOT_IDENTITIES[index];
496
+ if (
497
+ entry?.id !== expected.slotId ||
498
+ entry?.priority !== expected.priority ||
499
+ !FINGERPRINT.test(entry?.secret_fingerprint ?? "") ||
500
+ !Number.isFinite(Date.parse(entry?.expires_at ?? ""))
501
+ ) {
502
+ fail("model_auth_store_rejected");
503
+ }
504
+ const state = ["ok", "exhausted", "dead"].includes(entry.last_status)
505
+ ? entry.last_status
506
+ : "ok";
507
+ return Object.freeze({
508
+ slotId: entry.id,
509
+ grantFingerprint: entry.secret_fingerprint,
510
+ expiresAt: new Date(entry.expires_at).toISOString(),
511
+ state,
512
+ lastErrorReason: safeRuntimeReason(entry.last_error_reason),
513
+ lastErrorResetAt: resetAt(entry.last_error_reset_at),
514
+ });
515
+ });
516
+ return Object.freeze({
517
+ schemaVersion: MODEL_AUTH_READBACK_SCHEMA,
518
+ activationId: marker.activationId,
519
+ bundleGeneration: marker.bundleGeneration,
520
+ policyHash: marker.policyHash,
521
+ provider: MODEL_AUTH_PROVIDER,
522
+ model: MODEL_AUTH_DEFAULT_MODEL,
523
+ fallbackDepth: marker.models.length,
524
+ desiredStatus: "converged",
525
+ materializedStatus: "current",
526
+ slots: Object.freeze(slots),
527
+ openrouter: Object.freeze({
528
+ keyFingerprint: marker.openrouterKeyFingerprint,
529
+ state: ["ok", "exhausted", "dead"].includes(openrouter[0]?.last_status)
530
+ ? openrouter[0].last_status
531
+ : "ok",
532
+ }),
533
+ activeProvider:
534
+ auth.active_provider === "openrouter"
535
+ ? "openrouter"
536
+ : MODEL_AUTH_PROVIDER,
537
+ activeFallbackModelId:
538
+ auth.active_provider === "openrouter" &&
539
+ typeof auth.active_model === "string" &&
540
+ marker.models.includes(auth.active_model)
541
+ ? auth.active_model
542
+ : null,
543
+ });
544
+ }
545
+
546
+ /**
547
+ * @param {unknown} readback
548
+ * @param {{activationId: string, bundleGeneration: number, policyHash: string} | null} [expected]
549
+ */
550
+ export function evaluateModelAuthReadback(readback, expected = null) {
551
+ const reject = (rejection) => ({ accepted: false, rejection });
552
+ if (!isRecord(readback)) return reject("readback_unparsable");
553
+ if (readback.schemaVersion !== MODEL_AUTH_READBACK_SCHEMA) {
554
+ return reject("readback_schema_mismatch");
555
+ }
556
+ if (readback.provider !== MODEL_AUTH_PROVIDER)
557
+ return reject("provider_mismatch");
558
+ if (readback.materializedStatus !== "current") {
559
+ return reject("materialization_pending_start");
560
+ }
561
+ if (expected && readback.activationId !== expected.activationId) {
562
+ return reject("activation_id_mismatch");
563
+ }
564
+ if (expected && readback.bundleGeneration !== expected.bundleGeneration) {
565
+ return reject("bundle_generation_mismatch");
566
+ }
567
+ if (expected && readback.policyHash !== expected.policyHash) {
568
+ return reject("policy_hash_mismatch");
569
+ }
570
+ if (!Array.isArray(readback.slots) || readback.slots.length !== 2) {
571
+ return reject("slot_pool_incomplete");
572
+ }
573
+ return { accepted: true, rejection: null };
574
+ }