@rosthq/cli 0.7.10 → 0.7.11

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
@@ -47455,7 +47455,7 @@ The Compass is drafted, then activated by a human through supersession.
47455
47455
  order: 15,
47456
47456
  title: "AICOS chat guide",
47457
47457
  summary: "How the AI Chief of Staff chat works in the authenticated app shell and what it can safely do today.",
47458
- version: "2026-07-03.2",
47458
+ version: "2026-07-03.3",
47459
47459
  public: true,
47460
47460
  audiences: ["human", "in_app_agent"],
47461
47461
  stages: ["company_setup", "operating_rhythm"],
@@ -47499,7 +47499,7 @@ You can reopen prior threads, search thread titles and summaries, or start a new
47499
47499
 
47500
47500
  New companies receive the governed AICOS foundation during provisioning. Existing companies can also set it up later from **Settings -> AICOS**. Owners and admins can see whether the foundation exists, repair it if needed, and choose the run location after setup.
47501
47501
 
47502
- The repair action ensures the same structure new companies receive: a Founder/CEO root seat, an owner occupancy for stewardship, an **AI Chief of Staff** seat, the stock AICOS agent, and an active agent occupancy. It is idempotent; running it again repairs missing pieces instead of creating a second AICOS.
47502
+ The repair action ensures the same structure new companies receive: a Founder/CEO root seat, an owner occupancy for stewardship, an **AI Chief of Staff** seat, the stock AICOS agent, and one active agent occupancy. It is idempotent; running it again repairs missing pieces instead of creating a second AICOS. If older setup attempts left more than one active AICOS occupancy, the repair path keeps the best current stock AICOS agent \u2014 live before dry-run, draft, or paused \u2014 and ends the extra AICOS occupancies so Settings, lane readiness, and chat resolve the same governed agent.
47503
47503
 
47504
47504
  Settings is the infrastructure surface for AICOS. Use it to confirm foundation status, inspect the current lane, see whether a seat-scoped MCP token exists, and choose **Cloud**, **Runner**, or **MCP** when the server-side readiness checks allow it. The richer agent controls remain with the agent itself: open the AICOS seat from Settings or the agent fleet to manage Charter, tool permissions, approvals, go-live state, and run evidence.
47505
47505
 
@@ -48330,7 +48330,7 @@ External connectors are being rolled out provider by provider, conservatively (r
48330
48330
  order: 48,
48331
48331
  title: "CLI and MCP installation guide",
48332
48332
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
48333
- version: "2026-07-03.9",
48333
+ version: "2026-07-03.10",
48334
48334
  public: true,
48335
48335
  audiences: ["human", "cli", "mcp", "in_app_agent"],
48336
48336
  stages: ["company_setup", "staffing"],
@@ -49063,6 +49063,8 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
49063
49063
  | \`rost_create_a_forge_github_installation_token\` | \`software_factory.github.installation_token.create\` | Mint a short-lived GitHub App installation token for one bound repository after policy evaluation. The token is returned once and never stored. | Tenant | Call with \`{"software_project_id":"<project-id>","repository_id":789,"requested_actions":["read"],"requested_automation_mode":"plan_only"}\`. |
49064
49064
  | \`rost_list_forge_runner_capacity\` | \`software_factory.capacity.list\` | List Forge runner capacity observations (advisory scheduling input only, never an authority input). Read-only; requires the Forge add-on. | Tenant | Call with \`{}\` or \`{"runner_id":"<runner-id>"}\`. |
49065
49065
 
49066
+ The web Forge control room at \`/forge\` uses the same command path for project selection, build request creation/list/detail reads, runner capacity, configuration status, and Developer Team template install. The template install button deliberately stages a pending confirmation before durable seats, Skills, or authority grants are applied.
49067
+
49066
49068
  #### Forge GitHub App access
49067
49069
 
49068
49070
  Forge repository access is through a tenant-bound GitHub App installation, not through a human's ambient \`git\` or \`gh\` credentials. The server verifies GitHub webhook signatures before parsing payloads, resolves the installation and repository against active tenant bindings, records delivery ids for idempotency, and creates GitHub issue build requests only when an active repo binding and policy allow intake.
@@ -49410,7 +49412,7 @@ Set \`ROST_SKILL_INSTALL_ROOT\` only for a sandbox or CI override. Local copies
49410
49412
  order: 55,
49411
49413
  title: "Agent reference map",
49412
49414
  summary: "Where CLI sessions, MCP clients, and in-app agents should retrieve {{brand}} guidance before recommending setup changes.",
49413
- version: "2026-07-03.1",
49415
+ version: "2026-06-27.2",
49414
49416
  public: true,
49415
49417
  audiences: ["cli", "mcp", "in_app_agent"],
49416
49418
  stages: ["company_setup", "graph_design", "charter_design", "staffing", "operating_rhythm"],
@@ -49907,7 +49909,7 @@ Keep agent scope tight at first. Approve more autonomy only after evidence. Use
49907
49909
  order: 71,
49908
49910
  title: "Confirmations and human gates guide",
49909
49911
  summary: "How {{brand}} routes authority-changing work through human confirmation, and why agents never approve their own requests.",
49910
- version: "2026-06-27.2",
49912
+ version: "2026-07-03.1",
49911
49913
  public: true,
49912
49914
  audiences: ["human", "cli", "mcp", "in_app_agent"],
49913
49915
  stages: ["graph_design", "charter_design", "staffing", "operating_rhythm"],
@@ -49944,6 +49946,8 @@ A gated command does not mutate when an agent calls it. It returns a pending con
49944
49946
 
49945
49947
  When an **agent** triggers a gate, {{brand}} also notifies the seat's steward (Slack with an email fallback) so the held action does not sit silent \u2014 a human is told a decision is waiting and given the approve link. A human pausing their own gated command does not self-notify. See the notifications guide.
49946
49948
 
49949
+ Approval replays are validated again before execution. If the stored confirmation input no longer matches the original command schema, the approval fails without consuming the pending confirmation or running the durable command; reject it and re-issue the gated action from the current product surface.
49950
+
49947
49951
  \`\`\`bash
49948
49952
  # Human approves a pending confirmation
49949
49953
  {{cli}} command confirmation.approve --json '{"confirmation_id":"<confirmation-id>"}'
@@ -50169,7 +50173,7 @@ Agents may explain which setting is needed and why. They should not ask users to
50169
50173
  order: 73,
50170
50174
  title: "Members and invites guide",
50171
50175
  summary: "How to invite humans into the company without confusing user access with seat accountability.",
50172
- version: "2026-07-03.1",
50176
+ version: "2026-07-03.2",
50173
50177
  public: true,
50174
50178
  audiences: ["human", "cli", "mcp", "in_app_agent"],
50175
50179
  stages: ["company_setup", "staffing"],
@@ -50248,7 +50252,7 @@ Every notification should include the seat, cause, evidence, and requested decis
50248
50252
  order: 75,
50249
50253
  title: "Local runner guide",
50250
50254
  summary: "How local agent sessions and runner surfaces should operate through {{brand}} without bypassing Charters or audit.",
50251
- version: "2026-07-03.1",
50255
+ version: "2026-07-03.2",
50252
50256
  public: true,
50253
50257
  audiences: ["human", "cli", "mcp", "in_app_agent"],
50254
50258
  stages: ["staffing", "operating_rhythm"],
@@ -50280,13 +50284,13 @@ Use Local Runner when a Seat should run on a machine you control. Install it fro
50280
50284
 
50281
50285
  Recommended paths:
50282
50286
 
50283
- - **Always-on host:** \`{{cli}} runner install-service --name <runner-name> --execute\` installs a background service (macOS launchd user agent, \`user/<uid>\` domain) that keeps the runner online. Manage it with \`{{cli}} runner start|stop|restart|status|logs|uninstall --name <runner-name>\`. On macOS this loads reliably when the runner user is logged in at the console; on a purely headless SSH box where that user is not at the console, run \`{{cli}} runner serve\` under a process supervisor instead.
50287
+ - **Always-on host:** \`{{cli}} runner install-service --name <runner-name> --execute\` installs a background service (macOS launchd user agent) that keeps the runner online. Manage it with \`{{cli}} runner start|stop|restart|status|logs|uninstall --name <runner-name>\`. On macOS the CLI tries both \`user/<uid>\` and \`gui/<uid>\` launchd domains because session behavior differs across console and SSH contexts; \`runner status --name <runner-name>\` prints the loaded domain.
50284
50288
  - **Manual smoke:** \`{{cli}} runner serve --once\` heartbeats once and exits; \`{{cli}} runner serve --execute\` claims and executes runner work orders while it runs.
50285
50289
 
50286
50290
  Two ways to authenticate and pair \u2014 both first-class, an agent can drive either entirely from the terminal:
50287
50291
 
50288
50292
  - **CLI-native (agent):** \`{{cli}} login --device\` prints a code for one human OAuth approval; after that the session is stored locally and the agent runs unattended ("approve once, then autonomous"). Then \`{{cli}} runner serve\`/\`install-service\` pairs against your active company \u2014 an authenticated CLI can also self-issue a pairing code with \`runner.pairing.start\` and claim it.
50289
- - **Web-guided (human):** issue a pairing code from Settings > Runners > Add a Runner, then on the host run \`{{cli}} runner serve --user-code <code>\`. The host stores only the runner bearer secret in an owner-only file and never logs in.
50293
+ - **Web-guided (human):** issue a pairing code from Settings > Runners > Add a Runner, then on the host run \`{{cli}} runner serve --user-code <code>\`. An explicit user code always claims that code, even if the host has an older local runner state file, so the browser and terminal do not accidentally point at different runner ids. The host stores only the runner bearer secret in an owner-only file and never logs in.
50290
50294
 
50291
50295
  Headless and service accounts are supported first-class: when no usable OS keychain exists (for example a service or SSH account), the CLI stores the session in an owner-only file (directory 0700, file 0600) automatically \u2014 no development flag. That file holds only the CLI session token, never tenant secrets, API keys, or vault refs.
50292
50296
 
@@ -54927,11 +54931,13 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
54927
54931
  config2.expectedTenantId = expectedTenantId;
54928
54932
  }
54929
54933
  }
54930
- const existing = await loadState(config2.stateFile, config2.expectedTenantId);
54931
- if (existing) {
54932
- io.stdout.write(`reusing paired runner ${existing.runner_id}
54934
+ if (config2.userCode === void 0) {
54935
+ const existing = await loadState(config2.stateFile, config2.expectedTenantId);
54936
+ if (existing) {
54937
+ io.stdout.write(`reusing paired runner ${existing.runner_id}
54933
54938
  `);
54934
- return existing;
54939
+ return existing;
54940
+ }
54935
54941
  }
54936
54942
  let userCode;
54937
54943
  if (config2.userCode !== void 0) {
@@ -55286,7 +55292,12 @@ async function installService(options) {
55286
55292
  await writeFile3(paths.launchAgentPath, plist, { mode: 420 });
55287
55293
  await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
55288
55294
  try {
55289
- await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
55295
+ const bootstrapped = await bootstrapService(options.exec, options.uid, paths.launchAgentPath);
55296
+ options.io.stdout.write(
55297
+ bootstrapped.alreadyLoaded ? `Runner service was already loaded in launchd domain ${bootstrapped.domain}.
55298
+ ` : `Loaded runner service in launchd domain ${bootstrapped.domain}.
55299
+ `
55300
+ );
55290
55301
  } catch (error51) {
55291
55302
  options.io.stderr.write(
55292
55303
  `Failed to load the runner service: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
@@ -55321,15 +55332,13 @@ async function startService(options) {
55321
55332
  );
55322
55333
  return 1;
55323
55334
  }
55335
+ let bootstrapped;
55324
55336
  try {
55325
- await options.exec("launchctl", ["bootstrap", domainTarget(options.uid), paths.launchAgentPath]);
55326
- options.io.stdout.write(`Started runner service ${label}.
55327
- `);
55328
- return 0;
55329
- } catch (bootstrapError) {
55337
+ bootstrapped = await bootstrapService(options.exec, options.uid, paths.launchAgentPath);
55338
+ } catch {
55330
55339
  try {
55331
- await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
55332
- options.io.stdout.write(`Runner service ${label} was already loaded; restarted it.
55340
+ const target2 = await kickstartService(options.exec, options.uid, label);
55341
+ options.io.stdout.write(`Runner service ${label} was already loaded at ${target2}; restarted it.
55333
55342
  `);
55334
55343
  return 0;
55335
55344
  } catch (kickstartError) {
@@ -55340,6 +55349,24 @@ async function startService(options) {
55340
55349
  return 1;
55341
55350
  }
55342
55351
  }
55352
+ if (!bootstrapped.alreadyLoaded) {
55353
+ options.io.stdout.write(`Started runner service ${label} in launchd domain ${bootstrapped.domain}.
55354
+ `);
55355
+ return 0;
55356
+ }
55357
+ const target = `${bootstrapped.domain}/${label}`;
55358
+ try {
55359
+ await kickstartServiceTarget(options.exec, target);
55360
+ options.io.stdout.write(`Runner service ${label} was already loaded at ${target}; restarted it.
55361
+ `);
55362
+ return 0;
55363
+ } catch (kickstartError) {
55364
+ options.io.stderr.write(
55365
+ `Failed to start runner service ${label}: ${redactForLog(kickstartError instanceof Error ? kickstartError.message : String(kickstartError))}
55366
+ `
55367
+ );
55368
+ return 1;
55369
+ }
55343
55370
  }
55344
55371
  async function stopService(options) {
55345
55372
  if (options.platform !== "darwin") {
@@ -55348,11 +55375,11 @@ async function stopService(options) {
55348
55375
  const safeName = safeRunnerName(options.runnerName);
55349
55376
  const paths = runnerServicePaths(options.homeDir, safeName);
55350
55377
  const label = serviceLabel(safeName);
55351
- try {
55352
- await options.exec("launchctl", ["bootout", domainTarget(options.uid), paths.launchAgentPath]);
55378
+ const stopped = await bootoutAllDomains(options.exec, options.uid, paths.launchAgentPath);
55379
+ if (stopped) {
55353
55380
  options.io.stdout.write(`Stopped runner service ${label}.
55354
55381
  `);
55355
- } catch {
55382
+ } else {
55356
55383
  options.io.stdout.write(`Runner service ${label} was not running.
55357
55384
  `);
55358
55385
  }
@@ -55365,8 +55392,8 @@ async function restartService(options) {
55365
55392
  const safeName = safeRunnerName(options.runnerName);
55366
55393
  const label = serviceLabel(safeName);
55367
55394
  try {
55368
- await options.exec("launchctl", ["kickstart", "-k", serviceTarget(options.uid, label)]);
55369
- options.io.stdout.write(`Restarted runner service ${label}.
55395
+ const target = await kickstartService(options.exec, options.uid, label);
55396
+ options.io.stdout.write(`Restarted runner service ${label} at ${target}.
55370
55397
  `);
55371
55398
  return 0;
55372
55399
  } catch (error51) {
@@ -55386,13 +55413,14 @@ async function statusService(options) {
55386
55413
  const label = serviceLabel(safeName);
55387
55414
  const installed = await fileExists(paths.launchAgentPath);
55388
55415
  try {
55389
- const { stdout } = await options.exec("launchctl", ["print", serviceTarget(options.uid, label)]);
55390
- const state = extractLaunchdState(stdout);
55416
+ const printed = await printService(options.exec, options.uid, label);
55417
+ const state = extractLaunchdState(printed.stdout);
55391
55418
  options.io.stdout.write(
55392
55419
  `${[
55393
55420
  `runner service: ${label}`,
55394
55421
  `installed: ${installed ? "yes" : "no"}`,
55395
55422
  `loaded: yes`,
55423
+ `domain: ${printed.target}`,
55396
55424
  `state: ${state ?? "unknown"}`,
55397
55425
  `plist: ${paths.launchAgentPath}`,
55398
55426
  `logs: ${paths.logDir}`
@@ -55510,17 +55538,72 @@ function safeRunnerName(runnerName) {
55510
55538
  function serviceLabel(safeName) {
55511
55539
  return `com.${cliBrand.binName}.runner.${safeName}`;
55512
55540
  }
55513
- function domainTarget(uid) {
55514
- return `user/${uid}`;
55541
+ function launchdDomains(uid) {
55542
+ return [`user/${uid}`, `gui/${uid}`];
55515
55543
  }
55516
- function serviceTarget(uid, label) {
55517
- return `user/${uid}/${label}`;
55544
+ function serviceTargets(uid, label) {
55545
+ return launchdDomains(uid).map((domain2) => `${domain2}/${label}`);
55518
55546
  }
55519
55547
  async function bootoutQuietly(exec, uid, plistPath) {
55520
- try {
55521
- await exec("launchctl", ["bootout", domainTarget(uid), plistPath]);
55522
- } catch {
55548
+ await bootoutAllDomains(exec, uid, plistPath);
55549
+ }
55550
+ async function bootoutAllDomains(exec, uid, plistPath) {
55551
+ let bootedOut = false;
55552
+ for (const domain2 of launchdDomains(uid)) {
55553
+ try {
55554
+ await exec("launchctl", ["bootout", domain2, plistPath]);
55555
+ bootedOut = true;
55556
+ } catch {
55557
+ }
55558
+ }
55559
+ return bootedOut;
55560
+ }
55561
+ async function bootstrapService(exec, uid, plistPath) {
55562
+ const errors = [];
55563
+ for (const domain2 of launchdDomains(uid)) {
55564
+ try {
55565
+ await exec("launchctl", ["bootstrap", domain2, plistPath]);
55566
+ return { domain: domain2, alreadyLoaded: false };
55567
+ } catch (error51) {
55568
+ const message = error51 instanceof Error ? error51.message : String(error51);
55569
+ if (isLaunchdAlreadyLoadedError(message)) {
55570
+ return { domain: domain2, alreadyLoaded: true };
55571
+ }
55572
+ errors.push(`${domain2}: ${message}`);
55573
+ }
55574
+ }
55575
+ throw new Error(errors.join("; "));
55576
+ }
55577
+ function isLaunchdAlreadyLoadedError(message) {
55578
+ const normalized = message.toLowerCase();
55579
+ return normalized.includes("service is already loaded") || normalized.includes("service already loaded") || normalized.includes("already bootstrapped") || normalized.includes("already exists");
55580
+ }
55581
+ async function kickstartService(exec, uid, label) {
55582
+ const errors = [];
55583
+ for (const target of serviceTargets(uid, label)) {
55584
+ try {
55585
+ await kickstartServiceTarget(exec, target);
55586
+ return target;
55587
+ } catch (error51) {
55588
+ errors.push(`${target}: ${error51 instanceof Error ? error51.message : String(error51)}`);
55589
+ }
55590
+ }
55591
+ throw new Error(errors.join("; "));
55592
+ }
55593
+ async function kickstartServiceTarget(exec, target) {
55594
+ await exec("launchctl", ["kickstart", "-k", target]);
55595
+ }
55596
+ async function printService(exec, uid, label) {
55597
+ const errors = [];
55598
+ for (const target of serviceTargets(uid, label)) {
55599
+ try {
55600
+ const { stdout } = await exec("launchctl", ["print", target]);
55601
+ return { target, stdout };
55602
+ } catch (error51) {
55603
+ errors.push(`${target}: ${error51 instanceof Error ? error51.message : String(error51)}`);
55604
+ }
55523
55605
  }
55606
+ throw new Error(errors.join("; "));
55524
55607
  }
55525
55608
  function unsupportedPlatform(io, platform2) {
55526
55609
  io.stderr.write(