@rosthq/cli 0.7.29 → 0.7.31
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/cli-version.d.ts +2 -0
- package/dist/cli-version.d.ts.map +1 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/index.js +493 -142
- package/dist/index.js.map +4 -4
- package/dist/runner-sandbox.d.ts +94 -0
- package/dist/runner-sandbox.d.ts.map +1 -0
- package/dist/runner-serve.d.ts +1 -0
- package/dist/runner-serve.d.ts.map +1 -1
- package/dist/update-check.d.ts.map +1 -1
- 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(path6, preserveJsx) {
|
|
476
|
+
if (typeof path6 === "string" && /^\.\.?\//.test(path6)) {
|
|
477
|
+
return path6.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 path6;
|
|
482
482
|
}
|
|
483
483
|
var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
|
|
484
484
|
var init_tslib_es6 = __esm({
|
|
@@ -13257,7 +13257,7 @@ var require_main3 = __commonJS({
|
|
|
13257
13257
|
});
|
|
13258
13258
|
|
|
13259
13259
|
// src/index.ts
|
|
13260
|
-
import { realpathSync as
|
|
13260
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
13261
13261
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
13262
13262
|
|
|
13263
13263
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
@@ -14026,10 +14026,10 @@ function mergeDefs(...defs) {
|
|
|
14026
14026
|
function cloneDef(schema) {
|
|
14027
14027
|
return mergeDefs(schema._zod.def);
|
|
14028
14028
|
}
|
|
14029
|
-
function getElementAtPath(obj,
|
|
14030
|
-
if (!
|
|
14029
|
+
function getElementAtPath(obj, path6) {
|
|
14030
|
+
if (!path6)
|
|
14031
14031
|
return obj;
|
|
14032
|
-
return
|
|
14032
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
14033
14033
|
}
|
|
14034
14034
|
function promiseAllObject(promisesObj) {
|
|
14035
14035
|
const keys = Object.keys(promisesObj);
|
|
@@ -14438,11 +14438,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
14438
14438
|
}
|
|
14439
14439
|
return false;
|
|
14440
14440
|
}
|
|
14441
|
-
function prefixIssues(
|
|
14441
|
+
function prefixIssues(path6, issues) {
|
|
14442
14442
|
return issues.map((iss) => {
|
|
14443
14443
|
var _a3;
|
|
14444
14444
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
14445
|
-
iss.path.unshift(
|
|
14445
|
+
iss.path.unshift(path6);
|
|
14446
14446
|
return iss;
|
|
14447
14447
|
});
|
|
14448
14448
|
}
|
|
@@ -14589,16 +14589,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
14589
14589
|
}
|
|
14590
14590
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
14591
14591
|
const fieldErrors = { _errors: [] };
|
|
14592
|
-
const processError = (error52,
|
|
14592
|
+
const processError = (error52, path6 = []) => {
|
|
14593
14593
|
for (const issue2 of error52.issues) {
|
|
14594
14594
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
14595
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
14595
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
14596
14596
|
} else if (issue2.code === "invalid_key") {
|
|
14597
|
-
processError({ issues: issue2.issues }, [...
|
|
14597
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
14598
14598
|
} else if (issue2.code === "invalid_element") {
|
|
14599
|
-
processError({ issues: issue2.issues }, [...
|
|
14599
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
14600
14600
|
} else {
|
|
14601
|
-
const fullpath = [...
|
|
14601
|
+
const fullpath = [...path6, ...issue2.path];
|
|
14602
14602
|
if (fullpath.length === 0) {
|
|
14603
14603
|
fieldErrors._errors.push(mapper(issue2));
|
|
14604
14604
|
} else {
|
|
@@ -14625,17 +14625,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
14625
14625
|
}
|
|
14626
14626
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
14627
14627
|
const result = { errors: [] };
|
|
14628
|
-
const processError = (error52,
|
|
14628
|
+
const processError = (error52, path6 = []) => {
|
|
14629
14629
|
var _a3, _b;
|
|
14630
14630
|
for (const issue2 of error52.issues) {
|
|
14631
14631
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
14632
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
14632
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
14633
14633
|
} else if (issue2.code === "invalid_key") {
|
|
14634
|
-
processError({ issues: issue2.issues }, [...
|
|
14634
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
14635
14635
|
} else if (issue2.code === "invalid_element") {
|
|
14636
|
-
processError({ issues: issue2.issues }, [...
|
|
14636
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
14637
14637
|
} else {
|
|
14638
|
-
const fullpath = [...
|
|
14638
|
+
const fullpath = [...path6, ...issue2.path];
|
|
14639
14639
|
if (fullpath.length === 0) {
|
|
14640
14640
|
result.errors.push(mapper(issue2));
|
|
14641
14641
|
continue;
|
|
@@ -14667,8 +14667,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
14667
14667
|
}
|
|
14668
14668
|
function toDotPath(_path) {
|
|
14669
14669
|
const segs = [];
|
|
14670
|
-
const
|
|
14671
|
-
for (const seg of
|
|
14670
|
+
const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
14671
|
+
for (const seg of path6) {
|
|
14672
14672
|
if (typeof seg === "number")
|
|
14673
14673
|
segs.push(`[${seg}]`);
|
|
14674
14674
|
else if (typeof seg === "symbol")
|
|
@@ -27360,13 +27360,13 @@ function resolveRef(ref, ctx) {
|
|
|
27360
27360
|
if (!ref.startsWith("#")) {
|
|
27361
27361
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
27362
27362
|
}
|
|
27363
|
-
const
|
|
27364
|
-
if (
|
|
27363
|
+
const path6 = ref.slice(1).split("/").filter(Boolean);
|
|
27364
|
+
if (path6.length === 0) {
|
|
27365
27365
|
return ctx.rootSchema;
|
|
27366
27366
|
}
|
|
27367
27367
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
27368
|
-
if (
|
|
27369
|
-
const key =
|
|
27368
|
+
if (path6[0] === defsKey) {
|
|
27369
|
+
const key = path6[1];
|
|
27370
27370
|
if (!key || !ctx.defs[key]) {
|
|
27371
27371
|
throw new Error(`Reference not found: ${ref}`);
|
|
27372
27372
|
}
|
|
@@ -27828,7 +27828,7 @@ var CommandClient = class {
|
|
|
27828
27828
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
27829
27829
|
}
|
|
27830
27830
|
async execute(commandId, body = {}, options = {}) {
|
|
27831
|
-
const
|
|
27831
|
+
const path6 = `/api/commands/${encodeURIComponent(commandId)}`;
|
|
27832
27832
|
const headers = {
|
|
27833
27833
|
authorization: `Bearer ${this.token}`,
|
|
27834
27834
|
"content-type": "application/json"
|
|
@@ -27836,7 +27836,7 @@ var CommandClient = class {
|
|
|
27836
27836
|
if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
|
|
27837
27837
|
headers["x-rost-seat"] = options.targetSeatId;
|
|
27838
27838
|
}
|
|
27839
|
-
const response = await this.fetchImpl(`${this.appUrl}${
|
|
27839
|
+
const response = await this.fetchImpl(`${this.appUrl}${path6}`, {
|
|
27840
27840
|
method: "POST",
|
|
27841
27841
|
headers,
|
|
27842
27842
|
body: JSON.stringify(body)
|
|
@@ -27849,7 +27849,7 @@ var CommandClient = class {
|
|
|
27849
27849
|
const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
|
|
27850
27850
|
throw new CommandClientError(
|
|
27851
27851
|
response.status,
|
|
27852
|
-
`server returned HTTP ${response.status} (non-JSON) from ${
|
|
27852
|
+
`server returned HTTP ${response.status} (non-JSON) from ${path6}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
|
|
27853
27853
|
);
|
|
27854
27854
|
}
|
|
27855
27855
|
const parsed = commandResponseSchema.parse(raw);
|
|
@@ -33064,8 +33064,8 @@ var IcebergError = class extends Error {
|
|
|
33064
33064
|
return this.status === 419;
|
|
33065
33065
|
}
|
|
33066
33066
|
};
|
|
33067
|
-
function buildUrl(baseUrl,
|
|
33068
|
-
const url2 = new URL(
|
|
33067
|
+
function buildUrl(baseUrl, path6, query) {
|
|
33068
|
+
const url2 = new URL(path6, baseUrl);
|
|
33069
33069
|
if (query) {
|
|
33070
33070
|
for (const [key, value] of Object.entries(query)) {
|
|
33071
33071
|
if (value !== void 0) {
|
|
@@ -33095,12 +33095,12 @@ function createFetchClient(options) {
|
|
|
33095
33095
|
return {
|
|
33096
33096
|
async request({
|
|
33097
33097
|
method,
|
|
33098
|
-
path:
|
|
33098
|
+
path: path6,
|
|
33099
33099
|
query,
|
|
33100
33100
|
body,
|
|
33101
33101
|
headers
|
|
33102
33102
|
}) {
|
|
33103
|
-
const url2 = buildUrl(options.baseUrl,
|
|
33103
|
+
const url2 = buildUrl(options.baseUrl, path6, query);
|
|
33104
33104
|
const authHeaders = await buildAuthHeaders(options.auth);
|
|
33105
33105
|
const res = await fetchFn(url2, {
|
|
33106
33106
|
method,
|
|
@@ -33962,7 +33962,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
33962
33962
|
* @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
|
|
33963
33963
|
* @param fileBody The body of the file to be stored in the bucket.
|
|
33964
33964
|
*/
|
|
33965
|
-
async uploadOrUpdate(method,
|
|
33965
|
+
async uploadOrUpdate(method, path6, fileBody, fileOptions) {
|
|
33966
33966
|
var _this = this;
|
|
33967
33967
|
return _this.handleOperation(async () => {
|
|
33968
33968
|
let body;
|
|
@@ -33986,7 +33986,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
33986
33986
|
if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
|
|
33987
33987
|
}
|
|
33988
33988
|
if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
|
|
33989
|
-
const cleanPath = _this._removeEmptyFolders(
|
|
33989
|
+
const cleanPath = _this._removeEmptyFolders(path6);
|
|
33990
33990
|
const _path = _this._getFinalPath(cleanPath);
|
|
33991
33991
|
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 } : {}));
|
|
33992
33992
|
return {
|
|
@@ -34063,8 +34063,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34063
34063
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34064
34064
|
* - 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.
|
|
34065
34065
|
*/
|
|
34066
|
-
async upload(
|
|
34067
|
-
return this.uploadOrUpdate("POST",
|
|
34066
|
+
async upload(path6, fileBody, fileOptions) {
|
|
34067
|
+
return this.uploadOrUpdate("POST", path6, fileBody, fileOptions);
|
|
34068
34068
|
}
|
|
34069
34069
|
/**
|
|
34070
34070
|
* Upload a file with a token generated from `createSignedUploadUrl`.
|
|
@@ -34104,9 +34104,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34104
34104
|
* - `objects` table permissions: none
|
|
34105
34105
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34106
34106
|
*/
|
|
34107
|
-
async uploadToSignedUrl(
|
|
34107
|
+
async uploadToSignedUrl(path6, token, fileBody, fileOptions) {
|
|
34108
34108
|
var _this3 = this;
|
|
34109
|
-
const cleanPath = _this3._removeEmptyFolders(
|
|
34109
|
+
const cleanPath = _this3._removeEmptyFolders(path6);
|
|
34110
34110
|
const _path = _this3._getFinalPath(cleanPath);
|
|
34111
34111
|
const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
|
|
34112
34112
|
url2.searchParams.set("token", token);
|
|
@@ -34175,10 +34175,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34175
34175
|
* - `objects` table permissions: `insert`
|
|
34176
34176
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34177
34177
|
*/
|
|
34178
|
-
async createSignedUploadUrl(
|
|
34178
|
+
async createSignedUploadUrl(path6, options) {
|
|
34179
34179
|
var _this4 = this;
|
|
34180
34180
|
return _this4.handleOperation(async () => {
|
|
34181
|
-
let _path = _this4._getFinalPath(
|
|
34181
|
+
let _path = _this4._getFinalPath(path6);
|
|
34182
34182
|
const headers = _objectSpread22({}, _this4.headers);
|
|
34183
34183
|
if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
|
|
34184
34184
|
const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
|
|
@@ -34187,7 +34187,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34187
34187
|
if (!token) throw new StorageError("No token returned by API");
|
|
34188
34188
|
return {
|
|
34189
34189
|
signedUrl: url2.toString(),
|
|
34190
|
-
path:
|
|
34190
|
+
path: path6,
|
|
34191
34191
|
token
|
|
34192
34192
|
};
|
|
34193
34193
|
});
|
|
@@ -34247,8 +34247,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34247
34247
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34248
34248
|
* - 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.
|
|
34249
34249
|
*/
|
|
34250
|
-
async update(
|
|
34251
|
-
return this.uploadOrUpdate("PUT",
|
|
34250
|
+
async update(path6, fileBody, fileOptions) {
|
|
34251
|
+
return this.uploadOrUpdate("PUT", path6, fileBody, fileOptions);
|
|
34252
34252
|
}
|
|
34253
34253
|
/**
|
|
34254
34254
|
* Moves an existing file to a new path in the same bucket.
|
|
@@ -34399,10 +34399,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34399
34399
|
* - `objects` table permissions: `select`
|
|
34400
34400
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34401
34401
|
*/
|
|
34402
|
-
async createSignedUrl(
|
|
34402
|
+
async createSignedUrl(path6, expiresIn, options) {
|
|
34403
34403
|
var _this8 = this;
|
|
34404
34404
|
return _this8.handleOperation(async () => {
|
|
34405
|
-
let _path = _this8._getFinalPath(
|
|
34405
|
+
let _path = _this8._getFinalPath(path6);
|
|
34406
34406
|
const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
|
|
34407
34407
|
let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
|
|
34408
34408
|
const query = new URLSearchParams();
|
|
@@ -34538,13 +34538,13 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34538
34538
|
* - `objects` table permissions: `select`
|
|
34539
34539
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34540
34540
|
*/
|
|
34541
|
-
download(
|
|
34541
|
+
download(path6, options, parameters) {
|
|
34542
34542
|
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";
|
|
34543
34543
|
const query = new URLSearchParams();
|
|
34544
34544
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
34545
34545
|
if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
|
|
34546
34546
|
const queryString = query.toString();
|
|
34547
|
-
const _path = this._getFinalPath(
|
|
34547
|
+
const _path = this._getFinalPath(path6);
|
|
34548
34548
|
const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
|
|
34549
34549
|
headers: this.headers,
|
|
34550
34550
|
noResolveJson: true
|
|
@@ -34575,9 +34575,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34575
34575
|
* }
|
|
34576
34576
|
* ```
|
|
34577
34577
|
*/
|
|
34578
|
-
async info(
|
|
34578
|
+
async info(path6) {
|
|
34579
34579
|
var _this10 = this;
|
|
34580
|
-
const _path = _this10._getFinalPath(
|
|
34580
|
+
const _path = _this10._getFinalPath(path6);
|
|
34581
34581
|
return _this10.handleOperation(async () => {
|
|
34582
34582
|
return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
|
|
34583
34583
|
});
|
|
@@ -34598,9 +34598,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34598
34598
|
* .exists('folder/avatar1.png')
|
|
34599
34599
|
* ```
|
|
34600
34600
|
*/
|
|
34601
|
-
async exists(
|
|
34601
|
+
async exists(path6) {
|
|
34602
34602
|
var _this11 = this;
|
|
34603
|
-
const _path = _this11._getFinalPath(
|
|
34603
|
+
const _path = _this11._getFinalPath(path6);
|
|
34604
34604
|
try {
|
|
34605
34605
|
await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
|
|
34606
34606
|
return {
|
|
@@ -34679,8 +34679,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34679
34679
|
* - `objects` table permissions: none
|
|
34680
34680
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34681
34681
|
*/
|
|
34682
|
-
getPublicUrl(
|
|
34683
|
-
const _path = this._getFinalPath(
|
|
34682
|
+
getPublicUrl(path6, options) {
|
|
34683
|
+
const _path = this._getFinalPath(path6);
|
|
34684
34684
|
const query = new URLSearchParams();
|
|
34685
34685
|
if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
|
|
34686
34686
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
@@ -34819,10 +34819,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34819
34819
|
* - `objects` table permissions: `select`
|
|
34820
34820
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34821
34821
|
*/
|
|
34822
|
-
async list(
|
|
34822
|
+
async list(path6, options, parameters) {
|
|
34823
34823
|
var _this13 = this;
|
|
34824
34824
|
return _this13.handleOperation(async () => {
|
|
34825
|
-
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix:
|
|
34825
|
+
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path6 || "" });
|
|
34826
34826
|
return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
|
|
34827
34827
|
});
|
|
34828
34828
|
}
|
|
@@ -34887,11 +34887,11 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34887
34887
|
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
34888
34888
|
return btoa(data);
|
|
34889
34889
|
}
|
|
34890
|
-
_getFinalPath(
|
|
34891
|
-
return `${this.bucketId}/${
|
|
34890
|
+
_getFinalPath(path6) {
|
|
34891
|
+
return `${this.bucketId}/${path6.replace(/^\/+/, "")}`;
|
|
34892
34892
|
}
|
|
34893
|
-
_removeEmptyFolders(
|
|
34894
|
-
return
|
|
34893
|
+
_removeEmptyFolders(path6) {
|
|
34894
|
+
return path6.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
|
|
34895
34895
|
}
|
|
34896
34896
|
/** Modifies the `query`, appending values the from `transform` */
|
|
34897
34897
|
applyTransformOptsToQuery(query, transform2) {
|
|
@@ -42664,7 +42664,14 @@ var softwareCapacityObservationSummarySchema = external_exports.object({
|
|
|
42664
42664
|
account_alias: external_exports.string().nullable()
|
|
42665
42665
|
});
|
|
42666
42666
|
var softwareRequestCreateInputSchema = external_exports.object({
|
|
42667
|
-
|
|
42667
|
+
// DER-1310: the project the request opens against. Either identifier works —
|
|
42668
|
+
// the raw id (still fully supported) or `project`, the project's slug or name,
|
|
42669
|
+
// resolved server-side to the tenant's active project. The command layer (not
|
|
42670
|
+
// this schema) enforces that exactly one resolves, with an actionable error
|
|
42671
|
+
// when neither is given or neither matches — keeping this shape a plain
|
|
42672
|
+
// `z.object` so JSON-schema/CLI-flag projection stays simple (no `.refine`).
|
|
42673
|
+
software_project_id: uuid10.optional(),
|
|
42674
|
+
project: external_exports.string().trim().min(1).max(200).optional(),
|
|
42668
42675
|
// Untrusted display text (ADR-0018 §5) — bounded, never treated as an instruction.
|
|
42669
42676
|
title: external_exports.string().trim().min(1).max(500),
|
|
42670
42677
|
source_channel: softwareSourceChannelSchema.optional(),
|
|
@@ -45125,7 +45132,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
45125
45132
|
order: 48,
|
|
45126
45133
|
title: "CLI and MCP installation guide",
|
|
45127
45134
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
45128
|
-
version: "2026-07-
|
|
45135
|
+
version: "2026-07-05.1",
|
|
45129
45136
|
public: true,
|
|
45130
45137
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
45131
45138
|
stages: ["company_setup", "staffing"],
|
|
@@ -45604,7 +45611,7 @@ The signed-in app exposes the same Skill command surface at **Skills**, linked f
|
|
|
45604
45611
|
| \`{{cli}} deliverable list|get|create|attach\` | \`deliverable.list\`, \`deliverable.get\`, \`deliverable.create\`, \`deliverable.attach\` | List, get, create, or attach agent deliverables for a seat. Deliverables are durable work outputs visible across UI, CLI, and MCP. | Tenant (list/get), Seat (create/attach) | \`{{cli}} deliverable list --seat-id <id> --json\`; \`{{cli}} deliverable create --title "Brief" --kind brief\` |
|
|
45605
45612
|
| \`{{cli}} graph show\` | \`graph.get\` | Print a table view of the Responsibility Graph with explicit \`seat_id\` and \`parent_seat_id\` columns. | Tenant | \`{{cli}} graph show\` |
|
|
45606
45613
|
| \`{{cli}} seat list|get|create|rename|reparent|decommission\` | \`graph.get\`, \`seat.get\`, \`seat.create\`, \`seat.rename\`, \`seat.reparent\`, \`seat.decommission\`, \`seat.decommission_preview\` | Work with seats as first-class CLI primitives. \`seat decommission --dry-run\` previews affected occupancies, agents, Charters, tokens, credentials, work orders, and schedules before the human-gated teardown. | Tenant / Seat for targeted mutations | \`{{cli}} seat list\`; \`{{cli}} seat decommission --seat-id <id> --dry-run\` |
|
|
45607
|
-
| \`{{cli}} forge project-create|project-list|request-create|request-list|request-show|request-pause|request-cancel|request-resume\` | \`software_factory.project.create\`, \`software_factory.project.list\`, \`software_factory.request.create\`, \`software_factory.request.list\`, \`software_factory.request.show\`, \`software_factory.request.pause\`, \`software_factory.request.cancel\`, \`software_factory.request.resume\` | Create/list Forge projects, open/read governed Forge build requests, and control request lifecycle. Project creation plus pause/cancel/resume are tenant-admin, entitlement-gated, and human-gated; reads and request creation require the Forge add-on. Pause halts new runner claims at the next task checkpoint, cancel drives terminal canceled, and resume requeues from the last completed task checkpoint. | Tenant / Tenant-admin for create and lifecycle controls | \`{{cli}} forge project-create --name "Leiluna app" --base-branch main\`; \`{{cli}} forge request-create --
|
|
45614
|
+
| \`{{cli}} forge project-create|project-list|request-create|request-list|request-show|request-pause|request-cancel|request-resume\` | \`software_factory.project.create\`, \`software_factory.project.list\`, \`software_factory.request.create\`, \`software_factory.request.list\`, \`software_factory.request.show\`, \`software_factory.request.pause\`, \`software_factory.request.cancel\`, \`software_factory.request.resume\` | Create/list Forge projects, open/read governed Forge build requests, and control request lifecycle. Project creation plus pause/cancel/resume are tenant-admin, entitlement-gated, and human-gated; reads and request creation require the Forge add-on. Pause halts new runner claims at the next task checkpoint, cancel drives terminal canceled, and resume requeues from the last completed task checkpoint. | Tenant / Tenant-admin for create and lifecycle controls | \`{{cli}} forge project-create --name "Leiluna app" --base-branch main\`; \`{{cli}} forge request-create --project <slug-or-name> --title "Add invoice export"\` (accepts \`--software-project-id <id>\` instead of \`--project\` too); \`{{cli}} forge request-pause --build-request-id <id> --reason "Operator hold"\` |
|
|
45608
45615
|
|
|
45609
45616
|
Skills wrapper help:
|
|
45610
45617
|
|
|
@@ -45845,7 +45852,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
|
|
|
45845
45852
|
| \`rost_revoke_skill_from_seat\` | \`skill.revoke_from_seat\` | Human-gated revocation that stops future Skill use without deleting historical activations. | Tenant-admin | Call with \`{"assignment_id":"<assignment-id>"}\`; non-interactive callers receive a confirmation handoff. |
|
|
45846
45853
|
| \`rost_list_model_catalog\` | \`model.catalog\` | List guided model tiers \u2014 recommendations, token prices, cost bands, best-fit work, and model ids for \`--model\`. | Tenant | Call with \`{}\`. |
|
|
45847
45854
|
| \`rost_create_a_forge_project\` | \`software_factory.project.create\` | Create an active Forge software project before binding repositories or opening build requests. Tenant-admin, human-gated, and entitlement-gated. | Tenant-admin | Call with \`{"name":"Leiluna app","slug":"leiluna-app","base_branch":"main"}\`; non-interactive callers receive a confirmation handoff. |
|
|
45848
|
-
| \`rost_create_a_forge_build_request\` | \`software_factory.request.create\` | Open a governed Forge build request against a connected software project; records the request plus its initial intake phase run. Requires the Forge add-on; the title is untrusted display text. | Tenant | Call with \`{"
|
|
45855
|
+
| \`rost_create_a_forge_build_request\` | \`software_factory.request.create\` | Open a governed Forge build request against a connected software project; records the request plus its initial intake phase run. Requires the Forge add-on; the title is untrusted display text. | Tenant | Call with \`{"project":"<slug-or-name>","title":"Add invoice export"}\` (or \`{"software_project_id":"<project-id>", ...}\`). |
|
|
45849
45856
|
| \`rost_pause_a_forge_build_request\` | \`software_factory.request.pause\` | Pause an in-flight Forge build request. New runner claims stop immediately; running work halts at the next task checkpoint. Human-gated. | Tenant-admin | Call with \`{"build_request_id":"<request-id>","reason":"Operator hold"}\`; non-interactive callers receive a confirmation handoff. |
|
|
45850
45857
|
| \`rost_cancel_a_forge_build_request\` | \`software_factory.request.cancel\` | Cancel a Forge build request and expire active runner work orders. This is terminal; use pause for reversible holds. Human-gated. | Tenant-admin | Call with \`{"build_request_id":"<request-id>","reason":"Out of scope"}\`; non-interactive callers receive a confirmation handoff. |
|
|
45851
45858
|
| \`rost_resume_a_forge_build_request\` | \`software_factory.request.resume\` | Resume a paused or human-blocked Forge build request from the last completed task checkpoint and requeue the current phase. Human-gated. | Tenant-admin | Call with \`{"build_request_id":"<request-id>","reason":"Budget raised"}\`; non-interactive callers receive a confirmation handoff. |
|
|
@@ -48819,21 +48826,32 @@ function helpText() {
|
|
|
48819
48826
|
].join("\n");
|
|
48820
48827
|
}
|
|
48821
48828
|
|
|
48822
|
-
// src/
|
|
48829
|
+
// src/cli-version.ts
|
|
48823
48830
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
48824
48831
|
import { dirname as dirname2, resolve } from "node:path";
|
|
48825
48832
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
48826
|
-
var
|
|
48833
|
+
var packageMetadataSchema = external_exports.object({
|
|
48827
48834
|
version: external_exports.string()
|
|
48828
48835
|
});
|
|
48829
|
-
|
|
48836
|
+
async function readCliVersion() {
|
|
48837
|
+
const packageJsonPath = resolve(dirname2(fileURLToPath2(import.meta.url)), "../package.json");
|
|
48838
|
+
try {
|
|
48839
|
+
const metadata = packageMetadataSchema.parse(JSON.parse(await readFile2(packageJsonPath, "utf8")));
|
|
48840
|
+
return metadata.version;
|
|
48841
|
+
} catch {
|
|
48842
|
+
return null;
|
|
48843
|
+
}
|
|
48844
|
+
}
|
|
48845
|
+
|
|
48846
|
+
// src/update-check.ts
|
|
48847
|
+
var latestPackageSchema = external_exports.object({
|
|
48830
48848
|
version: external_exports.string()
|
|
48831
48849
|
});
|
|
48832
48850
|
async function maybePrintUpgradeNudge(io) {
|
|
48833
48851
|
if (process.env.CI || process.env.VITEST || process.env.CLI_SKIP_UPDATE_CHECK === "1") {
|
|
48834
48852
|
return;
|
|
48835
48853
|
}
|
|
48836
|
-
const currentVersion = await
|
|
48854
|
+
const currentVersion = await readCliVersion();
|
|
48837
48855
|
if (!currentVersion || currentVersion === "0.0.0") {
|
|
48838
48856
|
return;
|
|
48839
48857
|
}
|
|
@@ -48846,15 +48864,6 @@ async function maybePrintUpgradeNudge(io) {
|
|
|
48846
48864
|
`
|
|
48847
48865
|
);
|
|
48848
48866
|
}
|
|
48849
|
-
async function readCurrentVersion() {
|
|
48850
|
-
const packageJsonPath = resolve(dirname2(fileURLToPath2(import.meta.url)), "../package.json");
|
|
48851
|
-
try {
|
|
48852
|
-
const metadata = packageMetadataSchema.parse(JSON.parse(await readFile2(packageJsonPath, "utf8")));
|
|
48853
|
-
return metadata.version;
|
|
48854
|
-
} catch {
|
|
48855
|
-
return null;
|
|
48856
|
-
}
|
|
48857
|
-
}
|
|
48858
48867
|
async function fetchLatestVersion(packageName) {
|
|
48859
48868
|
const controller = new AbortController();
|
|
48860
48869
|
const timeout = setTimeout(() => controller.abort(), 750);
|
|
@@ -49089,7 +49098,7 @@ var COMMAND_MANIFEST = [
|
|
|
49089
49098
|
{ "id": "software_factory.project.list", "namespace": "software_factory", "action": "project.list", "title": "List Forge projects", "description": "List active Forge software projects so a tenant can choose a project for build requests, GitHub repository bindings, and config. Read-only and entitlement-gated.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [], "hasComplexInput": false, "help": "List active Forge software projects so a tenant can choose a project for build requests, GitHub repository bindings, and config. Read-only; requires the Forge add-on." },
|
|
49090
49099
|
{ "id": "software_factory.request.answer_clarification", "namespace": "software_factory", "action": "request.answer_clarification", "title": "Answer Forge plan clarifications", "description": "Write structured human answers to a draft plan's clarifying questions and optionally mark it ready for plan review. Answers append to the versioned planning artifact; this never widens authority or automation mode.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "build_request_id", "flag": "build-request-id", "type": "string", "required": true }, { "name": "mark_ready_for_plan_review", "flag": "mark-ready-for-plan-review", "type": "boolean", "required": false }], "hasComplexInput": true, "help": "Write structured human answers to a draft plan's clarifying questions and optionally mark it ready for plan review. Never widens authority or automation mode." },
|
|
49091
49100
|
{ "id": "software_factory.request.cancel", "namespace": "software_factory", "action": "request.cancel", "title": "Cancel a Forge build request", "description": "Cancel a Forge build request and expire active runner work orders. This is terminal; use pause for reversible holds. Human-gated and entitlement-gated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "build_request_id", "flag": "build-request-id", "type": "string", "required": true }, { "name": "reason", "flag": "reason", "type": "string", "required": false }], "hasComplexInput": false, "help": "Cancel a Forge build request, expire unstarted runner work, and terminalize active runner work. Terminal, tenant-admin, and human-gated." },
|
|
49092
|
-
{ "id": "software_factory.request.create", "namespace": "software_factory", "action": "request.create", "title": "Create a Forge build request", "description": "Open a governed Forge build request against a connected software project. Records the request plus its initial intake phase run. Entitlement-gated (Forge add-on); the title is untrusted display text, never an instruction.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required":
|
|
49101
|
+
{ "id": "software_factory.request.create", "namespace": "software_factory", "action": "request.create", "title": "Create a Forge build request", "description": "Open a governed Forge build request against a connected software project. Records the request plus its initial intake phase run. Entitlement-gated (Forge add-on); the title is untrusted display text, never an instruction.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required": false }, { "name": "project", "flag": "project", "type": "string", "required": false }, { "name": "title", "flag": "title", "type": "string", "required": true }, { "name": "source_channel", "flag": "source-channel", "type": "enum", "required": false, "enumValues": ["ui", "cli", "github_issue", "linear", "api"] }, { "name": "source_ref", "flag": "source-ref", "type": "string", "required": false }], "hasComplexInput": true, "help": "Open a governed Forge build request against a connected software project; the title is untrusted display text. Requires the Forge add-on." },
|
|
49093
49102
|
{ "id": "software_factory.request.list", "namespace": "software_factory", "action": "request.list", "title": "List Forge build requests", "description": "The Forge control-room board: the tenant's build requests with status, current phase, and risk/automation mode. Read-only, entitlement-gated.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "limit", "flag": "limit", "type": "integer", "required": false }], "hasComplexInput": false, "help": "List Forge build requests (the control-room board) with status, current phase, and risk. Read-only; requires the Forge add-on." },
|
|
49094
49103
|
{ "id": "software_factory.request.pause", "namespace": "software_factory", "action": "request.pause", "title": "Pause a Forge build request", "description": "Pause an in-flight Forge build request. New runner claims stop immediately; any running task halts at the next task checkpoint before resume. Human-gated and entitlement-gated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "build_request_id", "flag": "build-request-id", "type": "string", "required": true }, { "name": "reason", "flag": "reason", "type": "string", "required": false }], "hasComplexInput": false, "help": "Pause an in-flight Forge build request so no new runner work starts; running work stops at the next task checkpoint. Tenant-admin and human-gated." },
|
|
49095
49104
|
{ "id": "software_factory.request.resume", "namespace": "software_factory", "action": "request.resume", "title": "Resume a Forge build request", "description": "Resume a paused or human-blocked Forge build request from the last completed task checkpoint and requeue the current phase. Human-gated and entitlement-gated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "build_request_id", "flag": "build-request-id", "type": "string", "required": true }, { "name": "reason", "flag": "reason", "type": "string", "required": false }], "hasComplexInput": true, "help": "Resume a paused or blocked Forge build request from its last completed task checkpoint after human review. Tenant-admin and human-gated." },
|
|
@@ -49319,9 +49328,9 @@ function coerceScalar(field3, raw) {
|
|
|
49319
49328
|
}
|
|
49320
49329
|
}
|
|
49321
49330
|
function rawValueForField(entry, field3, parsed, aliasFlags) {
|
|
49322
|
-
const
|
|
49323
|
-
if (
|
|
49324
|
-
return
|
|
49331
|
+
const canonical2 = parsed.values.get(field3.flag);
|
|
49332
|
+
if (canonical2 !== void 0) {
|
|
49333
|
+
return canonical2;
|
|
49325
49334
|
}
|
|
49326
49335
|
for (const [aliasFlag, targetField] of aliasFlags) {
|
|
49327
49336
|
if (targetField === field3.name) {
|
|
@@ -51533,7 +51542,7 @@ function pendingConfirmationFromError(error51) {
|
|
|
51533
51542
|
|
|
51534
51543
|
// src/doctor.ts
|
|
51535
51544
|
import { execFile as execFileCallback2 } from "node:child_process";
|
|
51536
|
-
import { access
|
|
51545
|
+
import { access } from "node:fs/promises";
|
|
51537
51546
|
import { delimiter } from "node:path";
|
|
51538
51547
|
import { promisify as promisify2 } from "node:util";
|
|
51539
51548
|
var execFile2 = promisify2(execFileCallback2);
|
|
@@ -51544,7 +51553,7 @@ async function runDoctor(options) {
|
|
|
51544
51553
|
const config2 = loadConfig(env);
|
|
51545
51554
|
lines.push(`app_url: ${config2.appUrl}`);
|
|
51546
51555
|
lines.push(`supabase_url: ${config2.supabaseUrl}`);
|
|
51547
|
-
const currentVersion = await
|
|
51556
|
+
const currentVersion = await readCliVersion();
|
|
51548
51557
|
lines.push(`version: ${currentVersion ?? "unknown"}`);
|
|
51549
51558
|
const pathStatus = await checkPath(env);
|
|
51550
51559
|
lines.push(pathStatus);
|
|
@@ -51591,15 +51600,6 @@ async function probeCli(command) {
|
|
|
51591
51600
|
return "not found";
|
|
51592
51601
|
}
|
|
51593
51602
|
}
|
|
51594
|
-
async function readCurrentVersion2() {
|
|
51595
|
-
try {
|
|
51596
|
-
const url2 = new URL("../package.json", import.meta.url);
|
|
51597
|
-
const parsed = JSON.parse(await readFile3(url2, "utf8"));
|
|
51598
|
-
return typeof parsed.version === "string" ? parsed.version : null;
|
|
51599
|
-
} catch {
|
|
51600
|
-
return null;
|
|
51601
|
-
}
|
|
51602
|
-
}
|
|
51603
51603
|
async function fetchLatestVersion2(packageName, env) {
|
|
51604
51604
|
if (env.CI || env.VITEST || env.CLI_SKIP_UPDATE_CHECK === "1" || env[`${cliBrand.envPrefix}_CLI_NO_UPDATE_CHECK`] === "1") {
|
|
51605
51605
|
return null;
|
|
@@ -51655,11 +51655,247 @@ function parseSemver2(value) {
|
|
|
51655
51655
|
// src/runner-serve.ts
|
|
51656
51656
|
import { execFile as execFileCallback3, spawn } from "node:child_process";
|
|
51657
51657
|
import { createHash } from "node:crypto";
|
|
51658
|
-
import { chmod, mkdir as mkdir2, readFile as
|
|
51659
|
-
import { cpus, hostname as hostname3, platform, totalmem, tmpdir } from "node:os";
|
|
51660
|
-
import
|
|
51658
|
+
import { chmod, mkdir as mkdir2, readFile as readFile3, rename, rm as rm3, statfs, writeFile as writeFile2 } from "node:fs/promises";
|
|
51659
|
+
import { cpus, homedir as homedir2, hostname as hostname3, platform, totalmem, tmpdir } from "node:os";
|
|
51660
|
+
import path4 from "node:path";
|
|
51661
51661
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
51662
51662
|
import { promisify as promisify3 } from "node:util";
|
|
51663
|
+
|
|
51664
|
+
// src/runner-sandbox.ts
|
|
51665
|
+
import { existsSync as existsSync2, realpathSync } from "node:fs";
|
|
51666
|
+
import { readdir, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
51667
|
+
import path3 from "node:path";
|
|
51668
|
+
var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
51669
|
+
var RUNNER_TEMP_PREFIXES = [
|
|
51670
|
+
"rost-runner-mcp-",
|
|
51671
|
+
"rost-runner-sandbox-",
|
|
51672
|
+
"rost-runner-askpass-"
|
|
51673
|
+
];
|
|
51674
|
+
var SENSITIVE_READ_SUBPATHS = [
|
|
51675
|
+
".ssh",
|
|
51676
|
+
".aws",
|
|
51677
|
+
".gnupg",
|
|
51678
|
+
".config/gcloud",
|
|
51679
|
+
".config/gh",
|
|
51680
|
+
".kube",
|
|
51681
|
+
".docker",
|
|
51682
|
+
".npmrc"
|
|
51683
|
+
];
|
|
51684
|
+
var SENSITIVE_READ_LITERALS = [".netrc"];
|
|
51685
|
+
var SENSITIVE_WRITE_SUBPATHS = [
|
|
51686
|
+
".ssh",
|
|
51687
|
+
".aws",
|
|
51688
|
+
".gnupg",
|
|
51689
|
+
".config/git",
|
|
51690
|
+
"bin",
|
|
51691
|
+
"Library/LaunchAgents"
|
|
51692
|
+
];
|
|
51693
|
+
var SENSITIVE_WRITE_LITERALS = [
|
|
51694
|
+
".gitconfig",
|
|
51695
|
+
".zshrc",
|
|
51696
|
+
".zprofile",
|
|
51697
|
+
".zshenv",
|
|
51698
|
+
".zlogin",
|
|
51699
|
+
".bashrc",
|
|
51700
|
+
".bash_profile",
|
|
51701
|
+
".bash_login",
|
|
51702
|
+
".profile"
|
|
51703
|
+
];
|
|
51704
|
+
function parseSandboxMode(value) {
|
|
51705
|
+
const normalized = (value ?? "auto").trim().toLowerCase();
|
|
51706
|
+
if (normalized === "off" || normalized === "strict" || normalized === "auto" || normalized === "") {
|
|
51707
|
+
return normalized === "" ? "auto" : normalized;
|
|
51708
|
+
}
|
|
51709
|
+
return "auto";
|
|
51710
|
+
}
|
|
51711
|
+
function parseSandboxNetwork(value) {
|
|
51712
|
+
const normalized = (value ?? "").trim().toLowerCase();
|
|
51713
|
+
if (normalized === "" || normalized === "allow") {
|
|
51714
|
+
return { allowNetwork: true, unrecognized: null };
|
|
51715
|
+
}
|
|
51716
|
+
if (normalized === "deny") {
|
|
51717
|
+
return { allowNetwork: false, unrecognized: null };
|
|
51718
|
+
}
|
|
51719
|
+
return { allowNetwork: true, unrecognized: value ?? "" };
|
|
51720
|
+
}
|
|
51721
|
+
function sbplString(value) {
|
|
51722
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
51723
|
+
}
|
|
51724
|
+
function subpath(value) {
|
|
51725
|
+
return `(subpath ${sbplString(value)})`;
|
|
51726
|
+
}
|
|
51727
|
+
function literal2(value) {
|
|
51728
|
+
return `(literal ${sbplString(value)})`;
|
|
51729
|
+
}
|
|
51730
|
+
var STRICT_SYSTEM_READ_SUBPATHS = [
|
|
51731
|
+
"/usr",
|
|
51732
|
+
"/bin",
|
|
51733
|
+
"/sbin",
|
|
51734
|
+
"/System",
|
|
51735
|
+
"/Library",
|
|
51736
|
+
"/private/etc",
|
|
51737
|
+
"/private/var/db",
|
|
51738
|
+
"/dev",
|
|
51739
|
+
"/opt",
|
|
51740
|
+
"/nix"
|
|
51741
|
+
];
|
|
51742
|
+
function buildSeatbeltProfile(input) {
|
|
51743
|
+
const home = input.homeDir.replace(/\/+$/, "");
|
|
51744
|
+
const lines = ["(version 1)"];
|
|
51745
|
+
const denyReadBaseSubpaths = input.denyReadPaths.filter((p) => p.endsWith("/")).map((p) => subpath(p.replace(/\/+$/, "")));
|
|
51746
|
+
const denyReadLiterals = input.denyReadPaths.filter((p) => !p.endsWith("/")).map(literal2);
|
|
51747
|
+
const credentialReadDenies = [
|
|
51748
|
+
...SENSITIVE_READ_SUBPATHS.map((rel) => subpath(`${home}/${rel}`)),
|
|
51749
|
+
...SENSITIVE_READ_LITERALS.map((rel) => literal2(`${home}/${rel}`))
|
|
51750
|
+
];
|
|
51751
|
+
if (input.strict) {
|
|
51752
|
+
lines.push("(deny default)");
|
|
51753
|
+
lines.push("(allow process*)");
|
|
51754
|
+
lines.push("(allow sysctl-read)");
|
|
51755
|
+
lines.push("(allow mach-lookup)");
|
|
51756
|
+
lines.push("(allow iokit-open)");
|
|
51757
|
+
lines.push("(allow system-socket)");
|
|
51758
|
+
lines.push("(allow signal (target self))");
|
|
51759
|
+
lines.push("(allow file-read-metadata)");
|
|
51760
|
+
const strictReads = [
|
|
51761
|
+
...STRICT_SYSTEM_READ_SUBPATHS.map(subpath),
|
|
51762
|
+
subpath(`${home}/.claude`),
|
|
51763
|
+
subpath(`${home}/.codex`),
|
|
51764
|
+
subpath(input.tmpDir)
|
|
51765
|
+
];
|
|
51766
|
+
lines.push(`(allow file-read* ${strictReads.join(" ")})`);
|
|
51767
|
+
} else {
|
|
51768
|
+
lines.push("(allow default)");
|
|
51769
|
+
}
|
|
51770
|
+
lines.push("(deny appleevent-send)");
|
|
51771
|
+
if (denyReadBaseSubpaths.length > 0) {
|
|
51772
|
+
lines.push(`(deny file-read* ${denyReadBaseSubpaths.join(" ")})`);
|
|
51773
|
+
}
|
|
51774
|
+
const workspaceReads = [subpath(input.workspaceDir), ...input.allowWritePaths.map(subpath)];
|
|
51775
|
+
lines.push(`(allow file-read* ${workspaceReads.join(" ")})`);
|
|
51776
|
+
lines.push("(deny file-write*)");
|
|
51777
|
+
const allowWrites = [
|
|
51778
|
+
subpath(input.workspaceDir),
|
|
51779
|
+
subpath(input.tmpDir),
|
|
51780
|
+
subpath(`${home}/.claude`),
|
|
51781
|
+
subpath(`${home}/.codex`),
|
|
51782
|
+
...input.allowWritePaths.map(subpath),
|
|
51783
|
+
subpath("/dev/fd"),
|
|
51784
|
+
literal2("/dev/null"),
|
|
51785
|
+
literal2("/dev/stdout"),
|
|
51786
|
+
literal2("/dev/stderr"),
|
|
51787
|
+
literal2("/dev/tty"),
|
|
51788
|
+
literal2("/dev/dtracehelper")
|
|
51789
|
+
];
|
|
51790
|
+
lines.push(`(allow file-write* ${allowWrites.join(" ")})`);
|
|
51791
|
+
lines.push(`(deny file-read* ${[...credentialReadDenies, ...denyReadLiterals].join(" ")})`);
|
|
51792
|
+
const denyWrites = [
|
|
51793
|
+
...SENSITIVE_WRITE_SUBPATHS.map((rel) => subpath(`${home}/${rel}`)),
|
|
51794
|
+
...SENSITIVE_WRITE_LITERALS.map((rel) => literal2(`${home}/${rel}`))
|
|
51795
|
+
];
|
|
51796
|
+
lines.push(`(deny file-write* ${denyWrites.join(" ")})`);
|
|
51797
|
+
lines.push('(deny mach-lookup (global-name "com.apple.coreservices.appleevents"))');
|
|
51798
|
+
if (!input.allowNetwork) {
|
|
51799
|
+
lines.push("(deny network*)");
|
|
51800
|
+
} else if (input.strict) {
|
|
51801
|
+
lines.push("(allow network*)");
|
|
51802
|
+
}
|
|
51803
|
+
return `${lines.join("\n")}
|
|
51804
|
+
`;
|
|
51805
|
+
}
|
|
51806
|
+
function canonical(p) {
|
|
51807
|
+
try {
|
|
51808
|
+
return realpathSync(p);
|
|
51809
|
+
} catch {
|
|
51810
|
+
return p;
|
|
51811
|
+
}
|
|
51812
|
+
}
|
|
51813
|
+
function canonicalFile(p) {
|
|
51814
|
+
return path3.join(canonical(path3.dirname(p)), path3.basename(p));
|
|
51815
|
+
}
|
|
51816
|
+
function isSandboxExecAvailable() {
|
|
51817
|
+
try {
|
|
51818
|
+
return existsSync2(SANDBOX_EXEC_PATH);
|
|
51819
|
+
} catch {
|
|
51820
|
+
return false;
|
|
51821
|
+
}
|
|
51822
|
+
}
|
|
51823
|
+
function resolveSandboxSpec(options) {
|
|
51824
|
+
if (options.mode === "off") {
|
|
51825
|
+
return { kind: "none", reason: "RUNNER_SANDBOX=off (loosened fallback - OS confinement disabled)" };
|
|
51826
|
+
}
|
|
51827
|
+
if (options.platform !== "darwin") {
|
|
51828
|
+
return { kind: "none", reason: `no OS sandbox on ${options.platform} (seatbelt is macOS-only; Linux confinement is a follow-up)` };
|
|
51829
|
+
}
|
|
51830
|
+
const available = options.sandboxExecAvailable ?? isSandboxExecAvailable();
|
|
51831
|
+
if (!available) {
|
|
51832
|
+
return { kind: "none", reason: `sandbox-exec not found at ${SANDBOX_EXEC_PATH}` };
|
|
51833
|
+
}
|
|
51834
|
+
const workspaceDir = canonical(options.workspaceDir);
|
|
51835
|
+
const tmpDir = canonical(options.tmpDir);
|
|
51836
|
+
const homeDir = canonical(options.homeDir);
|
|
51837
|
+
const allowNetwork = options.allowNetwork ?? true;
|
|
51838
|
+
const denyReadPaths = [canonicalFile(options.stateFile)];
|
|
51839
|
+
if (options.runnerBaseDir) {
|
|
51840
|
+
denyReadPaths.push(`${canonical(options.runnerBaseDir).replace(/\/+$/, "")}/`);
|
|
51841
|
+
}
|
|
51842
|
+
const profile = buildSeatbeltProfile({
|
|
51843
|
+
workspaceDir,
|
|
51844
|
+
tmpDir,
|
|
51845
|
+
homeDir,
|
|
51846
|
+
denyReadPaths,
|
|
51847
|
+
allowWritePaths: (options.allowWritePaths ?? []).map(canonical),
|
|
51848
|
+
allowNetwork,
|
|
51849
|
+
strict: options.mode === "strict"
|
|
51850
|
+
});
|
|
51851
|
+
return {
|
|
51852
|
+
kind: "seatbelt",
|
|
51853
|
+
reason: options.mode === "strict" ? "seatbelt (strict: deny-default reads + write confinement)" : "seatbelt (guard: deny sensitive reads/writes)",
|
|
51854
|
+
profile,
|
|
51855
|
+
allowNetwork
|
|
51856
|
+
};
|
|
51857
|
+
}
|
|
51858
|
+
function wrapCommandWithSandbox(spec, command, args, profilePath) {
|
|
51859
|
+
if (spec.kind === "none" || profilePath === null) {
|
|
51860
|
+
return { command, args, profilePath: null };
|
|
51861
|
+
}
|
|
51862
|
+
return {
|
|
51863
|
+
command: SANDBOX_EXEC_PATH,
|
|
51864
|
+
args: ["-f", profilePath, command, ...args],
|
|
51865
|
+
profilePath
|
|
51866
|
+
};
|
|
51867
|
+
}
|
|
51868
|
+
async function sweepStaleRunnerTempFiles(dir, options = {}) {
|
|
51869
|
+
const maxAgeMs = options.maxAgeMs ?? 60 * 60 * 1e3;
|
|
51870
|
+
const now = options.now ?? Date.now();
|
|
51871
|
+
let entries;
|
|
51872
|
+
try {
|
|
51873
|
+
entries = await readdir(dir);
|
|
51874
|
+
} catch {
|
|
51875
|
+
return [];
|
|
51876
|
+
}
|
|
51877
|
+
const removed = [];
|
|
51878
|
+
await Promise.all(
|
|
51879
|
+
entries.map(async (name) => {
|
|
51880
|
+
if (!RUNNER_TEMP_PREFIXES.some((prefix) => name.startsWith(prefix))) {
|
|
51881
|
+
return;
|
|
51882
|
+
}
|
|
51883
|
+
const full = path3.join(dir, name);
|
|
51884
|
+
try {
|
|
51885
|
+
const info = await stat2(full);
|
|
51886
|
+
if (now - info.mtimeMs < maxAgeMs) {
|
|
51887
|
+
return;
|
|
51888
|
+
}
|
|
51889
|
+
await rm2(full, { force: true });
|
|
51890
|
+
removed.push(name);
|
|
51891
|
+
} catch {
|
|
51892
|
+
}
|
|
51893
|
+
})
|
|
51894
|
+
);
|
|
51895
|
+
return removed;
|
|
51896
|
+
}
|
|
51897
|
+
|
|
51898
|
+
// src/runner-serve.ts
|
|
51663
51899
|
var execFile3 = promisify3(execFileCallback3);
|
|
51664
51900
|
var RUNNER_WORK_ORDER_TOOLS = [
|
|
51665
51901
|
"mcp__rost__rost_get_context",
|
|
@@ -51674,6 +51910,8 @@ var RUNNER_AICOS_TURN_TOOLS = [
|
|
|
51674
51910
|
async function runRunnerServe(options) {
|
|
51675
51911
|
const env = options.env ?? process.env;
|
|
51676
51912
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
51913
|
+
const cliVersion = await readCliVersion();
|
|
51914
|
+
const homeDir = options.homeDir ?? homedir2();
|
|
51677
51915
|
let config2;
|
|
51678
51916
|
try {
|
|
51679
51917
|
config2 = parseServeArgs(options.args, options.appUrl, env);
|
|
@@ -51685,6 +51923,20 @@ ${usage()}
|
|
|
51685
51923
|
}
|
|
51686
51924
|
options.io.stdout.write(`runner serve: ${options.appUrl} name=${config2.name} execute=${config2.execute}
|
|
51687
51925
|
`);
|
|
51926
|
+
options.io.stdout.write(`runner sandbox: ${config2.sandbox.kind} - ${config2.sandbox.reason}
|
|
51927
|
+
`);
|
|
51928
|
+
if (config2.sandboxNetworkWarning) {
|
|
51929
|
+
options.io.stderr.write(`warning: ${config2.sandboxNetworkWarning}
|
|
51930
|
+
`);
|
|
51931
|
+
}
|
|
51932
|
+
try {
|
|
51933
|
+
const swept = await sweepStaleRunnerTempFiles(tmpdir());
|
|
51934
|
+
if (swept.length > 0) {
|
|
51935
|
+
options.io.stdout.write(`swept ${swept.length} stale runner temp file(s)
|
|
51936
|
+
`);
|
|
51937
|
+
}
|
|
51938
|
+
} catch {
|
|
51939
|
+
}
|
|
51688
51940
|
let state;
|
|
51689
51941
|
try {
|
|
51690
51942
|
state = await pairIfNeeded(options.client ?? null, fetchImpl, options.appUrl, config2, options.io);
|
|
@@ -51698,13 +51950,13 @@ ${usage()}
|
|
|
51698
51950
|
let failed = false;
|
|
51699
51951
|
let activeSessions = 0;
|
|
51700
51952
|
do {
|
|
51701
|
-
const detectedCapabilities = await detectCapabilities();
|
|
51953
|
+
const detectedCapabilities = await detectCapabilities(cliVersion, env, homeDir);
|
|
51702
51954
|
const capabilities = capabilitiesForConfiguredRuntime(detectedCapabilities, config2.runtime);
|
|
51703
51955
|
const localRuntime = config2.execute ? effectiveExecutionRuntime(config2.runtime, capabilities) : null;
|
|
51704
51956
|
try {
|
|
51705
51957
|
const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", {
|
|
51706
51958
|
capabilities,
|
|
51707
|
-
telemetry: detectTelemetry(capabilities, env, activeSessions)
|
|
51959
|
+
telemetry: detectTelemetry(capabilities, env, activeSessions, cliVersion)
|
|
51708
51960
|
}, state.runner_secret);
|
|
51709
51961
|
if (heartbeat.status !== 200) {
|
|
51710
51962
|
options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
|
|
@@ -51751,17 +52003,28 @@ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runt
|
|
|
51751
52003
|
}
|
|
51752
52004
|
}
|
|
51753
52005
|
}
|
|
51754
|
-
function
|
|
51755
|
-
|
|
52006
|
+
function resolveAccountAlias(env) {
|
|
52007
|
+
return (env.RUNNER_MODEL_ACCOUNT_ALIAS ?? env.ROST_RUNNER_MODEL_ACCOUNT_ALIAS ?? "").trim() || "default";
|
|
52008
|
+
}
|
|
52009
|
+
function capabilitiesForAnalytics(capabilities) {
|
|
52010
|
+
return {
|
|
52011
|
+
...capabilities,
|
|
52012
|
+
claude: { ...capabilities.claude, account_alias: null },
|
|
52013
|
+
codex: { ...capabilities.codex, account_alias: null }
|
|
52014
|
+
};
|
|
52015
|
+
}
|
|
52016
|
+
function detectTelemetry(capabilities, env, activeSessions, cliVersion) {
|
|
51756
52017
|
const usageState = env.RUNNER_MODEL_ACCOUNT_STATE ?? env.ROST_RUNNER_MODEL_ACCOUNT_STATE ?? "unknown";
|
|
51757
52018
|
return {
|
|
51758
52019
|
os: platform(),
|
|
51759
52020
|
cpu_count: cpus().length,
|
|
51760
52021
|
memory_mb: Math.round(totalmem() / (1024 * 1024)),
|
|
51761
|
-
|
|
52022
|
+
// DER-1319: sourced from the bundled package.json, not env.npm_package_version (undefined
|
|
52023
|
+
// under launchd — prod runners were silently reporting a null version).
|
|
52024
|
+
runner_version: cliVersion,
|
|
51762
52025
|
active_sessions: activeSessions,
|
|
51763
|
-
installed_runtimes: capabilities,
|
|
51764
|
-
account_alias:
|
|
52026
|
+
installed_runtimes: capabilitiesForAnalytics(capabilities),
|
|
52027
|
+
account_alias: resolveAccountAlias(env),
|
|
51765
52028
|
usage_limit_observation: {
|
|
51766
52029
|
state: usageState,
|
|
51767
52030
|
source: "runner_env_or_unknown"
|
|
@@ -51770,7 +52033,7 @@ function detectTelemetry(capabilities, env, activeSessions) {
|
|
|
51770
52033
|
}
|
|
51771
52034
|
function parseServeArgs(args, appUrl2, env) {
|
|
51772
52035
|
let name = env.RUNNER_NAME ?? `headless-${hostname3()}`;
|
|
51773
|
-
let stateFile = env.RUNNER_STATE_FILE ??
|
|
52036
|
+
let stateFile = env.RUNNER_STATE_FILE ?? path4.join(tmpdir(), `rost-runner-${createHash("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
|
|
51774
52037
|
let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
|
|
51775
52038
|
let interactiveClaimMs = Number(env.RUNNER_INTERACTIVE_CLAIM_MS ?? 1e3);
|
|
51776
52039
|
let execute2 = env.RUNNER_EXECUTE === "1";
|
|
@@ -51812,7 +52075,29 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
51812
52075
|
throw new Error("--interactive-claim-ms must be an integer >= 250.");
|
|
51813
52076
|
}
|
|
51814
52077
|
interactiveClaimMs = Math.min(interactiveClaimMs, heartbeatMs);
|
|
51815
|
-
|
|
52078
|
+
const network = parseSandboxNetwork(env.RUNNER_SANDBOX_NETWORK);
|
|
52079
|
+
const sandbox = resolveSandboxSpec({
|
|
52080
|
+
mode: parseSandboxMode(env.RUNNER_SANDBOX),
|
|
52081
|
+
platform: process.platform,
|
|
52082
|
+
workspaceDir: tmpdir(),
|
|
52083
|
+
tmpDir: tmpdir(),
|
|
52084
|
+
homeDir: homedir2(),
|
|
52085
|
+
stateFile,
|
|
52086
|
+
runnerBaseDir: path4.join(homedir2(), ".rost"),
|
|
52087
|
+
allowNetwork: network.allowNetwork
|
|
52088
|
+
});
|
|
52089
|
+
return {
|
|
52090
|
+
name,
|
|
52091
|
+
stateFile,
|
|
52092
|
+
heartbeatMs,
|
|
52093
|
+
interactiveClaimMs,
|
|
52094
|
+
execute: execute2,
|
|
52095
|
+
once,
|
|
52096
|
+
runtime,
|
|
52097
|
+
sandbox,
|
|
52098
|
+
...network.unrecognized !== null ? { sandboxNetworkWarning: `RUNNER_SANDBOX_NETWORK='${network.unrecognized}' not recognized (use allow|deny); defaulting to allow egress.` } : {},
|
|
52099
|
+
...userCode ? { userCode } : {}
|
|
52100
|
+
};
|
|
51816
52101
|
}
|
|
51817
52102
|
function parseRuntime(value) {
|
|
51818
52103
|
if (value === "auto" || value === "claude" || value === "codex") {
|
|
@@ -51901,7 +52186,7 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
|
|
|
51901
52186
|
}
|
|
51902
52187
|
async function loadState(filePath, expectedTenantId) {
|
|
51903
52188
|
try {
|
|
51904
|
-
const parsed = JSON.parse(await
|
|
52189
|
+
const parsed = JSON.parse(await readFile3(filePath, "utf8"));
|
|
51905
52190
|
if (typeof parsed.runner_id === "string" && typeof parsed.runner_secret === "string") {
|
|
51906
52191
|
if (expectedTenantId && parsed.tenant_id !== expectedTenantId) {
|
|
51907
52192
|
return null;
|
|
@@ -51932,7 +52217,7 @@ function isRuntimeSessionMap(value) {
|
|
|
51932
52217
|
});
|
|
51933
52218
|
}
|
|
51934
52219
|
async function saveState(filePath, state) {
|
|
51935
|
-
await mkdir2(
|
|
52220
|
+
await mkdir2(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
51936
52221
|
const tempPath = `${filePath}.${process.pid}.${createHash("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
|
|
51937
52222
|
try {
|
|
51938
52223
|
await writeFile2(tempPath, `${JSON.stringify(state)}
|
|
@@ -51940,7 +52225,7 @@ async function saveState(filePath, state) {
|
|
|
51940
52225
|
await chmod(tempPath, 384);
|
|
51941
52226
|
await rename(tempPath, filePath);
|
|
51942
52227
|
} catch (error51) {
|
|
51943
|
-
await
|
|
52228
|
+
await rm3(tempPath, { force: true });
|
|
51944
52229
|
throw error51;
|
|
51945
52230
|
}
|
|
51946
52231
|
}
|
|
@@ -51952,9 +52237,25 @@ async function startPairing(client, config2) {
|
|
|
51952
52237
|
}
|
|
51953
52238
|
return userCode;
|
|
51954
52239
|
}
|
|
51955
|
-
async function detectCapabilities() {
|
|
51956
|
-
const [claude, codex] = await Promise.all([
|
|
51957
|
-
|
|
52240
|
+
async function detectCapabilities(cliVersion, env, homeDir) {
|
|
52241
|
+
const [claude, codex, git, gh, forgeDiskFreeMb] = await Promise.all([
|
|
52242
|
+
probe("claude"),
|
|
52243
|
+
probe("codex"),
|
|
52244
|
+
probeGit(),
|
|
52245
|
+
probeGh(),
|
|
52246
|
+
probeForgeDiskFreeMb(homeDir)
|
|
52247
|
+
]);
|
|
52248
|
+
const accountAlias = resolveAccountAlias(env);
|
|
52249
|
+
return {
|
|
52250
|
+
claude: { ...claude, account_alias: accountAlias, rate_limited_until: null },
|
|
52251
|
+
codex: { ...codex, account_alias: accountAlias, rate_limited_until: null },
|
|
52252
|
+
turn_executions: { report_supported: true },
|
|
52253
|
+
...cliVersion ? { forge_build: cliVersion } : {},
|
|
52254
|
+
git,
|
|
52255
|
+
gh,
|
|
52256
|
+
scheduler: { max_parallel_sessions_estimate: null },
|
|
52257
|
+
host: { forge_disk_free_mb: forgeDiskFreeMb }
|
|
52258
|
+
};
|
|
51958
52259
|
}
|
|
51959
52260
|
async function probe(command) {
|
|
51960
52261
|
try {
|
|
@@ -51964,6 +52265,43 @@ async function probe(command) {
|
|
|
51964
52265
|
return { installed: false, version: null, auth_state: "unknown" };
|
|
51965
52266
|
}
|
|
51966
52267
|
}
|
|
52268
|
+
async function probeGit() {
|
|
52269
|
+
try {
|
|
52270
|
+
const { stdout, stderr } = await execFile3("git", ["--version"], { timeout: 1500 });
|
|
52271
|
+
const raw = (stdout || stderr).trim();
|
|
52272
|
+
return { installed: true, version: /(\d+\.\d+(?:\.\d+)?)/.exec(raw)?.[1] ?? (raw || null) };
|
|
52273
|
+
} catch {
|
|
52274
|
+
return { installed: false, version: null };
|
|
52275
|
+
}
|
|
52276
|
+
}
|
|
52277
|
+
async function probeGh() {
|
|
52278
|
+
try {
|
|
52279
|
+
const { stdout, stderr } = await execFile3("gh", ["--version"], { timeout: 1500 });
|
|
52280
|
+
const raw = (stdout || stderr).trim().split("\n")[0]?.trim() ?? "";
|
|
52281
|
+
const authenticated = await probeGhAuth();
|
|
52282
|
+
return { installed: true, version: /(\d+\.\d+\.\d+)/.exec(raw)?.[1] ?? (raw || null), authenticated };
|
|
52283
|
+
} catch {
|
|
52284
|
+
return { installed: false, version: null, authenticated: false };
|
|
52285
|
+
}
|
|
52286
|
+
}
|
|
52287
|
+
async function probeGhAuth() {
|
|
52288
|
+
try {
|
|
52289
|
+
await execFile3("gh", ["auth", "status"], { timeout: 1500 });
|
|
52290
|
+
return true;
|
|
52291
|
+
} catch {
|
|
52292
|
+
return false;
|
|
52293
|
+
}
|
|
52294
|
+
}
|
|
52295
|
+
async function probeForgeDiskFreeMb(homeDir) {
|
|
52296
|
+
const dir = path4.join(homeDir, ".rost", "forge");
|
|
52297
|
+
try {
|
|
52298
|
+
await mkdir2(dir, { recursive: true, mode: 448 });
|
|
52299
|
+
const stats = await statfs(dir);
|
|
52300
|
+
return Math.floor(stats.bavail * stats.bsize / (1024 * 1024));
|
|
52301
|
+
} catch {
|
|
52302
|
+
return null;
|
|
52303
|
+
}
|
|
52304
|
+
}
|
|
51967
52305
|
async function post2(fetchImpl, appUrl2, pathName, body, token) {
|
|
51968
52306
|
const headers = { "content-type": "application/json" };
|
|
51969
52307
|
if (token) {
|
|
@@ -52026,7 +52364,7 @@ async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtim
|
|
|
52026
52364
|
throw new Error(`${claimedUnitLabel(input.kind)} start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
|
|
52027
52365
|
}
|
|
52028
52366
|
const turnRuntime = runtimeForClaimedUnit(input.item, runtime, input.kind);
|
|
52029
|
-
const result = turnRuntime ? await runLocalTurn(appUrl2, input.item, turnRuntime, input.kind, state) : {
|
|
52367
|
+
const result = turnRuntime ? await runLocalTurn(appUrl2, input.item, turnRuntime, input.kind, state, config2.sandbox) : {
|
|
52030
52368
|
ok: false,
|
|
52031
52369
|
summary: `Runner runtime ${runtime} cannot execute requested ${String(input.item.brain ?? "unknown")} turn.`
|
|
52032
52370
|
};
|
|
@@ -52066,14 +52404,14 @@ function runtimeForClaimedUnit(item, fallback, kind) {
|
|
|
52066
52404
|
}
|
|
52067
52405
|
return fallback === "claude" ? "claude" : null;
|
|
52068
52406
|
}
|
|
52069
|
-
function runLocalTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
52070
|
-
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder, kind) : runClaudeTurn(appUrl2, workOrder, kind, state);
|
|
52407
|
+
function runLocalTurn(appUrl2, workOrder, runtime, kind, state, sandbox) {
|
|
52408
|
+
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder, kind, sandbox) : runClaudeTurn(appUrl2, workOrder, kind, state, sandbox);
|
|
52071
52409
|
}
|
|
52072
|
-
function runClaudeTurn(appUrl2, workOrder, kind, state) {
|
|
52073
|
-
return spawnRunnerTurn(appUrl2, workOrder, "claude", kind, state);
|
|
52410
|
+
function runClaudeTurn(appUrl2, workOrder, kind, state, sandbox) {
|
|
52411
|
+
return spawnRunnerTurn(appUrl2, workOrder, "claude", kind, sandbox, state);
|
|
52074
52412
|
}
|
|
52075
|
-
function runCodexTurn(appUrl2, workOrder, kind) {
|
|
52076
|
-
return spawnRunnerTurn(appUrl2, workOrder, "codex", kind);
|
|
52413
|
+
function runCodexTurn(appUrl2, workOrder, kind, sandbox) {
|
|
52414
|
+
return spawnRunnerTurn(appUrl2, workOrder, "codex", kind, sandbox);
|
|
52077
52415
|
}
|
|
52078
52416
|
function buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId) {
|
|
52079
52417
|
if (runtime === "codex") {
|
|
@@ -52098,12 +52436,12 @@ function buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId) {
|
|
|
52098
52436
|
]
|
|
52099
52437
|
};
|
|
52100
52438
|
}
|
|
52101
|
-
async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
52439
|
+
async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, sandbox, state) {
|
|
52102
52440
|
const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
|
|
52103
52441
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
52104
52442
|
const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
|
|
52105
52443
|
const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
|
|
52106
|
-
const configPath =
|
|
52444
|
+
const configPath = path4.join(tmpdir(), `rost-runner-mcp-${createHash("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
|
|
52107
52445
|
const context = {
|
|
52108
52446
|
work_order_id: kind === "work_order" ? workOrder.id : null,
|
|
52109
52447
|
turn_execution_id: kind === "turn_execution" ? workOrder.id : null,
|
|
@@ -52133,7 +52471,17 @@ async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
|
52133
52471
|
`, { mode: 384 });
|
|
52134
52472
|
await chmod(configPath, 384);
|
|
52135
52473
|
const resumeSessionId = runtime === "claude" ? getClaudeResumeSessionId(state, workOrder) : void 0;
|
|
52136
|
-
const
|
|
52474
|
+
const built = buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId);
|
|
52475
|
+
let sandboxProfilePath = null;
|
|
52476
|
+
if (sandbox.kind === "seatbelt") {
|
|
52477
|
+
sandboxProfilePath = path4.join(
|
|
52478
|
+
tmpdir(),
|
|
52479
|
+
`rost-runner-sandbox-${createHash("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
|
|
52480
|
+
);
|
|
52481
|
+
await writeFile2(sandboxProfilePath, sandbox.profile, { mode: 384 });
|
|
52482
|
+
await chmod(sandboxProfilePath, 384);
|
|
52483
|
+
}
|
|
52484
|
+
const { command, args } = wrapCommandWithSandbox(sandbox, built.command, built.args, sandboxProfilePath);
|
|
52137
52485
|
const expectSessionEnvelope = runtime === "claude" && kind === "turn_execution";
|
|
52138
52486
|
return new Promise((resolve2) => {
|
|
52139
52487
|
let settled = false;
|
|
@@ -52142,7 +52490,10 @@ async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
|
52142
52490
|
return;
|
|
52143
52491
|
}
|
|
52144
52492
|
settled = true;
|
|
52145
|
-
void
|
|
52493
|
+
void rm3(configPath, { force: true });
|
|
52494
|
+
if (sandboxProfilePath) {
|
|
52495
|
+
void rm3(sandboxProfilePath, { force: true });
|
|
52496
|
+
}
|
|
52146
52497
|
resolve2(resumeSessionId ? { ...result, resumedFromSessionId: resumeSessionId } : result);
|
|
52147
52498
|
};
|
|
52148
52499
|
const child = spawn(command, args, {
|
|
@@ -52275,10 +52626,10 @@ function usage() {
|
|
|
52275
52626
|
|
|
52276
52627
|
// src/runner-service.ts
|
|
52277
52628
|
import { execFile as execFileCallback4 } from "node:child_process";
|
|
52278
|
-
import { realpathSync } from "node:fs";
|
|
52279
|
-
import { access as access2, mkdir as mkdir3, readFile as
|
|
52280
|
-
import { homedir as
|
|
52281
|
-
import
|
|
52629
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
52630
|
+
import { access as access2, mkdir as mkdir3, readFile as readFile4, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
52631
|
+
import { homedir as homedir3 } from "node:os";
|
|
52632
|
+
import path5 from "node:path";
|
|
52282
52633
|
import { promisify as promisify4 } from "node:util";
|
|
52283
52634
|
var execFile4 = promisify4(execFileCallback4);
|
|
52284
52635
|
var SERVICE_VERBS = ["install-service", "start", "stop", "restart", "status", "logs", "uninstall"];
|
|
@@ -52295,9 +52646,9 @@ function isRunnerServiceInvocation(args) {
|
|
|
52295
52646
|
function runnerServicePaths(homeDir, runnerName) {
|
|
52296
52647
|
const safeName = safeRunnerName(runnerName);
|
|
52297
52648
|
return {
|
|
52298
|
-
launchAgentPath:
|
|
52299
|
-
stateFile:
|
|
52300
|
-
logDir:
|
|
52649
|
+
launchAgentPath: path5.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
|
|
52650
|
+
stateFile: path5.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
|
|
52651
|
+
logDir: path5.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
|
|
52301
52652
|
};
|
|
52302
52653
|
}
|
|
52303
52654
|
function renderLaunchdPlist(config2) {
|
|
@@ -52337,9 +52688,9 @@ ${programArgsXml}
|
|
|
52337
52688
|
<key>RunAtLoad</key>
|
|
52338
52689
|
<true/>
|
|
52339
52690
|
<key>StandardOutPath</key>
|
|
52340
|
-
<string>${escapePlist(
|
|
52691
|
+
<string>${escapePlist(path5.join(config2.logDir, "stdout.log"))}</string>
|
|
52341
52692
|
<key>StandardErrorPath</key>
|
|
52342
|
-
<string>${escapePlist(
|
|
52693
|
+
<string>${escapePlist(path5.join(config2.logDir, "stderr.log"))}</string>
|
|
52343
52694
|
</dict>
|
|
52344
52695
|
</plist>
|
|
52345
52696
|
`;
|
|
@@ -52374,7 +52725,7 @@ ${serviceUsage()}
|
|
|
52374
52725
|
userCode: parsed.userCode,
|
|
52375
52726
|
nodePath: options.nodePath ?? resolveNodePath(),
|
|
52376
52727
|
binPath: options.binPath ?? resolveBinPath(),
|
|
52377
|
-
homeDir: options.homeDir ??
|
|
52728
|
+
homeDir: options.homeDir ?? homedir3(),
|
|
52378
52729
|
platform: options.platform ?? process.platform,
|
|
52379
52730
|
exec: options.exec ?? defaultExec,
|
|
52380
52731
|
uid: options.uid ?? (process.getuid ? process.getuid() : 0)
|
|
@@ -52406,8 +52757,8 @@ async function installService(options) {
|
|
|
52406
52757
|
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
52407
52758
|
const label = serviceLabel(safeName);
|
|
52408
52759
|
await mkdir3(paths.logDir, { recursive: true });
|
|
52409
|
-
await mkdir3(
|
|
52410
|
-
await mkdir3(
|
|
52760
|
+
await mkdir3(path5.dirname(paths.stateFile), { recursive: true });
|
|
52761
|
+
await mkdir3(path5.dirname(paths.launchAgentPath), { recursive: true });
|
|
52411
52762
|
if (options.userCode !== void 0) {
|
|
52412
52763
|
const paired = await pairRunnerOnce(options, safeName, paths.stateFile, options.userCode);
|
|
52413
52764
|
if (!paired) {
|
|
@@ -52585,8 +52936,8 @@ async function logsService(options) {
|
|
|
52585
52936
|
const safeName = safeRunnerName(options.runnerName);
|
|
52586
52937
|
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
52587
52938
|
const label = serviceLabel(safeName);
|
|
52588
|
-
const stdoutLog =
|
|
52589
|
-
const stderrLog =
|
|
52939
|
+
const stdoutLog = path5.join(paths.logDir, "stdout.log");
|
|
52940
|
+
const stderrLog = path5.join(paths.logDir, "stderr.log");
|
|
52590
52941
|
options.io.stdout.write(
|
|
52591
52942
|
`${[
|
|
52592
52943
|
`runner service logs for ${label}:`,
|
|
@@ -52617,7 +52968,7 @@ async function uninstallService(options) {
|
|
|
52617
52968
|
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
52618
52969
|
let removed = false;
|
|
52619
52970
|
try {
|
|
52620
|
-
await
|
|
52971
|
+
await rm4(paths.launchAgentPath, { force: true });
|
|
52621
52972
|
removed = true;
|
|
52622
52973
|
} catch (error51) {
|
|
52623
52974
|
options.io.stderr.write(
|
|
@@ -52761,11 +53112,11 @@ function resolveNodePath() {
|
|
|
52761
53112
|
}
|
|
52762
53113
|
function buildServicePath(nodePath, homeDir) {
|
|
52763
53114
|
const entries = [
|
|
52764
|
-
|
|
53115
|
+
path5.dirname(nodePath),
|
|
52765
53116
|
"/opt/homebrew/bin",
|
|
52766
53117
|
"/usr/local/bin",
|
|
52767
|
-
|
|
52768
|
-
|
|
53118
|
+
path5.join(homeDir, ".local", "bin"),
|
|
53119
|
+
path5.join(homeDir, "bin"),
|
|
52769
53120
|
"/usr/bin",
|
|
52770
53121
|
"/bin",
|
|
52771
53122
|
"/usr/sbin",
|
|
@@ -52810,7 +53161,7 @@ function resolveBinPath() {
|
|
|
52810
53161
|
return cliBrand.binName;
|
|
52811
53162
|
}
|
|
52812
53163
|
try {
|
|
52813
|
-
return
|
|
53164
|
+
return realpathSync2(entry);
|
|
52814
53165
|
} catch {
|
|
52815
53166
|
return entry;
|
|
52816
53167
|
}
|
|
@@ -52829,7 +53180,7 @@ async function fileExists(file2) {
|
|
|
52829
53180
|
}
|
|
52830
53181
|
async function readTail(file2, maxBytes) {
|
|
52831
53182
|
try {
|
|
52832
|
-
const content = await
|
|
53183
|
+
const content = await readFile4(file2, "utf8");
|
|
52833
53184
|
return content.length > maxBytes ? content.slice(content.length - maxBytes) : content;
|
|
52834
53185
|
} catch {
|
|
52835
53186
|
return null;
|
|
@@ -53473,7 +53824,7 @@ function isCliEntrypoint(moduleUrl, argvPath) {
|
|
|
53473
53824
|
return false;
|
|
53474
53825
|
}
|
|
53475
53826
|
try {
|
|
53476
|
-
return
|
|
53827
|
+
return realpathSync3(fileURLToPath3(moduleUrl)) === realpathSync3(argvPath);
|
|
53477
53828
|
} catch {
|
|
53478
53829
|
return moduleUrl === pathToFileURL(argvPath).href;
|
|
53479
53830
|
}
|