@indigoai-us/hq-cli 5.97.2 → 5.97.3-rc.1

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.97.3-rc.1] — 2026-08-10
6
+
5
7
  ## [5.97.2]
6
8
 
7
9
  ### Fixed
@@ -27,6 +27,44 @@ import { type BillingErrorPayload } from "../utils/billing-gate.js";
27
27
  export declare const VALID_EFFORTS: Set<string>;
28
28
  /** Service-tier (speed) values hq-pro accepts on `runtime-config`. */
29
29
  export declare const VALID_TIERS: Set<string>;
30
+ /** Agent runtimes hq-pro accepts on `POST /v1/agents` (`AgentProvider`). */
31
+ export declare const VALID_PROVIDERS: Set<string>;
32
+ /** Auth modes hq-pro accepts on `POST /v1/agents` (`CodexAuthMode`). */
33
+ export declare const VALID_AUTH_MODES: Set<string>;
34
+ /**
35
+ * Resolve a closed-set option value, or exit(1) with a message naming the
36
+ * offending input and the legal set.
37
+ *
38
+ * WHY THIS EXISTS. `--effort` and `--tier` were already validated this way, but
39
+ * `--provider` and `--auth-mode` were resolved with a ternary that FELL BACK to
40
+ * the default on anything unrecognised:
41
+ *
42
+ * opts.provider === "grok" ? "grok" : opts.provider ? "codex" : undefined
43
+ * opts.authMode === "apiKey" ? "apiKey" : "subscription"
44
+ *
45
+ * So `--provider claude` (before claude was supported) silently provisioned a
46
+ * CODEX box, and `--auth-mode typo` silently provisioned in SUBSCRIPTION mode.
47
+ * Both paths are behind a `--yes` charge gate, so the operator paid $100/month
48
+ * for a runtime they did not ask for, with nothing in the output to say a
49
+ * substitution had happened. A wrong flag must fail loudly, never resolve to a
50
+ * plausible default.
51
+ *
52
+ * `undefined` in ⇒ `undefined` out: an OMITTED option is not an invalid one, and
53
+ * keeps the server-side default authoritative.
54
+ *
55
+ * Matching is trim + lowercase to be forgiving of shell padding and casing,
56
+ * EXCEPT that the returned value is the canonical member of the set — so the
57
+ * wire value is always exactly what hq-pro's validators expect. Note `apiKey`
58
+ * is camelCase on the wire, so the comparison set is keyed on the lowercased
59
+ * form and mapped back (see {@link canonicalizeOptionValue}).
60
+ */
61
+ export declare function parseEnumOption(raw: string | undefined, allowed: Set<string>, flagName: string): string | undefined;
62
+ /**
63
+ * Case-insensitively map `raw` onto its canonical member of `allowed`, or
64
+ * `undefined` when it is not a member. Split out from {@link parseEnumOption}
65
+ * so the mapping is unit-testable without a process exit.
66
+ */
67
+ export declare function canonicalizeOptionValue(raw: string, allowed: Set<string>): string | undefined;
30
68
  /**
31
69
  * A non-2xx from the `/v1/agents` control plane. Carries the HTTP status and
32
70
  * the registry error `code` (when present) so callers can branch — notably a
@@ -89,7 +127,7 @@ export interface ProvisionAgentInput {
89
127
  name: string;
90
128
  slug: string;
91
129
  codexAuthMode: "subscription" | "apiKey";
92
- provider?: "codex" | "grok";
130
+ provider?: "codex" | "grok" | "claude";
93
131
  codexApiKey?: string;
94
132
  idempotencyKey: string;
95
133
  title?: string;
@@ -37,6 +37,61 @@ export const VALID_EFFORTS = new Set([
37
37
  ]);
38
38
  /** Service-tier (speed) values hq-pro accepts on `runtime-config`. */
39
39
  export const VALID_TIERS = new Set(["default", "priority"]);
40
+ /** Agent runtimes hq-pro accepts on `POST /v1/agents` (`AgentProvider`). */
41
+ export const VALID_PROVIDERS = new Set(["codex", "grok", "claude"]);
42
+ /** Auth modes hq-pro accepts on `POST /v1/agents` (`CodexAuthMode`). */
43
+ export const VALID_AUTH_MODES = new Set(["subscription", "apiKey"]);
44
+ /**
45
+ * Resolve a closed-set option value, or exit(1) with a message naming the
46
+ * offending input and the legal set.
47
+ *
48
+ * WHY THIS EXISTS. `--effort` and `--tier` were already validated this way, but
49
+ * `--provider` and `--auth-mode` were resolved with a ternary that FELL BACK to
50
+ * the default on anything unrecognised:
51
+ *
52
+ * opts.provider === "grok" ? "grok" : opts.provider ? "codex" : undefined
53
+ * opts.authMode === "apiKey" ? "apiKey" : "subscription"
54
+ *
55
+ * So `--provider claude` (before claude was supported) silently provisioned a
56
+ * CODEX box, and `--auth-mode typo` silently provisioned in SUBSCRIPTION mode.
57
+ * Both paths are behind a `--yes` charge gate, so the operator paid $100/month
58
+ * for a runtime they did not ask for, with nothing in the output to say a
59
+ * substitution had happened. A wrong flag must fail loudly, never resolve to a
60
+ * plausible default.
61
+ *
62
+ * `undefined` in ⇒ `undefined` out: an OMITTED option is not an invalid one, and
63
+ * keeps the server-side default authoritative.
64
+ *
65
+ * Matching is trim + lowercase to be forgiving of shell padding and casing,
66
+ * EXCEPT that the returned value is the canonical member of the set — so the
67
+ * wire value is always exactly what hq-pro's validators expect. Note `apiKey`
68
+ * is camelCase on the wire, so the comparison set is keyed on the lowercased
69
+ * form and mapped back (see {@link canonicalizeOptionValue}).
70
+ */
71
+ export function parseEnumOption(raw, allowed, flagName) {
72
+ if (raw === undefined)
73
+ return undefined;
74
+ const canonical = canonicalizeOptionValue(raw, allowed);
75
+ if (canonical === undefined) {
76
+ const legal = [...allowed].join(", ");
77
+ console.error(chalk.red(`Invalid ${flagName} '${raw}': must be one of ${legal}`));
78
+ process.exit(1);
79
+ }
80
+ return canonical;
81
+ }
82
+ /**
83
+ * Case-insensitively map `raw` onto its canonical member of `allowed`, or
84
+ * `undefined` when it is not a member. Split out from {@link parseEnumOption}
85
+ * so the mapping is unit-testable without a process exit.
86
+ */
87
+ export function canonicalizeOptionValue(raw, allowed) {
88
+ const needle = raw.trim().toLowerCase();
89
+ for (const candidate of allowed) {
90
+ if (candidate.toLowerCase() === needle)
91
+ return candidate;
92
+ }
93
+ return undefined;
94
+ }
40
95
  /**
41
96
  * A non-2xx from the `/v1/agents` control plane. Carries the HTTP status and
42
97
  * the registry error `code` (when present) so callers can branch — notably a
@@ -335,16 +390,28 @@ export function registerAgentsCommand(program) {
335
390
  .description("Provision a new cloud agent ($100/month — requires --yes)")
336
391
  .option("--company <slug>", "Company slug (resolves to companyUid)")
337
392
  .option("--slug <slug>", "Agent slug (defaults to a slug of <name>)")
338
- .option("--provider <provider>", "Runtime: codex | grok (default codex)")
339
- .option("--auth-mode <mode>", "Codex auth: subscription | apiKey (default subscription)", "subscription")
393
+ .option("--provider <provider>", "Runtime: codex | grok | claude (default codex). claude is subscription-only")
394
+ .option("--auth-mode <mode>", "Auth: subscription | apiKey (default subscription)", "subscription")
340
395
  .option("--api-key-env <VAR>", "Env var holding the API key for --auth-mode apiKey (never pass the key as a flag)")
341
396
  .option("--title <title>", "Org-chart job title")
342
397
  .option("--description <text>", "Short description / bio")
343
398
  .option("--yes", "Confirm the $100/month charge (required to provision)")
344
399
  .action(async function (name, opts) {
345
400
  try {
346
- const authMode = opts.authMode === "apiKey" ? "apiKey" : "subscription";
347
- const provider = opts.provider === "grok" ? "grok" : opts.provider ? "codex" : undefined;
401
+ // Both of these previously fell back to their default on an
402
+ // unrecognised value, silently provisioning a billable box the operator
403
+ // did not ask for. They now exit(1) instead — see parseEnumOption.
404
+ const authMode = parseEnumOption(opts.authMode, VALID_AUTH_MODES, "--auth-mode") ?? "subscription";
405
+ const provider = parseEnumOption(opts.provider, VALID_PROVIDERS, "--provider");
406
+ // claude is subscription-only on hq-pro (rejectIncompatibleProviderAuthMode
407
+ // returns AGENT_PROVIDER_INCOMPATIBLE_WITH_AUTH_MODE). Catch it here so the
408
+ // operator gets a direct message instead of a 400 from the control plane
409
+ // AFTER clearing the charge gate. The server stays authoritative; this is
410
+ // only a friendlier, earlier copy of the same rule.
411
+ if (provider === "claude" && authMode === "apiKey") {
412
+ console.error(chalk.red("The claude provider is subscription-only — drop --auth-mode apiKey."));
413
+ process.exit(1);
414
+ }
348
415
  // Resolve the API key from the environment (never from a flag value) when
349
416
  // apiKey mode is requested — validated BEFORE the paid gate so we don't
350
417
  // charge and then fail.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.97.2",
3
+ "version": "5.97.3-rc.1",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {