@indigoai-us/hq-cli 5.77.13 → 5.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
  3. package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
  4. package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
  5. package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
  6. package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
  7. package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
  8. package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
  9. package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
  10. package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
  11. package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
  12. package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
  13. package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
  14. package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
  15. package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
  16. package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
  17. package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
  18. package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
  19. package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
  20. package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
  21. package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
  22. package/assets/scaffold/core/scripts/worktree.sh +136 -0
  23. package/dist/commands/agents.js +28 -12
  24. package/dist/commands/api-keys.js +100 -20
  25. package/dist/commands/core.d.ts +90 -0
  26. package/dist/commands/core.js +279 -0
  27. package/dist/commands/files-browse.d.ts +21 -0
  28. package/dist/commands/files-browse.js +145 -24
  29. package/dist/commands/files.js +33 -7
  30. package/dist/commands/members.js +16 -5
  31. package/dist/commands/pack-install.d.ts +14 -0
  32. package/dist/commands/pack-install.js +51 -2
  33. package/dist/commands/pkg-install.js +43 -0
  34. package/dist/commands/run.js +8 -0
  35. package/dist/commands/secrets.js +125 -65
  36. package/dist/main.js +6 -0
  37. package/dist/utils/api-key-command-gate.d.ts +45 -0
  38. package/dist/utils/api-key-command-gate.js +51 -0
  39. package/dist/utils/hq-roots.d.ts +91 -0
  40. package/dist/utils/hq-roots.js +212 -0
  41. package/dist/utils/resolve-vault-credential.d.ts +63 -3
  42. package/dist/utils/resolve-vault-credential.js +152 -6
  43. package/dist/utils/run-bundled-script.d.ts +66 -0
  44. package/dist/utils/run-bundled-script.js +80 -0
  45. package/dist/utils/vault-api.d.ts +21 -0
  46. package/dist/utils/vault-api.js +74 -3
  47. package/package.json +7 -1
  48. package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
  49. package/.github/workflows/ci.yml +0 -46
  50. package/.github/workflows/publish.yml +0 -36
  51. package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
  52. package/e2e/cli.test.ts +0 -128
  53. package/eslint.config.js +0 -37
  54. package/pnpm-workspace.yaml +0 -4
  55. package/scripts/chmod-bins.mjs +0 -49
  56. package/scripts/generate-dsn.mjs +0 -26
  57. package/scripts/generate-scan-packages-table.mjs +0 -113
  58. package/scripts/smoke-sources-signals.sh +0 -103
  59. package/src/bin/hq-auth-refresh.ts +0 -43
  60. package/src/cli-version.ts +0 -13
  61. package/src/commands/__fixtures__/make-tar.ts +0 -126
  62. package/src/commands/_patterns.ts +0 -10
  63. package/src/commands/add.ts +0 -75
  64. package/src/commands/agents.test.ts +0 -527
  65. package/src/commands/agents.ts +0 -923
  66. package/src/commands/api-keys.test.ts +0 -291
  67. package/src/commands/api-keys.ts +0 -382
  68. package/src/commands/artifact-verify.test.ts +0 -177
  69. package/src/commands/auth.test.ts +0 -82
  70. package/src/commands/auth.ts +0 -213
  71. package/src/commands/billing.test.ts +0 -158
  72. package/src/commands/billing.ts +0 -146
  73. package/src/commands/channels.test.ts +0 -74
  74. package/src/commands/channels.ts +0 -91
  75. package/src/commands/cloud-demote.test.ts +0 -401
  76. package/src/commands/cloud-demote.ts +0 -278
  77. package/src/commands/cloud-provision.test.ts +0 -1117
  78. package/src/commands/cloud-provision.ts +0 -942
  79. package/src/commands/cloud.pull-all.test.ts +0 -711
  80. package/src/commands/cloud.pull-per-company.test.ts +0 -188
  81. package/src/commands/cloud.push-all.test.ts +0 -485
  82. package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
  83. package/src/commands/cloud.selectors.test.ts +0 -208
  84. package/src/commands/cloud.status.test.ts +0 -160
  85. package/src/commands/cloud.test.ts +0 -41
  86. package/src/commands/cloud.ts +0 -2130
  87. package/src/commands/company.test.ts +0 -202
  88. package/src/commands/company.ts +0 -148
  89. package/src/commands/creators.test.ts +0 -60
  90. package/src/commands/creators.ts +0 -117
  91. package/src/commands/crm.test.ts +0 -179
  92. package/src/commands/crm.ts +0 -236
  93. package/src/commands/db-migrate.ts +0 -55
  94. package/src/commands/db-provision.ts +0 -114
  95. package/src/commands/db-sql.ts +0 -124
  96. package/src/commands/db-status.ts +0 -108
  97. package/src/commands/db.ts +0 -26
  98. package/src/commands/dm.test.ts +0 -748
  99. package/src/commands/dm.ts +0 -1226
  100. package/src/commands/feedback.test.ts +0 -413
  101. package/src/commands/feedback.ts +0 -169
  102. package/src/commands/files-browse.test.ts +0 -1249
  103. package/src/commands/files-browse.ts +0 -1506
  104. package/src/commands/files-delete.test.ts +0 -484
  105. package/src/commands/files-recovery.test.ts +0 -370
  106. package/src/commands/files.test.ts +0 -722
  107. package/src/commands/files.ts +0 -1446
  108. package/src/commands/group-grants.test.ts +0 -330
  109. package/src/commands/group-grants.ts +0 -455
  110. package/src/commands/groups.test.ts +0 -44
  111. package/src/commands/groups.ts +0 -368
  112. package/src/commands/integrations.test.ts +0 -633
  113. package/src/commands/integrations.ts +0 -507
  114. package/src/commands/list.ts +0 -85
  115. package/src/commands/login.ts +0 -52
  116. package/src/commands/logout.ts +0 -30
  117. package/src/commands/marketplace-install.test.ts +0 -543
  118. package/src/commands/marketplace-security.test.ts +0 -646
  119. package/src/commands/mcp-registration.test.ts +0 -2787
  120. package/src/commands/mcp-registration.ts +0 -2612
  121. package/src/commands/mcp-status.test.ts +0 -483
  122. package/src/commands/mcp-status.ts +0 -575
  123. package/src/commands/mcp-status.us011.test.ts +0 -243
  124. package/src/commands/meetings.test.ts +0 -522
  125. package/src/commands/meetings.ts +0 -789
  126. package/src/commands/members.test.ts +0 -1474
  127. package/src/commands/members.ts +0 -1181
  128. package/src/commands/onboard-identity-guard.test.ts +0 -99
  129. package/src/commands/onboard-identity-guard.ts +0 -103
  130. package/src/commands/onboard-join.test.ts +0 -96
  131. package/src/commands/onboard-join.ts +0 -101
  132. package/src/commands/onboard-warning.test.ts +0 -26
  133. package/src/commands/onboard-warning.ts +0 -12
  134. package/src/commands/onboard.ts +0 -267
  135. package/src/commands/outposts-heartbeat.test.ts +0 -299
  136. package/src/commands/outposts-heartbeat.ts +0 -310
  137. package/src/commands/outposts-replica-sync.test.ts +0 -272
  138. package/src/commands/outposts-self-deploy.test.ts +0 -243
  139. package/src/commands/outposts.test.ts +0 -1039
  140. package/src/commands/outposts.ts +0 -1935
  141. package/src/commands/pack-install-secret-authorization.test.ts +0 -115
  142. package/src/commands/pack-install.test.ts +0 -1469
  143. package/src/commands/pack-install.ts +0 -2247
  144. package/src/commands/pack-update-cache.test.ts +0 -149
  145. package/src/commands/packs.test.ts +0 -88
  146. package/src/commands/packs.ts +0 -622
  147. package/src/commands/people.test.ts +0 -807
  148. package/src/commands/people.ts +0 -422
  149. package/src/commands/pkg-install.dispatch.test.ts +0 -71
  150. package/src/commands/pkg-install.ts +0 -215
  151. package/src/commands/pkg-list.test.ts +0 -79
  152. package/src/commands/pkg-list.ts +0 -13
  153. package/src/commands/pkg-remove.ts +0 -71
  154. package/src/commands/pkg-update.ts +0 -189
  155. package/src/commands/publish.test.ts +0 -650
  156. package/src/commands/publish.ts +0 -580
  157. package/src/commands/reindex.test.ts +0 -259
  158. package/src/commands/reindex.ts +0 -274
  159. package/src/commands/rescue.test.ts +0 -39
  160. package/src/commands/rescue.ts +0 -210
  161. package/src/commands/run.env-local.test.ts +0 -88
  162. package/src/commands/run.test.ts +0 -45
  163. package/src/commands/run.ts +0 -184
  164. package/src/commands/safe-extract.test.ts +0 -459
  165. package/src/commands/safe-extract.ts +0 -444
  166. package/src/commands/secrets-scope.test.ts +0 -56
  167. package/src/commands/secrets-scope.ts +0 -32
  168. package/src/commands/secrets.parse-destination.test.ts +0 -38
  169. package/src/commands/secrets.test.ts +0 -2037
  170. package/src/commands/secrets.ts +0 -2240
  171. package/src/commands/signals.ts +0 -351
  172. package/src/commands/skill.test.ts +0 -770
  173. package/src/commands/skill.ts +0 -796
  174. package/src/commands/sources.ts +0 -362
  175. package/src/commands/sync-mode.test.ts +0 -366
  176. package/src/commands/sync-mode.ts +0 -387
  177. package/src/commands/sync-narrow.test.ts +0 -633
  178. package/src/commands/sync-narrow.ts +0 -555
  179. package/src/commands/sync.ts +0 -165
  180. package/src/commands/team-sync.ts +0 -628
  181. package/src/commands/update.ts +0 -71
  182. package/src/commands/whoami.ts +0 -83
  183. package/src/commands/workers.test.ts +0 -158
  184. package/src/commands/workers.ts +0 -301
  185. package/src/index.test.ts +0 -32
  186. package/src/index.ts +0 -20
  187. package/src/lib/db/control-plane.test.ts +0 -59
  188. package/src/lib/db/control-plane.ts +0 -113
  189. package/src/lib/db/local.test.ts +0 -81
  190. package/src/lib/db/local.ts +0 -148
  191. package/src/lib/db/migrate.test.ts +0 -133
  192. package/src/lib/db/migrate.ts +0 -137
  193. package/src/lib/db/paths.test.ts +0 -112
  194. package/src/lib/db/paths.ts +0 -128
  195. package/src/lib/db/remote-engine.test.ts +0 -44
  196. package/src/lib/db/remote-engine.ts +0 -148
  197. package/src/lib/db/remote-sql.test.ts +0 -32
  198. package/src/lib/db/remote-sql.ts +0 -62
  199. package/src/lib/db/sql.test.ts +0 -106
  200. package/src/lib/db/sql.ts +0 -192
  201. package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
  202. package/src/lib/local-tree-diff.test.ts +0 -301
  203. package/src/lib/local-tree-diff.ts +0 -362
  204. package/src/lib/narrow-hint-banner.test.ts +0 -235
  205. package/src/lib/narrow-hint-banner.ts +0 -212
  206. package/src/lib/plan-limit-nag.test.ts +0 -317
  207. package/src/lib/plan-limit-nag.ts +0 -264
  208. package/src/main.ts +0 -371
  209. package/src/node-preflight.test.ts +0 -60
  210. package/src/node-preflight.ts +0 -67
  211. package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
  212. package/src/outpost/session-heartbeat-guard.test.ts +0 -105
  213. package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
  214. package/src/outpost/session-heartbeat-publisher.ts +0 -186
  215. package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
  216. package/src/outpost/session-heartbeat.test.ts +0 -459
  217. package/src/outpost/session-heartbeat.ts +0 -877
  218. package/src/packaging.test.ts +0 -109
  219. package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
  220. package/src/run/discover-schemas.test.ts +0 -153
  221. package/src/run/discover-schemas.ts +0 -79
  222. package/src/run/hq-plugin.test.ts +0 -371
  223. package/src/run/hq-plugin.ts +0 -277
  224. package/src/run/varlock-shape.test.ts +0 -57
  225. package/src/schemas/hq-package.schema.json +0 -155
  226. package/src/sentry-before-send.test.ts +0 -359
  227. package/src/sentry-before-send.ts +0 -221
  228. package/src/sentry-epipe.test.ts +0 -37
  229. package/src/sentry-release.test.ts +0 -54
  230. package/src/sentry.test.ts +0 -24
  231. package/src/sentry.ts +0 -54
  232. package/src/strategies/link.ts +0 -62
  233. package/src/strategies/merge.ts +0 -142
  234. package/src/types.ts +0 -146
  235. package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
  236. package/src/utils/auth-error.test.ts +0 -40
  237. package/src/utils/auth-error.ts +0 -42
  238. package/src/utils/billing-gate.test.ts +0 -95
  239. package/src/utils/billing-gate.ts +0 -228
  240. package/src/utils/breadcrumb-buffer.ts +0 -18
  241. package/src/utils/cli-telemetry.test.ts +0 -153
  242. package/src/utils/cli-telemetry.ts +0 -61
  243. package/src/utils/cognito-session.machine.test.ts +0 -109
  244. package/src/utils/cognito-session.refresh.test.ts +0 -84
  245. package/src/utils/cognito-session.test.ts +0 -328
  246. package/src/utils/cognito-session.ts +0 -448
  247. package/src/utils/company-selection-error.test.ts +0 -42
  248. package/src/utils/company-selection-error.ts +0 -45
  249. package/src/utils/contribution-table.ts +0 -83
  250. package/src/utils/environmental-error.test.ts +0 -45
  251. package/src/utils/environmental-error.ts +0 -39
  252. package/src/utils/epipe.test.ts +0 -28
  253. package/src/utils/epipe.ts +0 -29
  254. package/src/utils/expected-cli-error.test.ts +0 -28
  255. package/src/utils/expected-cli-error.ts +0 -39
  256. package/src/utils/feedback-diagnostics.test.ts +0 -183
  257. package/src/utils/feedback-diagnostics.ts +0 -123
  258. package/src/utils/feedback-screenshots.test.ts +0 -134
  259. package/src/utils/feedback-screenshots.ts +0 -124
  260. package/src/utils/feedback-versions.test.ts +0 -98
  261. package/src/utils/feedback-versions.ts +0 -68
  262. package/src/utils/git.ts +0 -74
  263. package/src/utils/integrity.ts +0 -59
  264. package/src/utils/intercepted-process-exit.test.ts +0 -37
  265. package/src/utils/intercepted-process-exit.ts +0 -36
  266. package/src/utils/login-provider.test.ts +0 -42
  267. package/src/utils/login-provider.ts +0 -30
  268. package/src/utils/manifest.test.ts +0 -167
  269. package/src/utils/manifest.ts +0 -131
  270. package/src/utils/pack-contributions.test.ts +0 -603
  271. package/src/utils/pack-contributions.ts +0 -450
  272. package/src/utils/people.ts +0 -215
  273. package/src/utils/registry-client.ts +0 -204
  274. package/src/utils/registry.ts +0 -90
  275. package/src/utils/resolve-vault-credential.test.ts +0 -69
  276. package/src/utils/resolve-vault-credential.ts +0 -60
  277. package/src/utils/sandbox-runner-client.test.ts +0 -279
  278. package/src/utils/sandbox-runner-client.ts +0 -260
  279. package/src/utils/secrets-cache.ts +0 -183
  280. package/src/utils/sentry-identity.ts +0 -45
  281. package/src/utils/settle-with-timeout.test.ts +0 -21
  282. package/src/utils/settle-with-timeout.ts +0 -22
  283. package/src/utils/user-yaml-error.test.ts +0 -24
  284. package/src/utils/user-yaml-error.ts +0 -30
  285. package/src/utils/vault-api.test.ts +0 -536
  286. package/src/utils/vault-api.ts +0 -382
  287. package/src/utils/version-check.test.ts +0 -176
  288. package/src/utils/version-check.ts +0 -155
  289. package/src/utils/version-gate.test.ts +0 -1024
  290. package/src/utils/version-gate.ts +0 -702
  291. package/test/commands/db-tenant-isolation.test.ts +0 -94
  292. package/test/commands/db.test.ts +0 -85
  293. package/test/commands/signals.test.ts +0 -207
  294. package/test/commands/sources.test.ts +0 -231
  295. package/test/e2e/smoke-install-mcp.sh +0 -113
  296. package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
  297. package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
  298. package/test/fixtures/signals/action_item/sample.md +0 -16
  299. package/test/fixtures/signals/summary/sample.md +0 -12
  300. package/test/fixtures/sources/meetings/sample.md +0 -25
  301. package/test/helpers/cli-runner.ts +0 -150
  302. package/test/helpers/s3-list-mock.ts +0 -79
  303. package/test/helpers/vault-service-mock.ts +0 -295
  304. package/test/sources-signals/smoke.test.ts +0 -228
  305. package/tsconfig.json +0 -20
  306. package/vitest.config.ts +0 -11
  307. package/vitest.e2e.config.ts +0 -7
@@ -1,923 +0,0 @@
1
- /**
2
- * `hq agents` — manage a company's cloud (fleet) agents from the terminal
3
- * instead of the web console. Mirrors the console's `AgentsClient`
4
- * (hq-console `src/lib/agents-client.ts`), targeting the hq-pro `/v1/agents`
5
- * control plane on `DEFAULT_VAULT_API_URL` via the shared `vaultApiFetch`
6
- * helper. No new HTTP client, no console-SDK import — same auth + company
7
- * resolution stack as `members.ts`.
8
- *
9
- * Subcommands:
10
- * hq agents list — roster for the company
11
- * hq agents status <uid> — setup state + runtime detail
12
- * hq agents rename <uid> <name> — set the agent's display name
13
- * hq agents set <uid> [...] — patch profile (name/title/description)
14
- * hq agents config <uid> [...] — patch runtime config (model/effort/tier)
15
- * hq agents start|stop <uid> — EC2 start/stop
16
- * hq agents retry <uid> — resume setup from first non-done step
17
- * hq agents rm <uid> --yes — deprovision (destructive; flag-guarded)
18
- *
19
- * Agents are company-scoped. `--company <slug>` may sit on the group
20
- * (`hq agents --company acme list`) or on a subcommand
21
- * (`hq agents list --company acme`); when omitted, resolution falls back to
22
- * the caller's single active membership (same as `members.ts`).
23
- */
24
-
25
- import { Command } from "commander";
26
- import chalk from "chalk";
27
- import { randomUUID } from "node:crypto";
28
- import { ensureCognitoToken } from "../utils/cognito-session.js";
29
- import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
30
- import {
31
- AGENT_PRICE_CENTS,
32
- confirmChargeOrExit,
33
- parseBillingPayload,
34
- surfaceBillingBlocked,
35
- type BillingErrorPayload,
36
- } from "../utils/billing-gate.js";
37
-
38
- /** Reasoning-effort values hq-pro accepts on `runtime-config`. */
39
- export const VALID_EFFORTS = new Set([
40
- "minimal",
41
- "low",
42
- "medium",
43
- "high",
44
- "xhigh",
45
- ]);
46
- /** Service-tier (speed) values hq-pro accepts on `runtime-config`. */
47
- export const VALID_TIERS = new Set(["default", "priority"]);
48
-
49
- /**
50
- * A non-2xx from the `/v1/agents` control plane. Carries the HTTP status and
51
- * the registry error `code` (when present) so callers can branch — notably a
52
- * 404 on `list`, which hq-pro returns for a company that has the agents
53
- * feature flag OFF (`src/agents/feature-flag.ts`).
54
- */
55
- export class AgentsHttpError extends Error {
56
- status: number;
57
- code?: string;
58
- /** hq-pro's billing envelope on a `402 billing_required` provision block. */
59
- billing?: BillingErrorPayload;
60
- constructor(
61
- status: number,
62
- message: string,
63
- code?: string,
64
- billing?: BillingErrorPayload,
65
- ) {
66
- super(message);
67
- this.name = "AgentsHttpError";
68
- this.status = status;
69
- this.code = code;
70
- this.billing = billing;
71
- }
72
- }
73
-
74
- /** Roster row from `GET /v1/agents` — a superset is returned; we keep what we render. */
75
- export interface CompanyAgentView {
76
- uid: string;
77
- name: string;
78
- slug: string;
79
- companyUid: string;
80
- entityStatus?: string;
81
- agentStatus?: string;
82
- setupPhase?: string;
83
- provider?: string;
84
- codexModel?: string;
85
- codexReasoningEffort?: string;
86
- codexServiceTier?: string;
87
- profile?: {
88
- displayName?: string;
89
- title?: string;
90
- description?: string;
91
- };
92
- [key: string]: unknown;
93
- }
94
-
95
- export interface ProfilePatch {
96
- displayName?: string;
97
- title?: string;
98
- description?: string;
99
- }
100
-
101
- export interface RuntimeConfigPatch {
102
- codexModel?: string;
103
- codexReasoningEffort?: string;
104
- codexServiceTier?: string;
105
- }
106
-
107
- /**
108
- * Authenticated JSON round-trip against the agents control plane. Throws
109
- * `AgentsHttpError` on any non-2xx (never swallows — hq-never-swallow-errors),
110
- * decoding hq-pro's `{ error | message, code }` envelope for the reason.
111
- */
112
- export async function agentsRequest<T>(opts: {
113
- token: string;
114
- path: string;
115
- method?: string;
116
- body?: Record<string, unknown>;
117
- query?: Record<string, string>;
118
- }): Promise<T> {
119
- const res = await vaultApiFetch(opts);
120
- if (!res.ok) {
121
- const body = (await res.json().catch(() => ({}))) as {
122
- error?: string;
123
- message?: string;
124
- code?: string;
125
- };
126
- throw new AgentsHttpError(
127
- res.status,
128
- // `message` FIRST: on a payment_failed envelope it carries the friendly
129
- // decline copy ("Your card was declined…") while `error` is the generic
130
- // "payment required" — error-first would feed surfaceBillingBlocked the
131
- // generic string and lose the decline reason (mirrors outpostRequest).
132
- body.message ?? body.error ?? res.statusText,
133
- body.code,
134
- parseBillingPayload(body),
135
- );
136
- }
137
- return (await res.json()) as T;
138
- }
139
-
140
- /** Slugify an agent name into a lowercase, hyphen-separated slug. */
141
- export function slugifyAgentName(name: string): string {
142
- return name
143
- .toLowerCase()
144
- .trim()
145
- .replace(/[^a-z0-9]+/g, "-")
146
- .replace(/^-+|-+$/g, "");
147
- }
148
-
149
- export interface ProvisionAgentInput {
150
- companyUid: string;
151
- name: string;
152
- slug: string;
153
- codexAuthMode: "subscription" | "apiKey";
154
- provider?: "codex" | "grok";
155
- codexApiKey?: string;
156
- idempotencyKey: string;
157
- title?: string;
158
- description?: string;
159
- }
160
-
161
- export async function provisionAgent(
162
- token: string,
163
- input: ProvisionAgentInput,
164
- ): Promise<{ uid?: string; slug?: string; [key: string]: unknown }> {
165
- return agentsRequest({
166
- token,
167
- path: "/v1/agents",
168
- method: "POST",
169
- body: input as unknown as Record<string, unknown>,
170
- });
171
- }
172
-
173
- export async function listAgents(
174
- token: string,
175
- companyUid: string,
176
- ): Promise<CompanyAgentView[]> {
177
- const data = await agentsRequest<{ agents: CompanyAgentView[] }>({
178
- token,
179
- path: "/v1/agents",
180
- query: { companyUid },
181
- });
182
- return data.agents ?? [];
183
- }
184
-
185
- export async function getAgentStatus(
186
- token: string,
187
- agentUid: string,
188
- ): Promise<Record<string, unknown>> {
189
- return agentsRequest({
190
- token,
191
- path: `/v1/agents/${encodeURIComponent(agentUid)}/status`,
192
- });
193
- }
194
-
195
- export async function patchAgentProfile(
196
- token: string,
197
- agentUid: string,
198
- patch: ProfilePatch,
199
- ): Promise<{
200
- uid: string;
201
- profile: ProfilePatch;
202
- slackUpdated: boolean;
203
- }> {
204
- return agentsRequest({
205
- token,
206
- path: `/v1/agents/${encodeURIComponent(agentUid)}/profile`,
207
- method: "PATCH",
208
- body: patch as Record<string, unknown>,
209
- });
210
- }
211
-
212
- export async function patchAgentRuntimeConfig(
213
- token: string,
214
- agentUid: string,
215
- patch: RuntimeConfigPatch,
216
- ): Promise<{
217
- uid: string;
218
- codexModel?: string;
219
- codexReasoningEffort?: string;
220
- codexServiceTier?: string;
221
- applied: boolean;
222
- }> {
223
- return agentsRequest({
224
- token,
225
- path: `/v1/agents/${encodeURIComponent(agentUid)}/runtime-config`,
226
- method: "PATCH",
227
- body: patch as Record<string, unknown>,
228
- });
229
- }
230
-
231
- export async function startStopAgent(
232
- token: string,
233
- agentUid: string,
234
- action: "start" | "stop",
235
- ): Promise<{ uid: string; runtime?: Record<string, unknown> }> {
236
- return agentsRequest({
237
- token,
238
- path: `/v1/agents/${encodeURIComponent(agentUid)}/${action}`,
239
- method: "POST",
240
- });
241
- }
242
-
243
- export async function retryAgent(
244
- token: string,
245
- agentUid: string,
246
- ): Promise<Record<string, unknown>> {
247
- return agentsRequest({
248
- token,
249
- path: `/v1/agents/${encodeURIComponent(agentUid)}/retry`,
250
- method: "POST",
251
- });
252
- }
253
-
254
- export async function deprovisionAgent(
255
- token: string,
256
- agentUid: string,
257
- ): Promise<{ uid: string; setupState?: string; terminal?: boolean }> {
258
- return agentsRequest({
259
- token,
260
- path: `/v1/agents/${encodeURIComponent(agentUid)}`,
261
- method: "DELETE",
262
- });
263
- }
264
-
265
- /** Human-readable "hot-applied to the running box" vs "saved for next launch". */
266
- export function appliedHint(applied: boolean): string {
267
- return applied
268
- ? "applied to the running box now"
269
- : "saved — takes effect on the agent's next launch";
270
- }
271
-
272
- // ---------------------------------------------------------------------------
273
- // Talking to agents (DM). Agents are members addressable over HQ's DM system:
274
- // a message rides `POST /v1/notify/dm` (the wire contract accepts `agt_*`), the
275
- // agent's box picks it up and replies over DM (a first-class agent reply
276
- // channel), and the two-way conversation is read back via `GET /v1/notify/thread`.
277
- // ---------------------------------------------------------------------------
278
-
279
- /** Agent uid shape (`agt_…`). */
280
- const AGENT_UID_PATTERN = /^agt_[A-Za-z0-9_-]+$/;
281
-
282
- /** One message in a DM thread, as returned by `GET /v1/notify/thread`. */
283
- export interface DmThreadMessage {
284
- eventId: string;
285
- fromPersonUid: string;
286
- fromDisplayName?: string;
287
- fromEmail?: string;
288
- body: string;
289
- createdAt: string;
290
- /** "out" = sent by the caller; "in" = received (the agent's reply). */
291
- direction: "in" | "out";
292
- }
293
-
294
- /**
295
- * Resolve an agent reference to its `agt_` uid. An `agt_…` value is used
296
- * directly; anything else is treated as a slug or display name and resolved
297
- * against the company's roster (requires `--company` or a single active
298
- * membership).
299
- */
300
- export async function resolveAgentUid(
301
- token: string,
302
- ref: string,
303
- companySlug: string | undefined,
304
- ): Promise<string> {
305
- if (AGENT_UID_PATTERN.test(ref.trim())) return ref.trim();
306
- const companyUid = await getCompanyUid(token, companySlug);
307
- const roster = await listAgents(token, companyUid);
308
- const needle = ref.trim().toLowerCase();
309
- const match = roster.find(
310
- (a) =>
311
- a.slug?.toLowerCase() === needle ||
312
- a.name?.toLowerCase() === needle ||
313
- a.uid === ref.trim(),
314
- );
315
- if (!match) {
316
- throw new AgentsHttpError(
317
- 404,
318
- `No agent named "${ref}" in this company. Run \`hq agents list\` to see slugs/uids, ` +
319
- `or pass the agt_ uid directly.`,
320
- );
321
- }
322
- return match.uid;
323
- }
324
-
325
- /** Send a DM to an agent. Returns nothing meaningful beyond success. */
326
- export async function sendAgentDm(
327
- token: string,
328
- agentUid: string,
329
- message: string,
330
- ): Promise<void> {
331
- await agentsRequest({
332
- token,
333
- path: "/v1/notify/dm",
334
- method: "POST",
335
- body: { toPersonUid: agentUid, body: message },
336
- });
337
- }
338
-
339
- /** Read the two-way DM conversation with an agent (most recent `limit`). */
340
- export async function readAgentThread(
341
- token: string,
342
- agentUid: string,
343
- limit = 20,
344
- ): Promise<DmThreadMessage[]> {
345
- const data = await agentsRequest<{ messages: DmThreadMessage[] }>({
346
- token,
347
- path: "/v1/notify/thread",
348
- query: { withPersonUid: agentUid, limit: String(limit) },
349
- });
350
- return data.messages ?? [];
351
- }
352
-
353
- /** Render one thread message as a labeled line for the terminal. */
354
- export function formatThreadMessage(m: DmThreadMessage, agentLabel: string): string {
355
- const who =
356
- m.direction === "out"
357
- ? chalk.cyan("you")
358
- : chalk.magenta(m.fromDisplayName || agentLabel);
359
- const when = chalk.dim(shortTime(m.createdAt));
360
- return `${who} ${when}\n ${m.body.replace(/\n/g, "\n ")}`;
361
- }
362
-
363
- function shortTime(iso: string): string {
364
- // Keep it dependency-free and stable: trim the ISO string to `MM-DD HH:MM`.
365
- const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})/);
366
- return m ? `${m[2]}-${m[3]} ${m[4]}` : iso;
367
- }
368
-
369
- function sleep(ms: number): Promise<void> {
370
- return new Promise((resolve) => setTimeout(resolve, ms));
371
- }
372
-
373
- // ---------------------------------------------------------------------------
374
- // Command registration
375
- // ---------------------------------------------------------------------------
376
-
377
- function fail(err: unknown): never {
378
- if (err instanceof AgentsHttpError) {
379
- console.error(chalk.red(err.message));
380
- } else {
381
- console.error(
382
- chalk.red("Error:"),
383
- err instanceof Error ? err.message : String(err),
384
- );
385
- }
386
- process.exit(1);
387
- }
388
-
389
- export function registerAgentsCommand(program: Command): void {
390
- const agents = program
391
- .command("agents")
392
- .description("Manage a company's cloud (fleet) agents")
393
- .option("--company <slug>", "Company slug (resolves to companyUid)");
394
-
395
- // `--company` may live on the group or the subcommand; the subcommand value
396
- // wins so `hq agents list --company acme` works as well as
397
- // `hq agents --company acme list`.
398
- const companyOf = (sub: Command): string | undefined =>
399
- (sub.opts().company as string | undefined) ??
400
- (agents.opts().company as string | undefined);
401
-
402
- agents
403
- .command("message <agent> <message...>")
404
- .alias("msg")
405
- .description("Send a message to an agent and wait for its reply")
406
- .option("--company <slug>", "Company slug (to resolve an agent by name/slug)")
407
- .option("--no-wait", "Send without waiting for the agent's reply")
408
- .option("--timeout <seconds>", "How long to wait for a reply", "90")
409
- .option("--json", "Emit raw JSON")
410
- .action(async function (
411
- this: Command,
412
- agent: string,
413
- messageParts: string[],
414
- opts: { company?: string; wait?: boolean; timeout?: string; json?: boolean },
415
- ) {
416
- try {
417
- const message = messageParts.join(" ").trim();
418
- if (!message) {
419
- console.error(chalk.red("No message given. Usage: hq agents message <agent> <text>"));
420
- process.exit(1);
421
- }
422
- const token = await ensureCognitoToken();
423
- const agentUid = await resolveAgentUid(token, agent, companyOf(this));
424
-
425
- // Baseline the conversation so we can detect the agent's NEW reply.
426
- const before = opts.wait === false ? [] : await readAgentThread(token, agentUid, 1);
427
- const lastBeforeAt = before.length ? before[before.length - 1].createdAt : "";
428
-
429
- await sendAgentDm(token, agentUid, message);
430
-
431
- if (opts.wait === false) {
432
- console.log(chalk.green(`Sent to ${agent}.`));
433
- console.log(chalk.dim(`Read replies later: hq agents thread ${agent}`));
434
- return;
435
- }
436
-
437
- // Poll the thread until a new inbound (agent) message appears or timeout.
438
- const timeoutMs = Math.max(5, Number(opts.timeout) || 90) * 1000;
439
- const deadline = Date.now() + timeoutMs;
440
- if (!opts.json) console.log(chalk.dim(`Sent — waiting for ${agent} to reply…`));
441
- let reply: DmThreadMessage | undefined;
442
- while (Date.now() < deadline) {
443
- await sleep(3000);
444
- const msgs = await readAgentThread(token, agentUid, 10);
445
- reply = msgs.find(
446
- (m) => m.direction === "in" && m.createdAt > lastBeforeAt,
447
- );
448
- if (reply) break;
449
- }
450
-
451
- if (opts.json) {
452
- process.stdout.write(
453
- JSON.stringify({ agentUid, sent: message, reply: reply ?? null }, null, 2) + "\n",
454
- );
455
- return;
456
- }
457
- if (reply) {
458
- console.log(formatThreadMessage(reply, agent));
459
- } else {
460
- console.log(
461
- chalk.yellow(
462
- `No reply within ${Math.round(timeoutMs / 1000)}s. The agent may still respond — ` +
463
- `check with: hq agents thread ${agent}`,
464
- ),
465
- );
466
- }
467
- } catch (err) {
468
- fail(err);
469
- }
470
- });
471
-
472
- agents
473
- .command("thread <agent>")
474
- .alias("history")
475
- .description("Show your recent conversation with an agent")
476
- .option("--company <slug>", "Company slug (to resolve an agent by name/slug)")
477
- .option("--limit <n>", "How many recent messages to show", "20")
478
- .option("--json", "Emit raw JSON")
479
- .action(async function (
480
- this: Command,
481
- agent: string,
482
- opts: { company?: string; limit?: string; json?: boolean },
483
- ) {
484
- try {
485
- const token = await ensureCognitoToken();
486
- const agentUid = await resolveAgentUid(token, agent, companyOf(this));
487
- const limit = Math.max(1, Number(opts.limit) || 20);
488
- const msgs = await readAgentThread(token, agentUid, limit);
489
- if (opts.json) {
490
- process.stdout.write(JSON.stringify(msgs, null, 2) + "\n");
491
- return;
492
- }
493
- if (msgs.length === 0) {
494
- console.log(chalk.gray(`No conversation yet with ${agent}. Start one: hq agents message ${agent} "<text>"`));
495
- return;
496
- }
497
- console.log(msgs.map((m) => formatThreadMessage(m, agent)).join("\n\n"));
498
- } catch (err) {
499
- fail(err);
500
- }
501
- });
502
-
503
- agents
504
- .command("provision <name>")
505
- .alias("new")
506
- .description("Provision a new cloud agent ($100/month — requires --yes)")
507
- .option("--company <slug>", "Company slug (resolves to companyUid)")
508
- .option("--slug <slug>", "Agent slug (defaults to a slug of <name>)")
509
- .option("--provider <provider>", "Runtime: codex | grok (default codex)")
510
- .option(
511
- "--auth-mode <mode>",
512
- "Codex auth: subscription | apiKey (default subscription)",
513
- "subscription",
514
- )
515
- .option(
516
- "--api-key-env <VAR>",
517
- "Env var holding the API key for --auth-mode apiKey (never pass the key as a flag)",
518
- )
519
- .option("--title <title>", "Org-chart job title")
520
- .option("--description <text>", "Short description / bio")
521
- .option("--yes", "Confirm the $100/month charge (required to provision)")
522
- .action(async function (
523
- this: Command,
524
- name: string,
525
- opts: {
526
- slug?: string;
527
- provider?: string;
528
- authMode?: string;
529
- apiKeyEnv?: string;
530
- title?: string;
531
- description?: string;
532
- yes?: boolean;
533
- },
534
- ) {
535
- try {
536
- const authMode = opts.authMode === "apiKey" ? "apiKey" : "subscription";
537
- const provider =
538
- opts.provider === "grok" ? "grok" : opts.provider ? "codex" : undefined;
539
-
540
- // Resolve the API key from the environment (never from a flag value) when
541
- // apiKey mode is requested — validated BEFORE the paid gate so we don't
542
- // charge and then fail.
543
- let codexApiKey: string | undefined;
544
- if (authMode === "apiKey") {
545
- const envVar = opts.apiKeyEnv;
546
- if (!envVar) {
547
- console.error(
548
- chalk.red(
549
- "--auth-mode apiKey requires --api-key-env <VAR> naming the env var that holds the key.",
550
- ),
551
- );
552
- process.exit(1);
553
- }
554
- codexApiKey = process.env[envVar];
555
- if (!codexApiKey) {
556
- console.error(
557
- chalk.red(`Env var ${envVar} is empty or unset — no API key to use.`),
558
- );
559
- process.exit(1);
560
- }
561
- }
562
-
563
- const token = await ensureCognitoToken();
564
- const companyUid = await getCompanyUid(token, companyOf(this));
565
-
566
- // Paid gate: print the monthly cost and require --yes before any call.
567
- confirmChargeOrExit({
568
- resource: "agent",
569
- unitCents: AGENT_PRICE_CENTS,
570
- yes: opts.yes,
571
- });
572
-
573
- const slug = opts.slug ?? slugifyAgentName(name);
574
- try {
575
- const result = await provisionAgent(token, {
576
- companyUid,
577
- name,
578
- slug,
579
- codexAuthMode: authMode,
580
- ...(provider ? { provider } : {}),
581
- ...(codexApiKey ? { codexApiKey } : {}),
582
- idempotencyKey: `hq-cli-${randomUUID()}`,
583
- ...(opts.title ? { title: opts.title } : {}),
584
- ...(opts.description ? { description: opts.description } : {}),
585
- });
586
- const uid = typeof result.uid === "string" ? result.uid : slug;
587
- console.log(chalk.green(`Provisioning started for agent "${name}".`));
588
- console.log(
589
- chalk.dim(`Track setup: hq agents status ${uid} --company <slug>`),
590
- );
591
- } catch (err) {
592
- // No card on file → surface the shareable payment link instead of an
593
- // opaque 402, then exit non-zero so scripts can react.
594
- if (
595
- err instanceof AgentsHttpError &&
596
- err.status === 402 &&
597
- err.billing
598
- ) {
599
- await surfaceBillingBlocked(token, err.billing, err.message);
600
- process.exit(1);
601
- }
602
- throw err;
603
- }
604
- } catch (err) {
605
- fail(err);
606
- }
607
- });
608
-
609
- agents
610
- .command("list")
611
- .description("List the company's agents")
612
- .option("--company <slug>", "Company slug (resolves to companyUid)")
613
- .option("--json", "Emit raw JSON")
614
- .action(async function (this: Command, opts: { json?: boolean }) {
615
- try {
616
- const token = await ensureCognitoToken();
617
- const companyUid = await getCompanyUid(token, companyOf(this));
618
- let roster: CompanyAgentView[];
619
- try {
620
- roster = await listAgents(token, companyUid);
621
- } catch (err) {
622
- // A 404 here means the company doesn't have the agents feature
623
- // enabled (hq-pro returns 404 on every /v1/agents route for a
624
- // flag-OFF company) — surface that plainly rather than a raw error.
625
- if (err instanceof AgentsHttpError && err.status === 404) {
626
- console.log(
627
- chalk.gray(
628
- "No cloud agents are available for this company (the agents feature isn't enabled here).",
629
- ),
630
- );
631
- return;
632
- }
633
- throw err;
634
- }
635
-
636
- if (opts.json) {
637
- process.stdout.write(JSON.stringify(roster, null, 2) + "\n");
638
- return;
639
- }
640
- if (roster.length === 0) {
641
- console.log(chalk.gray("No agents found for this company."));
642
- return;
643
- }
644
- const nameW = Math.max(4, ...roster.map((a) => (a.name ?? "").length));
645
- const slugW = Math.max(4, ...roster.map((a) => (a.slug ?? "").length));
646
- const statusW = Math.max(
647
- 6,
648
- ...roster.map((a) => (a.agentStatus ?? "").length),
649
- );
650
- const modelW = Math.max(
651
- 5,
652
- ...roster.map((a) => (a.codexModel ?? "").length),
653
- );
654
- console.log(
655
- chalk.bold(
656
- [
657
- "NAME".padEnd(nameW),
658
- "SLUG".padEnd(slugW),
659
- "STATUS".padEnd(statusW),
660
- "MODEL".padEnd(modelW),
661
- "UID",
662
- ].join(" "),
663
- ),
664
- );
665
- for (const a of roster) {
666
- console.log(
667
- [
668
- (a.name ?? "").padEnd(nameW),
669
- (a.slug ?? "").padEnd(slugW),
670
- (a.agentStatus ?? "").padEnd(statusW),
671
- (a.codexModel ?? "").padEnd(modelW),
672
- a.uid,
673
- ].join(" "),
674
- );
675
- }
676
- } catch (err) {
677
- fail(err);
678
- }
679
- });
680
-
681
- agents
682
- .command("status <agentUid>")
683
- .description("Show an agent's setup state and runtime detail")
684
- .option("--company <slug>", "Company slug (resolves to companyUid)")
685
- .option("--json", "Emit raw JSON")
686
- .action(async function (
687
- this: Command,
688
- agentUid: string,
689
- opts: { json?: boolean },
690
- ) {
691
- try {
692
- const token = await ensureCognitoToken();
693
- const status = await getAgentStatus(token, agentUid);
694
- if (opts.json) {
695
- process.stdout.write(JSON.stringify(status, null, 2) + "\n");
696
- return;
697
- }
698
- // Pretty but faithful: dump the top-level fields as key: value.
699
- for (const [k, v] of Object.entries(status)) {
700
- const rendered =
701
- v && typeof v === "object" ? JSON.stringify(v) : String(v);
702
- console.log(`${chalk.bold(k)}: ${rendered}`);
703
- }
704
- } catch (err) {
705
- fail(err);
706
- }
707
- });
708
-
709
- agents
710
- .command("rename <agentUid> <name>")
711
- .description("Set an agent's display name")
712
- .option("--company <slug>", "Company slug (resolves to companyUid)")
713
- .action(async function (
714
- this: Command,
715
- agentUid: string,
716
- name: string,
717
- ) {
718
- try {
719
- const token = await ensureCognitoToken();
720
- const result = await patchAgentProfile(token, agentUid, {
721
- displayName: name,
722
- });
723
- console.log(chalk.green(`Renamed agent ${agentUid} to "${name}"`));
724
- if (result.slackUpdated) {
725
- console.log(chalk.dim("Propagated to the agent's live Slack app."));
726
- }
727
- } catch (err) {
728
- fail(err);
729
- }
730
- });
731
-
732
- agents
733
- .command("set <agentUid>")
734
- .description("Update an agent's profile (name / title / description)")
735
- .option("--company <slug>", "Company slug (resolves to companyUid)")
736
- .option("--name <displayName>", "Display name")
737
- .option("--title <title>", 'Org-chart job title (e.g. "Chief of Staff")')
738
- .option("--description <text>", "Short description / bio")
739
- .option("--json", "Emit raw JSON")
740
- .action(async function (
741
- this: Command,
742
- agentUid: string,
743
- opts: {
744
- name?: string;
745
- title?: string;
746
- description?: string;
747
- json?: boolean;
748
- },
749
- ) {
750
- try {
751
- const patch: ProfilePatch = {};
752
- if (opts.name !== undefined) patch.displayName = opts.name;
753
- if (opts.title !== undefined) patch.title = opts.title;
754
- if (opts.description !== undefined) patch.description = opts.description;
755
- if (Object.keys(patch).length === 0) {
756
- console.error(
757
- chalk.red(
758
- "Nothing to set. Pass at least one of --name, --title, --description.",
759
- ),
760
- );
761
- process.exit(1);
762
- }
763
- const token = await ensureCognitoToken();
764
- const result = await patchAgentProfile(token, agentUid, patch);
765
- if (opts.json) {
766
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
767
- return;
768
- }
769
- console.log(chalk.green(`Updated profile for agent ${agentUid}.`));
770
- if (result.slackUpdated) {
771
- console.log(chalk.dim("Propagated to the agent's live Slack app."));
772
- }
773
- } catch (err) {
774
- fail(err);
775
- }
776
- });
777
-
778
- agents
779
- .command("config <agentUid>")
780
- .description(
781
- "Update an agent's runtime config (model / reasoning effort / service tier)",
782
- )
783
- .option("--company <slug>", "Company slug (resolves to companyUid)")
784
- .option("--model <model>", "Codex model id")
785
- .option(
786
- "--effort <effort>",
787
- "Reasoning effort: minimal | low | medium | high | xhigh",
788
- )
789
- .option("--tier <tier>", "Service tier (speed): default | priority")
790
- .option("--json", "Emit raw JSON")
791
- .action(async function (
792
- this: Command,
793
- agentUid: string,
794
- opts: {
795
- model?: string;
796
- effort?: string;
797
- tier?: string;
798
- json?: boolean;
799
- },
800
- ) {
801
- try {
802
- const patch: RuntimeConfigPatch = {};
803
- if (opts.model !== undefined) patch.codexModel = opts.model;
804
- if (opts.effort !== undefined) {
805
- const effort = opts.effort.trim().toLowerCase();
806
- if (!VALID_EFFORTS.has(effort)) {
807
- console.error(
808
- chalk.red(
809
- `Invalid --effort '${opts.effort}': must be one of minimal, low, medium, high, xhigh`,
810
- ),
811
- );
812
- process.exit(1);
813
- }
814
- patch.codexReasoningEffort = effort;
815
- }
816
- if (opts.tier !== undefined) {
817
- const tier = opts.tier.trim().toLowerCase();
818
- if (!VALID_TIERS.has(tier)) {
819
- console.error(
820
- chalk.red(
821
- `Invalid --tier '${opts.tier}': must be one of default, priority`,
822
- ),
823
- );
824
- process.exit(1);
825
- }
826
- patch.codexServiceTier = tier;
827
- }
828
- if (Object.keys(patch).length === 0) {
829
- console.error(
830
- chalk.red(
831
- "Nothing to change. Pass at least one of --model, --effort, --tier.",
832
- ),
833
- );
834
- process.exit(1);
835
- }
836
- const token = await ensureCognitoToken();
837
- const result = await patchAgentRuntimeConfig(token, agentUid, patch);
838
- if (opts.json) {
839
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
840
- return;
841
- }
842
- console.log(
843
- chalk.green(
844
- `Updated runtime config for agent ${agentUid} — ${appliedHint(result.applied)}.`,
845
- ),
846
- );
847
- } catch (err) {
848
- fail(err);
849
- }
850
- });
851
-
852
- for (const action of ["start", "stop"] as const) {
853
- agents
854
- .command(`${action} <agentUid>`)
855
- .description(`${action === "start" ? "Start" : "Stop"} an agent's box`)
856
- .option("--company <slug>", "Company slug (resolves to companyUid)")
857
- .action(async function (this: Command, agentUid: string) {
858
- try {
859
- const token = await ensureCognitoToken();
860
- await startStopAgent(token, agentUid, action);
861
- console.log(
862
- chalk.green(
863
- `${action === "start" ? "Started" : "Stopped"} agent ${agentUid}.`,
864
- ),
865
- );
866
- } catch (err) {
867
- fail(err);
868
- }
869
- });
870
- }
871
-
872
- agents
873
- .command("retry <agentUid>")
874
- .description("Resume an agent's setup from the first non-done step")
875
- .option("--company <slug>", "Company slug (resolves to companyUid)")
876
- .action(async function (this: Command, agentUid: string) {
877
- try {
878
- const token = await ensureCognitoToken();
879
- await retryAgent(token, agentUid);
880
- console.log(chalk.green(`Retry queued for agent ${agentUid}.`));
881
- } catch (err) {
882
- fail(err);
883
- }
884
- });
885
-
886
- agents
887
- .command("rm <agentUid>")
888
- .alias("delete")
889
- .description("Deprovision (permanently tear down) an agent")
890
- .option("--company <slug>", "Company slug (resolves to companyUid)")
891
- .option("--yes", "Confirm the irreversible teardown (required)")
892
- .action(async function (
893
- this: Command,
894
- agentUid: string,
895
- opts: { yes?: boolean },
896
- ) {
897
- if (!opts.yes) {
898
- console.error(
899
- chalk.yellow(
900
- `This will permanently deprovision agent ${agentUid} and tear down its ` +
901
- `cloud resources. This cannot be undone.\n` +
902
- `Re-run with --yes to confirm: hq agents rm ${agentUid} --yes`,
903
- ),
904
- );
905
- process.exit(1);
906
- }
907
- try {
908
- const token = await ensureCognitoToken();
909
- const result = await deprovisionAgent(token, agentUid);
910
- if (result.terminal === false) {
911
- console.log(
912
- chalk.yellow(
913
- `Deprovision in progress for agent ${agentUid} (not yet fully torn down — re-run to continue).`,
914
- ),
915
- );
916
- } else {
917
- console.log(chalk.green(`Deprovisioned agent ${agentUid}.`));
918
- }
919
- } catch (err) {
920
- fail(err);
921
- }
922
- });
923
- }