@rosthq/cli 0.7.28 → 0.7.30
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/generated/command-manifest.d.ts.map +1 -1
- package/dist/index.js +494 -114
- 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.map +1 -1
- package/package.json +1 -1
- package/prompts/how-tos/human-confirmations.md +3 -0
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(),
|
|
@@ -43312,6 +43319,82 @@ var softwareGithubIssueIntakeMetadataSchema = external_exports.object({
|
|
|
43312
43319
|
labels: external_exports.array(external_exports.string().trim().min(1).max(80)).max(50).default([]),
|
|
43313
43320
|
requested_automation_mode: softwareAutomationModeSchema.optional()
|
|
43314
43321
|
}).strict();
|
|
43322
|
+
var softwareVercelDeploymentStatusSchema = external_exports.enum(["queued", "initializing", "building", "ready", "error", "canceled", "unknown"]);
|
|
43323
|
+
var softwareVercelConnectionConnectInputSchema = external_exports.object({
|
|
43324
|
+
account_id: external_exports.string().trim().min(1).max(200),
|
|
43325
|
+
account_name: external_exports.string().trim().min(1).max(255).nullable().optional(),
|
|
43326
|
+
team_id: external_exports.string().trim().min(1).max(200).nullable().optional(),
|
|
43327
|
+
team_slug: external_exports.string().trim().min(1).max(255).nullable().optional(),
|
|
43328
|
+
user_id: external_exports.string().trim().min(1).max(200).nullable().optional(),
|
|
43329
|
+
configuration_id: external_exports.string().trim().min(1).max(200).nullable().optional(),
|
|
43330
|
+
access_token_vault_ref: external_exports.string().trim().min(12).max(700),
|
|
43331
|
+
scopes: external_exports.array(external_exports.string().trim().min(1).max(120)).max(40).default([])
|
|
43332
|
+
}).strict();
|
|
43333
|
+
var softwareVercelConnectionConnectOutputSchema = external_exports.object({
|
|
43334
|
+
vercel_connection_id: uuid10,
|
|
43335
|
+
account_id: external_exports.string(),
|
|
43336
|
+
team_id: external_exports.string().nullable()
|
|
43337
|
+
});
|
|
43338
|
+
var softwareVercelProjectLinkInputSchema = external_exports.object({
|
|
43339
|
+
software_project_id: uuid10,
|
|
43340
|
+
vercel_connection_id: uuid10,
|
|
43341
|
+
vercel_project_id: external_exports.string().trim().min(1).max(200),
|
|
43342
|
+
vercel_project_name: external_exports.string().trim().min(1).max(255),
|
|
43343
|
+
framework: external_exports.string().trim().min(1).max(120).nullable().optional(),
|
|
43344
|
+
root_directory: external_exports.string().trim().min(1).max(500).nullable().optional(),
|
|
43345
|
+
production_branch: external_exports.string().trim().min(1).max(255).nullable().optional(),
|
|
43346
|
+
git_repository: external_exports.record(external_exports.string().min(1), external_exports.unknown()).default({})
|
|
43347
|
+
}).strict();
|
|
43348
|
+
var softwareVercelProjectLinkOutputSchema = external_exports.object({
|
|
43349
|
+
vercel_project_link_id: uuid10,
|
|
43350
|
+
software_project_id: uuid10,
|
|
43351
|
+
vercel_project_id: external_exports.string()
|
|
43352
|
+
});
|
|
43353
|
+
var softwareVercelConnectionViewSchema = external_exports.object({
|
|
43354
|
+
id: uuid10,
|
|
43355
|
+
account_id: external_exports.string(),
|
|
43356
|
+
account_name: external_exports.string().nullable(),
|
|
43357
|
+
team_id: external_exports.string().nullable(),
|
|
43358
|
+
team_slug: external_exports.string().nullable(),
|
|
43359
|
+
user_id: external_exports.string().nullable(),
|
|
43360
|
+
configuration_id: external_exports.string().nullable(),
|
|
43361
|
+
status: external_exports.enum(["active", "revoked"]),
|
|
43362
|
+
connected_at: external_exports.string()
|
|
43363
|
+
});
|
|
43364
|
+
var softwareVercelProjectLinkViewSchema = external_exports.object({
|
|
43365
|
+
id: uuid10,
|
|
43366
|
+
software_project_id: uuid10,
|
|
43367
|
+
software_project_name: external_exports.string(),
|
|
43368
|
+
software_project_slug: external_exports.string(),
|
|
43369
|
+
vercel_connection_id: uuid10,
|
|
43370
|
+
vercel_project_id: external_exports.string(),
|
|
43371
|
+
vercel_project_name: external_exports.string(),
|
|
43372
|
+
framework: external_exports.string().nullable(),
|
|
43373
|
+
root_directory: external_exports.string().nullable(),
|
|
43374
|
+
production_branch: external_exports.string().nullable(),
|
|
43375
|
+
status: external_exports.enum(["active", "revoked"])
|
|
43376
|
+
});
|
|
43377
|
+
var softwareVercelProjectListInputSchema = external_exports.object({}).strict();
|
|
43378
|
+
var softwareVercelProjectListOutputSchema = external_exports.object({
|
|
43379
|
+
connections: external_exports.array(softwareVercelConnectionViewSchema),
|
|
43380
|
+
project_links: external_exports.array(softwareVercelProjectLinkViewSchema)
|
|
43381
|
+
});
|
|
43382
|
+
var softwareVercelDeployPreviewInputSchema = external_exports.object({
|
|
43383
|
+
software_project_id: uuid10,
|
|
43384
|
+
commit_sha: external_exports.string().trim().regex(/^[A-Fa-f0-9]{7,64}$/),
|
|
43385
|
+
git_ref: external_exports.string().trim().min(1).max(255),
|
|
43386
|
+
build_request_id: uuid10.optional(),
|
|
43387
|
+
phase_run_id: uuid10.optional(),
|
|
43388
|
+
seat_id: uuid10.optional(),
|
|
43389
|
+
required_secret_keys: external_exports.array(external_exports.string().trim().min(1).max(200)).max(25).default([])
|
|
43390
|
+
}).strict();
|
|
43391
|
+
var softwareVercelDeployPreviewOutputSchema = external_exports.object({
|
|
43392
|
+
vercel_deployment_record_id: uuid10,
|
|
43393
|
+
deployment_id: external_exports.string(),
|
|
43394
|
+
deployment_url: external_exports.string().nullable(),
|
|
43395
|
+
commit_sha: external_exports.string(),
|
|
43396
|
+
status: softwareVercelDeploymentStatusSchema
|
|
43397
|
+
});
|
|
43315
43398
|
|
|
43316
43399
|
// ../../packages/protocol/src/sync-brief.ts
|
|
43317
43400
|
var uuidSchema14 = external_exports.string().uuid();
|
|
@@ -44556,7 +44639,7 @@ No orphan agents. No raw secrets in prompts, logs, or tool arguments. No durable
|
|
|
44556
44639
|
order: 41,
|
|
44557
44640
|
title: "Add agents to your Responsibility Graph",
|
|
44558
44641
|
summary: "The visual journey for adding an agent seat: where to start, choosing a mode, placing the seat, naming a Steward, setup, the safety gates, and go-live.",
|
|
44559
|
-
version: "2026-07-04.
|
|
44642
|
+
version: "2026-07-04.2",
|
|
44560
44643
|
public: true,
|
|
44561
44644
|
audiences: ["human", "in_app_agent"],
|
|
44562
44645
|
stages: ["staffing"],
|
|
@@ -44578,7 +44661,7 @@ A seat is a function, not a person. Adding an agent means staffing a seat with a
|
|
|
44578
44661
|
You can open agent creation from three places:
|
|
44579
44662
|
|
|
44580
44663
|
- **Responsibility Graph.** Hover or focus a seat node and use its **Add agent** action to add an agent seat under that seat. The graph toolbar's **Add agent** action adds an agent and then asks where it should report. On an empty or small graph, **Add first agent** starts the same flow with the founder or owner seat as the default Steward.
|
|
44581
|
-
- **Left sidebar.** The **Agents** item
|
|
44664
|
+
- **Left sidebar.** The **Agents** item opens the agents surface. **Add agent** at the top of that surface is the primary action.
|
|
44582
44665
|
- **Agents surface.** The unified agents page has **Templates**, **Custom**, and **Existing** modes. A template's **Use template** action enters the same wizard with that template selected.
|
|
44583
44666
|
|
|
44584
44667
|
Starting from a selected graph seat carries the parent seat and a default Steward candidate into the wizard so you do not re-pick them.
|
|
@@ -45049,7 +45132,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
45049
45132
|
order: 48,
|
|
45050
45133
|
title: "CLI and MCP installation guide",
|
|
45051
45134
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
45052
|
-
version: "2026-07-
|
|
45135
|
+
version: "2026-07-05.1",
|
|
45053
45136
|
public: true,
|
|
45054
45137
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
45055
45138
|
stages: ["company_setup", "staffing"],
|
|
@@ -45497,7 +45580,7 @@ These ergonomic wrappers (including the \`{{cli}} agent\` group) require **{{cli
|
|
|
45497
45580
|
|
|
45498
45581
|
**Generated verbs (0.6.0+).** Every registry command now has a first-class \`{{cli}} <namespace> <action>\` verb, projected directly from the command's input schema \u2014 so namespaces that previously had no ergonomic wrapper (for example \`{{cli}} measurable create\`, \`{{cli}} work_order enqueue\`, \`{{cli}} mcp_token create\`) are usable without dropping to the raw \`command <id>\` path. Flags follow the schema field names (\`--seat-id\`, \`--name\`, \u2026); read a command's exact input offline with \`{{cli}} <namespace> <action> --schema\` before calling it, and pass a nested payload with \`--input '<json>'\` when a field is a complex object. \`--json\` still selects machine output, \`--seat <id>\` still sets seat scope, and \`{{cli}} command <id> --json '<body>'\` remains the universal fallback.
|
|
45499
45582
|
|
|
45500
|
-
The signed-in app exposes the same Skill command surface
|
|
45583
|
+
The signed-in app exposes the same Skill command surface at **Skills**, linked from the Agents page (the nav collapse moved it out of the primary sidebar). Use \`/skills\` to filter the tenant library, \`/skills/new\` to build canonical \`SKILL.md\` frontmatter with \`tool.catalog\` dependencies, \`/skills/import\` for bounded GitHub or upload imports, and each Skill detail page to review validation, publish, check Seat dependencies, and assign a published version. During \`/agents/new\`, \`agent_setup.get\` can return \`next_action: "choose_skills"\`; selected Skills are proposed immutable-version assignments and required Skill tools block sign-off/go-live until tool configuration is ready.
|
|
45501
45584
|
|
|
45502
45585
|
| Command | Command ids | Purpose | Scope | Safe example |
|
|
45503
45586
|
|---|---|---|---|---|
|
|
@@ -45528,7 +45611,7 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
|
|
|
45528
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\` |
|
|
45529
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\` |
|
|
45530
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\` |
|
|
45531
|
-
| \`{{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"\` |
|
|
45532
45615
|
|
|
45533
45616
|
Skills wrapper help:
|
|
45534
45617
|
|
|
@@ -45769,7 +45852,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
|
|
|
45769
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. |
|
|
45770
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 \`{}\`. |
|
|
45771
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. |
|
|
45772
|
-
| \`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>", ...}\`). |
|
|
45773
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. |
|
|
45774
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. |
|
|
45775
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. |
|
|
@@ -45795,6 +45878,8 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
|
|
|
45795
45878
|
| \`rost_set_a_forge_github_automation_policy\` | \`software_factory.github.policy.upsert\` | Set the server-side GitHub automation policy: allowed actions, automation-mode ceiling, and operations requiring humans. The allowed-actions and human-gated-actions lists are stored server-side as structured JSON arrays. Owner-only and human-gated. | Tenant-admin | Call with \`{"software_project_id":"<project-id>","automation_mode_ceiling":"pr_only","allowed_actions":["webhook_intake","installation_token"],"requires_human_for":["deploy_production"]}\`. |
|
|
45796
45879
|
| \`rost_create_a_forge_github_installation_token\` | \`software_factory.github.installation_token.create\` | Mint a short-lived GitHub App installation token for one bound repository after policy evaluation. The token is returned once and never stored. | Tenant | Call with \`{"software_project_id":"<project-id>","repository_id":789,"requested_actions":["read"],"requested_automation_mode":"plan_only"}\`. |
|
|
45797
45880
|
| \`rost_list_forge_github_installations\` | \`software_factory.github.installation.list\` | List connected GitHub App installations and each repository's bind state (connected or active) with the Forge project it maps to. Read-only. | Tenant | Call with \`{}\`; a \`connected\` repository is verified but not yet bound to a project. |
|
|
45881
|
+
| \`rost_list_forge_vercel_links\` | \`software_factory.vercel.project.list\` | List connected Vercel accounts/teams and linked existing Vercel projects. Read-only; no OAuth tokens or vault refs are returned. | Tenant | Call with \`{}\`; tenant admins connect/link from \`/forge/settings/vercel\`. |
|
|
45882
|
+
| \`rost_create_a_forge_vercel_preview_deployment\` | \`software_factory.vercel.deploy_preview\` | Create a Vercel preview deployment for a linked project after Forge deploy-preview authority, the Vercel OAuth vault-ref connection, and required preview secret/config grants are verified. | Tenant | Call with \`{"software_project_id":"<project-id>","commit_sha":"<sha>","git_ref":"feature/example","seat_id":"<seat-id>"}\`; returns deployment metadata, not tokens. |
|
|
45798
45883
|
| \`rost_list_forge_runner_capacity\` | \`software_factory.capacity.list\` | List Forge runner capacity observations (advisory scheduling input only, never an authority input). Read-only; requires the Forge add-on. | Tenant | Call with \`{}\` or \`{"runner_id":"<runner-id>"}\`. |
|
|
45799
45884
|
|
|
45800
45885
|
The web Forge control room at \`/forge\` uses the same command path for project selection, build request creation/list/detail reads, request pause/cancel/resume controls, runner capacity, configuration status, and Developer Team install. A request detail page shows phase history, open gates, plan progress, loop/time/cost ceilings, and latest plan-review questions from \`software_factory.request.show\`; answering a clarification, approving a plan, rejecting it, pausing, canceling, or resuming calls the same Forge commands exposed over CLI/MCP. Lifecycle controls deliberately stage pending confirmations before changing runner state. The install button also stages a pending confirmation before durable seats, Skills, authority grants, or live agent activation are applied; the install page states plainly that approving activates the 6 agents at a pull-request-only, observe-first posture and surfaces the company autonomy ceiling.
|
|
@@ -45809,6 +45894,12 @@ Tenant GitHub App connect and bind are intentionally not CLI or MCP mutation too
|
|
|
45809
45894
|
|
|
45810
45895
|
The Forge GitHub App itself is org-owned. App private key, webhook secret, and client secret values are platform-owned ROST operator credentials stored through the vault's reserved ROST system namespace, not through a customer tenant namespace or a tenant UUID deployment variable. Manifest conversion and manual secret storage also require the app-level \`GITHUB_APP_OPERATOR_EMAILS\` allowlist, which should contain only approved ROST platform operator emails. Deployment config carries only refs (\`GITHUB_APP_PRIVATE_KEY_REF\`, \`GITHUB_APP_WEBHOOK_SECRET_REF\`, \`GITHUB_APP_CLIENT_SECRET_REF\`); raw secret env vars are local/legacy compatibility only. The dogfood app is \`ROST Forge\` under \`agenticoperatingsystem\` with App ID \`4208495\` and Client ID \`Iv23liImH7qlBvXYQ0Wx\`. The base app omits \`administration\` and \`workflows\`; elevated repo creation or workflow mutation needs a separate human-approved power path.
|
|
45811
45896
|
|
|
45897
|
+
#### Forge Vercel access
|
|
45898
|
+
|
|
45899
|
+
Forge Vercel access is through a tenant-bound OAuth connection, never a browser-visible token. Tenant admins start at \`/forge/settings/vercel\`; the callback exchanges the short-lived Vercel code server-side, stores the access token in the vault, and records only account/team metadata plus the vault-ref pointer via \`software_factory.vercel.connection.connect\`. Existing Vercel projects are linked through \`software_factory.vercel.project.link\`; greenfield project creation is intentionally absent from this slice and requires a future explicit authority flag.
|
|
45900
|
+
|
|
45901
|
+
Preview deployment creation is a governed command. \`software_factory.vercel.deploy_preview\` requires an active Vercel project link, a Vercel OAuth vault-ref connection, active \`deploy_preview\` Forge authority for the requesting seat when a seat is supplied, and any requested preview secret/config grants. It writes the deployment id, URL, commit SHA, checks, status, and error metadata into \`software_vercel_deployments\`; build-request deployments also write a Forge artifact and event. Raw OAuth tokens and vault refs are never returned.
|
|
45902
|
+
|
|
45812
45903
|
### Seat-scoped operating tools
|
|
45813
45904
|
|
|
45814
45905
|
Seat-scoped MCP tokens expose the operating protocol below. The server still checks the seat's permission manifest, Charter, task ownership, and tenant boundary. These are the **seat-token equivalents** of the CLI \`{{cli}} task\` / \`{{cli}} signal\` / \`{{cli}} friction\` wrappers and the tenant-admin task tools above: \`rost_get_tasks\`, \`{{cli}} task list\`, and \`task.list\` are the same operation reached three different ways. The command-id column makes the mapping explicit.
|
|
@@ -46813,7 +46904,7 @@ Do not claim that a live charge happened unless Stripe test/live evidence proves
|
|
|
46813
46904
|
order: 72,
|
|
46814
46905
|
title: "Settings guide",
|
|
46815
46906
|
summary: "How to use Settings as the control plane for company access, channels, providers, tokens, and operating defaults.",
|
|
46816
|
-
version: "2026-07-04.
|
|
46907
|
+
version: "2026-07-04.3",
|
|
46817
46908
|
public: true,
|
|
46818
46909
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
46819
46910
|
stages: ["company_setup", "staffing"],
|
|
@@ -46836,9 +46927,9 @@ Start with members and invites, then provider and channel connections, then MCP
|
|
|
46836
46927
|
|
|
46837
46928
|
## Navigating Settings
|
|
46838
46929
|
|
|
46839
|
-
Settings is a left-rail master\u2013detail layout: a grouped rail (General, Team & Access, Billing, AI & Agents, Connections) selects one section at a time, shown in the detail pane. The active section is a URL search param, so \`/settings?section=<id>\` is linkable and survives a refresh. The Connections section is the workspace-level provider surface: Google, Slack, QuickBooks, local runners, seat-scoped agent tokens, and stored secrets appear as provider cards with status, used-for chips, actions, and the shared Access language (**Off / Read / Draft / Act with approval**). Detailed management forms for tokens, channels, runners, provider links, and stored vault entries remain reachable from those cards.
|
|
46930
|
+
Settings is reached from the footer gear in the left sidebar (the nav collapse moved it out of the primary item list). It is a left-rail master\u2013detail layout: a grouped rail (General, Team & Access, Billing, AI & Agents, Connections, Advanced) selects one section at a time, shown in the detail pane. The active section is a URL search param, so \`/settings?section=<id>\` is linkable and survives a refresh. The Connections section is the workspace-level provider surface: Google, Slack, QuickBooks, local runners, seat-scoped agent tokens, and stored secrets appear as provider cards with status, used-for chips, actions, and the shared Access language (**Off / Read / Draft / Act with approval**). Detailed management forms for tokens, channels, runners, provider links, and stored vault entries remain reachable from those cards.
|
|
46840
46931
|
|
|
46841
|
-
Legacy hash deep-links still resolve \u2014 \`/settings#integrations\`, \`#channels\`, and \`#vault\` redirect to \`?section=connections\`; other hashes such as \`#members\` and \`#guardrails\` redirect to their matching section. \`#runners\` is the one exception: it keeps resolving to the dedicated Connected machines management table (not the Connections overview), since System Health and seat pages deep-link there expecting the per-runner revoke actions. Documented shortcut links resolve to the right section instead of a dead end: \`/settings/integrations\` and \`/readiness\` open the Connections section (connector readiness lives inside it, not on a standalone page),
|
|
46932
|
+
Legacy hash deep-links still resolve \u2014 \`/settings#integrations\`, \`#channels\`, and \`#vault\` redirect to \`?section=connections\`; other hashes such as \`#members\` and \`#guardrails\` redirect to their matching section. \`#runners\` is the one exception: it keeps resolving to the dedicated Connected machines management table (not the Connections overview), since System Health and seat pages deep-link there expecting the per-runner revoke actions. Documented shortcut links resolve to the right section instead of a dead end: \`/settings/integrations\` and \`/readiness\` open the Connections section (connector readiness lives inside it, not on a standalone page), \`/settings/policy\` opens the Product learning section, and MCP/CLI access tokens live in the Agent access tokens section (not a separate \`/mcp\` page \u2014 \`/mcp\` is the MCP protocol endpoint itself, not a browsable settings screen). The Advanced group links out to \`/migration/ninety\`, the one settings-adjacent page with no home in the grouped rail. After you save a change, the app keeps you on the section you edited rather than returning to the top.
|
|
46842
46933
|
|
|
46843
46934
|
## What belongs in Settings
|
|
46844
46935
|
|
|
@@ -47092,7 +47183,7 @@ The runner should not store raw credentials in local notes, prompts, or logs. It
|
|
|
47092
47183
|
order: 76,
|
|
47093
47184
|
title: "Stock agents guide",
|
|
47094
47185
|
summary: "How to use stock agent templates as starting points while preserving seat-specific Charters and human approval.",
|
|
47095
|
-
version: "2026-07-
|
|
47186
|
+
version: "2026-07-04.1",
|
|
47096
47187
|
public: true,
|
|
47097
47188
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
47098
47189
|
stages: ["staffing"],
|
|
@@ -47111,7 +47202,7 @@ Stock agents are templates, not finished employees. Use them to accelerate setup
|
|
|
47111
47202
|
|
|
47112
47203
|
## Where to create agents
|
|
47113
47204
|
|
|
47114
|
-
Add agents from the unified agents surface. In the app, open it from the **Agents** item in the left sidebar
|
|
47205
|
+
Add agents from the unified agents surface. In the app, open it from the **Agents** item in the left sidebar, or from an **Add agent** action on the Responsibility Graph. The first screen asks for a creation mode \u2014 use a template, design a custom agent, or connect an existing agent \u2014 then walks the same conservative path for every mode: seat placement, Steward, draft Charter, manifest sign-off, sandbox dry run, and human go-live. Each step is command-backed, so the same workflow runs from the app, the CLI, and MCP.
|
|
47115
47206
|
|
|
47116
47207
|
## Template review
|
|
47117
47208
|
|
|
@@ -49005,7 +49096,7 @@ var COMMAND_MANIFEST = [
|
|
|
49005
49096
|
{ "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." },
|
|
49006
49097
|
{ "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." },
|
|
49007
49098
|
{ "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." },
|
|
49008
|
-
{ "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":
|
|
49099
|
+
{ "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." },
|
|
49009
49100
|
{ "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." },
|
|
49010
49101
|
{ "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." },
|
|
49011
49102
|
{ "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." },
|
|
@@ -49015,6 +49106,10 @@ var COMMAND_MANIFEST = [
|
|
|
49015
49106
|
{ "id": "software_factory.secret.request", "namespace": "software_factory", "action": "secret.request", "title": "Request a Forge secret grant", "description": "An agent or user REQUESTS access to a project secret by key (draft-and-confirm \u2014 no value, no grant). Records the ask as a durable event for a human to grant. Never accepts a secret value (invariant #5). Entitlement-gated.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required": true }, { "name": "environment", "flag": "environment", "type": "enum", "required": true, "enumValues": ["development", "preview", "production"] }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "action", "flag": "action", "type": "string", "required": true }, { "name": "key_name", "flag": "key-name", "type": "string", "required": true }, { "name": "reason", "flag": "reason", "type": "string", "required": false }], "hasComplexInput": false, "help": "Request access to a project secret by key; persists the request and creates a human approval task. Never accepts a secret value." },
|
|
49016
49107
|
{ "id": "software_factory.secret.revoke", "namespace": "software_factory", "action": "secret.revoke", "title": "Revoke a Forge secret grant", "description": "Revoke a Forge secret grant (teardown \u2014 a compromised seat's access can be cut immediately, ADR-0018 \xA76). Owner-only, human-gated. Entitlement-gated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "grant_id", "flag": "grant-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Revoke a Forge secret grant (teardown \u2014 cut a compromised seat's access immediately). Owner-only and human-gated." },
|
|
49017
49108
|
{ "id": "software_factory.secret.use", "namespace": "software_factory", "action": "secret.use", "title": "Verify Forge secret broker access", "description": "Verify a scoped secret broker grant without returning or opening the raw value. Runtime vault access happens only in the runner-side broker helper; this command writes a redacted authorization log and creates a request task for missing grants.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required": true }, { "name": "environment", "flag": "environment", "type": "enum", "required": true, "enumValues": ["development", "preview", "production"] }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "action", "flag": "action", "type": "string", "required": true }, { "name": "key_name", "flag": "key-name", "type": "string", "required": true }, { "name": "operation", "flag": "operation", "type": "enum", "required": false, "enumValues": ["use", "vercel_env_sync", "test_execution"] }], "hasComplexInput": false, "help": "Verify scoped broker access without returning or opening the raw value; missing grants create or reuse a request task and all attempts write redacted access logs." },
|
|
49109
|
+
{ "id": "software_factory.vercel.connection.connect", "namespace": "software_factory", "action": "vercel.connection.connect", "title": "Connect a Forge Vercel account", "description": "Record a Vercel OAuth account/team connection. The access token is stored only as a vault ref; this command persists bounded account metadata. Owner-only and human-gated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": false, "fields": [{ "name": "account_id", "flag": "account-id", "type": "string", "required": true }, { "name": "account_name", "flag": "account-name", "type": "string", "required": false }, { "name": "team_id", "flag": "team-id", "type": "string", "required": false }, { "name": "team_slug", "flag": "team-slug", "type": "string", "required": false }, { "name": "user_id", "flag": "user-id", "type": "string", "required": false }, { "name": "configuration_id", "flag": "configuration-id", "type": "string", "required": false }, { "name": "access_token_vault_ref", "flag": "access-token-vault-ref", "type": "string", "required": true }, { "name": "scopes", "flag": "scopes", "type": "array", "required": false, "itemType": "string" }], "hasComplexInput": false, "help": "Record a Vercel OAuth account/team connection from the Forge Vercel settings flow. Stores only a vault-ref pointer to the token. Owner-only and human-gated." },
|
|
49110
|
+
{ "id": "software_factory.vercel.deploy_preview", "namespace": "software_factory", "action": "vercel.deploy_preview", "title": "Create a Forge Vercel preview deployment", "description": "Create a Vercel preview deployment for a linked existing project after Forge authority, Vercel connection, and required secret/config grants are verified. Records deployment metadata as artifacts and audit rows.", "requiredScope": "tenant", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required": true }, { "name": "commit_sha", "flag": "commit-sha", "type": "string", "required": true }, { "name": "git_ref", "flag": "git-ref", "type": "string", "required": true }, { "name": "build_request_id", "flag": "build-request-id", "type": "string", "required": false }, { "name": "phase_run_id", "flag": "phase-run-id", "type": "string", "required": false }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": false }, { "name": "required_secret_keys", "flag": "required-secret-keys", "type": "array", "required": false, "itemType": "string" }], "hasComplexInput": false, "help": "Create a Vercel preview deployment for a linked project after Forge authority, Vercel connection, and required secret/config grants are verified." },
|
|
49111
|
+
{ "id": "software_factory.vercel.project.link", "namespace": "software_factory", "action": "vercel.project.link", "title": "Link a Forge project to Vercel", "description": "Link a Forge project to an existing Vercel project. Greenfield Vercel project creation is intentionally out of scope and remains behind a future authority flag.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": false, "fields": [{ "name": "software_project_id", "flag": "software-project-id", "type": "string", "required": true }, { "name": "vercel_connection_id", "flag": "vercel-connection-id", "type": "string", "required": true }, { "name": "vercel_project_id", "flag": "vercel-project-id", "type": "string", "required": true }, { "name": "vercel_project_name", "flag": "vercel-project-name", "type": "string", "required": true }, { "name": "framework", "flag": "framework", "type": "string", "required": false }, { "name": "root_directory", "flag": "root-directory", "type": "string", "required": false }, { "name": "production_branch", "flag": "production-branch", "type": "string", "required": false }], "hasComplexInput": true, "help": "Link an existing Vercel project to a Forge software project. Owner-only and human-gated; greenfield project creation remains a future explicit authority." },
|
|
49112
|
+
{ "id": "software_factory.vercel.project.list", "namespace": "software_factory", "action": "vercel.project.list", "title": "List Forge Vercel links", "description": "List Vercel account/team connections and existing Vercel project links for this tenant. Read-only; token vault refs are never returned.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [], "hasComplexInput": false, "help": "List Vercel account/team connections and linked Vercel projects. Read-only; no OAuth tokens or vault refs are returned." },
|
|
49018
49113
|
{ "id": "staffing.assign", "namespace": "staffing", "action": "assign", "title": "Staff seat", "description": "Compatibility command for the DER-626 MCP staffing tool.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }], "hasComplexInput": true, "help": "Staff a seat only after the function, Charter, and human accountability are clear." },
|
|
49019
49114
|
{ "id": "staffing.assign_agent_dry_run", "namespace": "staffing", "action": "assign_agent_dry_run", "title": "Assign dry-run agent to seat", "description": "Assign an agent occupancy while the agent remains in dry-run.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "agent_id", "flag": "agent-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Start agent staffing with dry runs and a named human Steward." },
|
|
49020
49115
|
{ "id": "staffing.assign_user", "namespace": "staffing", "action": "assign_user", "title": "Assign user to seat", "description": "Assign a human user occupancy to a seat.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "user_id", "flag": "user-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Assign humans to seats after the seat accountability is clear." },
|
|
@@ -49231,9 +49326,9 @@ function coerceScalar(field3, raw) {
|
|
|
49231
49326
|
}
|
|
49232
49327
|
}
|
|
49233
49328
|
function rawValueForField(entry, field3, parsed, aliasFlags) {
|
|
49234
|
-
const
|
|
49235
|
-
if (
|
|
49236
|
-
return
|
|
49329
|
+
const canonical2 = parsed.values.get(field3.flag);
|
|
49330
|
+
if (canonical2 !== void 0) {
|
|
49331
|
+
return canonical2;
|
|
49237
49332
|
}
|
|
49238
49333
|
for (const [aliasFlag, targetField] of aliasFlags) {
|
|
49239
49334
|
if (targetField === field3.name) {
|
|
@@ -51567,11 +51662,247 @@ function parseSemver2(value) {
|
|
|
51567
51662
|
// src/runner-serve.ts
|
|
51568
51663
|
import { execFile as execFileCallback3, spawn } from "node:child_process";
|
|
51569
51664
|
import { createHash } from "node:crypto";
|
|
51570
|
-
import { chmod, mkdir as mkdir2, readFile as readFile4, rename, rm as
|
|
51571
|
-
import { cpus, hostname as hostname3, platform, totalmem, tmpdir } from "node:os";
|
|
51572
|
-
import
|
|
51665
|
+
import { chmod, mkdir as mkdir2, readFile as readFile4, rename, rm as rm3, writeFile as writeFile2 } from "node:fs/promises";
|
|
51666
|
+
import { cpus, homedir as homedir2, hostname as hostname3, platform, totalmem, tmpdir } from "node:os";
|
|
51667
|
+
import path4 from "node:path";
|
|
51573
51668
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
51574
51669
|
import { promisify as promisify3 } from "node:util";
|
|
51670
|
+
|
|
51671
|
+
// src/runner-sandbox.ts
|
|
51672
|
+
import { existsSync as existsSync2, realpathSync } from "node:fs";
|
|
51673
|
+
import { readdir, rm as rm2, stat as stat2 } from "node:fs/promises";
|
|
51674
|
+
import path3 from "node:path";
|
|
51675
|
+
var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
51676
|
+
var RUNNER_TEMP_PREFIXES = [
|
|
51677
|
+
"rost-runner-mcp-",
|
|
51678
|
+
"rost-runner-sandbox-",
|
|
51679
|
+
"rost-runner-askpass-"
|
|
51680
|
+
];
|
|
51681
|
+
var SENSITIVE_READ_SUBPATHS = [
|
|
51682
|
+
".ssh",
|
|
51683
|
+
".aws",
|
|
51684
|
+
".gnupg",
|
|
51685
|
+
".config/gcloud",
|
|
51686
|
+
".config/gh",
|
|
51687
|
+
".kube",
|
|
51688
|
+
".docker",
|
|
51689
|
+
".npmrc"
|
|
51690
|
+
];
|
|
51691
|
+
var SENSITIVE_READ_LITERALS = [".netrc"];
|
|
51692
|
+
var SENSITIVE_WRITE_SUBPATHS = [
|
|
51693
|
+
".ssh",
|
|
51694
|
+
".aws",
|
|
51695
|
+
".gnupg",
|
|
51696
|
+
".config/git",
|
|
51697
|
+
"bin",
|
|
51698
|
+
"Library/LaunchAgents"
|
|
51699
|
+
];
|
|
51700
|
+
var SENSITIVE_WRITE_LITERALS = [
|
|
51701
|
+
".gitconfig",
|
|
51702
|
+
".zshrc",
|
|
51703
|
+
".zprofile",
|
|
51704
|
+
".zshenv",
|
|
51705
|
+
".zlogin",
|
|
51706
|
+
".bashrc",
|
|
51707
|
+
".bash_profile",
|
|
51708
|
+
".bash_login",
|
|
51709
|
+
".profile"
|
|
51710
|
+
];
|
|
51711
|
+
function parseSandboxMode(value) {
|
|
51712
|
+
const normalized = (value ?? "auto").trim().toLowerCase();
|
|
51713
|
+
if (normalized === "off" || normalized === "strict" || normalized === "auto" || normalized === "") {
|
|
51714
|
+
return normalized === "" ? "auto" : normalized;
|
|
51715
|
+
}
|
|
51716
|
+
return "auto";
|
|
51717
|
+
}
|
|
51718
|
+
function parseSandboxNetwork(value) {
|
|
51719
|
+
const normalized = (value ?? "").trim().toLowerCase();
|
|
51720
|
+
if (normalized === "" || normalized === "allow") {
|
|
51721
|
+
return { allowNetwork: true, unrecognized: null };
|
|
51722
|
+
}
|
|
51723
|
+
if (normalized === "deny") {
|
|
51724
|
+
return { allowNetwork: false, unrecognized: null };
|
|
51725
|
+
}
|
|
51726
|
+
return { allowNetwork: true, unrecognized: value ?? "" };
|
|
51727
|
+
}
|
|
51728
|
+
function sbplString(value) {
|
|
51729
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
51730
|
+
}
|
|
51731
|
+
function subpath(value) {
|
|
51732
|
+
return `(subpath ${sbplString(value)})`;
|
|
51733
|
+
}
|
|
51734
|
+
function literal2(value) {
|
|
51735
|
+
return `(literal ${sbplString(value)})`;
|
|
51736
|
+
}
|
|
51737
|
+
var STRICT_SYSTEM_READ_SUBPATHS = [
|
|
51738
|
+
"/usr",
|
|
51739
|
+
"/bin",
|
|
51740
|
+
"/sbin",
|
|
51741
|
+
"/System",
|
|
51742
|
+
"/Library",
|
|
51743
|
+
"/private/etc",
|
|
51744
|
+
"/private/var/db",
|
|
51745
|
+
"/dev",
|
|
51746
|
+
"/opt",
|
|
51747
|
+
"/nix"
|
|
51748
|
+
];
|
|
51749
|
+
function buildSeatbeltProfile(input) {
|
|
51750
|
+
const home = input.homeDir.replace(/\/+$/, "");
|
|
51751
|
+
const lines = ["(version 1)"];
|
|
51752
|
+
const denyReadBaseSubpaths = input.denyReadPaths.filter((p) => p.endsWith("/")).map((p) => subpath(p.replace(/\/+$/, "")));
|
|
51753
|
+
const denyReadLiterals = input.denyReadPaths.filter((p) => !p.endsWith("/")).map(literal2);
|
|
51754
|
+
const credentialReadDenies = [
|
|
51755
|
+
...SENSITIVE_READ_SUBPATHS.map((rel) => subpath(`${home}/${rel}`)),
|
|
51756
|
+
...SENSITIVE_READ_LITERALS.map((rel) => literal2(`${home}/${rel}`))
|
|
51757
|
+
];
|
|
51758
|
+
if (input.strict) {
|
|
51759
|
+
lines.push("(deny default)");
|
|
51760
|
+
lines.push("(allow process*)");
|
|
51761
|
+
lines.push("(allow sysctl-read)");
|
|
51762
|
+
lines.push("(allow mach-lookup)");
|
|
51763
|
+
lines.push("(allow iokit-open)");
|
|
51764
|
+
lines.push("(allow system-socket)");
|
|
51765
|
+
lines.push("(allow signal (target self))");
|
|
51766
|
+
lines.push("(allow file-read-metadata)");
|
|
51767
|
+
const strictReads = [
|
|
51768
|
+
...STRICT_SYSTEM_READ_SUBPATHS.map(subpath),
|
|
51769
|
+
subpath(`${home}/.claude`),
|
|
51770
|
+
subpath(`${home}/.codex`),
|
|
51771
|
+
subpath(input.tmpDir)
|
|
51772
|
+
];
|
|
51773
|
+
lines.push(`(allow file-read* ${strictReads.join(" ")})`);
|
|
51774
|
+
} else {
|
|
51775
|
+
lines.push("(allow default)");
|
|
51776
|
+
}
|
|
51777
|
+
lines.push("(deny appleevent-send)");
|
|
51778
|
+
if (denyReadBaseSubpaths.length > 0) {
|
|
51779
|
+
lines.push(`(deny file-read* ${denyReadBaseSubpaths.join(" ")})`);
|
|
51780
|
+
}
|
|
51781
|
+
const workspaceReads = [subpath(input.workspaceDir), ...input.allowWritePaths.map(subpath)];
|
|
51782
|
+
lines.push(`(allow file-read* ${workspaceReads.join(" ")})`);
|
|
51783
|
+
lines.push("(deny file-write*)");
|
|
51784
|
+
const allowWrites = [
|
|
51785
|
+
subpath(input.workspaceDir),
|
|
51786
|
+
subpath(input.tmpDir),
|
|
51787
|
+
subpath(`${home}/.claude`),
|
|
51788
|
+
subpath(`${home}/.codex`),
|
|
51789
|
+
...input.allowWritePaths.map(subpath),
|
|
51790
|
+
subpath("/dev/fd"),
|
|
51791
|
+
literal2("/dev/null"),
|
|
51792
|
+
literal2("/dev/stdout"),
|
|
51793
|
+
literal2("/dev/stderr"),
|
|
51794
|
+
literal2("/dev/tty"),
|
|
51795
|
+
literal2("/dev/dtracehelper")
|
|
51796
|
+
];
|
|
51797
|
+
lines.push(`(allow file-write* ${allowWrites.join(" ")})`);
|
|
51798
|
+
lines.push(`(deny file-read* ${[...credentialReadDenies, ...denyReadLiterals].join(" ")})`);
|
|
51799
|
+
const denyWrites = [
|
|
51800
|
+
...SENSITIVE_WRITE_SUBPATHS.map((rel) => subpath(`${home}/${rel}`)),
|
|
51801
|
+
...SENSITIVE_WRITE_LITERALS.map((rel) => literal2(`${home}/${rel}`))
|
|
51802
|
+
];
|
|
51803
|
+
lines.push(`(deny file-write* ${denyWrites.join(" ")})`);
|
|
51804
|
+
lines.push('(deny mach-lookup (global-name "com.apple.coreservices.appleevents"))');
|
|
51805
|
+
if (!input.allowNetwork) {
|
|
51806
|
+
lines.push("(deny network*)");
|
|
51807
|
+
} else if (input.strict) {
|
|
51808
|
+
lines.push("(allow network*)");
|
|
51809
|
+
}
|
|
51810
|
+
return `${lines.join("\n")}
|
|
51811
|
+
`;
|
|
51812
|
+
}
|
|
51813
|
+
function canonical(p) {
|
|
51814
|
+
try {
|
|
51815
|
+
return realpathSync(p);
|
|
51816
|
+
} catch {
|
|
51817
|
+
return p;
|
|
51818
|
+
}
|
|
51819
|
+
}
|
|
51820
|
+
function canonicalFile(p) {
|
|
51821
|
+
return path3.join(canonical(path3.dirname(p)), path3.basename(p));
|
|
51822
|
+
}
|
|
51823
|
+
function isSandboxExecAvailable() {
|
|
51824
|
+
try {
|
|
51825
|
+
return existsSync2(SANDBOX_EXEC_PATH);
|
|
51826
|
+
} catch {
|
|
51827
|
+
return false;
|
|
51828
|
+
}
|
|
51829
|
+
}
|
|
51830
|
+
function resolveSandboxSpec(options) {
|
|
51831
|
+
if (options.mode === "off") {
|
|
51832
|
+
return { kind: "none", reason: "RUNNER_SANDBOX=off (loosened fallback - OS confinement disabled)" };
|
|
51833
|
+
}
|
|
51834
|
+
if (options.platform !== "darwin") {
|
|
51835
|
+
return { kind: "none", reason: `no OS sandbox on ${options.platform} (seatbelt is macOS-only; Linux confinement is a follow-up)` };
|
|
51836
|
+
}
|
|
51837
|
+
const available = options.sandboxExecAvailable ?? isSandboxExecAvailable();
|
|
51838
|
+
if (!available) {
|
|
51839
|
+
return { kind: "none", reason: `sandbox-exec not found at ${SANDBOX_EXEC_PATH}` };
|
|
51840
|
+
}
|
|
51841
|
+
const workspaceDir = canonical(options.workspaceDir);
|
|
51842
|
+
const tmpDir = canonical(options.tmpDir);
|
|
51843
|
+
const homeDir = canonical(options.homeDir);
|
|
51844
|
+
const allowNetwork = options.allowNetwork ?? true;
|
|
51845
|
+
const denyReadPaths = [canonicalFile(options.stateFile)];
|
|
51846
|
+
if (options.runnerBaseDir) {
|
|
51847
|
+
denyReadPaths.push(`${canonical(options.runnerBaseDir).replace(/\/+$/, "")}/`);
|
|
51848
|
+
}
|
|
51849
|
+
const profile = buildSeatbeltProfile({
|
|
51850
|
+
workspaceDir,
|
|
51851
|
+
tmpDir,
|
|
51852
|
+
homeDir,
|
|
51853
|
+
denyReadPaths,
|
|
51854
|
+
allowWritePaths: (options.allowWritePaths ?? []).map(canonical),
|
|
51855
|
+
allowNetwork,
|
|
51856
|
+
strict: options.mode === "strict"
|
|
51857
|
+
});
|
|
51858
|
+
return {
|
|
51859
|
+
kind: "seatbelt",
|
|
51860
|
+
reason: options.mode === "strict" ? "seatbelt (strict: deny-default reads + write confinement)" : "seatbelt (guard: deny sensitive reads/writes)",
|
|
51861
|
+
profile,
|
|
51862
|
+
allowNetwork
|
|
51863
|
+
};
|
|
51864
|
+
}
|
|
51865
|
+
function wrapCommandWithSandbox(spec, command, args, profilePath) {
|
|
51866
|
+
if (spec.kind === "none" || profilePath === null) {
|
|
51867
|
+
return { command, args, profilePath: null };
|
|
51868
|
+
}
|
|
51869
|
+
return {
|
|
51870
|
+
command: SANDBOX_EXEC_PATH,
|
|
51871
|
+
args: ["-f", profilePath, command, ...args],
|
|
51872
|
+
profilePath
|
|
51873
|
+
};
|
|
51874
|
+
}
|
|
51875
|
+
async function sweepStaleRunnerTempFiles(dir, options = {}) {
|
|
51876
|
+
const maxAgeMs = options.maxAgeMs ?? 60 * 60 * 1e3;
|
|
51877
|
+
const now = options.now ?? Date.now();
|
|
51878
|
+
let entries;
|
|
51879
|
+
try {
|
|
51880
|
+
entries = await readdir(dir);
|
|
51881
|
+
} catch {
|
|
51882
|
+
return [];
|
|
51883
|
+
}
|
|
51884
|
+
const removed = [];
|
|
51885
|
+
await Promise.all(
|
|
51886
|
+
entries.map(async (name) => {
|
|
51887
|
+
if (!RUNNER_TEMP_PREFIXES.some((prefix) => name.startsWith(prefix))) {
|
|
51888
|
+
return;
|
|
51889
|
+
}
|
|
51890
|
+
const full = path3.join(dir, name);
|
|
51891
|
+
try {
|
|
51892
|
+
const info = await stat2(full);
|
|
51893
|
+
if (now - info.mtimeMs < maxAgeMs) {
|
|
51894
|
+
return;
|
|
51895
|
+
}
|
|
51896
|
+
await rm2(full, { force: true });
|
|
51897
|
+
removed.push(name);
|
|
51898
|
+
} catch {
|
|
51899
|
+
}
|
|
51900
|
+
})
|
|
51901
|
+
);
|
|
51902
|
+
return removed;
|
|
51903
|
+
}
|
|
51904
|
+
|
|
51905
|
+
// src/runner-serve.ts
|
|
51575
51906
|
var execFile3 = promisify3(execFileCallback3);
|
|
51576
51907
|
var RUNNER_WORK_ORDER_TOOLS = [
|
|
51577
51908
|
"mcp__rost__rost_get_context",
|
|
@@ -51597,6 +51928,20 @@ ${usage()}
|
|
|
51597
51928
|
}
|
|
51598
51929
|
options.io.stdout.write(`runner serve: ${options.appUrl} name=${config2.name} execute=${config2.execute}
|
|
51599
51930
|
`);
|
|
51931
|
+
options.io.stdout.write(`runner sandbox: ${config2.sandbox.kind} - ${config2.sandbox.reason}
|
|
51932
|
+
`);
|
|
51933
|
+
if (config2.sandboxNetworkWarning) {
|
|
51934
|
+
options.io.stderr.write(`warning: ${config2.sandboxNetworkWarning}
|
|
51935
|
+
`);
|
|
51936
|
+
}
|
|
51937
|
+
try {
|
|
51938
|
+
const swept = await sweepStaleRunnerTempFiles(tmpdir());
|
|
51939
|
+
if (swept.length > 0) {
|
|
51940
|
+
options.io.stdout.write(`swept ${swept.length} stale runner temp file(s)
|
|
51941
|
+
`);
|
|
51942
|
+
}
|
|
51943
|
+
} catch {
|
|
51944
|
+
}
|
|
51600
51945
|
let state;
|
|
51601
51946
|
try {
|
|
51602
51947
|
state = await pairIfNeeded(options.client ?? null, fetchImpl, options.appUrl, config2, options.io);
|
|
@@ -51682,7 +52027,7 @@ function detectTelemetry(capabilities, env, activeSessions) {
|
|
|
51682
52027
|
}
|
|
51683
52028
|
function parseServeArgs(args, appUrl2, env) {
|
|
51684
52029
|
let name = env.RUNNER_NAME ?? `headless-${hostname3()}`;
|
|
51685
|
-
let stateFile = env.RUNNER_STATE_FILE ??
|
|
52030
|
+
let stateFile = env.RUNNER_STATE_FILE ?? path4.join(tmpdir(), `rost-runner-${createHash("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
|
|
51686
52031
|
let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
|
|
51687
52032
|
let interactiveClaimMs = Number(env.RUNNER_INTERACTIVE_CLAIM_MS ?? 1e3);
|
|
51688
52033
|
let execute2 = env.RUNNER_EXECUTE === "1";
|
|
@@ -51724,7 +52069,29 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
51724
52069
|
throw new Error("--interactive-claim-ms must be an integer >= 250.");
|
|
51725
52070
|
}
|
|
51726
52071
|
interactiveClaimMs = Math.min(interactiveClaimMs, heartbeatMs);
|
|
51727
|
-
|
|
52072
|
+
const network = parseSandboxNetwork(env.RUNNER_SANDBOX_NETWORK);
|
|
52073
|
+
const sandbox = resolveSandboxSpec({
|
|
52074
|
+
mode: parseSandboxMode(env.RUNNER_SANDBOX),
|
|
52075
|
+
platform: process.platform,
|
|
52076
|
+
workspaceDir: tmpdir(),
|
|
52077
|
+
tmpDir: tmpdir(),
|
|
52078
|
+
homeDir: homedir2(),
|
|
52079
|
+
stateFile,
|
|
52080
|
+
runnerBaseDir: path4.join(homedir2(), ".rost"),
|
|
52081
|
+
allowNetwork: network.allowNetwork
|
|
52082
|
+
});
|
|
52083
|
+
return {
|
|
52084
|
+
name,
|
|
52085
|
+
stateFile,
|
|
52086
|
+
heartbeatMs,
|
|
52087
|
+
interactiveClaimMs,
|
|
52088
|
+
execute: execute2,
|
|
52089
|
+
once,
|
|
52090
|
+
runtime,
|
|
52091
|
+
sandbox,
|
|
52092
|
+
...network.unrecognized !== null ? { sandboxNetworkWarning: `RUNNER_SANDBOX_NETWORK='${network.unrecognized}' not recognized (use allow|deny); defaulting to allow egress.` } : {},
|
|
52093
|
+
...userCode ? { userCode } : {}
|
|
52094
|
+
};
|
|
51728
52095
|
}
|
|
51729
52096
|
function parseRuntime(value) {
|
|
51730
52097
|
if (value === "auto" || value === "claude" || value === "codex") {
|
|
@@ -51844,7 +52211,7 @@ function isRuntimeSessionMap(value) {
|
|
|
51844
52211
|
});
|
|
51845
52212
|
}
|
|
51846
52213
|
async function saveState(filePath, state) {
|
|
51847
|
-
await mkdir2(
|
|
52214
|
+
await mkdir2(path4.dirname(filePath), { recursive: true, mode: 448 });
|
|
51848
52215
|
const tempPath = `${filePath}.${process.pid}.${createHash("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
|
|
51849
52216
|
try {
|
|
51850
52217
|
await writeFile2(tempPath, `${JSON.stringify(state)}
|
|
@@ -51852,7 +52219,7 @@ async function saveState(filePath, state) {
|
|
|
51852
52219
|
await chmod(tempPath, 384);
|
|
51853
52220
|
await rename(tempPath, filePath);
|
|
51854
52221
|
} catch (error51) {
|
|
51855
|
-
await
|
|
52222
|
+
await rm3(tempPath, { force: true });
|
|
51856
52223
|
throw error51;
|
|
51857
52224
|
}
|
|
51858
52225
|
}
|
|
@@ -51938,7 +52305,7 @@ async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtim
|
|
|
51938
52305
|
throw new Error(`${claimedUnitLabel(input.kind)} start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
|
|
51939
52306
|
}
|
|
51940
52307
|
const turnRuntime = runtimeForClaimedUnit(input.item, runtime, input.kind);
|
|
51941
|
-
const result = turnRuntime ? await runLocalTurn(appUrl2, input.item, turnRuntime, input.kind, state) : {
|
|
52308
|
+
const result = turnRuntime ? await runLocalTurn(appUrl2, input.item, turnRuntime, input.kind, state, config2.sandbox) : {
|
|
51942
52309
|
ok: false,
|
|
51943
52310
|
summary: `Runner runtime ${runtime} cannot execute requested ${String(input.item.brain ?? "unknown")} turn.`
|
|
51944
52311
|
};
|
|
@@ -51978,14 +52345,14 @@ function runtimeForClaimedUnit(item, fallback, kind) {
|
|
|
51978
52345
|
}
|
|
51979
52346
|
return fallback === "claude" ? "claude" : null;
|
|
51980
52347
|
}
|
|
51981
|
-
function runLocalTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
51982
|
-
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder, kind) : runClaudeTurn(appUrl2, workOrder, kind, state);
|
|
52348
|
+
function runLocalTurn(appUrl2, workOrder, runtime, kind, state, sandbox) {
|
|
52349
|
+
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder, kind, sandbox) : runClaudeTurn(appUrl2, workOrder, kind, state, sandbox);
|
|
51983
52350
|
}
|
|
51984
|
-
function runClaudeTurn(appUrl2, workOrder, kind, state) {
|
|
51985
|
-
return spawnRunnerTurn(appUrl2, workOrder, "claude", kind, state);
|
|
52351
|
+
function runClaudeTurn(appUrl2, workOrder, kind, state, sandbox) {
|
|
52352
|
+
return spawnRunnerTurn(appUrl2, workOrder, "claude", kind, sandbox, state);
|
|
51986
52353
|
}
|
|
51987
|
-
function runCodexTurn(appUrl2, workOrder, kind) {
|
|
51988
|
-
return spawnRunnerTurn(appUrl2, workOrder, "codex", kind);
|
|
52354
|
+
function runCodexTurn(appUrl2, workOrder, kind, sandbox) {
|
|
52355
|
+
return spawnRunnerTurn(appUrl2, workOrder, "codex", kind, sandbox);
|
|
51989
52356
|
}
|
|
51990
52357
|
function buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId) {
|
|
51991
52358
|
if (runtime === "codex") {
|
|
@@ -52010,12 +52377,12 @@ function buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId) {
|
|
|
52010
52377
|
]
|
|
52011
52378
|
};
|
|
52012
52379
|
}
|
|
52013
|
-
async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
52380
|
+
async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, sandbox, state) {
|
|
52014
52381
|
const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
|
|
52015
52382
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
52016
52383
|
const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
|
|
52017
52384
|
const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
|
|
52018
|
-
const configPath =
|
|
52385
|
+
const configPath = path4.join(tmpdir(), `rost-runner-mcp-${createHash("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
|
|
52019
52386
|
const context = {
|
|
52020
52387
|
work_order_id: kind === "work_order" ? workOrder.id : null,
|
|
52021
52388
|
turn_execution_id: kind === "turn_execution" ? workOrder.id : null,
|
|
@@ -52045,7 +52412,17 @@ async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
|
52045
52412
|
`, { mode: 384 });
|
|
52046
52413
|
await chmod(configPath, 384);
|
|
52047
52414
|
const resumeSessionId = runtime === "claude" ? getClaudeResumeSessionId(state, workOrder) : void 0;
|
|
52048
|
-
const
|
|
52415
|
+
const built = buildTurnCommand(runtime, prompt, configPath, kind, resumeSessionId);
|
|
52416
|
+
let sandboxProfilePath = null;
|
|
52417
|
+
if (sandbox.kind === "seatbelt") {
|
|
52418
|
+
sandboxProfilePath = path4.join(
|
|
52419
|
+
tmpdir(),
|
|
52420
|
+
`rost-runner-sandbox-${createHash("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
|
|
52421
|
+
);
|
|
52422
|
+
await writeFile2(sandboxProfilePath, sandbox.profile, { mode: 384 });
|
|
52423
|
+
await chmod(sandboxProfilePath, 384);
|
|
52424
|
+
}
|
|
52425
|
+
const { command, args } = wrapCommandWithSandbox(sandbox, built.command, built.args, sandboxProfilePath);
|
|
52049
52426
|
const expectSessionEnvelope = runtime === "claude" && kind === "turn_execution";
|
|
52050
52427
|
return new Promise((resolve2) => {
|
|
52051
52428
|
let settled = false;
|
|
@@ -52054,7 +52431,10 @@ async function spawnRunnerTurn(appUrl2, workOrder, runtime, kind, state) {
|
|
|
52054
52431
|
return;
|
|
52055
52432
|
}
|
|
52056
52433
|
settled = true;
|
|
52057
|
-
void
|
|
52434
|
+
void rm3(configPath, { force: true });
|
|
52435
|
+
if (sandboxProfilePath) {
|
|
52436
|
+
void rm3(sandboxProfilePath, { force: true });
|
|
52437
|
+
}
|
|
52058
52438
|
resolve2(resumeSessionId ? { ...result, resumedFromSessionId: resumeSessionId } : result);
|
|
52059
52439
|
};
|
|
52060
52440
|
const child = spawn(command, args, {
|
|
@@ -52187,10 +52567,10 @@ function usage() {
|
|
|
52187
52567
|
|
|
52188
52568
|
// src/runner-service.ts
|
|
52189
52569
|
import { execFile as execFileCallback4 } from "node:child_process";
|
|
52190
|
-
import { realpathSync } from "node:fs";
|
|
52191
|
-
import { access as access2, mkdir as mkdir3, readFile as readFile5, rm as
|
|
52192
|
-
import { homedir as
|
|
52193
|
-
import
|
|
52570
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
52571
|
+
import { access as access2, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
52572
|
+
import { homedir as homedir3 } from "node:os";
|
|
52573
|
+
import path5 from "node:path";
|
|
52194
52574
|
import { promisify as promisify4 } from "node:util";
|
|
52195
52575
|
var execFile4 = promisify4(execFileCallback4);
|
|
52196
52576
|
var SERVICE_VERBS = ["install-service", "start", "stop", "restart", "status", "logs", "uninstall"];
|
|
@@ -52207,9 +52587,9 @@ function isRunnerServiceInvocation(args) {
|
|
|
52207
52587
|
function runnerServicePaths(homeDir, runnerName) {
|
|
52208
52588
|
const safeName = safeRunnerName(runnerName);
|
|
52209
52589
|
return {
|
|
52210
|
-
launchAgentPath:
|
|
52211
|
-
stateFile:
|
|
52212
|
-
logDir:
|
|
52590
|
+
launchAgentPath: path5.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
|
|
52591
|
+
stateFile: path5.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
|
|
52592
|
+
logDir: path5.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
|
|
52213
52593
|
};
|
|
52214
52594
|
}
|
|
52215
52595
|
function renderLaunchdPlist(config2) {
|
|
@@ -52249,9 +52629,9 @@ ${programArgsXml}
|
|
|
52249
52629
|
<key>RunAtLoad</key>
|
|
52250
52630
|
<true/>
|
|
52251
52631
|
<key>StandardOutPath</key>
|
|
52252
|
-
<string>${escapePlist(
|
|
52632
|
+
<string>${escapePlist(path5.join(config2.logDir, "stdout.log"))}</string>
|
|
52253
52633
|
<key>StandardErrorPath</key>
|
|
52254
|
-
<string>${escapePlist(
|
|
52634
|
+
<string>${escapePlist(path5.join(config2.logDir, "stderr.log"))}</string>
|
|
52255
52635
|
</dict>
|
|
52256
52636
|
</plist>
|
|
52257
52637
|
`;
|
|
@@ -52286,7 +52666,7 @@ ${serviceUsage()}
|
|
|
52286
52666
|
userCode: parsed.userCode,
|
|
52287
52667
|
nodePath: options.nodePath ?? resolveNodePath(),
|
|
52288
52668
|
binPath: options.binPath ?? resolveBinPath(),
|
|
52289
|
-
homeDir: options.homeDir ??
|
|
52669
|
+
homeDir: options.homeDir ?? homedir3(),
|
|
52290
52670
|
platform: options.platform ?? process.platform,
|
|
52291
52671
|
exec: options.exec ?? defaultExec,
|
|
52292
52672
|
uid: options.uid ?? (process.getuid ? process.getuid() : 0)
|
|
@@ -52318,8 +52698,8 @@ async function installService(options) {
|
|
|
52318
52698
|
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
52319
52699
|
const label = serviceLabel(safeName);
|
|
52320
52700
|
await mkdir3(paths.logDir, { recursive: true });
|
|
52321
|
-
await mkdir3(
|
|
52322
|
-
await mkdir3(
|
|
52701
|
+
await mkdir3(path5.dirname(paths.stateFile), { recursive: true });
|
|
52702
|
+
await mkdir3(path5.dirname(paths.launchAgentPath), { recursive: true });
|
|
52323
52703
|
if (options.userCode !== void 0) {
|
|
52324
52704
|
const paired = await pairRunnerOnce(options, safeName, paths.stateFile, options.userCode);
|
|
52325
52705
|
if (!paired) {
|
|
@@ -52497,8 +52877,8 @@ async function logsService(options) {
|
|
|
52497
52877
|
const safeName = safeRunnerName(options.runnerName);
|
|
52498
52878
|
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
52499
52879
|
const label = serviceLabel(safeName);
|
|
52500
|
-
const stdoutLog =
|
|
52501
|
-
const stderrLog =
|
|
52880
|
+
const stdoutLog = path5.join(paths.logDir, "stdout.log");
|
|
52881
|
+
const stderrLog = path5.join(paths.logDir, "stderr.log");
|
|
52502
52882
|
options.io.stdout.write(
|
|
52503
52883
|
`${[
|
|
52504
52884
|
`runner service logs for ${label}:`,
|
|
@@ -52529,7 +52909,7 @@ async function uninstallService(options) {
|
|
|
52529
52909
|
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
52530
52910
|
let removed = false;
|
|
52531
52911
|
try {
|
|
52532
|
-
await
|
|
52912
|
+
await rm4(paths.launchAgentPath, { force: true });
|
|
52533
52913
|
removed = true;
|
|
52534
52914
|
} catch (error51) {
|
|
52535
52915
|
options.io.stderr.write(
|
|
@@ -52673,11 +53053,11 @@ function resolveNodePath() {
|
|
|
52673
53053
|
}
|
|
52674
53054
|
function buildServicePath(nodePath, homeDir) {
|
|
52675
53055
|
const entries = [
|
|
52676
|
-
|
|
53056
|
+
path5.dirname(nodePath),
|
|
52677
53057
|
"/opt/homebrew/bin",
|
|
52678
53058
|
"/usr/local/bin",
|
|
52679
|
-
|
|
52680
|
-
|
|
53059
|
+
path5.join(homeDir, ".local", "bin"),
|
|
53060
|
+
path5.join(homeDir, "bin"),
|
|
52681
53061
|
"/usr/bin",
|
|
52682
53062
|
"/bin",
|
|
52683
53063
|
"/usr/sbin",
|
|
@@ -52722,7 +53102,7 @@ function resolveBinPath() {
|
|
|
52722
53102
|
return cliBrand.binName;
|
|
52723
53103
|
}
|
|
52724
53104
|
try {
|
|
52725
|
-
return
|
|
53105
|
+
return realpathSync2(entry);
|
|
52726
53106
|
} catch {
|
|
52727
53107
|
return entry;
|
|
52728
53108
|
}
|
|
@@ -53385,7 +53765,7 @@ function isCliEntrypoint(moduleUrl, argvPath) {
|
|
|
53385
53765
|
return false;
|
|
53386
53766
|
}
|
|
53387
53767
|
try {
|
|
53388
|
-
return
|
|
53768
|
+
return realpathSync3(fileURLToPath3(moduleUrl)) === realpathSync3(argvPath);
|
|
53389
53769
|
} catch {
|
|
53390
53770
|
return moduleUrl === pathToFileURL(argvPath).href;
|
|
53391
53771
|
}
|