@rosthq/cli 0.5.27 → 0.5.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -472,13 +472,13 @@ function __disposeResources(env) {
472
472
  }
473
473
  return next();
474
474
  }
475
- function __rewriteRelativeImportExtension(path3, preserveJsx) {
476
- if (typeof path3 === "string" && /^\.\.?\//.test(path3)) {
477
- return path3.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
475
+ function __rewriteRelativeImportExtension(path4, preserveJsx) {
476
+ if (typeof path4 === "string" && /^\.\.?\//.test(path4)) {
477
+ return path4.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 path3;
481
+ return path4;
482
482
  }
483
483
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
484
484
  var init_tslib_es6 = __esm({
@@ -5471,7 +5471,7 @@ var require_helpers = __commonJS({
5471
5471
  exports.generateCallbackId = generateCallbackId;
5472
5472
  exports.parseParametersFromURL = parseParametersFromURL;
5473
5473
  exports.decodeJWT = decodeJWT;
5474
- exports.sleep = sleep2;
5474
+ exports.sleep = sleep3;
5475
5475
  exports.retryable = retryable;
5476
5476
  exports.generatePKCEVerifier = generatePKCEVerifier;
5477
5477
  exports.generatePKCEChallenge = generatePKCEChallenge;
@@ -5609,7 +5609,7 @@ var require_helpers = __commonJS({
5609
5609
  };
5610
5610
  return data;
5611
5611
  }
5612
- async function sleep2(time3) {
5612
+ async function sleep3(time3) {
5613
5613
  return await new Promise((accept) => {
5614
5614
  setTimeout(() => accept(null), time3);
5615
5615
  });
@@ -7718,10 +7718,10 @@ var require_webauthn = __commonJS({
7718
7718
  authenticatorAttachment: (_a3 = credentialWithAttachment.authenticatorAttachment) !== null && _a3 !== void 0 ? _a3 : void 0
7719
7719
  };
7720
7720
  }
7721
- function isValidDomain(hostname3) {
7721
+ function isValidDomain(hostname4) {
7722
7722
  return (
7723
7723
  // Consider localhost valid as well since it's okay wrt Secure Contexts
7724
- hostname3 === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname3)
7724
+ hostname4 === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname4)
7725
7725
  );
7726
7726
  }
7727
7727
  function browserSupportsWebAuthn() {
@@ -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, path3) {
14030
- if (!path3)
14029
+ function getElementAtPath(obj, path4) {
14030
+ if (!path4)
14031
14031
  return obj;
14032
- return path3.reduce((acc, key) => acc?.[key], obj);
14032
+ return path4.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(path3, issues) {
14441
+ function prefixIssues(path4, issues) {
14442
14442
  return issues.map((iss) => {
14443
14443
  var _a3;
14444
14444
  (_a3 = iss).path ?? (_a3.path = []);
14445
- iss.path.unshift(path3);
14445
+ iss.path.unshift(path4);
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, path3 = []) => {
14592
+ const processError = (error52, path4 = []) => {
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 }, [...path3, ...issue2.path]));
14595
+ issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
14596
14596
  } else if (issue2.code === "invalid_key") {
14597
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14597
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14598
14598
  } else if (issue2.code === "invalid_element") {
14599
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14599
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14600
14600
  } else {
14601
- const fullpath = [...path3, ...issue2.path];
14601
+ const fullpath = [...path4, ...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, path3 = []) => {
14628
+ const processError = (error52, path4 = []) => {
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 }, [...path3, ...issue2.path]));
14632
+ issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
14633
14633
  } else if (issue2.code === "invalid_key") {
14634
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14634
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14635
14635
  } else if (issue2.code === "invalid_element") {
14636
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14636
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14637
14637
  } else {
14638
- const fullpath = [...path3, ...issue2.path];
14638
+ const fullpath = [...path4, ...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 path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14671
- for (const seg of path3) {
14670
+ const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14671
+ for (const seg of path4) {
14672
14672
  if (typeof seg === "number")
14673
14673
  segs.push(`[${seg}]`);
14674
14674
  else if (typeof seg === "symbol")
@@ -14874,8 +14874,8 @@ function emoji() {
14874
14874
  }
14875
14875
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
14876
14876
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
14877
- var mac = (delimiter) => {
14878
- const escapedDelim = escapeRegex(delimiter ?? ":");
14877
+ var mac = (delimiter2) => {
14878
+ const escapedDelim = escapeRegex(delimiter2 ?? ":");
14879
14879
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
14880
14880
  };
14881
14881
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -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 path3 = ref.slice(1).split("/").filter(Boolean);
27364
- if (path3.length === 0) {
27363
+ const path4 = ref.slice(1).split("/").filter(Boolean);
27364
+ if (path4.length === 0) {
27365
27365
  return ctx.rootSchema;
27366
27366
  }
27367
27367
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
27368
- if (path3[0] === defsKey) {
27369
- const key = path3[1];
27368
+ if (path4[0] === defsKey) {
27369
+ const key = path4[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 path3 = `/api/commands/${encodeURIComponent(commandId)}`;
27831
+ const path4 = `/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}${path3}`, {
27839
+ const response = await this.fetchImpl(`${this.appUrl}${path4}`, {
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 ${path3}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
27852
+ `server returned HTTP ${response.status} (non-JSON) from ${path4}` + (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, path3, query) {
33060
- const url2 = new URL(path3, baseUrl);
33059
+ function buildUrl(baseUrl, path4, query) {
33060
+ const url2 = new URL(path4, 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: path3,
33090
+ path: path4,
33091
33091
  query,
33092
33092
  body,
33093
33093
  headers
33094
33094
  }) {
33095
- const url2 = buildUrl(options.baseUrl, path3, query);
33095
+ const url2 = buildUrl(options.baseUrl, path4, 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, path3, fileBody, fileOptions) {
33957
+ async uploadOrUpdate(method, path4, 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(path3);
33981
+ const cleanPath = _this._removeEmptyFolders(path4);
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(path3, fileBody, fileOptions) {
34059
- return this.uploadOrUpdate("POST", path3, fileBody, fileOptions);
34058
+ async upload(path4, fileBody, fileOptions) {
34059
+ return this.uploadOrUpdate("POST", path4, 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(path3, token, fileBody, fileOptions) {
34099
+ async uploadToSignedUrl(path4, token, fileBody, fileOptions) {
34100
34100
  var _this3 = this;
34101
- const cleanPath = _this3._removeEmptyFolders(path3);
34101
+ const cleanPath = _this3._removeEmptyFolders(path4);
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(path3, options) {
34170
+ async createSignedUploadUrl(path4, options) {
34171
34171
  var _this4 = this;
34172
34172
  return _this4.handleOperation(async () => {
34173
- let _path = _this4._getFinalPath(path3);
34173
+ let _path = _this4._getFinalPath(path4);
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: path3,
34182
+ path: path4,
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(path3, fileBody, fileOptions) {
34243
- return this.uploadOrUpdate("PUT", path3, fileBody, fileOptions);
34242
+ async update(path4, fileBody, fileOptions) {
34243
+ return this.uploadOrUpdate("PUT", path4, 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(path3, expiresIn, options) {
34394
+ async createSignedUrl(path4, expiresIn, options) {
34395
34395
  var _this8 = this;
34396
34396
  return _this8.handleOperation(async () => {
34397
- let _path = _this8._getFinalPath(path3);
34397
+ let _path = _this8._getFinalPath(path4);
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(path3, options, parameters) {
34533
+ download(path4, 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(path3);
34539
+ const _path = this._getFinalPath(path4);
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(path3) {
34570
+ async info(path4) {
34571
34571
  var _this10 = this;
34572
- const _path = _this10._getFinalPath(path3);
34572
+ const _path = _this10._getFinalPath(path4);
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(path3) {
34593
+ async exists(path4) {
34594
34594
  var _this11 = this;
34595
- const _path = _this11._getFinalPath(path3);
34595
+ const _path = _this11._getFinalPath(path4);
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(path3, options) {
34675
- const _path = this._getFinalPath(path3);
34674
+ getPublicUrl(path4, options) {
34675
+ const _path = this._getFinalPath(path4);
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(path3, options, parameters) {
34814
+ async list(path4, options, parameters) {
34815
34815
  var _this13 = this;
34816
34816
  return _this13.handleOperation(async () => {
34817
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path3 || "" });
34817
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path4 || "" });
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(path3) {
34883
- return `${this.bucketId}/${path3.replace(/^\/+/, "")}`;
34882
+ _getFinalPath(path4) {
34883
+ return `${this.bucketId}/${path4.replace(/^\/+/, "")}`;
34884
34884
  }
34885
- _removeEmptyFolders(path3) {
34886
- return path3.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
34885
+ _removeEmptyFolders(path4) {
34886
+ return path4.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
34887
34887
  }
34888
34888
  /** Modifies the `query`, appending values the from `transform` */
34889
34889
  applyTransformOptsToQuery(query, transform2) {
@@ -36381,12 +36381,12 @@ function shouldPropagateToTarget(targetUrl, targets) {
36381
36381
  }
36382
36382
  return false;
36383
36383
  }
36384
- function matchStringTarget(hostname3, target) {
36385
- if (target === hostname3) return true;
36384
+ function matchStringTarget(hostname4, target) {
36385
+ if (target === hostname4) return true;
36386
36386
  if (target.startsWith("*.")) {
36387
36387
  const domain2 = target.slice(2);
36388
- if (hostname3.endsWith(domain2)) {
36389
- if (hostname3 === domain2 || hostname3.endsWith("." + domain2)) return true;
36388
+ if (hostname4.endsWith(domain2)) {
36389
+ if (hostname4 === domain2 || hostname4.endsWith("." + domain2)) return true;
36390
36390
  }
36391
36391
  }
36392
36392
  return false;
@@ -37172,7 +37172,7 @@ async function defaultVerifyOtp(config2, input) {
37172
37172
  }
37173
37173
  async function loginWithDeviceCode(config2, io, deps = {}) {
37174
37174
  const fetchImpl = deps.fetchImpl ?? fetch;
37175
- const sleep2 = deps.sleep ?? defaultSleep;
37175
+ const sleep3 = deps.sleep ?? defaultSleep;
37176
37176
  const openBrowser2 = deps.openBrowser ?? tryOpenBrowser;
37177
37177
  const verifyOtp = deps.verifyOtp ?? defaultVerifyOtp;
37178
37178
  const now = deps.now ?? Date.now;
@@ -37206,7 +37206,7 @@ async function loginWithDeviceCode(config2, io, deps = {}) {
37206
37206
  `The code expired before it was approved. Run '${cliBrand.binName} login --device' again.`
37207
37207
  );
37208
37208
  }
37209
- await sleep2(intervalMs);
37209
+ await sleep3(intervalMs);
37210
37210
  const poll = await postJson(fetchImpl, `${baseUrl}/api/auth/device/token`, {
37211
37211
  device_code: session.device_code
37212
37212
  });
@@ -37359,8 +37359,8 @@ var FileTokenStore = class {
37359
37359
  return `plain file fallback at ${this.filePath}`;
37360
37360
  }
37361
37361
  async chmodOwnerOnly() {
37362
- const { chmod } = await import("node:fs/promises");
37363
- await chmod(this.filePath, 384);
37362
+ const { chmod: chmod2 } = await import("node:fs/promises");
37363
+ await chmod2(this.filePath, 384);
37364
37364
  }
37365
37365
  };
37366
37366
  function createTokenStore(options = {}) {
@@ -37920,8 +37920,8 @@ function getErrorMap2() {
37920
37920
 
37921
37921
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
37922
37922
  var makeIssue = (params) => {
37923
- const { data, path: path3, errorMaps, issueData } = params;
37924
- const fullPath = [...path3, ...issueData.path || []];
37923
+ const { data, path: path4, errorMaps, issueData } = params;
37924
+ const fullPath = [...path4, ...issueData.path || []];
37925
37925
  const fullIssue = {
37926
37926
  ...issueData,
37927
37927
  path: fullPath
@@ -38037,11 +38037,11 @@ var errorUtil;
38037
38037
 
38038
38038
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
38039
38039
  var ParseInputLazyPath = class {
38040
- constructor(parent, value, path3, key) {
38040
+ constructor(parent, value, path4, key) {
38041
38041
  this._cachedPath = [];
38042
38042
  this.parent = parent;
38043
38043
  this.data = value;
38044
- this._path = path3;
38044
+ this._path = path4;
38045
38045
  this._key = key;
38046
38046
  }
38047
38047
  get path() {
@@ -42085,6 +42085,7 @@ The dry run is a real sandbox rehearsal, not a stamp. It executes a mock-provide
42085
42085
  "sync.run.start",
42086
42086
  "sync.run.complete",
42087
42087
  "sync.item.assign",
42088
+ "coach.draft_guide",
42088
42089
  "confirmation.approve"
42089
42090
  ],
42090
42091
  legal: {
@@ -42156,6 +42157,7 @@ Decisions should be recorded as human decisions. Handoffs should attach to seats
42156
42157
  "agent.list_fleet",
42157
42158
  "agent.fleet_digest",
42158
42159
  "system.health",
42160
+ "team.health_score",
42159
42161
  "agent_setup.get",
42160
42162
  "agent_setup.update",
42161
42163
  "agent.decommission",
@@ -42255,7 +42257,7 @@ Use the lower-level commands after health names a finding: \`agent.get_run\` for
42255
42257
  order: 46,
42256
42258
  title: "Tool access and vault",
42257
42259
  summary: "How to give agents access to tools without exposing raw credentials or expanding authority by accident.",
42258
- version: "2026-06-20.5",
42260
+ version: "2026-06-27.6",
42259
42261
  public: true,
42260
42262
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42261
42263
  stages: ["staffing"],
@@ -42303,9 +42305,13 @@ For an API with no dedicated connector, the generic REST tool lets a seat call a
42303
42305
 
42304
42306
  \`slack.post_message\` reuses the connected Slack workspace credential and the seat's Slack channel binding. A live call posts only to that bound channel, through the server-side guard and vault-bound bot token. If the bound channel is marked sensitive, the handler escalates for human approval instead of posting. A sandbox dry run makes no Slack request and does not open the vault.
42305
42307
 
42308
+ ## Gmail connector
42309
+
42310
+ The native Gmail handlers run inside the same broker boundary as the REST and Slack connectors: every call passes the server guard and writes an audit row, and the vaulted Google OAuth token is bound only inside a credential closure \u2014 the agent never sees it, and both the access and refresh tokens are redacted from anything a handler returns. \`gmail.read\` (read scoped inbox) and \`gmail.draft\` (compose a draft, never send) are autonomous-eligible. \`gmail.send\` is approval-gated: it never sends autonomously \u2014 it holds the action for steward approval. A sandbox dry run previews the intended read/draft/held-send with no Gmail request and without opening the vault. Live external Gmail send stays gated on Google verification and domain cutover (DER-880 / DER-881); the draft and send-with-approval paths are live now.
42311
+
42306
42312
  ## Google connector status
42307
42313
 
42308
- Google can be connected from Settings once the workspace OAuth app is configured. The connection flow requests offline access for Gmail read/compose and Sheets, stores the returned credential in the vault, and records only account and scope metadata in the integration row. The test action refreshes the vaulted credential and performs a minimal Gmail profile read. Gmail and Sheets agent tools remain configuration-only until their guarded handlers ship; connecting Google does not by itself let an agent read mail or edit a sheet.
42314
+ Google can be connected from Settings once the workspace OAuth app is configured. The connection flow requests offline access for Gmail read/compose and Sheets, stores the returned credential in the vault, and records only account and scope metadata in the integration row. The test action refreshes the vaulted credential and performs a minimal Gmail profile read. Sheets agent tools remain configuration-only until their guarded handlers ship; connecting Google does not by itself let an agent edit a sheet.
42309
42315
 
42310
42316
  CLI and MCP can inspect connector readiness without seeing secrets: \`integration.list\` / \`rost_list_integrations\` lists connected providers and health metadata, \`integration.status\` / \`rost_get_integration_status\` reads one provider by id or name, and \`integration.test\` / \`rost_test_integration_connection\` runs the installed provider-specific health check. For Google, the test refreshes the vaulted OAuth credential and reads the Gmail profile, then records only account, scope, and health metadata.
42311
42317
 
@@ -42340,7 +42346,7 @@ There is exactly one way to give a connected tool its credential, and it is the
42340
42346
  order: 47,
42341
42347
  title: "Available tools guide",
42342
42348
  summary: "How to think about tool categories available to seats and what each category should be used for.",
42343
- version: "2026-06-21.1",
42349
+ version: "2026-06-27.2",
42344
42350
  public: true,
42345
42351
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42346
42352
  stages: ["staffing"],
@@ -42386,14 +42392,14 @@ Agents should explain the job, the required tool category, the minimum permissio
42386
42392
 
42387
42393
  Every tool call passes the server-side guard first: the guard checks the call against the seat's signed permission manifest and records a tool-call audit row for **every** call \u2014 allowed, denied, or escalated. Tool selection is never authorization. Only an allowed call reaches its handler. A connected credential is bound into the handler for the duration of the call only; the secret never appears in the result, the audit summary, logs, or the model's context.
42388
42394
 
42389
- External connectors are being rolled out provider by provider, conservatively (read and draft before send; write behind approval). A selected tool is only a permission until a live handler exists and the seat has the required credential or binding. Today the built-in execution path supports internal status reporting, the generic REST connector when a signed host/method/path allowlist and credential exist, and \`slack.post_message\` for a bound Slack channel. Other provider entries remain configuration-only until their connector ships, so nothing runs silently.`
42395
+ External connectors are being rolled out provider by provider, conservatively (read and draft before send; write behind approval). A selected tool is only a permission until a live handler exists and the seat has the required credential or binding. Today the built-in execution path supports internal status reporting, the generic REST connector when a signed host/method/path allowlist and credential exist, \`slack.post_message\` for a bound Slack channel, and the Gmail handlers (\`gmail.read\`/\`gmail.draft\` autonomous-eligible, \`gmail.send\` held for approval) when Google is connected. Other provider entries remain configuration-only until their connector ships, so nothing runs silently.`
42390
42396
  },
42391
42397
  {
42392
42398
  slug: "mcp-and-cli-guide",
42393
42399
  order: 48,
42394
42400
  title: "CLI and MCP installation guide",
42395
42401
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
42396
- version: "2026-06-27.1",
42402
+ version: "2026-06-28.3",
42397
42403
  public: true,
42398
42404
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42399
42405
  stages: ["company_setup", "staffing"],
@@ -42459,6 +42465,9 @@ External connectors are being rolled out provider by provider, conservatively (r
42459
42465
  "deliverable.get",
42460
42466
  "deliverable.create",
42461
42467
  "deliverable.attach",
42468
+ "deliverable.accept",
42469
+ "usage.report",
42470
+ "usage.snapshot",
42462
42471
  "error_log.list",
42463
42472
  "error_log.resolve",
42464
42473
  "error_log.supersede",
@@ -42789,6 +42798,7 @@ These are the security posture rules for operating after install \u2014 a checkl
42789
42798
  | Command | Purpose | Scope | Safe example |
42790
42799
  |---|---|---|---|
42791
42800
  | \`{{cli}} docs\` | Print the agent-facing how-to summary. | Public reference | \`{{cli}} docs\` |
42801
+ | \`{{cli}} doctor\` | Diagnose local CLI configuration: app URL, Supabase URL, installed version, PATH, session store, local Claude/Codex availability, and update status without printing tokens. | Local environment | \`{{cli}} doctor\` |
42792
42802
  | \`{{cli}} help [group]\` | Print top-level usage, or one operation group's usage; \`{{cli}} <group> --help\` works too. | Public reference | \`{{cli}} help agent\` |
42793
42803
  | \`{{cli}} reference <list|search|get> [options]\` | Help syntax for public reference commands. \`--audience <human|cli|mcp|in_app_agent>\` filters to one audience (omit for all). | Public reference | \`{{cli}} reference search "onboarding" --audience cli\` |
42794
42804
  | \`{{cli}} reference list\` | List public reference guides. | Public reference | \`{{cli}} reference list --audience cli\` |
@@ -42838,11 +42848,12 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
42838
42848
  | \`{{cli}} escalation list|get|resolve|reject\` | \`escalation.list\`, \`escalation.get\`, \`escalation.resolve\`, \`escalation.reject\` | Work the steward escalation queue. | Steward | \`{{cli}} escalation list --json\` |
42839
42849
  | \`{{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"\` |
42840
42850
  | \`{{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\` |
42841
- | \`{{cli}} runner list|status|diagnose|repair|work-orders|revoke\` | \`runner.list\`, \`runner.status\`, \`runner.diagnose\`, \`runner.repair\`, \`work_order.list\`, \`runner.revoke\` | Inspect runners and work orders, diagnose offline runners, get repair guidance, and revoke a runner. | Tenant | \`{{cli}} runner diagnose --runner-id <id> --json\` |
42851
+ | \`{{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 runners and 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\` |
42842
42852
  | \`{{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\` |
42843
42853
  | \`{{cli}} integration list|readiness|status|test\` | \`integration.list\`, \`integration.readiness\`, \`integration.status\`, \`integration.test\` | List connector metadata, read the setup-readiness checklist, read one connector's health, and run the provider-specific connection test without exposing credentials. | Tenant | \`{{cli}} integration readiness --provider google --json\` |
42844
42854
  | \`{{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\` |
42845
- | \`{{cli}} settings get|update|product-learning|rename\` | \`settings.get\`, \`settings.update\`, \`settings.product_learning.get\`, \`settings.product_learning.update\`, \`tenant.rename\` | Read tenant settings, update budget caps, read or update product-learning participation, and rename the company. Budget and product-learning updates stop at human confirmation in non-interactive CLI/MCP sessions; company rename is owner-only and human-gated. | Tenant / Tenant-admin for updates | \`{{cli}} settings product-learning get --json\`; \`{{cli}} settings product-learning update --mode disabled --json\`; \`{{cli}} settings rename --company "Acme Operations"\` |
42855
+ | \`{{cli}} command usage.report|usage.snapshot\` | \`usage.report\`, \`usage.snapshot\` | Read the AI-workforce ROI report (per-period agent turns, token/run cost, human-accepted value, exceptions, approvals) from immutable usage snapshots, and freeze a period's totals into a new snapshot (insert-only, idempotent). | Tenant | \`{{cli}} command usage.report --json\`; \`{{cli}} command usage.snapshot --json\` |
42856
+ | \`{{cli}} settings get|update|product-learning|agent-policy|rename\` | \`settings.get\`, \`settings.update\`, \`settings.product_learning.get\`, \`settings.product_learning.update\`, \`settings.agent_policy.get\`, \`settings.agent_policy.update\`, \`tenant.rename\` | Read tenant settings, update budget caps, read or update product-learning participation, read or set the company autonomy ceiling, and rename the company. Budget, product-learning, and policy updates stop at human confirmation in non-interactive CLI/MCP sessions; company rename is owner-only and human-gated. | Tenant / Tenant-admin for updates | \`{{cli}} settings product-learning get --json\`; \`{{cli}} settings agent-policy get --json\`; \`{{cli}} settings rename --company "Acme Operations"\` |
42846
42857
  | \`{{cli}} member invite|update|remove\` | \`member.invite\`, \`member.update\`, \`member.remove\` | Manage tenant members. | Tenant | \`{{cli}} member invite --email ops@example.com --role member\` |
42847
42858
  | \`{{cli}} agent templates|create|setup|tools|dry-run|go-live|status|run-now|fleet-digest|get-run|show\` | \`agent_template.list\`, \`agent.create_from_template\`, \`agent.create_custom\`, \`agent_setup.get\`, \`agent_setup.update\`, \`agent.configure_tools\`, \`agent.run_dry_run\`, \`agent.go_live\`, \`agent.status\`, \`agent.run_now\`, \`agent.fleet_digest\`, \`agent.get_run\`, \`agent.show_markdown\` | Run the full agent setup and operation flow: list templates, create a draft from a template or guided custom answers (with \`--model\` and \`--effort\`), read or answer setup state, connect or decline tools, dry-run, go live, run on demand, capture the fleet-health digest, read one run's transcript/error diagnostics, and show a markdown readout. Human CLI dry-runs print the rehearsal transcript and, when present, the per-tool preview labels (\`Would run\`, \`Blocked\`, \`Escalated\`) before go-live. Create and go-live stop at human gates; the dry-run is ungated by human approval but requires a signed manifest first. | Tenant and seat | \`{{cli}} agent fleet-digest --json\` |
42848
42859
  | \`{{cli}} tools list\` | \`tool.catalog\` | List the discoverable tool catalog the builder reads (id, scope tiers, credential requirement, access policy, and execution-boundary guidance). | Tenant | \`{{cli}} tools list --json\` |
@@ -42853,6 +42864,8 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
42853
42864
  | \`{{cli}} compass show\` | \`compass.show_markdown\` | Render the current Compass as a clean markdown card for review. | Tenant | \`{{cli}} compass show --markdown\` |
42854
42865
  | \`{{cli}} charter show\` | \`charter.show_markdown\` | Render a seat's Charter as a clean markdown card for review. | Tenant | \`{{cli}} charter show --seat-id <id> --markdown\` |
42855
42866
  | \`{{cli}} deliverable list|get|create|attach\` | \`deliverable.list\`, \`deliverable.get\`, \`deliverable.create\`, \`deliverable.attach\` | List, get, create, or attach agent deliverables for a seat. Deliverables are durable work outputs visible across UI, CLI, and MCP. | Tenant (list/get), Seat (create/attach) | \`{{cli}} deliverable list --seat-id <id> --json\`; \`{{cli}} deliverable create --title "Brief" --kind brief\` |
42867
+ | \`{{cli}} graph show\` | \`graph.get\` | Print a table view of the Responsibility Graph with explicit \`seat_id\` and \`parent_seat_id\` columns. | Tenant | \`{{cli}} graph show\` |
42868
+ | \`{{cli}} seat list|get|create|rename|reparent|decommission\` | \`graph.get\`, \`seat.get\`, \`seat.create\`, \`seat.rename\`, \`seat.reparent\`, \`seat.decommission\`, \`seat.decommission_preview\` | Work with seats as first-class CLI primitives. \`seat decommission --dry-run\` previews affected occupancies, agents, Charters, tokens, credentials, work orders, and schedules before the human-gated teardown. | Tenant / Seat for targeted mutations | \`{{cli}} seat list\`; \`{{cli}} seat decommission --seat-id <id> --dry-run\` |
42856
42869
 
42857
42870
  Skills wrapper help:
42858
42871
 
@@ -42935,6 +42948,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
42935
42948
  | \`rost_rename_seat\` | \`seat.rename\` | Rename a seat. | Seat or tenant-admin | Call with \`seat_id\` and \`name\`. |
42936
42949
  | \`rost_reparent_seat\` | \`seat.reparent\` | Move a seat under a new parent. | Seat or tenant-admin | Call with \`seat_id\` and \`parent_seat_id\`. |
42937
42950
  | \`rost_set_seat_type\` | \`seat.set_type\` | Set a seat type. | Seat or tenant-admin | Call with \`seat_id\` and \`seat_type\`. |
42951
+ | \`rost_preview_seat_decommission\` | \`seat.decommission_preview\` | Preview retiring a seat: affected occupancies, agents, Charters, MCP tokens, credentials, work orders, and schedules. No mutation. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`; no confirmation. |
42938
42952
  | \`rost_decommission_seat\` | \`seat.decommission\` | Retire a seat: end occupancies, archive the Charter, revoke tokens/credentials, cancel schedules, tombstone event. No-orphan guarded. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`; expect confirmation. |
42939
42953
  | \`rost_draft_charter\` | \`charter.draft\` | Create or fetch a draft Charter. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`. |
42940
42954
  | \`rost_draft_all_charters\` | \`charter.draft_all\` | Draft Charters for every eligible seat. | Tenant | Call after initial graph creation. |
@@ -42977,6 +42991,9 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
42977
42991
  | \`rost_get_agent_deliverable\` | \`deliverable.get\` | Read one durable agent deliverable by id for a seat. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>","deliverable_id":"<id>"}\`. |
42978
42992
  | \`rost_create_agent_deliverable\` | \`deliverable.create\` | Create a scrubbed durable work output for the acting seat. | Seat | Call with title, kind, summary/content, and optional safe links. |
42979
42993
  | \`rost_attach_agent_deliverable\` | \`deliverable.attach\` | Attach a scrubbed deliverable to a source run, task, or work order after server-side seat validation. | Seat | Call with title, kind, and the source ids that belong to the acting seat. |
42994
+ | \`rost_accept_deliverable_value\` | \`deliverable.accept\` | Record a human-confirmed accepted value (USD) on a deliverable; owner-only and human-gated. Humans decide \u2014 agents cannot self-accept. Re-accepting records a correction as a new event. | Tenant-admin | Call with \`{"deliverable_id":"<id>","impact_value_usd":<number>}\`; expect human confirmation. |
42995
+ | \`rost_ai_workforce_roi_report\` | \`usage.report\` | Read the AI-workforce ROI report \u2014 per-period agent turns, token/run cost, human-accepted value, exceptions, and approvals \u2014 from immutable usage snapshots. | Tenant | Call with \`{}\` or \`{"limit":<n>}\`. |
42996
+ | \`rost_capture_usage_period_snapshot\` | \`usage.snapshot\` | Freeze the current (or a given) period's usage and accepted-value totals into an immutable snapshot; insert-only and idempotent. | Tenant | Call with \`{}\` or \`{"period":"YYYY-MM-DD"}\`. |
42980
42997
  | \`rost_list_agent_templates\` | \`agent_template.list\` | List stock agent templates and metadata. | Tenant | Call with \`{}\`. |
42981
42998
  | \`rost_create_agent_from_template\` | \`agent.create_from_template\` | Create a draft stock agent and draft Charter from a template (draft-only; occupancy needs a human steward). | Tenant | Call with \`seat_id\` and \`template_slug\`; expect human confirmation. |
42982
42999
  | \`rost_start_agent_setup\` | \`agent_setup.start\` | Start an agent setup draft for template, custom, or existing mode. | Tenant | Call with \`mode\` and seat placement. |
@@ -43031,9 +43048,12 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
43031
43048
  | \`rost_create_cascade_goal\` | \`goal.create\` | Create a cycle goal under an objective. | Tenant | Call with cycle, seat, parent, title, definition of done. |
43032
43049
  | \`rost_update_cascade_goal\` | \`goal.update\` | Update a goal's title or definition of done. | Tenant | Call with \`goal_id\` and the changed fields. |
43033
43050
  | \`rost_set_cascade_goal_status\` | \`goal.set_status\` | Set a goal's status (on/off/done). | Seat or tenant-admin | Seats may set only their own goals. |
43051
+ | \`rost_set_cascade_goal_progress\` | \`goal.set_progress\` | Report a goal's quantified progress (0-100). An agent's progress is a proposal a human approves before the goal moves. | Seat or tenant-admin | Seats set only their own goals; expect confirmation. |
43052
+ | \`rost_list_at_risk_cascade_goals\` | \`goal.list_at_risk\` | List goals projected off pace (at-risk or projected-miss) with days behind pace. Read-only; status-only goals excluded. | Tenant | Call with \`{}\`. |
43034
43053
  | \`rost_reparent_cascade_goal\` | \`goal.reparent\` | Move a goal under a new parent. | Tenant | Authority change; expect confirmation. |
43035
43054
  | \`rost_drop_cascade_goal\` | \`goal.drop\` | Drop (retire) a goal, retained for audit. | Tenant | Expect confirmation. |
43036
43055
  | \`rost_list_friction_issues\` | \`friction.list\` | List Friction issues ranked by impact and severity. | Seat or tenant-admin | Call with \`{}\` or \`{"status":"active"}\`. |
43056
+ | \`rost_assist_friction_issue\` | \`friction.assist\` | Advisory IDS assist: the most-similar prior resolved issues with their root causes and decisions, a recurring-pattern flag, and recommended options. Read-only \u2014 it never resolves the issue. | Tenant | Call with \`{"issue_id":"<id>"}\`. |
43037
43057
  | \`rost_update_friction_issue_status\` | \`friction.update_status\` | Move a non-terminal issue between open and diagnosing. | Seat or tenant-admin | Call with \`issue_id\` and \`status\`. |
43038
43058
  | \`rost_resolve_friction_issue\` | \`friction.resolve\` | Resolve an issue with a root cause and remediation task. | Tenant | Human decision; expect confirmation. |
43039
43059
  | \`rost_link_task_to_friction_issue\` | \`friction.link_task\` | Attach an existing task as an issue's action task. | Seat or tenant-admin | Call with \`issue_id\` and \`task_id\`. |
@@ -43045,6 +43065,8 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
43045
43065
  | \`rost_assign_sync_follow_up\` | \`sync.item.assign\` | Create a follow-up task from a Sync agenda item. | Tenant | Call with brief, owner seat, title, description. |
43046
43066
  | \`rost_list_escalations\` | \`escalation.list\` | List Steward queue escalations (own steward chain for humans, own seat for agents). | Seat or tenant-admin | Call with \`{}\` or \`{"include_decided":true}\`. |
43047
43067
  | \`rost_get_escalation\` | \`escalation.get\` | Read one escalation's evidence, recommendation, and decision state. | Seat or tenant-admin | Call with \`{"id":"<escalation-id>"}\`. Resolve/reject are human-only and not exposed over MCP. |
43068
+ | \`rost_check_compass_alignment\` | \`compass.alignment\` | Advisory drift check: scores whether cycle goals still ladder to the active vision and flags orphan/contradictory goals. Read-only \u2014 it never edits the Compass or any goal. | Tenant | Call with \`{}\`. |
43069
+ | \`rost_team_health_score\` | \`team.health_score\` | Read the single rolled-up 0-100 team-health score (latest month) with its band and label. | Tenant | Call with \`{}\`. |
43048
43070
  | \`rost_show_compass_as_markdown\` | \`compass.show_markdown\` | Render the current Compass and its open gaps as a clean markdown card for review. | Tenant | Call with \`{}\`. |
43049
43071
  | \`rost_show_charter_as_markdown\` | \`charter.show_markdown\` | Render a seat's active or latest Charter as a clean markdown card for review. | Tenant | Call with \`{"seat_id":"<seat-id>"}\`. |
43050
43072
  | \`rost_show_agent_setup_as_markdown\` | \`agent.show_markdown\` | Render a seat's agent setup, model, steward, tools, and Charter as a clean markdown card for review. | Tenant | Call with \`{"seat_id":"<seat-id>"}\`. |
@@ -43532,7 +43554,7 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
43532
43554
  order: 60,
43533
43555
  title: "Cascade guide",
43534
43556
  summary: "How to connect company goals to seat-level work without turning {{brand}} into a project-management tool.",
43535
- version: "2026-06-20.2",
43557
+ version: "2026-06-28.1",
43536
43558
  public: true,
43537
43559
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43538
43560
  stages: ["operating_rhythm"],
@@ -43544,8 +43566,11 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
43544
43566
  "goal.create",
43545
43567
  "goal.update",
43546
43568
  "goal.set_status",
43569
+ "goal.set_progress",
43570
+ "goal.list_at_risk",
43547
43571
  "goal.reparent",
43548
43572
  "goal.drop",
43573
+ "compass.alignment",
43549
43574
  "confirmation.approve"
43550
43575
  ],
43551
43576
  legal: {
@@ -43587,6 +43612,10 @@ Cascade warnings are advisory, not blockers. They point to setup that should be
43587
43612
 
43588
43613
  On \`/cascade\`, the screen renders the full goal depth, not just one or two levels. Off-track descendant branches stay open so the operator can see why a parent rolled up to risk.
43589
43614
 
43615
+ ## Goal progress and at-risk goals
43616
+
43617
+ A goal can carry a quantified progress (0-100) distinct from its on/off/done status. Report it with \`goal.set_progress\`; when an agent reports progress it lands as a proposal a human approves before the goal moves. \`goal.list_at_risk\` projects which goals are off pace by comparing progress against the elapsed fraction of the cycle window (deterministic date math, no forecast model). Off-pace goals surface on \`/cascade\`, on the owner home, in the weekly Sync Brief, and as a weekly owner alert. Status-only goals (no progress recorded) are simply excluded from the projection.
43618
+
43590
43619
  On human and function seat pages, goal visibility includes both direct goals and descendant seat goals from the Responsibility Graph subtree, so a manager can review team progress without double-owning the agent's work.
43591
43620
 
43592
43621
  ## What does not belong
@@ -43689,6 +43718,7 @@ Agents may record readings when the Charter allows it. Agent-reported readings s
43689
43718
  "compass.answer_gap",
43690
43719
  "friction.file_issue",
43691
43720
  "friction.list",
43721
+ "friction.assist",
43692
43722
  "friction.update_status",
43693
43723
  "friction.resolve",
43694
43724
  "friction.link_task",
@@ -43752,7 +43782,7 @@ Resolving Friction should produce one of four outcomes: a decision, a task, a Ch
43752
43782
  order: 70,
43753
43783
  title: "Steward queue guide",
43754
43784
  summary: "How Stewards review escalations, approve agent boundaries, and keep agents accountable.",
43755
- version: "2026-06-27.1",
43785
+ version: "2026-06-27.2",
43756
43786
  public: true,
43757
43787
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43758
43788
  stages: ["staffing", "operating_rhythm"],
@@ -43795,6 +43825,10 @@ When nothing is waiting, the Steward page still proves the governance loop. It s
43795
43825
 
43796
43826
  An escalation reaches you the same way no matter how the agent raised it \u2014 in a chat or CLI session, or on its own scheduled cloud run. When the agent stops at a boundary it opens the queue item and sends a notification to the accountable human along the steward chain, preferring a connected Slack channel and falling back to email. If no human occupies the agent's seat, the chain walks up to the next Steward. Delivery is idempotent: a retried run never notifies you twice for the same escalation, and the notification carries only summarized, redacted evidence \u2014 never secret material.
43797
43827
 
43828
+ ## Where notifications land
43829
+
43830
+ Every notification sent to you \u2014 escalations, approval requests, sync briefs, and your daily digest \u2014 also appears in your in-app Inbox, with unread state you can clear. A Slack or email delivery that fails is retried automatically on a bounded schedule; once it can no longer be delivered it stays visible in the Inbox rather than being lost. A once-a-day digest rolls up the day's deliverables, open approvals, and exceptions into a single message, so a quiet day stays quiet and a busy one is summarized in one place.
43831
+
43798
43832
  ## Work the queue from CLI or MCP
43799
43833
 
43800
43834
  The Steward reads the queue from any surface but decides as a human.
@@ -45947,12 +45981,12 @@ function resolveModelConfig(model, effort) {
45947
45981
  ...resolvedEffort ? { effort: resolvedEffort } : {}
45948
45982
  };
45949
45983
  }
45950
- function usageWithSchemaFooter(usage, bin) {
45951
- return `${usage}
45984
+ function usageWithSchemaFooter(usage2, bin) {
45985
+ return `${usage2}
45952
45986
  discover a command's exact input with: ${bin} command schema <id>`;
45953
45987
  }
45954
- async function dispatch(context, group, args, actions, usage) {
45955
- const usageText = usageWithSchemaFooter(usage, context.binName);
45988
+ async function dispatch(context, group, args, actions, usage2) {
45989
+ const usageText = usageWithSchemaFooter(usage2, context.binName);
45956
45990
  const action = args[0];
45957
45991
  if (action === "--help" || action === "-h" || action === "help") {
45958
45992
  context.io.stdout.write(`${usageText}
@@ -45978,12 +46012,137 @@ ${usageText}
45978
46012
  }
45979
46013
  }
45980
46014
  function execute(context, parsed, commandId, body, format) {
45981
- const options = parsed.seat === void 0 ? void 0 : { targetSeatId: parsed.seat };
46015
+ const autoConfirm = parsed.flags.has("yes") || parsed.flags.has("y");
46016
+ const options = {
46017
+ ...parsed.seat === void 0 ? {} : { targetSeatId: parsed.seat },
46018
+ ...autoConfirm ? { autoConfirm: true } : {}
46019
+ };
45982
46020
  if (parsed.json) {
45983
46021
  return context.run(context.client, commandId, body, void 0, options);
45984
46022
  }
45985
46023
  return context.run(context.client, commandId, body, format, options);
45986
46024
  }
46025
+ var graphWrapper = (context, args) => dispatch(context, "graph", args, {
46026
+ show: (ctx, rest) => {
46027
+ const parsed = parseFlags(rest);
46028
+ return execute(ctx, parsed, "graph.get", {}, formatGraph);
46029
+ },
46030
+ get: (ctx, rest) => {
46031
+ const parsed = parseFlags(rest);
46032
+ return execute(ctx, parsed, "graph.get", {}, formatGraph);
46033
+ }
46034
+ }, graphUsage(context.binName));
46035
+ function formatGraph(output) {
46036
+ const record2 = asRecord(output);
46037
+ const nodes = asArray(record2.nodes);
46038
+ if (nodes.length === 0) {
46039
+ return "No seats in the Responsibility Graph.";
46040
+ }
46041
+ const lines = [
46042
+ `root_seat_id=${field(record2, "root_seat_id")}`,
46043
+ "seat_id parent_seat_id type status name"
46044
+ ];
46045
+ for (const node of nodes) {
46046
+ const row = asRecord(node);
46047
+ lines.push(`${field(row, "seat_id")} ${field(row, "parent_seat_id")} ${field(row, "seat_type")} ${field(row, "status")} ${field(row, "name")}`);
46048
+ }
46049
+ return lines.join("\n");
46050
+ }
46051
+ function graphUsage(bin) {
46052
+ return `Usage: ${bin} graph show|get [--json]
46053
+ ${bin} graph show`;
46054
+ }
46055
+ var seatWrapper = (context, args) => dispatch(context, "seat", args, {
46056
+ list: (ctx, rest) => {
46057
+ const parsed = parseFlags(rest);
46058
+ return execute(ctx, parsed, "graph.get", {}, formatSeatList);
46059
+ },
46060
+ get: (ctx, rest) => {
46061
+ const parsed = parseFlags(rest);
46062
+ const seatId = requireValue2(parsed, "seat-id");
46063
+ return execute(ctx, parsed, "seat.get", { seat_id: seatId }, formatSeatDetail);
46064
+ },
46065
+ create: (ctx, rest) => {
46066
+ const parsed = parseFlags(rest);
46067
+ const body = withOptional({
46068
+ name: requireValue2(parsed, "name"),
46069
+ seat_type: requireValue2(parsed, "type")
46070
+ }, {
46071
+ parent_seat_id: optionalValue(parsed, "parent-seat-id")
46072
+ });
46073
+ return execute(ctx, parsed, "seat.create", body, formatSeatMutation);
46074
+ },
46075
+ rename: (ctx, rest) => {
46076
+ const parsed = parseFlags(rest);
46077
+ return execute(ctx, parsed, "seat.rename", {
46078
+ seat_id: requireValue2(parsed, "seat-id"),
46079
+ name: requireValue2(parsed, "name")
46080
+ }, formatSeatMutation);
46081
+ },
46082
+ reparent: (ctx, rest) => {
46083
+ const parsed = parseFlags(rest);
46084
+ return execute(ctx, parsed, "seat.reparent", {
46085
+ seat_id: requireValue2(parsed, "seat-id"),
46086
+ new_parent_seat_id: requireValue2(parsed, "parent-seat-id")
46087
+ }, formatSeatMutation);
46088
+ },
46089
+ decommission: (ctx, rest) => {
46090
+ const parsed = parseFlags(rest, /* @__PURE__ */ new Set(["dry-run", "preview"]));
46091
+ if (parsed.flags.has("dry-run") || parsed.flags.has("preview")) {
46092
+ return execute(ctx, parsed, "seat.decommission_preview", {
46093
+ seat_id: requireValue2(parsed, "seat-id")
46094
+ }, formatSeatDecommissionPreview);
46095
+ }
46096
+ return execute(ctx, parsed, "seat.decommission", {
46097
+ seat_id: requireValue2(parsed, "seat-id")
46098
+ }, formatSeatMutation);
46099
+ }
46100
+ }, seatUsage(context.binName));
46101
+ function formatSeatList(output) {
46102
+ const nodes = asArray(asRecord(output).nodes);
46103
+ if (nodes.length === 0) {
46104
+ return "No seats.";
46105
+ }
46106
+ return nodes.map((node) => {
46107
+ const row = asRecord(node);
46108
+ return `seat_id=${field(row, "seat_id")} parent_seat_id=${field(row, "parent_seat_id")} type=${field(row, "seat_type")} status=${field(row, "status")} name=${field(row, "name")}`;
46109
+ }).join("\n");
46110
+ }
46111
+ function formatSeatDetail(output) {
46112
+ const record2 = asRecord(output);
46113
+ const counts = asRecord(record2.counts);
46114
+ return [
46115
+ `seat_id=${field(record2, "seat_id")} name=${field(record2, "name")} type=${field(record2, "seat_type")} status=${field(record2, "status")}`,
46116
+ `parent_seat_id=${field(record2, "parent_seat_id")} active_charter_version_id=${field(record2, "active_charter_version_id")} draft_charter_version_id=${field(record2, "draft_charter_version_id")}`,
46117
+ `agent_status=${field(record2, "agent_status")} agent_lane=${field(record2, "agent_lane")}`,
46118
+ `counts: open_tasks=${field(counts, "open_tasks")} owned_goals=${field(counts, "owned_goals")} active_measurables=${field(counts, "active_measurables")} open_issues=${field(counts, "open_issues")}`
46119
+ ].join("\n");
46120
+ }
46121
+ function formatSeatMutation(output) {
46122
+ const record2 = asRecord(output);
46123
+ return `seat_id=${field(record2, "seat_id")}`;
46124
+ }
46125
+ function formatSeatDecommissionPreview(output) {
46126
+ const record2 = asRecord(output);
46127
+ const effects = asArray(record2.effects).map((effect) => `- ${String(effect)}`);
46128
+ return [
46129
+ `seat_id=${field(record2, "seat_id")} name=${field(record2, "seat_name")} status=${field(record2, "previous_status")}`,
46130
+ `active_occupancies=${field(record2, "active_occupancy_count")} active_agents=${field(record2, "active_agent_ids")}`,
46131
+ `active_charters=${field(record2, "active_charter_count")} mcp_tokens=${field(record2, "active_mcp_token_count")} credentials=${field(record2, "active_credential_count")}`,
46132
+ `open_work_orders=${field(record2, "open_work_order_count")} schedules=${field(record2, "schedule_count")} no_orphan_steward_guard=${field(record2, "no_orphan_steward_guard")}`,
46133
+ "effects:",
46134
+ ...effects
46135
+ ].join("\n");
46136
+ }
46137
+ function seatUsage(bin) {
46138
+ return `Usage: ${bin} seat list|get|create|rename|reparent|decommission [--json] [--yes]
46139
+ ${bin} seat list
46140
+ ${bin} seat get --seat-id <id>
46141
+ ${bin} seat create --name <text> --type human|agent|hybrid [--parent-seat-id <id>]
46142
+ ${bin} seat rename --seat-id <id> --name <text>
46143
+ ${bin} seat reparent --seat-id <id> --parent-seat-id <id> [--yes]
46144
+ ${bin} seat decommission --seat-id <id> [--dry-run] [--yes]`;
46145
+ }
45987
46146
  var taskWrapper = (context, args) => dispatch(context, "task", args, {
45988
46147
  list: (ctx, rest) => {
45989
46148
  const parsed = parseFlags(rest);
@@ -46496,13 +46655,14 @@ var runnerWrapper = (context, args) => dispatch(context, "runner", args, {
46496
46655
  }
46497
46656
  }, runnerUsage(context.binName));
46498
46657
  function runnerUsage(bin) {
46499
- return `Usage: ${bin} runner list|status|diagnose|repair|work-orders|revoke [--json]
46658
+ return `Usage: ${bin} runner list|status|diagnose|repair|work-orders|revoke|serve [--json]
46500
46659
  ${bin} runner list [--include-revoked]
46501
46660
  ${bin} runner status --runner-id <id>
46502
46661
  ${bin} runner diagnose --runner-id <id>
46503
46662
  ${bin} runner repair --runner-id <id> [--issue restart|re_pair|revoke_stale|missing_cli_runtime]
46504
46663
  ${bin} runner work-orders [--status <s>] [--agent-id <id>] [--runner-id <id>] [--limit <n>]
46505
- ${bin} runner revoke --runner-id <id>`;
46664
+ ${bin} runner revoke --runner-id <id>
46665
+ ${bin} runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--once] [--execute]`;
46506
46666
  }
46507
46667
  var notificationWrapper = (context, args) => dispatch(context, "notification", args, {
46508
46668
  settings: (ctx, rest) => {
@@ -47487,7 +47647,9 @@ var OPERATION_GROUPS = [
47487
47647
  "compass",
47488
47648
  "charter",
47489
47649
  "deliverable",
47490
- "system"
47650
+ "system",
47651
+ "graph",
47652
+ "seat"
47491
47653
  ];
47492
47654
  var wrappers = {
47493
47655
  task: taskWrapper,
@@ -47510,7 +47672,9 @@ var wrappers = {
47510
47672
  compass: compassWrapper,
47511
47673
  charter: charterWrapper,
47512
47674
  deliverable: deliverableWrapper,
47513
- system: systemWrapper
47675
+ system: systemWrapper,
47676
+ graph: graphWrapper,
47677
+ seat: seatWrapper
47514
47678
  };
47515
47679
  function isOperationGroup(value) {
47516
47680
  return value !== void 0 && OPERATION_GROUPS.includes(value);
@@ -47536,7 +47700,9 @@ var groupUsageBuilders = {
47536
47700
  compass: compassUsage,
47537
47701
  charter: charterUsage,
47538
47702
  deliverable: deliverableUsage,
47539
- system: systemUsage
47703
+ system: systemUsage,
47704
+ graph: graphUsage,
47705
+ seat: seatUsage
47540
47706
  };
47541
47707
  function operationGroupUsage(group, bin) {
47542
47708
  return usageWithSchemaFooter(groupUsageBuilders[group](bin), bin);
@@ -47559,9 +47725,9 @@ function operationUsageLines(bin) {
47559
47725
  `${bin} escalation list|get|resolve|reject`,
47560
47726
  `${bin} error list|resolve|supersede`,
47561
47727
  `${bin} sync brief|compile|complete`,
47562
- `${bin} runner list|status|diagnose|repair|work-orders|revoke`,
47728
+ `${bin} runner list|status|diagnose|repair|work-orders|revoke|serve`,
47563
47729
  `${bin} notification settings|test|errors`,
47564
- `${bin} settings get|update|product-learning|rename`,
47730
+ `${bin} settings get|update|product-learning|agent-policy|rename`,
47565
47731
  `${bin} member invite|update|remove`,
47566
47732
  `${bin} agent templates|create|setup|tools|dry-run|go-live|status|run-now|fleet-digest|get-run|show`,
47567
47733
  `${bin} tools list`,
@@ -47569,7 +47735,9 @@ function operationUsageLines(bin) {
47569
47735
  `${bin} model list`,
47570
47736
  `${bin} compass show`,
47571
47737
  `${bin} charter show`,
47572
- `${bin} deliverable list|get|create|attach`
47738
+ `${bin} deliverable list|get|create|attach`,
47739
+ `${bin} graph show`,
47740
+ `${bin} seat list|get|create|rename|reparent|decommission`
47573
47741
  ];
47574
47742
  }
47575
47743
 
@@ -48064,6 +48232,424 @@ function pendingConfirmationFromError(error51) {
48064
48232
  return { confirmationId, commandId };
48065
48233
  }
48066
48234
 
48235
+ // src/doctor.ts
48236
+ import { execFile as execFileCallback2 } from "node:child_process";
48237
+ import { access, readFile as readFile3 } from "node:fs/promises";
48238
+ import { delimiter } from "node:path";
48239
+ import { promisify as promisify3 } from "node:util";
48240
+ var execFile3 = promisify3(execFileCallback2);
48241
+ async function runDoctor(options) {
48242
+ const env = options.env ?? process.env;
48243
+ const store = options.store ?? createTokenStore();
48244
+ const lines = [`${cliBrand.name} CLI doctor`];
48245
+ const config2 = loadConfig(env);
48246
+ lines.push(`app_url: ${config2.appUrl}`);
48247
+ lines.push(`supabase_url: ${config2.supabaseUrl}`);
48248
+ const currentVersion = await readCurrentVersion2();
48249
+ lines.push(`version: ${currentVersion ?? "unknown"}`);
48250
+ const pathStatus = await checkPath(env);
48251
+ lines.push(pathStatus);
48252
+ try {
48253
+ const session = await store.read();
48254
+ lines.push(`session: ${session ? `present (${store.describe()})` : `missing - run ${cliBrand.binName} login --device`}`);
48255
+ } catch (error51) {
48256
+ lines.push(`session: unreadable - ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}`);
48257
+ }
48258
+ const [claude, codex] = await Promise.all([probeCli("claude"), probeCli("codex")]);
48259
+ lines.push(`claude: ${claude}`);
48260
+ lines.push(`codex: ${codex}`);
48261
+ const latest = await fetchLatestVersion2(cliBrand.packageName, env);
48262
+ if (currentVersion && latest && isSemverBehind2(currentVersion, latest)) {
48263
+ lines.push(`update: ${currentVersion} -> ${latest}; run npm i -g ${cliBrand.packageName}@latest`);
48264
+ } else if (latest) {
48265
+ lines.push(`update: current (${latest})`);
48266
+ } else {
48267
+ lines.push("update: skipped or unavailable");
48268
+ }
48269
+ options.io.stdout.write(`${lines.join("\n")}
48270
+ `);
48271
+ return 0;
48272
+ }
48273
+ async function checkPath(env) {
48274
+ const pathValue = env.PATH ?? "";
48275
+ const candidates = pathValue.split(delimiter).filter(Boolean).map((entry) => `${entry}/${cliBrand.binName}`);
48276
+ for (const candidate of candidates) {
48277
+ try {
48278
+ await access(candidate);
48279
+ return `path: found ${candidate}`;
48280
+ } catch {
48281
+ continue;
48282
+ }
48283
+ }
48284
+ return `path: ${cliBrand.binName} not found on PATH - use npx ${cliBrand.packageName}@latest or add your npm global bin directory`;
48285
+ }
48286
+ async function probeCli(command) {
48287
+ try {
48288
+ const { stdout, stderr } = await execFile3(command, ["--version"], { timeout: 1500 });
48289
+ const version4 = (stdout || stderr).trim().split("\n")[0]?.trim();
48290
+ return version4 ? `installed (${version4})` : "installed";
48291
+ } catch {
48292
+ return "not found";
48293
+ }
48294
+ }
48295
+ async function readCurrentVersion2() {
48296
+ try {
48297
+ const url2 = new URL("../package.json", import.meta.url);
48298
+ const parsed = JSON.parse(await readFile3(url2, "utf8"));
48299
+ return typeof parsed.version === "string" ? parsed.version : null;
48300
+ } catch {
48301
+ return null;
48302
+ }
48303
+ }
48304
+ async function fetchLatestVersion2(packageName, env) {
48305
+ if (env.CI || env.VITEST || env.CLI_SKIP_UPDATE_CHECK === "1" || env[`${cliBrand.envPrefix}_CLI_NO_UPDATE_CHECK`] === "1") {
48306
+ return null;
48307
+ }
48308
+ const controller = new AbortController();
48309
+ const timeout = setTimeout(() => controller.abort(), 750);
48310
+ try {
48311
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
48312
+ headers: { accept: "application/json" },
48313
+ signal: controller.signal
48314
+ });
48315
+ if (!response.ok) {
48316
+ return null;
48317
+ }
48318
+ const body = await response.json();
48319
+ return typeof body.version === "string" ? body.version : null;
48320
+ } catch {
48321
+ return null;
48322
+ } finally {
48323
+ clearTimeout(timeout);
48324
+ }
48325
+ }
48326
+ function isSemverBehind2(currentVersion, latestVersion) {
48327
+ const current = parseSemver2(currentVersion);
48328
+ const latest = parseSemver2(latestVersion);
48329
+ if (!current || !latest) {
48330
+ return currentVersion !== latestVersion;
48331
+ }
48332
+ if (current.major !== latest.major) {
48333
+ return current.major < latest.major;
48334
+ }
48335
+ if (current.minor !== latest.minor) {
48336
+ return current.minor < latest.minor;
48337
+ }
48338
+ return current.patch < latest.patch;
48339
+ }
48340
+ function parseSemver2(value) {
48341
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value);
48342
+ if (!match) {
48343
+ return null;
48344
+ }
48345
+ const [, major, minor, patch] = match;
48346
+ if (major === void 0 || minor === void 0 || patch === void 0) {
48347
+ return null;
48348
+ }
48349
+ return {
48350
+ major: Number.parseInt(major, 10),
48351
+ minor: Number.parseInt(minor, 10),
48352
+ patch: Number.parseInt(patch, 10)
48353
+ };
48354
+ }
48355
+
48356
+ // src/runner-serve.ts
48357
+ import { execFile as execFileCallback3, spawn } from "node:child_process";
48358
+ import { createHash } from "node:crypto";
48359
+ import { chmod, mkdir as mkdir2, readFile as readFile4, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
48360
+ import { hostname as hostname3, tmpdir } from "node:os";
48361
+ import path3 from "node:path";
48362
+ import { setTimeout as sleep2 } from "node:timers/promises";
48363
+ import { promisify as promisify4 } from "node:util";
48364
+ var execFile4 = promisify4(execFileCallback3);
48365
+ async function runRunnerServe(options) {
48366
+ const env = options.env ?? process.env;
48367
+ const fetchImpl = options.fetchImpl ?? fetch;
48368
+ let config2;
48369
+ try {
48370
+ config2 = parseServeArgs(options.args, options.appUrl, env);
48371
+ } catch (error51) {
48372
+ options.io.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
48373
+ ${usage()}
48374
+ `);
48375
+ return 1;
48376
+ }
48377
+ options.io.stdout.write(`runner serve: ${options.appUrl} name=${config2.name} execute=${config2.execute}
48378
+ `);
48379
+ let state;
48380
+ try {
48381
+ state = await pairIfNeeded(options.client ?? null, fetchImpl, options.appUrl, config2, options.io);
48382
+ } catch (error51) {
48383
+ options.io.stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48384
+ `);
48385
+ return 1;
48386
+ }
48387
+ let beat = 0;
48388
+ let heartbeatSucceeded = false;
48389
+ let failed = false;
48390
+ do {
48391
+ const capabilities = await detectCapabilities();
48392
+ try {
48393
+ const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", { capabilities }, state.runner_secret);
48394
+ if (heartbeat.status !== 200) {
48395
+ options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
48396
+ `);
48397
+ failed = true;
48398
+ } else {
48399
+ heartbeatSucceeded = true;
48400
+ options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
48401
+ `);
48402
+ }
48403
+ if (config2.execute && capabilities.claude.installed) {
48404
+ await claimAndExecute(fetchImpl, options.appUrl, state, options.io);
48405
+ }
48406
+ } catch (error51) {
48407
+ failed = true;
48408
+ options.io.stderr.write(`transient runner error (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48409
+ `);
48410
+ }
48411
+ beat += 1;
48412
+ if (!config2.once) {
48413
+ await sleep2(config2.heartbeatMs);
48414
+ }
48415
+ } while (!config2.once);
48416
+ return heartbeatSucceeded && !failed ? 0 : 1;
48417
+ }
48418
+ function parseServeArgs(args, appUrl2, env) {
48419
+ let name = env.RUNNER_NAME ?? `headless-${hostname3()}`;
48420
+ let stateFile = env.RUNNER_STATE_FILE ?? path3.join(tmpdir(), `rost-runner-${createHash("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
48421
+ let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
48422
+ let execute2 = env.RUNNER_EXECUTE === "1";
48423
+ let once = false;
48424
+ let userCode = env.RUNNER_USER_CODE;
48425
+ for (let index = 0; index < args.length; index += 1) {
48426
+ const token = args[index];
48427
+ if (token === "--name") {
48428
+ name = requiredValue(args, index);
48429
+ index += 1;
48430
+ } else if (token === "--state-file") {
48431
+ stateFile = requiredValue(args, index);
48432
+ index += 1;
48433
+ } else if (token === "--heartbeat-ms") {
48434
+ heartbeatMs = Number(requiredValue(args, index));
48435
+ index += 1;
48436
+ } else if (token === "--user-code") {
48437
+ userCode = requiredValue(args, index);
48438
+ index += 1;
48439
+ } else if (token === "--execute") {
48440
+ execute2 = true;
48441
+ } else if (token === "--once") {
48442
+ once = true;
48443
+ } else {
48444
+ throw new Error(`Unexpected runner serve argument: ${token ?? ""}`);
48445
+ }
48446
+ }
48447
+ if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3) {
48448
+ throw new Error("--heartbeat-ms must be an integer >= 1000.");
48449
+ }
48450
+ return { name, stateFile, heartbeatMs, execute: execute2, once, ...userCode ? { userCode } : {} };
48451
+ }
48452
+ function requiredValue(args, index) {
48453
+ const value = args[index + 1];
48454
+ if (value === void 0 || value.startsWith("--")) {
48455
+ throw new Error(`${args[index] ?? "flag"} requires a value.`);
48456
+ }
48457
+ return value;
48458
+ }
48459
+ async function resolveRunnerServeTenant(client) {
48460
+ try {
48461
+ const response = await client.execute("user.whoami", {});
48462
+ const output = response.output && typeof response.output === "object" && !Array.isArray(response.output) ? response.output : {};
48463
+ const activeTenantId = output.active_tenant_id ?? output.current_tenant_id ?? output.tenant_id;
48464
+ return typeof activeTenantId === "string" && activeTenantId.length > 0 ? activeTenantId : null;
48465
+ } catch {
48466
+ return null;
48467
+ }
48468
+ }
48469
+ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
48470
+ if (client) {
48471
+ const expectedTenantId = await resolveRunnerServeTenant(client);
48472
+ if (expectedTenantId) {
48473
+ config2.expectedTenantId = expectedTenantId;
48474
+ }
48475
+ }
48476
+ const existing = await loadState(config2.stateFile, config2.expectedTenantId);
48477
+ if (existing) {
48478
+ io.stdout.write(`reusing paired runner ${existing.runner_id}
48479
+ `);
48480
+ return existing;
48481
+ }
48482
+ if (!client) {
48483
+ throw new Error(`No paired runner state at ${config2.stateFile}. Run with a CLI login once to pair this runner before starting without a CLI session.`);
48484
+ }
48485
+ const userCode = config2.userCode ?? await startPairing(client, config2);
48486
+ const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", { user_code: userCode });
48487
+ const runnerSecret = claim.json.runner_secret;
48488
+ const runnerId = claim.json.runner_id;
48489
+ const tenantId = claim.json.tenant_id;
48490
+ if (claim.status !== 200 || typeof runnerSecret !== "string" || typeof runnerId !== "string") {
48491
+ throw new Error(`pairing claim failed ${claim.status}: ${redactForLog(JSON.stringify(claim.json))}`);
48492
+ }
48493
+ const state = {
48494
+ runner_id: runnerId,
48495
+ runner_secret: runnerSecret,
48496
+ ...typeof tenantId === "string" ? { tenant_id: tenantId } : config2.expectedTenantId ? { tenant_id: config2.expectedTenantId } : {},
48497
+ name: typeof claim.json.name === "string" ? claim.json.name : config2.name
48498
+ };
48499
+ await mkdir2(path3.dirname(config2.stateFile), { recursive: true, mode: 448 });
48500
+ await writeFile2(config2.stateFile, `${JSON.stringify(state)}
48501
+ `, { mode: 384 });
48502
+ await chmod(config2.stateFile, 384);
48503
+ io.stdout.write(`paired runner_id=${runnerId} state_file=${config2.stateFile}
48504
+ `);
48505
+ return state;
48506
+ }
48507
+ async function loadState(filePath, expectedTenantId) {
48508
+ try {
48509
+ const parsed = JSON.parse(await readFile4(filePath, "utf8"));
48510
+ if (typeof parsed.runner_id === "string" && typeof parsed.runner_secret === "string") {
48511
+ if (expectedTenantId && parsed.tenant_id !== expectedTenantId) {
48512
+ return null;
48513
+ }
48514
+ return {
48515
+ runner_id: parsed.runner_id,
48516
+ runner_secret: parsed.runner_secret,
48517
+ ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
48518
+ ...typeof parsed.name === "string" ? { name: parsed.name } : {}
48519
+ };
48520
+ }
48521
+ } catch {
48522
+ return null;
48523
+ }
48524
+ return null;
48525
+ }
48526
+ async function startPairing(client, config2) {
48527
+ const response = await client.execute("runner.pairing.start", { name: config2.name, platform: process.platform });
48528
+ const userCode = response.output && typeof response.output === "object" ? response.output.user_code : void 0;
48529
+ if (typeof userCode !== "string" || userCode.length === 0) {
48530
+ throw new Error("runner.pairing.start did not return a user_code.");
48531
+ }
48532
+ return userCode;
48533
+ }
48534
+ async function detectCapabilities() {
48535
+ const [claude, codex] = await Promise.all([probe("claude"), probe("codex")]);
48536
+ return { claude, codex };
48537
+ }
48538
+ async function probe(command) {
48539
+ try {
48540
+ const { stdout, stderr } = await execFile4(command, ["--version"], { timeout: 1500 });
48541
+ return { installed: true, version: (stdout || stderr).trim() || null, auth_state: "ready" };
48542
+ } catch {
48543
+ return { installed: false, version: null, auth_state: "unknown" };
48544
+ }
48545
+ }
48546
+ async function post2(fetchImpl, appUrl2, pathName, body, token) {
48547
+ const headers = { "content-type": "application/json" };
48548
+ if (token) {
48549
+ headers.authorization = `Bearer ${token}`;
48550
+ }
48551
+ const response = await fetchImpl(`${appUrl2.replace(/\/+$/, "")}${pathName}`, {
48552
+ method: "POST",
48553
+ headers,
48554
+ body: JSON.stringify(body)
48555
+ });
48556
+ const text = await response.text();
48557
+ let json2;
48558
+ try {
48559
+ json2 = JSON.parse(text);
48560
+ } catch {
48561
+ json2 = { raw: text.slice(0, 300) };
48562
+ }
48563
+ return { status: response.status, json: json2 };
48564
+ }
48565
+ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
48566
+ const claimed = await post2(fetchImpl, appUrl2, "/api/runner/work-orders/claim", {}, state.runner_secret);
48567
+ const workOrder = claimed.json.work_order;
48568
+ if (claimed.status !== 200 || !workOrder || typeof workOrder !== "object") {
48569
+ return;
48570
+ }
48571
+ const workOrderId = workOrder.id;
48572
+ if (typeof workOrderId !== "string") {
48573
+ return;
48574
+ }
48575
+ const started = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/start`, {}, state.runner_secret);
48576
+ if (started.status !== 200) {
48577
+ throw new Error(`work order start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
48578
+ }
48579
+ const result = await runClaudeTurn(appUrl2, workOrder);
48580
+ const reported = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/result`, {
48581
+ status: result.ok ? "succeeded" : "failed",
48582
+ summary: result.summary,
48583
+ log_ref: `runner-local:${workOrderId}`,
48584
+ model: "claude-cli"
48585
+ }, state.runner_secret);
48586
+ if (reported.status !== 200) {
48587
+ throw new Error(`work order result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
48588
+ }
48589
+ io.stdout.write(`work_order_id=${workOrderId} status=${result.ok ? "succeeded" : "failed"}
48590
+ `);
48591
+ }
48592
+ async function runClaudeTurn(appUrl2, workOrder) {
48593
+ const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
48594
+ const token = typeof mcp.token === "string" ? mcp.token : "";
48595
+ const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
48596
+ const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
48597
+ const configPath = path3.join(tmpdir(), `rost-runner-mcp-${createHash("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
48598
+ const context = {
48599
+ work_order_id: workOrder.id,
48600
+ task_id: workOrder.task_id ?? null,
48601
+ seat: workOrder.seat ?? null,
48602
+ charter: workOrder.charter ?? null,
48603
+ permission_manifest: workOrder.permission_manifest ?? null
48604
+ };
48605
+ const prompt = [
48606
+ `Load your ${cliBrand.name} context, report one read-only status, and stop.`,
48607
+ "Do not call external systems or mutating tools.",
48608
+ `Claimed work order context: ${JSON.stringify(context).slice(0, 8e3)}`
48609
+ ].join("\n");
48610
+ await writeFile2(configPath, `${mcpConfig}
48611
+ `, { mode: 384 });
48612
+ await chmod(configPath, 384);
48613
+ return new Promise((resolve2) => {
48614
+ let settled = false;
48615
+ const finish = (result) => {
48616
+ if (settled) {
48617
+ return;
48618
+ }
48619
+ settled = true;
48620
+ void rm2(configPath, { force: true });
48621
+ resolve2(result);
48622
+ };
48623
+ const child = spawn("claude", ["-p", prompt, "--mcp-config", configPath, "--strict-mcp-config", "--output-format", "text"], {
48624
+ cwd: tmpdir(),
48625
+ stdio: ["ignore", "pipe", "pipe"]
48626
+ });
48627
+ let out = "";
48628
+ let err = "";
48629
+ const timer = setTimeout(() => {
48630
+ child.kill("SIGKILL");
48631
+ finish({ ok: false, summary: "turn timed out after 180s" });
48632
+ }, 18e4);
48633
+ child.stdout.on("data", (data) => {
48634
+ out += data.toString("utf8");
48635
+ });
48636
+ child.stderr.on("data", (data) => {
48637
+ err += data.toString("utf8");
48638
+ });
48639
+ child.on("close", (code) => {
48640
+ clearTimeout(timer);
48641
+ finish({ ok: code === 0, summary: redactForLog((out || err || "completed").trim()).slice(0, 600) });
48642
+ });
48643
+ child.on("error", (error51) => {
48644
+ clearTimeout(timer);
48645
+ finish({ ok: false, summary: `spawn error: ${redactForLog(error51.message)}` });
48646
+ });
48647
+ });
48648
+ }
48649
+ function usage() {
48650
+ return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--once] [--execute]";
48651
+ }
48652
+
48067
48653
  // src/index.ts
48068
48654
  async function main(argv = process.argv.slice(2), options = {}) {
48069
48655
  const io = options.io ?? {
@@ -48117,6 +48703,13 @@ async function main(argv = process.argv.slice(2), options = {}) {
48117
48703
  }
48118
48704
  return result.exitCode;
48119
48705
  }
48706
+ if (command === "doctor") {
48707
+ return runDoctor({
48708
+ io,
48709
+ env: process.env,
48710
+ ...options.store ? { store: options.store } : {}
48711
+ });
48712
+ }
48120
48713
  if (command === "onboard" && args[0] === "run") {
48121
48714
  io.stdout.write(`${renderOnboardRunPrompt()}
48122
48715
  `);
@@ -48172,12 +48765,35 @@ async function main(argv = process.argv.slice(2), options = {}) {
48172
48765
  `);
48173
48766
  justLoggedIn = true;
48174
48767
  }
48768
+ const config2 = loadConfig();
48769
+ if (command === "runner" && args[0] === "serve") {
48770
+ let runnerClient = null;
48771
+ if (session) {
48772
+ try {
48773
+ const refreshed2 = await refreshSession(config2, session);
48774
+ if (refreshed2 !== session) {
48775
+ session = refreshed2;
48776
+ await store.write(session);
48777
+ }
48778
+ runnerClient = new CommandClient({ appUrl: config2.appUrl, token: session.accessToken });
48779
+ } catch (error51) {
48780
+ io.stderr.write(`CLI session unavailable; trying paired runner state only: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48781
+ `);
48782
+ }
48783
+ }
48784
+ return runRunnerServe({
48785
+ io,
48786
+ client: runnerClient,
48787
+ appUrl: config2.appUrl,
48788
+ args: args.slice(1),
48789
+ env: process.env
48790
+ });
48791
+ }
48175
48792
  if (!session) {
48176
48793
  io.stderr.write(`Not logged in. Run ${cliBrand.binName} login first.
48177
48794
  `);
48178
48795
  return 1;
48179
48796
  }
48180
- const config2 = loadConfig();
48181
48797
  let refreshed;
48182
48798
  try {
48183
48799
  refreshed = await refreshSession(config2, session);
@@ -48294,9 +48910,10 @@ async function executeDirectCommand(io, client, args) {
48294
48910
  }
48295
48911
  const commandId = first;
48296
48912
  let seat;
48913
+ let autoConfirm = false;
48297
48914
  let rest;
48298
48915
  try {
48299
- ({ seat, rest } = extractSeatFlag(args.slice(1)));
48916
+ ({ seat, autoConfirm, rest } = extractCommandMetaFlags(args.slice(1)));
48300
48917
  } catch (error51) {
48301
48918
  io.stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48302
48919
  `);
@@ -48317,11 +48934,15 @@ async function executeDirectCommand(io, client, args) {
48317
48934
  `);
48318
48935
  return 1;
48319
48936
  }
48320
- return printCommandOutput(io, client, commandId, body, void 0, seat === void 0 ? {} : { targetSeatId: seat });
48937
+ return printCommandOutput(io, client, commandId, body, void 0, {
48938
+ ...seat === void 0 ? {} : { targetSeatId: seat },
48939
+ autoConfirm
48940
+ });
48321
48941
  }
48322
- function extractSeatFlag(args) {
48942
+ function extractCommandMetaFlags(args) {
48323
48943
  const rest = [];
48324
48944
  let seat;
48945
+ let autoConfirm = false;
48325
48946
  for (let index = 0; index < args.length; index += 1) {
48326
48947
  const token = args[index];
48327
48948
  if (token === "--seat") {
@@ -48333,11 +48954,15 @@ function extractSeatFlag(args) {
48333
48954
  index += 1;
48334
48955
  continue;
48335
48956
  }
48957
+ if (token === "--yes" || token === "-y") {
48958
+ autoConfirm = true;
48959
+ continue;
48960
+ }
48336
48961
  if (token !== void 0) {
48337
48962
  rest.push(token);
48338
48963
  }
48339
48964
  }
48340
- return { ...seat === void 0 ? {} : { seat }, rest };
48965
+ return { ...seat === void 0 ? {} : { seat }, autoConfirm, rest };
48341
48966
  }
48342
48967
  var SeatFlagError = class extends Error {
48343
48968
  };
@@ -48352,7 +48977,9 @@ function formatCommandSchema(output) {
48352
48977
  if (typeof help === "string" && help.length > 0) {
48353
48978
  lines.push(`help: ${help}`);
48354
48979
  }
48355
- lines.push("", "input_schema:", JSON.stringify(record2.input_schema, null, 2));
48980
+ const schemaSummary = formatInputSchemaSummary(record2.input_schema);
48981
+ lines.push("", "input:");
48982
+ lines.push(schemaSummary || " no input fields");
48356
48983
  if (record2.example !== void 0) {
48357
48984
  lines.push("", "example input:", JSON.stringify(record2.example, null, 2));
48358
48985
  }
@@ -48366,6 +48993,23 @@ function formatCommandSchema(output) {
48366
48993
  }
48367
48994
  return lines.join("\n");
48368
48995
  }
48996
+ function formatInputSchemaSummary(schema) {
48997
+ const root = asRecord3(schema);
48998
+ const properties = asRecord3(root.properties);
48999
+ const required2 = new Set(asStringArray(root.required));
49000
+ const entries = Object.entries(properties);
49001
+ if (entries.length === 0) {
49002
+ return "";
49003
+ }
49004
+ return entries.map(([key, value]) => {
49005
+ const prop = asRecord3(value);
49006
+ const type = Array.isArray(prop.type) ? prop.type.join("|") : field2(prop, "type");
49007
+ const enumValues = asStringArray(prop.enum);
49008
+ const description = typeof prop.description === "string" ? ` \u2014 ${prop.description}` : "";
49009
+ const enumText = enumValues.length > 0 ? ` enum=${enumValues.join("|")}` : "";
49010
+ return ` ${key}${required2.has(key) ? " (required)" : " (optional)"}: ${type}${enumText}${description}`;
49011
+ }).join("\n");
49012
+ }
48369
49013
  function formatCommandList(output) {
48370
49014
  const commands = Array.isArray(asRecord3(output).commands) ? asRecord3(output).commands : [];
48371
49015
  if (commands.length === 0) {
@@ -48492,10 +49136,12 @@ async function printCommandOutput(io, client, commandId, body = {}, format, opti
48492
49136
  `);
48493
49137
  return 0;
48494
49138
  } catch (error51) {
48495
- if (io.interactive === true) {
49139
+ const envAutoConfirm = process.env[`${cliBrand.envPrefix}_CLI_AUTO_CONFIRM`] === "1";
49140
+ if (io.interactive === true || options.autoConfirm === true || envAutoConfirm) {
48496
49141
  const pending = pendingConfirmationFromError2(error51);
48497
49142
  if (pending && pending.commandId === commandId) {
48498
- io.stderr.write(`Approving ${pending.commandId} confirmation ${pending.confirmationId} as the interactive CLI user.
49143
+ const reason = io.interactive === true ? "interactive CLI user" : options.autoConfirm === true ? "explicit CLI auto-confirm" : `${cliBrand.envPrefix}_CLI_AUTO_CONFIRM environment auto-confirm`;
49144
+ io.stderr.write(`Approving ${pending.commandId} confirmation ${pending.confirmationId} as the ${reason}.
48499
49145
  `);
48500
49146
  try {
48501
49147
  const approved = await client.execute("confirmation.approve", { confirmation_id: pending.confirmationId });
@@ -48540,6 +49186,14 @@ function printCommandError2(io, error51) {
48540
49186
  io.stderr.write(`\u2192 try: ${redactForLog(help)}
48541
49187
  `);
48542
49188
  }
49189
+ if (error51.response.error.code === "COMMAND_CONFIRMATION_REQUIRED") {
49190
+ const details = asRecord3(error51.response.error.details);
49191
+ const expiresAt = details.expiresAt;
49192
+ if (typeof expiresAt === "string" && expiresAt.length > 0) {
49193
+ io.stderr.write(`confirmation expires at: ${redactForLog(expiresAt)}
49194
+ `);
49195
+ }
49196
+ }
48543
49197
  if (error51.status === 409) {
48544
49198
  return 3;
48545
49199
  }
@@ -48561,7 +49215,7 @@ function parseJsonBody(args) {
48561
49215
  return JSON.parse(body);
48562
49216
  }
48563
49217
  function isCommand(value) {
48564
- return value === "login" || value === "logout" || value === "signup" || value === "whoami" || value === "tenants" || value === "use" || value === "command" || value === "onboard" || value === "mcp" || value === "skills" || value === "init" || value === "docs" || value === "reference" || value === "help";
49218
+ return value === "login" || value === "logout" || value === "signup" || value === "whoami" || value === "tenants" || value === "use" || value === "command" || value === "onboard" || value === "mcp" || value === "skills" || value === "init" || value === "docs" || value === "reference" || value === "doctor" || value === "help";
48565
49219
  }
48566
49220
  function printUsage(io) {
48567
49221
  io.stdout.write([
@@ -48575,6 +49229,7 @@ function printUsage(io) {
48575
49229
  `${cliBrand.binName} command <id> [--seat <seat-id>] --json [json-body]`,
48576
49230
  `${cliBrand.binName} command schema <id> [--json]`,
48577
49231
  `${cliBrand.binName} command list [--json]`,
49232
+ `${cliBrand.binName} doctor`,
48578
49233
  ...operationUsageLines(cliBrand.binName),
48579
49234
  `${cliBrand.binName} onboard status`,
48580
49235
  `${cliBrand.binName} onboard run`,