@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.
- package/CHANGELOG.md +34 -0
- package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
- package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
- package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
- package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
- package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
- package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
- package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
- package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
- package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
- package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
- package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
- package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
- package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
- package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
- package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
- package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
- package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
- package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
- package/assets/scaffold/core/scripts/worktree.sh +136 -0
- package/dist/commands/agents.js +28 -12
- package/dist/commands/api-keys.js +100 -20
- package/dist/commands/core.d.ts +90 -0
- package/dist/commands/core.js +279 -0
- package/dist/commands/files-browse.d.ts +21 -0
- package/dist/commands/files-browse.js +145 -24
- package/dist/commands/files.js +33 -7
- package/dist/commands/members.js +16 -5
- package/dist/commands/pack-install.d.ts +14 -0
- package/dist/commands/pack-install.js +51 -2
- package/dist/commands/pkg-install.js +43 -0
- package/dist/commands/run.js +8 -0
- package/dist/commands/secrets.js +125 -65
- package/dist/main.js +6 -0
- package/dist/utils/api-key-command-gate.d.ts +45 -0
- package/dist/utils/api-key-command-gate.js +51 -0
- package/dist/utils/hq-roots.d.ts +91 -0
- package/dist/utils/hq-roots.js +212 -0
- package/dist/utils/resolve-vault-credential.d.ts +63 -3
- package/dist/utils/resolve-vault-credential.js +152 -6
- package/dist/utils/run-bundled-script.d.ts +66 -0
- package/dist/utils/run-bundled-script.js +80 -0
- package/dist/utils/vault-api.d.ts +21 -0
- package/dist/utils/vault-api.js +74 -3
- package/package.json +7 -1
- package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
- package/.github/workflows/ci.yml +0 -46
- package/.github/workflows/publish.yml +0 -36
- package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
- package/e2e/cli.test.ts +0 -128
- package/eslint.config.js +0 -37
- package/pnpm-workspace.yaml +0 -4
- package/scripts/chmod-bins.mjs +0 -49
- package/scripts/generate-dsn.mjs +0 -26
- package/scripts/generate-scan-packages-table.mjs +0 -113
- package/scripts/smoke-sources-signals.sh +0 -103
- package/src/bin/hq-auth-refresh.ts +0 -43
- package/src/cli-version.ts +0 -13
- package/src/commands/__fixtures__/make-tar.ts +0 -126
- package/src/commands/_patterns.ts +0 -10
- package/src/commands/add.ts +0 -75
- package/src/commands/agents.test.ts +0 -527
- package/src/commands/agents.ts +0 -923
- package/src/commands/api-keys.test.ts +0 -291
- package/src/commands/api-keys.ts +0 -382
- package/src/commands/artifact-verify.test.ts +0 -177
- package/src/commands/auth.test.ts +0 -82
- package/src/commands/auth.ts +0 -213
- package/src/commands/billing.test.ts +0 -158
- package/src/commands/billing.ts +0 -146
- package/src/commands/channels.test.ts +0 -74
- package/src/commands/channels.ts +0 -91
- package/src/commands/cloud-demote.test.ts +0 -401
- package/src/commands/cloud-demote.ts +0 -278
- package/src/commands/cloud-provision.test.ts +0 -1117
- package/src/commands/cloud-provision.ts +0 -942
- package/src/commands/cloud.pull-all.test.ts +0 -711
- package/src/commands/cloud.pull-per-company.test.ts +0 -188
- package/src/commands/cloud.push-all.test.ts +0 -485
- package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
- package/src/commands/cloud.selectors.test.ts +0 -208
- package/src/commands/cloud.status.test.ts +0 -160
- package/src/commands/cloud.test.ts +0 -41
- package/src/commands/cloud.ts +0 -2130
- package/src/commands/company.test.ts +0 -202
- package/src/commands/company.ts +0 -148
- package/src/commands/creators.test.ts +0 -60
- package/src/commands/creators.ts +0 -117
- package/src/commands/crm.test.ts +0 -179
- package/src/commands/crm.ts +0 -236
- package/src/commands/db-migrate.ts +0 -55
- package/src/commands/db-provision.ts +0 -114
- package/src/commands/db-sql.ts +0 -124
- package/src/commands/db-status.ts +0 -108
- package/src/commands/db.ts +0 -26
- package/src/commands/dm.test.ts +0 -748
- package/src/commands/dm.ts +0 -1226
- package/src/commands/feedback.test.ts +0 -413
- package/src/commands/feedback.ts +0 -169
- package/src/commands/files-browse.test.ts +0 -1249
- package/src/commands/files-browse.ts +0 -1506
- package/src/commands/files-delete.test.ts +0 -484
- package/src/commands/files-recovery.test.ts +0 -370
- package/src/commands/files.test.ts +0 -722
- package/src/commands/files.ts +0 -1446
- package/src/commands/group-grants.test.ts +0 -330
- package/src/commands/group-grants.ts +0 -455
- package/src/commands/groups.test.ts +0 -44
- package/src/commands/groups.ts +0 -368
- package/src/commands/integrations.test.ts +0 -633
- package/src/commands/integrations.ts +0 -507
- package/src/commands/list.ts +0 -85
- package/src/commands/login.ts +0 -52
- package/src/commands/logout.ts +0 -30
- package/src/commands/marketplace-install.test.ts +0 -543
- package/src/commands/marketplace-security.test.ts +0 -646
- package/src/commands/mcp-registration.test.ts +0 -2787
- package/src/commands/mcp-registration.ts +0 -2612
- package/src/commands/mcp-status.test.ts +0 -483
- package/src/commands/mcp-status.ts +0 -575
- package/src/commands/mcp-status.us011.test.ts +0 -243
- package/src/commands/meetings.test.ts +0 -522
- package/src/commands/meetings.ts +0 -789
- package/src/commands/members.test.ts +0 -1474
- package/src/commands/members.ts +0 -1181
- package/src/commands/onboard-identity-guard.test.ts +0 -99
- package/src/commands/onboard-identity-guard.ts +0 -103
- package/src/commands/onboard-join.test.ts +0 -96
- package/src/commands/onboard-join.ts +0 -101
- package/src/commands/onboard-warning.test.ts +0 -26
- package/src/commands/onboard-warning.ts +0 -12
- package/src/commands/onboard.ts +0 -267
- package/src/commands/outposts-heartbeat.test.ts +0 -299
- package/src/commands/outposts-heartbeat.ts +0 -310
- package/src/commands/outposts-replica-sync.test.ts +0 -272
- package/src/commands/outposts-self-deploy.test.ts +0 -243
- package/src/commands/outposts.test.ts +0 -1039
- package/src/commands/outposts.ts +0 -1935
- package/src/commands/pack-install-secret-authorization.test.ts +0 -115
- package/src/commands/pack-install.test.ts +0 -1469
- package/src/commands/pack-install.ts +0 -2247
- package/src/commands/pack-update-cache.test.ts +0 -149
- package/src/commands/packs.test.ts +0 -88
- package/src/commands/packs.ts +0 -622
- package/src/commands/people.test.ts +0 -807
- package/src/commands/people.ts +0 -422
- package/src/commands/pkg-install.dispatch.test.ts +0 -71
- package/src/commands/pkg-install.ts +0 -215
- package/src/commands/pkg-list.test.ts +0 -79
- package/src/commands/pkg-list.ts +0 -13
- package/src/commands/pkg-remove.ts +0 -71
- package/src/commands/pkg-update.ts +0 -189
- package/src/commands/publish.test.ts +0 -650
- package/src/commands/publish.ts +0 -580
- package/src/commands/reindex.test.ts +0 -259
- package/src/commands/reindex.ts +0 -274
- package/src/commands/rescue.test.ts +0 -39
- package/src/commands/rescue.ts +0 -210
- package/src/commands/run.env-local.test.ts +0 -88
- package/src/commands/run.test.ts +0 -45
- package/src/commands/run.ts +0 -184
- package/src/commands/safe-extract.test.ts +0 -459
- package/src/commands/safe-extract.ts +0 -444
- package/src/commands/secrets-scope.test.ts +0 -56
- package/src/commands/secrets-scope.ts +0 -32
- package/src/commands/secrets.parse-destination.test.ts +0 -38
- package/src/commands/secrets.test.ts +0 -2037
- package/src/commands/secrets.ts +0 -2240
- package/src/commands/signals.ts +0 -351
- package/src/commands/skill.test.ts +0 -770
- package/src/commands/skill.ts +0 -796
- package/src/commands/sources.ts +0 -362
- package/src/commands/sync-mode.test.ts +0 -366
- package/src/commands/sync-mode.ts +0 -387
- package/src/commands/sync-narrow.test.ts +0 -633
- package/src/commands/sync-narrow.ts +0 -555
- package/src/commands/sync.ts +0 -165
- package/src/commands/team-sync.ts +0 -628
- package/src/commands/update.ts +0 -71
- package/src/commands/whoami.ts +0 -83
- package/src/commands/workers.test.ts +0 -158
- package/src/commands/workers.ts +0 -301
- package/src/index.test.ts +0 -32
- package/src/index.ts +0 -20
- package/src/lib/db/control-plane.test.ts +0 -59
- package/src/lib/db/control-plane.ts +0 -113
- package/src/lib/db/local.test.ts +0 -81
- package/src/lib/db/local.ts +0 -148
- package/src/lib/db/migrate.test.ts +0 -133
- package/src/lib/db/migrate.ts +0 -137
- package/src/lib/db/paths.test.ts +0 -112
- package/src/lib/db/paths.ts +0 -128
- package/src/lib/db/remote-engine.test.ts +0 -44
- package/src/lib/db/remote-engine.ts +0 -148
- package/src/lib/db/remote-sql.test.ts +0 -32
- package/src/lib/db/remote-sql.ts +0 -62
- package/src/lib/db/sql.test.ts +0 -106
- package/src/lib/db/sql.ts +0 -192
- package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
- package/src/lib/local-tree-diff.test.ts +0 -301
- package/src/lib/local-tree-diff.ts +0 -362
- package/src/lib/narrow-hint-banner.test.ts +0 -235
- package/src/lib/narrow-hint-banner.ts +0 -212
- package/src/lib/plan-limit-nag.test.ts +0 -317
- package/src/lib/plan-limit-nag.ts +0 -264
- package/src/main.ts +0 -371
- package/src/node-preflight.test.ts +0 -60
- package/src/node-preflight.ts +0 -67
- package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
- package/src/outpost/session-heartbeat-guard.test.ts +0 -105
- package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
- package/src/outpost/session-heartbeat-publisher.ts +0 -186
- package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
- package/src/outpost/session-heartbeat.test.ts +0 -459
- package/src/outpost/session-heartbeat.ts +0 -877
- package/src/packaging.test.ts +0 -109
- package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
- package/src/run/discover-schemas.test.ts +0 -153
- package/src/run/discover-schemas.ts +0 -79
- package/src/run/hq-plugin.test.ts +0 -371
- package/src/run/hq-plugin.ts +0 -277
- package/src/run/varlock-shape.test.ts +0 -57
- package/src/schemas/hq-package.schema.json +0 -155
- package/src/sentry-before-send.test.ts +0 -359
- package/src/sentry-before-send.ts +0 -221
- package/src/sentry-epipe.test.ts +0 -37
- package/src/sentry-release.test.ts +0 -54
- package/src/sentry.test.ts +0 -24
- package/src/sentry.ts +0 -54
- package/src/strategies/link.ts +0 -62
- package/src/strategies/merge.ts +0 -142
- package/src/types.ts +0 -146
- package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
- package/src/utils/auth-error.test.ts +0 -40
- package/src/utils/auth-error.ts +0 -42
- package/src/utils/billing-gate.test.ts +0 -95
- package/src/utils/billing-gate.ts +0 -228
- package/src/utils/breadcrumb-buffer.ts +0 -18
- package/src/utils/cli-telemetry.test.ts +0 -153
- package/src/utils/cli-telemetry.ts +0 -61
- package/src/utils/cognito-session.machine.test.ts +0 -109
- package/src/utils/cognito-session.refresh.test.ts +0 -84
- package/src/utils/cognito-session.test.ts +0 -328
- package/src/utils/cognito-session.ts +0 -448
- package/src/utils/company-selection-error.test.ts +0 -42
- package/src/utils/company-selection-error.ts +0 -45
- package/src/utils/contribution-table.ts +0 -83
- package/src/utils/environmental-error.test.ts +0 -45
- package/src/utils/environmental-error.ts +0 -39
- package/src/utils/epipe.test.ts +0 -28
- package/src/utils/epipe.ts +0 -29
- package/src/utils/expected-cli-error.test.ts +0 -28
- package/src/utils/expected-cli-error.ts +0 -39
- package/src/utils/feedback-diagnostics.test.ts +0 -183
- package/src/utils/feedback-diagnostics.ts +0 -123
- package/src/utils/feedback-screenshots.test.ts +0 -134
- package/src/utils/feedback-screenshots.ts +0 -124
- package/src/utils/feedback-versions.test.ts +0 -98
- package/src/utils/feedback-versions.ts +0 -68
- package/src/utils/git.ts +0 -74
- package/src/utils/integrity.ts +0 -59
- package/src/utils/intercepted-process-exit.test.ts +0 -37
- package/src/utils/intercepted-process-exit.ts +0 -36
- package/src/utils/login-provider.test.ts +0 -42
- package/src/utils/login-provider.ts +0 -30
- package/src/utils/manifest.test.ts +0 -167
- package/src/utils/manifest.ts +0 -131
- package/src/utils/pack-contributions.test.ts +0 -603
- package/src/utils/pack-contributions.ts +0 -450
- package/src/utils/people.ts +0 -215
- package/src/utils/registry-client.ts +0 -204
- package/src/utils/registry.ts +0 -90
- package/src/utils/resolve-vault-credential.test.ts +0 -69
- package/src/utils/resolve-vault-credential.ts +0 -60
- package/src/utils/sandbox-runner-client.test.ts +0 -279
- package/src/utils/sandbox-runner-client.ts +0 -260
- package/src/utils/secrets-cache.ts +0 -183
- package/src/utils/sentry-identity.ts +0 -45
- package/src/utils/settle-with-timeout.test.ts +0 -21
- package/src/utils/settle-with-timeout.ts +0 -22
- package/src/utils/user-yaml-error.test.ts +0 -24
- package/src/utils/user-yaml-error.ts +0 -30
- package/src/utils/vault-api.test.ts +0 -536
- package/src/utils/vault-api.ts +0 -382
- package/src/utils/version-check.test.ts +0 -176
- package/src/utils/version-check.ts +0 -155
- package/src/utils/version-gate.test.ts +0 -1024
- package/src/utils/version-gate.ts +0 -702
- package/test/commands/db-tenant-isolation.test.ts +0 -94
- package/test/commands/db.test.ts +0 -85
- package/test/commands/signals.test.ts +0 -207
- package/test/commands/sources.test.ts +0 -231
- package/test/e2e/smoke-install-mcp.sh +0 -113
- package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
- package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
- package/test/fixtures/signals/action_item/sample.md +0 -16
- package/test/fixtures/signals/summary/sample.md +0 -12
- package/test/fixtures/sources/meetings/sample.md +0 -25
- package/test/helpers/cli-runner.ts +0 -150
- package/test/helpers/s3-list-mock.ts +0 -79
- package/test/helpers/vault-service-mock.ts +0 -295
- package/test/sources-signals/smoke.test.ts +0 -228
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -11
- package/vitest.e2e.config.ts +0 -7
package/src/commands/agents.ts
DELETED
|
@@ -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
|
-
}
|