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