@rosthq/cli 0.6.8 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +926 -127
- package/dist/index.js.map +4 -4
- package/dist/operations.d.ts.map +1 -1
- package/dist/runner-serve.d.ts.map +1 -1
- package/dist/runner-service.d.ts +36 -0
- package/dist/runner-service.d.ts.map +1 -0
- package/dist/token-store.d.ts +66 -1
- package/dist/token-store.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -472,13 +472,13 @@ function __disposeResources(env) {
|
|
|
472
472
|
}
|
|
473
473
|
return next();
|
|
474
474
|
}
|
|
475
|
-
function __rewriteRelativeImportExtension(
|
|
476
|
-
if (typeof
|
|
477
|
-
return
|
|
475
|
+
function __rewriteRelativeImportExtension(path5, preserveJsx) {
|
|
476
|
+
if (typeof path5 === "string" && /^\.\.?\//.test(path5)) {
|
|
477
|
+
return path5.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 path5;
|
|
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 } from "node:fs";
|
|
13260
|
+
import { realpathSync as realpathSync2 } 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, path5) {
|
|
14030
|
+
if (!path5)
|
|
14031
14031
|
return obj;
|
|
14032
|
-
return
|
|
14032
|
+
return path5.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(path5, 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(path5);
|
|
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, path5 = []) => {
|
|
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 }, [...path5, ...issue2.path]));
|
|
14596
14596
|
} else if (issue2.code === "invalid_key") {
|
|
14597
|
-
processError({ issues: issue2.issues }, [...
|
|
14597
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
14598
14598
|
} else if (issue2.code === "invalid_element") {
|
|
14599
|
-
processError({ issues: issue2.issues }, [...
|
|
14599
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
14600
14600
|
} else {
|
|
14601
|
-
const fullpath = [...
|
|
14601
|
+
const fullpath = [...path5, ...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, path5 = []) => {
|
|
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 }, [...path5, ...issue2.path]));
|
|
14633
14633
|
} else if (issue2.code === "invalid_key") {
|
|
14634
|
-
processError({ issues: issue2.issues }, [...
|
|
14634
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
14635
14635
|
} else if (issue2.code === "invalid_element") {
|
|
14636
|
-
processError({ issues: issue2.issues }, [...
|
|
14636
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
14637
14637
|
} else {
|
|
14638
|
-
const fullpath = [...
|
|
14638
|
+
const fullpath = [...path5, ...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 path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
14671
|
+
for (const seg of path5) {
|
|
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 path5 = ref.slice(1).split("/").filter(Boolean);
|
|
27364
|
+
if (path5.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 (path5[0] === defsKey) {
|
|
27369
|
+
const key = path5[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 path5 = `/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}${path5}`, {
|
|
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 ${path5}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
|
|
27853
27853
|
);
|
|
27854
27854
|
}
|
|
27855
27855
|
const parsed = commandResponseSchema.parse(raw);
|
|
@@ -33056,8 +33056,8 @@ var IcebergError = class extends Error {
|
|
|
33056
33056
|
return this.status === 419;
|
|
33057
33057
|
}
|
|
33058
33058
|
};
|
|
33059
|
-
function buildUrl(baseUrl,
|
|
33060
|
-
const url2 = new URL(
|
|
33059
|
+
function buildUrl(baseUrl, path5, query) {
|
|
33060
|
+
const url2 = new URL(path5, baseUrl);
|
|
33061
33061
|
if (query) {
|
|
33062
33062
|
for (const [key, value] of Object.entries(query)) {
|
|
33063
33063
|
if (value !== void 0) {
|
|
@@ -33087,12 +33087,12 @@ function createFetchClient(options) {
|
|
|
33087
33087
|
return {
|
|
33088
33088
|
async request({
|
|
33089
33089
|
method,
|
|
33090
|
-
path:
|
|
33090
|
+
path: path5,
|
|
33091
33091
|
query,
|
|
33092
33092
|
body,
|
|
33093
33093
|
headers
|
|
33094
33094
|
}) {
|
|
33095
|
-
const url2 = buildUrl(options.baseUrl,
|
|
33095
|
+
const url2 = buildUrl(options.baseUrl, path5, query);
|
|
33096
33096
|
const authHeaders = await buildAuthHeaders(options.auth);
|
|
33097
33097
|
const res = await fetchFn(url2, {
|
|
33098
33098
|
method,
|
|
@@ -33954,7 +33954,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
33954
33954
|
* @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
|
|
33955
33955
|
* @param fileBody The body of the file to be stored in the bucket.
|
|
33956
33956
|
*/
|
|
33957
|
-
async uploadOrUpdate(method,
|
|
33957
|
+
async uploadOrUpdate(method, path5, fileBody, fileOptions) {
|
|
33958
33958
|
var _this = this;
|
|
33959
33959
|
return _this.handleOperation(async () => {
|
|
33960
33960
|
let body;
|
|
@@ -33978,7 +33978,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
33978
33978
|
if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
|
|
33979
33979
|
}
|
|
33980
33980
|
if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
|
|
33981
|
-
const cleanPath = _this._removeEmptyFolders(
|
|
33981
|
+
const cleanPath = _this._removeEmptyFolders(path5);
|
|
33982
33982
|
const _path = _this._getFinalPath(cleanPath);
|
|
33983
33983
|
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 } : {}));
|
|
33984
33984
|
return {
|
|
@@ -34055,8 +34055,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34055
34055
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34056
34056
|
* - 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.
|
|
34057
34057
|
*/
|
|
34058
|
-
async upload(
|
|
34059
|
-
return this.uploadOrUpdate("POST",
|
|
34058
|
+
async upload(path5, fileBody, fileOptions) {
|
|
34059
|
+
return this.uploadOrUpdate("POST", path5, fileBody, fileOptions);
|
|
34060
34060
|
}
|
|
34061
34061
|
/**
|
|
34062
34062
|
* Upload a file with a token generated from `createSignedUploadUrl`.
|
|
@@ -34096,9 +34096,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34096
34096
|
* - `objects` table permissions: none
|
|
34097
34097
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34098
34098
|
*/
|
|
34099
|
-
async uploadToSignedUrl(
|
|
34099
|
+
async uploadToSignedUrl(path5, token, fileBody, fileOptions) {
|
|
34100
34100
|
var _this3 = this;
|
|
34101
|
-
const cleanPath = _this3._removeEmptyFolders(
|
|
34101
|
+
const cleanPath = _this3._removeEmptyFolders(path5);
|
|
34102
34102
|
const _path = _this3._getFinalPath(cleanPath);
|
|
34103
34103
|
const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
|
|
34104
34104
|
url2.searchParams.set("token", token);
|
|
@@ -34167,10 +34167,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34167
34167
|
* - `objects` table permissions: `insert`
|
|
34168
34168
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34169
34169
|
*/
|
|
34170
|
-
async createSignedUploadUrl(
|
|
34170
|
+
async createSignedUploadUrl(path5, options) {
|
|
34171
34171
|
var _this4 = this;
|
|
34172
34172
|
return _this4.handleOperation(async () => {
|
|
34173
|
-
let _path = _this4._getFinalPath(
|
|
34173
|
+
let _path = _this4._getFinalPath(path5);
|
|
34174
34174
|
const headers = _objectSpread22({}, _this4.headers);
|
|
34175
34175
|
if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
|
|
34176
34176
|
const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
|
|
@@ -34179,7 +34179,7 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34179
34179
|
if (!token) throw new StorageError("No token returned by API");
|
|
34180
34180
|
return {
|
|
34181
34181
|
signedUrl: url2.toString(),
|
|
34182
|
-
path:
|
|
34182
|
+
path: path5,
|
|
34183
34183
|
token
|
|
34184
34184
|
};
|
|
34185
34185
|
});
|
|
@@ -34239,8 +34239,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34239
34239
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34240
34240
|
* - 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.
|
|
34241
34241
|
*/
|
|
34242
|
-
async update(
|
|
34243
|
-
return this.uploadOrUpdate("PUT",
|
|
34242
|
+
async update(path5, fileBody, fileOptions) {
|
|
34243
|
+
return this.uploadOrUpdate("PUT", path5, fileBody, fileOptions);
|
|
34244
34244
|
}
|
|
34245
34245
|
/**
|
|
34246
34246
|
* Moves an existing file to a new path in the same bucket.
|
|
@@ -34391,10 +34391,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34391
34391
|
* - `objects` table permissions: `select`
|
|
34392
34392
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34393
34393
|
*/
|
|
34394
|
-
async createSignedUrl(
|
|
34394
|
+
async createSignedUrl(path5, expiresIn, options) {
|
|
34395
34395
|
var _this8 = this;
|
|
34396
34396
|
return _this8.handleOperation(async () => {
|
|
34397
|
-
let _path = _this8._getFinalPath(
|
|
34397
|
+
let _path = _this8._getFinalPath(path5);
|
|
34398
34398
|
const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
|
|
34399
34399
|
let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
|
|
34400
34400
|
const query = new URLSearchParams();
|
|
@@ -34530,13 +34530,13 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34530
34530
|
* - `objects` table permissions: `select`
|
|
34531
34531
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34532
34532
|
*/
|
|
34533
|
-
download(
|
|
34533
|
+
download(path5, options, parameters) {
|
|
34534
34534
|
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";
|
|
34535
34535
|
const query = new URLSearchParams();
|
|
34536
34536
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
34537
34537
|
if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
|
|
34538
34538
|
const queryString = query.toString();
|
|
34539
|
-
const _path = this._getFinalPath(
|
|
34539
|
+
const _path = this._getFinalPath(path5);
|
|
34540
34540
|
const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
|
|
34541
34541
|
headers: this.headers,
|
|
34542
34542
|
noResolveJson: true
|
|
@@ -34567,9 +34567,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34567
34567
|
* }
|
|
34568
34568
|
* ```
|
|
34569
34569
|
*/
|
|
34570
|
-
async info(
|
|
34570
|
+
async info(path5) {
|
|
34571
34571
|
var _this10 = this;
|
|
34572
|
-
const _path = _this10._getFinalPath(
|
|
34572
|
+
const _path = _this10._getFinalPath(path5);
|
|
34573
34573
|
return _this10.handleOperation(async () => {
|
|
34574
34574
|
return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
|
|
34575
34575
|
});
|
|
@@ -34590,9 +34590,9 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34590
34590
|
* .exists('folder/avatar1.png')
|
|
34591
34591
|
* ```
|
|
34592
34592
|
*/
|
|
34593
|
-
async exists(
|
|
34593
|
+
async exists(path5) {
|
|
34594
34594
|
var _this11 = this;
|
|
34595
|
-
const _path = _this11._getFinalPath(
|
|
34595
|
+
const _path = _this11._getFinalPath(path5);
|
|
34596
34596
|
try {
|
|
34597
34597
|
await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
|
|
34598
34598
|
return {
|
|
@@ -34671,8 +34671,8 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34671
34671
|
* - `objects` table permissions: none
|
|
34672
34672
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34673
34673
|
*/
|
|
34674
|
-
getPublicUrl(
|
|
34675
|
-
const _path = this._getFinalPath(
|
|
34674
|
+
getPublicUrl(path5, options) {
|
|
34675
|
+
const _path = this._getFinalPath(path5);
|
|
34676
34676
|
const query = new URLSearchParams();
|
|
34677
34677
|
if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
|
|
34678
34678
|
if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
|
|
@@ -34811,10 +34811,10 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34811
34811
|
* - `objects` table permissions: `select`
|
|
34812
34812
|
* - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
|
|
34813
34813
|
*/
|
|
34814
|
-
async list(
|
|
34814
|
+
async list(path5, options, parameters) {
|
|
34815
34815
|
var _this13 = this;
|
|
34816
34816
|
return _this13.handleOperation(async () => {
|
|
34817
|
-
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix:
|
|
34817
|
+
const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path5 || "" });
|
|
34818
34818
|
return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
|
|
34819
34819
|
});
|
|
34820
34820
|
}
|
|
@@ -34879,11 +34879,11 @@ var StorageFileApi = class extends BaseApiClient {
|
|
|
34879
34879
|
if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
|
|
34880
34880
|
return btoa(data);
|
|
34881
34881
|
}
|
|
34882
|
-
_getFinalPath(
|
|
34883
|
-
return `${this.bucketId}/${
|
|
34882
|
+
_getFinalPath(path5) {
|
|
34883
|
+
return `${this.bucketId}/${path5.replace(/^\/+/, "")}`;
|
|
34884
34884
|
}
|
|
34885
|
-
_removeEmptyFolders(
|
|
34886
|
-
return
|
|
34885
|
+
_removeEmptyFolders(path5) {
|
|
34886
|
+
return path5.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
|
|
34887
34887
|
}
|
|
34888
34888
|
/** Modifies the `query`, appending values the from `transform` */
|
|
34889
34889
|
applyTransformOptsToQuery(query, transform2) {
|
|
@@ -37273,17 +37273,30 @@ import { constants as fsConstants } from "node:fs";
|
|
|
37273
37273
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
37274
37274
|
import { homedir } from "node:os";
|
|
37275
37275
|
import path from "node:path";
|
|
37276
|
-
import { promisify as promisify2 } from "node:util";
|
|
37277
|
-
var execFile2 = promisify2(execFileCallback);
|
|
37278
37276
|
var sessionSchema = external_exports.object({
|
|
37279
37277
|
accessToken: external_exports.string().min(1),
|
|
37280
37278
|
refreshToken: external_exports.string().min(1).optional(),
|
|
37281
37279
|
expiresAt: external_exports.string().datetime({ offset: true }).optional()
|
|
37282
37280
|
}).strict();
|
|
37281
|
+
var runExecFile = (file2, args, options) => new Promise((resolve2, reject) => {
|
|
37282
|
+
const child = execFileCallback(file2, [...args], (error51, stdout, stderr) => {
|
|
37283
|
+
if (error51) {
|
|
37284
|
+
reject(error51);
|
|
37285
|
+
return;
|
|
37286
|
+
}
|
|
37287
|
+
resolve2({ stdout, stderr });
|
|
37288
|
+
});
|
|
37289
|
+
const { stdin } = child;
|
|
37290
|
+
if (stdin) {
|
|
37291
|
+
stdin.on("error", () => {
|
|
37292
|
+
});
|
|
37293
|
+
stdin.end(options?.input ?? "");
|
|
37294
|
+
}
|
|
37295
|
+
});
|
|
37283
37296
|
var serviceName = `${cliBrand.binName}-cli`;
|
|
37284
37297
|
var accountName = "session";
|
|
37285
37298
|
var KeychainTokenStore = class {
|
|
37286
|
-
constructor(run2 =
|
|
37299
|
+
constructor(run2 = runExecFile) {
|
|
37287
37300
|
this.run = run2;
|
|
37288
37301
|
}
|
|
37289
37302
|
run;
|
|
@@ -37304,16 +37317,14 @@ var KeychainTokenStore = class {
|
|
|
37304
37317
|
}
|
|
37305
37318
|
async write(session) {
|
|
37306
37319
|
sessionSchema.parse(session);
|
|
37307
|
-
|
|
37308
|
-
|
|
37309
|
-
"
|
|
37310
|
-
"-s",
|
|
37311
|
-
|
|
37312
|
-
|
|
37313
|
-
|
|
37314
|
-
|
|
37315
|
-
JSON.stringify(session)
|
|
37316
|
-
]);
|
|
37320
|
+
const secret = JSON.stringify(session);
|
|
37321
|
+
await this.run(
|
|
37322
|
+
"security",
|
|
37323
|
+
["add-generic-password", "-U", "-s", serviceName, "-a", accountName, "-w"],
|
|
37324
|
+
{ input: `${secret}
|
|
37325
|
+
${secret}
|
|
37326
|
+
` }
|
|
37327
|
+
);
|
|
37317
37328
|
}
|
|
37318
37329
|
async clear() {
|
|
37319
37330
|
try {
|
|
@@ -37326,6 +37337,122 @@ var KeychainTokenStore = class {
|
|
|
37326
37337
|
return "macOS Keychain";
|
|
37327
37338
|
}
|
|
37328
37339
|
};
|
|
37340
|
+
var DarwinTokenStore = class extends KeychainTokenStore {
|
|
37341
|
+
constructor(run2 = runExecFile, filePath) {
|
|
37342
|
+
super(run2);
|
|
37343
|
+
this.filePath = filePath;
|
|
37344
|
+
}
|
|
37345
|
+
filePath;
|
|
37346
|
+
backend = null;
|
|
37347
|
+
fileStore = null;
|
|
37348
|
+
async read() {
|
|
37349
|
+
return await this.selectBackend() === "file" ? this.file().read() : super.read();
|
|
37350
|
+
}
|
|
37351
|
+
async write(session) {
|
|
37352
|
+
if (await this.selectBackend() === "file") {
|
|
37353
|
+
await this.file().write(session);
|
|
37354
|
+
return;
|
|
37355
|
+
}
|
|
37356
|
+
await super.write(session);
|
|
37357
|
+
}
|
|
37358
|
+
async clear() {
|
|
37359
|
+
if (await this.selectBackend() === "file") {
|
|
37360
|
+
await this.file().clear();
|
|
37361
|
+
return;
|
|
37362
|
+
}
|
|
37363
|
+
await super.clear();
|
|
37364
|
+
}
|
|
37365
|
+
describe() {
|
|
37366
|
+
return this.backend === "file" ? "owner-only file store (no usable macOS keychain)" : super.describe();
|
|
37367
|
+
}
|
|
37368
|
+
file() {
|
|
37369
|
+
return this.fileStore ??= new FileTokenStore(this.filePath);
|
|
37370
|
+
}
|
|
37371
|
+
async selectBackend() {
|
|
37372
|
+
if (this.backend) {
|
|
37373
|
+
return this.backend;
|
|
37374
|
+
}
|
|
37375
|
+
try {
|
|
37376
|
+
await this.run("security", ["find-generic-password", "-s", serviceName, "-a", accountName, "-w"]);
|
|
37377
|
+
this.backend = "keychain";
|
|
37378
|
+
} catch (error51) {
|
|
37379
|
+
this.backend = isKeychainUnusableError(error51) ? "file" : "keychain";
|
|
37380
|
+
}
|
|
37381
|
+
return this.backend;
|
|
37382
|
+
}
|
|
37383
|
+
};
|
|
37384
|
+
var WindowsCredentialTokenStore = class {
|
|
37385
|
+
constructor(run2 = runExecFile) {
|
|
37386
|
+
this.run = run2;
|
|
37387
|
+
}
|
|
37388
|
+
run;
|
|
37389
|
+
async read() {
|
|
37390
|
+
try {
|
|
37391
|
+
const { stdout } = await this.run("powershell", powershellArgs(windowsReadScript(windowsCredentialTarget)));
|
|
37392
|
+
const trimmed = stdout.trim();
|
|
37393
|
+
if (trimmed === "") {
|
|
37394
|
+
return null;
|
|
37395
|
+
}
|
|
37396
|
+
return sessionSchema.parse(JSON.parse(trimmed));
|
|
37397
|
+
} catch {
|
|
37398
|
+
return null;
|
|
37399
|
+
}
|
|
37400
|
+
}
|
|
37401
|
+
async write(session) {
|
|
37402
|
+
sessionSchema.parse(session);
|
|
37403
|
+
await this.run(
|
|
37404
|
+
"powershell",
|
|
37405
|
+
powershellArgs(windowsWriteScript(windowsCredentialTarget)),
|
|
37406
|
+
{ input: JSON.stringify(session) }
|
|
37407
|
+
);
|
|
37408
|
+
}
|
|
37409
|
+
async clear() {
|
|
37410
|
+
try {
|
|
37411
|
+
await this.run("powershell", powershellArgs(windowsClearScript(windowsCredentialTarget)));
|
|
37412
|
+
} catch {
|
|
37413
|
+
return;
|
|
37414
|
+
}
|
|
37415
|
+
}
|
|
37416
|
+
describe() {
|
|
37417
|
+
return "Windows Credential Manager";
|
|
37418
|
+
}
|
|
37419
|
+
};
|
|
37420
|
+
var SecretServiceTokenStore = class {
|
|
37421
|
+
constructor(run2 = runExecFile) {
|
|
37422
|
+
this.run = run2;
|
|
37423
|
+
}
|
|
37424
|
+
run;
|
|
37425
|
+
async read() {
|
|
37426
|
+
try {
|
|
37427
|
+
const { stdout } = await this.run("secret-tool", ["lookup", "service", serviceName, "account", accountName]);
|
|
37428
|
+
const trimmed = stdout.trim();
|
|
37429
|
+
if (trimmed === "") {
|
|
37430
|
+
return null;
|
|
37431
|
+
}
|
|
37432
|
+
return sessionSchema.parse(JSON.parse(trimmed));
|
|
37433
|
+
} catch {
|
|
37434
|
+
return null;
|
|
37435
|
+
}
|
|
37436
|
+
}
|
|
37437
|
+
async write(session) {
|
|
37438
|
+
sessionSchema.parse(session);
|
|
37439
|
+
await this.run(
|
|
37440
|
+
"secret-tool",
|
|
37441
|
+
["store", `--label=${cliBrand.name} CLI session`, "service", serviceName, "account", accountName],
|
|
37442
|
+
{ input: JSON.stringify(session) }
|
|
37443
|
+
);
|
|
37444
|
+
}
|
|
37445
|
+
async clear() {
|
|
37446
|
+
try {
|
|
37447
|
+
await this.run("secret-tool", ["clear", "service", serviceName, "account", accountName]);
|
|
37448
|
+
} catch {
|
|
37449
|
+
return;
|
|
37450
|
+
}
|
|
37451
|
+
}
|
|
37452
|
+
describe() {
|
|
37453
|
+
return "Secret Service";
|
|
37454
|
+
}
|
|
37455
|
+
};
|
|
37329
37456
|
var FileTokenStore = class {
|
|
37330
37457
|
constructor(filePath = defaultTokenPath()) {
|
|
37331
37458
|
this.filePath = filePath;
|
|
@@ -37366,6 +37493,7 @@ var FileTokenStore = class {
|
|
|
37366
37493
|
function createTokenStore(options = {}) {
|
|
37367
37494
|
const env = options.env ?? process.env;
|
|
37368
37495
|
const allowFileStoreKey = brandEnvKey2("CLI_ALLOW_FILE_TOKEN_STORE");
|
|
37496
|
+
const tokenPath = env[brandEnvKey2("CLI_TOKEN_PATH")];
|
|
37369
37497
|
if (env[allowFileStoreKey] === "1") {
|
|
37370
37498
|
const warning = [
|
|
37371
37499
|
"WARNING: using the development-only plain-file CLI token store.",
|
|
@@ -37373,17 +37501,23 @@ function createTokenStore(options = {}) {
|
|
|
37373
37501
|
].join(" ");
|
|
37374
37502
|
(options.stderr ?? process.stderr).write(`${warning}
|
|
37375
37503
|
`);
|
|
37376
|
-
return new FileTokenStore(
|
|
37504
|
+
return new FileTokenStore(tokenPath);
|
|
37377
37505
|
}
|
|
37378
37506
|
const platform = options.platform ?? process.platform;
|
|
37379
37507
|
if (platform === "darwin") {
|
|
37380
|
-
return new
|
|
37508
|
+
return new DarwinTokenStore(options.execFile, tokenPath);
|
|
37509
|
+
}
|
|
37510
|
+
if (platform === "win32") {
|
|
37511
|
+
return new WindowsCredentialTokenStore(options.execFile);
|
|
37512
|
+
}
|
|
37513
|
+
if (platform === "linux" && env[brandEnvKey2("CLI_USE_SECRET_SERVICE")] === "1") {
|
|
37514
|
+
return new SecretServiceTokenStore(options.execFile);
|
|
37381
37515
|
}
|
|
37382
37516
|
throw new Error(
|
|
37383
37517
|
[
|
|
37384
|
-
"No OS credential store is available (the CLI uses the macOS Keychain by default
|
|
37385
|
-
`For headless Linux,
|
|
37386
|
-
"
|
|
37518
|
+
"No OS credential store is available (the CLI uses the macOS Keychain by default, Windows Credential Manager on Windows, and the Linux Secret Service when enabled).",
|
|
37519
|
+
`For headless Linux, export ${brandEnvKey2("CLI_USE_SECRET_SERVICE")}=1 to use the Secret Service, or ${allowFileStoreKey}=1 (set it for every invocation) to opt into the development-only plain-file token store.`,
|
|
37520
|
+
"The file store is not encrypted: it writes an owner-only file (directory 0700, file 0600) holding only CLI session tokens \u2014 never tenant secrets, API keys, or vault refs."
|
|
37387
37521
|
].join(" ")
|
|
37388
37522
|
);
|
|
37389
37523
|
}
|
|
@@ -37393,6 +37527,105 @@ function defaultTokenPath() {
|
|
|
37393
37527
|
function brandEnvKey2(suffix) {
|
|
37394
37528
|
return `${cliBrand.envPrefix}_${suffix}`;
|
|
37395
37529
|
}
|
|
37530
|
+
function isKeychainUnusableError(error51) {
|
|
37531
|
+
if (isKeychainItemMissing(error51)) {
|
|
37532
|
+
return false;
|
|
37533
|
+
}
|
|
37534
|
+
return true;
|
|
37535
|
+
}
|
|
37536
|
+
function isKeychainItemMissing(error51) {
|
|
37537
|
+
if (!error51 || typeof error51 !== "object") {
|
|
37538
|
+
return false;
|
|
37539
|
+
}
|
|
37540
|
+
if (error51.code === 44) {
|
|
37541
|
+
return true;
|
|
37542
|
+
}
|
|
37543
|
+
return /could not be found|SecKeychainSearchCopyNext/i.test(errorText(error51));
|
|
37544
|
+
}
|
|
37545
|
+
function errorText(error51) {
|
|
37546
|
+
if (!error51 || typeof error51 !== "object") {
|
|
37547
|
+
return String(error51 ?? "");
|
|
37548
|
+
}
|
|
37549
|
+
const record2 = error51;
|
|
37550
|
+
const parts = [];
|
|
37551
|
+
for (const key of ["message", "stderr", "stdout"]) {
|
|
37552
|
+
const value = record2[key];
|
|
37553
|
+
if (typeof value === "string") {
|
|
37554
|
+
parts.push(value);
|
|
37555
|
+
}
|
|
37556
|
+
}
|
|
37557
|
+
return parts.join("\n");
|
|
37558
|
+
}
|
|
37559
|
+
var windowsCredentialTarget = `${serviceName}/${accountName}`;
|
|
37560
|
+
function powershellArgs(script) {
|
|
37561
|
+
return ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script];
|
|
37562
|
+
}
|
|
37563
|
+
function psQuote(value) {
|
|
37564
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
37565
|
+
}
|
|
37566
|
+
var WINDOWS_CRED_MODULE = [
|
|
37567
|
+
"$ErrorActionPreference = 'Stop'",
|
|
37568
|
+
"Add-Type -TypeDefinition @'",
|
|
37569
|
+
"using System;",
|
|
37570
|
+
"using System.Runtime.InteropServices;",
|
|
37571
|
+
"public static class RostCred {",
|
|
37572
|
+
" [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]",
|
|
37573
|
+
" public struct CREDENTIAL {",
|
|
37574
|
+
" public uint Flags; public uint Type;",
|
|
37575
|
+
" [MarshalAs(UnmanagedType.LPWStr)] public string TargetName;",
|
|
37576
|
+
" [MarshalAs(UnmanagedType.LPWStr)] public string Comment;",
|
|
37577
|
+
" public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;",
|
|
37578
|
+
" public uint CredentialBlobSize; public IntPtr CredentialBlob;",
|
|
37579
|
+
" public uint Persist; public uint AttributeCount; public IntPtr Attributes;",
|
|
37580
|
+
" [MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias;",
|
|
37581
|
+
" [MarshalAs(UnmanagedType.LPWStr)] public string UserName;",
|
|
37582
|
+
" }",
|
|
37583
|
+
' [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]',
|
|
37584
|
+
" public static extern bool CredWriteW(ref CREDENTIAL cred, uint flags);",
|
|
37585
|
+
' [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]',
|
|
37586
|
+
" public static extern bool CredReadW(string target, uint type, uint flags, out IntPtr cred);",
|
|
37587
|
+
' [DllImport("advapi32.dll")] public static extern void CredFree(IntPtr cred);',
|
|
37588
|
+
' [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]',
|
|
37589
|
+
" public static extern bool CredDeleteW(string target, uint type, uint flags);",
|
|
37590
|
+
"}",
|
|
37591
|
+
"'@"
|
|
37592
|
+
].join("\n");
|
|
37593
|
+
function windowsWriteScript(target) {
|
|
37594
|
+
return [
|
|
37595
|
+
WINDOWS_CRED_MODULE,
|
|
37596
|
+
"$secret = [Console]::In.ReadToEnd()",
|
|
37597
|
+
"$bytes = [System.Text.Encoding]::Unicode.GetBytes($secret)",
|
|
37598
|
+
"$blob = [Runtime.InteropServices.Marshal]::AllocHGlobal($bytes.Length)",
|
|
37599
|
+
"[Runtime.InteropServices.Marshal]::Copy($bytes, 0, $blob, $bytes.Length)",
|
|
37600
|
+
"$cred = New-Object RostCred+CREDENTIAL",
|
|
37601
|
+
"$cred.Type = 1",
|
|
37602
|
+
`$cred.TargetName = ${psQuote(target)}`,
|
|
37603
|
+
"$cred.UserName = 'session'",
|
|
37604
|
+
"$cred.CredentialBlobSize = $bytes.Length",
|
|
37605
|
+
"$cred.CredentialBlob = $blob",
|
|
37606
|
+
"$cred.Persist = 2",
|
|
37607
|
+
"$ok = [RostCred]::CredWriteW([ref]$cred, 0)",
|
|
37608
|
+
"[Runtime.InteropServices.Marshal]::FreeHGlobal($blob)",
|
|
37609
|
+
"if (-not $ok) { exit 1 }"
|
|
37610
|
+
].join("\n");
|
|
37611
|
+
}
|
|
37612
|
+
function windowsReadScript(target) {
|
|
37613
|
+
return [
|
|
37614
|
+
WINDOWS_CRED_MODULE,
|
|
37615
|
+
"$ptr = [IntPtr]::Zero",
|
|
37616
|
+
`if (-not [RostCred]::CredReadW(${psQuote(target)}, 1, 0, [ref]$ptr)) { exit 0 }`,
|
|
37617
|
+
"$cred = [Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [Type][RostCred+CREDENTIAL])",
|
|
37618
|
+
"$secret = [Runtime.InteropServices.Marshal]::PtrToStringUni($cred.CredentialBlob, [int]($cred.CredentialBlobSize / 2))",
|
|
37619
|
+
"[RostCred]::CredFree($ptr)",
|
|
37620
|
+
"[Console]::Out.Write($secret)"
|
|
37621
|
+
].join("\n");
|
|
37622
|
+
}
|
|
37623
|
+
function windowsClearScript(target) {
|
|
37624
|
+
return [
|
|
37625
|
+
WINDOWS_CRED_MODULE,
|
|
37626
|
+
`[void][RostCred]::CredDeleteW(${psQuote(target)}, 1, 0)`
|
|
37627
|
+
].join("\n");
|
|
37628
|
+
}
|
|
37396
37629
|
|
|
37397
37630
|
// ../../packages/prompts/src/index.ts
|
|
37398
37631
|
import { readFileSync } from "node:fs";
|
|
@@ -37569,10 +37802,10 @@ var util;
|
|
|
37569
37802
|
function assertIs2(_arg) {
|
|
37570
37803
|
}
|
|
37571
37804
|
util2.assertIs = assertIs2;
|
|
37572
|
-
function
|
|
37805
|
+
function assertNever3(_x) {
|
|
37573
37806
|
throw new Error();
|
|
37574
37807
|
}
|
|
37575
|
-
util2.assertNever =
|
|
37808
|
+
util2.assertNever = assertNever3;
|
|
37576
37809
|
util2.arrayToEnum = (items) => {
|
|
37577
37810
|
const obj = {};
|
|
37578
37811
|
for (const item of items) {
|
|
@@ -37927,8 +38160,8 @@ function getErrorMap2() {
|
|
|
37927
38160
|
|
|
37928
38161
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
37929
38162
|
var makeIssue = (params) => {
|
|
37930
|
-
const { data, path:
|
|
37931
|
-
const fullPath = [...
|
|
38163
|
+
const { data, path: path5, errorMaps, issueData } = params;
|
|
38164
|
+
const fullPath = [...path5, ...issueData.path || []];
|
|
37932
38165
|
const fullIssue = {
|
|
37933
38166
|
...issueData,
|
|
37934
38167
|
path: fullPath
|
|
@@ -38044,11 +38277,11 @@ var errorUtil;
|
|
|
38044
38277
|
|
|
38045
38278
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
38046
38279
|
var ParseInputLazyPath = class {
|
|
38047
|
-
constructor(parent, value,
|
|
38280
|
+
constructor(parent, value, path5, key) {
|
|
38048
38281
|
this._cachedPath = [];
|
|
38049
38282
|
this.parent = parent;
|
|
38050
38283
|
this.data = value;
|
|
38051
|
-
this._path =
|
|
38284
|
+
this._path = path5;
|
|
38052
38285
|
this._key = key;
|
|
38053
38286
|
}
|
|
38054
38287
|
get path() {
|
|
@@ -41638,6 +41871,78 @@ The Compass is drafted, then activated by a human through supersession.
|
|
|
41638
41871
|
## When to stop for confirmation
|
|
41639
41872
|
|
|
41640
41873
|
\`onboarding.finish\`, \`compass.approve_version\`, \`compass.reject_draft\`, and \`compass.set\` are \`human_required\`. \`onboarding.advance_step\`, \`onboarding.create_invite\`, \`onboarding.attach_reference\`, \`compass.answer_gap\`, and drafting a Compass are \`none\` \u2014 none of them returns a pending confirmation. \`none\` is not the same as "agent-callable", though: \`onboarding.create_invite\` and \`compass.answer_gap\` record the human who ran them, so they run as a person, not from an agent session, while advancing a step, attaching a reference, and drafting a Compass are safe for an agent. Approving a version (a supersession) is a human act. The agent drafts and surfaces the approve link. See the confirmations guide.`
|
|
41874
|
+
},
|
|
41875
|
+
{
|
|
41876
|
+
slug: "aicos-chat-guide",
|
|
41877
|
+
order: 15,
|
|
41878
|
+
title: "AICOS chat guide",
|
|
41879
|
+
summary: "How the AI Chief of Staff chat works in the authenticated app shell and what it can safely do today.",
|
|
41880
|
+
version: "2026-07-02.3",
|
|
41881
|
+
public: true,
|
|
41882
|
+
audiences: ["human", "in_app_agent"],
|
|
41883
|
+
stages: ["company_setup", "operating_rhythm"],
|
|
41884
|
+
relatedCommandIds: ["confirmation.approve", "onboarding.status", "graph.get", "seat.get"],
|
|
41885
|
+
legal: {
|
|
41886
|
+
publicRisk: "low",
|
|
41887
|
+
notes: [
|
|
41888
|
+
"Describes the authenticated AICOS chat surface without promising autonomous action.",
|
|
41889
|
+
"Keeps authority, lane, and confirmation language aligned with the platform governance model."
|
|
41890
|
+
]
|
|
41891
|
+
},
|
|
41892
|
+
sources: [
|
|
41893
|
+
{
|
|
41894
|
+
label: "AICOS Company Chief of Staff plan",
|
|
41895
|
+
internalPath: "docs/plans/2026-07-01-003-feat-aicos-company-chief-of-staff-plan.md",
|
|
41896
|
+
note: "Defines the AICOS product contract, lane portability, and governance boundaries."
|
|
41897
|
+
},
|
|
41898
|
+
{
|
|
41899
|
+
label: "MVP implementation plan",
|
|
41900
|
+
internalPath: "docs/specs/MVP-implementation-plan.md",
|
|
41901
|
+
note: "FR-10.6 records the AICOS extension and chat harness rollout."
|
|
41902
|
+
}
|
|
41903
|
+
],
|
|
41904
|
+
body: `# AICOS chat guide
|
|
41905
|
+
|
|
41906
|
+
AICOS is the AI Chief of Staff for an authenticated {{brand}} tenant. It is a governed agent seat in the Responsibility Graph, not an anonymous public chatbot and not a hidden superuser.
|
|
41907
|
+
|
|
41908
|
+
## Where AICOS appears
|
|
41909
|
+
|
|
41910
|
+
In the authenticated app shell, AICOS opens from the floating assistant control. The panel is available across normal app pages and keeps the same thread while you move through the product. Full-bleed public, login, onboarding, docs, invite, and demo routes do not receive the authenticated shell.
|
|
41911
|
+
|
|
41912
|
+
Each thread has a purpose:
|
|
41913
|
+
|
|
41914
|
+
- **Onboarding** \u2014 setup help and page-aware setup context.
|
|
41915
|
+
- **Seat** \u2014 seat and agent setup context.
|
|
41916
|
+
- **General** \u2014 company operating help.
|
|
41917
|
+
|
|
41918
|
+
You can reopen prior threads or start a new one. Session history is stored in tenant-scoped conversation tables so chat history is not reconstructed from analytics, events, or run logs.
|
|
41919
|
+
|
|
41920
|
+
## What context AICOS receives
|
|
41921
|
+
|
|
41922
|
+
Each turn carries server-normalized page context: route template, current path, safe search state, selected seat or object ids when authorized, and onboarding state when relevant. The server derives tenant and user authority from the authenticated session. Client-supplied tenant context is never accepted as authority.
|
|
41923
|
+
|
|
41924
|
+
The current chat harness shows the effective lane and model. The initial lane is cloud, with a deterministic harness response while live model execution, action execution, and runner execution waves land. The contract is intentionally backend-neutral: future cloud and runner turns use the same sessions, messages, lane labels, sources, and linked artifacts.
|
|
41925
|
+
|
|
41926
|
+
Model behavior is now explicit in the AICOS session contract. A thread can run in **Auto** or use a pinned compatible model from the guided catalog. Auto chooses a fast tier for short low-risk turns, the balanced default for normal operating help, and a higher-reasoning tier for onboarding synthesis or large context. The panel shows the effective model, disables lower-context choices once the active thread outgrows them, and tells the user to start a new session for those models. Session context is estimated and compacted into a durable summary when it crosses the configured threshold; raw messages remain in history, but future model calls can use the summary rather than resending the whole thread. Platform allowance and BYOK state are checked before routing; an exhausted included allowance keeps AICOS in guided mode and points the user toward BYOK/manual continuation. Because the DER-1068 implementation still uses the deterministic harness, it does not create fake llm_calls rows or settle fake allowance usage; reservation/settlement belongs to the real provider call path.
|
|
41927
|
+
|
|
41928
|
+
AICOS grounds answers from two source classes before producing user-visible setup or operating guidance:
|
|
41929
|
+
|
|
41930
|
+
- **Reference documents** \u2014 public {{brand}} guides such as this chat guide, onboarding guidance, the Responsibility Graph playbook, tool-access guidance, and confirmation guidance. AICOS retrieves the narrowest relevant guide before setup, Charter, Signal, Friction, Cascade, Sync, tool-access, or onboarding advice.
|
|
41931
|
+
- **Tenant read models** \u2014 bounded summaries of tenant operating state, including onboarding status, graph/setup readiness, Compass/Charter readiness, operating health, agent/system health, approvals/inbox, and selected Seat context. These read models expose counts and short observations rather than raw database dumps.
|
|
41932
|
+
|
|
41933
|
+
Assistant messages can carry source counts and persisted source metadata so a thread can later show which reference docs or read models informed the answer.
|
|
41934
|
+
|
|
41935
|
+
## Governance boundary
|
|
41936
|
+
|
|
41937
|
+
AICOS can preserve context and explain what it is ready to do, but durable operating changes still use the same {{brand}} controls:
|
|
41938
|
+
|
|
41939
|
+
- Tenant isolation is enforced by RLS and server-side tenant context.
|
|
41940
|
+
- Raw secrets, vault refs, hidden tool arguments, storage refs, provider payloads, agent custom prompts, and secret-shaped text are not stored in chat history or read-model context.
|
|
41941
|
+
- Durable decisions remain human decisions.
|
|
41942
|
+
- Action execution must route through command guards and confirmation when required.
|
|
41943
|
+
- Expanding authority, approving Charters or Compass versions, going live, credentials, external sends, money, destructive operations, and policy changes stay human-gated.
|
|
41944
|
+
|
|
41945
|
+
Treat AICOS as a coordinator over the operating system. It can become more useful as model grounding, read tools, draft actions, and runner execution are enabled, but it does not bypass the Responsibility Graph, Seat Steward accountability, command guards, or confirmations.`
|
|
41641
41946
|
},
|
|
41642
41947
|
{
|
|
41643
41948
|
slug: "responsibility-graph-playbook",
|
|
@@ -42412,7 +42717,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
42412
42717
|
order: 48,
|
|
42413
42718
|
title: "CLI and MCP installation guide",
|
|
42414
42719
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
42415
|
-
version: "2026-07-02.
|
|
42720
|
+
version: "2026-07-02.6",
|
|
42416
42721
|
public: true,
|
|
42417
42722
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
42418
42723
|
stages: ["company_setup", "staffing"],
|
|
@@ -42869,6 +43174,7 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
|
|
|
42869
43174
|
| \`{{cli}} error list|resolve|supersede\` | \`error_log.list\`, \`error_log.resolve\`, \`error_log.supersede\` | List active, acknowledged, resolved, or superseded product error logs and let a human acknowledge, resolve, or supersede stale errors with evidence links. | Tenant | \`{{cli}} error list --resolved active --json\`; \`{{cli}} error resolve --error-log-id <id> --reason "Fixed in PR #123"\` |
|
|
42870
43175
|
| \`{{cli}} sync brief|compile|complete\` | \`sync.brief.get\`, \`sync.brief.compile\`, \`sync.run.complete\` | Compile, read, and complete a weekly Sync. | Tenant | \`{{cli}} sync brief --json\` |
|
|
42871
43176
|
| \`{{cli}} runner list|status|diagnose|repair|work-orders|revoke|serve\` | \`runner.list\`, \`runner.status\`, \`runner.diagnose\`, \`runner.repair\`, \`work_order.list\`, \`runner.revoke\`, runner endpoint APIs | Inspect heartbeat and execute-readiness evidence, inspect work orders, diagnose offline runners, get repair guidance, revoke a runner, or run the headless local runner loop. | Tenant | \`{{cli}} runner diagnose --runner-id <id> --json\`; \`{{cli}} runner serve --once\` |
|
|
43177
|
+
| \`{{cli}} runner install-service|start|stop|restart|status|logs|uninstall --name <name>\` | launchd/service manager (macOS today) + \`{{cli}} runner serve\` | Install and manage an always-on local runner service that keeps the runner online across reboots; the service shells out to \`{{cli}} runner serve --execute\` and stores no secret in the service definition, its arguments, or logs. Non-macOS emits an explicit unsupported message. | Tenant (local machine) | \`{{cli}} runner install-service --name mac-mini --execute\`; \`{{cli}} runner logs --name mac-mini\` |
|
|
42872
43178
|
| \`{{cli}} notification settings|test|errors\` | \`notification.settings.get\`, \`notification.test\`, \`notification.list_errors\` | Read notification settings, send a test, and list failed deliveries with linked product error source, seat id, and run id when available. | Tenant | \`{{cli}} notification errors --limit 10 --json\` |
|
|
42873
43179
|
| \`{{cli}} integration list|readiness|status|test\` / credential flow for Baserow REST connect | \`integration.connect_rest\`, \`integration.list\`, \`integration.readiness\`, \`integration.status\`, \`integration.test\` | Create a vault-backed Baserow REST integration through the credential flow, list connector metadata, read setup-readiness, read one connector's health, and run provider-specific tests without exposing credentials. | Tenant-admin for connect; tenant for reads/tests | \`{{cli}} integration readiness --provider google --json\` |
|
|
42874
43180
|
| \`{{cli}} system health\` | \`system.health\` | Read the scoped system-health snapshot across agent runs, unresolved errors, Signals, Cascade setup, work loop, integrations, runners, notifications, and Sync Brief readiness. | Tenant, member, or seat token | \`{{cli}} system health --json\`; \`{{cli}} system health --seat <id> --json\` |
|
|
@@ -43461,7 +43767,7 @@ Set \`ROST_SKILL_INSTALL_ROOT\` only for a sandbox or CI override. Local copies
|
|
|
43461
43767
|
order: 55,
|
|
43462
43768
|
title: "Agent reference map",
|
|
43463
43769
|
summary: "Where CLI sessions, MCP clients, and in-app agents should retrieve {{brand}} guidance before recommending setup changes.",
|
|
43464
|
-
version: "2026-07-02.
|
|
43770
|
+
version: "2026-07-02.3",
|
|
43465
43771
|
public: true,
|
|
43466
43772
|
audiences: ["cli", "mcp", "in_app_agent"],
|
|
43467
43773
|
stages: ["company_setup", "graph_design", "charter_design", "staffing", "operating_rhythm"],
|
|
@@ -43525,28 +43831,29 @@ Agents that run on {{brand}}-managed inference draw against a tenant inference b
|
|
|
43525
43831
|
|
|
43526
43832
|
1. rost-implementation-method
|
|
43527
43833
|
2. compass-authoring-guide
|
|
43528
|
-
3.
|
|
43529
|
-
4.
|
|
43530
|
-
5. charter-
|
|
43531
|
-
6.
|
|
43532
|
-
7.
|
|
43533
|
-
8.
|
|
43534
|
-
9.
|
|
43535
|
-
10.
|
|
43536
|
-
11.
|
|
43537
|
-
12.
|
|
43538
|
-
13.
|
|
43539
|
-
14.
|
|
43540
|
-
15.
|
|
43541
|
-
16. agent-skill-
|
|
43542
|
-
17.
|
|
43543
|
-
18.
|
|
43544
|
-
19.
|
|
43545
|
-
20.
|
|
43546
|
-
21.
|
|
43547
|
-
22.
|
|
43548
|
-
23.
|
|
43549
|
-
24.
|
|
43834
|
+
3. aicos-chat-guide
|
|
43835
|
+
4. responsibility-graph-playbook
|
|
43836
|
+
5. charter-design-playbook
|
|
43837
|
+
6. charter-authoring-deep-dive
|
|
43838
|
+
7. agent-staffing-playbook
|
|
43839
|
+
8. add-agents-guide
|
|
43840
|
+
9. custom-agents-guide
|
|
43841
|
+
10. agent-builder-guide
|
|
43842
|
+
11. how-agents-work
|
|
43843
|
+
12. tool-access-and-vault
|
|
43844
|
+
13. available-tools-guide
|
|
43845
|
+
14. mcp-and-cli-guide
|
|
43846
|
+
15. skill-builder-guide
|
|
43847
|
+
16. agent-skill-authoring-guide
|
|
43848
|
+
17. agent-skill-setup-guide
|
|
43849
|
+
18. ai-model-data-handling-guide
|
|
43850
|
+
19. cascade-guide
|
|
43851
|
+
20. signal-guide
|
|
43852
|
+
21. friction-guide
|
|
43853
|
+
22. confirmations-guide
|
|
43854
|
+
23. steward-queue-guide
|
|
43855
|
+
24. sync-rhythm-playbook
|
|
43856
|
+
25. billing-and-pricing-guide
|
|
43550
43857
|
|
|
43551
43858
|
## Workflow to guide map
|
|
43552
43859
|
|
|
@@ -43554,6 +43861,7 @@ Read the listed guide before recommending or running each workflow. Every workfl
|
|
|
43554
43861
|
|
|
43555
43862
|
- Connect a local agent (CLI login, MCP token, client registration): mcp-and-cli-guide, then agent-reference-map.
|
|
43556
43863
|
- Company onboarding (context, Compass draft, finish): rost-implementation-method, then settings-members-and-invites-guide for invites.
|
|
43864
|
+
- Use the in-app AI Chief of Staff chat or explain its current authority boundary: aicos-chat-guide, then confirmations-guide for any durable change.
|
|
43557
43865
|
- Author a strong Compass directly (attach references, draft, answer gaps, approve): compass-authoring-guide \u2014 it shows the full JSON contract, what good vs weak looks like per field, and worked examples. Attaching reference docs (onboarding.attach_reference) is reference-only; you author the Compass, the server does not build it for you.
|
|
43558
43866
|
- Design the graph (create, rename, reparent, set type): responsibility-graph-playbook.
|
|
43559
43867
|
- Write Charters (draft, edit, approve, sign manifest): charter-design-playbook for the narrative, then charter-authoring-deep-dive for the full document contract field by field (decision_authority, escalation_rules, measurables, budget, permission_manifest, seat-type), then tool-access-and-vault for permissions. Submit the finished Charter directly with charter.update_draft or charter.set.
|
|
@@ -43577,11 +43885,11 @@ Read the listed guide before recommending or running each workflow. Every workfl
|
|
|
43577
43885
|
|
|
43578
43886
|
## Stage map
|
|
43579
43887
|
|
|
43580
|
-
- Company setup: rost-implementation-method, compass-authoring-guide, settings-guide, settings-members-and-invites-guide
|
|
43888
|
+
- Company setup: rost-implementation-method, compass-authoring-guide, aicos-chat-guide, settings-guide, settings-members-and-invites-guide
|
|
43581
43889
|
- Graph design: responsibility-graph-playbook
|
|
43582
43890
|
- Charter design: charter-design-playbook, charter-authoring-deep-dive
|
|
43583
43891
|
- Staffing: agent-staffing-playbook, add-agents-guide, custom-agents-guide, agent-builder-guide, how-agents-work, tool-access-and-vault, available-tools-guide, skill-builder-guide, agent-skill-authoring-guide, agent-skill-setup-guide, mcp-and-cli-guide, stock-agents-guide
|
|
43584
|
-
- Operating rhythm: cascade-guide, signal-guide, friction-guide, confirmations-guide, steward-queue-guide, sync-rhythm-playbook, notifications-guide
|
|
43892
|
+
- Operating rhythm: aicos-chat-guide, cascade-guide, signal-guide, friction-guide, confirmations-guide, steward-queue-guide, sync-rhythm-playbook, notifications-guide
|
|
43585
43893
|
- Local agents and runners: mcp-and-cli-guide, runner-guide, agent-reference-map
|
|
43586
43894
|
- Security and troubleshooting: ai-model-data-handling-guide, security-model-guide, troubleshooting-guide
|
|
43587
43895
|
|
|
@@ -44292,7 +44600,7 @@ Every notification should include the seat, cause, evidence, and requested decis
|
|
|
44292
44600
|
order: 75,
|
|
44293
44601
|
title: "Local runner guide",
|
|
44294
44602
|
summary: "How local agent sessions and runner surfaces should operate through {{brand}} without bypassing Charters or audit.",
|
|
44295
|
-
version: "2026-
|
|
44603
|
+
version: "2026-07-02.1",
|
|
44296
44604
|
public: true,
|
|
44297
44605
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
44298
44606
|
stages: ["staffing", "operating_rhythm"],
|
|
@@ -44318,6 +44626,26 @@ The local runner is for human-controlled local agent work. It should retrieve {{
|
|
|
44318
44626
|
5. Record work, Friction, handoffs, and escalations.
|
|
44319
44627
|
6. Revoke access when the session ends.
|
|
44320
44628
|
|
|
44629
|
+
## Install a local Runner
|
|
44630
|
+
|
|
44631
|
+
Use Local Runner when a Seat should run on a machine you control. Install it from the public CLI \u2014 no repository checkout and no desktop app are required. A signed desktop app is planned; until verified installers ship, Settings > Runners shows no desktop download link.
|
|
44632
|
+
|
|
44633
|
+
Recommended paths:
|
|
44634
|
+
|
|
44635
|
+
- **Always-on host:** \`{{cli}} runner install-service --name <runner-name> --execute\` installs a background service (macOS launchd user agent) that keeps the runner online across reboots. Manage it with \`{{cli}} runner start|stop|restart|status|logs|uninstall --name <runner-name>\`.
|
|
44636
|
+
- **Manual smoke:** \`{{cli}} runner serve --once\` heartbeats once and exits; \`{{cli}} runner serve --execute\` claims and executes runner work orders while it runs.
|
|
44637
|
+
|
|
44638
|
+
Two ways to authenticate and pair \u2014 both first-class, an agent can drive either entirely from the terminal:
|
|
44639
|
+
|
|
44640
|
+
- **CLI-native (agent):** \`{{cli}} login --device\` prints a code for one human OAuth approval; after that the session is stored locally and the agent runs unattended ("approve once, then autonomous"). Then \`{{cli}} runner serve\`/\`install-service\` pairs against your active company \u2014 an authenticated CLI can also self-issue a pairing code with \`runner.pairing.start\` and claim it.
|
|
44641
|
+
- **Web-guided (human):** issue a pairing code from Settings > Runners > Add a Runner, then on the host run \`{{cli}} runner serve --user-code <code>\`. The host stores only the runner bearer secret in an owner-only file and never logs in.
|
|
44642
|
+
|
|
44643
|
+
Headless and service accounts are supported first-class: when no usable OS keychain exists (for example a service or SSH account), the CLI stores the session in an owner-only file (directory 0700, file 0600) automatically \u2014 no development flag. That file holds only the CLI session token, never tenant secrets, API keys, or vault refs.
|
|
44644
|
+
|
|
44645
|
+
Choose the local runtime with \`--runtime auto|claude|codex\` (default \`auto\`). The same short-lived seat-scoped MCP token and server-side guard apply regardless of runtime; results are tagged \`claude-cli\` or \`codex-cli\`.
|
|
44646
|
+
|
|
44647
|
+
The runner bearer secret is stored locally and is never displayed. Each claimed turn receives a short-lived seat-scoped MCP token from {{brand}}, and server-side guards still decide what tools the Seat may use.
|
|
44648
|
+
|
|
44321
44649
|
## Inspect and control runners from CLI or MCP
|
|
44322
44650
|
|
|
44323
44651
|
- Pair a new runner: \`runner.pairing.start\` / \`rost_start_runner_pairing\` with \`name\` and \`platform\` returns a human pairing code. This is owner/admin-only because it mints a short-lived runner pairing session that leads to machine credentials.
|
|
@@ -47955,6 +48283,9 @@ function operationUsageLines(bin) {
|
|
|
47955
48283
|
`${bin} error list|resolve|supersede`,
|
|
47956
48284
|
`${bin} sync brief|compile|complete`,
|
|
47957
48285
|
`${bin} runner list|status|diagnose|repair|work-orders|revoke|serve`,
|
|
48286
|
+
// Local runner service lifecycle (DER-1142). The orchestrator mirrors this
|
|
48287
|
+
// exact line into the mcp-and-cli-guide so the help/guide parity gate passes.
|
|
48288
|
+
`${bin} runner install-service|start|stop|restart|status|logs|uninstall --name <name>`,
|
|
47958
48289
|
`${bin} notification settings|test|errors`,
|
|
47959
48290
|
`${bin} settings get|update|product-learning|agent-policy|rename`,
|
|
47960
48291
|
`${bin} member invite|update|remove`,
|
|
@@ -48529,8 +48860,8 @@ function pendingConfirmationFromError(error51) {
|
|
|
48529
48860
|
import { execFile as execFileCallback2 } from "node:child_process";
|
|
48530
48861
|
import { access, readFile as readFile3 } from "node:fs/promises";
|
|
48531
48862
|
import { delimiter } from "node:path";
|
|
48532
|
-
import { promisify as
|
|
48533
|
-
var
|
|
48863
|
+
import { promisify as promisify2 } from "node:util";
|
|
48864
|
+
var execFile2 = promisify2(execFileCallback2);
|
|
48534
48865
|
async function runDoctor(options) {
|
|
48535
48866
|
const env = options.env ?? process.env;
|
|
48536
48867
|
const store = options.store ?? createTokenStore();
|
|
@@ -48578,7 +48909,7 @@ async function checkPath(env) {
|
|
|
48578
48909
|
}
|
|
48579
48910
|
async function probeCli(command) {
|
|
48580
48911
|
try {
|
|
48581
|
-
const { stdout, stderr } = await
|
|
48912
|
+
const { stdout, stderr } = await execFile2(command, ["--version"], { timeout: 1500 });
|
|
48582
48913
|
const version4 = (stdout || stderr).trim().split("\n")[0]?.trim();
|
|
48583
48914
|
return version4 ? `installed (${version4})` : "installed";
|
|
48584
48915
|
} catch {
|
|
@@ -48653,8 +48984,8 @@ import { chmod, mkdir as mkdir2, readFile as readFile4, rm as rm2, writeFile as
|
|
|
48653
48984
|
import { hostname as hostname3, tmpdir } from "node:os";
|
|
48654
48985
|
import path3 from "node:path";
|
|
48655
48986
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
48656
|
-
import { promisify as
|
|
48657
|
-
var
|
|
48987
|
+
import { promisify as promisify3 } from "node:util";
|
|
48988
|
+
var execFile3 = promisify3(execFileCallback3);
|
|
48658
48989
|
async function runRunnerServe(options) {
|
|
48659
48990
|
const env = options.env ?? process.env;
|
|
48660
48991
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -48693,8 +49024,11 @@ ${usage()}
|
|
|
48693
49024
|
options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
|
|
48694
49025
|
`);
|
|
48695
49026
|
}
|
|
48696
|
-
if (config2.execute
|
|
48697
|
-
|
|
49027
|
+
if (config2.execute) {
|
|
49028
|
+
const localRuntime = effectiveExecutionRuntime(config2.runtime, capabilities);
|
|
49029
|
+
if (localRuntime) {
|
|
49030
|
+
await claimAndExecute(fetchImpl, options.appUrl, state, options.io, localRuntime);
|
|
49031
|
+
}
|
|
48698
49032
|
}
|
|
48699
49033
|
} catch (error51) {
|
|
48700
49034
|
failed = true;
|
|
@@ -48714,6 +49048,7 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48714
49048
|
let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
|
|
48715
49049
|
let execute2 = env.RUNNER_EXECUTE === "1";
|
|
48716
49050
|
let once = false;
|
|
49051
|
+
let runtime = parseRuntime(env.RUNNER_RUNTIME ?? "auto");
|
|
48717
49052
|
let userCode = env.RUNNER_USER_CODE;
|
|
48718
49053
|
for (let index = 0; index < args.length; index += 1) {
|
|
48719
49054
|
const token = args[index];
|
|
@@ -48729,6 +49064,9 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48729
49064
|
} else if (token === "--user-code") {
|
|
48730
49065
|
userCode = requiredValue(args, index);
|
|
48731
49066
|
index += 1;
|
|
49067
|
+
} else if (token === "--runtime") {
|
|
49068
|
+
runtime = parseRuntime(requiredValue(args, index));
|
|
49069
|
+
index += 1;
|
|
48732
49070
|
} else if (token === "--execute") {
|
|
48733
49071
|
execute2 = true;
|
|
48734
49072
|
} else if (token === "--once") {
|
|
@@ -48740,7 +49078,19 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48740
49078
|
if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3) {
|
|
48741
49079
|
throw new Error("--heartbeat-ms must be an integer >= 1000.");
|
|
48742
49080
|
}
|
|
48743
|
-
return { name, stateFile, heartbeatMs, execute: execute2, once, ...userCode ? { userCode } : {} };
|
|
49081
|
+
return { name, stateFile, heartbeatMs, execute: execute2, once, runtime, ...userCode ? { userCode } : {} };
|
|
49082
|
+
}
|
|
49083
|
+
function parseRuntime(value) {
|
|
49084
|
+
if (value === "auto" || value === "claude" || value === "codex") {
|
|
49085
|
+
return value;
|
|
49086
|
+
}
|
|
49087
|
+
throw new Error(`--runtime must be one of auto, claude, codex (got ${value}).`);
|
|
49088
|
+
}
|
|
49089
|
+
function effectiveExecutionRuntime(runtime, capabilities) {
|
|
49090
|
+
if (runtime === "codex") {
|
|
49091
|
+
return capabilities.codex.installed ? "codex" : null;
|
|
49092
|
+
}
|
|
49093
|
+
return capabilities.claude.installed ? "claude" : null;
|
|
48744
49094
|
}
|
|
48745
49095
|
function requiredValue(args, index) {
|
|
48746
49096
|
const value = args[index + 1];
|
|
@@ -48830,7 +49180,7 @@ async function detectCapabilities() {
|
|
|
48830
49180
|
}
|
|
48831
49181
|
async function probe(command) {
|
|
48832
49182
|
try {
|
|
48833
|
-
const { stdout, stderr } = await
|
|
49183
|
+
const { stdout, stderr } = await execFile3(command, ["--version"], { timeout: 1500 });
|
|
48834
49184
|
return { installed: true, version: (stdout || stderr).trim() || null, auth_state: "ready" };
|
|
48835
49185
|
} catch {
|
|
48836
49186
|
return { installed: false, version: null, auth_state: "unknown" };
|
|
@@ -48855,7 +49205,7 @@ async function post2(fetchImpl, appUrl2, pathName, body, token) {
|
|
|
48855
49205
|
}
|
|
48856
49206
|
return { status: response.status, json: json2 };
|
|
48857
49207
|
}
|
|
48858
|
-
async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
49208
|
+
async function claimAndExecute(fetchImpl, appUrl2, state, io, runtime) {
|
|
48859
49209
|
const claimed = await post2(fetchImpl, appUrl2, "/api/runner/work-orders/claim", {}, state.runner_secret);
|
|
48860
49210
|
const workOrder = claimed.json.work_order;
|
|
48861
49211
|
if (claimed.status !== 200 || !workOrder || typeof workOrder !== "object") {
|
|
@@ -48869,12 +49219,12 @@ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
|
48869
49219
|
if (started.status !== 200) {
|
|
48870
49220
|
throw new Error(`work order start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
|
|
48871
49221
|
}
|
|
48872
|
-
const result = await
|
|
49222
|
+
const result = await runLocalTurn(appUrl2, workOrder, runtime);
|
|
48873
49223
|
const reported = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/result`, {
|
|
48874
49224
|
status: result.ok ? "succeeded" : "failed",
|
|
48875
49225
|
summary: result.summary,
|
|
48876
49226
|
log_ref: `runner-local:${workOrderId}`,
|
|
48877
|
-
model: "claude-cli"
|
|
49227
|
+
model: runtime === "codex" ? "codex-cli" : "claude-cli"
|
|
48878
49228
|
}, state.runner_secret);
|
|
48879
49229
|
if (reported.status !== 200) {
|
|
48880
49230
|
throw new Error(`work order result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
|
|
@@ -48882,7 +49232,28 @@ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
|
48882
49232
|
io.stdout.write(`work_order_id=${workOrderId} status=${result.ok ? "succeeded" : "failed"}
|
|
48883
49233
|
`);
|
|
48884
49234
|
}
|
|
48885
|
-
|
|
49235
|
+
function runLocalTurn(appUrl2, workOrder, runtime) {
|
|
49236
|
+
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder) : runClaudeTurn(appUrl2, workOrder);
|
|
49237
|
+
}
|
|
49238
|
+
function runClaudeTurn(appUrl2, workOrder) {
|
|
49239
|
+
return spawnRunnerTurn(appUrl2, workOrder, "claude");
|
|
49240
|
+
}
|
|
49241
|
+
function runCodexTurn(appUrl2, workOrder) {
|
|
49242
|
+
return spawnRunnerTurn(appUrl2, workOrder, "codex");
|
|
49243
|
+
}
|
|
49244
|
+
function buildTurnCommand(runtime, prompt, configPath) {
|
|
49245
|
+
if (runtime === "codex") {
|
|
49246
|
+
return {
|
|
49247
|
+
command: "codex",
|
|
49248
|
+
args: ["exec", prompt, "--mcp-config", configPath, "--sandbox", "read-only", "--skip-git-repo-check"]
|
|
49249
|
+
};
|
|
49250
|
+
}
|
|
49251
|
+
return {
|
|
49252
|
+
command: "claude",
|
|
49253
|
+
args: ["-p", prompt, "--mcp-config", configPath, "--strict-mcp-config", "--output-format", "text"]
|
|
49254
|
+
};
|
|
49255
|
+
}
|
|
49256
|
+
async function spawnRunnerTurn(appUrl2, workOrder, runtime) {
|
|
48886
49257
|
const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
|
|
48887
49258
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
48888
49259
|
const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
|
|
@@ -48903,6 +49274,7 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48903
49274
|
await writeFile2(configPath, `${mcpConfig}
|
|
48904
49275
|
`, { mode: 384 });
|
|
48905
49276
|
await chmod(configPath, 384);
|
|
49277
|
+
const { command, args } = buildTurnCommand(runtime, prompt, configPath);
|
|
48906
49278
|
return new Promise((resolve2) => {
|
|
48907
49279
|
let settled = false;
|
|
48908
49280
|
const finish = (result) => {
|
|
@@ -48913,7 +49285,7 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48913
49285
|
void rm2(configPath, { force: true });
|
|
48914
49286
|
resolve2(result);
|
|
48915
49287
|
};
|
|
48916
|
-
const child = spawn(
|
|
49288
|
+
const child = spawn(command, args, {
|
|
48917
49289
|
cwd: tmpdir(),
|
|
48918
49290
|
stdio: ["ignore", "pipe", "pipe"]
|
|
48919
49291
|
});
|
|
@@ -48940,7 +49312,431 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48940
49312
|
});
|
|
48941
49313
|
}
|
|
48942
49314
|
function usage() {
|
|
48943
|
-
return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--once] [--execute]";
|
|
49315
|
+
return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--runtime auto|claude|codex] [--once] [--execute]";
|
|
49316
|
+
}
|
|
49317
|
+
|
|
49318
|
+
// src/runner-service.ts
|
|
49319
|
+
import { execFile as execFileCallback4 } from "node:child_process";
|
|
49320
|
+
import { realpathSync } from "node:fs";
|
|
49321
|
+
import { access as access2, mkdir as mkdir3, readFile as readFile5, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
49322
|
+
import { homedir as homedir2 } from "node:os";
|
|
49323
|
+
import path4 from "node:path";
|
|
49324
|
+
import { promisify as promisify4 } from "node:util";
|
|
49325
|
+
var execFile4 = promisify4(execFileCallback4);
|
|
49326
|
+
var SERVICE_VERBS = ["install-service", "start", "stop", "restart", "status", "logs", "uninstall"];
|
|
49327
|
+
function isServiceVerb(value) {
|
|
49328
|
+
return value !== void 0 && SERVICE_VERBS.includes(value);
|
|
49329
|
+
}
|
|
49330
|
+
function isRunnerServiceInvocation(args) {
|
|
49331
|
+
const verb = args[0];
|
|
49332
|
+
if (verb === "status") {
|
|
49333
|
+
return args.includes("--name");
|
|
49334
|
+
}
|
|
49335
|
+
return isServiceVerb(verb);
|
|
49336
|
+
}
|
|
49337
|
+
function runnerServicePaths(homeDir, runnerName) {
|
|
49338
|
+
const safeName = safeRunnerName(runnerName);
|
|
49339
|
+
return {
|
|
49340
|
+
launchAgentPath: path4.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
|
|
49341
|
+
stateFile: path4.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
|
|
49342
|
+
logDir: path4.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
|
|
49343
|
+
};
|
|
49344
|
+
}
|
|
49345
|
+
function renderLaunchdPlist(config2) {
|
|
49346
|
+
const programArgs = [
|
|
49347
|
+
config2.binPath,
|
|
49348
|
+
"runner",
|
|
49349
|
+
"serve",
|
|
49350
|
+
...config2.execute ? ["--execute"] : [],
|
|
49351
|
+
"--name",
|
|
49352
|
+
config2.runnerName,
|
|
49353
|
+
"--state-file",
|
|
49354
|
+
config2.stateFile
|
|
49355
|
+
];
|
|
49356
|
+
const programArgsXml = programArgs.map((arg) => ` <string>${escapePlist(arg)}</string>`).join("\n");
|
|
49357
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
49358
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
49359
|
+
<plist version="1.0">
|
|
49360
|
+
<dict>
|
|
49361
|
+
<key>Label</key>
|
|
49362
|
+
<string>${escapePlist(serviceLabel(config2.runnerName))}</string>
|
|
49363
|
+
<key>ProgramArguments</key>
|
|
49364
|
+
<array>
|
|
49365
|
+
${programArgsXml}
|
|
49366
|
+
</array>
|
|
49367
|
+
<key>EnvironmentVariables</key>
|
|
49368
|
+
<dict>
|
|
49369
|
+
<key>${cliBrand.envPrefix}_APP_URL</key>
|
|
49370
|
+
<string>${escapePlist(config2.appUrl)}</string>
|
|
49371
|
+
</dict>
|
|
49372
|
+
<key>KeepAlive</key>
|
|
49373
|
+
<true/>
|
|
49374
|
+
<key>RunAtLoad</key>
|
|
49375
|
+
<true/>
|
|
49376
|
+
<key>StandardOutPath</key>
|
|
49377
|
+
<string>${escapePlist(path4.join(config2.logDir, "stdout.log"))}</string>
|
|
49378
|
+
<key>StandardErrorPath</key>
|
|
49379
|
+
<string>${escapePlist(path4.join(config2.logDir, "stderr.log"))}</string>
|
|
49380
|
+
</dict>
|
|
49381
|
+
</plist>
|
|
49382
|
+
`;
|
|
49383
|
+
}
|
|
49384
|
+
async function runRunnerService(options) {
|
|
49385
|
+
const verb = options.args[0];
|
|
49386
|
+
if (!isServiceVerb(verb)) {
|
|
49387
|
+
options.io.stderr.write(`Unknown runner service command: ${verb ?? ""}
|
|
49388
|
+
${serviceUsage()}
|
|
49389
|
+
`);
|
|
49390
|
+
return 1;
|
|
49391
|
+
}
|
|
49392
|
+
if (options.args.includes("--help") || options.args.includes("-h")) {
|
|
49393
|
+
options.io.stdout.write(`${serviceUsage()}
|
|
49394
|
+
`);
|
|
49395
|
+
return 0;
|
|
49396
|
+
}
|
|
49397
|
+
let parsed;
|
|
49398
|
+
try {
|
|
49399
|
+
parsed = parseServiceArgs(options.args.slice(1));
|
|
49400
|
+
} catch (error51) {
|
|
49401
|
+
options.io.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
|
|
49402
|
+
${serviceUsage()}
|
|
49403
|
+
`);
|
|
49404
|
+
return 1;
|
|
49405
|
+
}
|
|
49406
|
+
const cmd = {
|
|
49407
|
+
io: options.io,
|
|
49408
|
+
runnerName: parsed.runnerName,
|
|
49409
|
+
appUrl: options.appUrl,
|
|
49410
|
+
execute: parsed.execute,
|
|
49411
|
+
binPath: options.binPath ?? resolveBinPath(),
|
|
49412
|
+
homeDir: options.homeDir ?? homedir2(),
|
|
49413
|
+
platform: options.platform ?? process.platform,
|
|
49414
|
+
exec: options.exec ?? defaultExec,
|
|
49415
|
+
uid: options.uid ?? (process.getuid ? process.getuid() : 0)
|
|
49416
|
+
};
|
|
49417
|
+
switch (verb) {
|
|
49418
|
+
case "install-service":
|
|
49419
|
+
return installService(cmd);
|
|
49420
|
+
case "start":
|
|
49421
|
+
return startService(cmd);
|
|
49422
|
+
case "stop":
|
|
49423
|
+
return stopService(cmd);
|
|
49424
|
+
case "restart":
|
|
49425
|
+
return restartService(cmd);
|
|
49426
|
+
case "status":
|
|
49427
|
+
return statusService(cmd);
|
|
49428
|
+
case "logs":
|
|
49429
|
+
return logsService(cmd);
|
|
49430
|
+
case "uninstall":
|
|
49431
|
+
return uninstallService(cmd);
|
|
49432
|
+
default:
|
|
49433
|
+
return assertNever2(verb);
|
|
49434
|
+
}
|
|
49435
|
+
}
|
|
49436
|
+
async function installService(options) {
|
|
49437
|
+
if (options.platform !== "darwin") {
|
|
49438
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49439
|
+
}
|
|
49440
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49441
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49442
|
+
const label = serviceLabel(safeName);
|
|
49443
|
+
await mkdir3(paths.logDir, { recursive: true });
|
|
49444
|
+
await mkdir3(path4.dirname(paths.stateFile), { recursive: true });
|
|
49445
|
+
await mkdir3(path4.dirname(paths.launchAgentPath), { recursive: true });
|
|
49446
|
+
const plist = renderLaunchdPlist({
|
|
49447
|
+
binPath: options.binPath,
|
|
49448
|
+
runnerName: safeName,
|
|
49449
|
+
stateFile: paths.stateFile,
|
|
49450
|
+
logDir: paths.logDir,
|
|
49451
|
+
appUrl: options.appUrl,
|
|
49452
|
+
execute: options.execute
|
|
49453
|
+
});
|
|
49454
|
+
await writeFile3(paths.launchAgentPath, plist, { mode: 420 });
|
|
49455
|
+
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
49456
|
+
try {
|
|
49457
|
+
await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49458
|
+
} catch (error51) {
|
|
49459
|
+
options.io.stderr.write(
|
|
49460
|
+
`Failed to load the runner service: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
49461
|
+
`
|
|
49462
|
+
);
|
|
49463
|
+
return 1;
|
|
49464
|
+
}
|
|
49465
|
+
options.io.stdout.write(
|
|
49466
|
+
`${[
|
|
49467
|
+
`Installed runner service ${label}.`,
|
|
49468
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49469
|
+
`state file: ${paths.stateFile}`,
|
|
49470
|
+
`logs: ${paths.logDir}`,
|
|
49471
|
+
`execute: ${options.execute ? "on (claims and runs work orders)" : "off (heartbeat only)"}`,
|
|
49472
|
+
`The runner pairs on first run; its bearer secret is stored locally and never printed.`
|
|
49473
|
+
].join("\n")}
|
|
49474
|
+
`
|
|
49475
|
+
);
|
|
49476
|
+
return 0;
|
|
49477
|
+
}
|
|
49478
|
+
async function startService(options) {
|
|
49479
|
+
if (options.platform !== "darwin") {
|
|
49480
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49481
|
+
}
|
|
49482
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49483
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49484
|
+
const label = serviceLabel(safeName);
|
|
49485
|
+
if (!await fileExists(paths.launchAgentPath)) {
|
|
49486
|
+
options.io.stderr.write(
|
|
49487
|
+
`No runner service found for "${safeName}". Run \`${cliBrand.binName} runner install-service --name ${safeName}\` first.
|
|
49488
|
+
`
|
|
49489
|
+
);
|
|
49490
|
+
return 1;
|
|
49491
|
+
}
|
|
49492
|
+
try {
|
|
49493
|
+
await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49494
|
+
options.io.stdout.write(`Started runner service ${label}.
|
|
49495
|
+
`);
|
|
49496
|
+
return 0;
|
|
49497
|
+
} catch (bootstrapError) {
|
|
49498
|
+
try {
|
|
49499
|
+
await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
|
|
49500
|
+
options.io.stdout.write(`Runner service ${label} was already loaded; restarted it.
|
|
49501
|
+
`);
|
|
49502
|
+
return 0;
|
|
49503
|
+
} catch (kickstartError) {
|
|
49504
|
+
options.io.stderr.write(
|
|
49505
|
+
`Failed to start runner service ${label}: ${redactForLog(kickstartError instanceof Error ? kickstartError.message : String(kickstartError))}
|
|
49506
|
+
`
|
|
49507
|
+
);
|
|
49508
|
+
return 1;
|
|
49509
|
+
}
|
|
49510
|
+
}
|
|
49511
|
+
}
|
|
49512
|
+
async function stopService(options) {
|
|
49513
|
+
if (options.platform !== "darwin") {
|
|
49514
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49515
|
+
}
|
|
49516
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49517
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49518
|
+
const label = serviceLabel(safeName);
|
|
49519
|
+
try {
|
|
49520
|
+
await options.exec("launchctl", ["bootout", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49521
|
+
options.io.stdout.write(`Stopped runner service ${label}.
|
|
49522
|
+
`);
|
|
49523
|
+
} catch {
|
|
49524
|
+
options.io.stdout.write(`Runner service ${label} was not running.
|
|
49525
|
+
`);
|
|
49526
|
+
}
|
|
49527
|
+
return 0;
|
|
49528
|
+
}
|
|
49529
|
+
async function restartService(options) {
|
|
49530
|
+
if (options.platform !== "darwin") {
|
|
49531
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49532
|
+
}
|
|
49533
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49534
|
+
const label = serviceLabel(safeName);
|
|
49535
|
+
try {
|
|
49536
|
+
await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
|
|
49537
|
+
options.io.stdout.write(`Restarted runner service ${label}.
|
|
49538
|
+
`);
|
|
49539
|
+
return 0;
|
|
49540
|
+
} catch (error51) {
|
|
49541
|
+
options.io.stderr.write(
|
|
49542
|
+
`Failed to restart runner service ${label}: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}. Is it installed? Run \`${cliBrand.binName} runner install-service --name ${safeName}\`.
|
|
49543
|
+
`
|
|
49544
|
+
);
|
|
49545
|
+
return 1;
|
|
49546
|
+
}
|
|
49547
|
+
}
|
|
49548
|
+
async function statusService(options) {
|
|
49549
|
+
if (options.platform !== "darwin") {
|
|
49550
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49551
|
+
}
|
|
49552
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49553
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49554
|
+
const label = serviceLabel(safeName);
|
|
49555
|
+
const installed = await fileExists(paths.launchAgentPath);
|
|
49556
|
+
try {
|
|
49557
|
+
const { stdout } = await options.exec("launchctl", ["print", serviceTarget(options.uid, label)]);
|
|
49558
|
+
const state = extractLaunchdState(stdout);
|
|
49559
|
+
options.io.stdout.write(
|
|
49560
|
+
`${[
|
|
49561
|
+
`runner service: ${label}`,
|
|
49562
|
+
`installed: ${installed ? "yes" : "no"}`,
|
|
49563
|
+
`loaded: yes`,
|
|
49564
|
+
`state: ${state ?? "unknown"}`,
|
|
49565
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49566
|
+
`logs: ${paths.logDir}`
|
|
49567
|
+
].join("\n")}
|
|
49568
|
+
`
|
|
49569
|
+
);
|
|
49570
|
+
return 0;
|
|
49571
|
+
} catch {
|
|
49572
|
+
options.io.stdout.write(
|
|
49573
|
+
`${[
|
|
49574
|
+
`runner service: ${label}`,
|
|
49575
|
+
`installed: ${installed ? "yes" : "no"}`,
|
|
49576
|
+
`loaded: no`,
|
|
49577
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49578
|
+
`logs: ${paths.logDir}`
|
|
49579
|
+
].join("\n")}
|
|
49580
|
+
`
|
|
49581
|
+
);
|
|
49582
|
+
return 1;
|
|
49583
|
+
}
|
|
49584
|
+
}
|
|
49585
|
+
async function logsService(options) {
|
|
49586
|
+
if (options.platform !== "darwin") {
|
|
49587
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49588
|
+
}
|
|
49589
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49590
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49591
|
+
const label = serviceLabel(safeName);
|
|
49592
|
+
const stdoutLog = path4.join(paths.logDir, "stdout.log");
|
|
49593
|
+
const stderrLog = path4.join(paths.logDir, "stderr.log");
|
|
49594
|
+
options.io.stdout.write(
|
|
49595
|
+
`${[
|
|
49596
|
+
`runner service logs for ${label}:`,
|
|
49597
|
+
` stdout: ${stdoutLog}`,
|
|
49598
|
+
` stderr: ${stderrLog}`,
|
|
49599
|
+
`Follow live with: tail -f ${JSON.stringify(stdoutLog)} ${JSON.stringify(stderrLog)}`
|
|
49600
|
+
].join("\n")}
|
|
49601
|
+
`
|
|
49602
|
+
);
|
|
49603
|
+
for (const [name, file2] of [["stdout", stdoutLog], ["stderr", stderrLog]]) {
|
|
49604
|
+
const tail = await readTail(file2, 4e3);
|
|
49605
|
+
if (tail !== null && tail.trim().length > 0) {
|
|
49606
|
+
options.io.stdout.write(`
|
|
49607
|
+
----- ${name} (tail) -----
|
|
49608
|
+
${redactForLog(tail)}
|
|
49609
|
+
`);
|
|
49610
|
+
}
|
|
49611
|
+
}
|
|
49612
|
+
return 0;
|
|
49613
|
+
}
|
|
49614
|
+
async function uninstallService(options) {
|
|
49615
|
+
if (options.platform !== "darwin") {
|
|
49616
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49617
|
+
}
|
|
49618
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49619
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49620
|
+
const label = serviceLabel(safeName);
|
|
49621
|
+
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
49622
|
+
let removed = false;
|
|
49623
|
+
try {
|
|
49624
|
+
await rm3(paths.launchAgentPath, { force: true });
|
|
49625
|
+
removed = true;
|
|
49626
|
+
} catch (error51) {
|
|
49627
|
+
options.io.stderr.write(
|
|
49628
|
+
`Failed to remove ${paths.launchAgentPath}: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
49629
|
+
`
|
|
49630
|
+
);
|
|
49631
|
+
}
|
|
49632
|
+
options.io.stdout.write(
|
|
49633
|
+
`${[
|
|
49634
|
+
`Uninstalled runner service ${label}.`,
|
|
49635
|
+
removed ? `Removed ${paths.launchAgentPath}.` : `Could not remove ${paths.launchAgentPath}; remove it manually.`,
|
|
49636
|
+
`Left the runner state file at ${paths.stateFile} (it holds the local bearer secret).`,
|
|
49637
|
+
`Delete that file to forget this runner locally, and run \`${cliBrand.binName} runner revoke <runner-id>\` to revoke it in ${cliBrand.name}.`
|
|
49638
|
+
].join("\n")}
|
|
49639
|
+
`
|
|
49640
|
+
);
|
|
49641
|
+
return removed ? 0 : 1;
|
|
49642
|
+
}
|
|
49643
|
+
function parseServiceArgs(args) {
|
|
49644
|
+
let runnerName;
|
|
49645
|
+
let execute2 = false;
|
|
49646
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
49647
|
+
const token = args[index];
|
|
49648
|
+
if (token === "--name") {
|
|
49649
|
+
const value = args[index + 1];
|
|
49650
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
49651
|
+
throw new Error("--name requires a value.");
|
|
49652
|
+
}
|
|
49653
|
+
runnerName = value;
|
|
49654
|
+
index += 1;
|
|
49655
|
+
} else if (token === "--execute") {
|
|
49656
|
+
execute2 = true;
|
|
49657
|
+
} else {
|
|
49658
|
+
throw new Error(`Unexpected runner service argument: ${token ?? ""}`);
|
|
49659
|
+
}
|
|
49660
|
+
}
|
|
49661
|
+
if (runnerName === void 0 || runnerName.trim().length === 0) {
|
|
49662
|
+
throw new Error("--name <runner-name> is required.");
|
|
49663
|
+
}
|
|
49664
|
+
return { runnerName, execute: execute2 };
|
|
49665
|
+
}
|
|
49666
|
+
function serviceUsage() {
|
|
49667
|
+
const bin = cliBrand.binName;
|
|
49668
|
+
return [
|
|
49669
|
+
`Usage:`,
|
|
49670
|
+
` ${bin} runner install-service --name <name> [--execute]`,
|
|
49671
|
+
` ${bin} runner start|stop|restart|status|logs|uninstall --name <name>`
|
|
49672
|
+
].join("\n");
|
|
49673
|
+
}
|
|
49674
|
+
function safeRunnerName(runnerName) {
|
|
49675
|
+
const safe = runnerName.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
49676
|
+
return safe.length > 0 ? safe : "runner";
|
|
49677
|
+
}
|
|
49678
|
+
function serviceLabel(safeName) {
|
|
49679
|
+
return `com.${cliBrand.binName}.runner.${safeName}`;
|
|
49680
|
+
}
|
|
49681
|
+
function domainTarget(uid) {
|
|
49682
|
+
return `gui/${uid}`;
|
|
49683
|
+
}
|
|
49684
|
+
function serviceTarget(uid, label) {
|
|
49685
|
+
return `gui/${uid}/${label}`;
|
|
49686
|
+
}
|
|
49687
|
+
async function bootoutQuietly(exec, uid, plistPath) {
|
|
49688
|
+
try {
|
|
49689
|
+
await exec("launchctl", ["bootout", domainTarget(uid), plistPath]);
|
|
49690
|
+
} catch {
|
|
49691
|
+
}
|
|
49692
|
+
}
|
|
49693
|
+
function unsupportedPlatform(io, platform) {
|
|
49694
|
+
io.stderr.write(
|
|
49695
|
+
`Service install is not supported on ${platform} yet. Run \`${cliBrand.binName} runner serve --execute\` under a process manager (for example systemd, pm2, or nssm) to keep a runner online.
|
|
49696
|
+
`
|
|
49697
|
+
);
|
|
49698
|
+
return 1;
|
|
49699
|
+
}
|
|
49700
|
+
function resolveBinPath() {
|
|
49701
|
+
const entry = process.argv[1];
|
|
49702
|
+
if (entry === void 0 || entry.length === 0) {
|
|
49703
|
+
return cliBrand.binName;
|
|
49704
|
+
}
|
|
49705
|
+
try {
|
|
49706
|
+
return realpathSync(entry);
|
|
49707
|
+
} catch {
|
|
49708
|
+
return entry;
|
|
49709
|
+
}
|
|
49710
|
+
}
|
|
49711
|
+
function extractLaunchdState(printOutput) {
|
|
49712
|
+
const match = printOutput.match(/state\s*=\s*(\S+)/);
|
|
49713
|
+
return match ? match[1] ?? null : null;
|
|
49714
|
+
}
|
|
49715
|
+
async function fileExists(file2) {
|
|
49716
|
+
try {
|
|
49717
|
+
await access2(file2);
|
|
49718
|
+
return true;
|
|
49719
|
+
} catch {
|
|
49720
|
+
return false;
|
|
49721
|
+
}
|
|
49722
|
+
}
|
|
49723
|
+
async function readTail(file2, maxBytes) {
|
|
49724
|
+
try {
|
|
49725
|
+
const content = await readFile5(file2, "utf8");
|
|
49726
|
+
return content.length > maxBytes ? content.slice(content.length - maxBytes) : content;
|
|
49727
|
+
} catch {
|
|
49728
|
+
return null;
|
|
49729
|
+
}
|
|
49730
|
+
}
|
|
49731
|
+
function escapePlist(value) {
|
|
49732
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
49733
|
+
}
|
|
49734
|
+
var defaultExec = async (file2, args) => {
|
|
49735
|
+
const result = await execFile4(file2, args);
|
|
49736
|
+
return { stdout: String(result.stdout), stderr: String(result.stderr) };
|
|
49737
|
+
};
|
|
49738
|
+
function assertNever2(value) {
|
|
49739
|
+
throw new Error(`Unhandled runner service verb: ${String(value)}`);
|
|
48944
49740
|
}
|
|
48945
49741
|
|
|
48946
49742
|
// src/index.ts
|
|
@@ -49090,6 +49886,9 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
49090
49886
|
env: process.env
|
|
49091
49887
|
});
|
|
49092
49888
|
}
|
|
49889
|
+
if (command === "runner" && isRunnerServiceInvocation(args)) {
|
|
49890
|
+
return runRunnerService({ io, args, appUrl: config2.appUrl });
|
|
49891
|
+
}
|
|
49093
49892
|
if (!session) {
|
|
49094
49893
|
io.stderr.write(`Not logged in. Run ${cliBrand.binName} login first.
|
|
49095
49894
|
`);
|
|
@@ -49567,7 +50366,7 @@ function isCliEntrypoint(moduleUrl, argvPath) {
|
|
|
49567
50366
|
return false;
|
|
49568
50367
|
}
|
|
49569
50368
|
try {
|
|
49570
|
-
return
|
|
50369
|
+
return realpathSync2(fileURLToPath3(moduleUrl)) === realpathSync2(argvPath);
|
|
49571
50370
|
} catch {
|
|
49572
50371
|
return moduleUrl === pathToFileURL(argvPath).href;
|
|
49573
50372
|
}
|