@rosthq/cli 0.6.8 → 0.7.1
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 +939 -130
- 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,82 @@ 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.5",
|
|
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
|
+
During onboarding, the deterministic harness may execute only explicit low-risk draft actions. In the Responsibility Graph step, a user with setup/action authority can ask AICOS to create one named draft seat per turn; the seat stays vacant, is traceable to the AICOS message through artifact sources, and still needs human review before staffing, Charters, manifests, or go-live. Compass and Charter help remains draft guidance and clarifying questions until the live drafting planner lands. AICOS never silently finishes onboarding, approves Compass or Charter content, signs manifests, goes live, stores credentials, changes autonomy policy, or invites external people; those actions remain human-confirmed.
|
|
41929
|
+
|
|
41930
|
+
AICOS posture is resolved on the server from the current user's AICOS access scope plus the tenant's Company guardrails. **Report-only** can read, explain, and draft guidance, but it cannot mutate setup or operating state. **Balanced** can perform narrow low-risk internal draft/setup actions and stops at human gates. **High autonomy** can perform allowed internal low-risk actions without changing the hard prohibitions: AICOS still cannot approve its own Charter or manifest, handle credentials, send externally, spend money, perform destructive/admin changes, go live, expand policy/access, or receive hidden sensitive data.
|
|
41931
|
+
|
|
41932
|
+
AICOS grounds answers from two source classes before producing user-visible setup or operating guidance:
|
|
41933
|
+
|
|
41934
|
+
- **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.
|
|
41935
|
+
- **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.
|
|
41936
|
+
|
|
41937
|
+
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.
|
|
41938
|
+
|
|
41939
|
+
## Governance boundary
|
|
41940
|
+
|
|
41941
|
+
AICOS can preserve context and explain what it is ready to do, but durable operating changes still use the same {{brand}} controls:
|
|
41942
|
+
|
|
41943
|
+
- Tenant isolation is enforced by RLS and server-side tenant context.
|
|
41944
|
+
- 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.
|
|
41945
|
+
- Durable decisions remain human decisions.
|
|
41946
|
+
- Action execution must route through command guards and confirmation when required.
|
|
41947
|
+
- Expanding authority, approving Charters or Compass versions, going live, credentials, external sends, money, destructive operations, and policy changes stay human-gated.
|
|
41948
|
+
|
|
41949
|
+
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
41950
|
},
|
|
41642
41951
|
{
|
|
41643
41952
|
slug: "responsibility-graph-playbook",
|
|
@@ -42412,7 +42721,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
42412
42721
|
order: 48,
|
|
42413
42722
|
title: "CLI and MCP installation guide",
|
|
42414
42723
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
42415
|
-
version: "2026-07-02.
|
|
42724
|
+
version: "2026-07-02.6",
|
|
42416
42725
|
public: true,
|
|
42417
42726
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
42418
42727
|
stages: ["company_setup", "staffing"],
|
|
@@ -42869,6 +43178,7 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
|
|
|
42869
43178
|
| \`{{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
43179
|
| \`{{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
43180
|
| \`{{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\` |
|
|
43181
|
+
| \`{{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
43182
|
| \`{{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
43183
|
| \`{{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
43184
|
| \`{{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 +43771,7 @@ Set \`ROST_SKILL_INSTALL_ROOT\` only for a sandbox or CI override. Local copies
|
|
|
43461
43771
|
order: 55,
|
|
43462
43772
|
title: "Agent reference map",
|
|
43463
43773
|
summary: "Where CLI sessions, MCP clients, and in-app agents should retrieve {{brand}} guidance before recommending setup changes.",
|
|
43464
|
-
version: "2026-07-02.
|
|
43774
|
+
version: "2026-07-02.3",
|
|
43465
43775
|
public: true,
|
|
43466
43776
|
audiences: ["cli", "mcp", "in_app_agent"],
|
|
43467
43777
|
stages: ["company_setup", "graph_design", "charter_design", "staffing", "operating_rhythm"],
|
|
@@ -43525,28 +43835,29 @@ Agents that run on {{brand}}-managed inference draw against a tenant inference b
|
|
|
43525
43835
|
|
|
43526
43836
|
1. rost-implementation-method
|
|
43527
43837
|
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.
|
|
43838
|
+
3. aicos-chat-guide
|
|
43839
|
+
4. responsibility-graph-playbook
|
|
43840
|
+
5. charter-design-playbook
|
|
43841
|
+
6. charter-authoring-deep-dive
|
|
43842
|
+
7. agent-staffing-playbook
|
|
43843
|
+
8. add-agents-guide
|
|
43844
|
+
9. custom-agents-guide
|
|
43845
|
+
10. agent-builder-guide
|
|
43846
|
+
11. how-agents-work
|
|
43847
|
+
12. tool-access-and-vault
|
|
43848
|
+
13. available-tools-guide
|
|
43849
|
+
14. mcp-and-cli-guide
|
|
43850
|
+
15. skill-builder-guide
|
|
43851
|
+
16. agent-skill-authoring-guide
|
|
43852
|
+
17. agent-skill-setup-guide
|
|
43853
|
+
18. ai-model-data-handling-guide
|
|
43854
|
+
19. cascade-guide
|
|
43855
|
+
20. signal-guide
|
|
43856
|
+
21. friction-guide
|
|
43857
|
+
22. confirmations-guide
|
|
43858
|
+
23. steward-queue-guide
|
|
43859
|
+
24. sync-rhythm-playbook
|
|
43860
|
+
25. billing-and-pricing-guide
|
|
43550
43861
|
|
|
43551
43862
|
## Workflow to guide map
|
|
43552
43863
|
|
|
@@ -43554,6 +43865,7 @@ Read the listed guide before recommending or running each workflow. Every workfl
|
|
|
43554
43865
|
|
|
43555
43866
|
- Connect a local agent (CLI login, MCP token, client registration): mcp-and-cli-guide, then agent-reference-map.
|
|
43556
43867
|
- Company onboarding (context, Compass draft, finish): rost-implementation-method, then settings-members-and-invites-guide for invites.
|
|
43868
|
+
- 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
43869
|
- 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
43870
|
- Design the graph (create, rename, reparent, set type): responsibility-graph-playbook.
|
|
43559
43871
|
- 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 +43889,11 @@ Read the listed guide before recommending or running each workflow. Every workfl
|
|
|
43577
43889
|
|
|
43578
43890
|
## Stage map
|
|
43579
43891
|
|
|
43580
|
-
- Company setup: rost-implementation-method, compass-authoring-guide, settings-guide, settings-members-and-invites-guide
|
|
43892
|
+
- Company setup: rost-implementation-method, compass-authoring-guide, aicos-chat-guide, settings-guide, settings-members-and-invites-guide
|
|
43581
43893
|
- Graph design: responsibility-graph-playbook
|
|
43582
43894
|
- Charter design: charter-design-playbook, charter-authoring-deep-dive
|
|
43583
43895
|
- 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
|
|
43896
|
+
- Operating rhythm: aicos-chat-guide, cascade-guide, signal-guide, friction-guide, confirmations-guide, steward-queue-guide, sync-rhythm-playbook, notifications-guide
|
|
43585
43897
|
- Local agents and runners: mcp-and-cli-guide, runner-guide, agent-reference-map
|
|
43586
43898
|
- Security and troubleshooting: ai-model-data-handling-guide, security-model-guide, troubleshooting-guide
|
|
43587
43899
|
|
|
@@ -44292,7 +44604,7 @@ Every notification should include the seat, cause, evidence, and requested decis
|
|
|
44292
44604
|
order: 75,
|
|
44293
44605
|
title: "Local runner guide",
|
|
44294
44606
|
summary: "How local agent sessions and runner surfaces should operate through {{brand}} without bypassing Charters or audit.",
|
|
44295
|
-
version: "2026-
|
|
44607
|
+
version: "2026-07-02.2",
|
|
44296
44608
|
public: true,
|
|
44297
44609
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
44298
44610
|
stages: ["staffing", "operating_rhythm"],
|
|
@@ -44318,6 +44630,26 @@ The local runner is for human-controlled local agent work. It should retrieve {{
|
|
|
44318
44630
|
5. Record work, Friction, handoffs, and escalations.
|
|
44319
44631
|
6. Revoke access when the session ends.
|
|
44320
44632
|
|
|
44633
|
+
## Install a local Runner
|
|
44634
|
+
|
|
44635
|
+
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.
|
|
44636
|
+
|
|
44637
|
+
Recommended paths:
|
|
44638
|
+
|
|
44639
|
+
- **Always-on host:** \`{{cli}} runner install-service --name <runner-name> --execute\` installs a background service (macOS launchd user agent, \`user/<uid>\` domain) that keeps the runner online. Manage it with \`{{cli}} runner start|stop|restart|status|logs|uninstall --name <runner-name>\`. On macOS this loads reliably when the runner user is logged in at the console; on a purely headless SSH box where that user is not at the console, run \`{{cli}} runner serve\` under a process supervisor instead.
|
|
44640
|
+
- **Manual smoke:** \`{{cli}} runner serve --once\` heartbeats once and exits; \`{{cli}} runner serve --execute\` claims and executes runner work orders while it runs.
|
|
44641
|
+
|
|
44642
|
+
Two ways to authenticate and pair \u2014 both first-class, an agent can drive either entirely from the terminal:
|
|
44643
|
+
|
|
44644
|
+
- **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.
|
|
44645
|
+
- **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.
|
|
44646
|
+
|
|
44647
|
+
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.
|
|
44648
|
+
|
|
44649
|
+
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\`.
|
|
44650
|
+
|
|
44651
|
+
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.
|
|
44652
|
+
|
|
44321
44653
|
## Inspect and control runners from CLI or MCP
|
|
44322
44654
|
|
|
44323
44655
|
- 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 +48287,9 @@ function operationUsageLines(bin) {
|
|
|
47955
48287
|
`${bin} error list|resolve|supersede`,
|
|
47956
48288
|
`${bin} sync brief|compile|complete`,
|
|
47957
48289
|
`${bin} runner list|status|diagnose|repair|work-orders|revoke|serve`,
|
|
48290
|
+
// Local runner service lifecycle (DER-1142). The orchestrator mirrors this
|
|
48291
|
+
// exact line into the mcp-and-cli-guide so the help/guide parity gate passes.
|
|
48292
|
+
`${bin} runner install-service|start|stop|restart|status|logs|uninstall --name <name>`,
|
|
47958
48293
|
`${bin} notification settings|test|errors`,
|
|
47959
48294
|
`${bin} settings get|update|product-learning|agent-policy|rename`,
|
|
47960
48295
|
`${bin} member invite|update|remove`,
|
|
@@ -48529,8 +48864,8 @@ function pendingConfirmationFromError(error51) {
|
|
|
48529
48864
|
import { execFile as execFileCallback2 } from "node:child_process";
|
|
48530
48865
|
import { access, readFile as readFile3 } from "node:fs/promises";
|
|
48531
48866
|
import { delimiter } from "node:path";
|
|
48532
|
-
import { promisify as
|
|
48533
|
-
var
|
|
48867
|
+
import { promisify as promisify2 } from "node:util";
|
|
48868
|
+
var execFile2 = promisify2(execFileCallback2);
|
|
48534
48869
|
async function runDoctor(options) {
|
|
48535
48870
|
const env = options.env ?? process.env;
|
|
48536
48871
|
const store = options.store ?? createTokenStore();
|
|
@@ -48578,7 +48913,7 @@ async function checkPath(env) {
|
|
|
48578
48913
|
}
|
|
48579
48914
|
async function probeCli(command) {
|
|
48580
48915
|
try {
|
|
48581
|
-
const { stdout, stderr } = await
|
|
48916
|
+
const { stdout, stderr } = await execFile2(command, ["--version"], { timeout: 1500 });
|
|
48582
48917
|
const version4 = (stdout || stderr).trim().split("\n")[0]?.trim();
|
|
48583
48918
|
return version4 ? `installed (${version4})` : "installed";
|
|
48584
48919
|
} catch {
|
|
@@ -48653,8 +48988,8 @@ import { chmod, mkdir as mkdir2, readFile as readFile4, rm as rm2, writeFile as
|
|
|
48653
48988
|
import { hostname as hostname3, tmpdir } from "node:os";
|
|
48654
48989
|
import path3 from "node:path";
|
|
48655
48990
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
48656
|
-
import { promisify as
|
|
48657
|
-
var
|
|
48991
|
+
import { promisify as promisify3 } from "node:util";
|
|
48992
|
+
var execFile3 = promisify3(execFileCallback3);
|
|
48658
48993
|
async function runRunnerServe(options) {
|
|
48659
48994
|
const env = options.env ?? process.env;
|
|
48660
48995
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -48693,8 +49028,11 @@ ${usage()}
|
|
|
48693
49028
|
options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
|
|
48694
49029
|
`);
|
|
48695
49030
|
}
|
|
48696
|
-
if (config2.execute
|
|
48697
|
-
|
|
49031
|
+
if (config2.execute) {
|
|
49032
|
+
const localRuntime = effectiveExecutionRuntime(config2.runtime, capabilities);
|
|
49033
|
+
if (localRuntime) {
|
|
49034
|
+
await claimAndExecute(fetchImpl, options.appUrl, state, options.io, localRuntime);
|
|
49035
|
+
}
|
|
48698
49036
|
}
|
|
48699
49037
|
} catch (error51) {
|
|
48700
49038
|
failed = true;
|
|
@@ -48714,6 +49052,7 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48714
49052
|
let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
|
|
48715
49053
|
let execute2 = env.RUNNER_EXECUTE === "1";
|
|
48716
49054
|
let once = false;
|
|
49055
|
+
let runtime = parseRuntime(env.RUNNER_RUNTIME ?? "auto");
|
|
48717
49056
|
let userCode = env.RUNNER_USER_CODE;
|
|
48718
49057
|
for (let index = 0; index < args.length; index += 1) {
|
|
48719
49058
|
const token = args[index];
|
|
@@ -48729,6 +49068,9 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48729
49068
|
} else if (token === "--user-code") {
|
|
48730
49069
|
userCode = requiredValue(args, index);
|
|
48731
49070
|
index += 1;
|
|
49071
|
+
} else if (token === "--runtime") {
|
|
49072
|
+
runtime = parseRuntime(requiredValue(args, index));
|
|
49073
|
+
index += 1;
|
|
48732
49074
|
} else if (token === "--execute") {
|
|
48733
49075
|
execute2 = true;
|
|
48734
49076
|
} else if (token === "--once") {
|
|
@@ -48740,7 +49082,19 @@ function parseServeArgs(args, appUrl2, env) {
|
|
|
48740
49082
|
if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3) {
|
|
48741
49083
|
throw new Error("--heartbeat-ms must be an integer >= 1000.");
|
|
48742
49084
|
}
|
|
48743
|
-
return { name, stateFile, heartbeatMs, execute: execute2, once, ...userCode ? { userCode } : {} };
|
|
49085
|
+
return { name, stateFile, heartbeatMs, execute: execute2, once, runtime, ...userCode ? { userCode } : {} };
|
|
49086
|
+
}
|
|
49087
|
+
function parseRuntime(value) {
|
|
49088
|
+
if (value === "auto" || value === "claude" || value === "codex") {
|
|
49089
|
+
return value;
|
|
49090
|
+
}
|
|
49091
|
+
throw new Error(`--runtime must be one of auto, claude, codex (got ${value}).`);
|
|
49092
|
+
}
|
|
49093
|
+
function effectiveExecutionRuntime(runtime, capabilities) {
|
|
49094
|
+
if (runtime === "codex") {
|
|
49095
|
+
return capabilities.codex.installed ? "codex" : null;
|
|
49096
|
+
}
|
|
49097
|
+
return capabilities.claude.installed ? "claude" : null;
|
|
48744
49098
|
}
|
|
48745
49099
|
function requiredValue(args, index) {
|
|
48746
49100
|
const value = args[index + 1];
|
|
@@ -48772,10 +49126,16 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
|
|
|
48772
49126
|
`);
|
|
48773
49127
|
return existing;
|
|
48774
49128
|
}
|
|
48775
|
-
|
|
48776
|
-
|
|
49129
|
+
let userCode;
|
|
49130
|
+
if (config2.userCode !== void 0) {
|
|
49131
|
+
userCode = config2.userCode;
|
|
49132
|
+
} else if (client) {
|
|
49133
|
+
userCode = await startPairing(client, config2);
|
|
49134
|
+
} else {
|
|
49135
|
+
throw new Error(
|
|
49136
|
+
`No paired runner state at ${config2.stateFile}. Provide --user-code <code> from Settings > Runners (Add a runner), or run \`${cliBrand.binName} login --device\` once to pair this runner.`
|
|
49137
|
+
);
|
|
48777
49138
|
}
|
|
48778
|
-
const userCode = config2.userCode ?? await startPairing(client, config2);
|
|
48779
49139
|
const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", { user_code: userCode });
|
|
48780
49140
|
const runnerSecret = claim.json.runner_secret;
|
|
48781
49141
|
const runnerId = claim.json.runner_id;
|
|
@@ -48830,7 +49190,7 @@ async function detectCapabilities() {
|
|
|
48830
49190
|
}
|
|
48831
49191
|
async function probe(command) {
|
|
48832
49192
|
try {
|
|
48833
|
-
const { stdout, stderr } = await
|
|
49193
|
+
const { stdout, stderr } = await execFile3(command, ["--version"], { timeout: 1500 });
|
|
48834
49194
|
return { installed: true, version: (stdout || stderr).trim() || null, auth_state: "ready" };
|
|
48835
49195
|
} catch {
|
|
48836
49196
|
return { installed: false, version: null, auth_state: "unknown" };
|
|
@@ -48855,7 +49215,7 @@ async function post2(fetchImpl, appUrl2, pathName, body, token) {
|
|
|
48855
49215
|
}
|
|
48856
49216
|
return { status: response.status, json: json2 };
|
|
48857
49217
|
}
|
|
48858
|
-
async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
49218
|
+
async function claimAndExecute(fetchImpl, appUrl2, state, io, runtime) {
|
|
48859
49219
|
const claimed = await post2(fetchImpl, appUrl2, "/api/runner/work-orders/claim", {}, state.runner_secret);
|
|
48860
49220
|
const workOrder = claimed.json.work_order;
|
|
48861
49221
|
if (claimed.status !== 200 || !workOrder || typeof workOrder !== "object") {
|
|
@@ -48869,12 +49229,12 @@ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
|
48869
49229
|
if (started.status !== 200) {
|
|
48870
49230
|
throw new Error(`work order start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
|
|
48871
49231
|
}
|
|
48872
|
-
const result = await
|
|
49232
|
+
const result = await runLocalTurn(appUrl2, workOrder, runtime);
|
|
48873
49233
|
const reported = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/result`, {
|
|
48874
49234
|
status: result.ok ? "succeeded" : "failed",
|
|
48875
49235
|
summary: result.summary,
|
|
48876
49236
|
log_ref: `runner-local:${workOrderId}`,
|
|
48877
|
-
model: "claude-cli"
|
|
49237
|
+
model: runtime === "codex" ? "codex-cli" : "claude-cli"
|
|
48878
49238
|
}, state.runner_secret);
|
|
48879
49239
|
if (reported.status !== 200) {
|
|
48880
49240
|
throw new Error(`work order result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
|
|
@@ -48882,7 +49242,28 @@ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
|
|
|
48882
49242
|
io.stdout.write(`work_order_id=${workOrderId} status=${result.ok ? "succeeded" : "failed"}
|
|
48883
49243
|
`);
|
|
48884
49244
|
}
|
|
48885
|
-
|
|
49245
|
+
function runLocalTurn(appUrl2, workOrder, runtime) {
|
|
49246
|
+
return runtime === "codex" ? runCodexTurn(appUrl2, workOrder) : runClaudeTurn(appUrl2, workOrder);
|
|
49247
|
+
}
|
|
49248
|
+
function runClaudeTurn(appUrl2, workOrder) {
|
|
49249
|
+
return spawnRunnerTurn(appUrl2, workOrder, "claude");
|
|
49250
|
+
}
|
|
49251
|
+
function runCodexTurn(appUrl2, workOrder) {
|
|
49252
|
+
return spawnRunnerTurn(appUrl2, workOrder, "codex");
|
|
49253
|
+
}
|
|
49254
|
+
function buildTurnCommand(runtime, prompt, configPath) {
|
|
49255
|
+
if (runtime === "codex") {
|
|
49256
|
+
return {
|
|
49257
|
+
command: "codex",
|
|
49258
|
+
args: ["exec", prompt, "--mcp-config", configPath, "--sandbox", "read-only", "--skip-git-repo-check"]
|
|
49259
|
+
};
|
|
49260
|
+
}
|
|
49261
|
+
return {
|
|
49262
|
+
command: "claude",
|
|
49263
|
+
args: ["-p", prompt, "--mcp-config", configPath, "--strict-mcp-config", "--output-format", "text"]
|
|
49264
|
+
};
|
|
49265
|
+
}
|
|
49266
|
+
async function spawnRunnerTurn(appUrl2, workOrder, runtime) {
|
|
48886
49267
|
const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
|
|
48887
49268
|
const token = typeof mcp.token === "string" ? mcp.token : "";
|
|
48888
49269
|
const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
|
|
@@ -48903,6 +49284,7 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48903
49284
|
await writeFile2(configPath, `${mcpConfig}
|
|
48904
49285
|
`, { mode: 384 });
|
|
48905
49286
|
await chmod(configPath, 384);
|
|
49287
|
+
const { command, args } = buildTurnCommand(runtime, prompt, configPath);
|
|
48906
49288
|
return new Promise((resolve2) => {
|
|
48907
49289
|
let settled = false;
|
|
48908
49290
|
const finish = (result) => {
|
|
@@ -48913,7 +49295,7 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48913
49295
|
void rm2(configPath, { force: true });
|
|
48914
49296
|
resolve2(result);
|
|
48915
49297
|
};
|
|
48916
|
-
const child = spawn(
|
|
49298
|
+
const child = spawn(command, args, {
|
|
48917
49299
|
cwd: tmpdir(),
|
|
48918
49300
|
stdio: ["ignore", "pipe", "pipe"]
|
|
48919
49301
|
});
|
|
@@ -48940,7 +49322,431 @@ async function runClaudeTurn(appUrl2, workOrder) {
|
|
|
48940
49322
|
});
|
|
48941
49323
|
}
|
|
48942
49324
|
function usage() {
|
|
48943
|
-
return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--once] [--execute]";
|
|
49325
|
+
return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--runtime auto|claude|codex] [--once] [--execute]";
|
|
49326
|
+
}
|
|
49327
|
+
|
|
49328
|
+
// src/runner-service.ts
|
|
49329
|
+
import { execFile as execFileCallback4 } from "node:child_process";
|
|
49330
|
+
import { realpathSync } from "node:fs";
|
|
49331
|
+
import { access as access2, mkdir as mkdir3, readFile as readFile5, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
49332
|
+
import { homedir as homedir2 } from "node:os";
|
|
49333
|
+
import path4 from "node:path";
|
|
49334
|
+
import { promisify as promisify4 } from "node:util";
|
|
49335
|
+
var execFile4 = promisify4(execFileCallback4);
|
|
49336
|
+
var SERVICE_VERBS = ["install-service", "start", "stop", "restart", "status", "logs", "uninstall"];
|
|
49337
|
+
function isServiceVerb(value) {
|
|
49338
|
+
return value !== void 0 && SERVICE_VERBS.includes(value);
|
|
49339
|
+
}
|
|
49340
|
+
function isRunnerServiceInvocation(args) {
|
|
49341
|
+
const verb = args[0];
|
|
49342
|
+
if (verb === "status") {
|
|
49343
|
+
return args.includes("--name");
|
|
49344
|
+
}
|
|
49345
|
+
return isServiceVerb(verb);
|
|
49346
|
+
}
|
|
49347
|
+
function runnerServicePaths(homeDir, runnerName) {
|
|
49348
|
+
const safeName = safeRunnerName(runnerName);
|
|
49349
|
+
return {
|
|
49350
|
+
launchAgentPath: path4.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
|
|
49351
|
+
stateFile: path4.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
|
|
49352
|
+
logDir: path4.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
|
|
49353
|
+
};
|
|
49354
|
+
}
|
|
49355
|
+
function renderLaunchdPlist(config2) {
|
|
49356
|
+
const programArgs = [
|
|
49357
|
+
config2.binPath,
|
|
49358
|
+
"runner",
|
|
49359
|
+
"serve",
|
|
49360
|
+
...config2.execute ? ["--execute"] : [],
|
|
49361
|
+
"--name",
|
|
49362
|
+
config2.runnerName,
|
|
49363
|
+
"--state-file",
|
|
49364
|
+
config2.stateFile
|
|
49365
|
+
];
|
|
49366
|
+
const programArgsXml = programArgs.map((arg) => ` <string>${escapePlist(arg)}</string>`).join("\n");
|
|
49367
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
49368
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
49369
|
+
<plist version="1.0">
|
|
49370
|
+
<dict>
|
|
49371
|
+
<key>Label</key>
|
|
49372
|
+
<string>${escapePlist(serviceLabel(config2.runnerName))}</string>
|
|
49373
|
+
<key>ProgramArguments</key>
|
|
49374
|
+
<array>
|
|
49375
|
+
${programArgsXml}
|
|
49376
|
+
</array>
|
|
49377
|
+
<key>EnvironmentVariables</key>
|
|
49378
|
+
<dict>
|
|
49379
|
+
<key>${cliBrand.envPrefix}_APP_URL</key>
|
|
49380
|
+
<string>${escapePlist(config2.appUrl)}</string>
|
|
49381
|
+
</dict>
|
|
49382
|
+
<key>KeepAlive</key>
|
|
49383
|
+
<true/>
|
|
49384
|
+
<key>RunAtLoad</key>
|
|
49385
|
+
<true/>
|
|
49386
|
+
<key>StandardOutPath</key>
|
|
49387
|
+
<string>${escapePlist(path4.join(config2.logDir, "stdout.log"))}</string>
|
|
49388
|
+
<key>StandardErrorPath</key>
|
|
49389
|
+
<string>${escapePlist(path4.join(config2.logDir, "stderr.log"))}</string>
|
|
49390
|
+
</dict>
|
|
49391
|
+
</plist>
|
|
49392
|
+
`;
|
|
49393
|
+
}
|
|
49394
|
+
async function runRunnerService(options) {
|
|
49395
|
+
const verb = options.args[0];
|
|
49396
|
+
if (!isServiceVerb(verb)) {
|
|
49397
|
+
options.io.stderr.write(`Unknown runner service command: ${verb ?? ""}
|
|
49398
|
+
${serviceUsage()}
|
|
49399
|
+
`);
|
|
49400
|
+
return 1;
|
|
49401
|
+
}
|
|
49402
|
+
if (options.args.includes("--help") || options.args.includes("-h")) {
|
|
49403
|
+
options.io.stdout.write(`${serviceUsage()}
|
|
49404
|
+
`);
|
|
49405
|
+
return 0;
|
|
49406
|
+
}
|
|
49407
|
+
let parsed;
|
|
49408
|
+
try {
|
|
49409
|
+
parsed = parseServiceArgs(options.args.slice(1));
|
|
49410
|
+
} catch (error51) {
|
|
49411
|
+
options.io.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
|
|
49412
|
+
${serviceUsage()}
|
|
49413
|
+
`);
|
|
49414
|
+
return 1;
|
|
49415
|
+
}
|
|
49416
|
+
const cmd = {
|
|
49417
|
+
io: options.io,
|
|
49418
|
+
runnerName: parsed.runnerName,
|
|
49419
|
+
appUrl: options.appUrl,
|
|
49420
|
+
execute: parsed.execute,
|
|
49421
|
+
binPath: options.binPath ?? resolveBinPath(),
|
|
49422
|
+
homeDir: options.homeDir ?? homedir2(),
|
|
49423
|
+
platform: options.platform ?? process.platform,
|
|
49424
|
+
exec: options.exec ?? defaultExec,
|
|
49425
|
+
uid: options.uid ?? (process.getuid ? process.getuid() : 0)
|
|
49426
|
+
};
|
|
49427
|
+
switch (verb) {
|
|
49428
|
+
case "install-service":
|
|
49429
|
+
return installService(cmd);
|
|
49430
|
+
case "start":
|
|
49431
|
+
return startService(cmd);
|
|
49432
|
+
case "stop":
|
|
49433
|
+
return stopService(cmd);
|
|
49434
|
+
case "restart":
|
|
49435
|
+
return restartService(cmd);
|
|
49436
|
+
case "status":
|
|
49437
|
+
return statusService(cmd);
|
|
49438
|
+
case "logs":
|
|
49439
|
+
return logsService(cmd);
|
|
49440
|
+
case "uninstall":
|
|
49441
|
+
return uninstallService(cmd);
|
|
49442
|
+
default:
|
|
49443
|
+
return assertNever2(verb);
|
|
49444
|
+
}
|
|
49445
|
+
}
|
|
49446
|
+
async function installService(options) {
|
|
49447
|
+
if (options.platform !== "darwin") {
|
|
49448
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49449
|
+
}
|
|
49450
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49451
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49452
|
+
const label = serviceLabel(safeName);
|
|
49453
|
+
await mkdir3(paths.logDir, { recursive: true });
|
|
49454
|
+
await mkdir3(path4.dirname(paths.stateFile), { recursive: true });
|
|
49455
|
+
await mkdir3(path4.dirname(paths.launchAgentPath), { recursive: true });
|
|
49456
|
+
const plist = renderLaunchdPlist({
|
|
49457
|
+
binPath: options.binPath,
|
|
49458
|
+
runnerName: safeName,
|
|
49459
|
+
stateFile: paths.stateFile,
|
|
49460
|
+
logDir: paths.logDir,
|
|
49461
|
+
appUrl: options.appUrl,
|
|
49462
|
+
execute: options.execute
|
|
49463
|
+
});
|
|
49464
|
+
await writeFile3(paths.launchAgentPath, plist, { mode: 420 });
|
|
49465
|
+
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
49466
|
+
try {
|
|
49467
|
+
await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49468
|
+
} catch (error51) {
|
|
49469
|
+
options.io.stderr.write(
|
|
49470
|
+
`Failed to load the runner service: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
49471
|
+
`
|
|
49472
|
+
);
|
|
49473
|
+
return 1;
|
|
49474
|
+
}
|
|
49475
|
+
options.io.stdout.write(
|
|
49476
|
+
`${[
|
|
49477
|
+
`Installed runner service ${label}.`,
|
|
49478
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49479
|
+
`state file: ${paths.stateFile}`,
|
|
49480
|
+
`logs: ${paths.logDir}`,
|
|
49481
|
+
`execute: ${options.execute ? "on (claims and runs work orders)" : "off (heartbeat only)"}`,
|
|
49482
|
+
`The runner pairs on first run; its bearer secret is stored locally and never printed.`
|
|
49483
|
+
].join("\n")}
|
|
49484
|
+
`
|
|
49485
|
+
);
|
|
49486
|
+
return 0;
|
|
49487
|
+
}
|
|
49488
|
+
async function startService(options) {
|
|
49489
|
+
if (options.platform !== "darwin") {
|
|
49490
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49491
|
+
}
|
|
49492
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49493
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49494
|
+
const label = serviceLabel(safeName);
|
|
49495
|
+
if (!await fileExists(paths.launchAgentPath)) {
|
|
49496
|
+
options.io.stderr.write(
|
|
49497
|
+
`No runner service found for "${safeName}". Run \`${cliBrand.binName} runner install-service --name ${safeName}\` first.
|
|
49498
|
+
`
|
|
49499
|
+
);
|
|
49500
|
+
return 1;
|
|
49501
|
+
}
|
|
49502
|
+
try {
|
|
49503
|
+
await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49504
|
+
options.io.stdout.write(`Started runner service ${label}.
|
|
49505
|
+
`);
|
|
49506
|
+
return 0;
|
|
49507
|
+
} catch (bootstrapError) {
|
|
49508
|
+
try {
|
|
49509
|
+
await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
|
|
49510
|
+
options.io.stdout.write(`Runner service ${label} was already loaded; restarted it.
|
|
49511
|
+
`);
|
|
49512
|
+
return 0;
|
|
49513
|
+
} catch (kickstartError) {
|
|
49514
|
+
options.io.stderr.write(
|
|
49515
|
+
`Failed to start runner service ${label}: ${redactForLog(kickstartError instanceof Error ? kickstartError.message : String(kickstartError))}
|
|
49516
|
+
`
|
|
49517
|
+
);
|
|
49518
|
+
return 1;
|
|
49519
|
+
}
|
|
49520
|
+
}
|
|
49521
|
+
}
|
|
49522
|
+
async function stopService(options) {
|
|
49523
|
+
if (options.platform !== "darwin") {
|
|
49524
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49525
|
+
}
|
|
49526
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49527
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49528
|
+
const label = serviceLabel(safeName);
|
|
49529
|
+
try {
|
|
49530
|
+
await options.exec("launchctl", ["bootout", domainTarget(options.uid), paths.launchAgentPath]);
|
|
49531
|
+
options.io.stdout.write(`Stopped runner service ${label}.
|
|
49532
|
+
`);
|
|
49533
|
+
} catch {
|
|
49534
|
+
options.io.stdout.write(`Runner service ${label} was not running.
|
|
49535
|
+
`);
|
|
49536
|
+
}
|
|
49537
|
+
return 0;
|
|
49538
|
+
}
|
|
49539
|
+
async function restartService(options) {
|
|
49540
|
+
if (options.platform !== "darwin") {
|
|
49541
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49542
|
+
}
|
|
49543
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49544
|
+
const label = serviceLabel(safeName);
|
|
49545
|
+
try {
|
|
49546
|
+
await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
|
|
49547
|
+
options.io.stdout.write(`Restarted runner service ${label}.
|
|
49548
|
+
`);
|
|
49549
|
+
return 0;
|
|
49550
|
+
} catch (error51) {
|
|
49551
|
+
options.io.stderr.write(
|
|
49552
|
+
`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}\`.
|
|
49553
|
+
`
|
|
49554
|
+
);
|
|
49555
|
+
return 1;
|
|
49556
|
+
}
|
|
49557
|
+
}
|
|
49558
|
+
async function statusService(options) {
|
|
49559
|
+
if (options.platform !== "darwin") {
|
|
49560
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49561
|
+
}
|
|
49562
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49563
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49564
|
+
const label = serviceLabel(safeName);
|
|
49565
|
+
const installed = await fileExists(paths.launchAgentPath);
|
|
49566
|
+
try {
|
|
49567
|
+
const { stdout } = await options.exec("launchctl", ["print", serviceTarget(options.uid, label)]);
|
|
49568
|
+
const state = extractLaunchdState(stdout);
|
|
49569
|
+
options.io.stdout.write(
|
|
49570
|
+
`${[
|
|
49571
|
+
`runner service: ${label}`,
|
|
49572
|
+
`installed: ${installed ? "yes" : "no"}`,
|
|
49573
|
+
`loaded: yes`,
|
|
49574
|
+
`state: ${state ?? "unknown"}`,
|
|
49575
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49576
|
+
`logs: ${paths.logDir}`
|
|
49577
|
+
].join("\n")}
|
|
49578
|
+
`
|
|
49579
|
+
);
|
|
49580
|
+
return 0;
|
|
49581
|
+
} catch {
|
|
49582
|
+
options.io.stdout.write(
|
|
49583
|
+
`${[
|
|
49584
|
+
`runner service: ${label}`,
|
|
49585
|
+
`installed: ${installed ? "yes" : "no"}`,
|
|
49586
|
+
`loaded: no`,
|
|
49587
|
+
`plist: ${paths.launchAgentPath}`,
|
|
49588
|
+
`logs: ${paths.logDir}`
|
|
49589
|
+
].join("\n")}
|
|
49590
|
+
`
|
|
49591
|
+
);
|
|
49592
|
+
return 1;
|
|
49593
|
+
}
|
|
49594
|
+
}
|
|
49595
|
+
async function logsService(options) {
|
|
49596
|
+
if (options.platform !== "darwin") {
|
|
49597
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49598
|
+
}
|
|
49599
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49600
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49601
|
+
const label = serviceLabel(safeName);
|
|
49602
|
+
const stdoutLog = path4.join(paths.logDir, "stdout.log");
|
|
49603
|
+
const stderrLog = path4.join(paths.logDir, "stderr.log");
|
|
49604
|
+
options.io.stdout.write(
|
|
49605
|
+
`${[
|
|
49606
|
+
`runner service logs for ${label}:`,
|
|
49607
|
+
` stdout: ${stdoutLog}`,
|
|
49608
|
+
` stderr: ${stderrLog}`,
|
|
49609
|
+
`Follow live with: tail -f ${JSON.stringify(stdoutLog)} ${JSON.stringify(stderrLog)}`
|
|
49610
|
+
].join("\n")}
|
|
49611
|
+
`
|
|
49612
|
+
);
|
|
49613
|
+
for (const [name, file2] of [["stdout", stdoutLog], ["stderr", stderrLog]]) {
|
|
49614
|
+
const tail = await readTail(file2, 4e3);
|
|
49615
|
+
if (tail !== null && tail.trim().length > 0) {
|
|
49616
|
+
options.io.stdout.write(`
|
|
49617
|
+
----- ${name} (tail) -----
|
|
49618
|
+
${redactForLog(tail)}
|
|
49619
|
+
`);
|
|
49620
|
+
}
|
|
49621
|
+
}
|
|
49622
|
+
return 0;
|
|
49623
|
+
}
|
|
49624
|
+
async function uninstallService(options) {
|
|
49625
|
+
if (options.platform !== "darwin") {
|
|
49626
|
+
return unsupportedPlatform(options.io, options.platform);
|
|
49627
|
+
}
|
|
49628
|
+
const safeName = safeRunnerName(options.runnerName);
|
|
49629
|
+
const paths = runnerServicePaths(options.homeDir, safeName);
|
|
49630
|
+
const label = serviceLabel(safeName);
|
|
49631
|
+
await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
|
|
49632
|
+
let removed = false;
|
|
49633
|
+
try {
|
|
49634
|
+
await rm3(paths.launchAgentPath, { force: true });
|
|
49635
|
+
removed = true;
|
|
49636
|
+
} catch (error51) {
|
|
49637
|
+
options.io.stderr.write(
|
|
49638
|
+
`Failed to remove ${paths.launchAgentPath}: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
49639
|
+
`
|
|
49640
|
+
);
|
|
49641
|
+
}
|
|
49642
|
+
options.io.stdout.write(
|
|
49643
|
+
`${[
|
|
49644
|
+
`Uninstalled runner service ${label}.`,
|
|
49645
|
+
removed ? `Removed ${paths.launchAgentPath}.` : `Could not remove ${paths.launchAgentPath}; remove it manually.`,
|
|
49646
|
+
`Left the runner state file at ${paths.stateFile} (it holds the local bearer secret).`,
|
|
49647
|
+
`Delete that file to forget this runner locally, and run \`${cliBrand.binName} runner revoke <runner-id>\` to revoke it in ${cliBrand.name}.`
|
|
49648
|
+
].join("\n")}
|
|
49649
|
+
`
|
|
49650
|
+
);
|
|
49651
|
+
return removed ? 0 : 1;
|
|
49652
|
+
}
|
|
49653
|
+
function parseServiceArgs(args) {
|
|
49654
|
+
let runnerName;
|
|
49655
|
+
let execute2 = false;
|
|
49656
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
49657
|
+
const token = args[index];
|
|
49658
|
+
if (token === "--name") {
|
|
49659
|
+
const value = args[index + 1];
|
|
49660
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
49661
|
+
throw new Error("--name requires a value.");
|
|
49662
|
+
}
|
|
49663
|
+
runnerName = value;
|
|
49664
|
+
index += 1;
|
|
49665
|
+
} else if (token === "--execute") {
|
|
49666
|
+
execute2 = true;
|
|
49667
|
+
} else {
|
|
49668
|
+
throw new Error(`Unexpected runner service argument: ${token ?? ""}`);
|
|
49669
|
+
}
|
|
49670
|
+
}
|
|
49671
|
+
if (runnerName === void 0 || runnerName.trim().length === 0) {
|
|
49672
|
+
throw new Error("--name <runner-name> is required.");
|
|
49673
|
+
}
|
|
49674
|
+
return { runnerName, execute: execute2 };
|
|
49675
|
+
}
|
|
49676
|
+
function serviceUsage() {
|
|
49677
|
+
const bin = cliBrand.binName;
|
|
49678
|
+
return [
|
|
49679
|
+
`Usage:`,
|
|
49680
|
+
` ${bin} runner install-service --name <name> [--execute]`,
|
|
49681
|
+
` ${bin} runner start|stop|restart|status|logs|uninstall --name <name>`
|
|
49682
|
+
].join("\n");
|
|
49683
|
+
}
|
|
49684
|
+
function safeRunnerName(runnerName) {
|
|
49685
|
+
const safe = runnerName.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
49686
|
+
return safe.length > 0 ? safe : "runner";
|
|
49687
|
+
}
|
|
49688
|
+
function serviceLabel(safeName) {
|
|
49689
|
+
return `com.${cliBrand.binName}.runner.${safeName}`;
|
|
49690
|
+
}
|
|
49691
|
+
function domainTarget(uid) {
|
|
49692
|
+
return `user/${uid}`;
|
|
49693
|
+
}
|
|
49694
|
+
function serviceTarget(uid, label) {
|
|
49695
|
+
return `user/${uid}/${label}`;
|
|
49696
|
+
}
|
|
49697
|
+
async function bootoutQuietly(exec, uid, plistPath) {
|
|
49698
|
+
try {
|
|
49699
|
+
await exec("launchctl", ["bootout", domainTarget(uid), plistPath]);
|
|
49700
|
+
} catch {
|
|
49701
|
+
}
|
|
49702
|
+
}
|
|
49703
|
+
function unsupportedPlatform(io, platform) {
|
|
49704
|
+
io.stderr.write(
|
|
49705
|
+
`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.
|
|
49706
|
+
`
|
|
49707
|
+
);
|
|
49708
|
+
return 1;
|
|
49709
|
+
}
|
|
49710
|
+
function resolveBinPath() {
|
|
49711
|
+
const entry = process.argv[1];
|
|
49712
|
+
if (entry === void 0 || entry.length === 0) {
|
|
49713
|
+
return cliBrand.binName;
|
|
49714
|
+
}
|
|
49715
|
+
try {
|
|
49716
|
+
return realpathSync(entry);
|
|
49717
|
+
} catch {
|
|
49718
|
+
return entry;
|
|
49719
|
+
}
|
|
49720
|
+
}
|
|
49721
|
+
function extractLaunchdState(printOutput) {
|
|
49722
|
+
const match = printOutput.match(/state\s*=\s*(\S+)/);
|
|
49723
|
+
return match ? match[1] ?? null : null;
|
|
49724
|
+
}
|
|
49725
|
+
async function fileExists(file2) {
|
|
49726
|
+
try {
|
|
49727
|
+
await access2(file2);
|
|
49728
|
+
return true;
|
|
49729
|
+
} catch {
|
|
49730
|
+
return false;
|
|
49731
|
+
}
|
|
49732
|
+
}
|
|
49733
|
+
async function readTail(file2, maxBytes) {
|
|
49734
|
+
try {
|
|
49735
|
+
const content = await readFile5(file2, "utf8");
|
|
49736
|
+
return content.length > maxBytes ? content.slice(content.length - maxBytes) : content;
|
|
49737
|
+
} catch {
|
|
49738
|
+
return null;
|
|
49739
|
+
}
|
|
49740
|
+
}
|
|
49741
|
+
function escapePlist(value) {
|
|
49742
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
49743
|
+
}
|
|
49744
|
+
var defaultExec = async (file2, args) => {
|
|
49745
|
+
const result = await execFile4(file2, args);
|
|
49746
|
+
return { stdout: String(result.stdout), stderr: String(result.stderr) };
|
|
49747
|
+
};
|
|
49748
|
+
function assertNever2(value) {
|
|
49749
|
+
throw new Error(`Unhandled runner service verb: ${String(value)}`);
|
|
48944
49750
|
}
|
|
48945
49751
|
|
|
48946
49752
|
// src/index.ts
|
|
@@ -49090,6 +49896,9 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
49090
49896
|
env: process.env
|
|
49091
49897
|
});
|
|
49092
49898
|
}
|
|
49899
|
+
if (command === "runner" && isRunnerServiceInvocation(args)) {
|
|
49900
|
+
return runRunnerService({ io, args, appUrl: config2.appUrl });
|
|
49901
|
+
}
|
|
49093
49902
|
if (!session) {
|
|
49094
49903
|
io.stderr.write(`Not logged in. Run ${cliBrand.binName} login first.
|
|
49095
49904
|
`);
|
|
@@ -49567,7 +50376,7 @@ function isCliEntrypoint(moduleUrl, argvPath) {
|
|
|
49567
50376
|
return false;
|
|
49568
50377
|
}
|
|
49569
50378
|
try {
|
|
49570
|
-
return
|
|
50379
|
+
return realpathSync2(fileURLToPath3(moduleUrl)) === realpathSync2(argvPath);
|
|
49571
50380
|
} catch {
|
|
49572
50381
|
return moduleUrl === pathToFileURL(argvPath).href;
|
|
49573
50382
|
}
|