@rosthq/cli 0.7.163 → 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,37 @@ 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"];
29228
+ var RUNNER_SETUP_PROBE_CAPABILITY_VERSION = 1;
29229
+ var runnerSetupValidationMcpCapabilityPayloadSchema = external_exports.object({
29230
+ capability_version: external_exports.literal(RUNNER_SETUP_PROBE_CAPABILITY_VERSION),
29231
+ tenant_id: uuid3,
29232
+ runner_id: uuid3,
29233
+ setup_id: uuid3,
29234
+ job_id: uuid3,
29235
+ runtime: runnerSetupRuntimeSchema,
29236
+ service_key_generation: external_exports.number().int().nonnegative(),
29237
+ nonce: external_exports.string().min(16).max(120),
29238
+ expires_at: external_exports.number().int().positive(),
29239
+ allowed_command_ids: external_exports.tuple([external_exports.literal("runner_setup.status")])
29240
+ }).strict();
29241
+ var runnerSetupValidationMcpConfigSchema = external_exports.object({
29242
+ url: external_exports.string().url(),
29243
+ token: external_exports.string().min(1).max(4096),
29244
+ expires_at: isoDateTime
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
+ }
29226
29258
  var runnerSetupValidationClaimRequestSchema = external_exports.object({
29227
29259
  setup_id: uuid3,
29228
29260
  runtime: runnerSetupRuntimeSchema
@@ -29231,7 +29263,8 @@ var runnerSetupValidationClaimResponseSchema = external_exports.object({
29231
29263
  job_id: uuid3,
29232
29264
  challenge: runnerSetupValidationChallengeSchema,
29233
29265
  probe_capability_version: external_exports.number().int().positive(),
29234
- allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20)
29266
+ allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20),
29267
+ mcp: runnerSetupValidationMcpConfigSchema
29235
29268
  }).strict();
29236
29269
  var runnerSetupValidationReportRequestSchema = external_exports.object({
29237
29270
  job_id: uuid3,
@@ -30957,7 +30990,7 @@ var forbiddenImportKeys = /* @__PURE__ */ new Set([
30957
30990
  "integration_id",
30958
30991
  "integrationId"
30959
30992
  ]);
30960
- function rejectForbiddenImportShape(value, ctx, path15 = []) {
30993
+ function rejectForbiddenImportShape(value, ctx, path16 = []) {
30961
30994
  if (value === null || value === void 0) {
30962
30995
  return;
30963
30996
  }
@@ -30965,21 +30998,21 @@ function rejectForbiddenImportShape(value, ctx, path15 = []) {
30965
30998
  if (hasSecretShapedValue(value)) {
30966
30999
  ctx.addIssue({
30967
31000
  code: external_exports.ZodIssueCode.custom,
30968
- path: path15,
31001
+ path: path16,
30969
31002
  message: "Definition imports cannot contain secret-shaped values. Provide credentials later through vault-backed connection flows."
30970
31003
  });
30971
31004
  }
30972
31005
  return;
30973
31006
  }
30974
31007
  if (Array.isArray(value)) {
30975
- value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...path15, index]));
31008
+ value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...path16, index]));
30976
31009
  return;
30977
31010
  }
30978
31011
  if (typeof value !== "object") {
30979
31012
  return;
30980
31013
  }
30981
31014
  for (const [key, nested] of Object.entries(value)) {
30982
- const nestedPath = [...path15, key];
31015
+ const nestedPath = [...path16, key];
30983
31016
  if (forbiddenImportKeys.has(key)) {
30984
31017
  ctx.addIssue({
30985
31018
  code: external_exports.ZodIssueCode.custom,
@@ -35916,7 +35949,7 @@ var preflightArtifactDispositionSchema = external_exports.object({
35916
35949
  // on this envelope.
35917
35950
  transformations: external_exports.array(preflightTransformationSchema)
35918
35951
  }).strict().superRefine((data, ctx) => {
35919
- 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 });
35920
35953
  const reasonIsBlank = data.reason !== null && data.reason.trim().length === 0;
35921
35954
  if (reasonIsBlank) add(["reason"], "reason must be nonblank when supplied");
35922
35955
  if (data.presence === "missing") {
@@ -36955,14 +36988,14 @@ var COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES = 5e3;
36955
36988
  function jsonByteLength(value) {
36956
36989
  return new TextEncoder().encode(JSON.stringify(value)).byteLength;
36957
36990
  }
36958
- function boundCharterList(value, path15, ctx) {
36991
+ function boundCharterList(value, path16, ctx) {
36959
36992
  if (value.length > COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST) {
36960
36993
  ctx.addIssue({
36961
36994
  code: external_exports.ZodIssueCode.too_big,
36962
36995
  maximum: COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST,
36963
36996
  origin: "array",
36964
36997
  inclusive: true,
36965
- path: path15,
36998
+ path: path16,
36966
36999
  message: `Complete-Seat Charter lists are limited to ${COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST} entries.`
36967
37000
  });
36968
37001
  }
@@ -36979,7 +37012,7 @@ function addCompleteSeatCharterBounds(charter, ctx) {
36979
37012
  boundCharterList(charter.unanswered_boundaries, ["unanswered_boundaries"], ctx);
36980
37013
  }
36981
37014
  let argumentNodes = 0;
36982
- const visitArgument = (value, depth, path15) => {
37015
+ const visitArgument = (value, depth, path16) => {
36983
37016
  argumentNodes += 1;
36984
37017
  if (argumentNodes > COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES) {
36985
37018
  return;
@@ -36987,15 +37020,15 @@ function addCompleteSeatCharterBounds(charter, ctx) {
36987
37020
  if (depth > COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH) {
36988
37021
  ctx.addIssue({
36989
37022
  code: external_exports.ZodIssueCode.custom,
36990
- path: path15,
37023
+ path: path16,
36991
37024
  message: `Complete-Seat permission arguments are limited to ${COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH} nested levels.`
36992
37025
  });
36993
37026
  return;
36994
37027
  }
36995
37028
  if (Array.isArray(value)) {
36996
- value.forEach((entry, index) => visitArgument(entry, depth + 1, [...path15, index]));
37029
+ value.forEach((entry, index) => visitArgument(entry, depth + 1, [...path16, index]));
36997
37030
  } else if (value && typeof value === "object") {
36998
- 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]));
36999
37032
  }
37000
37033
  };
37001
37034
  charter.permission_manifest.forEach((permission, index) => {
@@ -37156,9 +37189,9 @@ var createCompleteSeatRequestSchema = external_exports.object({
37156
37189
  }
37157
37190
  const useKeys = /* @__PURE__ */ new Set();
37158
37191
  for (const [index, use] of value.seat.source_uses.entries()) {
37159
- const path15 = ["seat", "source_uses", index, "source_key"];
37160
- 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." });
37161
- 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." });
37162
37195
  useKeys.add(use.source_key);
37163
37196
  }
37164
37197
  if (value.seat.human_staffing.kind === "planned" && value.seat.human_staffing.source_ref) {
@@ -37171,8 +37204,8 @@ var createCompleteSeatRequestSchema = external_exports.object({
37171
37204
  ...value.seat.parent ? [{ ref: value.seat.parent, path: ["seat", "parent"] }] : [],
37172
37205
  ...value.seat.agent ? [{ ref: value.seat.agent.steward, path: ["seat", "agent", "steward"] }] : []
37173
37206
  ];
37174
- refs.forEach(({ ref, path: path15 }) => {
37175
- 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." });
37176
37209
  });
37177
37210
  }).transform((value) => ({
37178
37211
  ...value,
@@ -37250,7 +37283,7 @@ var completeSeatApprovalProjectionSchema = external_exports.object({
37250
37283
 
37251
37284
  // ../../packages/protocol/src/setup-application.ts
37252
37285
  import { Buffer as Buffer2 } from "node:buffer";
37253
- import { createHash } from "node:crypto";
37286
+ import { createHash as createHash2 } from "node:crypto";
37254
37287
 
37255
37288
  // ../../packages/protocol/src/baserow-filter-operators.ts
37256
37289
  var baserowFilterOperatorSchema = external_exports.enum([
@@ -37668,26 +37701,26 @@ var PROHIBITED_CONTENT_MESSAGE_PREFIX = "Prohibited content";
37668
37701
  function prohibitedContentMessage(rule_id, found) {
37669
37702
  return `${PROHIBITED_CONTENT_MESSAGE_PREFIX} [${rule_id}]: ${found}. ${CONTENT_LINT_REMEDY[rule_id]}`;
37670
37703
  }
37671
- function isIndexedPath(path15, family, field4) {
37672
- 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;
37673
37706
  }
37674
- function isSchemaProvenMachineString(path15) {
37675
- 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") {
37676
37709
  return true;
37677
37710
  }
37678
- 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")) {
37679
37712
  return true;
37680
37713
  }
37681
- 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")) {
37682
37715
  return true;
37683
37716
  }
37684
- 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") {
37685
37718
  return true;
37686
37719
  }
37687
- 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") {
37688
37721
  return true;
37689
37722
  }
37690
- 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");
37691
37724
  }
37692
37725
  function jsonByteLength2(value) {
37693
37726
  try {
@@ -37697,20 +37730,20 @@ function jsonByteLength2(value) {
37697
37730
  return Number.POSITIVE_INFINITY;
37698
37731
  }
37699
37732
  }
37700
- function prohibitedSetupContentViolations(value, path15 = []) {
37733
+ function prohibitedSetupContentViolations(value, path16 = []) {
37701
37734
  if (typeof value === "string") {
37702
- if (isSchemaProvenMachineString(path15)) return [];
37735
+ if (isSchemaProvenMachineString(path16)) return [];
37703
37736
  return contentViolationsForString(value).map(({ rule_id, found }) => ({
37704
37737
  rule_id,
37705
- path: path15,
37738
+ path: path16,
37706
37739
  message: prohibitedContentMessage(rule_id, found)
37707
37740
  }));
37708
37741
  }
37709
37742
  if (Array.isArray(value)) {
37710
- return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...path15, index]));
37743
+ return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...path16, index]));
37711
37744
  }
37712
37745
  if (value && typeof value === "object") {
37713
- return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...path15, key]));
37746
+ return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...path16, key]));
37714
37747
  }
37715
37748
  return [];
37716
37749
  }
@@ -38027,7 +38060,7 @@ function canonicalJson(value) {
38027
38060
  return JSON.stringify(value);
38028
38061
  }
38029
38062
  function digestCanonicalJson(value) {
38030
- return `sha256:${createHash("sha256").update(canonicalJson(value)).digest("hex")}`;
38063
+ return `sha256:${createHash2("sha256").update(canonicalJson(value)).digest("hex")}`;
38031
38064
  }
38032
38065
  var onboardingSetupOutputSchema = external_exports.object({
38033
38066
  application_id: uuidSchema19,
@@ -43690,7 +43723,7 @@ var CommandClient = class {
43690
43723
  this._credentialKind = options.credentialKind;
43691
43724
  }
43692
43725
  async execute(commandId, body = {}, options = {}) {
43693
- const path15 = `/api/commands/${encodeURIComponent(commandId)}`;
43726
+ const path16 = `/api/commands/${encodeURIComponent(commandId)}`;
43694
43727
  const headers = {
43695
43728
  authorization: `Bearer ${this.token}`,
43696
43729
  "content-type": "application/json"
@@ -43698,7 +43731,7 @@ var CommandClient = class {
43698
43731
  if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
43699
43732
  headers["x-rost-seat"] = options.targetSeatId;
43700
43733
  }
43701
- const response = await this.fetchImpl(`${this.appUrl}${path15}`, {
43734
+ const response = await this.fetchImpl(`${this.appUrl}${path16}`, {
43702
43735
  method: "POST",
43703
43736
  headers,
43704
43737
  body: JSON.stringify(body)
@@ -43711,7 +43744,7 @@ var CommandClient = class {
43711
43744
  const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
43712
43745
  throw new CommandClientError(
43713
43746
  response.status,
43714
- `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"
43715
43748
  );
43716
43749
  }
43717
43750
  const parsed = commandResponseSchema.parse(raw);
@@ -49042,8 +49075,8 @@ var IcebergError = class extends Error {
49042
49075
  return this.status === 419;
49043
49076
  }
49044
49077
  };
49045
- function buildUrl(baseUrl, path15, query) {
49046
- const url2 = new URL(path15, baseUrl);
49078
+ function buildUrl(baseUrl, path16, query) {
49079
+ const url2 = new URL(path16, baseUrl);
49047
49080
  if (query) {
49048
49081
  for (const [key, value] of Object.entries(query)) {
49049
49082
  if (value !== void 0) {
@@ -49073,12 +49106,12 @@ function createFetchClient(options) {
49073
49106
  return {
49074
49107
  async request({
49075
49108
  method,
49076
- path: path15,
49109
+ path: path16,
49077
49110
  query,
49078
49111
  body,
49079
49112
  headers
49080
49113
  }) {
49081
- const url2 = buildUrl(options.baseUrl, path15, query);
49114
+ const url2 = buildUrl(options.baseUrl, path16, query);
49082
49115
  const authHeaders = await buildAuthHeaders(options.auth);
49083
49116
  const res = await fetchFn(url2, {
49084
49117
  method,
@@ -49940,7 +49973,7 @@ var StorageFileApi = class extends BaseApiClient {
49940
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.
49941
49974
  * @param fileBody The body of the file to be stored in the bucket.
49942
49975
  */
49943
- async uploadOrUpdate(method, path15, fileBody, fileOptions) {
49976
+ async uploadOrUpdate(method, path16, fileBody, fileOptions) {
49944
49977
  var _this = this;
49945
49978
  return _this.handleOperation(async () => {
49946
49979
  let body;
@@ -49964,7 +49997,7 @@ var StorageFileApi = class extends BaseApiClient {
49964
49997
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
49965
49998
  }
49966
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);
49967
- const cleanPath = _this._removeEmptyFolders(path15);
50000
+ const cleanPath = _this._removeEmptyFolders(path16);
49968
50001
  const _path = _this._getFinalPath(cleanPath);
49969
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 } : {}));
49970
50003
  return {
@@ -50041,8 +50074,8 @@ var StorageFileApi = class extends BaseApiClient {
50041
50074
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50042
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.
50043
50076
  */
50044
- async upload(path15, fileBody, fileOptions) {
50045
- return this.uploadOrUpdate("POST", path15, fileBody, fileOptions);
50077
+ async upload(path16, fileBody, fileOptions) {
50078
+ return this.uploadOrUpdate("POST", path16, fileBody, fileOptions);
50046
50079
  }
50047
50080
  /**
50048
50081
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -50082,9 +50115,9 @@ var StorageFileApi = class extends BaseApiClient {
50082
50115
  * - `objects` table permissions: none
50083
50116
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50084
50117
  */
50085
- async uploadToSignedUrl(path15, token, fileBody, fileOptions) {
50118
+ async uploadToSignedUrl(path16, token, fileBody, fileOptions) {
50086
50119
  var _this3 = this;
50087
- const cleanPath = _this3._removeEmptyFolders(path15);
50120
+ const cleanPath = _this3._removeEmptyFolders(path16);
50088
50121
  const _path = _this3._getFinalPath(cleanPath);
50089
50122
  const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
50090
50123
  url2.searchParams.set("token", token);
@@ -50153,10 +50186,10 @@ var StorageFileApi = class extends BaseApiClient {
50153
50186
  * - `objects` table permissions: `insert`
50154
50187
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50155
50188
  */
50156
- async createSignedUploadUrl(path15, options) {
50189
+ async createSignedUploadUrl(path16, options) {
50157
50190
  var _this4 = this;
50158
50191
  return _this4.handleOperation(async () => {
50159
- let _path = _this4._getFinalPath(path15);
50192
+ let _path = _this4._getFinalPath(path16);
50160
50193
  const headers = _objectSpread22({}, _this4.headers);
50161
50194
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
50162
50195
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -50165,7 +50198,7 @@ var StorageFileApi = class extends BaseApiClient {
50165
50198
  if (!token) throw new StorageError("No token returned by API");
50166
50199
  return {
50167
50200
  signedUrl: url2.toString(),
50168
- path: path15,
50201
+ path: path16,
50169
50202
  token
50170
50203
  };
50171
50204
  });
@@ -50225,8 +50258,8 @@ var StorageFileApi = class extends BaseApiClient {
50225
50258
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50226
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.
50227
50260
  */
50228
- async update(path15, fileBody, fileOptions) {
50229
- return this.uploadOrUpdate("PUT", path15, fileBody, fileOptions);
50261
+ async update(path16, fileBody, fileOptions) {
50262
+ return this.uploadOrUpdate("PUT", path16, fileBody, fileOptions);
50230
50263
  }
50231
50264
  /**
50232
50265
  * Moves an existing file to a new path in the same bucket.
@@ -50377,10 +50410,10 @@ var StorageFileApi = class extends BaseApiClient {
50377
50410
  * - `objects` table permissions: `select`
50378
50411
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50379
50412
  */
50380
- async createSignedUrl(path15, expiresIn, options) {
50413
+ async createSignedUrl(path16, expiresIn, options) {
50381
50414
  var _this8 = this;
50382
50415
  return _this8.handleOperation(async () => {
50383
- let _path = _this8._getFinalPath(path15);
50416
+ let _path = _this8._getFinalPath(path16);
50384
50417
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
50385
50418
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
50386
50419
  const query = new URLSearchParams();
@@ -50516,13 +50549,13 @@ var StorageFileApi = class extends BaseApiClient {
50516
50549
  * - `objects` table permissions: `select`
50517
50550
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50518
50551
  */
50519
- download(path15, options, parameters) {
50552
+ download(path16, options, parameters) {
50520
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";
50521
50554
  const query = new URLSearchParams();
50522
50555
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
50523
50556
  if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
50524
50557
  const queryString = query.toString();
50525
- const _path = this._getFinalPath(path15);
50558
+ const _path = this._getFinalPath(path16);
50526
50559
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
50527
50560
  headers: this.headers,
50528
50561
  noResolveJson: true
@@ -50553,9 +50586,9 @@ var StorageFileApi = class extends BaseApiClient {
50553
50586
  * }
50554
50587
  * ```
50555
50588
  */
50556
- async info(path15) {
50589
+ async info(path16) {
50557
50590
  var _this10 = this;
50558
- const _path = _this10._getFinalPath(path15);
50591
+ const _path = _this10._getFinalPath(path16);
50559
50592
  return _this10.handleOperation(async () => {
50560
50593
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
50561
50594
  });
@@ -50576,9 +50609,9 @@ var StorageFileApi = class extends BaseApiClient {
50576
50609
  * .exists('folder/avatar1.png')
50577
50610
  * ```
50578
50611
  */
50579
- async exists(path15) {
50612
+ async exists(path16) {
50580
50613
  var _this11 = this;
50581
- const _path = _this11._getFinalPath(path15);
50614
+ const _path = _this11._getFinalPath(path16);
50582
50615
  try {
50583
50616
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
50584
50617
  return {
@@ -50657,8 +50690,8 @@ var StorageFileApi = class extends BaseApiClient {
50657
50690
  * - `objects` table permissions: none
50658
50691
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50659
50692
  */
50660
- getPublicUrl(path15, options) {
50661
- const _path = this._getFinalPath(path15);
50693
+ getPublicUrl(path16, options) {
50694
+ const _path = this._getFinalPath(path16);
50662
50695
  const query = new URLSearchParams();
50663
50696
  if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
50664
50697
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
@@ -50797,10 +50830,10 @@ var StorageFileApi = class extends BaseApiClient {
50797
50830
  * - `objects` table permissions: `select`
50798
50831
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
50799
50832
  */
50800
- async list(path15, options, parameters) {
50833
+ async list(path16, options, parameters) {
50801
50834
  var _this13 = this;
50802
50835
  return _this13.handleOperation(async () => {
50803
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path15 || "" });
50836
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path16 || "" });
50804
50837
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
50805
50838
  });
50806
50839
  }
@@ -50865,11 +50898,11 @@ var StorageFileApi = class extends BaseApiClient {
50865
50898
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
50866
50899
  return btoa(data);
50867
50900
  }
50868
- _getFinalPath(path15) {
50869
- return `${this.bucketId}/${path15.replace(/^\/+/, "")}`;
50901
+ _getFinalPath(path16) {
50902
+ return `${this.bucketId}/${path16.replace(/^\/+/, "")}`;
50870
50903
  }
50871
- _removeEmptyFolders(path15) {
50872
- return path15.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
50904
+ _removeEmptyFolders(path16) {
50905
+ return path16.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
50873
50906
  }
50874
50907
  /** Modifies the `query`, appending values the from `transform` */
50875
50908
  applyTransformOptsToQuery(query, transform2) {
@@ -55104,7 +55137,7 @@ async function recordImplementationRunClosed(store, status, fields, closedAt = /
55104
55137
  }
55105
55138
 
55106
55139
  // src/skill-trust.ts
55107
- import { createHash as createHash2, createPublicKey, verify } from "node:crypto";
55140
+ import { createHash as createHash3, createPublicKey, verify } from "node:crypto";
55108
55141
  var TRUSTED_SIGNER_PUBLIC_KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOXSsKe/zFjaoAdZdvP/esPoeM1fsCGFmxv0INDlCxaW rost-implementation-skill-release";
55109
55142
  var TRUSTED_SIGNER_FINGERPRINT = "SHA256:ziSyU9vhGi0Q3GaeePB1R6BI3GqXutLhepnF/4Hx4rY";
55110
55143
  var RELEASE_REPOSITORY = {
@@ -55152,7 +55185,7 @@ var skillManifestSchema = external_exports.object({
55152
55185
  ).min(1)
55153
55186
  }).strict();
55154
55187
  function sha256Hex2(content) {
55155
- return createHash2("sha256").update(content).digest("hex");
55188
+ return createHash3("sha256").update(content).digest("hex");
55156
55189
  }
55157
55190
  function readSshString(buffer, offset) {
55158
55191
  if (offset + 4 > buffer.length) {
@@ -55223,7 +55256,7 @@ function splitSignedTag(rawTagObject) {
55223
55256
  };
55224
55257
  }
55225
55258
  function buildSshSigMessage(payload) {
55226
- const digest = createHash2("sha512").update(payload, "utf8").digest();
55259
+ const digest = createHash3("sha512").update(payload, "utf8").digest();
55227
55260
  return Buffer.concat([
55228
55261
  Buffer.from("SSHSIG"),
55229
55262
  makeSshString(Buffer.from("git")),
@@ -68138,7 +68171,7 @@ import path13 from "node:path";
68138
68171
 
68139
68172
  // src/forge-workspace.ts
68140
68173
  import { execFile as execFileCallback4 } from "node:child_process";
68141
- import { createHash as createHash3, randomBytes } from "node:crypto";
68174
+ import { createHash as createHash4, randomBytes } from "node:crypto";
68142
68175
  import { constants as fsConstants5 } from "node:fs";
68143
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";
68144
68177
  import os2 from "node:os";
@@ -68399,7 +68432,7 @@ async function hasUsableClaudeCredentialFile(cell2) {
68399
68432
  return parseUsableClaudeCredentialJson(read.data.toString("utf8")) !== null;
68400
68433
  }
68401
68434
  function claudeKeychainServiceNameForSecureStorageDir(secureStorageDir) {
68402
- const digest = createHash3("sha256").update(secureStorageDir).digest("hex").slice(0, 8);
68435
+ const digest = createHash4("sha256").update(secureStorageDir).digest("hex").slice(0, 8);
68403
68436
  return `Claude Code-credentials-${digest}`;
68404
68437
  }
68405
68438
  function parseUsableClaudeCredentialJson(raw) {
@@ -68934,7 +68967,7 @@ var FORGE_GIT_AUTHOR_EMAIL = `${RSF_ADDON_BRAND.cliGroup}@${BRAND_DISTRIBUTION.c
68934
68967
  function shortRequestId(buildRequestId) {
68935
68968
  const hex3 = buildRequestId.replace(/-/g, "");
68936
68969
  const slice = hex3.slice(0, 8);
68937
- 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);
68938
68971
  }
68939
68972
  function requestRoot(baseDir, buildRequestId) {
68940
68973
  return path10.join(baseDir, "work", shortRequestId(buildRequestId));
@@ -69622,7 +69655,7 @@ async function removeWorkspace(ws) {
69622
69655
 
69623
69656
  // src/runner-serve.ts
69624
69657
  import { execFile as execFileCallback5, spawn } from "node:child_process";
69625
- 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";
69626
69659
  import { constants as fsConstants6 } from "node:fs";
69627
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";
69628
69661
  import { cpus, homedir as homedir7, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
@@ -69866,6 +69899,131 @@ function decideReconcileAction(input) {
69866
69899
  };
69867
69900
  }
69868
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
+
69869
70027
  // src/runner-serve.ts
69870
70028
  var execFile5 = promisify5(execFileCallback5);
69871
70029
  var EXCLUSIVE_NOFOLLOW_FLAGS = fsConstants6.O_CREAT | fsConstants6.O_EXCL | fsConstants6.O_WRONLY | fsConstants6.O_NOFOLLOW;
@@ -69984,6 +70142,43 @@ ${usage()}
69984
70142
  serverClaimContractVersion = learnedVersion !== null && learnedVersion >= HEARTBEAT_CLAIM_MIN_CONTRACT_VERSION ? learnedVersion : null;
69985
70143
  options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
69986
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
+ }
69987
70182
  }
69988
70183
  if (localRuntime) {
69989
70184
  activeSessions += 1;
@@ -70405,7 +70600,7 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
70405
70600
  return state;
70406
70601
  }
70407
70602
  async function migrateLegacyCodexHome(state, homeDir, io) {
70408
- 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);
70409
70604
  const newHome = stableCodexHomeDir(state, homeDir);
70410
70605
  const legacyCandidates = [
70411
70606
  path12.join(tmpdir2(), "rost-runner-codex-home", `rost-runner-codex-home-${runnerKey}`),
@@ -70465,7 +70660,7 @@ async function migrateOneLegacyCodexHome(legacyHome, newHome, io) {
70465
70660
  }
70466
70661
  }
70467
70662
  function defaultRunnerStateFile(appUrl2) {
70468
- 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`);
70469
70664
  }
70470
70665
  async function loadState(filePath, expectedTenantId) {
70471
70666
  try {
@@ -70479,8 +70674,12 @@ async function loadState(filePath, expectedTenantId) {
70479
70674
  runner_secret: parsed.runner_secret,
70480
70675
  ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
70481
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 } : {},
70482
70679
  ...typeof parsed.service_key_private === "string" ? { service_key_private: parsed.service_key_private } : {},
70483
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 } : {},
70484
70683
  ...isRuntimeSessionMap(parsed.runtime_sessions) ? { runtime_sessions: parsed.runtime_sessions } : {},
70485
70684
  ...isInFlightWorkOrderMap(parsed.in_flight_work_orders) ? { in_flight_work_orders: parsed.in_flight_work_orders } : {}
70486
70685
  };
@@ -70490,6 +70689,11 @@ async function loadState(filePath, expectedTenantId) {
70490
70689
  }
70491
70690
  return null;
70492
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
+ }
70493
70697
  function isRuntimeSessionMap(value) {
70494
70698
  if (!value || typeof value !== "object" || Array.isArray(value)) {
70495
70699
  return false;
@@ -70504,7 +70708,7 @@ function isRuntimeSessionMap(value) {
70504
70708
  }
70505
70709
  async function saveState(filePath, state) {
70506
70710
  await mkdir8(path12.dirname(filePath), { recursive: true, mode: 448 });
70507
- 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`;
70508
70712
  try {
70509
70713
  await writeFile8(tempPath, `${JSON.stringify(state)}
70510
70714
  `, { mode: 384 });
@@ -70547,7 +70751,7 @@ function generateRunnerServiceKeyPair() {
70547
70751
  return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(publicKey) };
70548
70752
  }
70549
70753
  function runnerServiceKeyPairFromPrivate(privatePem) {
70550
- const privateKey = createPrivateKey(privatePem);
70754
+ const privateKey = createPrivateKey2(privatePem);
70551
70755
  return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(createPublicKey2(privateKey)) };
70552
70756
  }
70553
70757
  function runnerServiceKeyPairForState(state) {
@@ -71292,6 +71496,120 @@ function buildTurnCommand(input) {
71292
71496
  ]
71293
71497
  };
71294
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
+ }
71295
71613
  function runLocalTurn(ctx, workOrder, runtime, kind) {
71296
71614
  return spawnRunnerTurn(ctx, workOrder, runtime, kind);
71297
71615
  }
@@ -71428,7 +71746,7 @@ async function prepareForgeTurn(ctx, workOrder, execution, runtime, seams) {
71428
71746
  var FORGE_MAX_CHANGED_PATHS = 2e3;
71429
71747
  var FORGE_MAX_CHANGED_PATH_LENGTH = 500;
71430
71748
  function forgeChangedPathsField(changedPaths) {
71431
- 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);
71432
71750
  return withinCaps ? { changedPaths: [...changedPaths] } : {};
71433
71751
  }
71434
71752
  async function finalizeForgeBuildPush(ctx, prep, result, seams) {
@@ -71557,11 +71875,11 @@ function journaledClaudeConfigDirNames(state) {
71557
71875
  );
71558
71876
  }
71559
71877
  function stableCodexHomeDir(state, homeDir) {
71560
- 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);
71561
71879
  return path12.join(runnerCodexHomeBaseDir(homeDir), `rost-runner-codex-home-${key}`);
71562
71880
  }
71563
71881
  function stableClaudeHomeDir(state, homeDir) {
71564
- 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);
71565
71883
  return path12.join(runnerClaudeHomeBaseDir(homeDir), `rost-runner-claude-home-${key}`);
71566
71884
  }
71567
71885
  function credentialCellDirForRuntime(runtime, state, homeDir) {
@@ -71674,11 +71992,11 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
71674
71992
  const token = typeof mcp.token === "string" ? mcp.token : "";
71675
71993
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
71676
71994
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
71677
- 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`);
71678
71996
  const prompt = buildTurnPrompt({ kind, workOrder, execution, hasWorkspace: forgePrep !== null });
71679
71997
  const claudeCell = runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
71680
71998
  const claudeConfigDir = runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
71681
- 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)}`),
71682
72000
  claudeCell,
71683
72001
  (message) => ctx.io.stderr.write(`${redactForLog(message)}
71684
72002
  `)
@@ -71719,7 +72037,7 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
71719
72037
  if (sandbox.kind === "seatbelt") {
71720
72038
  sandboxProfilePath = path12.join(
71721
72039
  tmpdir2(),
71722
- `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`
71723
72041
  );
71724
72042
  await writeFile8(sandboxProfilePath, sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
71725
72043
  }
@@ -71971,7 +72289,7 @@ async function spawnMergeConflictModel(ctx, input) {
71971
72289
  const token = typeof mcp.token === "string" ? mcp.token : "";
71972
72290
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
71973
72291
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
71974
- 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`);
71975
72293
  const credentialCellLockWaitMs = 30 * 6e4;
71976
72294
  const credentialCellLock = ctx.credentialCellLockHeld !== true ? await acquireCodexHomeLock(credentialCellDirForRuntime(input.runtime, ctx.state, ctx.config.homeDir), { waitMs: credentialCellLockWaitMs, label: credentialCellLabelForRuntime(input.runtime) }) : null;
71977
72295
  if (ctx.credentialCellLockHeld !== true && credentialCellLock === null) {
@@ -71980,7 +72298,7 @@ async function spawnMergeConflictModel(ctx, input) {
71980
72298
  try {
71981
72299
  const claudeCell = input.runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
71982
72300
  const claudeConfigDir = input.runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
71983
- 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)}`),
71984
72302
  claudeCell,
71985
72303
  (message) => ctx.io.stderr.write(`${redactForLog(message)}
71986
72304
  `)
@@ -72002,7 +72320,7 @@ async function spawnMergeConflictModel(ctx, input) {
72002
72320
  ].join("\n");
72003
72321
  let sandboxProfilePath = null;
72004
72322
  if (input.sandbox.kind === "seatbelt") {
72005
- 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`);
72006
72324
  await writeFile8(sandboxProfilePath, input.sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
72007
72325
  }
72008
72326
  const built = buildTurnCommand({
@@ -73411,11 +73729,724 @@ function isEnoent(error51) {
73411
73729
  }
73412
73730
 
73413
73731
  // src/runner-setup-orchestrator.ts
73414
- import { createHash as createHash5 } from "node:crypto";
73732
+ import { createHash as createHash7 } from "node:crypto";
73415
73733
  import { constants as fsConstants8, realpathSync as realpathSync3 } from "node:fs";
73416
- import { open as open3, rm as rm10 } from "node:fs/promises";
73417
- import path14 from "node:path";
73734
+ import { open as open3, rm as rm11 } from "node:fs/promises";
73735
+ import path15 from "node:path";
73418
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
73419
74450
  var SETUP_SIDECAR_VERSION = 1;
73420
74451
  var SETUP_STAGES_IN_ORDER = [
73421
74452
  runnerSetupStageSchema.enum.pairing,
@@ -73484,13 +74515,13 @@ var runnerSetupSessionEnvelopeSchema = external_exports.object({
73484
74515
  action_required: runnerSetupActionRequiredSchema.nullable()
73485
74516
  }).passthrough();
73486
74517
  function runnerSetupSidecarBaseDir(homeDir) {
73487
- return path14.join(homeDir, ".rost", "runner", "setup");
74518
+ return path15.join(homeDir, ".rost", "runner", "setup");
73488
74519
  }
73489
74520
  function sidecarFilePath(homeDir, setupId) {
73490
- return path14.join(runnerSetupSidecarBaseDir(homeDir), `${setupId}.json`);
74521
+ return path15.join(runnerSetupSidecarBaseDir(homeDir), `${setupId}.json`);
73491
74522
  }
73492
74523
  function currentSidecarPath(homeDir) {
73493
- return path14.join(runnerSetupSidecarBaseDir(homeDir), "current");
74524
+ return path15.join(runnerSetupSidecarBaseDir(homeDir), "current");
73494
74525
  }
73495
74526
  async function readPrivateTextFile(filePath) {
73496
74527
  let handle = null;
@@ -73559,7 +74590,7 @@ async function writeSidecar(homeDir, sidecar, options = {}) {
73559
74590
  }
73560
74591
  }
73561
74592
  async function clearCurrentSidecar(homeDir) {
73562
- await rm10(currentSidecarPath(homeDir), { force: true });
74593
+ await rm11(currentSidecarPath(homeDir), { force: true });
73563
74594
  }
73564
74595
  function makeFreshSidecar(session, appUrl2, stateFile, now) {
73565
74596
  return {
@@ -73811,8 +74842,8 @@ function parseArgs2(args, options = {}) {
73811
74842
  return { ok: true, json: json2, agent, yes, noExecute, execute: execute2, runtime, userCode, setupId, stateFile, explicitStateFile, timeoutMs, rest };
73812
74843
  }
73813
74844
  function durableSetupStateFile(homeDir, appUrl2) {
73814
- const key = createHash5("sha1").update(appUrl2).digest("hex").slice(0, 8);
73815
- 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`);
73816
74847
  }
73817
74848
  function isEphemeralStateFile(stateFile, appUrl2) {
73818
74849
  return sameStateFile(stateFile, defaultRunnerStateFile(appUrl2));
@@ -73870,11 +74901,11 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
73870
74901
  actionRequired: { kind: "manual_service_install", runtime: null, url: null, user_code: null, expires_at: null }
73871
74902
  };
73872
74903
  }
73873
- if (!path14.isAbsolute(stateFile)) {
74904
+ if (!path15.isAbsolute(stateFile)) {
73874
74905
  return {
73875
74906
  ok: false,
73876
74907
  exitCode: 1,
73877
- 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)}"\`.`
73878
74909
  };
73879
74910
  }
73880
74911
  if (isEphemeralStateFile(stateFile, appUrl2)) {
@@ -73908,9 +74939,9 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
73908
74939
  }
73909
74940
  function formatUsage(binName) {
73910
74941
  return [
73911
- `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]`,
73912
74943
  ` ${binName} runner setup status [--setup-id <id>] [--json]`,
73913
- ` ${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]`,
73914
74945
  ` ${binName} runner setup cancel [--setup-id <id>] [--json]`
73915
74946
  ].join("\n");
73916
74947
  }
@@ -73922,9 +74953,6 @@ function validateRuntimeFlag(value) {
73922
74953
  if (!parsed.success) {
73923
74954
  return { ok: false, message: `Unsupported runtime: ${value}.` };
73924
74955
  }
73925
- if (parsed.data === "codex") {
73926
- return { ok: false, message: "Codex setup is not yet available." };
73927
- }
73928
74956
  return { ok: true, runtime: parsed.data };
73929
74957
  }
73930
74958
  function validateAgentUserCode(agent, userCode, verb, io, json2) {
@@ -73965,8 +74993,15 @@ async function validateProviderConsentCheckpoint(sidecar, homeDir) {
73965
74993
  return false;
73966
74994
  }
73967
74995
  const runnerId = pairingCheckpoint.runner_id;
73968
- const cell2 = stableClaudeHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
73969
- 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";
73970
75005
  }
73971
75006
  async function validateServiceInstallCheckpoint(sidecar, options, execute2) {
73972
75007
  const checkpoint = sidecar.stages.service_install;
@@ -74072,10 +75107,13 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
74072
75107
  const newState = {
74073
75108
  runner_id: claimResult.runnerId,
74074
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] } : {},
74075
75112
  ...claimResult.tenantId !== null ? { tenant_id: claimResult.tenantId } : {},
74076
75113
  ...claimResult.name !== null ? { name: claimResult.name } : {},
74077
75114
  ...claimResult.serviceKeyPrivate !== void 0 ? { service_key_private: claimResult.serviceKeyPrivate } : {},
74078
- ...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 } : {}
74079
75117
  };
74080
75118
  await saveState(sidecar.state_file, newState);
74081
75119
  await reapSupersededRunnerHomes(priorState, newState.runner_id, homeDir);
@@ -74118,14 +75156,33 @@ async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceK
74118
75156
  }
74119
75157
  }
74120
75158
  async function runProviderConsentStage(sidecar, homeDir, appUrl2, env, io, loginImpl, now, allowLocalCeremony, json2) {
74121
- if (!sidecar.selected_runtimes.includes(runnerSetupRuntimeSchema.enum.claude)) {
74122
- return { kind: "login_failed", exitCode: 1, message: "Codex setup is not yet available." };
74123
- }
74124
75159
  const pairing = sidecar.stages.pairing;
74125
75160
  if (pairing === void 0) {
74126
75161
  return { kind: "login_failed", exitCode: 1, message: "Pairing checkpoint missing before provider consent." };
74127
75162
  }
74128
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
+ }
74129
75186
  const cell2 = stableClaudeHomeDir(state, homeDir);
74130
75187
  if (!await claudeCellNeedsLogin(cell2)) {
74131
75188
  const provenRuntimes = [runnerSetupRuntimeSchema.enum.claude];