@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.
@@ -16,12 +16,13 @@ import {
16
16
  } from "node:fs";
17
17
  import { dirname, join } from "node:path";
18
18
  import {
19
- FLY_CUSTOMER_MODEL_ID,
20
- FLY_CUSTOMER_MODEL_PROVIDER,
21
- FLY_CUSTOMER_MODEL_REASONING_EFFORT,
22
19
  evaluateModelAuthReadback,
23
- parseFlyCustomerModelCredential,
24
- } from "../fly-customer-model.mjs";
20
+ modelAuthConfigOverlay,
21
+ parseModelAuthRuntimeCredential,
22
+ readModelAuthMaterialization,
23
+ reconcileHermesModelAuth,
24
+ } from "../model-auth-reconciler.mjs";
25
+ import { createModelHealthReporter } from "../model-health-reporter.mjs";
25
26
  import {
26
27
  buildNativeHermesLaunch,
27
28
  clearCustomerRuntimeSecrets,
@@ -37,6 +38,7 @@ import {
37
38
  loadOrCreateFlyRuntimeIdentity,
38
39
  obtainFlyOidcToken,
39
40
  renewFlyCustomerControlSession,
41
+ signedWorkerAuthorization,
40
42
  } from "../fly-runtime-identity.mjs";
41
43
  import {
42
44
  ensureHermesSnapshotCron,
@@ -48,7 +50,6 @@ import {
48
50
  materializeNativeSlackCustomerProfile,
49
51
  validateNativeSlackCustomerProfile,
50
52
  } from "../profile-materializer.mjs";
51
- import { reconcileHermesRootModelAuth } from "./model-auth-store.mjs";
52
53
 
53
54
  const DATA_ROOT = "/opt/data";
54
55
  const PROFILES_ROOT = join(DATA_ROOT, "profiles");
@@ -70,7 +71,7 @@ const RUNTIME_GENERATION = Number(
70
71
  const BOOT_SESSION_ID = randomUUID();
71
72
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
72
73
  const INSTALLER_PACKAGE =
73
- process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.484";
74
+ process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.486-wip.phase169.20260804.2";
74
75
  const MCP_PACKAGE =
75
76
  process.env.SELLABLE_AGENT_MCP_PACKAGE ??
76
77
  "@sellable/mcp@0.1.805";
@@ -182,6 +183,22 @@ function compileDesired(claim, trustRootPem) {
182
183
  ) {
183
184
  throw new Error("customer_claim_contract_rejected");
184
185
  }
186
+ const modelEnvelope = claim.secretEnvelopes.modelCodex;
187
+ if (
188
+ model.provider !== "codex" ||
189
+ !/^[A-Za-z0-9_.:-]{1,256}$/.test(model.activationId ?? "") ||
190
+ !Number.isSafeInteger(model.bundleGeneration) ||
191
+ model.bundleGeneration < 1 ||
192
+ !/^[a-f0-9]{64}$/.test(model.policyHash ?? "") ||
193
+ !/^[a-f0-9]{16}$/.test(model.credentialFingerprint ?? "") ||
194
+ modelEnvelope.generation !== model.bundleGeneration ||
195
+ modelEnvelope.fingerprint !== model.credentialFingerprint ||
196
+ modelEnvelope.activationId !== model.activationId ||
197
+ modelEnvelope.bundleGeneration !== model.bundleGeneration ||
198
+ modelEnvelope.policyHash !== model.policyHash
199
+ ) {
200
+ throw new Error("customer_model_auth_identity_rejected");
201
+ }
185
202
  const routeGenerations = [
186
203
  ...new Set(selectedBindings.map((binding) => binding.routeGeneration)),
187
204
  ];
@@ -245,7 +262,10 @@ function compileDesired(claim, trustRootPem) {
245
262
  routeGeneration: routeGenerations[0],
246
263
  membershipGeneration: membershipGenerations[0],
247
264
  serviceCredentialGeneration: service.credentialGeneration,
248
- modelCredentialGeneration: model.credentialGeneration,
265
+ modelCredentialGeneration: model.bundleGeneration,
266
+ modelActivationId: model.activationId,
267
+ modelBundleGeneration: model.bundleGeneration,
268
+ modelPolicyHash: model.policyHash,
249
269
  runtimeGeneration: RUNTIME_GENERATION,
250
270
  selectedChannelIds,
251
271
  defaultChannelId: defaultBinding.channelId,
@@ -456,15 +476,25 @@ function writeRuntimeHermesConfig({
456
476
  const testModelOrigin = allowTestProviders()
457
477
  ? process.env.SELLABLE_AGENT_TEST_MODEL_ORIGIN
458
478
  : null;
459
- const modelCredential = parseFlyCustomerModelCredential(secrets.modelCodex);
460
- const modelAuthPath = join(secretRoot, "auth.json");
461
- atomicFile(modelAuthPath, modelCredential.authJson, 0o600);
462
- const modelAuthState = reconcileHermesRootModelAuth({
479
+ const modelCredential = parseModelAuthRuntimeCredential(secrets.modelCodex);
480
+ if (
481
+ modelCredential.activationId !== desired.modelActivationId ||
482
+ modelCredential.bundleGeneration !== desired.modelBundleGeneration ||
483
+ modelCredential.policyHash !== desired.modelPolicyHash
484
+ ) {
485
+ throw new Error("customer_model_auth_identity_rejected");
486
+ }
487
+ const modelBundlePath = join(secretRoot, "model-auth-runtime-v2.json");
488
+ atomicFile(modelBundlePath, modelCredential.authJson, 0o600);
489
+ const modelLifecycle =
490
+ claim.operation.action === "INSTANTIATE" ? "OFF" : "ON";
491
+ const modelAuthState = reconcileHermesModelAuth({
463
492
  authStorePath: hermesRootAuthStorePath,
464
493
  markerPath: hermesRootAuthMarkerPath,
465
- modelCredential,
466
- generation: desired.modelCredentialGeneration,
494
+ credential: modelCredential,
495
+ lifecycle: modelLifecycle,
467
496
  });
497
+ const modelConfig = modelAuthConfigOverlay(modelCredential);
468
498
  atomicJson(runtimeConfigPath, {
469
499
  ...baseConfig,
470
500
  ...(testModelOrigin
@@ -487,12 +517,7 @@ function writeRuntimeHermesConfig({
487
517
  ],
488
518
  }
489
519
  : {
490
- model: {
491
- default: FLY_CUSTOMER_MODEL_ID,
492
- provider: FLY_CUSTOMER_MODEL_PROVIDER,
493
- reasoning_effort: FLY_CUSTOMER_MODEL_REASONING_EFFORT,
494
- generation: desired.modelCredentialGeneration,
495
- },
520
+ ...modelConfig,
496
521
  }),
497
522
  mcp_servers: {
498
523
  ...baseConfig.mcp_servers,
@@ -520,10 +545,10 @@ function writeRuntimeHermesConfig({
520
545
  });
521
546
  const gatesPath = join(secretRoot, "gates.json");
522
547
  atomicJson(gatesPath, {
523
- lifecycle: "ON",
524
- dispatch: "ENABLED",
525
- route: "OPEN",
526
- output: "OPEN",
548
+ lifecycle: modelLifecycle,
549
+ dispatch: modelLifecycle === "ON" ? "ENABLED" : "DISABLED",
550
+ route: modelLifecycle === "ON" ? "OPEN" : "CLOSED",
551
+ output: modelLifecycle === "ON" ? "OPEN" : "CLOSED",
527
552
  });
528
553
  atomicJson(join(runtimeConfigRoot, "runtime-identity.json"), {
529
554
  runtimeId: RUNTIME_ID,
@@ -536,16 +561,20 @@ function writeRuntimeHermesConfig({
536
561
  membershipGeneration: desired.membershipGeneration,
537
562
  serviceCredentialGeneration: desired.serviceCredentialGeneration,
538
563
  modelCredentialGeneration: desired.modelCredentialGeneration,
564
+ modelActivationId: desired.modelActivationId,
565
+ modelBundleGeneration: desired.modelBundleGeneration,
566
+ modelPolicyHash: desired.modelPolicyHash,
539
567
  });
540
568
  return {
541
569
  runtimeHome: runtimeConfigRoot,
542
570
  runtimeConfigPath,
543
571
  runtimeMcpPath,
544
572
  gatesPath,
545
- modelAuthPath,
573
+ modelBundlePath,
546
574
  hermesRootAuthStorePath,
547
575
  hermesRootAuthMarkerPath,
548
576
  modelAuthSourceFingerprint: modelAuthState.sourceFingerprint,
577
+ modelAuthState,
549
578
  modelCredential,
550
579
  };
551
580
  }
@@ -585,7 +614,7 @@ function applicationRuntimeEnv({ desired, runtimeConfig, secrets }) {
585
614
  HERMES_HOME: DATA_ROOT,
586
615
  SELLABLE_HERMES_PROFILE_ID: desired.profileId,
587
616
  HERMES_CONFIG_PATH: runtimeConfig.runtimeConfigPath,
588
- CODEX_HOME: dirname(runtimeConfig.modelAuthPath),
617
+ CODEX_HOME: dirname(runtimeConfig.modelBundlePath),
589
618
  SELLABLE_AGENT_RUNTIME: "1",
590
619
  SELLABLE_AGENT_RUNTIME_GATE_FILE: runtimeConfig.gatesPath,
591
620
  SLACK_HOME_CHANNEL: desired.defaultChannelId,
@@ -745,59 +774,100 @@ async function proveProvisionedApplication({
745
774
  profileDigest,
746
775
  }) {
747
776
  const runtimeEnv = applicationRuntimeEnv({ desired, runtimeConfig, secrets });
748
- const [hermesVersion, mcpVersion, modelAuth] = await Promise.all([
777
+ const materializedModelAuth = runtimeConfig.modelAuthState.materialized;
778
+ const [hermesVersion, mcpVersion, hermesModelAuth] = await Promise.all([
749
779
  run(HERMES, ["--version"], { env: runtimeEnv, timeoutMs: 180_000 }),
750
780
  run(MCP, ["--version"], { env: runtimeEnv, timeoutMs: 180_000 }),
751
- run(
752
- HERMES_PYTHON,
753
- [
754
- "-c",
755
- [
756
- "import json",
757
- "from pathlib import Path",
758
- "from hermes_constants import get_default_hermes_root, get_hermes_home",
759
- "from hermes_cli.auth import resolve_codex_runtime_credentials",
760
- "value = resolve_codex_runtime_credentials(refresh_if_expiring=False)",
761
- 'root_auth = get_default_hermes_root() / "auth.json"',
762
- 'root_auth_marker = get_default_hermes_root() / ".sellable-agent" / "model-auth-source.json"',
763
- 'local_auth = get_hermes_home() / "auth.json"',
764
- "marker = json.loads(root_auth_marker.read_text()) if root_auth_marker.is_file() else {}",
765
- 'print(json.dumps({"provider": value.get("provider"), "source": value.get("source"), "source_class": "root_fallback" if root_auth.is_file() and not local_auth.exists() else "unexpected", "auth_mode": value.get("auth_mode"), "credential_present": bool(value.get("api_key")), "local_auth_entry_count": 0 if not local_auth.exists() else 1, "source_fingerprint": marker.get("sourceFingerprint"), "credential_generation": marker.get("generation")}))',
766
- ].join(";"),
767
- ],
768
- {
769
- env: { ...runtimeEnv, HERMES_HOME: runtimeConfig.runtimeHome },
770
- timeoutMs: 180_000,
771
- }
772
- ),
781
+ materializedModelAuth
782
+ ? run(
783
+ HERMES_PYTHON,
784
+ [
785
+ "-c",
786
+ [
787
+ "import json",
788
+ "from agent.credential_pool import load_pool",
789
+ 'pool = load_pool("openai-codex")',
790
+ "entry = pool.select() if pool else None",
791
+ 'print(json.dumps({"provider": "openai-codex", "source": "credential_pool", "credential_present": bool(entry and entry.runtime_api_key), "credential_id": getattr(entry, "id", None), "runtime_refresh_owned": bool(getattr(entry, "refresh_token", None))}))',
792
+ ].join(";"),
793
+ ],
794
+ {
795
+ env: { ...runtimeEnv, HERMES_HOME: runtimeConfig.runtimeHome },
796
+ timeoutMs: 180_000,
797
+ }
798
+ )
799
+ : Promise.resolve({
800
+ status: 0,
801
+ stdout: JSON.stringify({ materialization_pending_start: true }),
802
+ stderr: "",
803
+ }),
773
804
  ]);
774
- let modelAuthReadback = null;
805
+ let hermesModelAuthReadback = null;
775
806
  try {
776
- modelAuthReadback = JSON.parse(modelAuth.stdout);
807
+ hermesModelAuthReadback = JSON.parse(hermesModelAuth.stdout);
777
808
  } catch {
778
- modelAuthReadback = null;
809
+ hermesModelAuthReadback = null;
779
810
  }
780
- const modelAuthVerdict = evaluateModelAuthReadback(modelAuthReadback, {
781
- sourceFingerprint: runtimeConfig.modelAuthSourceFingerprint,
782
- generation: desired.modelCredentialGeneration,
783
- });
784
- if (modelAuth.status !== 0 || !modelAuthVerdict.accepted) {
785
- // Name the failed invariant: a bare rejection sent the 2026-07-28 outage
786
- // hunting the credential itself when the credential was valid and only its
787
- // storage shape in the root auth store had changed.
811
+ const modelAuthReadback = materializedModelAuth
812
+ ? readModelAuthMaterialization({
813
+ authStorePath: runtimeConfig.hermesRootAuthStorePath,
814
+ markerPath: runtimeConfig.hermesRootAuthMarkerPath,
815
+ })
816
+ : {
817
+ schemaVersion: "sellable-agent-model-auth-readback/v2",
818
+ activationId: desired.modelActivationId,
819
+ bundleGeneration: desired.modelBundleGeneration,
820
+ policyHash: desired.modelPolicyHash,
821
+ provider: "openai-codex",
822
+ model: runtimeConfig.modelCredential.model,
823
+ fallbackDepth:
824
+ runtimeConfig.modelCredential.value.openrouter.models.length,
825
+ desiredStatus: "converged",
826
+ materializedStatus: "pending_start",
827
+ slots: runtimeConfig.modelCredential.identity.slots.map((slot) => ({
828
+ slotId: slot.slotId,
829
+ grantFingerprint: slot.grantFingerprint,
830
+ expiresAt: new Date(slot.expiresAt).toISOString(),
831
+ state: "ok",
832
+ lastErrorReason: null,
833
+ lastErrorResetAt: null,
834
+ })),
835
+ openrouter: {
836
+ keyFingerprint:
837
+ runtimeConfig.modelCredential.identity.openrouterKeyFingerprint,
838
+ state: "ok",
839
+ },
840
+ };
841
+ const modelAuthVerdict = materializedModelAuth
842
+ ? evaluateModelAuthReadback(modelAuthReadback, {
843
+ activationId: desired.modelActivationId,
844
+ bundleGeneration: desired.modelBundleGeneration,
845
+ policyHash: desired.modelPolicyHash,
846
+ })
847
+ : { accepted: true, rejection: null };
848
+ if (
849
+ materializedModelAuth &&
850
+ (hermesModelAuth.status !== 0 ||
851
+ hermesModelAuthReadback?.provider !== "openai-codex" ||
852
+ hermesModelAuthReadback?.source !== "credential_pool" ||
853
+ hermesModelAuthReadback?.credential_present !== true ||
854
+ hermesModelAuthReadback?.runtime_refresh_owned !== false ||
855
+ !["dittto-primary", "kaigo-secondary"].includes(
856
+ hermesModelAuthReadback?.credential_id
857
+ ))
858
+ ) {
859
+ throw new Error("access_only_snapshot_infeasible");
860
+ }
861
+ if (!modelAuthVerdict.accepted) {
788
862
  throw new Error(
789
- `customer_model_auth_readback_rejected:${
790
- modelAuth.status !== 0
791
- ? "probe_failed"
792
- : (modelAuthVerdict.rejection ?? "unknown")
793
- }`
863
+ `customer_model_auth_readback_rejected:${modelAuthVerdict.rejection ?? "unknown"}`
794
864
  );
795
865
  }
796
866
  if (
797
867
  hermesVersion.status !== 0 ||
798
868
  mcpVersion.status !== 0 ||
799
869
  /Lazy-installing|pip install|npm install/i.test(
800
- `${hermesVersion.stdout}\n${hermesVersion.stderr}\n${mcpVersion.stdout}\n${mcpVersion.stderr}\n${modelAuth.stdout}\n${modelAuth.stderr}`
870
+ `${hermesVersion.stdout}\n${hermesVersion.stderr}\n${mcpVersion.stdout}\n${mcpVersion.stderr}\n${hermesModelAuth.stdout}\n${hermesModelAuth.stderr}`
801
871
  )
802
872
  ) {
803
873
  throw new Error("customer_runtime_binary_rejected");
@@ -806,7 +876,10 @@ async function proveProvisionedApplication({
806
876
  profileDigest,
807
877
  hermesOutput: hermesVersion.stdout,
808
878
  mcpOutput: mcpVersion.stdout,
809
- modelAuthOutput: modelAuth.stdout,
879
+ modelAuthOutput: `${hermesModelAuth.stdout}\n${JSON.stringify(
880
+ modelAuthReadback
881
+ )}`,
882
+ modelAuthReadback,
810
883
  };
811
884
  }
812
885
 
@@ -935,7 +1008,13 @@ function completionReceipt({
935
1008
  { code: "NATIVE_SLACK_GATEWAY", status: "TRUE" },
936
1009
  ]),
937
1010
  { code: "MCP_PREBAKED", status: "TRUE" },
938
- { code: "CODEX_OAUTH_READBACK", status: "TRUE" },
1011
+ {
1012
+ code:
1013
+ claim.operation.action === "INSTANTIATE"
1014
+ ? "MODEL_AUTH_DESIRED_PENDING_START"
1015
+ : "CODEX_OAUTH_READBACK",
1016
+ status: "TRUE",
1017
+ },
939
1018
  { code: "NO_BOOT_INSTALL", status: "TRUE" },
940
1019
  { code: "DEDICATED_SLACK_APP", status: "TRUE" },
941
1020
  ],
@@ -978,7 +1057,52 @@ async function flushHermesSnapshot({ client, profileId, profileRoot }) {
978
1057
  }
979
1058
  }
980
1059
 
981
- async function processOperation({ claim, exchange, client }) {
1060
+ function runtimeModelHealthReporter({ identity, exchange, runtimeConfig }) {
1061
+ return createModelHealthReporter({
1062
+ origin: ORIGIN,
1063
+ identity: {
1064
+ kind: "CUSTOMER",
1065
+ runtimeGeneration: RUNTIME_GENERATION,
1066
+ workloadKeyHash: exchange.publicKeyHash,
1067
+ machineId: exchange.workerId,
1068
+ adminRuntimeGenerationId: null,
1069
+ adminRuntimeGenerationFence: null,
1070
+ },
1071
+ observe: () =>
1072
+ readModelAuthMaterialization({
1073
+ authStorePath: runtimeConfig.hermesRootAuthStorePath,
1074
+ markerPath: runtimeConfig.hermesRootAuthMarkerPath,
1075
+ }),
1076
+ authorize: (body) =>
1077
+ signedWorkerAuthorization({
1078
+ identity,
1079
+ exchange,
1080
+ verb: "model-health",
1081
+ body,
1082
+ now: new Date(),
1083
+ unbound: true,
1084
+ }),
1085
+ onError: (error) => {
1086
+ process.stderr.write(
1087
+ `${JSON.stringify({
1088
+ ok: false,
1089
+ code:
1090
+ error instanceof Error
1091
+ ? error.message
1092
+ : "model_health_report_failed",
1093
+ })}\n`
1094
+ );
1095
+ },
1096
+ allowHttpLoopback: allowTestProviders(),
1097
+ sequencePath: join(
1098
+ DATA_ROOT,
1099
+ ".sellable-agent",
1100
+ "model-health-sequence.json"
1101
+ ),
1102
+ });
1103
+ }
1104
+
1105
+ async function processOperation({ claim, exchange, client, identity }) {
982
1106
  const trustRootPem = readFileSync(TRUST_ROOT, "utf8");
983
1107
  const { desired, toolInclude } = compileDesired(claim, trustRootPem);
984
1108
  mkdirSync(PROFILES_ROOT, { recursive: true, mode: 0o700 });
@@ -1042,6 +1166,27 @@ async function processOperation({ claim, exchange, client }) {
1042
1166
  const completed = await client.complete(receipt);
1043
1167
  if (completed?.accepted !== true)
1044
1168
  throw new Error("customer_completion_rejected");
1169
+ let modelHealthReporter = null;
1170
+ if (claim.operation.action !== "INSTANTIATE") {
1171
+ modelHealthReporter = runtimeModelHealthReporter({
1172
+ identity,
1173
+ exchange,
1174
+ runtimeConfig,
1175
+ });
1176
+ try {
1177
+ await modelHealthReporter.start("materialized");
1178
+ } catch (error) {
1179
+ process.stderr.write(
1180
+ `${JSON.stringify({
1181
+ ok: false,
1182
+ code:
1183
+ error instanceof Error
1184
+ ? error.message
1185
+ : "model_health_report_failed",
1186
+ })}\n`
1187
+ );
1188
+ }
1189
+ }
1045
1190
  atomicJson(join(DATA_ROOT, "sellable-agent-runtime-proof.json"), {
1046
1191
  ok: true,
1047
1192
  runtimeId: RUNTIME_ID,
@@ -1052,6 +1197,7 @@ async function processOperation({ claim, exchange, client }) {
1052
1197
  revisionId: desired.revisionId,
1053
1198
  observedLifecycle: receipt.observedLifecycle,
1054
1199
  profileDigest: proof.profileDigest,
1200
+ modelAuth: proof.modelAuthReadback,
1055
1201
  runtimeUid: process.getuid?.() ?? null,
1056
1202
  runtimeGid: process.getgid?.() ?? null,
1057
1203
  profileMode: (lstatSync(profileRoot).mode & 0o777)
@@ -1078,6 +1224,7 @@ async function processOperation({ claim, exchange, client }) {
1078
1224
  trustRootPem,
1079
1225
  action: claim.operation.action,
1080
1226
  gateway: proof.gateway ?? null,
1227
+ modelHealthReporter,
1081
1228
  };
1082
1229
  }
1083
1230
 
@@ -1220,8 +1367,14 @@ async function main() {
1220
1367
  exchange,
1221
1368
  client,
1222
1369
  });
1370
+ await activeRuntime?.modelHealthReporter?.stop();
1223
1371
  await stopNativeGateway(activeRuntime?.gateway);
1224
- activeRuntime = await processOperation({ claim, exchange, client });
1372
+ activeRuntime = await processOperation({
1373
+ claim,
1374
+ exchange,
1375
+ client,
1376
+ identity,
1377
+ });
1225
1378
  operationHeartbeat.assertHealthy();
1226
1379
  await operationHeartbeat.stop();
1227
1380
  operationHeartbeat = null;
@@ -1256,6 +1409,7 @@ async function main() {
1256
1409
  );
1257
1410
  }
1258
1411
  await stopNativeGateway(activeRuntime?.gateway);
1412
+ await activeRuntime?.modelHealthReporter?.stop();
1259
1413
  if (activeRuntime?.desired.profileId) {
1260
1414
  clearCustomerRuntimeSecrets({
1261
1415
  secretsRoot: SECRET_ROOT,
@@ -1,143 +1,6 @@
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
- const MARKER_VERSION = 1;
14
- const MARKER_KEYS = ["generation", "sourceFingerprint", "version"];
15
-
16
- const sha256 = (value) => createHash("sha256").update(value).digest("hex");
17
-
18
- function assertRegularFileOrMissing(path) {
19
- if (!existsSync(path)) return;
20
- const entry = lstatSync(path);
21
- if (entry.isSymbolicLink() || !entry.isFile()) {
22
- throw new Error("customer_model_auth_store_rejected");
23
- }
24
- }
25
-
26
- function atomicPrivateFile(path, bytes) {
27
- mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
28
- const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
29
- writeFileSync(temporary, bytes, { flag: "wx", mode: 0o600 });
30
- chmodSync(temporary, 0o600);
31
- renameSync(temporary, path);
32
- chmodSync(path, 0o600);
33
- }
34
-
35
- function readMarker(path) {
36
- assertRegularFileOrMissing(path);
37
- if (!existsSync(path)) return null;
38
- try {
39
- const marker = JSON.parse(readFileSync(path, "utf8"));
40
- if (
41
- !marker ||
42
- typeof marker !== "object" ||
43
- Array.isArray(marker) ||
44
- Object.keys(marker).sort().join("\0") !==
45
- [...MARKER_KEYS].sort().join("\0") ||
46
- marker.version !== MARKER_VERSION ||
47
- !Number.isSafeInteger(marker.generation) ||
48
- marker.generation < 1 ||
49
- !/^[a-f0-9]{16}$/.test(marker.sourceFingerprint)
50
- ) {
51
- return null;
52
- }
53
- return marker;
54
- } catch {
55
- return null;
56
- }
57
- }
58
-
59
- function usableHermesRootAuth(path) {
60
- if (!existsSync(path)) return false;
61
- try {
62
- const value = JSON.parse(readFileSync(path, "utf8"));
63
- const provider = value?.providers?.["openai-codex"];
64
- const pool = value?.credential_pool?.["openai-codex"];
65
- return Boolean(
66
- value?.active_provider === "openai-codex" &&
67
- provider?.auth_mode === "chatgpt" &&
68
- provider.tokens &&
69
- ["id_token", "access_token", "refresh_token", "account_id"].every(
70
- (key) =>
71
- typeof provider.tokens[key] === "string" &&
72
- provider.tokens[key].length > 0
73
- ) &&
74
- Array.isArray(pool) &&
75
- pool.length > 0
76
- );
77
- } catch {
78
- return false;
79
- }
80
- }
81
-
82
- /**
83
- * Reconcile Hermes' persistent root auth store with the sealed control-plane
84
- * credential without undoing a later in-place OAuth refresh.
85
- *
86
- * The marker is written only after the matching auth store is durable. A
87
- * missing or mismatched marker therefore means the root store must be reseeded,
88
- * even when runtime-identity.json already records the new generation after an
89
- * interrupted or overlapping reconciliation.
90
- */
91
- export function reconcileHermesRootModelAuth({
92
- authStorePath,
93
- markerPath,
94
- modelCredential,
95
- generation,
96
- }) {
97
- if (
98
- typeof authStorePath !== "string" ||
99
- !authStorePath ||
100
- typeof markerPath !== "string" ||
101
- !markerPath ||
102
- !Number.isSafeInteger(generation) ||
103
- generation < 1 ||
104
- !modelCredential ||
105
- typeof modelCredential.authJson !== "string" ||
106
- typeof modelCredential.hermesRootAuthJson !== "string"
107
- ) {
108
- throw new Error("customer_model_auth_store_rejected");
109
- }
110
-
111
- assertRegularFileOrMissing(authStorePath);
112
- const sourceFingerprint = sha256(modelCredential.authJson).slice(0, 16);
113
- const priorMarker = readMarker(markerPath);
114
- const markerMatches =
115
- priorMarker?.generation === generation &&
116
- priorMarker.sourceFingerprint === sourceFingerprint;
117
- const authStoreUsable = usableHermesRootAuth(authStorePath);
118
-
119
- if (!markerMatches || !authStoreUsable) {
120
- atomicPrivateFile(authStorePath, modelCredential.hermesRootAuthJson);
121
- atomicPrivateFile(
122
- markerPath,
123
- `${JSON.stringify(
124
- {
125
- version: MARKER_VERSION,
126
- generation,
127
- sourceFingerprint,
128
- },
129
- null,
130
- 2
131
- )}\n`
132
- );
133
- } else {
134
- chmodSync(authStorePath, 0o600);
135
- chmodSync(markerPath, 0o600);
136
- }
137
-
138
- return Object.freeze({
139
- sourceFingerprint,
140
- generation,
141
- reseeded: !markerMatches || !authStoreUsable,
142
- });
143
- }
1
+ // Compatibility-only export. Runtime behavior lives in the neutral module
2
+ // copied byte-for-byte into both CUSTOMER and ADMIN image contexts.
3
+ export {
4
+ reconcileHermesModelAuth,
5
+ reconcileHermesModelAuth as reconcileHermesRootModelAuth,
6
+ } from "../model-auth-reconciler.mjs";