@rosthq/cli 0.7.163 → 0.7.165
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1191 -134
- 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,37 @@ var runnerSetupValidationAttemptSchema = external_exports.object({
|
|
|
29223
29224
|
(attempt) => attempt.kind === "aicos_acceptance" === (attempt.activation_receipt_id !== null),
|
|
29224
29225
|
"aicos_acceptance must be attributed to an activation receipt"
|
|
29225
29226
|
);
|
|
29227
|
+
var RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS = ["runner_setup.status"];
|
|
29228
|
+
var RUNNER_SETUP_PROBE_CAPABILITY_VERSION = 1;
|
|
29229
|
+
var runnerSetupValidationMcpCapabilityPayloadSchema = external_exports.object({
|
|
29230
|
+
capability_version: external_exports.literal(RUNNER_SETUP_PROBE_CAPABILITY_VERSION),
|
|
29231
|
+
tenant_id: uuid3,
|
|
29232
|
+
runner_id: uuid3,
|
|
29233
|
+
setup_id: uuid3,
|
|
29234
|
+
job_id: uuid3,
|
|
29235
|
+
runtime: runnerSetupRuntimeSchema,
|
|
29236
|
+
service_key_generation: external_exports.number().int().nonnegative(),
|
|
29237
|
+
nonce: external_exports.string().min(16).max(120),
|
|
29238
|
+
expires_at: external_exports.number().int().positive(),
|
|
29239
|
+
allowed_command_ids: external_exports.tuple([external_exports.literal("runner_setup.status")])
|
|
29240
|
+
}).strict();
|
|
29241
|
+
var runnerSetupValidationMcpConfigSchema = external_exports.object({
|
|
29242
|
+
url: external_exports.string().url(),
|
|
29243
|
+
token: external_exports.string().min(1).max(4096),
|
|
29244
|
+
expires_at: isoDateTime
|
|
29245
|
+
}).strict();
|
|
29246
|
+
function runnerSetupProbeNonceProof(input) {
|
|
29247
|
+
const preimage = [
|
|
29248
|
+
"rost.runner-setup.probe.v2",
|
|
29249
|
+
input.nonce,
|
|
29250
|
+
input.setup_id,
|
|
29251
|
+
input.job_id,
|
|
29252
|
+
input.runtime,
|
|
29253
|
+
input.runner_id,
|
|
29254
|
+
String(input.service_key_generation)
|
|
29255
|
+
].join("\n");
|
|
29256
|
+
return createHash("sha256").update(preimage).digest("hex");
|
|
29257
|
+
}
|
|
29226
29258
|
var runnerSetupValidationClaimRequestSchema = external_exports.object({
|
|
29227
29259
|
setup_id: uuid3,
|
|
29228
29260
|
runtime: runnerSetupRuntimeSchema
|
|
@@ -29231,7 +29263,8 @@ var runnerSetupValidationClaimResponseSchema = external_exports.object({
|
|
|
29231
29263
|
job_id: uuid3,
|
|
29232
29264
|
challenge: runnerSetupValidationChallengeSchema,
|
|
29233
29265
|
probe_capability_version: external_exports.number().int().positive(),
|
|
29234
|
-
allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20)
|
|
29266
|
+
allowed_command_ids: external_exports.array(external_exports.string().min(1).max(120)).min(1).max(20),
|
|
29267
|
+
mcp: runnerSetupValidationMcpConfigSchema
|
|
29235
29268
|
}).strict();
|
|
29236
29269
|
var runnerSetupValidationReportRequestSchema = external_exports.object({
|
|
29237
29270
|
job_id: uuid3,
|
|
@@ -30957,7 +30990,7 @@ var forbiddenImportKeys = /* @__PURE__ */ new Set([
|
|
|
30957
30990
|
"integration_id",
|
|
30958
30991
|
"integrationId"
|
|
30959
30992
|
]);
|
|
30960
|
-
function rejectForbiddenImportShape(value, ctx,
|
|
30993
|
+
function rejectForbiddenImportShape(value, ctx, path16 = []) {
|
|
30961
30994
|
if (value === null || value === void 0) {
|
|
30962
30995
|
return;
|
|
30963
30996
|
}
|
|
@@ -30965,21 +30998,21 @@ function rejectForbiddenImportShape(value, ctx, path15 = []) {
|
|
|
30965
30998
|
if (hasSecretShapedValue(value)) {
|
|
30966
30999
|
ctx.addIssue({
|
|
30967
31000
|
code: external_exports.ZodIssueCode.custom,
|
|
30968
|
-
path:
|
|
31001
|
+
path: path16,
|
|
30969
31002
|
message: "Definition imports cannot contain secret-shaped values. Provide credentials later through vault-backed connection flows."
|
|
30970
31003
|
});
|
|
30971
31004
|
}
|
|
30972
31005
|
return;
|
|
30973
31006
|
}
|
|
30974
31007
|
if (Array.isArray(value)) {
|
|
30975
|
-
value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...
|
|
31008
|
+
value.forEach((item, index) => rejectForbiddenImportShape(item, ctx, [...path16, index]));
|
|
30976
31009
|
return;
|
|
30977
31010
|
}
|
|
30978
31011
|
if (typeof value !== "object") {
|
|
30979
31012
|
return;
|
|
30980
31013
|
}
|
|
30981
31014
|
for (const [key, nested] of Object.entries(value)) {
|
|
30982
|
-
const nestedPath = [...
|
|
31015
|
+
const nestedPath = [...path16, key];
|
|
30983
31016
|
if (forbiddenImportKeys.has(key)) {
|
|
30984
31017
|
ctx.addIssue({
|
|
30985
31018
|
code: external_exports.ZodIssueCode.custom,
|
|
@@ -35916,7 +35949,7 @@ var preflightArtifactDispositionSchema = external_exports.object({
|
|
|
35916
35949
|
// on this envelope.
|
|
35917
35950
|
transformations: external_exports.array(preflightTransformationSchema)
|
|
35918
35951
|
}).strict().superRefine((data, ctx) => {
|
|
35919
|
-
const add = (
|
|
35952
|
+
const add = (path16, message) => ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: path16, message });
|
|
35920
35953
|
const reasonIsBlank = data.reason !== null && data.reason.trim().length === 0;
|
|
35921
35954
|
if (reasonIsBlank) add(["reason"], "reason must be nonblank when supplied");
|
|
35922
35955
|
if (data.presence === "missing") {
|
|
@@ -36955,14 +36988,14 @@ var COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES = 5e3;
|
|
|
36955
36988
|
function jsonByteLength(value) {
|
|
36956
36989
|
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
36957
36990
|
}
|
|
36958
|
-
function boundCharterList(value,
|
|
36991
|
+
function boundCharterList(value, path16, ctx) {
|
|
36959
36992
|
if (value.length > COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST) {
|
|
36960
36993
|
ctx.addIssue({
|
|
36961
36994
|
code: external_exports.ZodIssueCode.too_big,
|
|
36962
36995
|
maximum: COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST,
|
|
36963
36996
|
origin: "array",
|
|
36964
36997
|
inclusive: true,
|
|
36965
|
-
path:
|
|
36998
|
+
path: path16,
|
|
36966
36999
|
message: `Complete-Seat Charter lists are limited to ${COMPLETE_SEAT_CHARTER_MAX_ITEMS_PER_LIST} entries.`
|
|
36967
37000
|
});
|
|
36968
37001
|
}
|
|
@@ -36979,7 +37012,7 @@ function addCompleteSeatCharterBounds(charter, ctx) {
|
|
|
36979
37012
|
boundCharterList(charter.unanswered_boundaries, ["unanswered_boundaries"], ctx);
|
|
36980
37013
|
}
|
|
36981
37014
|
let argumentNodes = 0;
|
|
36982
|
-
const visitArgument = (value, depth,
|
|
37015
|
+
const visitArgument = (value, depth, path16) => {
|
|
36983
37016
|
argumentNodes += 1;
|
|
36984
37017
|
if (argumentNodes > COMPLETE_SEAT_PERMISSION_ARGS_MAX_NODES) {
|
|
36985
37018
|
return;
|
|
@@ -36987,15 +37020,15 @@ function addCompleteSeatCharterBounds(charter, ctx) {
|
|
|
36987
37020
|
if (depth > COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH) {
|
|
36988
37021
|
ctx.addIssue({
|
|
36989
37022
|
code: external_exports.ZodIssueCode.custom,
|
|
36990
|
-
path:
|
|
37023
|
+
path: path16,
|
|
36991
37024
|
message: `Complete-Seat permission arguments are limited to ${COMPLETE_SEAT_PERMISSION_ARGS_MAX_DEPTH} nested levels.`
|
|
36992
37025
|
});
|
|
36993
37026
|
return;
|
|
36994
37027
|
}
|
|
36995
37028
|
if (Array.isArray(value)) {
|
|
36996
|
-
value.forEach((entry, index) => visitArgument(entry, depth + 1, [...
|
|
37029
|
+
value.forEach((entry, index) => visitArgument(entry, depth + 1, [...path16, index]));
|
|
36997
37030
|
} else if (value && typeof value === "object") {
|
|
36998
|
-
Object.entries(value).forEach(([key, entry]) => visitArgument(entry, depth + 1, [...
|
|
37031
|
+
Object.entries(value).forEach(([key, entry]) => visitArgument(entry, depth + 1, [...path16, key]));
|
|
36999
37032
|
}
|
|
37000
37033
|
};
|
|
37001
37034
|
charter.permission_manifest.forEach((permission, index) => {
|
|
@@ -37156,9 +37189,9 @@ var createCompleteSeatRequestSchema = external_exports.object({
|
|
|
37156
37189
|
}
|
|
37157
37190
|
const useKeys = /* @__PURE__ */ new Set();
|
|
37158
37191
|
for (const [index, use] of value.seat.source_uses.entries()) {
|
|
37159
|
-
const
|
|
37160
|
-
if (!sourceKeys.has(use.source_key)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path:
|
|
37161
|
-
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." });
|
|
37162
37195
|
useKeys.add(use.source_key);
|
|
37163
37196
|
}
|
|
37164
37197
|
if (value.seat.human_staffing.kind === "planned" && value.seat.human_staffing.source_ref) {
|
|
@@ -37171,8 +37204,8 @@ var createCompleteSeatRequestSchema = external_exports.object({
|
|
|
37171
37204
|
...value.seat.parent ? [{ ref: value.seat.parent, path: ["seat", "parent"] }] : [],
|
|
37172
37205
|
...value.seat.agent ? [{ ref: value.seat.agent.steward, path: ["seat", "agent", "steward"] }] : []
|
|
37173
37206
|
];
|
|
37174
|
-
refs.forEach(({ ref, path:
|
|
37175
|
-
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." });
|
|
37176
37209
|
});
|
|
37177
37210
|
}).transform((value) => ({
|
|
37178
37211
|
...value,
|
|
@@ -37250,7 +37283,7 @@ var completeSeatApprovalProjectionSchema = external_exports.object({
|
|
|
37250
37283
|
|
|
37251
37284
|
// ../../packages/protocol/src/setup-application.ts
|
|
37252
37285
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
37253
|
-
import { createHash } from "node:crypto";
|
|
37286
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
37254
37287
|
|
|
37255
37288
|
// ../../packages/protocol/src/baserow-filter-operators.ts
|
|
37256
37289
|
var baserowFilterOperatorSchema = external_exports.enum([
|
|
@@ -37668,26 +37701,26 @@ var PROHIBITED_CONTENT_MESSAGE_PREFIX = "Prohibited content";
|
|
|
37668
37701
|
function prohibitedContentMessage(rule_id, found) {
|
|
37669
37702
|
return `${PROHIBITED_CONTENT_MESSAGE_PREFIX} [${rule_id}]: ${found}. ${CONTENT_LINT_REMEDY[rule_id]}`;
|
|
37670
37703
|
}
|
|
37671
|
-
function isIndexedPath(
|
|
37672
|
-
return
|
|
37704
|
+
function isIndexedPath(path16, family, field4) {
|
|
37705
|
+
return path16.length === 3 && path16[0] === family && typeof path16[1] === "number" && path16[2] === field4;
|
|
37673
37706
|
}
|
|
37674
|
-
function isSchemaProvenMachineString(
|
|
37675
|
-
if (
|
|
37707
|
+
function isSchemaProvenMachineString(path16) {
|
|
37708
|
+
if (path16.length === 1 && path16[0] === "managed_inference_hard_cap_usd") {
|
|
37676
37709
|
return true;
|
|
37677
37710
|
}
|
|
37678
|
-
if (isIndexedPath(
|
|
37711
|
+
if (isIndexedPath(path16, "sources", "source_upload_id") || isIndexedPath(path16, "sources", "sha256") || isIndexedPath(path16, "reconciliation", "source_sha256") || isIndexedPath(path16, "reconciliation", "normalized_identity_digest") || isIndexedPath(path16, "signal_readings", "period_start") || isIndexedPath(path16, "signal_readings", "value") || isIndexedPath(path16, "tasks", "due_on")) {
|
|
37679
37712
|
return true;
|
|
37680
37713
|
}
|
|
37681
|
-
if (
|
|
37714
|
+
if (path16.length === 2 && path16[0] === "cycle" && (path16[1] === "starts_on" || path16[1] === "ends_on")) {
|
|
37682
37715
|
return true;
|
|
37683
37716
|
}
|
|
37684
|
-
if (
|
|
37717
|
+
if (path16.length >= 3 && path16.at(-3) === "citations" && typeof path16.at(-2) === "number" && path16.at(-1) === "document_id") {
|
|
37685
37718
|
return true;
|
|
37686
37719
|
}
|
|
37687
|
-
if (
|
|
37720
|
+
if (path16.length === 6 && path16[0] === "seats" && typeof path16[1] === "number" && path16[2] === "charter" && path16[3] === "permission_manifest" && typeof path16[4] === "number" && path16[5] === "integration_id") {
|
|
37688
37721
|
return true;
|
|
37689
37722
|
}
|
|
37690
|
-
return
|
|
37723
|
+
return path16.length === 6 && path16[0] === "seats" && typeof path16[1] === "number" && path16[2] === "agent" && path16[3] === "skills" && typeof path16[4] === "number" && (path16[5] === "skill_version_id" || path16[5] === "content_sha256");
|
|
37691
37724
|
}
|
|
37692
37725
|
function jsonByteLength2(value) {
|
|
37693
37726
|
try {
|
|
@@ -37697,20 +37730,20 @@ function jsonByteLength2(value) {
|
|
|
37697
37730
|
return Number.POSITIVE_INFINITY;
|
|
37698
37731
|
}
|
|
37699
37732
|
}
|
|
37700
|
-
function prohibitedSetupContentViolations(value,
|
|
37733
|
+
function prohibitedSetupContentViolations(value, path16 = []) {
|
|
37701
37734
|
if (typeof value === "string") {
|
|
37702
|
-
if (isSchemaProvenMachineString(
|
|
37735
|
+
if (isSchemaProvenMachineString(path16)) return [];
|
|
37703
37736
|
return contentViolationsForString(value).map(({ rule_id, found }) => ({
|
|
37704
37737
|
rule_id,
|
|
37705
|
-
path:
|
|
37738
|
+
path: path16,
|
|
37706
37739
|
message: prohibitedContentMessage(rule_id, found)
|
|
37707
37740
|
}));
|
|
37708
37741
|
}
|
|
37709
37742
|
if (Array.isArray(value)) {
|
|
37710
|
-
return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...
|
|
37743
|
+
return value.flatMap((entry, index) => prohibitedSetupContentViolations(entry, [...path16, index]));
|
|
37711
37744
|
}
|
|
37712
37745
|
if (value && typeof value === "object") {
|
|
37713
|
-
return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...
|
|
37746
|
+
return Object.entries(value).flatMap(([key, entry]) => prohibitedSetupContentViolations(entry, [...path16, key]));
|
|
37714
37747
|
}
|
|
37715
37748
|
return [];
|
|
37716
37749
|
}
|
|
@@ -38027,7 +38060,7 @@ function canonicalJson(value) {
|
|
|
38027
38060
|
return JSON.stringify(value);
|
|
38028
38061
|
}
|
|
38029
38062
|
function digestCanonicalJson(value) {
|
|
38030
|
-
return `sha256:${
|
|
38063
|
+
return `sha256:${createHash2("sha256").update(canonicalJson(value)).digest("hex")}`;
|
|
38031
38064
|
}
|
|
38032
38065
|
var onboardingSetupOutputSchema = external_exports.object({
|
|
38033
38066
|
application_id: uuidSchema19,
|
|
@@ -43690,7 +43723,7 @@ var CommandClient = class {
|
|
|
43690
43723
|
this._credentialKind = options.credentialKind;
|
|
43691
43724
|
}
|
|
43692
43725
|
async execute(commandId, body = {}, options = {}) {
|
|
43693
|
-
const
|
|
43726
|
+
const path16 = `/api/commands/${encodeURIComponent(commandId)}`;
|
|
43694
43727
|
const headers = {
|
|
43695
43728
|
authorization: `Bearer ${this.token}`,
|
|
43696
43729
|
"content-type": "application/json"
|
|
@@ -43698,7 +43731,7 @@ var CommandClient = class {
|
|
|
43698
43731
|
if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
|
|
43699
43732
|
headers["x-rost-seat"] = options.targetSeatId;
|
|
43700
43733
|
}
|
|
43701
|
-
const response = await this.fetchImpl(`${this.appUrl}${
|
|
43734
|
+
const response = await this.fetchImpl(`${this.appUrl}${path16}`, {
|
|
43702
43735
|
method: "POST",
|
|
43703
43736
|
headers,
|
|
43704
43737
|
body: JSON.stringify(body)
|
|
@@ -43711,7 +43744,7 @@ var CommandClient = class {
|
|
|
43711
43744
|
const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
|
|
43712
43745
|
throw new CommandClientError(
|
|
43713
43746
|
response.status,
|
|
43714
|
-
`server returned HTTP ${response.status} (non-JSON) from ${
|
|
43747
|
+
`server returned HTTP ${response.status} (non-JSON) from ${path16}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
|
|
43715
43748
|
);
|
|
43716
43749
|
}
|
|
43717
43750
|
const parsed = commandResponseSchema.parse(raw);
|
|
@@ -49042,8 +49075,8 @@ var IcebergError = class extends Error {
|
|
|
49042
49075
|
return this.status === 419;
|
|
49043
49076
|
}
|
|
49044
49077
|
};
|
|
49045
|
-
function buildUrl(baseUrl,
|
|
49046
|
-
const url2 = new URL(
|
|
49078
|
+
function buildUrl(baseUrl, path16, query) {
|
|
49079
|
+
const url2 = new URL(path16, baseUrl);
|
|
49047
49080
|
if (query) {
|
|
49048
49081
|
for (const [key, value] of Object.entries(query)) {
|
|
49049
49082
|
if (value !== void 0) {
|
|
@@ -49073,12 +49106,12 @@ function createFetchClient(options) {
|
|
|
49073
49106
|
return {
|
|
49074
49107
|
async request({
|
|
49075
49108
|
method,
|
|
49076
|
-
path:
|
|
49109
|
+
path: path16,
|
|
49077
49110
|
query,
|
|
49078
49111
|
body,
|
|
49079
49112
|
headers
|
|
49080
49113
|
}) {
|
|
49081
|
-
const url2 = buildUrl(options.baseUrl,
|
|
49114
|
+
const url2 = buildUrl(options.baseUrl, path16, query);
|
|
49082
49115
|
const authHeaders = await buildAuthHeaders(options.auth);
|
|
49083
49116
|
const res = await fetchFn(url2, {
|
|
49084
49117
|
method,
|
|
@@ -49940,7 +49973,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
49940
49973
|
* @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
|
|
49941
49974
|
* @param fileBody The body of the file to be stored in the bucket.
|
|
49942
49975
|
*/
|
|
49943
|
-
async uploadOrUpdate(method,
|
|
49976
|
+
async uploadOrUpdate(method, path16, fileBody, fileOptions) {
|
|
49944
49977
|
var _this = this;
|
|
49945
49978
|
return _this.handleOperation(async () => {
|
|
49946
49979
|
let body;
|
|
@@ -49964,7 +49997,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
49964
49997
|
if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
|
|
49965
49998
|
}
|
|
49966
49999
|
if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
|
|
49967
|
-
const cleanPath = _this._removeEmptyFolders(
|
|
50000
|
+
const cleanPath = _this._removeEmptyFolders(path16);
|
|
49968
50001
|
const _path = _this._getFinalPath(cleanPath);
|
|
49969
50002
|
const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
|
|
49970
50003
|
return {
|
|
@@ -50041,8 +50074,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50041
50074
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50042
50075
|
* - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
|
|
50043
50076
|
*/
|
|
50044
|
-
async upload(
|
|
50045
|
-
return this.uploadOrUpdate("POST",
|
|
50077
|
+
async upload(path16, fileBody, fileOptions) {
|
|
50078
|
+
return this.uploadOrUpdate("POST", path16, fileBody, fileOptions);
|
|
50046
50079
|
}
|
|
50047
50080
|
/**
|
|
50048
50081
|
* Upload a file with a token generated from `createSignedUploadUrl`.
|
|
@@ -50082,9 +50115,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50082
50115
|
* - `objects` table permissions: none
|
|
50083
50116
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50084
50117
|
*/
|
|
50085
|
-
async uploadToSignedUrl(
|
|
50118
|
+
async uploadToSignedUrl(path16, token, fileBody, fileOptions) {
|
|
50086
50119
|
var _this3 = this;
|
|
50087
|
-
const cleanPath = _this3._removeEmptyFolders(
|
|
50120
|
+
const cleanPath = _this3._removeEmptyFolders(path16);
|
|
50088
50121
|
const _path = _this3._getFinalPath(cleanPath);
|
|
50089
50122
|
const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
|
|
50090
50123
|
url2.searchParams.set("token", token);
|
|
@@ -50153,10 +50186,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50153
50186
|
* - `objects` table permissions: `insert`
|
|
50154
50187
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50155
50188
|
*/
|
|
50156
|
-
async createSignedUploadUrl(
|
|
50189
|
+
async createSignedUploadUrl(path16, options) {
|
|
50157
50190
|
var _this4 = this;
|
|
50158
50191
|
return _this4.handleOperation(async () => {
|
|
50159
|
-
let _path = _this4._getFinalPath(
|
|
50192
|
+
let _path = _this4._getFinalPath(path16);
|
|
50160
50193
|
const headers = _objectSpread22({}, _this4.headers);
|
|
50161
50194
|
if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
|
|
50162
50195
|
const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
|
|
@@ -50165,7 +50198,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50165
50198
|
if (!token) throw new StorageError("No token returned by API");
|
|
50166
50199
|
return {
|
|
50167
50200
|
signedUrl: url2.toString(),
|
|
50168
|
-
path:
|
|
50201
|
+
path: path16,
|
|
50169
50202
|
token
|
|
50170
50203
|
};
|
|
50171
50204
|
});
|
|
@@ -50225,8 +50258,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50225
50258
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50226
50259
|
* - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
|
|
50227
50260
|
*/
|
|
50228
|
-
async update(
|
|
50229
|
-
return this.uploadOrUpdate("PUT",
|
|
50261
|
+
async update(path16, fileBody, fileOptions) {
|
|
50262
|
+
return this.uploadOrUpdate("PUT", path16, fileBody, fileOptions);
|
|
50230
50263
|
}
|
|
50231
50264
|
/**
|
|
50232
50265
|
* Moves an existing file to a new path in the same bucket.
|
|
@@ -50377,10 +50410,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50377
50410
|
* - `objects` table permissions: `select`
|
|
50378
50411
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50379
50412
|
*/
|
|
50380
|
-
async createSignedUrl(
|
|
50413
|
+
async createSignedUrl(path16, expiresIn, options) {
|
|
50381
50414
|
var _this8 = this;
|
|
50382
50415
|
return _this8.handleOperation(async () => {
|
|
50383
|
-
let _path = _this8._getFinalPath(
|
|
50416
|
+
let _path = _this8._getFinalPath(path16);
|
|
50384
50417
|
const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
|
|
50385
50418
|
let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
|
|
50386
50419
|
const query = new URLSearchParams();
|
|
@@ -50516,13 +50549,13 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50516
50549
|
* - `objects` table permissions: `select`
|
|
50517
50550
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50518
50551
|
*/
|
|
50519
|
-
download(
|
|
50552
|
+
download(path16, options, parameters) {
|
|
50520
50553
|
const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0 ? "render/image/authenticated" : "object";
|
|
50521
50554
|
const query = new URLSearchParams();
|
|
50522
50555
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
50523
50556
|
if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
|
|
50524
50557
|
const queryString = query.toString();
|
|
50525
|
-
const _path = this._getFinalPath(
|
|
50558
|
+
const _path = this._getFinalPath(path16);
|
|
50526
50559
|
const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
|
|
50527
50560
|
headers: this.headers,
|
|
50528
50561
|
noResolveJson: true
|
|
@@ -50553,9 +50586,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50553
50586
|
* }
|
|
50554
50587
|
* ```
|
|
50555
50588
|
*/
|
|
50556
|
-
async info(
|
|
50589
|
+
async info(path16) {
|
|
50557
50590
|
var _this10 = this;
|
|
50558
|
-
const _path = _this10._getFinalPath(
|
|
50591
|
+
const _path = _this10._getFinalPath(path16);
|
|
50559
50592
|
return _this10.handleOperation(async () => {
|
|
50560
50593
|
return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
|
|
50561
50594
|
});
|
|
@@ -50576,9 +50609,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50576
50609
|
* .exists('folder/avatar1.png')
|
|
50577
50610
|
* ```
|
|
50578
50611
|
*/
|
|
50579
|
-
async exists(
|
|
50612
|
+
async exists(path16) {
|
|
50580
50613
|
var _this11 = this;
|
|
50581
|
-
const _path = _this11._getFinalPath(
|
|
50614
|
+
const _path = _this11._getFinalPath(path16);
|
|
50582
50615
|
try {
|
|
50583
50616
|
await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
|
|
50584
50617
|
return {
|
|
@@ -50657,8 +50690,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50657
50690
|
* - `objects` table permissions: none
|
|
50658
50691
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50659
50692
|
*/
|
|
50660
|
-
getPublicUrl(
|
|
50661
|
-
const _path = this._getFinalPath(
|
|
50693
|
+
getPublicUrl(path16, options) {
|
|
50694
|
+
const _path = this._getFinalPath(path16);
|
|
50662
50695
|
const query = new URLSearchParams();
|
|
50663
50696
|
if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
|
|
50664
50697
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
@@ -50797,10 +50830,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50797
50830
|
* - `objects` table permissions: `select`
|
|
50798
50831
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
50799
50832
|
*/
|
|
50800
|
-
async list(
|
|
50833
|
+
async list(path16, options, parameters) {
|
|
50801
50834
|
var _this13 = this;
|
|
50802
50835
|
return _this13.handleOperation(async () => {
|
|
50803
|
-
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix:
|
|
50836
|
+
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path16 || "" });
|
|
50804
50837
|
return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
|
|
50805
50838
|
});
|
|
50806
50839
|
}
|
|
@@ -50865,11 +50898,11 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
50865
50898
|
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
50866
50899
|
return btoa(data);
|
|
50867
50900
|
}
|
|
50868
|
-
_getFinalPath(
|
|
50869
|
-
return `${this.bucketId}/${
|
|
50901
|
+
_getFinalPath(path16) {
|
|
50902
|
+
return `${this.bucketId}/${path16.replace(/^\/+/, "")}`;
|
|
50870
50903
|
}
|
|
50871
|
-
_removeEmptyFolders(
|
|
50872
|
-
return
|
|
50904
|
+
_removeEmptyFolders(path16) {
|
|
50905
|
+
return path16.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
|
|
50873
50906
|
}
|
|
50874
50907
|
/** Modifies the `query`, appending values the from `transform` */
|
|
50875
50908
|
applyTransformOptsToQuery(query, transform2) {
|
|
@@ -55104,7 +55137,7 @@ async function recordImplementationRunClosed(store, status, fields, closedAt = /
|
|
|
55104
55137
|
}
|
|
55105
55138
|
|
|
55106
55139
|
// src/skill-trust.ts
|
|
55107
|
-
import { createHash as
|
|
55140
|
+
import { createHash as createHash3, createPublicKey, verify } from "node:crypto";
|
|
55108
55141
|
var TRUSTED_SIGNER_PUBLIC_KEY = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOXSsKe/zFjaoAdZdvP/esPoeM1fsCGFmxv0INDlCxaW rost-implementation-skill-release";
|
|
55109
55142
|
var TRUSTED_SIGNER_FINGERPRINT = "SHA256:ziSyU9vhGi0Q3GaeePB1R6BI3GqXutLhepnF/4Hx4rY";
|
|
55110
55143
|
var RELEASE_REPOSITORY = {
|
|
@@ -55152,7 +55185,7 @@ var skillManifestSchema = external_exports.object({
|
|
|
55152
55185
|
).min(1)
|
|
55153
55186
|
}).strict();
|
|
55154
55187
|
function sha256Hex2(content) {
|
|
55155
|
-
return
|
|
55188
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
55156
55189
|
}
|
|
55157
55190
|
function readSshString(buffer, offset) {
|
|
55158
55191
|
if (offset + 4 > buffer.length) {
|
|
@@ -55223,7 +55256,7 @@ function splitSignedTag(rawTagObject) {
|
|
|
55223
55256
|
};
|
|
55224
55257
|
}
|
|
55225
55258
|
function buildSshSigMessage(payload) {
|
|
55226
|
-
const digest =
|
|
55259
|
+
const digest = createHash3("sha512").update(payload, "utf8").digest();
|
|
55227
55260
|
return Buffer.concat([
|
|
55228
55261
|
Buffer.from("SSHSIG"),
|
|
55229
55262
|
makeSshString(Buffer.from("git")),
|
|
@@ -68138,7 +68171,7 @@ import path13 from "node:path";
|
|
|
68138
68171
|
|
|
68139
68172
|
// src/forge-workspace.ts
|
|
68140
68173
|
import { execFile as execFileCallback4 } from "node:child_process";
|
|
68141
|
-
import { createHash as
|
|
68174
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
68142
68175
|
import { constants as fsConstants5 } from "node:fs";
|
|
68143
68176
|
import { chmod, link, lstat, mkdir as mkdir7, mkdtemp, open, readdir as readdir3, readFile as readFile10, realpath, rename, rm as rm7, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
|
|
68144
68177
|
import os2 from "node:os";
|
|
@@ -68399,7 +68432,7 @@ async function hasUsableClaudeCredentialFile(cell2) {
|
|
|
68399
68432
|
return parseUsableClaudeCredentialJson(read.data.toString("utf8")) !== null;
|
|
68400
68433
|
}
|
|
68401
68434
|
function claudeKeychainServiceNameForSecureStorageDir(secureStorageDir) {
|
|
68402
|
-
const digest =
|
|
68435
|
+
const digest = createHash4("sha256").update(secureStorageDir).digest("hex").slice(0, 8);
|
|
68403
68436
|
return `Claude Code-credentials-${digest}`;
|
|
68404
68437
|
}
|
|
68405
68438
|
function parseUsableClaudeCredentialJson(raw) {
|
|
@@ -68934,7 +68967,7 @@ var FORGE_GIT_AUTHOR_EMAIL = `${RSF_ADDON_BRAND.cliGroup}@${BRAND_DISTRIBUTION.c
|
|
|
68934
68967
|
function shortRequestId(buildRequestId) {
|
|
68935
68968
|
const hex3 = buildRequestId.replace(/-/g, "");
|
|
68936
68969
|
const slice = hex3.slice(0, 8);
|
|
68937
|
-
return slice.length > 0 ? slice :
|
|
68970
|
+
return slice.length > 0 ? slice : createHash4("sha1").update(buildRequestId).digest("hex").slice(0, 8);
|
|
68938
68971
|
}
|
|
68939
68972
|
function requestRoot(baseDir, buildRequestId) {
|
|
68940
68973
|
return path10.join(baseDir, "work", shortRequestId(buildRequestId));
|
|
@@ -69622,7 +69655,7 @@ async function removeWorkspace(ws) {
|
|
|
69622
69655
|
|
|
69623
69656
|
// src/runner-serve.ts
|
|
69624
69657
|
import { execFile as execFileCallback5, spawn } from "node:child_process";
|
|
69625
|
-
import { createHash as
|
|
69658
|
+
import { createHash as createHash6, createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey2, generateKeyPairSync, randomUUID } from "node:crypto";
|
|
69626
69659
|
import { constants as fsConstants6 } from "node:fs";
|
|
69627
69660
|
import { access as access3, chmod as chmod2, lstat as lstat2, mkdir as mkdir8, open as open2, readFile as readFile11, rename as rename2, rm as rm8, statfs, writeFile as writeFile8 } from "node:fs/promises";
|
|
69628
69661
|
import { cpus, homedir as homedir7, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
|
|
@@ -69866,6 +69899,131 @@ function decideReconcileAction(input) {
|
|
|
69866
69899
|
};
|
|
69867
69900
|
}
|
|
69868
69901
|
|
|
69902
|
+
// src/runner-setup-validation.ts
|
|
69903
|
+
import { createHash as createHash5, createPrivateKey, sign as cryptoSign } from "node:crypto";
|
|
69904
|
+
var CLAIM_PATH = "/api/runner/setup-validation/claim";
|
|
69905
|
+
var RESULT_PATH = "/api/runner/setup-validation/result";
|
|
69906
|
+
var SERVICE_KEY_DOMAIN_SEPARATOR = "rost.runner-setup.service-key.v1";
|
|
69907
|
+
var SIGNATURE_HEADER = "x-rost-service-key-signature";
|
|
69908
|
+
var TIMESTAMP_HEADER = "x-rost-service-key-timestamp";
|
|
69909
|
+
function canonicalRequest(method, requestPath, timestamp, body) {
|
|
69910
|
+
return [
|
|
69911
|
+
SERVICE_KEY_DOMAIN_SEPARATOR,
|
|
69912
|
+
method.toUpperCase(),
|
|
69913
|
+
requestPath,
|
|
69914
|
+
timestamp,
|
|
69915
|
+
createHash5("sha256").update(body, "utf8").digest("hex")
|
|
69916
|
+
].join("\n");
|
|
69917
|
+
}
|
|
69918
|
+
async function signedPost(deps, requestPath, body) {
|
|
69919
|
+
if (deps.state.service_key_private === void 0) {
|
|
69920
|
+
throw new Error("Runner setup validation requires a bound service key.");
|
|
69921
|
+
}
|
|
69922
|
+
const rawBody = JSON.stringify(body);
|
|
69923
|
+
const timestamp = String((deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime());
|
|
69924
|
+
const signature = cryptoSign(
|
|
69925
|
+
null,
|
|
69926
|
+
Buffer.from(canonicalRequest("POST", requestPath, timestamp, rawBody), "utf8"),
|
|
69927
|
+
createPrivateKey(deps.state.service_key_private)
|
|
69928
|
+
).toString("base64");
|
|
69929
|
+
return deps.fetchImpl(`${deps.appUrl.replace(/\/+$/, "")}${requestPath}`, {
|
|
69930
|
+
method: "POST",
|
|
69931
|
+
headers: {
|
|
69932
|
+
authorization: `Bearer ${deps.state.runner_secret}`,
|
|
69933
|
+
"content-type": "application/json",
|
|
69934
|
+
[SIGNATURE_HEADER]: signature,
|
|
69935
|
+
[TIMESTAMP_HEADER]: timestamp
|
|
69936
|
+
},
|
|
69937
|
+
body: rawBody
|
|
69938
|
+
});
|
|
69939
|
+
}
|
|
69940
|
+
async function responseMessage(response) {
|
|
69941
|
+
const text = redactForLog(await response.text());
|
|
69942
|
+
return `Runner setup validation request failed (${response.status})${text.length > 0 ? `: ${text}` : "."}`;
|
|
69943
|
+
}
|
|
69944
|
+
async function executeRunnerSetupValidation(deps) {
|
|
69945
|
+
const setupId = deps.state.active_setup_id;
|
|
69946
|
+
if (setupId === void 0 || deps.state.service_key_private === void 0 || deps.state.service_key_generation === void 0) {
|
|
69947
|
+
return { kind: "not_applicable" };
|
|
69948
|
+
}
|
|
69949
|
+
let claimResponse;
|
|
69950
|
+
try {
|
|
69951
|
+
claimResponse = await signedPost(deps, CLAIM_PATH, { setup_id: setupId, runtime: deps.runtime });
|
|
69952
|
+
} catch (error51) {
|
|
69953
|
+
return { kind: "retryable", message: redactForLog(error51 instanceof Error ? error51.message : String(error51)) };
|
|
69954
|
+
}
|
|
69955
|
+
if (!claimResponse.ok) {
|
|
69956
|
+
return { kind: "retryable", message: await responseMessage(claimResponse) };
|
|
69957
|
+
}
|
|
69958
|
+
const claimParsed = runnerSetupValidationClaimResponseSchema.safeParse(await claimResponse.json().catch(() => null));
|
|
69959
|
+
if (!claimParsed.success) {
|
|
69960
|
+
return { kind: "retryable", message: "Runner setup validation claim returned an unexpected payload." };
|
|
69961
|
+
}
|
|
69962
|
+
const claim = claimParsed.data;
|
|
69963
|
+
if (claim.challenge.setup_id !== setupId || claim.challenge.runtime !== deps.runtime) {
|
|
69964
|
+
return { kind: "retryable", message: "Runner setup validation claim did not match the selected setup and runtime." };
|
|
69965
|
+
}
|
|
69966
|
+
if (new Date(claim.challenge.expires_at).getTime() <= (deps.now ?? (() => /* @__PURE__ */ new Date()))().getTime()) {
|
|
69967
|
+
return { kind: "retryable", message: "Runner setup validation claim expired before the probe started." };
|
|
69968
|
+
}
|
|
69969
|
+
if (claim.allowed_command_ids.length !== 1 || claim.allowed_command_ids[0] !== RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS[0]) {
|
|
69970
|
+
return { kind: "retryable", message: "Runner setup validation claim granted an unexpected command set." };
|
|
69971
|
+
}
|
|
69972
|
+
const probe2 = await deps.runProbe({ runtime: deps.runtime, setupId, mcp: claim.mcp });
|
|
69973
|
+
if (probe2.retryable === true) {
|
|
69974
|
+
return { kind: "retryable", message: "Runner credential cell is busy; validation will retry." };
|
|
69975
|
+
}
|
|
69976
|
+
const cliVersion = await readCliVersion();
|
|
69977
|
+
const mcpConfigDigest = createHash5("sha256").update(JSON.stringify({ url: claim.mcp.url, expires_at: claim.mcp.expires_at, allowed_command_ids: claim.allowed_command_ids })).digest("hex");
|
|
69978
|
+
const proof = probe2.ok && probe2.setupId === setupId ? runnerSetupProbeNonceProof({
|
|
69979
|
+
nonce: claim.challenge.nonce,
|
|
69980
|
+
setup_id: setupId,
|
|
69981
|
+
job_id: claim.job_id,
|
|
69982
|
+
runtime: deps.runtime,
|
|
69983
|
+
runner_id: deps.state.runner_id,
|
|
69984
|
+
service_key_generation: deps.state.service_key_generation
|
|
69985
|
+
}) : "0".repeat(64);
|
|
69986
|
+
const reportBody = {
|
|
69987
|
+
job_id: claim.job_id,
|
|
69988
|
+
nonce: claim.challenge.nonce,
|
|
69989
|
+
result: {
|
|
69990
|
+
setup_id: setupId,
|
|
69991
|
+
runtime: deps.runtime,
|
|
69992
|
+
nonce_proof: proof,
|
|
69993
|
+
service_key_generation: deps.state.service_key_generation
|
|
69994
|
+
},
|
|
69995
|
+
attempt: {
|
|
69996
|
+
runner_id: deps.state.runner_id,
|
|
69997
|
+
runtime: deps.runtime,
|
|
69998
|
+
model: null,
|
|
69999
|
+
provider_cli_version: probe2.providerCliVersion,
|
|
70000
|
+
observed_at: probe2.observedAt
|
|
70001
|
+
},
|
|
70002
|
+
invoked_command_ids: [RUNNER_SETUP_PROBE_ALLOWED_COMMAND_IDS[0]],
|
|
70003
|
+
receipt_facts: {
|
|
70004
|
+
credential_cell_generation: 1,
|
|
70005
|
+
provider_cli_version: probe2.providerCliVersion ?? "unknown",
|
|
70006
|
+
rost_cli_version: cliVersion ?? "unknown",
|
|
70007
|
+
sandbox_policy_version: probe2.sandboxPolicyVersion,
|
|
70008
|
+
mcp_config_digest: mcpConfigDigest
|
|
70009
|
+
}
|
|
70010
|
+
};
|
|
70011
|
+
let resultResponse;
|
|
70012
|
+
try {
|
|
70013
|
+
resultResponse = await signedPost(deps, RESULT_PATH, reportBody);
|
|
70014
|
+
} catch (error51) {
|
|
70015
|
+
return { kind: "retryable", message: redactForLog(error51 instanceof Error ? error51.message : String(error51)) };
|
|
70016
|
+
}
|
|
70017
|
+
if (!resultResponse.ok) {
|
|
70018
|
+
return { kind: "retryable", message: await responseMessage(resultResponse) };
|
|
70019
|
+
}
|
|
70020
|
+
const resultParsed = runnerSetupValidationReportResponseSchema.safeParse(await resultResponse.json().catch(() => null));
|
|
70021
|
+
if (!resultParsed.success || resultParsed.data.verdict !== "passed" || resultParsed.data.receipt === null) {
|
|
70022
|
+
return { kind: "retryable", message: "Runner setup validation did not pass server verification." };
|
|
70023
|
+
}
|
|
70024
|
+
return { kind: "passed", expiresAt: resultParsed.data.receipt.expires_at };
|
|
70025
|
+
}
|
|
70026
|
+
|
|
69869
70027
|
// src/runner-serve.ts
|
|
69870
70028
|
var execFile5 = promisify5(execFileCallback5);
|
|
69871
70029
|
var EXCLUSIVE_NOFOLLOW_FLAGS = fsConstants6.O_CREAT | fsConstants6.O_EXCL | fsConstants6.O_WRONLY | fsConstants6.O_NOFOLLOW;
|
|
@@ -69984,6 +70142,43 @@ ${usage()}
|
|
|
69984
70142
|
serverClaimContractVersion = learnedVersion !== null && learnedVersion >= HEARTBEAT_CLAIM_MIN_CONTRACT_VERSION ? learnedVersion : null;
|
|
69985
70143
|
options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
|
|
69986
70144
|
`);
|
|
70145
|
+
const setupRuntime = state.setup_runtime;
|
|
70146
|
+
const persistedValidation = state.setup_validation;
|
|
70147
|
+
const validationStillCurrent = persistedValidation !== void 0 && persistedValidation.setup_id === state.active_setup_id && persistedValidation.runtime === setupRuntime && new Date(persistedValidation.expires_at).getTime() > Date.now();
|
|
70148
|
+
if (setupRuntime !== void 0 && !validationStillCurrent) {
|
|
70149
|
+
const runtimeCapability = detectedCapabilities[setupRuntime];
|
|
70150
|
+
const validation = await executeRunnerSetupValidation({
|
|
70151
|
+
fetchImpl,
|
|
70152
|
+
appUrl: options.appUrl,
|
|
70153
|
+
runtime: setupRuntime,
|
|
70154
|
+
state,
|
|
70155
|
+
runProbe: ({ runtime, setupId, mcp }) => runSetupValidationProbe({
|
|
70156
|
+
runtime,
|
|
70157
|
+
setupId,
|
|
70158
|
+
mcp,
|
|
70159
|
+
state,
|
|
70160
|
+
config: config2,
|
|
70161
|
+
env,
|
|
70162
|
+
providerCliVersion: runtimeCapability.version
|
|
70163
|
+
})
|
|
70164
|
+
});
|
|
70165
|
+
if (validation.kind === "passed" && state.active_setup_id !== void 0) {
|
|
70166
|
+
state = {
|
|
70167
|
+
...state,
|
|
70168
|
+
setup_validation: {
|
|
70169
|
+
setup_id: state.active_setup_id,
|
|
70170
|
+
runtime: setupRuntime,
|
|
70171
|
+
expires_at: validation.expiresAt
|
|
70172
|
+
}
|
|
70173
|
+
};
|
|
70174
|
+
await saveState(config2.stateFile, state);
|
|
70175
|
+
options.io.stdout.write(`runner setup validation passed runtime=${setupRuntime}
|
|
70176
|
+
`);
|
|
70177
|
+
} else if (validation.kind === "retryable") {
|
|
70178
|
+
options.io.stderr.write(`${redactForLog(validation.message)}
|
|
70179
|
+
`);
|
|
70180
|
+
}
|
|
70181
|
+
}
|
|
69987
70182
|
}
|
|
69988
70183
|
if (localRuntime) {
|
|
69989
70184
|
activeSessions += 1;
|
|
@@ -70405,7 +70600,7 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
|
|
|
70405
70600
|
return state;
|
|
70406
70601
|
}
|
|
70407
70602
|
async function migrateLegacyCodexHome(state, homeDir, io) {
|
|
70408
|
-
const runnerKey =
|
|
70603
|
+
const runnerKey = createHash6("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
|
|
70409
70604
|
const newHome = stableCodexHomeDir(state, homeDir);
|
|
70410
70605
|
const legacyCandidates = [
|
|
70411
70606
|
path12.join(tmpdir2(), "rost-runner-codex-home", `rost-runner-codex-home-${runnerKey}`),
|
|
@@ -70465,7 +70660,7 @@ async function migrateOneLegacyCodexHome(legacyHome, newHome, io) {
|
|
|
70465
70660
|
}
|
|
70466
70661
|
}
|
|
70467
70662
|
function defaultRunnerStateFile(appUrl2) {
|
|
70468
|
-
return path12.join(tmpdir2(), `rost-runner-${
|
|
70663
|
+
return path12.join(tmpdir2(), `rost-runner-${createHash6("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
|
|
70469
70664
|
}
|
|
70470
70665
|
async function loadState(filePath, expectedTenantId) {
|
|
70471
70666
|
try {
|
|
@@ -70479,8 +70674,12 @@ async function loadState(filePath, expectedTenantId) {
|
|
|
70479
70674
|
runner_secret: parsed.runner_secret,
|
|
70480
70675
|
...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
|
|
70481
70676
|
...typeof parsed.name === "string" ? { name: parsed.name } : {},
|
|
70677
|
+
...typeof parsed.active_setup_id === "string" ? { active_setup_id: parsed.active_setup_id } : {},
|
|
70678
|
+
...parsed.setup_runtime === "claude" || parsed.setup_runtime === "codex" ? { setup_runtime: parsed.setup_runtime } : {},
|
|
70482
70679
|
...typeof parsed.service_key_private === "string" ? { service_key_private: parsed.service_key_private } : {},
|
|
70483
70680
|
...typeof parsed.service_key_public === "string" ? { service_key_public: parsed.service_key_public } : {},
|
|
70681
|
+
...typeof parsed.service_key_generation === "number" ? { service_key_generation: parsed.service_key_generation } : {},
|
|
70682
|
+
...isSetupValidationState(parsed.setup_validation) ? { setup_validation: parsed.setup_validation } : {},
|
|
70484
70683
|
...isRuntimeSessionMap(parsed.runtime_sessions) ? { runtime_sessions: parsed.runtime_sessions } : {},
|
|
70485
70684
|
...isInFlightWorkOrderMap(parsed.in_flight_work_orders) ? { in_flight_work_orders: parsed.in_flight_work_orders } : {}
|
|
70486
70685
|
};
|
|
@@ -70490,6 +70689,11 @@ async function loadState(filePath, expectedTenantId) {
|
|
|
70490
70689
|
}
|
|
70491
70690
|
return null;
|
|
70492
70691
|
}
|
|
70692
|
+
function isSetupValidationState(value) {
|
|
70693
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
70694
|
+
const record2 = value;
|
|
70695
|
+
return typeof record2.setup_id === "string" && (record2.runtime === "claude" || record2.runtime === "codex") && typeof record2.expires_at === "string";
|
|
70696
|
+
}
|
|
70493
70697
|
function isRuntimeSessionMap(value) {
|
|
70494
70698
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
70495
70699
|
return false;
|
|
@@ -70504,7 +70708,7 @@ function isRuntimeSessionMap(value) {
|
|
|
70504
70708
|
}
|
|
70505
70709
|
async function saveState(filePath, state) {
|
|
70506
70710
|
await mkdir8(path12.dirname(filePath), { recursive: true, mode: 448 });
|
|
70507
|
-
const tempPath = `${filePath}.${process.pid}.${
|
|
70711
|
+
const tempPath = `${filePath}.${process.pid}.${createHash6("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
|
|
70508
70712
|
try {
|
|
70509
70713
|
await writeFile8(tempPath, `${JSON.stringify(state)}
|
|
70510
70714
|
`, { mode: 384 });
|
|
@@ -70547,7 +70751,7 @@ function generateRunnerServiceKeyPair() {
|
|
|
70547
70751
|
return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(publicKey) };
|
|
70548
70752
|
}
|
|
70549
70753
|
function runnerServiceKeyPairFromPrivate(privatePem) {
|
|
70550
|
-
const privateKey =
|
|
70754
|
+
const privateKey = createPrivateKey2(privatePem);
|
|
70551
70755
|
return { privateKey: privatePem, publicKey: openSshEd25519PublicKey(createPublicKey2(privateKey)) };
|
|
70552
70756
|
}
|
|
70553
70757
|
function runnerServiceKeyPairForState(state) {
|
|
@@ -71292,6 +71496,120 @@ function buildTurnCommand(input) {
|
|
|
71292
71496
|
]
|
|
71293
71497
|
};
|
|
71294
71498
|
}
|
|
71499
|
+
async function runSetupValidationProbe(input) {
|
|
71500
|
+
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
71501
|
+
if (new Date(input.mcp.expires_at).getTime() <= Date.now()) {
|
|
71502
|
+
return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
|
|
71503
|
+
}
|
|
71504
|
+
const configPath = path12.join(tmpdir2(), `rost-runner-setup-mcp-${randomUUID()}.json`);
|
|
71505
|
+
const prompt = `Call rost_runner_setup_status exactly once with setup_id ${input.setupId}. Do not call any other tool. Then respond only with ROST_SETUP_ID=${input.setupId}.`;
|
|
71506
|
+
const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: input.mcp.url, headers: { Authorization: `Bearer ${input.mcp.token}` } } } });
|
|
71507
|
+
let claudeConfigDir = null;
|
|
71508
|
+
let codexHomeDir = null;
|
|
71509
|
+
let sandboxProfilePath = null;
|
|
71510
|
+
const credentialCell = credentialCellDirForRuntime(input.runtime, input.state, input.config.homeDir);
|
|
71511
|
+
const credentialCellLock = await acquireCodexHomeLock(credentialCell, {
|
|
71512
|
+
waitMs: 0,
|
|
71513
|
+
label: credentialCellLabelForRuntime(input.runtime)
|
|
71514
|
+
});
|
|
71515
|
+
if (credentialCellLock === null) {
|
|
71516
|
+
return { ok: false, retryable: true, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
|
|
71517
|
+
}
|
|
71518
|
+
try {
|
|
71519
|
+
if (input.runtime === "claude") {
|
|
71520
|
+
const cell2 = stableClaudeHomeDir(input.state, input.config.homeDir);
|
|
71521
|
+
if (await claudeCellNeedsLogin(cell2)) {
|
|
71522
|
+
return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
|
|
71523
|
+
}
|
|
71524
|
+
claudeConfigDir = await provisionIsolatedClaudeConfigDir(
|
|
71525
|
+
path12.join(tmpdir2(), `rost-runner-setup-claude-${randomUUID()}`),
|
|
71526
|
+
cell2
|
|
71527
|
+
);
|
|
71528
|
+
} else {
|
|
71529
|
+
codexHomeDir = await provisionIsolatedCodexHomeDir(
|
|
71530
|
+
input.env,
|
|
71531
|
+
stableCodexHomeDir(input.state, input.config.homeDir),
|
|
71532
|
+
tmpdir2()
|
|
71533
|
+
);
|
|
71534
|
+
}
|
|
71535
|
+
await writeFile8(configPath, `${mcpConfig}
|
|
71536
|
+
`, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
|
|
71537
|
+
const built = buildSetupValidationProbeCommand(input.runtime, prompt, configPath);
|
|
71538
|
+
if (input.config.sandbox.kind === "seatbelt") {
|
|
71539
|
+
sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-setup-${randomUUID()}.sb`);
|
|
71540
|
+
await writeFile8(sandboxProfilePath, input.config.sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
|
|
71541
|
+
}
|
|
71542
|
+
const wrapped = wrapCommandWithSandbox(input.config.sandbox, built.command, built.args, sandboxProfilePath);
|
|
71543
|
+
const fakeModel = (input.env.ROST_RUNNER_FAKE_MODEL ?? "").trim();
|
|
71544
|
+
const result = await runModelProcess({
|
|
71545
|
+
command: fakeModel.length > 0 ? fakeModel : wrapped.command,
|
|
71546
|
+
args: wrapped.args,
|
|
71547
|
+
cwd: tmpdir2(),
|
|
71548
|
+
timeoutMs: 12e4,
|
|
71549
|
+
env: buildModelSpawnEnv(
|
|
71550
|
+
input.env,
|
|
71551
|
+
claudeConfigDir ?? void 0,
|
|
71552
|
+
codexHomeDir ?? void 0,
|
|
71553
|
+
input.runtime === "claude" ? stableClaudeHomeDir(input.state, input.config.homeDir) : void 0
|
|
71554
|
+
),
|
|
71555
|
+
expectSessionEnvelope: input.runtime === "claude",
|
|
71556
|
+
interactiveTurn: true,
|
|
71557
|
+
...input.runtime === "claude" ? { requiredMcpTools: ["mcp__rost__rost_runner_setup_status"] } : {}
|
|
71558
|
+
});
|
|
71559
|
+
const output = `${result.assistantText ?? ""}
|
|
71560
|
+
${result.transcript ?? ""}`;
|
|
71561
|
+
const match = /(?:^|\s)ROST_SETUP_ID=([0-9a-f-]{36})(?:\s|$)/i.exec(output);
|
|
71562
|
+
return {
|
|
71563
|
+
ok: result.ok && match?.[1] === input.setupId,
|
|
71564
|
+
setupId: match?.[1] ?? null,
|
|
71565
|
+
providerCliVersion: input.providerCliVersion,
|
|
71566
|
+
observedAt,
|
|
71567
|
+
sandboxPolicyVersion: input.config.sandbox.kind
|
|
71568
|
+
};
|
|
71569
|
+
} catch {
|
|
71570
|
+
return { ok: false, setupId: null, providerCliVersion: input.providerCliVersion, observedAt, sandboxPolicyVersion: input.config.sandbox.kind };
|
|
71571
|
+
} finally {
|
|
71572
|
+
await rm8(configPath, { force: true });
|
|
71573
|
+
if (sandboxProfilePath !== null) await rm8(sandboxProfilePath, { force: true });
|
|
71574
|
+
if (claudeConfigDir !== null) await rm8(claudeConfigDir, { recursive: true, force: true });
|
|
71575
|
+
await credentialCellLock.release();
|
|
71576
|
+
}
|
|
71577
|
+
}
|
|
71578
|
+
function buildSetupValidationProbeCommand(runtime, prompt, configPath) {
|
|
71579
|
+
return {
|
|
71580
|
+
command: runtime,
|
|
71581
|
+
args: runtime === "claude" ? [
|
|
71582
|
+
"-p",
|
|
71583
|
+
prompt,
|
|
71584
|
+
"--mcp-config",
|
|
71585
|
+
configPath,
|
|
71586
|
+
"--strict-mcp-config",
|
|
71587
|
+
"--tools",
|
|
71588
|
+
"",
|
|
71589
|
+
"--allowedTools",
|
|
71590
|
+
"mcp__rost__rost_runner_setup_status",
|
|
71591
|
+
"--permission-mode",
|
|
71592
|
+
"dontAsk",
|
|
71593
|
+
"--output-format",
|
|
71594
|
+
"stream-json",
|
|
71595
|
+
"--verbose"
|
|
71596
|
+
] : [
|
|
71597
|
+
"exec",
|
|
71598
|
+
prompt,
|
|
71599
|
+
"--mcp-config",
|
|
71600
|
+
configPath,
|
|
71601
|
+
"--sandbox",
|
|
71602
|
+
"read-only",
|
|
71603
|
+
"--skip-git-repo-check",
|
|
71604
|
+
"--ephemeral",
|
|
71605
|
+
"--ignore-user-config",
|
|
71606
|
+
"--ignore-rules",
|
|
71607
|
+
"-C",
|
|
71608
|
+
tmpdir2(),
|
|
71609
|
+
...CODEX_NATIVE_TOOL_ISOLATION_CONFIG_ARGS
|
|
71610
|
+
]
|
|
71611
|
+
};
|
|
71612
|
+
}
|
|
71295
71613
|
function runLocalTurn(ctx, workOrder, runtime, kind) {
|
|
71296
71614
|
return spawnRunnerTurn(ctx, workOrder, runtime, kind);
|
|
71297
71615
|
}
|
|
@@ -71428,7 +71746,7 @@ async function prepareForgeTurn(ctx, workOrder, execution, runtime, seams) {
|
|
|
71428
71746
|
var FORGE_MAX_CHANGED_PATHS = 2e3;
|
|
71429
71747
|
var FORGE_MAX_CHANGED_PATH_LENGTH = 500;
|
|
71430
71748
|
function forgeChangedPathsField(changedPaths) {
|
|
71431
|
-
const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((
|
|
71749
|
+
const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((path16) => path16.length >= 1 && path16.length <= FORGE_MAX_CHANGED_PATH_LENGTH);
|
|
71432
71750
|
return withinCaps ? { changedPaths: [...changedPaths] } : {};
|
|
71433
71751
|
}
|
|
71434
71752
|
async function finalizeForgeBuildPush(ctx, prep, result, seams) {
|
|
@@ -71557,11 +71875,11 @@ function journaledClaudeConfigDirNames(state) {
|
|
|
71557
71875
|
);
|
|
71558
71876
|
}
|
|
71559
71877
|
function stableCodexHomeDir(state, homeDir) {
|
|
71560
|
-
const key =
|
|
71878
|
+
const key = createHash6("sha256").update(`codex-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
|
|
71561
71879
|
return path12.join(runnerCodexHomeBaseDir(homeDir), `rost-runner-codex-home-${key}`);
|
|
71562
71880
|
}
|
|
71563
71881
|
function stableClaudeHomeDir(state, homeDir) {
|
|
71564
|
-
const key =
|
|
71882
|
+
const key = createHash6("sha256").update(`claude-home:${state.runner_id ?? "runner"}`).digest("hex").slice(0, 16);
|
|
71565
71883
|
return path12.join(runnerClaudeHomeBaseDir(homeDir), `rost-runner-claude-home-${key}`);
|
|
71566
71884
|
}
|
|
71567
71885
|
function credentialCellDirForRuntime(runtime, state, homeDir) {
|
|
@@ -71674,11 +71992,11 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
|
|
|
71674
71992
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
71675
71993
|
const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
|
|
71676
71994
|
const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
|
|
71677
|
-
const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${
|
|
71995
|
+
const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash6("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
|
|
71678
71996
|
const prompt = buildTurnPrompt({ kind, workOrder, execution, hasWorkspace: forgePrep !== null });
|
|
71679
71997
|
const claudeCell = runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
|
|
71680
71998
|
const claudeConfigDir = runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
|
|
71681
|
-
resumeClaudeConfigDir ?? path12.join(tmpdir2(), `rost-runner-claude-config-${
|
|
71999
|
+
resumeClaudeConfigDir ?? path12.join(tmpdir2(), `rost-runner-claude-config-${createHash6("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
|
|
71682
72000
|
claudeCell,
|
|
71683
72001
|
(message) => ctx.io.stderr.write(`${redactForLog(message)}
|
|
71684
72002
|
`)
|
|
@@ -71719,7 +72037,7 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind, resumeSessionIdOve
|
|
|
71719
72037
|
if (sandbox.kind === "seatbelt") {
|
|
71720
72038
|
sandboxProfilePath = path12.join(
|
|
71721
72039
|
tmpdir2(),
|
|
71722
|
-
`rost-runner-sandbox-${
|
|
72040
|
+
`rost-runner-sandbox-${createHash6("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
|
|
71723
72041
|
);
|
|
71724
72042
|
await writeFile8(sandboxProfilePath, sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
|
|
71725
72043
|
}
|
|
@@ -71971,7 +72289,7 @@ async function spawnMergeConflictModel(ctx, input) {
|
|
|
71971
72289
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
71972
72290
|
const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
|
|
71973
72291
|
const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
|
|
71974
|
-
const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${
|
|
72292
|
+
const configPath = path12.join(tmpdir2(), `rost-runner-mcp-${createHash6("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
|
|
71975
72293
|
const credentialCellLockWaitMs = 30 * 6e4;
|
|
71976
72294
|
const credentialCellLock = ctx.credentialCellLockHeld !== true ? await acquireCodexHomeLock(credentialCellDirForRuntime(input.runtime, ctx.state, ctx.config.homeDir), { waitMs: credentialCellLockWaitMs, label: credentialCellLabelForRuntime(input.runtime) }) : null;
|
|
71977
72295
|
if (ctx.credentialCellLockHeld !== true && credentialCellLock === null) {
|
|
@@ -71980,7 +72298,7 @@ async function spawnMergeConflictModel(ctx, input) {
|
|
|
71980
72298
|
try {
|
|
71981
72299
|
const claudeCell = input.runtime === "claude" ? stableClaudeHomeDir(ctx.state, ctx.config.homeDir) : null;
|
|
71982
72300
|
const claudeConfigDir = input.runtime === "claude" && claudeCell !== null ? await provisionIsolatedClaudeConfigDir(
|
|
71983
|
-
path12.join(tmpdir2(), `rost-runner-claude-config-${
|
|
72301
|
+
path12.join(tmpdir2(), `rost-runner-claude-config-${createHash6("sha256").update(`${configPath}:cfg`).digest("hex").slice(0, 16)}`),
|
|
71984
72302
|
claudeCell,
|
|
71985
72303
|
(message) => ctx.io.stderr.write(`${redactForLog(message)}
|
|
71986
72304
|
`)
|
|
@@ -72002,7 +72320,7 @@ async function spawnMergeConflictModel(ctx, input) {
|
|
|
72002
72320
|
].join("\n");
|
|
72003
72321
|
let sandboxProfilePath = null;
|
|
72004
72322
|
if (input.sandbox.kind === "seatbelt") {
|
|
72005
|
-
sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-sandbox-${
|
|
72323
|
+
sandboxProfilePath = path12.join(tmpdir2(), `rost-runner-sandbox-${createHash6("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`);
|
|
72006
72324
|
await writeFile8(sandboxProfilePath, input.sandbox.profile, { mode: 384, flag: EXCLUSIVE_NOFOLLOW_FLAGS });
|
|
72007
72325
|
}
|
|
72008
72326
|
const built = buildTurnCommand({
|
|
@@ -73411,11 +73729,724 @@ function isEnoent(error51) {
|
|
|
73411
73729
|
}
|
|
73412
73730
|
|
|
73413
73731
|
// src/runner-setup-orchestrator.ts
|
|
73414
|
-
import { createHash as
|
|
73732
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
73415
73733
|
import { constants as fsConstants8, realpathSync as realpathSync3 } from "node:fs";
|
|
73416
|
-
import { open as open3, rm as
|
|
73417
|
-
import
|
|
73734
|
+
import { open as open3, rm as rm11 } from "node:fs/promises";
|
|
73735
|
+
import path15 from "node:path";
|
|
73418
73736
|
import { setTimeout as defaultSleep2 } from "node:timers/promises";
|
|
73737
|
+
|
|
73738
|
+
// src/codex-auth-adapter.ts
|
|
73739
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
73740
|
+
import { lstat as lstat4, readdir as readdir6, rm as rm10 } from "node:fs/promises";
|
|
73741
|
+
import path14 from "node:path";
|
|
73742
|
+
var CODEX_MIN_AUTH_ADAPTER_VERSION = "0.144.6";
|
|
73743
|
+
function parseCodexVersion(raw) {
|
|
73744
|
+
const trimmed = raw.trim();
|
|
73745
|
+
const match = /^(?:codex-cli\s+)?(\d+)\.(\d+)\.(\d+)$/.exec(trimmed);
|
|
73746
|
+
if (match === null) {
|
|
73747
|
+
return null;
|
|
73748
|
+
}
|
|
73749
|
+
return `${match[1]}.${match[2]}.${match[3]}`;
|
|
73750
|
+
}
|
|
73751
|
+
function parseVersionComponents(version4) {
|
|
73752
|
+
const parts = version4.split(".");
|
|
73753
|
+
const major = Number(parts[0]);
|
|
73754
|
+
const minor = Number(parts[1]);
|
|
73755
|
+
const patch = Number(parts[2]);
|
|
73756
|
+
return [major, minor, patch];
|
|
73757
|
+
}
|
|
73758
|
+
function compareVersions(a, b) {
|
|
73759
|
+
const [aMajor, aMinor, aPatch] = parseVersionComponents(a);
|
|
73760
|
+
const [bMajor, bMinor, bPatch] = parseVersionComponents(b);
|
|
73761
|
+
if (aMajor !== bMajor) return aMajor - bMajor;
|
|
73762
|
+
if (aMinor !== bMinor) return aMinor - bMinor;
|
|
73763
|
+
return aPatch - bPatch;
|
|
73764
|
+
}
|
|
73765
|
+
var CODEX_VERSION_CEILING = "0.145.0";
|
|
73766
|
+
function evaluateCodexVersion(raw) {
|
|
73767
|
+
const version4 = parseCodexVersion(raw);
|
|
73768
|
+
if (version4 === null) {
|
|
73769
|
+
return { kind: "unparsable" };
|
|
73770
|
+
}
|
|
73771
|
+
if (compareVersions(version4, CODEX_MIN_AUTH_ADAPTER_VERSION) < 0) {
|
|
73772
|
+
return { kind: "too_old", version: version4, minimum: CODEX_MIN_AUTH_ADAPTER_VERSION };
|
|
73773
|
+
}
|
|
73774
|
+
if (compareVersions(version4, CODEX_VERSION_CEILING) >= 0) {
|
|
73775
|
+
return { kind: "unsupported_line", version: version4 };
|
|
73776
|
+
}
|
|
73777
|
+
return { kind: "supported", version: version4 };
|
|
73778
|
+
}
|
|
73779
|
+
var API_KEY_LINE_PREFIX = "Logged in using an API key";
|
|
73780
|
+
function parseCodexLoginStatus(result) {
|
|
73781
|
+
const lines = `${result.stderr}
|
|
73782
|
+
${result.stdout}`.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
73783
|
+
const isChatgptLine = lines.some((line) => line === "Logged in using ChatGPT");
|
|
73784
|
+
const isApiKeyLine = lines.some((line) => line.startsWith(API_KEY_LINE_PREFIX));
|
|
73785
|
+
const isLoggedOutLine = lines.some((line) => line === "Not logged in");
|
|
73786
|
+
const recognizedMatchCount = [isChatgptLine, isApiKeyLine, isLoggedOutLine].filter(Boolean).length;
|
|
73787
|
+
if (recognizedMatchCount > 1) {
|
|
73788
|
+
return { kind: "unrecognized" };
|
|
73789
|
+
}
|
|
73790
|
+
if (result.code === 0 && isChatgptLine) {
|
|
73791
|
+
return { kind: "chatgpt" };
|
|
73792
|
+
}
|
|
73793
|
+
if (result.code === 0 && isApiKeyLine) {
|
|
73794
|
+
return { kind: "api_key" };
|
|
73795
|
+
}
|
|
73796
|
+
if (result.code !== 0 && isLoggedOutLine) {
|
|
73797
|
+
return { kind: "logged_out" };
|
|
73798
|
+
}
|
|
73799
|
+
if (result.code !== 0 && !isChatgptLine && !isApiKeyLine && !isLoggedOutLine) {
|
|
73800
|
+
return { kind: "command_failed" };
|
|
73801
|
+
}
|
|
73802
|
+
return { kind: "unrecognized" };
|
|
73803
|
+
}
|
|
73804
|
+
function classifyAuthJsonStat(info, selfUid) {
|
|
73805
|
+
const ownerMismatch = selfUid !== null && info.uid !== selfUid;
|
|
73806
|
+
const permissiveBits = (info.mode & 63) !== 0;
|
|
73807
|
+
if (info.isSymbolicLink || !info.isFile || info.nlink !== 1 || ownerMismatch || permissiveBits) {
|
|
73808
|
+
return { kind: "foreign" };
|
|
73809
|
+
}
|
|
73810
|
+
if (info.size === 0) {
|
|
73811
|
+
return { kind: "empty" };
|
|
73812
|
+
}
|
|
73813
|
+
return { kind: "owned" };
|
|
73814
|
+
}
|
|
73815
|
+
function isTrustworthyAncestorShape(info, selfUid) {
|
|
73816
|
+
return info.isDirectory && !info.isSymbolicLink && (info.mode & 18) === 0 && (selfUid === null || info.uid === selfUid);
|
|
73817
|
+
}
|
|
73818
|
+
async function isTrustworthyAbsentCellChain(cell2, opts) {
|
|
73819
|
+
const lstatImpl = opts?.lstatImpl ?? lstat4;
|
|
73820
|
+
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
73821
|
+
let current = path14.dirname(path14.resolve(cell2));
|
|
73822
|
+
for (; ; ) {
|
|
73823
|
+
let info;
|
|
73824
|
+
try {
|
|
73825
|
+
info = await lstatImpl(current);
|
|
73826
|
+
} catch (error51) {
|
|
73827
|
+
if (error51.code !== "ENOENT") {
|
|
73828
|
+
return false;
|
|
73829
|
+
}
|
|
73830
|
+
info = null;
|
|
73831
|
+
}
|
|
73832
|
+
if (info === null) {
|
|
73833
|
+
const parent = path14.dirname(current);
|
|
73834
|
+
if (parent === current) {
|
|
73835
|
+
return false;
|
|
73836
|
+
}
|
|
73837
|
+
current = parent;
|
|
73838
|
+
continue;
|
|
73839
|
+
}
|
|
73840
|
+
return isTrustworthyAncestorShape(
|
|
73841
|
+
{
|
|
73842
|
+
isDirectory: info.isDirectory(),
|
|
73843
|
+
isSymbolicLink: info.isSymbolicLink(),
|
|
73844
|
+
mode: info.mode,
|
|
73845
|
+
uid: info.uid
|
|
73846
|
+
},
|
|
73847
|
+
selfUid
|
|
73848
|
+
);
|
|
73849
|
+
}
|
|
73850
|
+
}
|
|
73851
|
+
async function inspectCodexAuthCell(cell2) {
|
|
73852
|
+
if (!await isVerifiedExistingCodexHomeDir(cell2)) {
|
|
73853
|
+
const resolvedCell = path14.resolve(cell2);
|
|
73854
|
+
const leafInfo = await lstat4(resolvedCell).catch((error51) => error51.code === "ENOENT" ? null : "error").catch(() => "error");
|
|
73855
|
+
if (leafInfo !== null) {
|
|
73856
|
+
return { kind: "unreadable" };
|
|
73857
|
+
}
|
|
73858
|
+
const chainTrustworthy = await isTrustworthyAbsentCellChain(cell2);
|
|
73859
|
+
return chainTrustworthy ? { kind: "absent" } : { kind: "unreadable" };
|
|
73860
|
+
}
|
|
73861
|
+
const cellInfo = await lstat4(cell2).catch(() => null);
|
|
73862
|
+
if (cellInfo === null) {
|
|
73863
|
+
return { kind: "unreadable" };
|
|
73864
|
+
}
|
|
73865
|
+
if ((cellInfo.mode & 63) !== 0) {
|
|
73866
|
+
return { kind: "foreign" };
|
|
73867
|
+
}
|
|
73868
|
+
const authJsonPath = path14.join(cell2, "auth.json");
|
|
73869
|
+
const info = await lstat4(authJsonPath).catch((error51) => {
|
|
73870
|
+
if (error51.code === "ENOENT") {
|
|
73871
|
+
return null;
|
|
73872
|
+
}
|
|
73873
|
+
throw error51;
|
|
73874
|
+
}).catch(() => "unreadable");
|
|
73875
|
+
if (info === "unreadable") {
|
|
73876
|
+
return { kind: "unreadable" };
|
|
73877
|
+
}
|
|
73878
|
+
if (info === null) {
|
|
73879
|
+
return { kind: "empty" };
|
|
73880
|
+
}
|
|
73881
|
+
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
73882
|
+
return classifyAuthJsonStat(
|
|
73883
|
+
{
|
|
73884
|
+
isSymbolicLink: info.isSymbolicLink(),
|
|
73885
|
+
isFile: info.isFile(),
|
|
73886
|
+
nlink: info.nlink,
|
|
73887
|
+
uid: info.uid,
|
|
73888
|
+
mode: info.mode,
|
|
73889
|
+
size: info.size
|
|
73890
|
+
},
|
|
73891
|
+
selfUid
|
|
73892
|
+
);
|
|
73893
|
+
}
|
|
73894
|
+
function decideCodexAuth(version4, cell2, status) {
|
|
73895
|
+
switch (version4.kind) {
|
|
73896
|
+
case "too_old":
|
|
73897
|
+
return { kind: "refuse", reason: "codex_version_too_old" };
|
|
73898
|
+
case "unsupported_line":
|
|
73899
|
+
return { kind: "refuse", reason: "codex_version_unsupported_line" };
|
|
73900
|
+
case "unparsable":
|
|
73901
|
+
return { kind: "refuse", reason: "codex_version_unparsable" };
|
|
73902
|
+
case "supported":
|
|
73903
|
+
break;
|
|
73904
|
+
default: {
|
|
73905
|
+
const exhaustive = version4;
|
|
73906
|
+
throw new Error(`unreachable codex version verdict: ${JSON.stringify(exhaustive)}`);
|
|
73907
|
+
}
|
|
73908
|
+
}
|
|
73909
|
+
if (cell2.kind === "unreadable") {
|
|
73910
|
+
return { kind: "refuse", reason: "cell_unreadable" };
|
|
73911
|
+
}
|
|
73912
|
+
if (cell2.kind === "foreign") {
|
|
73913
|
+
return { kind: "refuse", reason: "foreign_cell_ownership" };
|
|
73914
|
+
}
|
|
73915
|
+
switch (cell2.kind) {
|
|
73916
|
+
case "absent": {
|
|
73917
|
+
switch (status.kind) {
|
|
73918
|
+
case "chatgpt":
|
|
73919
|
+
case "api_key":
|
|
73920
|
+
return { kind: "refuse", reason: "status_not_from_this_cell" };
|
|
73921
|
+
case "logged_out":
|
|
73922
|
+
case "command_failed":
|
|
73923
|
+
case "unrecognized":
|
|
73924
|
+
return { kind: "device_auth_login_required" };
|
|
73925
|
+
case "exit_unconfirmed":
|
|
73926
|
+
return { kind: "refuse", reason: "probe_exit_unconfirmed" };
|
|
73927
|
+
default: {
|
|
73928
|
+
const exhaustive = status;
|
|
73929
|
+
throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
|
|
73930
|
+
}
|
|
73931
|
+
}
|
|
73932
|
+
}
|
|
73933
|
+
case "empty": {
|
|
73934
|
+
switch (status.kind) {
|
|
73935
|
+
case "chatgpt":
|
|
73936
|
+
case "api_key":
|
|
73937
|
+
return { kind: "refuse", reason: "status_not_from_this_cell" };
|
|
73938
|
+
case "logged_out":
|
|
73939
|
+
return { kind: "device_auth_login_required" };
|
|
73940
|
+
case "command_failed":
|
|
73941
|
+
return { kind: "refuse", reason: "status_unavailable" };
|
|
73942
|
+
case "exit_unconfirmed":
|
|
73943
|
+
return { kind: "refuse", reason: "probe_exit_unconfirmed" };
|
|
73944
|
+
case "unrecognized":
|
|
73945
|
+
return { kind: "refuse", reason: "unrecognized_status" };
|
|
73946
|
+
default: {
|
|
73947
|
+
const exhaustive = status;
|
|
73948
|
+
throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
|
|
73949
|
+
}
|
|
73950
|
+
}
|
|
73951
|
+
}
|
|
73952
|
+
case "owned": {
|
|
73953
|
+
switch (status.kind) {
|
|
73954
|
+
case "chatgpt":
|
|
73955
|
+
return { kind: "proceed_to_validation" };
|
|
73956
|
+
case "api_key":
|
|
73957
|
+
return { kind: "refuse", reason: "api_key_auth_unsupported" };
|
|
73958
|
+
case "logged_out":
|
|
73959
|
+
return { kind: "device_auth_login_required" };
|
|
73960
|
+
case "command_failed":
|
|
73961
|
+
return { kind: "refuse", reason: "status_unavailable" };
|
|
73962
|
+
case "exit_unconfirmed":
|
|
73963
|
+
return { kind: "refuse", reason: "probe_exit_unconfirmed" };
|
|
73964
|
+
case "unrecognized":
|
|
73965
|
+
return { kind: "refuse", reason: "unrecognized_status" };
|
|
73966
|
+
default: {
|
|
73967
|
+
const exhaustive = status;
|
|
73968
|
+
throw new Error(`unreachable codex login status: ${JSON.stringify(exhaustive)}`);
|
|
73969
|
+
}
|
|
73970
|
+
}
|
|
73971
|
+
}
|
|
73972
|
+
default: {
|
|
73973
|
+
const exhaustive = cell2;
|
|
73974
|
+
throw new Error(`unreachable codex auth cell state: ${JSON.stringify(exhaustive)}`);
|
|
73975
|
+
}
|
|
73976
|
+
}
|
|
73977
|
+
}
|
|
73978
|
+
var CODEX_LOGIN_ENV_ALLOWLIST = LOGIN_ENV_ALLOWLIST;
|
|
73979
|
+
function copyAllowlistedEnv(base) {
|
|
73980
|
+
const env = {};
|
|
73981
|
+
for (const key of CODEX_LOGIN_ENV_ALLOWLIST) {
|
|
73982
|
+
const value = base[key];
|
|
73983
|
+
if (typeof value === "string") {
|
|
73984
|
+
env[key] = value;
|
|
73985
|
+
}
|
|
73986
|
+
}
|
|
73987
|
+
return env;
|
|
73988
|
+
}
|
|
73989
|
+
function buildCodexAuthEnv(base, cell2) {
|
|
73990
|
+
const env = copyAllowlistedEnv(base);
|
|
73991
|
+
env.CODEX_HOME = cell2;
|
|
73992
|
+
return env;
|
|
73993
|
+
}
|
|
73994
|
+
var CODEX_PROBE_TIMEOUT_MS = 15e3;
|
|
73995
|
+
var CODEX_PROBE_KILL_GRACE_MS = 2e3;
|
|
73996
|
+
var CODEX_PROBE_SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
73997
|
+
var CODEX_PROBE_SANDBOX_PROFILE = "(version 1) (allow default) (deny process-fork)";
|
|
73998
|
+
function probeContainmentUnavailable() {
|
|
73999
|
+
return Object.assign(
|
|
74000
|
+
new Error(
|
|
74001
|
+
`refusing to run the codex probe: complete descendant containment is unavailable on ${process.platform}`
|
|
74002
|
+
),
|
|
74003
|
+
{
|
|
74004
|
+
code: "EROST_PROBE_CONTAINMENT_UNAVAILABLE",
|
|
74005
|
+
codexProbeContainmentUnavailable: true
|
|
74006
|
+
}
|
|
74007
|
+
);
|
|
74008
|
+
}
|
|
74009
|
+
function decideProbeGroupKill(observed) {
|
|
74010
|
+
if (observed.childExited && observed.groupHeldAtChildExit === false) {
|
|
74011
|
+
return "skip";
|
|
74012
|
+
}
|
|
74013
|
+
return observed.groupPopulatedNow ? "signal" : "skip";
|
|
74014
|
+
}
|
|
74015
|
+
function spawnCapture(cmd, args, opts) {
|
|
74016
|
+
const timeoutMs = opts.timeoutMs ?? CODEX_PROBE_TIMEOUT_MS;
|
|
74017
|
+
const killGraceMs = opts.killGraceMs ?? CODEX_PROBE_KILL_GRACE_MS;
|
|
74018
|
+
const containment = opts.containment ?? "required";
|
|
74019
|
+
return new Promise((resolve2, reject) => {
|
|
74020
|
+
const stdoutChunks = [];
|
|
74021
|
+
const stderrChunks = [];
|
|
74022
|
+
let spawnCommand = cmd;
|
|
74023
|
+
let spawnArgs = args;
|
|
74024
|
+
if (containment === "required") {
|
|
74025
|
+
if (process.platform !== "darwin") {
|
|
74026
|
+
reject(probeContainmentUnavailable());
|
|
74027
|
+
return;
|
|
74028
|
+
}
|
|
74029
|
+
spawnCommand = CODEX_PROBE_SANDBOX_EXEC_PATH;
|
|
74030
|
+
spawnArgs = ["-p", CODEX_PROBE_SANDBOX_PROFILE, cmd, ...args];
|
|
74031
|
+
}
|
|
74032
|
+
const child = spawn3(spawnCommand, spawnArgs, {
|
|
74033
|
+
env: opts.env,
|
|
74034
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
74035
|
+
...process.platform !== "win32" ? { detached: true } : {}
|
|
74036
|
+
});
|
|
74037
|
+
let settled = false;
|
|
74038
|
+
let timedOut = false;
|
|
74039
|
+
let killOutcome = null;
|
|
74040
|
+
let timer = null;
|
|
74041
|
+
let graceTimer = null;
|
|
74042
|
+
let childExited = false;
|
|
74043
|
+
let groupHeldAtChildExit = null;
|
|
74044
|
+
const probeGroupPopulated = () => {
|
|
74045
|
+
const pid = child.pid;
|
|
74046
|
+
if (typeof pid !== "number") {
|
|
74047
|
+
return false;
|
|
74048
|
+
}
|
|
74049
|
+
if (process.platform !== "win32") {
|
|
74050
|
+
try {
|
|
74051
|
+
process.kill(-pid, 0);
|
|
74052
|
+
return true;
|
|
74053
|
+
} catch {
|
|
74054
|
+
return false;
|
|
74055
|
+
}
|
|
74056
|
+
}
|
|
74057
|
+
return !childExited;
|
|
74058
|
+
};
|
|
74059
|
+
const terminateProbeGroup = () => {
|
|
74060
|
+
if (killOutcome !== null) {
|
|
74061
|
+
return;
|
|
74062
|
+
}
|
|
74063
|
+
if (decideProbeGroupKill({ childExited, groupHeldAtChildExit, groupPopulatedNow: probeGroupPopulated() }) === "skip") {
|
|
74064
|
+
killOutcome = "skipped_nothing_ours";
|
|
74065
|
+
return;
|
|
74066
|
+
}
|
|
74067
|
+
killProcessTree(child);
|
|
74068
|
+
killOutcome = "signalled";
|
|
74069
|
+
};
|
|
74070
|
+
const clearTimers = () => {
|
|
74071
|
+
if (timer !== null) {
|
|
74072
|
+
clearTimeout(timer);
|
|
74073
|
+
}
|
|
74074
|
+
if (graceTimer !== null) {
|
|
74075
|
+
clearTimeout(graceTimer);
|
|
74076
|
+
}
|
|
74077
|
+
};
|
|
74078
|
+
const teardownCapture = () => {
|
|
74079
|
+
child.stdout?.removeAllListeners("data");
|
|
74080
|
+
child.stderr?.removeAllListeners("data");
|
|
74081
|
+
child.stdout?.destroy();
|
|
74082
|
+
child.stderr?.destroy();
|
|
74083
|
+
};
|
|
74084
|
+
const startSettleGrace = (phase) => {
|
|
74085
|
+
if (graceTimer !== null || settled) {
|
|
74086
|
+
return;
|
|
74087
|
+
}
|
|
74088
|
+
graceTimer = setTimeout(() => {
|
|
74089
|
+
if (settled) {
|
|
74090
|
+
return;
|
|
74091
|
+
}
|
|
74092
|
+
settled = true;
|
|
74093
|
+
clearTimers();
|
|
74094
|
+
teardownCapture();
|
|
74095
|
+
reject(Object.assign(
|
|
74096
|
+
new Error(
|
|
74097
|
+
phase === "timeout" ? `${cmd} ${args.join(" ")} timed out after ${timeoutMs}ms and the exit of its process tree could not be confirmed within ${killGraceMs}ms` : `${cmd} ${args.join(" ")} exited, but a descendant kept its output pipes open and its exit could not be confirmed within ${killGraceMs}ms`
|
|
74098
|
+
),
|
|
74099
|
+
{
|
|
74100
|
+
code: "ETIMEDOUT",
|
|
74101
|
+
codexProbeExitUnconfirmedMs: killGraceMs,
|
|
74102
|
+
codexProbeKillOutcome: killOutcome,
|
|
74103
|
+
codexProbeSettlePhase: phase,
|
|
74104
|
+
codexProbeContainment: containment
|
|
74105
|
+
}
|
|
74106
|
+
));
|
|
74107
|
+
}, killGraceMs);
|
|
74108
|
+
};
|
|
74109
|
+
timer = setTimeout(() => {
|
|
74110
|
+
if (settled || childExited) {
|
|
74111
|
+
return;
|
|
74112
|
+
}
|
|
74113
|
+
timedOut = true;
|
|
74114
|
+
terminateProbeGroup();
|
|
74115
|
+
startSettleGrace("timeout");
|
|
74116
|
+
}, timeoutMs);
|
|
74117
|
+
child.on("exit", () => {
|
|
74118
|
+
childExited = true;
|
|
74119
|
+
groupHeldAtChildExit = probeGroupPopulated();
|
|
74120
|
+
if (settled) {
|
|
74121
|
+
return;
|
|
74122
|
+
}
|
|
74123
|
+
terminateProbeGroup();
|
|
74124
|
+
startSettleGrace("child_exit");
|
|
74125
|
+
});
|
|
74126
|
+
child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
74127
|
+
child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
74128
|
+
child.on("error", (error51) => {
|
|
74129
|
+
if (settled) {
|
|
74130
|
+
return;
|
|
74131
|
+
}
|
|
74132
|
+
settled = true;
|
|
74133
|
+
clearTimers();
|
|
74134
|
+
teardownCapture();
|
|
74135
|
+
if (containment === "required" && error51.code !== void 0 && ["ENOENT", "EACCES"].includes(String(error51.code))) {
|
|
74136
|
+
reject(probeContainmentUnavailable());
|
|
74137
|
+
return;
|
|
74138
|
+
}
|
|
74139
|
+
reject(error51);
|
|
74140
|
+
});
|
|
74141
|
+
child.on("close", (code) => {
|
|
74142
|
+
if (settled) {
|
|
74143
|
+
return;
|
|
74144
|
+
}
|
|
74145
|
+
settled = true;
|
|
74146
|
+
clearTimers();
|
|
74147
|
+
teardownCapture();
|
|
74148
|
+
if (timedOut) {
|
|
74149
|
+
reject(Object.assign(new Error(`${cmd} ${args.join(" ")} timed out after ${timeoutMs}ms`), {
|
|
74150
|
+
code: "ETIMEDOUT",
|
|
74151
|
+
codexProbeTimedOutMs: timeoutMs,
|
|
74152
|
+
codexProbeKillOutcome: killOutcome,
|
|
74153
|
+
codexProbeContainment: containment
|
|
74154
|
+
}));
|
|
74155
|
+
return;
|
|
74156
|
+
}
|
|
74157
|
+
resolve2({
|
|
74158
|
+
code: code ?? 1,
|
|
74159
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
74160
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
74161
|
+
});
|
|
74162
|
+
});
|
|
74163
|
+
});
|
|
74164
|
+
}
|
|
74165
|
+
var CODEX_IGNORE_USER_CONFIG_ARGS = ["--ignore-user-config"];
|
|
74166
|
+
function describeProbeKillOutcome(error51) {
|
|
74167
|
+
const outcome = "codexProbeKillOutcome" in error51 ? error51.codexProbeKillOutcome : null;
|
|
74168
|
+
if (outcome === "signalled") {
|
|
74169
|
+
return "SIGKILL was sent to its process group";
|
|
74170
|
+
}
|
|
74171
|
+
if (outcome === "skipped_nothing_ours") {
|
|
74172
|
+
return "no signal was sent: the probe's process group held nothing of ours left to signal";
|
|
74173
|
+
}
|
|
74174
|
+
return "its kill outcome was not recorded";
|
|
74175
|
+
}
|
|
74176
|
+
function describeSpawnFailure(what, error51) {
|
|
74177
|
+
if (typeof error51 === "object" && error51 !== null && "codexProbeContainmentUnavailable" in error51) {
|
|
74178
|
+
return `${what} refused: complete descendant containment is unavailable on ${process.platform}`;
|
|
74179
|
+
}
|
|
74180
|
+
if (typeof error51 === "object" && error51 !== null && "codexProbeExitUnconfirmedMs" in error51) {
|
|
74181
|
+
const graceMs = error51.codexProbeExitUnconfirmedMs;
|
|
74182
|
+
const containment = "codexProbeContainment" in error51 ? error51.codexProbeContainment : null;
|
|
74183
|
+
const phase = "codexProbeSettlePhase" in error51 ? error51.codexProbeSettlePhase : null;
|
|
74184
|
+
const lede = phase === "child_exit" ? `${what} exited, but a descendant kept its output pipes open` : `${what} timed out`;
|
|
74185
|
+
return containment === "required" ? `${lede}; ${describeProbeKillOutcome(error51)}, and its exit could not be confirmed within ${String(graceMs)}ms despite the probe containment boundary` : `${lede}; ${describeProbeKillOutcome(error51)}, and its exit could not be confirmed within ${String(graceMs)}ms; a descendant process may still be running`;
|
|
74186
|
+
}
|
|
74187
|
+
if (typeof error51 === "object" && error51 !== null && "codexProbeTimedOutMs" in error51) {
|
|
74188
|
+
const timedOutMs = error51.codexProbeTimedOutMs;
|
|
74189
|
+
const containment = "codexProbeContainment" in error51 ? error51.codexProbeContainment : null;
|
|
74190
|
+
return containment === "required" ? `${what} timed out after ${String(timedOutMs)}ms; ${describeProbeKillOutcome(error51)}, and the probe process exited and closed its output pipes under the descendant-containment boundary` : `${what} timed out after ${String(timedOutMs)}ms; ${describeProbeKillOutcome(error51)}, and the probe process exited and closed its output pipes; a descendant that left the process group with its own stdio would not be observed here`;
|
|
74191
|
+
}
|
|
74192
|
+
const code = typeof error51 === "object" && error51 !== null && "code" in error51 ? String(error51.code) : "unknown";
|
|
74193
|
+
return `${what} could not be spawned (${code})`;
|
|
74194
|
+
}
|
|
74195
|
+
async function probeCodexVersion(codexBin, opts) {
|
|
74196
|
+
const spawnImpl = opts?.spawnImpl ?? spawnCapture;
|
|
74197
|
+
const env = copyAllowlistedEnv(opts?.env ?? process.env);
|
|
74198
|
+
let result;
|
|
74199
|
+
try {
|
|
74200
|
+
result = await spawnImpl(codexBin, ["--version", ...CODEX_IGNORE_USER_CONFIG_ARGS], {
|
|
74201
|
+
env,
|
|
74202
|
+
...opts?.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
74203
|
+
...opts?.killGraceMs !== void 0 ? { killGraceMs: opts.killGraceMs } : {},
|
|
74204
|
+
...opts?.containmentForTest !== void 0 ? { containment: opts.containmentForTest } : {}
|
|
74205
|
+
});
|
|
74206
|
+
} catch (error51) {
|
|
74207
|
+
opts?.onProbeError?.(describeSpawnFailure("codex --version", error51));
|
|
74208
|
+
return { kind: "unparsable" };
|
|
74209
|
+
}
|
|
74210
|
+
if (result.code !== 0) {
|
|
74211
|
+
return { kind: "unparsable" };
|
|
74212
|
+
}
|
|
74213
|
+
for (const line of `${result.stdout}
|
|
74214
|
+
${result.stderr}`.split("\n")) {
|
|
74215
|
+
if (parseCodexVersion(line) !== null) {
|
|
74216
|
+
return evaluateCodexVersion(line);
|
|
74217
|
+
}
|
|
74218
|
+
}
|
|
74219
|
+
return { kind: "unparsable" };
|
|
74220
|
+
}
|
|
74221
|
+
var CODEX_AUTH_CELL_ROUTINE_ENTRIES = /* @__PURE__ */ new Set(["log", "tmp", "config.toml"]);
|
|
74222
|
+
function isRoutineCodexBookkeepingShape(entry, info, selfUid) {
|
|
74223
|
+
if (!CODEX_AUTH_CELL_ROUTINE_ENTRIES.has(entry) || info.isSymbolicLink()) {
|
|
74224
|
+
return false;
|
|
74225
|
+
}
|
|
74226
|
+
if (selfUid !== null && info.uid !== selfUid) {
|
|
74227
|
+
return false;
|
|
74228
|
+
}
|
|
74229
|
+
if ((info.mode & 18) !== 0) {
|
|
74230
|
+
return false;
|
|
74231
|
+
}
|
|
74232
|
+
return entry === "config.toml" ? info.isFile() && info.nlink === 1 : info.isDirectory();
|
|
74233
|
+
}
|
|
74234
|
+
async function sweepCodexAuthCell(cell2, onRepair, rmImpl) {
|
|
74235
|
+
const removeImpl = rmImpl ?? ((target) => rm10(target, { recursive: true, force: true }));
|
|
74236
|
+
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
74237
|
+
const entries = await readdir6(cell2);
|
|
74238
|
+
for (const entry of entries) {
|
|
74239
|
+
const entryPath = path14.join(cell2, entry);
|
|
74240
|
+
const info = await lstat4(entryPath).catch(() => null);
|
|
74241
|
+
const isOwnedAuthJson = entry === "auth.json" && info !== null && classifyAuthJsonStat(
|
|
74242
|
+
{
|
|
74243
|
+
isSymbolicLink: info.isSymbolicLink(),
|
|
74244
|
+
isFile: info.isFile(),
|
|
74245
|
+
nlink: info.nlink,
|
|
74246
|
+
uid: info.uid,
|
|
74247
|
+
mode: info.mode,
|
|
74248
|
+
size: info.size
|
|
74249
|
+
},
|
|
74250
|
+
selfUid
|
|
74251
|
+
).kind === "owned";
|
|
74252
|
+
if (isOwnedAuthJson) {
|
|
74253
|
+
continue;
|
|
74254
|
+
}
|
|
74255
|
+
const isRoutine = info !== null && isRoutineCodexBookkeepingShape(entry, info, selfUid);
|
|
74256
|
+
if (!isRoutine) {
|
|
74257
|
+
onRepair?.(`codex credential cell ${cell2}: removing unexpected entry ${JSON.stringify(entry)}`);
|
|
74258
|
+
}
|
|
74259
|
+
try {
|
|
74260
|
+
await removeImpl(entryPath);
|
|
74261
|
+
} catch (error51) {
|
|
74262
|
+
const code = error51.code ?? "unknown";
|
|
74263
|
+
throw new Error(
|
|
74264
|
+
`refusing to use the codex credential cell: could not remove ${JSON.stringify(entry)} from the codex credential cell ${cell2} (${code}). The credential-cell sweep must not fail open \u2014 clear the entry (e.g. \`chflags -R nouchg\`, then \`rm -rf\`) and retry.`
|
|
74265
|
+
);
|
|
74266
|
+
}
|
|
74267
|
+
}
|
|
74268
|
+
}
|
|
74269
|
+
var CODEX_PROBE_CREDENTIAL_CELL_LOCK_WAIT_MS = 5e3;
|
|
74270
|
+
async function probeCodexLoginStatus(codexBin, cell2, opts) {
|
|
74271
|
+
const spawnImpl = opts?.spawnImpl ?? spawnCapture;
|
|
74272
|
+
const acquireLockImpl = opts?.acquireLockImpl ?? acquireCodexHomeLock;
|
|
74273
|
+
const waitMs = opts?.credentialCellLockWaitMs ?? CODEX_PROBE_CREDENTIAL_CELL_LOCK_WAIT_MS;
|
|
74274
|
+
let lock;
|
|
74275
|
+
try {
|
|
74276
|
+
lock = await acquireLockImpl(cell2, { waitMs, label: "codex credential cell" });
|
|
74277
|
+
} catch (error51) {
|
|
74278
|
+
opts?.onProbeError?.(
|
|
74279
|
+
`codex credential cell ${cell2}: could not acquire the credential-cell lock (${redactForLog(error51 instanceof Error ? error51.message : String(error51))}); could not determine login status`
|
|
74280
|
+
);
|
|
74281
|
+
return { kind: "command_failed" };
|
|
74282
|
+
}
|
|
74283
|
+
if (lock === null) {
|
|
74284
|
+
opts?.onProbeError?.(
|
|
74285
|
+
`codex credential cell ${cell2}: another process is using this cell (a running codex turn, or a concurrent device-auth login); could not determine login status without racing it`
|
|
74286
|
+
);
|
|
74287
|
+
return { kind: "command_failed" };
|
|
74288
|
+
}
|
|
74289
|
+
try {
|
|
74290
|
+
try {
|
|
74291
|
+
await ensureVerifiedCodexHomeDir(cell2, opts?.onRepair, "codex credential cell");
|
|
74292
|
+
await sweepCodexAuthCell(cell2, opts?.onRepair, opts?.rmImpl);
|
|
74293
|
+
} catch (error51) {
|
|
74294
|
+
opts?.onProbeError?.(
|
|
74295
|
+
`codex credential cell ${cell2}: could not verify or sweep the cell before probing login status (${redactForLog(error51 instanceof Error ? error51.message : String(error51))}); could not determine login status`
|
|
74296
|
+
);
|
|
74297
|
+
return { kind: "command_failed" };
|
|
74298
|
+
}
|
|
74299
|
+
const env = buildCodexAuthEnv(opts?.env ?? process.env, cell2);
|
|
74300
|
+
let result;
|
|
74301
|
+
try {
|
|
74302
|
+
result = await spawnImpl(codexBin, ["login", "status"], {
|
|
74303
|
+
env,
|
|
74304
|
+
...opts?.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
74305
|
+
...opts?.killGraceMs !== void 0 ? { killGraceMs: opts.killGraceMs } : {},
|
|
74306
|
+
...opts?.containmentForTest !== void 0 ? { containment: opts.containmentForTest } : {}
|
|
74307
|
+
});
|
|
74308
|
+
} catch (error51) {
|
|
74309
|
+
opts?.onProbeError?.(describeSpawnFailure("codex login status", error51));
|
|
74310
|
+
if (typeof error51 === "object" && error51 !== null && "codexProbeExitUnconfirmedMs" in error51) {
|
|
74311
|
+
return { kind: "exit_unconfirmed" };
|
|
74312
|
+
}
|
|
74313
|
+
return { kind: "command_failed" };
|
|
74314
|
+
}
|
|
74315
|
+
return parseCodexLoginStatus(result);
|
|
74316
|
+
} finally {
|
|
74317
|
+
await lock.release();
|
|
74318
|
+
}
|
|
74319
|
+
}
|
|
74320
|
+
var CODEX_LOGIN_CREDENTIAL_CELL_LOCK_WAIT_MS = 5e3;
|
|
74321
|
+
async function hasUsableCodexAuthCredential(cell2) {
|
|
74322
|
+
const info = await lstat4(path14.join(cell2, "auth.json")).catch(() => null);
|
|
74323
|
+
if (info === null) {
|
|
74324
|
+
return false;
|
|
74325
|
+
}
|
|
74326
|
+
const selfUid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
74327
|
+
return classifyAuthJsonStat(
|
|
74328
|
+
{
|
|
74329
|
+
isSymbolicLink: info.isSymbolicLink(),
|
|
74330
|
+
isFile: info.isFile(),
|
|
74331
|
+
nlink: info.nlink,
|
|
74332
|
+
uid: info.uid,
|
|
74333
|
+
mode: info.mode,
|
|
74334
|
+
size: info.size
|
|
74335
|
+
},
|
|
74336
|
+
selfUid
|
|
74337
|
+
).kind === "owned";
|
|
74338
|
+
}
|
|
74339
|
+
async function captureCodexAuthJsonIdentity(cell2) {
|
|
74340
|
+
const info = await lstat4(path14.join(cell2, "auth.json"), { bigint: true }).catch(() => null);
|
|
74341
|
+
if (info === null) {
|
|
74342
|
+
return null;
|
|
74343
|
+
}
|
|
74344
|
+
return { ino: info.ino, mtimeNs: info.mtimeNs, size: info.size };
|
|
74345
|
+
}
|
|
74346
|
+
function isEnoentError(error51) {
|
|
74347
|
+
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
74348
|
+
}
|
|
74349
|
+
async function runCodexDeviceAuthLogin(options) {
|
|
74350
|
+
const acquireLockImpl = options.acquireLockImpl ?? acquireCodexHomeLock;
|
|
74351
|
+
const spawnLoginImpl = options.spawnLoginImpl ?? spawnLogin;
|
|
74352
|
+
const commitSeedMarkerImpl = options.commitSeedMarkerImpl ?? ((cell2) => commitCodexHomeSeedMarker(cell2, { required: true }));
|
|
74353
|
+
const waitMs = options.credentialCellLockWaitMs ?? CODEX_LOGIN_CREDENTIAL_CELL_LOCK_WAIT_MS;
|
|
74354
|
+
const stderr = options.io?.stderr ?? process.stderr;
|
|
74355
|
+
try {
|
|
74356
|
+
const lock = await acquireLockImpl(options.cell, { waitMs, label: "codex credential cell" });
|
|
74357
|
+
if (lock === null) {
|
|
74358
|
+
stderr.write(
|
|
74359
|
+
"another process is using this runner's codex credential cell (a running codex turn, or a concurrent device-auth login); wait for it to finish and retry.\n"
|
|
74360
|
+
);
|
|
74361
|
+
return 1;
|
|
74362
|
+
}
|
|
74363
|
+
try {
|
|
74364
|
+
await ensureVerifiedCodexHomeDir(options.cell, options.onRepair, "codex credential cell");
|
|
74365
|
+
const markerPath = path14.join(path14.dirname(options.cell), `${path14.basename(options.cell)}.seeded`);
|
|
74366
|
+
try {
|
|
74367
|
+
await rm10(markerPath, { force: true });
|
|
74368
|
+
} catch (error51) {
|
|
74369
|
+
throw new Error(
|
|
74370
|
+
`refusing to run codex device-auth login: could not clear the existing auth marker ${markerPath}; the server must keep codex work blocked until login completes (${redactForLog(error51 instanceof Error ? error51.message : String(error51))})`
|
|
74371
|
+
);
|
|
74372
|
+
}
|
|
74373
|
+
await sweepCodexAuthCell(options.cell, options.onRepair);
|
|
74374
|
+
const preLoginAuthJsonIdentity = await captureCodexAuthJsonIdentity(options.cell);
|
|
74375
|
+
let loginCode;
|
|
74376
|
+
try {
|
|
74377
|
+
loginCode = await spawnLoginImpl(options.codexBin, ["login", "--device-auth"], {
|
|
74378
|
+
env: buildCodexAuthEnv(options.env, options.cell),
|
|
74379
|
+
...options.io ? { io: options.io } : {},
|
|
74380
|
+
// FIX 8 (DER-3338 round 2, P3): default to "to-stderr", NOT spawnLogin's own "inherit"
|
|
74381
|
+
// default. Conservative-by-default (invariant 10): an unspecified caller may be running
|
|
74382
|
+
// under `--json`, and the device-auth URL/one-time code are human copy that must never land
|
|
74383
|
+
// on stdout and corrupt a machine-readable stream. A caller that specifically wants the
|
|
74384
|
+
// ceremony on an inherited TTY stdout (interactive standalone use) opts in explicitly.
|
|
74385
|
+
ceremonyStdout: options.ceremonyStdout ?? "to-stderr"
|
|
74386
|
+
});
|
|
74387
|
+
} catch (error51) {
|
|
74388
|
+
if (isEnoentError(error51)) {
|
|
74389
|
+
stderr.write("codex CLI not found on PATH; install codex first.\n");
|
|
74390
|
+
return 1;
|
|
74391
|
+
}
|
|
74392
|
+
throw error51;
|
|
74393
|
+
}
|
|
74394
|
+
if (loginCode !== 0) {
|
|
74395
|
+
stderr.write(
|
|
74396
|
+
`codex login --device-auth exited ${loginCode}; the runner credential was not provisioned.
|
|
74397
|
+
`
|
|
74398
|
+
);
|
|
74399
|
+
return 1;
|
|
74400
|
+
}
|
|
74401
|
+
const postLoginUsable = await hasUsableCodexAuthCredential(options.cell);
|
|
74402
|
+
const postLoginAuthJsonIdentity = postLoginUsable ? await captureCodexAuthJsonIdentity(options.cell) : null;
|
|
74403
|
+
const credentialWasRenewed = preLoginAuthJsonIdentity === null || postLoginAuthJsonIdentity !== null && (preLoginAuthJsonIdentity.ino !== postLoginAuthJsonIdentity.ino || preLoginAuthJsonIdentity.mtimeNs !== postLoginAuthJsonIdentity.mtimeNs || preLoginAuthJsonIdentity.size !== postLoginAuthJsonIdentity.size);
|
|
74404
|
+
if (!postLoginUsable || !credentialWasRenewed) {
|
|
74405
|
+
stderr.write(
|
|
74406
|
+
postLoginUsable ? "codex login --device-auth reported success, but left the existing credential in the runner's codex credential cell untouched; the runner's authentication was not renewed. Retry the login.\n" : "codex login --device-auth reported success, but no usable credential is present in the runner's codex credential cell; the runner is NOT authenticated. Retry the login.\n"
|
|
74407
|
+
);
|
|
74408
|
+
return 1;
|
|
74409
|
+
}
|
|
74410
|
+
try {
|
|
74411
|
+
await commitSeedMarkerImpl(options.cell);
|
|
74412
|
+
} catch (error51) {
|
|
74413
|
+
stderr.write(
|
|
74414
|
+
`codex login --device-auth completed, but the runner auth marker could not be committed; the server will keep codex work blocked until it is. Retry the login: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
74415
|
+
`
|
|
74416
|
+
);
|
|
74417
|
+
return 1;
|
|
74418
|
+
}
|
|
74419
|
+
return 0;
|
|
74420
|
+
} finally {
|
|
74421
|
+
await lock.release();
|
|
74422
|
+
}
|
|
74423
|
+
} catch (error51) {
|
|
74424
|
+
stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
74425
|
+
`);
|
|
74426
|
+
return 1;
|
|
74427
|
+
}
|
|
74428
|
+
}
|
|
74429
|
+
async function evaluateCodexAuth(options) {
|
|
74430
|
+
const probeOpts = {
|
|
74431
|
+
...options.env !== void 0 ? { env: options.env } : {},
|
|
74432
|
+
...options.spawnImpl !== void 0 ? { spawnImpl: options.spawnImpl } : {},
|
|
74433
|
+
...options.onProbeError !== void 0 ? { onProbeError: options.onProbeError } : {}
|
|
74434
|
+
};
|
|
74435
|
+
const statusProbeOpts = {
|
|
74436
|
+
...probeOpts,
|
|
74437
|
+
...options.acquireLockImpl !== void 0 ? { acquireLockImpl: options.acquireLockImpl } : {},
|
|
74438
|
+
...options.credentialCellLockWaitMs !== void 0 ? { credentialCellLockWaitMs: options.credentialCellLockWaitMs } : {},
|
|
74439
|
+
...options.onRepair !== void 0 ? { onRepair: options.onRepair } : {}
|
|
74440
|
+
};
|
|
74441
|
+
const version4 = await probeCodexVersion(options.codexBin, probeOpts);
|
|
74442
|
+
const cellState = await inspectCodexAuthCell(options.cell);
|
|
74443
|
+
const versionRefused = version4.kind !== "supported";
|
|
74444
|
+
const status = !versionRefused && (cellState.kind === "empty" || cellState.kind === "owned") ? await probeCodexLoginStatus(options.codexBin, options.cell, statusProbeOpts) : null;
|
|
74445
|
+
const decision = decideCodexAuth(version4, cellState, status ?? { kind: "unrecognized" });
|
|
74446
|
+
return { version: version4, cell: cellState, status, decision };
|
|
74447
|
+
}
|
|
74448
|
+
|
|
74449
|
+
// src/runner-setup-orchestrator.ts
|
|
73419
74450
|
var SETUP_SIDECAR_VERSION = 1;
|
|
73420
74451
|
var SETUP_STAGES_IN_ORDER = [
|
|
73421
74452
|
runnerSetupStageSchema.enum.pairing,
|
|
@@ -73484,13 +74515,13 @@ var runnerSetupSessionEnvelopeSchema = external_exports.object({
|
|
|
73484
74515
|
action_required: runnerSetupActionRequiredSchema.nullable()
|
|
73485
74516
|
}).passthrough();
|
|
73486
74517
|
function runnerSetupSidecarBaseDir(homeDir) {
|
|
73487
|
-
return
|
|
74518
|
+
return path15.join(homeDir, ".rost", "runner", "setup");
|
|
73488
74519
|
}
|
|
73489
74520
|
function sidecarFilePath(homeDir, setupId) {
|
|
73490
|
-
return
|
|
74521
|
+
return path15.join(runnerSetupSidecarBaseDir(homeDir), `${setupId}.json`);
|
|
73491
74522
|
}
|
|
73492
74523
|
function currentSidecarPath(homeDir) {
|
|
73493
|
-
return
|
|
74524
|
+
return path15.join(runnerSetupSidecarBaseDir(homeDir), "current");
|
|
73494
74525
|
}
|
|
73495
74526
|
async function readPrivateTextFile(filePath) {
|
|
73496
74527
|
let handle = null;
|
|
@@ -73559,7 +74590,7 @@ async function writeSidecar(homeDir, sidecar, options = {}) {
|
|
|
73559
74590
|
}
|
|
73560
74591
|
}
|
|
73561
74592
|
async function clearCurrentSidecar(homeDir) {
|
|
73562
|
-
await
|
|
74593
|
+
await rm11(currentSidecarPath(homeDir), { force: true });
|
|
73563
74594
|
}
|
|
73564
74595
|
function makeFreshSidecar(session, appUrl2, stateFile, now) {
|
|
73565
74596
|
return {
|
|
@@ -73811,8 +74842,8 @@ function parseArgs2(args, options = {}) {
|
|
|
73811
74842
|
return { ok: true, json: json2, agent, yes, noExecute, execute: execute2, runtime, userCode, setupId, stateFile, explicitStateFile, timeoutMs, rest };
|
|
73812
74843
|
}
|
|
73813
74844
|
function durableSetupStateFile(homeDir, appUrl2) {
|
|
73814
|
-
const key =
|
|
73815
|
-
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`);
|
|
73816
74847
|
}
|
|
73817
74848
|
function isEphemeralStateFile(stateFile, appUrl2) {
|
|
73818
74849
|
return sameStateFile(stateFile, defaultRunnerStateFile(appUrl2));
|
|
@@ -73870,11 +74901,11 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
|
|
|
73870
74901
|
actionRequired: { kind: "manual_service_install", runtime: null, url: null, user_code: null, expires_at: null }
|
|
73871
74902
|
};
|
|
73872
74903
|
}
|
|
73873
|
-
if (!
|
|
74904
|
+
if (!path15.isAbsolute(stateFile)) {
|
|
73874
74905
|
return {
|
|
73875
74906
|
ok: false,
|
|
73876
74907
|
exitCode: 1,
|
|
73877
|
-
message: `The runner state file ${stateFile} is a relative path, and a launchd service resolves it against its own working directory, not yours. Re-run setup with an absolute path, for example \`--state-file "${
|
|
74908
|
+
message: `The runner state file ${stateFile} is a relative path, and a launchd service resolves it against its own working directory, not yours. Re-run setup with an absolute path, for example \`--state-file "${path15.resolve(stateFile)}"\`.`
|
|
73878
74909
|
};
|
|
73879
74910
|
}
|
|
73880
74911
|
if (isEphemeralStateFile(stateFile, appUrl2)) {
|
|
@@ -73908,9 +74939,9 @@ async function preflightServiceInstall(stateFile, appUrl2, options, own) {
|
|
|
73908
74939
|
}
|
|
73909
74940
|
function formatUsage(binName) {
|
|
73910
74941
|
return [
|
|
73911
|
-
`Usage: ${binName} runner setup start --runtime claude [--user-code <code>] [--json] [--agent] [--state-file <path>] [--timeout-ms <ms>] [--no-execute | --execute]`,
|
|
74942
|
+
`Usage: ${binName} runner setup start --runtime claude|codex [--user-code <code>] [--json] [--agent] [--state-file <path>] [--timeout-ms <ms>] [--no-execute | --execute]`,
|
|
73912
74943
|
` ${binName} runner setup status [--setup-id <id>] [--json]`,
|
|
73913
|
-
` ${binName} runner setup resume [--setup-id <id>] [--runtime claude] [--user-code <code>] [--json] [--agent] [--timeout-ms <ms>] [--no-execute | --execute]`,
|
|
74944
|
+
` ${binName} runner setup resume [--setup-id <id>] [--runtime claude|codex] [--user-code <code>] [--json] [--agent] [--timeout-ms <ms>] [--no-execute | --execute]`,
|
|
73914
74945
|
` ${binName} runner setup cancel [--setup-id <id>] [--json]`
|
|
73915
74946
|
].join("\n");
|
|
73916
74947
|
}
|
|
@@ -73922,9 +74953,6 @@ function validateRuntimeFlag(value) {
|
|
|
73922
74953
|
if (!parsed.success) {
|
|
73923
74954
|
return { ok: false, message: `Unsupported runtime: ${value}.` };
|
|
73924
74955
|
}
|
|
73925
|
-
if (parsed.data === "codex") {
|
|
73926
|
-
return { ok: false, message: "Codex setup is not yet available." };
|
|
73927
|
-
}
|
|
73928
74956
|
return { ok: true, runtime: parsed.data };
|
|
73929
74957
|
}
|
|
73930
74958
|
function validateAgentUserCode(agent, userCode, verb, io, json2) {
|
|
@@ -73965,8 +74993,15 @@ async function validateProviderConsentCheckpoint(sidecar, homeDir) {
|
|
|
73965
74993
|
return false;
|
|
73966
74994
|
}
|
|
73967
74995
|
const runnerId = pairingCheckpoint.runner_id;
|
|
73968
|
-
|
|
73969
|
-
|
|
74996
|
+
if (provenRuntimes.length !== 1) return false;
|
|
74997
|
+
if (provenRuntimes[0] === "claude") {
|
|
74998
|
+
const cell3 = stableClaudeHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
|
|
74999
|
+
return cell3 === checkpoint.cell && !await claudeCellNeedsLogin(cell3);
|
|
75000
|
+
}
|
|
75001
|
+
const cell2 = stableCodexHomeDir({ runner_id: runnerId, runner_secret: "" }, homeDir);
|
|
75002
|
+
if (cell2 !== checkpoint.cell) return false;
|
|
75003
|
+
const auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell2 });
|
|
75004
|
+
return auth.decision.kind === "proceed_to_validation";
|
|
73970
75005
|
}
|
|
73971
75006
|
async function validateServiceInstallCheckpoint(sidecar, options, execute2) {
|
|
73972
75007
|
const checkpoint = sidecar.stages.service_install;
|
|
@@ -74072,10 +75107,13 @@ async function runPairingStage(sidecar, userCode, agent, fetchImpl, appUrl2, hom
|
|
|
74072
75107
|
const newState = {
|
|
74073
75108
|
runner_id: claimResult.runnerId,
|
|
74074
75109
|
runner_secret: claimResult.runnerSecret,
|
|
75110
|
+
active_setup_id: sidecar.setup_id,
|
|
75111
|
+
...sidecar.selected_runtimes[0] !== void 0 ? { setup_runtime: sidecar.selected_runtimes[0] } : {},
|
|
74075
75112
|
...claimResult.tenantId !== null ? { tenant_id: claimResult.tenantId } : {},
|
|
74076
75113
|
...claimResult.name !== null ? { name: claimResult.name } : {},
|
|
74077
75114
|
...claimResult.serviceKeyPrivate !== void 0 ? { service_key_private: claimResult.serviceKeyPrivate } : {},
|
|
74078
|
-
...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {}
|
|
75115
|
+
...claimResult.serviceKeyPublic !== void 0 ? { service_key_public: claimResult.serviceKeyPublic } : {},
|
|
75116
|
+
...claimResult.serviceKeyGeneration !== void 0 ? { service_key_generation: claimResult.serviceKeyGeneration } : {}
|
|
74079
75117
|
};
|
|
74080
75118
|
await saveState(sidecar.state_file, newState);
|
|
74081
75119
|
await reapSupersededRunnerHomes(priorState, newState.runner_id, homeDir);
|
|
@@ -74118,14 +75156,33 @@ async function claimRunnerPairingCodeSafe(fetchImpl, appUrl2, userCode, serviceK
|
|
|
74118
75156
|
}
|
|
74119
75157
|
}
|
|
74120
75158
|
async function runProviderConsentStage(sidecar, homeDir, appUrl2, env, io, loginImpl, now, allowLocalCeremony, json2) {
|
|
74121
|
-
if (!sidecar.selected_runtimes.includes(runnerSetupRuntimeSchema.enum.claude)) {
|
|
74122
|
-
return { kind: "login_failed", exitCode: 1, message: "Codex setup is not yet available." };
|
|
74123
|
-
}
|
|
74124
75159
|
const pairing = sidecar.stages.pairing;
|
|
74125
75160
|
if (pairing === void 0) {
|
|
74126
75161
|
return { kind: "login_failed", exitCode: 1, message: "Pairing checkpoint missing before provider consent." };
|
|
74127
75162
|
}
|
|
74128
75163
|
const state = { runner_id: pairing.runner_id, runner_secret: "" };
|
|
75164
|
+
if (sidecar.selected_runtimes.length === 1 && sidecar.selected_runtimes[0] === "codex") {
|
|
75165
|
+
const cell3 = stableCodexHomeDir(state, homeDir);
|
|
75166
|
+
let auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell3, env });
|
|
75167
|
+
if (auth.decision.kind === "proceed_to_validation") {
|
|
75168
|
+
return { kind: "done", sidecar: addStageCheckpoint(sidecar, runnerSetupStageSchema.enum.provider_consent, { cell: cell3, runtimes: ["codex"] }, now) };
|
|
75169
|
+
}
|
|
75170
|
+
if (auth.decision.kind !== "device_auth_login_required" || !allowLocalCeremony) {
|
|
75171
|
+
return { kind: "needs_human_action" };
|
|
75172
|
+
}
|
|
75173
|
+
const loginExit2 = await runCodexDeviceAuthLogin({
|
|
75174
|
+
codexBin: "codex",
|
|
75175
|
+
cell: cell3,
|
|
75176
|
+
env,
|
|
75177
|
+
io: { stdout: io.stderr, stderr: io.stderr },
|
|
75178
|
+
ceremonyStdout: json2 ? "to-stderr" : "inherit"
|
|
75179
|
+
});
|
|
75180
|
+
if (loginExit2 !== 0) {
|
|
75181
|
+
return { kind: "login_failed", exitCode: 1, message: `Codex provider consent failed or was declined (exit ${loginExit2}). Re-run resume to retry.` };
|
|
75182
|
+
}
|
|
75183
|
+
auth = await evaluateCodexAuth({ codexBin: "codex", cell: cell3, env });
|
|
75184
|
+
return auth.decision.kind === "proceed_to_validation" ? { kind: "done", sidecar: addStageCheckpoint(sidecar, runnerSetupStageSchema.enum.provider_consent, { cell: cell3, runtimes: ["codex"] }, now) } : { kind: "login_failed", exitCode: 1, message: "Codex credential cell is not ready after login." };
|
|
75185
|
+
}
|
|
74129
75186
|
const cell2 = stableClaudeHomeDir(state, homeDir);
|
|
74130
75187
|
if (!await claudeCellNeedsLogin(cell2)) {
|
|
74131
75188
|
const provenRuntimes = [runnerSetupRuntimeSchema.enum.claude];
|