@rosthq/cli 0.7.164 → 0.7.165

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.
package/dist/index.js CHANGED
@@ -472,13 +472,13 @@ function __disposeResources(env) {
472
472
  }
473
473
  return next();
474
474
  }
475
- function __rewriteRelativeImportExtension(path15, preserveJsx) {
476
- if (typeof path15 === "string" && /^\.\.?\//.test(path15)) {
477
- return path15.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
475
+ function __rewriteRelativeImportExtension(path16, preserveJsx) {
476
+ if (typeof path16 === "string" && /^\.\.?\//.test(path16)) {
477
+ return path16.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
478
478
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
479
479
  });
480
480
  }
481
- return path15;
481
+ return path16;
482
482
  }
483
483
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
484
484
  var init_tslib_es6 = __esm({
@@ -14343,10 +14343,10 @@ function mergeDefs(...defs) {
14343
14343
  function cloneDef(schema) {
14344
14344
  return mergeDefs(schema._zod.def);
14345
14345
  }
14346
- function getElementAtPath(obj, path15) {
14347
- if (!path15)
14346
+ function getElementAtPath(obj, path16) {
14347
+ if (!path16)
14348
14348
  return obj;
14349
- return path15.reduce((acc, key) => acc?.[key], obj);
14349
+ return path16.reduce((acc, key) => acc?.[key], obj);
14350
14350
  }
14351
14351
  function promiseAllObject(promisesObj) {
14352
14352
  const keys = Object.keys(promisesObj);
@@ -14755,11 +14755,11 @@ function explicitlyAborted(x, startIndex = 0) {
14755
14755
  }
14756
14756
  return false;
14757
14757
  }
14758
- function prefixIssues(path15, issues) {
14758
+ function prefixIssues(path16, issues) {
14759
14759
  return issues.map((iss) => {
14760
14760
  var _a3;
14761
14761
  (_a3 = iss).path ?? (_a3.path = []);
14762
- iss.path.unshift(path15);
14762
+ iss.path.unshift(path16);
14763
14763
  return iss;
14764
14764
  });
14765
14765
  }
@@ -14906,16 +14906,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
14906
14906
  }
14907
14907
  function formatError(error51, mapper = (issue2) => issue2.message) {
14908
14908
  const fieldErrors = { _errors: [] };
14909
- const processError = (error52, path15 = []) => {
14909
+ const processError = (error52, path16 = []) => {
14910
14910
  for (const issue2 of error52.issues) {
14911
14911
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14912
- issue2.errors.map((issues) => processError({ issues }, [...path15, ...issue2.path]));
14912
+ issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
14913
14913
  } else if (issue2.code === "invalid_key") {
14914
- processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
14914
+ processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
14915
14915
  } else if (issue2.code === "invalid_element") {
14916
- processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
14916
+ processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
14917
14917
  } else {
14918
- const fullpath = [...path15, ...issue2.path];
14918
+ const fullpath = [...path16, ...issue2.path];
14919
14919
  if (fullpath.length === 0) {
14920
14920
  fieldErrors._errors.push(mapper(issue2));
14921
14921
  } else {
@@ -14942,17 +14942,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
14942
14942
  }
14943
14943
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
14944
14944
  const result = { errors: [] };
14945
- const processError = (error52, path15 = []) => {
14945
+ const processError = (error52, path16 = []) => {
14946
14946
  var _a3, _b;
14947
14947
  for (const issue2 of error52.issues) {
14948
14948
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14949
- issue2.errors.map((issues) => processError({ issues }, [...path15, ...issue2.path]));
14949
+ issue2.errors.map((issues) => processError({ issues }, [...path16, ...issue2.path]));
14950
14950
  } else if (issue2.code === "invalid_key") {
14951
- processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
14951
+ processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
14952
14952
  } else if (issue2.code === "invalid_element") {
14953
- processError({ issues: issue2.issues }, [...path15, ...issue2.path]);
14953
+ processError({ issues: issue2.issues }, [...path16, ...issue2.path]);
14954
14954
  } else {
14955
- const fullpath = [...path15, ...issue2.path];
14955
+ const fullpath = [...path16, ...issue2.path];
14956
14956
  if (fullpath.length === 0) {
14957
14957
  result.errors.push(mapper(issue2));
14958
14958
  continue;
@@ -14984,8 +14984,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
14984
14984
  }
14985
14985
  function toDotPath(_path) {
14986
14986
  const segs = [];
14987
- const path15 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14988
- for (const seg of path15) {
14987
+ const path16 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14988
+ for (const seg of path16) {
14989
14989
  if (typeof seg === "number")
14990
14990
  segs.push(`[${seg}]`);
14991
14991
  else if (typeof seg === "symbol")
@@ -27677,13 +27677,13 @@ function resolveRef(ref, ctx) {
27677
27677
  if (!ref.startsWith("#")) {
27678
27678
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
27679
27679
  }
27680
- const path15 = ref.slice(1).split("/").filter(Boolean);
27681
- if (path15.length === 0) {
27680
+ const path16 = ref.slice(1).split("/").filter(Boolean);
27681
+ if (path16.length === 0) {
27682
27682
  return ctx.rootSchema;
27683
27683
  }
27684
27684
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
27685
- if (path15[0] === defsKey) {
27686
- const key = path15[1];
27685
+ if (path16[0] === defsKey) {
27686
+ const key = path16[1];
27687
27687
  if (!key || !ctx.defs[key]) {
27688
27688
  throw new Error(`Reference not found: ${ref}`);
27689
27689
  }
@@ -29030,6 +29030,7 @@ var storedConfirmationPolicySchema = external_exports.object({
29030
29030
  }).strict();
29031
29031
 
29032
29032
  // ../../packages/protocol/src/runner-setup.ts
29033
+ import { createHash, timingSafeEqual } from "node:crypto";
29033
29034
  var uuid3 = external_exports.string().uuid();
29034
29035
  var isoDateTime = external_exports.string().datetime({ offset: true });
29035
29036
  var sha256Hex = external_exports.string().regex(/^[0-9a-f]{64}$/, "expected a lowercase sha-256 hex digest");
@@ -29223,6 +29224,7 @@ var runnerSetupValidationAttemptSchema = external_exports.object({
29223
29224
  (attempt) => attempt.kind === "aicos_acceptance" === (attempt.activation_receipt_id !== null),
29224
29225
  "aicos_acceptance must be attributed to an activation receipt"
29225
29226
  );
29227
+ var RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS = ["runner_setup.status"];
29226
29228
  var RUNNER_SETUP_PROBE_CAPABILITY_VERSION = 1;
29227
29229
  var runnerSetupValidationMcpCapabilityPayloadSchema = external_exports.object({
29228
29230
  capability_version: external_exports.literal(RUNNER_SETUP_PROBE_CAPABILITY_VERSION),
@@ -29241,6 +29243,18 @@ var runnerSetupValidationMcpConfigSchema = external_exports.object({
29241
29243
  token: external_exports.string().min(1).max(4096),
29242
29244
  expires_at: isoDateTime
29243
29245
  }).strict();
29246
+ function runnerSetupProbeNonceProof(input) {
29247
+ const preimage = [
29248
+ "rost.runner-setup.probe.v2",
29249
+ input.nonce,
29250
+ input.setup_id,
29251
+ input.job_id,
29252
+ input.runtime,
29253
+ input.runner_id,
29254
+ String(input.service_key_generation)
29255
+ ].join("\n");
29256
+ return createHash("sha256").update(preimage).digest("hex");
29257
+ }
29244
29258
  var runnerSetupValidationClaimRequestSchema = external_exports.object({
29245
29259
  setup_id: uuid3,
29246
29260
  runtime: runnerSetupRuntimeSchema
@@ -30976,7 +30990,7 @@ var forbiddenImportKeys = /* @__PURE__ */ new Set([
30976
30990
  "integration_id",
30977
30991
  "integrationId"
30978
30992
  ]);
30979
- function rejectForbiddenImportShape(value, ctx, path15 = []) {
30993
+ function rejectForbiddenImportShape(value, ctx, path16 = []) {
30980
30994
  if (value === null || value === void 0) {
30981
30995
  return;
30982
30996
  }
@@ -30984,21 +30998,21 @@ function rejectForbiddenImportShape(value, ctx, path15 = []) {
30984
30998
  if (hasSecretShapedValue(value)) {
30985
30999
  ctx.addIssue({
30986
31000
  code: external_exports.ZodIssueCode.custom,
30987
- path: path15,
31001
+ path: path16,
30988
31002
  message: "Definition imports cannot contain secret-shaped values. Provide credentials later through vault-backed connection flows."
30989
31003
  });
30990
31004
  }
30991
31005
  return;
30992
31006
  }
30993
31007
  if (Array.isArray(value)) {
30994
- value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...path15, index]));
31008
+ value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...path16, index]));
30995
31009
  return;
30996
31010
  }
30997
31011
  if (typeof value !== "object") {
30998
31012
  return;
30999
31013
  }
31000
31014
  for (const [key, nested] of Object.entries(value)) {
31001
- const nestedPath = [...path15, key];
31015
+ const nestedPath = [...path16, key];
31002
31016
  if (forbiddenImportKeys.has(key)) {
31003
31017
  ctx.addIssue({
31004
31018
  code: external_exports.ZodIssueCode.custom,
@@ -35935,7 +35949,7 @@ var preflightArtifactDispositionSchema = external_exports.object({
35935
35949
  // on this envelope.
35936
35950
  transformations: external_exports.array(preflightTransformationSchema)
35937
35951
  }).strict().superRefine((data, ctx) => {
35938
- const add = (path15, message) => ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path15, message });
35952
+ const add = (path16, message) => ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path16, message });
35939
35953
  const reasonIsBlank = data.reason !== null && data.reason.trim().length === 0;
35940
35954
  if (reasonIsBlank) add(["reason"], "reason must be nonblank when supplied");
35941
35955
  if (data.presence === "missing") {
@@ -36974,14 +36988,14 @@ var COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES = 5e3;
36974
36988
  function jsonByteLength(value) {
36975
36989
  return new TextEncoder().encode(JSON.stringify(value)).byteLength;
36976
36990
  }
36977
- function boundCharterList(value, path15, ctx) {
36991
+ function boundCharterList(value, path16, ctx) {
36978
36992
  if (value.length > COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST) {
36979
36993
  ctx.addIssue({
36980
36994
  code: external_exports.ZodIssueCode.too_big,
36981
36995
  maximum: COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST,
36982
36996
  origin: "array",
36983
36997
  inclusive: true,
36984
- path: path15,
36998
+ path: path16,
36985
36999
  message: `Complete-Seat Charter lists are limited to ${COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST} entries.`
36986
37000
  });
36987
37001
  }
@@ -36998,7 +37012,7 @@ function addCompleteSeatCharterBounds(charter, ctx) {
36998
37012
  boundCharterList(charter.unanswered_boundaries, ["unanswered_boundaries"], ctx);
36999
37013
  }
37000
37014
  let argumentNodes = 0;
37001
- const visitArgument = (value, depth, path15) => {
37015
+ const visitArgument = (value, depth, path16) => {
37002
37016
  argumentNodes += 1;
37003
37017
  if (argumentNodes > COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES) {
37004
37018
  return;
@@ -37006,15 +37020,15 @@ function addCompleteSeatCharterBounds(charter, ctx) {
37006
37020
  if (depth > COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH) {
37007
37021
  ctx.addIssue({
37008
37022
  code: external_exports.ZodIssueCode.custom,
37009
- path: path15,
37023
+ path: path16,
37010
37024
  message: `Complete-Seat permission arguments are limited to ${COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH} nested levels.`
37011
37025
  });
37012
37026
  return;
37013
37027
  }
37014
37028
  if (Array.isArray(value)) {
37015
- value.forEach((entry, index) => visitArgument(entry, depth + 1, [...path15, index]));
37029
+ value.forEach((entry, index) => visitArgument(entry, depth + 1, [...path16, index]));
37016
37030
  } else if (value && typeof value === "object") {
37017
- Object.entries(value).forEach(([key, entry]) => visitArgument(entry, depth + 1, [...path15, key]));
37031
+ Object.entries(value).forEach(([key, entry]) => visitArgument(entry, depth + 1, [...path16, key]));
37018
37032
  }
37019
37033
  };
37020
37034
  charter.permission_manifest.forEach((permission, index) => {
@@ -37175,9 +37189,9 @@ var createCompleteSeatRequestSchema = external_exports.object({
37175
37189
  }
37176
37190
  const useKeys = /* @__PURE__ */ new Set();
37177
37191
  for (const [index, use] of value.seat.source_uses.entries()) {
37178
- const path15 = ["seat", "source_uses", index, "source_key"];
37179
- if (!sourceKeys.has(use.source_key)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path15, message: "Source use must resolve to the request source registry." });
37180
- if (useKeys.has(use.source_key)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path15, message: "Source use must not be duplicated." });
37192
+ const path16 = ["seat", "source_uses", index, "source_key"];
37193
+ if (!sourceKeys.has(use.source_key)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path16, message: "Source use must resolve to the request source registry." });
37194
+ if (useKeys.has(use.source_key)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path16, message: "Source use must not be duplicated." });
37181
37195
  useKeys.add(use.source_key);
37182
37196
  }
37183
37197
  if (value.seat.human_staffing.kind === "planned" && value.seat.human_staffing.source_ref) {
@@ -37190,8 +37204,8 @@ var createCompleteSeatRequestSchema = external_exports.object({
37190
37204
  ...value.seat.parent ? [{ ref: value.seat.parent, path: ["seat", "parent"] }] : [],
37191
37205
  ...value.seat.agent ? [{ ref: value.seat.agent.steward, path: ["seat", "agent", "steward"] }] : []
37192
37206
  ];
37193
- refs.forEach(({ ref, path: path15 }) => {
37194
- if (ref.kind !== "existing") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path15, message: "Direct complete-seat requests require existing Seat references." });
37207
+ refs.forEach(({ ref, path: path16 }) => {
37208
+ if (ref.kind !== "existing") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path16, message: "Direct complete-seat requests require existing Seat references." });
37195
37209
  });
37196
37210
  }).transform((value) => ({
37197
37211
  ...value,
@@ -37269,7 +37283,7 @@ var completeSeatApprovalProjectionSchema = external_exports.object({
37269
37283
 
37270
37284
  // ../../packages/protocol/src/setup-application.ts
37271
37285
  import { Buffer as Buffer2 } from "node:buffer";
37272
- import { createHash } from "node:crypto";
37286
+ import { createHash as createHash2 } from "node:crypto";
37273
37287
 
37274
37288
  // ../../packages/protocol/src/baserow-filter-operators.ts
37275
37289
  var baserowFilterOperatorSchema = external_exports.enum([
@@ -37687,26 +37701,26 @@ var PROHIBITED_CONTENT_MESSAGE_PREFIX = "Prohibited content";
37687
37701
  function prohibitedContentMessage(rule_id, found) {
37688
37702
  return `${PROHIBITED_CONTENT_MESSAGE_PREFIX} [${rule_id}]: ${found}. ${CONTENT_LINT_REMEDY[rule_id]}`;
37689
37703
  }
37690
- function isIndexedPath(path15, family, field4) {
37691
- return path15.length === 3 && path15[0] === family && typeof path15[1] === "number" && path15[2] === field4;
37704
+ function isIndexedPath(path16, family, field4) {
37705
+ return path16.length === 3 && path16[0] === family && typeof path16[1] === "number" && path16[2] === field4;
37692
37706
  }
37693
- function isSchemaProvenMachineString(path15) {
37694
- if (path15.length === 1 && path15[0] === "managed_inference_hard_cap_usd") {
37707
+ function isSchemaProvenMachineString(path16) {
37708
+ if (path16.length === 1 && path16[0] === "managed_inference_hard_cap_usd") {
37695
37709
  return true;
37696
37710
  }
37697
- if (isIndexedPath(path15, "sources", "source_upload_id") || isIndexedPath(path15, "sources", "sha256") || isIndexedPath(path15, "reconciliation", "source_sha256") || isIndexedPath(path15, "reconciliation", "normalized_identity_digest") || isIndexedPath(path15, "signal_readings", "period_start") || isIndexedPath(path15, "signal_readings", "value") || isIndexedPath(path15, "tasks", "due_on")) {
37711
+ if (isIndexedPath(path16, "sources", "source_upload_id") || isIndexedPath(path16, "sources", "sha256") || isIndexedPath(path16, "reconciliation", "source_sha256") || isIndexedPath(path16, "reconciliation", "normalized_identity_digest") || isIndexedPath(path16, "signal_readings", "period_start") || isIndexedPath(path16, "signal_readings", "value") || isIndexedPath(path16, "tasks", "due_on")) {
37698
37712
  return true;
37699
37713
  }
37700
- if (path15.length === 2 && path15[0] === "cycle" && (path15[1] === "starts_on" || path15[1] === "ends_on")) {
37714
+ if (path16.length === 2 && path16[0] === "cycle" && (path16[1] === "starts_on" || path16[1] === "ends_on")) {
37701
37715
  return true;
37702
37716
  }
37703
- if (path15.length >= 3 && path15.at(-3) === "citations" && typeof path15.at(-2) === "number" && path15.at(-1) === "document_id") {
37717
+ if (path16.length >= 3 && path16.at(-3) === "citations" && typeof path16.at(-2) === "number" && path16.at(-1) === "document_id") {
37704
37718
  return true;
37705
37719
  }
37706
- if (path15.length === 6 && path15[0] === "seats" && typeof path15[1] === "number" && path15[2] === "charter" && path15[3] === "permission_manifest" && typeof path15[4] === "number" && path15[5] === "integration_id") {
37720
+ if (path16.length === 6 && path16[0] === "seats" && typeof path16[1] === "number" && path16[2] === "charter" && path16[3] === "permission_manifest" && typeof path16[4] === "number" && path16[5] === "integration_id") {
37707
37721
  return true;
37708
37722
  }
37709
- return path15.length === 6 && path15[0] === "seats" && typeof path15[1] === "number" && path15[2] === "agent" && path15[3] === "skills" && typeof path15[4] === "number" && (path15[5] === "skill_version_id" || path15[5] === "content_sha256");
37723
+ return path16.length === 6 && path16[0] === "seats" && typeof path16[1] === "number" && path16[2] === "agent" && path16[3] === "skills" && typeof path16[4] === "number" && (path16[5] === "skill_version_id" || path16[5] === "content_sha256");
37710
37724
  }
37711
37725
  function jsonByteLength2(value) {
37712
37726
  try {
@@ -37716,20 +37730,20 @@ function jsonByteLength2(value) {
37716
37730
  return Number.POSITIVE_INFINITY;
37717
37731
  }
37718
37732
  }
37719
- function prohibitedSetupContentViolations(value, path15 = []) {
37733
+ function prohibitedSetupContentViolations(value, path16 = []) {
37720
37734
  if (typeof value === "string") {
37721
- if (isSchemaProvenMachineString(path15)) return [];
37735
+ if (isSchemaProvenMachineString(path16)) return [];
37722
37736
  return contentViolationsForString(value).map(({ rule_id, found }) => ({
37723
37737
  rule_id,
37724
- path: path15,
37738
+ path: path16,
37725
37739
  message: prohibitedContentMessage(rule_id, found)
37726
37740
  }));
37727
37741
  }
37728
37742
  if (Array.isArray(value)) {
37729
- return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...path15, index]));
37743
+ return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...path16, index]));
37730
37744
  }
37731
37745
  if (value && typeof value === "object") {
37732
- return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...path15, key]));
37746
+ return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...path16, key]));
37733
37747
  }
37734
37748
  return [];
37735
37749
  }
@@ -38046,7 +38060,7 @@ function canonicalJson(value) {
38046
38060
  return JSON.stringify(value);
38047
38061
  }
38048
38062
  function digestCanonicalJson(value) {
38049
- return `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
38063
+ return `sha256:${createHash2("sha256").update(canonicalJson(value)).digest("hex")}`;
38050
38064
  }
38051
38065
  var onboardingSetupOutputSchema = external_exports.object({
38052
38066
  application_id: uuidSchema19,
@@ -43709,7 +43723,7 @@ var CommandClient = class {
43709
43723
  this._credentialKind = options.credentialKind;
43710
43724
  }
43711
43725
  async execute(commandId, body = {}, options = {}) {
43712
- const path15 = `/api/commands/${encodeURIComponent(commandId)}`;
43726
+ const path16 = `/api/commands/${encodeURIComponent(commandId)}`;
43713
43727
  const headers = {
43714
43728
  authorization: `Bearer ${this.token}`,
43715
43729
  "content-type": "application/json"
@@ -43717,7 +43731,7 @@ var CommandClient = class {
43717
43731
  if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
43718
43732
  headers["x-rost-seat"] = options.targetSeatId;
43719
43733
  }
43720
- const response = await this.fetchImpl(`${this.appUrl}${path15}`, {
43734
+ const response = await this.fetchImpl(`${this.appUrl}${path16}`, {
43721
43735
  method: "POST",
43722
43736
  headers,
43723
43737
  body: JSON.stringify(body)
@@ -43730,7 +43744,7 @@ var CommandClient = class {
43730
43744
  const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
43731
43745
  throw new CommandClientError(
43732
43746
  response.status,
43733
- `server returned HTTP ${response.status} (non-JSON) from ${path15}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
43747
+ `server returned HTTP ${response.status} (non-JSON) from ${path16}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
43734
43748
  );
43735
43749
  }
43736
43750
  const parsed = commandResponseSchema.parse(raw);
@@ -49061,8 +49075,8 @@ var IcebergError = class extends Error {
49061
49075
  return this.status === 419;
49062
49076
  }
49063
49077
  };
49064
- function buildUrl(baseUrl, path15, query) {
49065
- const url2 = new URL(path15, baseUrl);
49078
+ function buildUrl(baseUrl, path16, query) {
49079
+ const url2 = new URL(path16, baseUrl);
49066
49080
  if (query) {
49067
49081
  for (const [key, value] of Object.entries(query)) {
49068
49082
  if (value !== void 0) {
@@ -49092,12 +49106,12 @@ function createFetchClient(options) {
49092
49106
  return {
49093
49107
  async request({
49094
49108
  method,
49095
- path: path15,
49109
+ path: path16,
49096
49110
  query,
49097
49111
  body,
49098
49112
  headers
49099
49113
  }) {
49100
- const url2 = buildUrl(options.baseUrl, path15, query);
49114
+ const url2 = buildUrl(options.baseUrl, path16, query);
49101
49115
  const authHeaders = await buildAuthHeaders(options.auth);
49102
49116
  const res = await fetchFn(url2, {
49103
49117
  method,
@@ -49959,7 +49973,7 @@ var StorageFileApi = class extends BaseApiClient {
49959
49973
  * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
49960
49974
  * @param fileBody The body of the file to be stored in the bucket.
49961
49975
  */
49962
- async uploadOrUpdate(method, path15, fileBody, fileOptions) {
49976
+ async uploadOrUpdate(method, path16, fileBody, fileOptions) {
49963
49977
  var _this = this;
49964
49978
  return _this.handleOperation(async () => {
49965
49979
  let body;
@@ -49983,7 +49997,7 @@ var StorageFileApi = class extends BaseApiClient {
49983
49997
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
49984
49998
  }
49985
49999
  if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
49986
- const cleanPath = _this._removeEmptyFolders(path15);
50000
+ const cleanPath = _this._removeEmptyFolders(path16);
49987
50001
  const _path = _this._getFinalPath(cleanPath);
49988
50002
  const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
49989
50003
  return {
@@ -50060,8 +50074,8 @@ var StorageFileApi = class extends BaseApiClient {
50060
50074
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50061
50075
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
50062
50076
  */
50063
- async upload(path15, fileBody, fileOptions) {
50064
- return this.uploadOrUpdate("POST", path15, fileBody, fileOptions);
50077
+ async upload(path16, fileBody, fileOptions) {
50078
+ return this.uploadOrUpdate("POST", path16, fileBody, fileOptions);
50065
50079
  }
50066
50080
  /**
50067
50081
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -50101,9 +50115,9 @@ var StorageFileApi = class extends BaseApiClient {
50101
50115
  * - `objects` table permissions: none
50102
50116
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50103
50117
  */
50104
- async uploadToSignedUrl(path15, token, fileBody, fileOptions) {
50118
+ async uploadToSignedUrl(path16, token, fileBody, fileOptions) {
50105
50119
  var _this3 = this;
50106
- const cleanPath = _this3._removeEmptyFolders(path15);
50120
+ const cleanPath = _this3._removeEmptyFolders(path16);
50107
50121
  const _path = _this3._getFinalPath(cleanPath);
50108
50122
  const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
50109
50123
  url2.searchParams.set("token", token);
@@ -50172,10 +50186,10 @@ var StorageFileApi = class extends BaseApiClient {
50172
50186
  * - `objects` table permissions: `insert`
50173
50187
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50174
50188
  */
50175
- async createSignedUploadUrl(path15, options) {
50189
+ async createSignedUploadUrl(path16, options) {
50176
50190
  var _this4 = this;
50177
50191
  return _this4.handleOperation(async () => {
50178
- let _path = _this4._getFinalPath(path15);
50192
+ let _path = _this4._getFinalPath(path16);
50179
50193
  const headers = _objectSpread22({}, _this4.headers);
50180
50194
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
50181
50195
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -50184,7 +50198,7 @@ var StorageFileApi = class extends BaseApiClient {
50184
50198
  if (!token) throw new StorageError("No token returned by API");
50185
50199
  return {
50186
50200
  signedUrl: url2.toString(),
50187
- path: path15,
50201
+ path: path16,
50188
50202
  token
50189
50203
  };
50190
50204
  });
@@ -50244,8 +50258,8 @@ var StorageFileApi = class extends BaseApiClient {
50244
50258
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50245
50259
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
50246
50260
  */
50247
- async update(path15, fileBody, fileOptions) {
50248
- return this.uploadOrUpdate("PUT", path15, fileBody, fileOptions);
50261
+ async update(path16, fileBody, fileOptions) {
50262
+ return this.uploadOrUpdate("PUT", path16, fileBody, fileOptions);
50249
50263
  }
50250
50264
  /**
50251
50265
  * Moves an existing file to a new path in the same bucket.
@@ -50396,10 +50410,10 @@ var StorageFileApi = class extends BaseApiClient {
50396
50410
  * - `objects` table permissions: `select`
50397
50411
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50398
50412
  */
50399
- async createSignedUrl(path15, expiresIn, options) {
50413
+ async createSignedUrl(path16, expiresIn, options) {
50400
50414
  var _this8 = this;
50401
50415
  return _this8.handleOperation(async () => {
50402
- let _path = _this8._getFinalPath(path15);
50416
+ let _path = _this8._getFinalPath(path16);
50403
50417
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
50404
50418
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
50405
50419
  const query = new URLSearchParams();
@@ -50535,13 +50549,13 @@ var StorageFileApi = class extends BaseApiClient {
50535
50549
  * - `objects` table permissions: `select`
50536
50550
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50537
50551
  */
50538
- download(path15, options, parameters) {
50552
+ download(path16, options, parameters) {
50539
50553
  const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0 ? "render/image/authenticated" : "object";
50540
50554
  const query = new URLSearchParams();
50541
50555
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
50542
50556
  if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
50543
50557
  const queryString = query.toString();
50544
- const _path = this._getFinalPath(path15);
50558
+ const _path = this._getFinalPath(path16);
50545
50559
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
50546
50560
  headers: this.headers,
50547
50561
  noResolveJson: true
@@ -50572,9 +50586,9 @@ var StorageFileApi = class extends BaseApiClient {
50572
50586
  * }
50573
50587
  * ```
50574
50588
  */
50575
- async info(path15) {
50589
+ async info(path16) {
50576
50590
  var _this10 = this;
50577
- const _path = _this10._getFinalPath(path15);
50591
+ const _path = _this10._getFinalPath(path16);
50578
50592
  return _this10.handleOperation(async () => {
50579
50593
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
50580
50594
  });
@@ -50595,9 +50609,9 @@ var StorageFileApi = class extends BaseApiClient {
50595
50609
  * .exists('folder/avatar1.png')
50596
50610
  * ```
50597
50611
  */
50598
- async exists(path15) {
50612
+ async exists(path16) {
50599
50613
  var _this11 = this;
50600
- const _path = _this11._getFinalPath(path15);
50614
+ const _path = _this11._getFinalPath(path16);
50601
50615
  try {
50602
50616
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
50603
50617
  return {
@@ -50676,8 +50690,8 @@ var StorageFileApi = class extends BaseApiClient {
50676
50690
  * - `objects` table permissions: none
50677
50691
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50678
50692
  */
50679
- getPublicUrl(path15, options) {
50680
- const _path = this._getFinalPath(path15);
50693
+ getPublicUrl(path16, options) {
50694
+ const _path = this._getFinalPath(path16);
50681
50695
  const query = new URLSearchParams();
50682
50696
  if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
50683
50697
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
@@ -50816,10 +50830,10 @@ var StorageFileApi = class extends BaseApiClient {
50816
50830
  * - `objects` table permissions: `select`
50817
50831
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50818
50832
  */
50819
- async list(path15, options, parameters) {
50833
+ async list(path16, options, parameters) {
50820
50834
  var _this13 = this;
50821
50835
  return _this13.handleOperation(async () => {
50822
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path15 || "" });
50836
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path16 || "" });
50823
50837
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
50824
50838
  });
50825
50839
  }
@@ -50884,11 +50898,11 @@ var StorageFileApi = class extends BaseApiClient {
50884
50898
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
50885
50899
  return btoa(data);
50886
50900
  }
50887
- _getFinalPath(path15) {
50888
- return `${this.bucketId}/${path15.replace(/^\/+/, "")}`;
50901
+ _getFinalPath(path16) {
50902
+ return `${this.bucketId}/${path16.replace(/^\/+/, "")}`;
50889
50903
  }
50890
- _removeEmptyFolders(path15) {
50891
- return path15.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
50904
+ _removeEmptyFolders(path16) {
50905
+ return path16.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
50892
50906
  }
50893
50907
  /** Modifies the `query`, appending values the from `transform` */
50894
50908
  applyTransformOptsToQuery(query, transform2) {
@@ -55123,7 +55137,7 @@ async function recordImplementationRunClosed(store, status, fields, closedAt = /
55123
55137
  }
55124
55138
 
55125
55139
  // src/skill-trust.ts
55126
- import { createHash as createHash2, createPublicKey, verify } from "node:crypto";
55140
+ import { createHash as createHash3, createPublicKey, verify } from "node:crypto";
55127
55141
  var TRUSTED_SIGNER_PUBLIC_KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOXSsKe/zFjaoAdZdvP/esPoeM1fsCGFmxv0INDlCxaW rost-implementation-skill-release";
55128
55142
  var TRUSTED_SIGNER_FINGERPRINT = "SHA256:ziSyU9vhGi0Q3GaeePB1R6BI3GqXutLhepnF/4Hx4rY";
55129
55143
  var RELEASE_REPOSITORY = {
@@ -55171,7 +55185,7 @@ var skillManifestSchema = external_exports.object({
55171
55185
  ).min(1)
55172
55186
  }).strict();
55173
55187
  function sha256Hex2(content) {
55174
- return createHash2("sha256").update(content).digest("hex");
55188
+ return createHash3("sha256").update(content).digest("hex");
55175
55189
  }
55176
55190
  function readSshString(buffer, offset) {
55177
55191
  if (offset + 4 > buffer.length) {
@@ -55242,7 +55256,7 @@ function splitSignedTag(rawTagObject) {
55242
55256
  };
55243
55257
  }
55244
55258
  function buildSshSigMessage(payload) {
55245
- const digest = createHash2("sha512").update(payload, "utf8").digest();
55259
+ const digest = createHash3("sha512").update(payload, "utf8").digest();
55246
55260
  return Buffer.concat([
55247
55261
  Buffer.from("SSHSIG"),
55248
55262
  makeSshString(Buffer.from("git")),
@@ -68157,7 +68171,7 @@ import path13 from "node:path";
68157
68171
 
68158
68172
  // src/forge-workspace.ts
68159
68173
  import { execFile as execFileCallback4 } from "node:child_process";
68160
- import { createHash as createHash3, randomBytes } from "node:crypto";
68174
+ import { createHash as createHash4, randomBytes } from "node:crypto";
68161
68175
  import { constants as fsConstants5 } from "node:fs";
68162
68176
  import { chmod, link, lstat, mkdir as mkdir7, mkdtemp, open, readdir as readdir3, readFile as readFile10, realpath, rename, rm as rm7, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
68163
68177
  import os2 from "node:os";
@@ -68418,7 +68432,7 @@ async function hasUsableClaudeCredentialFile(cell2) {
68418
68432
  return parseUsableClaudeCredentialJson(read.data.toString("utf8")) !== null;
68419
68433
  }
68420
68434
  function claudeKeychainServiceNameForSecureStorageDir(secureStorageDir) {
68421
- const digest = createHash3("sha256").update(secureStorageDir).digest("hex").slice(0, 8);
68435
+ const digest = createHash4("sha256").update(secureStorageDir).digest("hex").slice(0, 8);
68422
68436
  return `Claude Code-credentials-${digest}`;
68423
68437
  }
68424
68438
  function parseUsableClaudeCredentialJson(raw) {
@@ -68953,7 +68967,7 @@ var FORGE_GIT_AUTHOR_EMAIL = `${RSF_ADDON_BRAND.cliGroup}@${BRAND_DISTRIBUTION.c
68953
68967
  function shortRequestId(buildRequestId) {
68954
68968
  const hex3 = buildRequestId.replace(/-/g, "");
68955
68969
  const slice = hex3.slice(0, 8);
68956
- return slice.length > 0 ? slice : createHash3("sha1").update(buildRequestId).digest("hex").slice(0, 8);
68970
+ return slice.length > 0 ? slice : createHash4("sha1").update(buildRequestId).digest("hex").slice(0, 8);
68957
68971
  }
68958
68972
  function requestRoot(baseDir, buildRequestId) {
68959
68973
  return path10.join(baseDir, "work", shortRequestId(buildRequestId));
@@ -69641,7 +69655,7 @@ async function removeWorkspace(ws) {
69641
69655
 
69642
69656
  // src/runner-serve.ts
69643
69657
  import { execFile as execFileCallback5, spawn } from "node:child_process";
69644
- import { createHash as createHash4, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, randomUUID } from "node:crypto";
69658
+ import { createHash as createHash6, createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey2, generateKeyPairSync, randomUUID } from "node:crypto";
69645
69659
  import { constants as fsConstants6 } from "node:fs";
69646
69660
  import { access as access3, chmod as chmod2, lstat as lstat2, mkdir as mkdir8, open as open2, readFile as readFile11, rename as rename2, rm as rm8, statfs, writeFile as writeFile8 } from "node:fs/promises";
69647
69661
  import { cpus, homedir as homedir7, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
@@ -69885,6 +69899,131 @@ function decideReconcileAction(input) {
69885
69899
  };
69886
69900
  }
69887
69901
 
69902
+ // src/runner-setup-validation.ts
69903
+ import { createHash as createHash5, createPrivateKey, sign as cryptoSign } from "node:crypto";
69904
+ var CLAIM_PATH = "/api/runner/setup-validation/claim";
69905
+ var RESULT_PATH = "/api/runner/setup-validation/result";
69906
+ var SERVICE_KEY_DOMAIN_SEPARATOR = "rost.runner-setup.service-key.v1";
69907
+ var SIGNATURE_HEADER = "x-rost-service-key-signature";
69908
+ var TIMESTAMP_HEADER = "x-rost-service-key-timestamp";
69909
+ function canonicalRequest(method, requestPath, timestamp, body) {
69910
+ return [
69911
+ SERVICE_KEY_DOMAIN_SEPARATOR,
69912
+ method.toUpperCase(),
69913
+ requestPath,
69914
+ timestamp,
69915
+ createHash5("sha256").update(body, "utf8").digest("hex")
69916
+ ].join("\n");
69917
+ }
69918
+ async function signedPost(deps, requestPath, body) {
69919
+ if (deps.state.service_key_private === void 0) {
69920
+ throw new Error("Runner setup validation requires a bound service key.");
69921
+ }
69922
+ const rawBody = JSON.stringify(body);
69923
+ const timestamp = String((deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime());
69924
+ const signature = cryptoSign(
69925
+ null,
69926
+ Buffer.from(canonicalRequest("POST", requestPath, timestamp, rawBody), "utf8"),
69927
+ createPrivateKey(deps.state.service_key_private)
69928
+ ).toString("base64");
69929
+ return deps.fetchImpl(`${deps.appUrl.replace(/\/+$/, "")}${requestPath}`, {
69930
+ method: "POST",
69931
+ headers: {
69932
+ authorization: `Bearer ${deps.state.runner_secret}`,
69933
+ "content-type": "application/json",
69934
+ [SIGNATURE_HEADER]: signature,
69935
+ [TIMESTAMP_HEADER]: timestamp
69936
+ },
69937
+ body: rawBody
69938
+ });
69939
+ }
69940
+ async function responseMessage(response) {
69941
+ const text = redactForLog(await response.text());
69942
+ return `Runner setup validation request failed (${response.status})${text.length > 0 ? `: ${text}` : "."}`;
69943
+ }
69944
+ async function executeRunnerSetupValidation(deps) {
69945
+ const setupId = deps.state.active_setup_id;
69946
+ if (setupId === void 0 || deps.state.service_key_private === void 0 || deps.state.service_key_generation === void 0) {
69947
+ return { kind: "not_applicable" };
69948
+ }
69949
+ let claimResponse;
69950
+ try {
69951
+ claimResponse = await signedPost(deps, CLAIM_PATH, { setup_id: setupId, runtime: deps.runtime });
69952
+ } catch (error51) {
69953
+ return { kind: "retryable", message: redactForLog(error51 instanceof Error ? error51.message : String(error51)) };
69954
+ }
69955
+ if (!claimResponse.ok) {
69956
+ return { kind: "retryable", message: await responseMessage(claimResponse) };
69957
+ }
69958
+ const claimParsed = runnerSetupValidationClaimResponseSchema.safeParse(await claimResponse.json().catch(() => null));
69959
+ if (!claimParsed.success) {
69960
+ return { kind: "retryable", message: "Runner setup validation claim returned an unexpected payload." };
69961
+ }
69962
+ const claim = claimParsed.data;
69963
+ if (claim.challenge.setup_id !== setupId || claim.challenge.runtime !== deps.runtime) {
69964
+ return { kind: "retryable", message: "Runner setup validation claim did not match the selected setup and runtime." };
69965
+ }
69966
+ if (new Date(claim.challenge.expires_at).getTime() <= (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime()) {
69967
+ return { kind: "retryable", message: "Runner setup validation claim expired before the probe started." };
69968
+ }
69969
+ if (claim.allowed_command_ids.length !== 1 || claim.allowed_command_ids[0] !== RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS[0]) {
69970
+ return { kind: "retryable", message: "Runner setup validation claim granted an unexpected command set." };
69971
+ }
69972
+ const probe2 = await deps.runProbe({ runtime: deps.runtime, setupId, mcp: claim.mcp });
69973
+ if (probe2.retryable === true) {
69974
+ return { kind: "retryable", message: "Runner credential cell is busy; validation will retry." };
69975
+ }
69976
+ const cliVersion = await readCliVersion();
69977
+ const mcpConfigDigest = createHash5("sha256").update(JSON.stringify({ url: claim.mcp.url, expires_at: claim.mcp.expires_at, allowed_command_ids: claim.allowed_command_ids })).digest("hex");
69978
+ const proof = probe2.ok && probe2.setupId === setupId ? runnerSetupProbeNonceProof({
69979
+ nonce: claim.challenge.nonce,
69980
+ setup_id: setupId,
69981
+ job_id: claim.job_id,
69982
+ runtime: deps.runtime,
69983
+ runner_id: deps.state.runner_id,
69984
+ service_key_generation: deps.state.service_key_generation
69985
+ }) : "0".repeat(64);
69986
+ const reportBody = {
69987
+ job_id: claim.job_id,
69988
+ nonce: claim.challenge.nonce,
69989
+ result: {
69990
+ setup_id: setupId,
69991
+ runtime: deps.runtime,
69992
+ nonce_proof: proof,
69993
+ service_key_generation: deps.state.service_key_generation
69994
+ },
69995
+ attempt: {
69996
+ runner_id: deps.state.runner_id,
69997
+ runtime: deps.runtime,
69998
+ model: null,
69999
+ provider_cli_version: probe2.providerCliVersion,
70000
+ observed_at: probe2.observedAt
70001
+ },
70002
+ invoked_command_ids: [RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS[0]],
70003
+ receipt_facts: {
70004
+ credential_cell_generation: 1,
70005
+ provider_cli_version: probe2.providerCliVersion ?? "unknown",
70006
+ rost_cli_version: cliVersion ?? "unknown",
70007
+ sandbox_policy_version: probe2.sandboxPolicyVersion,
70008
+ mcp_config_digest: mcpConfigDigest
70009
+ }
70010
+ };
70011
+ let resultResponse;
70012
+ try {
70013
+ resultResponse = await signedPost(deps, RESULT_PATH, reportBody);
70014
+ } catch (error51) {
70015
+ return { kind: "retryable", message: redactForLog(error51 instanceof Error ? error51.message : String(error51)) };
70016
+ }
70017
+ if (!resultResponse.ok) {
70018
+ return { kind: "retryable", message: await responseMessage(resultResponse) };
70019
+ }
70020
+ const resultParsed = runnerSetupValidationReportResponseSchema.safeParse(await resultResponse.json().catch(() => null));
70021
+ if (!resultParsed.success || resultParsed.data.verdict !== "passed" || resultParsed.data.receipt === null) {
70022
+ return { kind: "retryable", message: "Runner setup validation did not pass server verification." };
70023
+ }
70024
+ return { kind: "passed", expiresAt: resultParsed.data.receipt.expires_at };
70025
+ }
70026
+
69888
70027
  // src/runner-serve.ts
69889
70028
  var execFile5 = promisify5(execFileCallback5);
69890
70029
  var EXCLUSIVE_NOFOLLOW_FLAGS = fsConstants6.O_CREAT | fsConstants6.O_EXCL | fsConstants6.O_WRONLY | fsConstants6.O_NOFOLLOW;
@@ -70003,6 +70142,43 @@ ${usage()}
70003
70142
  serverClaimContractVersion = learnedVersion !== null && learnedVersion >= HEARTBEAT_CLAIM_MIN_CONTRACT_VERSION ? learnedVersion : null;
70004
70143
  options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
70005
70144
  `);
70145
+ const setupRuntime = state.setup_runtime;
70146
+ const persistedValidation = state.setup_validation;
70147
+ const validationStillCurrent = persistedValidation !== void 0 && persistedValidation.setup_id === state.active_setup_id && persistedValidation.runtime === setupRuntime && new Date(persistedValidation.expires_at).getTime() > Date.now();
70148
+ if (setupRuntime !== void 0 && !validationStillCurrent) {
70149
+ const runtimeCapability = detectedCapabilities[setupRuntime];
70150
+ const validation = await executeRunnerSetupValidation({
70151
+ fetchImpl,
70152
+ appUrl: options.appUrl,
70153
+ runtime: setupRuntime,
70154
+ state,
70155
+ runProbe: ({ runtime, setupId, mcp }) => runSetupValidationProbe({
70156
+ runtime,
70157
+ setupId,
70158
+ mcp,
70159
+ state,
70160
+ config: config2,
70161
+ env,
70162
+ providerCliVersion: runtimeCapability.version
70163
+ })
70164
+ });
70165
+ if (validation.kind === "passed" && state.active_setup_id !== void 0) {
70166
+ state = {
70167
+ ...state,
70168
+ setup_validation: {
70169
+ setup_id: state.active_setup_id,
70170
+ runtime: setupRuntime,
70171
+ expires_at: validation.expiresAt
70172
+ }
70173
+ };
70174
+ await saveState(config2.stateFile, state);
70175
+ options.io.stdout.write(`runner setup validation passed runtime=${setupRuntime}
70176
+ `);
70177
+ } else if (validation.kind === "retryable") {
70178
+ options.io.stderr.write(`${redactForLog(validation.message)}
70179
+ `);
70180
+ }
70181
+ }
70006
70182
  }
70007
70183
  if (localRuntime) {
70008
70184
  activeSessions += 1;
@@ -70424,7 +70600,7 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
70424
70600
  return state;
70425
70601
  }
70426
70602
  async function migrateLegacyCodexHome(state, homeDir, io) {
70427
- const runnerKey = createHash4("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
70603
+ const runnerKey = createHash6("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
70428
70604
  const newHome = stableCodexHomeDir(state, homeDir);
70429
70605
  const legacyCandidates = [
70430
70606
  path12.join(tmpdir2(), "rost-runner-codex-home", `rost-runner-codex-home-${runnerKey}`),
@@ -70484,7 +70660,7 @@ async function migrateOneLegacyCodexHome(legacyHome, newHome, io) {
70484
70660
  }
70485
70661
  }
70486
70662
  function defaultRunnerStateFile(appUrl2) {
70487
- return path12.join(tmpdir2(), `rost-runner-${createHash4("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
70663
+ return path12.join(tmpdir2(), `rost-runner-${createHash6("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
70488
70664
  }
70489
70665
  async function loadState(filePath, expectedTenantId) {
70490
70666
  try {
@@ -70498,8 +70674,12 @@ async function loadState(filePath, expectedTenantId) {
70498
70674
  runner_secret: parsed.runner_secret,
70499
70675
  ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
70500
70676
  ...typeof parsed.name === "string" ? { name: parsed.name } : {},
70677
+ ...typeof parsed.active_setup_id === "string" ? { active_setup_id: parsed.active_setup_id } : {},
70678
+ ...parsed.setup_runtime === "claude" || parsed.setup_runtime === "codex" ? { setup_runtime: parsed.setup_runtime } : {},
70501
70679
  ...typeof parsed.service_key_private === "string" ? { service_key_private: parsed.service_key_private } : {},
70502
70680
  ...typeof parsed.service_key_public === "string" ? { service_key_public: parsed.service_key_public } : {},
70681
+ ...typeof parsed.service_key_generation === "number" ? { service_key_generation: parsed.service_key_generation } : {},
70682
+ ...isSetupValidationState(parsed.setup_validation) ? { setup_validation: parsed.setup_validation } : {},
70503
70683
  ...isRuntimeSessionMap(parsed.runtime_sessions) ? { runtime_sessions: parsed.runtime_sessions } : {},
70504
70684
  ...isInFlightWorkOrderMap(parsed.in_flight_work_orders) ? { in_flight_work_orders: parsed.in_flight_work_orders } : {}
70505
70685
  };
@@ -70509,6 +70689,11 @@ async function loadState(filePath, expectedTenantId) {
70509
70689
  }
70510
70690
  return null;
70511
70691
  }
70692
+ function isSetupValidationState(value) {
70693
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
70694
+ const record2 = value;
70695
+ return typeof record2.setup_id === "string" && (record2.runtime === "claude" || record2.runtime === "codex") && typeof record2.expires_at === "string";
70696
+ }
70512
70697
  function isRuntimeSessionMap(value) {
70513
70698
  if (!value || typeof value !== "object" || Array.isArray(value)) {
70514
70699
  return false;
@@ -70523,7 +70708,7 @@ function isRuntimeSessionMap(value) {
70523
70708
  }
70524
70709
  async function saveState(filePath, state) {
70525
70710
  await mkdir8(path12.dirname(filePath), { recursive: true, mode: 448 });
70526
- const tempPath = `${filePath}.${process.pid}.${createHash4("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
70711
+ const tempPath = `${filePath}.${process.pid}.${createHash6("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
70527
70712
  try {
70528
70713
  await writeFile8(tempPath, `${JSON.stringify(state)}
70529
70714
  `, { mode: 384 });
@@ -70566,7 +70751,7 @@ function generateRunnerServiceKeyPair() {
70566
70751
  return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(publicKey) };
70567
70752
  }
70568
70753
  function runnerServiceKeyPairFromPrivate(privatePem) {
70569
- const privateKey = createPrivateKey(privatePem);
70754
+ const privateKey = createPrivateKey2(privatePem);
70570
70755
  return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(createPublicKey2(privateKey)) };
70571
70756
  }
70572
70757
  function runnerServiceKeyPairForState(state) {
@@ -71311,6 +71496,120 @@ function buildTurnCommand(input) {
71311
71496
  ]
71312
71497
  };
71313
71498
  }
71499
+ async function runSetupValidationProbe(input) {
71500
+ const observedAt = (/* @__PURE__ */ new Date()).toISOString();
71501
+ if (new Date(input.mcp.expires_at).getTime() <= Date.now()) {
71502
+ return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
71503
+ }
71504
+ const configPath = path12.join(tmpdir2(), `rost-runner-setup-mcp-${randomUUID()}.json`);
71505
+ const prompt = `Call rost_runner_setup_status exactly once with setup_id ${input.setupId}. Do not call any other tool. Then respond only with ROST_SETUP_ID=${input.setupId}.`;
71506
+ const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: input.mcp.url, headers: { Authorization: `Bearer ${input.mcp.token}` } } } });
71507
+ let claudeConfigDir = null;
71508
+ let codexHomeDir = null;
71509
+ let sandboxProfilePath = null;
71510
+ const credentialCell = credentialCellDirForRuntime(input.runtime, input.state, input.config.homeDir);
71511
+ const credentialCellLock = await acquireCodexHomeLock(credentialCell, {
71512
+ waitMs: 0,
71513
+ label: credentialCellLabelForRuntime(input.runtime)
71514
+ });
71515
+ if (credentialCellLock === null) {
71516
+ return { ok: false, retryable: true, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
71517
+ }
71518
+ try {
71519
+ if (input.runtime === "claude") {
71520
+ const cell2 = stableClaudeHomeDir(input.state, input.config.homeDir);
71521
+ if (await claudeCellNeedsLogin(cell2)) {
71522
+ return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
71523
+ }
71524
+ claudeConfigDir = await provisionIsolatedClaudeConfigDir(
71525
+ path12.join(tmpdir2(), `rost-runner-setup-claude-${randomUUID()}`),
71526
+ cell2
71527
+ );
71528
+ } else {
71529
+ codexHomeDir = await provisionIsolatedCodexHomeDir(
71530
+ input.env,
71531
+ stableCodexHomeDir(input.state, input.config.homeDir),
71532
+ tmpdir2()
71533
+ );
71534
+ }
71535
+ await writeFile8(configPath, `${mcpConfig}
71536
+ `, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
71537
+ const built = buildSetupValidationProbeCommand(input.runtime, prompt, configPath);
71538
+ if (input.config.sandbox.kind === "seatbelt") {
71539
+ sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-setup-${randomUUID()}.sb`);
71540
+ await writeFile8(sandboxProfilePath, input.config.sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
71541
+ }
71542
+ const wrapped = wrapCommandWithSandbox(input.config.sandbox, built.command, built.args, sandboxProfilePath);
71543
+ const fakeModel = (input.env.ROST_RUNNER_FAKE_MODEL ?? "").trim();
71544
+ const result = await runModelProcess({
71545
+ command: fakeModel.length > 0 ? fakeModel : wrapped.command,
71546
+ args: wrapped.args,
71547
+ cwd: tmpdir2(),
71548
+ timeoutMs: 12e4,
71549
+ env: buildModelSpawnEnv(
71550
+ input.env,
71551
+ claudeConfigDir ?? void 0,
71552
+ codexHomeDir ?? void 0,
71553
+ input.runtime === "claude" ? stableClaudeHomeDir(input.state, input.config.homeDir) : void 0
71554
+ ),
71555
+ expectSessionEnvelope: input.runtime === "claude",
71556
+ interactiveTurn: true,
71557
+ ...input.runtime === "claude" ? { requiredMcpTools: ["mcp__rost__rost_runner_setup_status"] } : {}
71558
+ });
71559
+ const output = `${result.assistantText ?? ""}
71560
+ ${result.transcript ?? ""}`;
71561
+ const match = /(?:^|\s)ROST_SETUP_ID=([0-9a-f-]{36})(?:\s|$)/i.exec(output);
71562
+ return {
71563
+ ok: result.ok && match?.[1] === input.setupId,
71564
+ setupId: match?.[1] ?? null,
71565
+ providerCliVersion: input.providerCliVersion,
71566
+ observedAt,
71567
+ sandboxPolicyVersion: input.config.sandbox.kind
71568
+ };
71569
+ } catch {
71570
+ return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
71571
+ } finally {
71572
+ await rm8(configPath, { force: true });
71573
+ if (sandboxProfilePath !== null) await rm8(sandboxProfilePath, { force: true });
71574
+ if (claudeConfigDir !== null) await rm8(claudeConfigDir, { recursive: true, force: true });
71575
+ await credentialCellLock.release();
71576
+ }
71577
+ }
71578
+ function buildSetupValidationProbeCommand(runtime, prompt, configPath) {
71579
+ return {
71580
+ command: runtime,
71581
+ args: runtime === "claude" ? [
71582
+ "-p",
71583
+ prompt,
71584
+ "--mcp-config",
71585
+ configPath,
71586
+ "--strict-mcp-config",
71587
+ "--tools",
71588
+ "",
71589
+ "--allowedTools",
71590
+ "mcp__rost__rost_runner_setup_status",
71591
+ "--permission-mode",
71592
+ "dontAsk",
71593
+ "--output-format",
71594
+ "stream-json",
71595
+ "--verbose"
71596
+ ] : [
71597
+ "exec",
71598
+ prompt,
71599
+ "--mcp-config",
71600
+ configPath,
71601
+ "--sandbox",
71602
+ "read-only",
71603
+ "--skip-git-repo-check",
71604
+ "--ephemeral",
71605
+ "--ignore-user-config",
71606
+ "--ignore-rules",
71607
+ "-C",
71608
+ tmpdir2(),
71609
+ ...CODEX_NATIVE_TOOL_ISOLATION_CONFIG_ARGS
71610
+ ]
71611
+ };
71612
+ }
71314
71613
  function runLocalTurn(ctx, workOrder, runtime, kind) {
71315
71614
  return spawnRunnerTurn(ctx, workOrder, runtime, kind);
71316
71615
  }
@@ -71447,7 +71746,7 @@ async function prepareForgeTurn(ctx, workOrder, execution, runtime, seams) {
71447
71746
  var FORGE_MAX_CHANGED_PATHS = 2e3;
71448
71747
  var FORGE_MAX_CHANGED_PATH_LENGTH = 500;
71449
71748
  function forgeChangedPathsField(changedPaths) {
71450
- const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((path15) => path15.length >= 1 && path15.length <= FORGE_MAX_CHANGED_PATH_LENGTH);
71749
+ const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((path16) => path16.length >= 1 && path16.length <= FORGE_MAX_CHANGED_PATH_LENGTH);
71451
71750
  return withinCaps ? { changedPaths: [...changedPaths] } : {};
71452
71751
  }
71453
71752
  async function finalizeForgeBuildPush(ctx, prep, result, seams) {
@@ -71576,11 +71875,11 @@ function journaledClaudeConfigDirNames(state) {
71576
71875
  );
71577
71876
  }
71578
71877
  function stableCodexHomeDir(state, homeDir) {
71579
- const key = createHash4("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
71878
+ const key = createHash6("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
71580
71879
  return path12.join(runnerCodexHomeBaseDir(homeDir), `rost-runner-codex-home-${key}`);
71581
71880
  }
71582
71881
  function stableClaudeHomeDir(state, homeDir) {
71583
- const key = createHash4("sha256").update(`claude-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
71882
+ const key = createHash6("sha256").update(`claude-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
71584
71883
  return path12.join(runnerClaudeHomeBaseDir(homeDir), `rost-runner-claude-home-${key}`);
71585
71884
  }
71586
71885
  function credentialCellDirForRuntime(runtime, state, homeDir) {
@@ -71693,11 +71992,11 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
71693
71992
  const token = typeof mcp.token === "string" ? mcp.token : "";
71694
71993
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
71695
71994
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
71696
- const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash4("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
71995
+ const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash6("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
71697
71996
  const prompt = buildTurnPrompt({ kind, workOrder, execution, hasWorkspace: forgePrep !== null });
71698
71997
  const claudeCell = runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
71699
71998
  const claudeConfigDir = runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
71700
- resumeClaudeConfigDir ?? path12.join(tmpdir2(), `rost-runner-claude-config-${createHash4("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
71999
+ resumeClaudeConfigDir ?? path12.join(tmpdir2(), `rost-runner-claude-config-${createHash6("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
71701
72000
  claudeCell,
71702
72001
  (message) => ctx.io.stderr.write(`${redactForLog(message)}
71703
72002
  `)
@@ -71738,7 +72037,7 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
71738
72037
  if (sandbox.kind === "seatbelt") {
71739
72038
  sandboxProfilePath = path12.join(
71740
72039
  tmpdir2(),
71741
- `rost-runner-sandbox-${createHash4("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
72040
+ `rost-runner-sandbox-${createHash6("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
71742
72041
  );
71743
72042
  await writeFile8(sandboxProfilePath, sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
71744
72043
  }
@@ -71990,7 +72289,7 @@ async function spawnMergeConflictModel(ctx, input) {
71990
72289
  const token = typeof mcp.token === "string" ? mcp.token : "";
71991
72290
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
71992
72291
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
71993
- const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash4("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
72292
+ const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash6("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
71994
72293
  const credentialCellLockWaitMs = 30 * 6e4;
71995
72294
  const credentialCellLock = ctx.credentialCellLockHeld !== true ? await acquireCodexHomeLock(credentialCellDirForRuntime(input.runtime, ctx.state, ctx.config.homeDir), { waitMs: credentialCellLockWaitMs, label: credentialCellLabelForRuntime(input.runtime) }) : null;
71996
72295
  if (ctx.credentialCellLockHeld !== true && credentialCellLock === null) {
@@ -71999,7 +72298,7 @@ async function spawnMergeConflictModel(ctx, input) {
71999
72298
  try {
72000
72299
  const claudeCell = input.runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
72001
72300
  const claudeConfigDir = input.runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
72002
- path12.join(tmpdir2(), `rost-runner-claude-config-${createHash4("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
72301
+ path12.join(tmpdir2(), `rost-runner-claude-config-${createHash6("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
72003
72302
  claudeCell,
72004
72303
  (message) => ctx.io.stderr.write(`${redactForLog(message)}
72005
72304
  `)
@@ -72021,7 +72320,7 @@ async function spawnMergeConflictModel(ctx, input) {
72021
72320
  ].join("\n");
72022
72321
  let sandboxProfilePath = null;
72023
72322
  if (input.sandbox.kind === "seatbelt") {
72024
- sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-sandbox-${createHash4("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`);
72323
+ sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-sandbox-${createHash6("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`);
72025
72324
  await writeFile8(sandboxProfilePath, input.sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
72026
72325
  }
72027
72326
  const built = buildTurnCommand({
@@ -73430,11 +73729,724 @@ function isEnoent(error51) {
73430
73729
  }
73431
73730
 
73432
73731
  // src/runner-setup-orchestrator.ts
73433
- import { createHash as createHash5 } from "node:crypto";
73732
+ import { createHash as createHash7 } from "node:crypto";
73434
73733
  import { constants as fsConstants8, realpathSync as realpathSync3 } from "node:fs";
73435
- import { open as open3, rm as rm10 } from "node:fs/promises";
73436
- import path14 from "node:path";
73734
+ import { open as open3, rm as rm11 } from "node:fs/promises";
73735
+ import path15 from "node:path";
73437
73736
  import { setTimeout as defaultSleep2 } from "node:timers/promises";
73737
+
73738
+ // src/codex-auth-adapter.ts
73739
+ import { spawn as spawn3 } from "node:child_process";
73740
+ import { lstat as lstat4, readdir as readdir6, rm as rm10 } from "node:fs/promises";
73741
+ import path14 from "node:path";
73742
+ var CODEX_MIN_AUTH_ADAPTER_VERSION = "0.144.6";
73743
+ function parseCodexVersion(raw) {
73744
+ const trimmed = raw.trim();
73745
+ const match = /^(?:codex-cli\s+)?(\d+)\.(\d+)\.(\d+)$/.exec(trimmed);
73746
+ if (match === null) {
73747
+ return null;
73748
+ }
73749
+ return `${match[1]}.${match[2]}.${match[3]}`;
73750
+ }
73751
+ function parseVersionComponents(version4) {
73752
+ const parts = version4.split(".");
73753
+ const major = Number(parts[0]);
73754
+ const minor = Number(parts[1]);
73755
+ const patch = Number(parts[2]);
73756
+ return [major, minor, patch];
73757
+ }
73758
+ function compareVersions(a, b) {
73759
+ const [aMajor, aMinor, aPatch] = parseVersionComponents(a);
73760
+ const [bMajor, bMinor, bPatch] = parseVersionComponents(b);
73761
+ if (aMajor !== bMajor) return aMajor - bMajor;
73762
+ if (aMinor !== bMinor) return aMinor - bMinor;
73763
+ return aPatch - bPatch;
73764
+ }
73765
+ var CODEX_VERSION_CEILING = "0.145.0";
73766
+ function evaluateCodexVersion(raw) {
73767
+ const version4 = parseCodexVersion(raw);
73768
+ if (version4 === null) {
73769
+ return { kind: "unparsable" };
73770
+ }
73771
+ if (compareVersions(version4, CODEX_MIN_AUTH_ADAPTER_VERSION) < 0) {
73772
+ return { kind: "too_old", version: version4, minimum: CODEX_MIN_AUTH_ADAPTER_VERSION };
73773
+ }
73774
+ if (compareVersions(version4, CODEX_VERSION_CEILING) >= 0) {
73775
+ return { kind: "unsupported_line", version: version4 };
73776
+ }
73777
+ return { kind: "supported", version: version4 };
73778
+ }
73779
+ var API_KEY_LINE_PREFIX = "Logged in using an API key";
73780
+ function parseCodexLoginStatus(result) {
73781
+ const lines = `${result.stderr}
73782
+ ${result.stdout}`.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
73783
+ const isChatgptLine = lines.some((line) => line === "Logged in using ChatGPT");
73784
+ const isApiKeyLine = lines.some((line) => line.startsWith(API_KEY_LINE_PREFIX));
73785
+ const isLoggedOutLine = lines.some((line) => line === "Not logged in");
73786
+ const recognizedMatchCount = [isChatgptLine, isApiKeyLine, isLoggedOutLine].filter(Boolean).length;
73787
+ if (recognizedMatchCount > 1) {
73788
+ return { kind: "unrecognized" };
73789
+ }
73790
+ if (result.code === 0 && isChatgptLine) {
73791
+ return { kind: "chatgpt" };
73792
+ }
73793
+ if (result.code === 0 && isApiKeyLine) {
73794
+ return { kind: "api_key" };
73795
+ }
73796
+ if (result.code !== 0 && isLoggedOutLine) {
73797
+ return { kind: "logged_out" };
73798
+ }
73799
+ if (result.code !== 0 && !isChatgptLine && !isApiKeyLine && !isLoggedOutLine) {
73800
+ return { kind: "command_failed" };
73801
+ }
73802
+ return { kind: "unrecognized" };
73803
+ }
73804
+ function classifyAuthJsonStat(info, selfUid) {
73805
+ const ownerMismatch = selfUid !== null && info.uid !== selfUid;
73806
+ const permissiveBits = (info.mode & 63) !== 0;
73807
+ if (info.isSymbolicLink || !info.isFile || info.nlink !== 1 || ownerMismatch || permissiveBits) {
73808
+ return { kind: "foreign" };
73809
+ }
73810
+ if (info.size === 0) {
73811
+ return { kind: "empty" };
73812
+ }
73813
+ return { kind: "owned" };
73814
+ }
73815
+ function isTrustworthyAncestorShape(info, selfUid) {
73816
+ return info.isDirectory && !info.isSymbolicLink && (info.mode & 18) === 0 && (selfUid === null || info.uid === selfUid);
73817
+ }
73818
+ async function isTrustworthyAbsentCellChain(cell2, opts) {
73819
+ const lstatImpl = opts?.lstatImpl ?? lstat4;
73820
+ const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
73821
+ let current = path14.dirname(path14.resolve(cell2));
73822
+ for (; ; ) {
73823
+ let info;
73824
+ try {
73825
+ info = await lstatImpl(current);
73826
+ } catch (error51) {
73827
+ if (error51.code !== "ENOENT") {
73828
+ return false;
73829
+ }
73830
+ info = null;
73831
+ }
73832
+ if (info === null) {
73833
+ const parent = path14.dirname(current);
73834
+ if (parent === current) {
73835
+ return false;
73836
+ }
73837
+ current = parent;
73838
+ continue;
73839
+ }
73840
+ return isTrustworthyAncestorShape(
73841
+ {
73842
+ isDirectory: info.isDirectory(),
73843
+ isSymbolicLink: info.isSymbolicLink(),
73844
+ mode: info.mode,
73845
+ uid: info.uid
73846
+ },
73847
+ selfUid
73848
+ );
73849
+ }
73850
+ }
73851
+ async function inspectCodexAuthCell(cell2) {
73852
+ if (!await isVerifiedExistingCodexHomeDir(cell2)) {
73853
+ const resolvedCell = path14.resolve(cell2);
73854
+ const leafInfo = await lstat4(resolvedCell).catch((error51) => error51.code === "ENOENT" ? null : "error").catch(() => "error");
73855
+ if (leafInfo !== null) {
73856
+ return { kind: "unreadable" };
73857
+ }
73858
+ const chainTrustworthy = await isTrustworthyAbsentCellChain(cell2);
73859
+ return chainTrustworthy ? { kind: "absent" } : { kind: "unreadable" };
73860
+ }
73861
+ const cellInfo = await lstat4(cell2).catch(() => null);
73862
+ if (cellInfo === null) {
73863
+ return { kind: "unreadable" };
73864
+ }
73865
+ if ((cellInfo.mode & 63) !== 0) {
73866
+ return { kind: "foreign" };
73867
+ }
73868
+ const authJsonPath = path14.join(cell2, "auth.json");
73869
+ const info = await lstat4(authJsonPath).catch((error51) => {
73870
+ if (error51.code === "ENOENT") {
73871
+ return null;
73872
+ }
73873
+ throw error51;
73874
+ }).catch(() => "unreadable");
73875
+ if (info === "unreadable") {
73876
+ return { kind: "unreadable" };
73877
+ }
73878
+ if (info === null) {
73879
+ return { kind: "empty" };
73880
+ }
73881
+ const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
73882
+ return classifyAuthJsonStat(
73883
+ {
73884
+ isSymbolicLink: info.isSymbolicLink(),
73885
+ isFile: info.isFile(),
73886
+ nlink: info.nlink,
73887
+ uid: info.uid,
73888
+ mode: info.mode,
73889
+ size: info.size
73890
+ },
73891
+ selfUid
73892
+ );
73893
+ }
73894
+ function decideCodexAuth(version4, cell2, status) {
73895
+ switch (version4.kind) {
73896
+ case "too_old":
73897
+ return { kind: "refuse", reason: "codex_version_too_old" };
73898
+ case "unsupported_line":
73899
+ return { kind: "refuse", reason: "codex_version_unsupported_line" };
73900
+ case "unparsable":
73901
+ return { kind: "refuse", reason: "codex_version_unparsable" };
73902
+ case "supported":
73903
+ break;
73904
+ default: {
73905
+ const exhaustive = version4;
73906
+ throw new Error(`unreachable codex version verdict: ${JSON.stringify(exhaustive)}`);
73907
+ }
73908
+ }
73909
+ if (cell2.kind === "unreadable") {
73910
+ return { kind: "refuse", reason: "cell_unreadable" };
73911
+ }
73912
+ if (cell2.kind === "foreign") {
73913
+ return { kind: "refuse", reason: "foreign_cell_ownership" };
73914
+ }
73915
+ switch (cell2.kind) {
73916
+ case "absent": {
73917
+ switch (status.kind) {
73918
+ case "chatgpt":
73919
+ case "api_key":
73920
+ return { kind: "refuse", reason: "status_not_from_this_cell" };
73921
+ case "logged_out":
73922
+ case "command_failed":
73923
+ case "unrecognized":
73924
+ return { kind: "device_auth_login_required" };
73925
+ case "exit_unconfirmed":
73926
+ return { kind: "refuse", reason: "probe_exit_unconfirmed" };
73927
+ default: {
73928
+ const exhaustive = status;
73929
+ throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
73930
+ }
73931
+ }
73932
+ }
73933
+ case "empty": {
73934
+ switch (status.kind) {
73935
+ case "chatgpt":
73936
+ case "api_key":
73937
+ return { kind: "refuse", reason: "status_not_from_this_cell" };
73938
+ case "logged_out":
73939
+ return { kind: "device_auth_login_required" };
73940
+ case "command_failed":
73941
+ return { kind: "refuse", reason: "status_unavailable" };
73942
+ case "exit_unconfirmed":
73943
+ return { kind: "refuse", reason: "probe_exit_unconfirmed" };
73944
+ case "unrecognized":
73945
+ return { kind: "refuse", reason: "unrecognized_status" };
73946
+ default: {
73947
+ const exhaustive = status;
73948
+ throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
73949
+ }
73950
+ }
73951
+ }
73952
+ case "owned": {
73953
+ switch (status.kind) {
73954
+ case "chatgpt":
73955
+ return { kind: "proceed_to_validation" };
73956
+ case "api_key":
73957
+ return { kind: "refuse", reason: "api_key_auth_unsupported" };
73958
+ case "logged_out":
73959
+ return { kind: "device_auth_login_required" };
73960
+ case "command_failed":
73961
+ return { kind: "refuse", reason: "status_unavailable" };
73962
+ case "exit_unconfirmed":
73963
+ return { kind: "refuse", reason: "probe_exit_unconfirmed" };
73964
+ case "unrecognized":
73965
+ return { kind: "refuse", reason: "unrecognized_status" };
73966
+ default: {
73967
+ const exhaustive = status;
73968
+ throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
73969
+ }
73970
+ }
73971
+ }
73972
+ default: {
73973
+ const exhaustive = cell2;
73974
+ throw new Error(`unreachable codex auth cell state: ${JSON.stringify(exhaustive)}`);
73975
+ }
73976
+ }
73977
+ }
73978
+ var CODEX_LOGIN_ENV_ALLOWLIST = LOGIN_ENV_ALLOWLIST;
73979
+ function copyAllowlistedEnv(base) {
73980
+ const env = {};
73981
+ for (const key of CODEX_LOGIN_ENV_ALLOWLIST) {
73982
+ const value = base[key];
73983
+ if (typeof value === "string") {
73984
+ env[key] = value;
73985
+ }
73986
+ }
73987
+ return env;
73988
+ }
73989
+ function buildCodexAuthEnv(base, cell2) {
73990
+ const env = copyAllowlistedEnv(base);
73991
+ env.CODEX_HOME = cell2;
73992
+ return env;
73993
+ }
73994
+ var CODEX_PROBE_TIMEOUT_MS = 15e3;
73995
+ var CODEX_PROBE_KILL_GRACE_MS = 2e3;
73996
+ var CODEX_PROBE_SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
73997
+ var CODEX_PROBE_SANDBOX_PROFILE = "(version 1) (allow default) (deny process-fork)";
73998
+ function probeContainmentUnavailable() {
73999
+ return Object.assign(
74000
+ new Error(
74001
+ `refusing to run the codex probe: complete descendant containment is unavailable on ${process.platform}`
74002
+ ),
74003
+ {
74004
+ code: "EROST_PROBE_CONTAINMENT_UNAVAILABLE",
74005
+ codexProbeContainmentUnavailable: true
74006
+ }
74007
+ );
74008
+ }
74009
+ function decideProbeGroupKill(observed) {
74010
+ if (observed.childExited && observed.groupHeldAtChildExit === false) {
74011
+ return "skip";
74012
+ }
74013
+ return observed.groupPopulatedNow ? "signal" : "skip";
74014
+ }
74015
+ function spawnCapture(cmd, args, opts) {
74016
+ const timeoutMs = opts.timeoutMs ?? CODEX_PROBE_TIMEOUT_MS;
74017
+ const killGraceMs = opts.killGraceMs ?? CODEX_PROBE_KILL_GRACE_MS;
74018
+ const containment = opts.containment ?? "required";
74019
+ return new Promise((resolve2, reject) => {
74020
+ const stdoutChunks = [];
74021
+ const stderrChunks = [];
74022
+ let spawnCommand = cmd;
74023
+ let spawnArgs = args;
74024
+ if (containment === "required") {
74025
+ if (process.platform !== "darwin") {
74026
+ reject(probeContainmentUnavailable());
74027
+ return;
74028
+ }
74029
+ spawnCommand = CODEX_PROBE_SANDBOX_EXEC_PATH;
74030
+ spawnArgs = ["-p", CODEX_PROBE_SANDBOX_PROFILE, cmd, ...args];
74031
+ }
74032
+ const child = spawn3(spawnCommand, spawnArgs, {
74033
+ env: opts.env,
74034
+ stdio: ["ignore", "pipe", "pipe"],
74035
+ ...process.platform !== "win32" ? { detached: true } : {}
74036
+ });
74037
+ let settled = false;
74038
+ let timedOut = false;
74039
+ let killOutcome = null;
74040
+ let timer = null;
74041
+ let graceTimer = null;
74042
+ let childExited = false;
74043
+ let groupHeldAtChildExit = null;
74044
+ const probeGroupPopulated = () => {
74045
+ const pid = child.pid;
74046
+ if (typeof pid !== "number") {
74047
+ return false;
74048
+ }
74049
+ if (process.platform !== "win32") {
74050
+ try {
74051
+ process.kill(-pid, 0);
74052
+ return true;
74053
+ } catch {
74054
+ return false;
74055
+ }
74056
+ }
74057
+ return !childExited;
74058
+ };
74059
+ const terminateProbeGroup = () => {
74060
+ if (killOutcome !== null) {
74061
+ return;
74062
+ }
74063
+ if (decideProbeGroupKill({ childExited, groupHeldAtChildExit, groupPopulatedNow: probeGroupPopulated() }) === "skip") {
74064
+ killOutcome = "skipped_nothing_ours";
74065
+ return;
74066
+ }
74067
+ killProcessTree(child);
74068
+ killOutcome = "signalled";
74069
+ };
74070
+ const clearTimers = () => {
74071
+ if (timer !== null) {
74072
+ clearTimeout(timer);
74073
+ }
74074
+ if (graceTimer !== null) {
74075
+ clearTimeout(graceTimer);
74076
+ }
74077
+ };
74078
+ const teardownCapture = () => {
74079
+ child.stdout?.removeAllListeners("data");
74080
+ child.stderr?.removeAllListeners("data");
74081
+ child.stdout?.destroy();
74082
+ child.stderr?.destroy();
74083
+ };
74084
+ const startSettleGrace = (phase) => {
74085
+ if (graceTimer !== null || settled) {
74086
+ return;
74087
+ }
74088
+ graceTimer = setTimeout(() => {
74089
+ if (settled) {
74090
+ return;
74091
+ }
74092
+ settled = true;
74093
+ clearTimers();
74094
+ teardownCapture();
74095
+ reject(Object.assign(
74096
+ new Error(
74097
+ phase === "timeout" ? `${cmd} ${args.join(" ")} timed out after ${timeoutMs}ms and the exit of its process tree could not be confirmed within ${killGraceMs}ms` : `${cmd} ${args.join(" ")} exited, but a descendant kept its output pipes open and its exit could not be confirmed within ${killGraceMs}ms`
74098
+ ),
74099
+ {
74100
+ code: "ETIMEDOUT",
74101
+ codexProbeExitUnconfirmedMs: killGraceMs,
74102
+ codexProbeKillOutcome: killOutcome,
74103
+ codexProbeSettlePhase: phase,
74104
+ codexProbeContainment: containment
74105
+ }
74106
+ ));
74107
+ }, killGraceMs);
74108
+ };
74109
+ timer = setTimeout(() => {
74110
+ if (settled || childExited) {
74111
+ return;
74112
+ }
74113
+ timedOut = true;
74114
+ terminateProbeGroup();
74115
+ startSettleGrace("timeout");
74116
+ }, timeoutMs);
74117
+ child.on("exit", () => {
74118
+ childExited = true;
74119
+ groupHeldAtChildExit = probeGroupPopulated();
74120
+ if (settled) {
74121
+ return;
74122
+ }
74123
+ terminateProbeGroup();
74124
+ startSettleGrace("child_exit");
74125
+ });
74126
+ child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
74127
+ child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
74128
+ child.on("error", (error51) => {
74129
+ if (settled) {
74130
+ return;
74131
+ }
74132
+ settled = true;
74133
+ clearTimers();
74134
+ teardownCapture();
74135
+ if (containment === "required" && error51.code !== void 0 && ["ENOENT", "EACCES"].includes(String(error51.code))) {
74136
+ reject(probeContainmentUnavailable());
74137
+ return;
74138
+ }
74139
+ reject(error51);
74140
+ });
74141
+ child.on("close", (code) => {
74142
+ if (settled) {
74143
+ return;
74144
+ }
74145
+ settled = true;
74146
+ clearTimers();
74147
+ teardownCapture();
74148
+ if (timedOut) {
74149
+ reject(Object.assign(new Error(`${cmd} ${args.join(" ")} timed out after ${timeoutMs}ms`), {
74150
+ code: "ETIMEDOUT",
74151
+ codexProbeTimedOutMs: timeoutMs,
74152
+ codexProbeKillOutcome: killOutcome,
74153
+ codexProbeContainment: containment
74154
+ }));
74155
+ return;
74156
+ }
74157
+ resolve2({
74158
+ code: code ?? 1,
74159
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
74160
+ stderr: Buffer.concat(stderrChunks).toString("utf8")
74161
+ });
74162
+ });
74163
+ });
74164
+ }
74165
+ var CODEX_IGNORE_USER_CONFIG_ARGS = ["--ignore-user-config"];
74166
+ function describeProbeKillOutcome(error51) {
74167
+ const outcome = "codexProbeKillOutcome" in error51 ? error51.codexProbeKillOutcome : null;
74168
+ if (outcome === "signalled") {
74169
+ return "SIGKILL was sent to its process group";
74170
+ }
74171
+ if (outcome === "skipped_nothing_ours") {
74172
+ return "no signal was sent: the probe's process group held nothing of ours left to signal";
74173
+ }
74174
+ return "its kill outcome was not recorded";
74175
+ }
74176
+ function describeSpawnFailure(what, error51) {
74177
+ if (typeof error51 === "object" && error51 !== null && "codexProbeContainmentUnavailable" in error51) {
74178
+ return `${what} refused: complete descendant containment is unavailable on ${process.platform}`;
74179
+ }
74180
+ if (typeof error51 === "object" && error51 !== null && "codexProbeExitUnconfirmedMs" in error51) {
74181
+ const graceMs = error51.codexProbeExitUnconfirmedMs;
74182
+ const containment = "codexProbeContainment" in error51 ? error51.codexProbeContainment : null;
74183
+ const phase = "codexProbeSettlePhase" in error51 ? error51.codexProbeSettlePhase : null;
74184
+ const lede = phase === "child_exit" ? `${what} exited, but a descendant kept its output pipes open` : `${what} timed out`;
74185
+ return containment === "required" ? `${lede}; ${describeProbeKillOutcome(error51)}, and its exit could not be confirmed within ${String(graceMs)}ms despite the probe containment boundary` : `${lede}; ${describeProbeKillOutcome(error51)}, and its exit could not be confirmed within ${String(graceMs)}ms; a descendant process may still be running`;
74186
+ }
74187
+ if (typeof error51 === "object" && error51 !== null && "codexProbeTimedOutMs" in error51) {
74188
+ const timedOutMs = error51.codexProbeTimedOutMs;
74189
+ const containment = "codexProbeContainment" in error51 ? error51.codexProbeContainment : null;
74190
+ return containment === "required" ? `${what} timed out after ${String(timedOutMs)}ms; ${describeProbeKillOutcome(error51)}, and the probe process exited and closed its output pipes under the descendant-containment boundary` : `${what} timed out after ${String(timedOutMs)}ms; ${describeProbeKillOutcome(error51)}, and the probe process exited and closed its output pipes; a descendant that left the process group with its own stdio would not be observed here`;
74191
+ }
74192
+ const code = typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : "unknown";
74193
+ return `${what} could not be spawned (${code})`;
74194
+ }
74195
+ async function probeCodexVersion(codexBin, opts) {
74196
+ const spawnImpl = opts?.spawnImpl ?? spawnCapture;
74197
+ const env = copyAllowlistedEnv(opts?.env ?? process.env);
74198
+ let result;
74199
+ try {
74200
+ result = await spawnImpl(codexBin, ["--version", ...CODEX_IGNORE_USER_CONFIG_ARGS], {
74201
+ env,
74202
+ ...opts?.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
74203
+ ...opts?.killGraceMs !== void 0 ? { killGraceMs: opts.killGraceMs } : {},
74204
+ ...opts?.containmentForTest !== void 0 ? { containment: opts.containmentForTest } : {}
74205
+ });
74206
+ } catch (error51) {
74207
+ opts?.onProbeError?.(describeSpawnFailure("codex --version", error51));
74208
+ return { kind: "unparsable" };
74209
+ }
74210
+ if (result.code !== 0) {
74211
+ return { kind: "unparsable" };
74212
+ }
74213
+ for (const line of `${result.stdout}
74214
+ ${result.stderr}`.split("\n")) {
74215
+ if (parseCodexVersion(line) !== null) {
74216
+ return evaluateCodexVersion(line);
74217
+ }
74218
+ }
74219
+ return { kind: "unparsable" };
74220
+ }
74221
+ var CODEX_AUTH_CELL_ROUTINE_ENTRIES = /* @__PURE__ */ new Set(["log", "tmp", "config.toml"]);
74222
+ function isRoutineCodexBookkeepingShape(entry, info, selfUid) {
74223
+ if (!CODEX_AUTH_CELL_ROUTINE_ENTRIES.has(entry) || info.isSymbolicLink()) {
74224
+ return false;
74225
+ }
74226
+ if (selfUid !== null && info.uid !== selfUid) {
74227
+ return false;
74228
+ }
74229
+ if ((info.mode & 18) !== 0) {
74230
+ return false;
74231
+ }
74232
+ return entry === "config.toml" ? info.isFile() && info.nlink === 1 : info.isDirectory();
74233
+ }
74234
+ async function sweepCodexAuthCell(cell2, onRepair, rmImpl) {
74235
+ const removeImpl = rmImpl ?? ((target) => rm10(target, { recursive: true, force: true }));
74236
+ const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
74237
+ const entries = await readdir6(cell2);
74238
+ for (const entry of entries) {
74239
+ const entryPath = path14.join(cell2, entry);
74240
+ const info = await lstat4(entryPath).catch(() => null);
74241
+ const isOwnedAuthJson = entry === "auth.json" && info !== null && classifyAuthJsonStat(
74242
+ {
74243
+ isSymbolicLink: info.isSymbolicLink(),
74244
+ isFile: info.isFile(),
74245
+ nlink: info.nlink,
74246
+ uid: info.uid,
74247
+ mode: info.mode,
74248
+ size: info.size
74249
+ },
74250
+ selfUid
74251
+ ).kind === "owned";
74252
+ if (isOwnedAuthJson) {
74253
+ continue;
74254
+ }
74255
+ const isRoutine = info !== null && isRoutineCodexBookkeepingShape(entry, info, selfUid);
74256
+ if (!isRoutine) {
74257
+ onRepair?.(`codex credential cell ${cell2}: removing unexpected entry ${JSON.stringify(entry)}`);
74258
+ }
74259
+ try {
74260
+ await removeImpl(entryPath);
74261
+ } catch (error51) {
74262
+ const code = error51.code ?? "unknown";
74263
+ throw new Error(
74264
+ `refusing to use the codex credential cell: could not remove ${JSON.stringify(entry)} from the codex credential cell ${cell2} (${code}). The credential-cell sweep must not fail open \u2014 clear the entry (e.g. \`chflags -R nouchg\`, then \`rm -rf\`) and retry.`
74265
+ );
74266
+ }
74267
+ }
74268
+ }
74269
+ var CODEX_PROBE_CREDENTIAL_CELL_LOCK_WAIT_MS = 5e3;
74270
+ async function probeCodexLoginStatus(codexBin, cell2, opts) {
74271
+ const spawnImpl = opts?.spawnImpl ?? spawnCapture;
74272
+ const acquireLockImpl = opts?.acquireLockImpl ?? acquireCodexHomeLock;
74273
+ const waitMs = opts?.credentialCellLockWaitMs ?? CODEX_PROBE_CREDENTIAL_CELL_LOCK_WAIT_MS;
74274
+ let lock;
74275
+ try {
74276
+ lock = await acquireLockImpl(cell2, { waitMs, label: "codex credential cell" });
74277
+ } catch (error51) {
74278
+ opts?.onProbeError?.(
74279
+ `codex credential cell ${cell2}: could not acquire the credential-cell lock (${redactForLog(error51 instanceof Error ? error51.message : String(error51))}); could not determine login status`
74280
+ );
74281
+ return { kind: "command_failed" };
74282
+ }
74283
+ if (lock === null) {
74284
+ opts?.onProbeError?.(
74285
+ `codex credential cell ${cell2}: another process is using this cell (a running codex turn, or a concurrent device-auth login); could not determine login status without racing it`
74286
+ );
74287
+ return { kind: "command_failed" };
74288
+ }
74289
+ try {
74290
+ try {
74291
+ await ensureVerifiedCodexHomeDir(cell2, opts?.onRepair, "codex credential cell");
74292
+ await sweepCodexAuthCell(cell2, opts?.onRepair, opts?.rmImpl);
74293
+ } catch (error51) {
74294
+ opts?.onProbeError?.(
74295
+ `codex credential cell ${cell2}: could not verify or sweep the cell before probing login status (${redactForLog(error51 instanceof Error ? error51.message : String(error51))}); could not determine login status`
74296
+ );
74297
+ return { kind: "command_failed" };
74298
+ }
74299
+ const env = buildCodexAuthEnv(opts?.env ?? process.env, cell2);
74300
+ let result;
74301
+ try {
74302
+ result = await spawnImpl(codexBin, ["login", "status"], {
74303
+ env,
74304
+ ...opts?.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
74305
+ ...opts?.killGraceMs !== void 0 ? { killGraceMs: opts.killGraceMs } : {},
74306
+ ...opts?.containmentForTest !== void 0 ? { containment: opts.containmentForTest } : {}
74307
+ });
74308
+ } catch (error51) {
74309
+ opts?.onProbeError?.(describeSpawnFailure("codex login status", error51));
74310
+ if (typeof error51 === "object" && error51 !== null && "codexProbeExitUnconfirmedMs" in error51) {
74311
+ return { kind: "exit_unconfirmed" };
74312
+ }
74313
+ return { kind: "command_failed" };
74314
+ }
74315
+ return parseCodexLoginStatus(result);
74316
+ } finally {
74317
+ await lock.release();
74318
+ }
74319
+ }
74320
+ var CODEX_LOGIN_CREDENTIAL_CELL_LOCK_WAIT_MS = 5e3;
74321
+ async function hasUsableCodexAuthCredential(cell2) {
74322
+ const info = await lstat4(path14.join(cell2, "auth.json")).catch(() => null);
74323
+ if (info === null) {
74324
+ return false;
74325
+ }
74326
+ const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
74327
+ return classifyAuthJsonStat(
74328
+ {
74329
+ isSymbolicLink: info.isSymbolicLink(),
74330
+ isFile: info.isFile(),
74331
+ nlink: info.nlink,
74332
+ uid: info.uid,
74333
+ mode: info.mode,
74334
+ size: info.size
74335
+ },
74336
+ selfUid
74337
+ ).kind === "owned";
74338
+ }
74339
+ async function captureCodexAuthJsonIdentity(cell2) {
74340
+ const info = await lstat4(path14.join(cell2, "auth.json"), { bigint: true }).catch(() => null);
74341
+ if (info === null) {
74342
+ return null;
74343
+ }
74344
+ return { ino: info.ino, mtimeNs: info.mtimeNs, size: info.size };
74345
+ }
74346
+ function isEnoentError(error51) {
74347
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
74348
+ }
74349
+ async function runCodexDeviceAuthLogin(options) {
74350
+ const acquireLockImpl = options.acquireLockImpl ?? acquireCodexHomeLock;
74351
+ const spawnLoginImpl = options.spawnLoginImpl ?? spawnLogin;
74352
+ const commitSeedMarkerImpl = options.commitSeedMarkerImpl ?? ((cell2) => commitCodexHomeSeedMarker(cell2, { required: true }));
74353
+ const waitMs = options.credentialCellLockWaitMs ?? CODEX_LOGIN_CREDENTIAL_CELL_LOCK_WAIT_MS;
74354
+ const stderr = options.io?.stderr ?? process.stderr;
74355
+ try {
74356
+ const lock = await acquireLockImpl(options.cell, { waitMs, label: "codex credential cell" });
74357
+ if (lock === null) {
74358
+ stderr.write(
74359
+ "another process is using this runner's codex credential cell (a running codex turn, or a concurrent device-auth login); wait for it to finish and retry.\n"
74360
+ );
74361
+ return 1;
74362
+ }
74363
+ try {
74364
+ await ensureVerifiedCodexHomeDir(options.cell, options.onRepair, "codex credential cell");
74365
+ const markerPath = path14.join(path14.dirname(options.cell), `${path14.basename(options.cell)}.seeded`);
74366
+ try {
74367
+ await rm10(markerPath, { force: true });
74368
+ } catch (error51) {
74369
+ throw new Error(
74370
+ `refusing to run codex device-auth login: could not clear the existing auth marker ${markerPath}; the server must keep codex work blocked until login completes (${redactForLog(error51 instanceof Error ? error51.message : String(error51))})`
74371
+ );
74372
+ }
74373
+ await sweepCodexAuthCell(options.cell, options.onRepair);
74374
+ const preLoginAuthJsonIdentity = await captureCodexAuthJsonIdentity(options.cell);
74375
+ let loginCode;
74376
+ try {
74377
+ loginCode = await spawnLoginImpl(options.codexBin, ["login", "--device-auth"], {
74378
+ env: buildCodexAuthEnv(options.env, options.cell),
74379
+ ...options.io ? { io: options.io } : {},
74380
+ // FIX 8 (DER-3338 round 2, P3): default to "to-stderr", NOT spawnLogin's own "inherit"
74381
+ // default. Conservative-by-default (invariant 10): an unspecified caller may be running
74382
+ // under `--json`, and the device-auth URL/one-time code are human copy that must never land
74383
+ // on stdout and corrupt a machine-readable stream. A caller that specifically wants the
74384
+ // ceremony on an inherited TTY stdout (interactive standalone use) opts in explicitly.
74385
+ ceremonyStdout: options.ceremonyStdout ?? "to-stderr"
74386
+ });
74387
+ } catch (error51) {
74388
+ if (isEnoentError(error51)) {
74389
+ stderr.write("codex CLI not found on PATH; install codex first.\n");
74390
+ return 1;
74391
+ }
74392
+ throw error51;
74393
+ }
74394
+ if (loginCode !== 0) {
74395
+ stderr.write(
74396
+ `codex login --device-auth exited ${loginCode}; the runner credential was not provisioned.
74397
+ `
74398
+ );
74399
+ return 1;
74400
+ }
74401
+ const postLoginUsable = await hasUsableCodexAuthCredential(options.cell);
74402
+ const postLoginAuthJsonIdentity = postLoginUsable ? await captureCodexAuthJsonIdentity(options.cell) : null;
74403
+ const credentialWasRenewed = preLoginAuthJsonIdentity === null || postLoginAuthJsonIdentity !== null && (preLoginAuthJsonIdentity.ino !== postLoginAuthJsonIdentity.ino || preLoginAuthJsonIdentity.mtimeNs !== postLoginAuthJsonIdentity.mtimeNs || preLoginAuthJsonIdentity.size !== postLoginAuthJsonIdentity.size);
74404
+ if (!postLoginUsable || !credentialWasRenewed) {
74405
+ stderr.write(
74406
+ postLoginUsable ? "codex login --device-auth reported success, but left the existing credential in the runner's codex credential cell untouched; the runner's authentication was not renewed. Retry the login.\n" : "codex login --device-auth reported success, but no usable credential is present in the runner's codex credential cell; the runner is NOT authenticated. Retry the login.\n"
74407
+ );
74408
+ return 1;
74409
+ }
74410
+ try {
74411
+ await commitSeedMarkerImpl(options.cell);
74412
+ } catch (error51) {
74413
+ stderr.write(
74414
+ `codex login --device-auth completed, but the runner auth marker could not be committed; the server will keep codex work blocked until it is. Retry the login: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
74415
+ `
74416
+ );
74417
+ return 1;
74418
+ }
74419
+ return 0;
74420
+ } finally {
74421
+ await lock.release();
74422
+ }
74423
+ } catch (error51) {
74424
+ stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
74425
+ `);
74426
+ return 1;
74427
+ }
74428
+ }
74429
+ async function evaluateCodexAuth(options) {
74430
+ const probeOpts = {
74431
+ ...options.env !== void 0 ? { env: options.env } : {},
74432
+ ...options.spawnImpl !== void 0 ? { spawnImpl: options.spawnImpl } : {},
74433
+ ...options.onProbeError !== void 0 ? { onProbeError: options.onProbeError } : {}
74434
+ };
74435
+ const statusProbeOpts = {
74436
+ ...probeOpts,
74437
+ ...options.acquireLockImpl !== void 0 ? { acquireLockImpl: options.acquireLockImpl } : {},
74438
+ ...options.credentialCellLockWaitMs !== void 0 ? { credentialCellLockWaitMs: options.credentialCellLockWaitMs } : {},
74439
+ ...options.onRepair !== void 0 ? { onRepair: options.onRepair } : {}
74440
+ };
74441
+ const version4 = await probeCodexVersion(options.codexBin, probeOpts);
74442
+ const cellState = await inspectCodexAuthCell(options.cell);
74443
+ const versionRefused = version4.kind !== "supported";
74444
+ const status = !versionRefused && (cellState.kind === "empty" || cellState.kind === "owned") ? await probeCodexLoginStatus(options.codexBin, options.cell, statusProbeOpts) : null;
74445
+ const decision = decideCodexAuth(version4, cellState, status ?? { kind: "unrecognized" });
74446
+ return { version: version4, cell: cellState, status, decision };
74447
+ }
74448
+
74449
+ // src/runner-setup-orchestrator.ts
73438
74450
  var SETUP_SIDECAR_VERSION = 1;
73439
74451
  var SETUP_STAGES_IN_ORDER = [
73440
74452
  runnerSetupStageSchema.enum.pairing,
@@ -73503,13 +74515,13 @@ var runnerSetupSessionEnvelopeSchema = external_exports.object({
73503
74515
  action_required: runnerSetupActionRequiredSchema.nullable()
73504
74516
  }).passthrough();
73505
74517
  function runnerSetupSidecarBaseDir(homeDir) {
73506
- return path14.join(homeDir, ".rost", "runner", "setup");
74518
+ return path15.join(homeDir, ".rost", "runner", "setup");
73507
74519
  }
73508
74520
  function sidecarFilePath(homeDir, setupId) {
73509
- return path14.join(runnerSetupSidecarBaseDir(homeDir), `${setupId}.json`);
74521
+ return path15.join(runnerSetupSidecarBaseDir(homeDir), `${setupId}.json`);
73510
74522
  }
73511
74523
  function currentSidecarPath(homeDir) {
73512
- return path14.join(runnerSetupSidecarBaseDir(homeDir), "current");
74524
+ return path15.join(runnerSetupSidecarBaseDir(homeDir), "current");
73513
74525
  }
73514
74526
  async function readPrivateTextFile(filePath) {
73515
74527
  let handle = null;
@@ -73578,7 +74590,7 @@ async function writeSidecar(homeDir, sidecar, options = {}) {
73578
74590
  }
73579
74591
  }
73580
74592
  async function clearCurrentSidecar(homeDir) {
73581
- await rm10(currentSidecarPath(homeDir), { force: true });
74593
+ await rm11(currentSidecarPath(homeDir), { force: true });
73582
74594
  }
73583
74595
  function makeFreshSidecar(session, appUrl2, stateFile, now) {
73584
74596
  return {
@@ -73830,8 +74842,8 @@ function parseArgs2(args, options = {}) {
73830
74842
  return { ok: true, json: json2, agent, yes, noExecute, execute: execute2, runtime, userCode, setupId, stateFile, explicitStateFile, timeoutMs, rest };
73831
74843
  }
73832
74844
  function durableSetupStateFile(homeDir, appUrl2) {
73833
- const key = createHash5("sha1").update(appUrl2).digest("hex").slice(0, 8);
73834
- return path14.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-setup-${key}.json`);
74845
+ const key = createHash7("sha1").update(appUrl2).digest("hex").slice(0, 8);
74846
+ return path15.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-setup-${key}.json`);
73835
74847
  }
73836
74848
  function isEphemeralStateFile(stateFile, appUrl2) {
73837
74849
  return sameStateFile(stateFile, defaultRunnerStateFile(appUrl2));
@@ -73889,11 +74901,11 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
73889
74901
  actionRequired: { kind: "manual_service_install", runtime: null, url: null, user_code: null, expires_at: null }
73890
74902
  };
73891
74903
  }
73892
- if (!path14.isAbsolute(stateFile)) {
74904
+ if (!path15.isAbsolute(stateFile)) {
73893
74905
  return {
73894
74906
  ok: false,
73895
74907
  exitCode: 1,
73896
- message: `The runner state file ${stateFile} is a relative path, and a launchd service resolves it against its own working directory, not yours. Re-run setup with an absolute path, for example \`--state-file "${path14.resolve(stateFile)}"\`.`
74908
+ message: `The runner state file ${stateFile} is a relative path, and a launchd service resolves it against its own working directory, not yours. Re-run setup with an absolute path, for example \`--state-file "${path15.resolve(stateFile)}"\`.`
73897
74909
  };
73898
74910
  }
73899
74911
  if (isEphemeralStateFile(stateFile, appUrl2)) {
@@ -73927,9 +74939,9 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
73927
74939
  }
73928
74940
  function formatUsage(binName) {
73929
74941
  return [
73930
- `Usage: ${binName} runner setup start --runtime claude [--user-code <code>] [--json] [--agent] [--state-file <path>] [--timeout-ms <ms>] [--no-execute | --execute]`,
74942
+ `Usage: ${binName} runner setup start --runtime claude|codex [--user-code <code>] [--json] [--agent] [--state-file <path>] [--timeout-ms <ms>] [--no-execute | --execute]`,
73931
74943
  ` ${binName} runner setup status [--setup-id <id>] [--json]`,
73932
- ` ${binName} runner setup resume [--setup-id <id>] [--runtime claude] [--user-code <code>] [--json] [--agent] [--timeout-ms <ms>] [--no-execute | --execute]`,
74944
+ ` ${binName} runner setup resume [--setup-id <id>] [--runtime claude|codex] [--user-code <code>] [--json] [--agent] [--timeout-ms <ms>] [--no-execute | --execute]`,
73933
74945
  ` ${binName} runner setup cancel [--setup-id <id>] [--json]`
73934
74946
  ].join("\n");
73935
74947
  }
@@ -73941,9 +74953,6 @@ function validateRuntimeFlag(value) {
73941
74953
  if (!parsed.success) {
73942
74954
  return { ok: false, message: `Unsupported runtime: ${value}.` };
73943
74955
  }
73944
- if (parsed.data === "codex") {
73945
- return { ok: false, message: "Codex setup is not yet available." };
73946
- }
73947
74956
  return { ok: true, runtime: parsed.data };
73948
74957
  }
73949
74958
  function validateAgentUserCode(agent, userCode, verb, io, json2) {
@@ -73984,8 +74993,15 @@ async function validateProviderConsentCheckpoint(sidecar, homeDir) {
73984
74993
  return false;
73985
74994
  }
73986
74995
  const runnerId = pairingCheckpoint.runner_id;
73987
- const cell2 = stableClaudeHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
73988
- return cell2 === checkpoint.cell && !await claudeCellNeedsLogin(cell2);
74996
+ if (provenRuntimes.length !== 1) return false;
74997
+ if (provenRuntimes[0] === "claude") {
74998
+ const cell3 = stableClaudeHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
74999
+ return cell3 === checkpoint.cell && !await claudeCellNeedsLogin(cell3);
75000
+ }
75001
+ const cell2 = stableCodexHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
75002
+ if (cell2 !== checkpoint.cell) return false;
75003
+ const auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell2 });
75004
+ return auth.decision.kind === "proceed_to_validation";
73989
75005
  }
73990
75006
  async function validateServiceInstallCheckpoint(sidecar, options, execute2) {
73991
75007
  const checkpoint = sidecar.stages.service_install;
@@ -74091,10 +75107,13 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
74091
75107
  const newState = {
74092
75108
  runner_id: claimResult.runnerId,
74093
75109
  runner_secret: claimResult.runnerSecret,
75110
+ active_setup_id: sidecar.setup_id,
75111
+ ...sidecar.selected_runtimes[0] !== void 0 ? { setup_runtime: sidecar.selected_runtimes[0] } : {},
74094
75112
  ...claimResult.tenantId !== null ? { tenant_id: claimResult.tenantId } : {},
74095
75113
  ...claimResult.name !== null ? { name: claimResult.name } : {},
74096
75114
  ...claimResult.serviceKeyPrivate !== void 0 ? { service_key_private: claimResult.serviceKeyPrivate } : {},
74097
- ...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {}
75115
+ ...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {},
75116
+ ...claimResult.serviceKeyGeneration !== void 0 ? { service_key_generation: claimResult.serviceKeyGeneration } : {}
74098
75117
  };
74099
75118
  await saveState(sidecar.state_file, newState);
74100
75119
  await reapSupersededRunnerHomes(priorState, newState.runner_id, homeDir);
@@ -74137,14 +75156,33 @@ async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceK
74137
75156
  }
74138
75157
  }
74139
75158
  async function runProviderConsentStage(sidecar, homeDir, appUrl2, env, io, loginImpl, now, allowLocalCeremony, json2) {
74140
- if (!sidecar.selected_runtimes.includes(runnerSetupRuntimeSchema.enum.claude)) {
74141
- return { kind: "login_failed", exitCode: 1, message: "Codex setup is not yet available." };
74142
- }
74143
75159
  const pairing = sidecar.stages.pairing;
74144
75160
  if (pairing === void 0) {
74145
75161
  return { kind: "login_failed", exitCode: 1, message: "Pairing checkpoint missing before provider consent." };
74146
75162
  }
74147
75163
  const state = { runner_id: pairing.runner_id, runner_secret: "" };
75164
+ if (sidecar.selected_runtimes.length === 1 && sidecar.selected_runtimes[0] === "codex") {
75165
+ const cell3 = stableCodexHomeDir(state, homeDir);
75166
+ let auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell3, env });
75167
+ if (auth.decision.kind === "proceed_to_validation") {
75168
+ return { kind: "done", sidecar: addStageCheckpoint(sidecar, runnerSetupStageSchema.enum.provider_consent, { cell: cell3, runtimes: ["codex"] }, now) };
75169
+ }
75170
+ if (auth.decision.kind !== "device_auth_login_required" || !allowLocalCeremony) {
75171
+ return { kind: "needs_human_action" };
75172
+ }
75173
+ const loginExit2 = await runCodexDeviceAuthLogin({
75174
+ codexBin: "codex",
75175
+ cell: cell3,
75176
+ env,
75177
+ io: { stdout: io.stderr, stderr: io.stderr },
75178
+ ceremonyStdout: json2 ? "to-stderr" : "inherit"
75179
+ });
75180
+ if (loginExit2 !== 0) {
75181
+ return { kind: "login_failed", exitCode: 1, message: `Codex provider consent failed or was declined (exit ${loginExit2}). Re-run resume to retry.` };
75182
+ }
75183
+ auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell3, env });
75184
+ return auth.decision.kind === "proceed_to_validation" ? { kind: "done", sidecar: addStageCheckpoint(sidecar, runnerSetupStageSchema.enum.provider_consent, { cell: cell3, runtimes: ["codex"] }, now) } : { kind: "login_failed", exitCode: 1, message: "Codex credential cell is not ready after login." };
75185
+ }
74148
75186
  const cell2 = stableClaudeHomeDir(state, homeDir);
74149
75187
  if (!await claudeCellNeedsLogin(cell2)) {
74150
75188
  const provenRuntimes = [runnerSetupRuntimeSchema.enum.claude];