@indigoai-us/hq-cli 5.77.14 → 5.79.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 (309) hide show
  1. package/CHANGELOG.md +31 -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/meetings.js +16 -9
  31. package/dist/commands/members.js +16 -5
  32. package/dist/commands/pack-install.d.ts +14 -0
  33. package/dist/commands/pack-install.js +42 -2
  34. package/dist/commands/pkg-install.js +39 -2
  35. package/dist/commands/run.js +6 -2
  36. package/dist/commands/secrets.js +120 -65
  37. package/dist/main.js +6 -0
  38. package/dist/utils/api-key-command-gate.d.ts +45 -0
  39. package/dist/utils/api-key-command-gate.js +51 -0
  40. package/dist/utils/hq-roots.d.ts +91 -0
  41. package/dist/utils/hq-roots.js +212 -0
  42. package/dist/utils/resolve-vault-credential.d.ts +63 -3
  43. package/dist/utils/resolve-vault-credential.js +152 -6
  44. package/dist/utils/run-bundled-script.d.ts +66 -0
  45. package/dist/utils/run-bundled-script.js +80 -0
  46. package/dist/utils/vault-api.d.ts +21 -0
  47. package/dist/utils/vault-api.js +74 -3
  48. package/package.json +7 -1
  49. package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
  50. package/.github/workflows/ci.yml +0 -46
  51. package/.github/workflows/publish.yml +0 -36
  52. package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
  53. package/e2e/cli.test.ts +0 -128
  54. package/eslint.config.js +0 -37
  55. package/pnpm-workspace.yaml +0 -4
  56. package/scripts/chmod-bins.mjs +0 -49
  57. package/scripts/generate-dsn.mjs +0 -26
  58. package/scripts/generate-scan-packages-table.mjs +0 -113
  59. package/scripts/smoke-sources-signals.sh +0 -103
  60. package/src/bin/hq-auth-refresh.ts +0 -43
  61. package/src/cli-version.ts +0 -13
  62. package/src/commands/__fixtures__/make-tar.ts +0 -126
  63. package/src/commands/_patterns.ts +0 -10
  64. package/src/commands/add.ts +0 -75
  65. package/src/commands/agents.test.ts +0 -527
  66. package/src/commands/agents.ts +0 -923
  67. package/src/commands/api-keys.test.ts +0 -291
  68. package/src/commands/api-keys.ts +0 -382
  69. package/src/commands/artifact-verify.test.ts +0 -177
  70. package/src/commands/auth.test.ts +0 -82
  71. package/src/commands/auth.ts +0 -213
  72. package/src/commands/billing.test.ts +0 -158
  73. package/src/commands/billing.ts +0 -146
  74. package/src/commands/channels.test.ts +0 -74
  75. package/src/commands/channels.ts +0 -91
  76. package/src/commands/cloud-demote.test.ts +0 -401
  77. package/src/commands/cloud-demote.ts +0 -278
  78. package/src/commands/cloud-provision.test.ts +0 -1117
  79. package/src/commands/cloud-provision.ts +0 -942
  80. package/src/commands/cloud.pull-all.test.ts +0 -711
  81. package/src/commands/cloud.pull-per-company.test.ts +0 -188
  82. package/src/commands/cloud.push-all.test.ts +0 -485
  83. package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
  84. package/src/commands/cloud.selectors.test.ts +0 -208
  85. package/src/commands/cloud.status.test.ts +0 -160
  86. package/src/commands/cloud.test.ts +0 -41
  87. package/src/commands/cloud.ts +0 -2130
  88. package/src/commands/company.test.ts +0 -202
  89. package/src/commands/company.ts +0 -148
  90. package/src/commands/creators.test.ts +0 -60
  91. package/src/commands/creators.ts +0 -117
  92. package/src/commands/crm.test.ts +0 -179
  93. package/src/commands/crm.ts +0 -236
  94. package/src/commands/db-migrate.ts +0 -55
  95. package/src/commands/db-provision.ts +0 -114
  96. package/src/commands/db-sql.ts +0 -124
  97. package/src/commands/db-status.ts +0 -108
  98. package/src/commands/db.ts +0 -26
  99. package/src/commands/dm.test.ts +0 -748
  100. package/src/commands/dm.ts +0 -1226
  101. package/src/commands/feedback.test.ts +0 -413
  102. package/src/commands/feedback.ts +0 -169
  103. package/src/commands/files-browse.test.ts +0 -1249
  104. package/src/commands/files-browse.ts +0 -1506
  105. package/src/commands/files-delete.test.ts +0 -484
  106. package/src/commands/files-recovery.test.ts +0 -370
  107. package/src/commands/files.test.ts +0 -722
  108. package/src/commands/files.ts +0 -1446
  109. package/src/commands/group-grants.test.ts +0 -330
  110. package/src/commands/group-grants.ts +0 -455
  111. package/src/commands/groups.test.ts +0 -44
  112. package/src/commands/groups.ts +0 -368
  113. package/src/commands/integrations.test.ts +0 -633
  114. package/src/commands/integrations.ts +0 -507
  115. package/src/commands/list.ts +0 -85
  116. package/src/commands/login.ts +0 -52
  117. package/src/commands/logout.ts +0 -30
  118. package/src/commands/marketplace-install.test.ts +0 -543
  119. package/src/commands/marketplace-security.test.ts +0 -646
  120. package/src/commands/mcp-registration.test.ts +0 -2787
  121. package/src/commands/mcp-registration.ts +0 -2612
  122. package/src/commands/mcp-status.test.ts +0 -483
  123. package/src/commands/mcp-status.ts +0 -575
  124. package/src/commands/mcp-status.us011.test.ts +0 -243
  125. package/src/commands/meetings.test.ts +0 -522
  126. package/src/commands/meetings.ts +0 -789
  127. package/src/commands/members.test.ts +0 -1474
  128. package/src/commands/members.ts +0 -1181
  129. package/src/commands/onboard-identity-guard.test.ts +0 -99
  130. package/src/commands/onboard-identity-guard.ts +0 -103
  131. package/src/commands/onboard-join.test.ts +0 -96
  132. package/src/commands/onboard-join.ts +0 -101
  133. package/src/commands/onboard-warning.test.ts +0 -26
  134. package/src/commands/onboard-warning.ts +0 -12
  135. package/src/commands/onboard.ts +0 -267
  136. package/src/commands/outposts-heartbeat.test.ts +0 -299
  137. package/src/commands/outposts-heartbeat.ts +0 -310
  138. package/src/commands/outposts-replica-sync.test.ts +0 -272
  139. package/src/commands/outposts-self-deploy.test.ts +0 -243
  140. package/src/commands/outposts.test.ts +0 -1039
  141. package/src/commands/outposts.ts +0 -1935
  142. package/src/commands/pack-install-secret-authorization.test.ts +0 -115
  143. package/src/commands/pack-install.test.ts +0 -1469
  144. package/src/commands/pack-install.ts +0 -2256
  145. package/src/commands/pack-update-cache.test.ts +0 -149
  146. package/src/commands/packs-update-api-key.test.ts +0 -105
  147. package/src/commands/packs.test.ts +0 -88
  148. package/src/commands/packs.ts +0 -622
  149. package/src/commands/people.test.ts +0 -807
  150. package/src/commands/people.ts +0 -422
  151. package/src/commands/pkg-install.dispatch.test.ts +0 -103
  152. package/src/commands/pkg-install.ts +0 -221
  153. package/src/commands/pkg-list.test.ts +0 -79
  154. package/src/commands/pkg-list.ts +0 -13
  155. package/src/commands/pkg-remove.ts +0 -71
  156. package/src/commands/pkg-update.ts +0 -189
  157. package/src/commands/publish.test.ts +0 -650
  158. package/src/commands/publish.ts +0 -580
  159. package/src/commands/reindex.test.ts +0 -259
  160. package/src/commands/reindex.ts +0 -274
  161. package/src/commands/rescue.test.ts +0 -39
  162. package/src/commands/rescue.ts +0 -210
  163. package/src/commands/run.env-local.test.ts +0 -88
  164. package/src/commands/run.test.ts +0 -45
  165. package/src/commands/run.ts +0 -190
  166. package/src/commands/safe-extract.test.ts +0 -459
  167. package/src/commands/safe-extract.ts +0 -444
  168. package/src/commands/secrets-scope.test.ts +0 -56
  169. package/src/commands/secrets-scope.ts +0 -32
  170. package/src/commands/secrets.parse-destination.test.ts +0 -38
  171. package/src/commands/secrets.test.ts +0 -2050
  172. package/src/commands/secrets.ts +0 -2249
  173. package/src/commands/signals.ts +0 -351
  174. package/src/commands/skill.test.ts +0 -770
  175. package/src/commands/skill.ts +0 -796
  176. package/src/commands/sources.ts +0 -362
  177. package/src/commands/sync-mode.test.ts +0 -366
  178. package/src/commands/sync-mode.ts +0 -387
  179. package/src/commands/sync-narrow.test.ts +0 -633
  180. package/src/commands/sync-narrow.ts +0 -555
  181. package/src/commands/sync.ts +0 -165
  182. package/src/commands/team-sync.ts +0 -628
  183. package/src/commands/update.ts +0 -71
  184. package/src/commands/whoami.ts +0 -83
  185. package/src/commands/workers.test.ts +0 -158
  186. package/src/commands/workers.ts +0 -301
  187. package/src/index.test.ts +0 -32
  188. package/src/index.ts +0 -20
  189. package/src/lib/db/control-plane.test.ts +0 -59
  190. package/src/lib/db/control-plane.ts +0 -113
  191. package/src/lib/db/local.test.ts +0 -81
  192. package/src/lib/db/local.ts +0 -148
  193. package/src/lib/db/migrate.test.ts +0 -133
  194. package/src/lib/db/migrate.ts +0 -137
  195. package/src/lib/db/paths.test.ts +0 -112
  196. package/src/lib/db/paths.ts +0 -128
  197. package/src/lib/db/remote-engine.test.ts +0 -44
  198. package/src/lib/db/remote-engine.ts +0 -148
  199. package/src/lib/db/remote-sql.test.ts +0 -32
  200. package/src/lib/db/remote-sql.ts +0 -62
  201. package/src/lib/db/sql.test.ts +0 -106
  202. package/src/lib/db/sql.ts +0 -192
  203. package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
  204. package/src/lib/local-tree-diff.test.ts +0 -301
  205. package/src/lib/local-tree-diff.ts +0 -362
  206. package/src/lib/narrow-hint-banner.test.ts +0 -235
  207. package/src/lib/narrow-hint-banner.ts +0 -212
  208. package/src/lib/plan-limit-nag.test.ts +0 -317
  209. package/src/lib/plan-limit-nag.ts +0 -264
  210. package/src/main.ts +0 -371
  211. package/src/node-preflight.test.ts +0 -60
  212. package/src/node-preflight.ts +0 -67
  213. package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
  214. package/src/outpost/session-heartbeat-guard.test.ts +0 -105
  215. package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
  216. package/src/outpost/session-heartbeat-publisher.ts +0 -186
  217. package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
  218. package/src/outpost/session-heartbeat.test.ts +0 -459
  219. package/src/outpost/session-heartbeat.ts +0 -877
  220. package/src/packaging.test.ts +0 -109
  221. package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
  222. package/src/run/discover-schemas.test.ts +0 -153
  223. package/src/run/discover-schemas.ts +0 -79
  224. package/src/run/hq-plugin.test.ts +0 -371
  225. package/src/run/hq-plugin.ts +0 -277
  226. package/src/run/varlock-shape.test.ts +0 -57
  227. package/src/schemas/hq-package.schema.json +0 -155
  228. package/src/sentry-before-send.test.ts +0 -359
  229. package/src/sentry-before-send.ts +0 -221
  230. package/src/sentry-epipe.test.ts +0 -37
  231. package/src/sentry-release.test.ts +0 -54
  232. package/src/sentry.test.ts +0 -24
  233. package/src/sentry.ts +0 -54
  234. package/src/strategies/link.ts +0 -62
  235. package/src/strategies/merge.ts +0 -142
  236. package/src/types.ts +0 -146
  237. package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
  238. package/src/utils/auth-error.test.ts +0 -40
  239. package/src/utils/auth-error.ts +0 -42
  240. package/src/utils/billing-gate.test.ts +0 -95
  241. package/src/utils/billing-gate.ts +0 -228
  242. package/src/utils/breadcrumb-buffer.ts +0 -18
  243. package/src/utils/cli-telemetry.test.ts +0 -153
  244. package/src/utils/cli-telemetry.ts +0 -61
  245. package/src/utils/cognito-session.machine.test.ts +0 -109
  246. package/src/utils/cognito-session.refresh.test.ts +0 -84
  247. package/src/utils/cognito-session.test.ts +0 -328
  248. package/src/utils/cognito-session.ts +0 -448
  249. package/src/utils/company-selection-error.test.ts +0 -42
  250. package/src/utils/company-selection-error.ts +0 -45
  251. package/src/utils/contribution-table.ts +0 -83
  252. package/src/utils/environmental-error.test.ts +0 -45
  253. package/src/utils/environmental-error.ts +0 -39
  254. package/src/utils/epipe.test.ts +0 -28
  255. package/src/utils/epipe.ts +0 -29
  256. package/src/utils/expected-cli-error.test.ts +0 -28
  257. package/src/utils/expected-cli-error.ts +0 -39
  258. package/src/utils/feedback-diagnostics.test.ts +0 -183
  259. package/src/utils/feedback-diagnostics.ts +0 -123
  260. package/src/utils/feedback-screenshots.test.ts +0 -134
  261. package/src/utils/feedback-screenshots.ts +0 -124
  262. package/src/utils/feedback-versions.test.ts +0 -98
  263. package/src/utils/feedback-versions.ts +0 -68
  264. package/src/utils/git.ts +0 -74
  265. package/src/utils/integrity.ts +0 -59
  266. package/src/utils/intercepted-process-exit.test.ts +0 -37
  267. package/src/utils/intercepted-process-exit.ts +0 -36
  268. package/src/utils/login-provider.test.ts +0 -42
  269. package/src/utils/login-provider.ts +0 -30
  270. package/src/utils/manifest.test.ts +0 -167
  271. package/src/utils/manifest.ts +0 -131
  272. package/src/utils/pack-contributions.test.ts +0 -603
  273. package/src/utils/pack-contributions.ts +0 -450
  274. package/src/utils/people.ts +0 -215
  275. package/src/utils/registry-client.ts +0 -204
  276. package/src/utils/registry.ts +0 -90
  277. package/src/utils/resolve-vault-credential.test.ts +0 -69
  278. package/src/utils/resolve-vault-credential.ts +0 -60
  279. package/src/utils/sandbox-runner-client.test.ts +0 -279
  280. package/src/utils/sandbox-runner-client.ts +0 -260
  281. package/src/utils/secrets-cache.ts +0 -183
  282. package/src/utils/sentry-identity.ts +0 -45
  283. package/src/utils/settle-with-timeout.test.ts +0 -21
  284. package/src/utils/settle-with-timeout.ts +0 -22
  285. package/src/utils/user-yaml-error.test.ts +0 -24
  286. package/src/utils/user-yaml-error.ts +0 -30
  287. package/src/utils/vault-api.test.ts +0 -536
  288. package/src/utils/vault-api.ts +0 -382
  289. package/src/utils/version-check.test.ts +0 -176
  290. package/src/utils/version-check.ts +0 -155
  291. package/src/utils/version-gate.test.ts +0 -1024
  292. package/src/utils/version-gate.ts +0 -702
  293. package/test/commands/db-tenant-isolation.test.ts +0 -94
  294. package/test/commands/db.test.ts +0 -85
  295. package/test/commands/signals.test.ts +0 -207
  296. package/test/commands/sources.test.ts +0 -231
  297. package/test/e2e/smoke-install-mcp.sh +0 -113
  298. package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
  299. package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
  300. package/test/fixtures/signals/action_item/sample.md +0 -16
  301. package/test/fixtures/signals/summary/sample.md +0 -12
  302. package/test/fixtures/sources/meetings/sample.md +0 -25
  303. package/test/helpers/cli-runner.ts +0 -150
  304. package/test/helpers/s3-list-mock.ts +0 -79
  305. package/test/helpers/vault-service-mock.ts +0 -295
  306. package/test/sources-signals/smoke.test.ts +0 -228
  307. package/tsconfig.json +0 -20
  308. package/vitest.config.ts +0 -11
  309. 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
- }