@indigoai-us/hq-cli 5.101.6 → 5.101.7

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,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.101.7] — 2026-08-17
6
+
7
+ ### Changed
8
+
9
+ - **`hq outposts`** is now sourced from `@indigoai-us/hq-cloud` (≥ 6.15.5)
10
+ instead of being implemented in the CLI. The command surface and behavior are
11
+ unchanged; the control-plane client, box-side helpers, and command tree moved
12
+ into hq-cloud so they can also be consumed directly (including from a Next.js
13
+ app) via the new `@indigoai-us/hq-cloud/outposts` subpath exports. The CLI
14
+ keeps its own authenticated transport, so Sentry breadcrumbs, API-key routing,
15
+ and plan-gate handling are preserved.
16
+
5
17
  ## [5.101.6] — 2026-08-17
6
18
 
7
19
  ### Added
@@ -1,240 +1,26 @@
1
1
  /**
2
- * `hq outposts` — manage your personal HQ Outposts (EC2 boxes) from the
3
- * terminal instead of the web console. Targets the hq-pro `/outpost/*`
4
- * control plane on `DEFAULT_VAULT_API_URL` via the shared `vaultApiFetch`
5
- * helper — the same routes the console's outpost panel calls.
2
+ * `hq outposts` — thin adapter over the Outpost command tree, which now lives in
3
+ * @indigoai-us/hq-cloud (`/outposts/cli`).
6
4
  *
7
- * Outposts are PERSONAL / caller-scoped: hq-pro keys every `/outpost/*` route
8
- * on the caller's Cognito sub, so there is no `--company`. `--id <outpostId>`
9
- * selects a specific box (passed as the `outpostId` query param); when omitted
10
- * hq-pro targets the caller's primary slot.
5
+ * The wire contract, the command surface, and the box-side helpers all moved out
6
+ * of the CLI so they can be shared — notably so a Next.js app can drive the same
7
+ * `/outpost/*` control plane through `@indigoai-us/hq-cloud/outposts`. All the CLI
8
+ * keeps is the wiring: its own authenticated transport (`vaultApiFetch`, which
9
+ * carries Sentry breadcrumbs, the `hqk_` route-rewrite table, and plan-gate
10
+ * decoding), its token resolution, and its cached-session read.
11
11
  *
12
- * Subcommands:
13
- * hq outposts list every Outpost you own (row summaries)
14
- * hq outposts status [--id] — live detail for one box
15
- * hq outposts codex-enable [--id] — enable / retry Codex on the box
16
- * hq outposts login [--id] — request a fresh login URL
17
- * hq outposts destroy [--id] --yes — tear the box down (destructive; flag-guarded)
18
- *
19
- * NOTE: hq-pro exposes no rename or settings-mutation route for Outposts (the
20
- * web console can't rename them either), so this CLI wraps only the lifecycle
21
- * and status routes that exist. Renaming an Outpost is not a backend capability.
22
- */
23
- import { Command } from "commander";
24
- import { spawnSync } from "node:child_process";
25
- import { type BillingErrorPayload } from "../utils/billing-gate.js";
26
- /**
27
- * hq-pro's per-person cap envelope on a `409` provision block. Unlike every
28
- * other `/outpost/*` failure this body carries NO `message`/`error` field —
29
- * only the cap facts — so it has to be decoded structurally or the reason
30
- * degrades to a bare `res.statusText` ("Conflict").
31
- */
32
- export interface OutpostCappedPayload {
33
- limit: number;
34
- outposts: OutpostSummary[];
35
- }
36
- /** Decode hq-pro's `{ capped, limit, outposts }` cap envelope, if present. */
37
- export declare function parseCappedPayload(body: unknown): OutpostCappedPayload | undefined;
38
- /** A non-2xx from the `/outpost/*` control plane. Carries status + `step`. */
39
- export declare class OutpostHttpError extends Error {
40
- status: number;
41
- step?: string;
42
- /** hq-pro's billing envelope on a `402 billing_required` provision block. */
43
- billing?: BillingErrorPayload;
44
- /** hq-pro's cap envelope on a `409` provision block. */
45
- capped?: OutpostCappedPayload;
46
- constructor(status: number, message: string, step?: string, billing?: BillingErrorPayload, capped?: OutpostCappedPayload);
47
- }
48
- /** Row summary from `GET /outpost/list`. */
49
- export interface OutpostSummary {
50
- outpostId: string;
51
- state: string;
52
- instanceName: string;
53
- region: string;
54
- agentRuntime: string;
55
- platform: string;
56
- createdAt: string;
57
- [key: string]: unknown;
58
- }
59
- /**
60
- * Authenticated JSON round-trip against the outpost control plane. Throws
61
- * `OutpostHttpError` on any non-2xx (never swallows — hq-never-swallow-errors),
62
- * decoding hq-pro's `{ error | message, step }` envelope for the reason. The
63
- * `step` is preserved so callers can recognise the `destroy` route's
64
- * `teardown-incomplete` 409 (which means "retry", not "failed").
65
- */
66
- export declare function outpostRequest<T>(opts: {
67
- token: string;
68
- path: string;
69
- method?: string;
70
- body?: Record<string, unknown>;
71
- query?: Record<string, string>;
72
- }): Promise<T>;
73
- /**
74
- * Provision the caller's Outpost. Sends the cached Cognito refresh token so the
75
- * box can authenticate AS the caller (the same body the console's
76
- * `provisionMyOutpost` sends). No duplicate is ever created: a caller already at
77
- * their per-person cap gets a `409` whose body lists their existing boxes —
78
- * thrown here as an `OutpostHttpError` carrying `capped` (hq-pro checks the cap
79
- * BEFORE activation billing, so a capped call is never charged). The refresh
80
- * token is sent over HTTPS and NEVER printed.
81
- */
82
- export declare function provisionOutpost(token: string, input: {
83
- refreshToken: string;
84
- clientIp?: string;
85
- diskSizeGb?: number;
86
- agentRuntime?: "claude" | "codex";
87
- }): Promise<Record<string, unknown>>;
88
- export declare function listOutposts(token: string): Promise<OutpostSummary[]>;
89
- export declare function getOutpostStatus(token: string, outpostId?: string): Promise<Record<string, unknown>>;
90
- export declare function enableCodex(token: string, outpostId?: string): Promise<Record<string, unknown>>;
91
- export declare function regenerateLoginUrl(token: string, outpostId?: string): Promise<Record<string, unknown>>;
92
- /**
93
- * Hand the box the one-time Claude sign-in code the operator got from the login
94
- * URL. hq-pro stores it as the row's pending code; the box polls for it, feeds
95
- * it to `claude`, and flips itself `awaiting-claude-login → ready`. This is the
96
- * terminal-native equivalent of pasting the code into the web console.
97
- */
98
- export declare function submitLoginCode(token: string, code: string, outpostId?: string): Promise<Record<string, unknown>>;
99
- export declare function destroyOutpost(token: string, outpostId?: string): Promise<Record<string, unknown>>;
100
- /** Result of `POST /outpost/exec` — a terminal SSM invocation on the box. */
101
- export interface OutpostExecResult {
102
- ok: true;
103
- outpostId: string;
104
- instanceId: string;
105
- commandId: string;
106
- /** SSM invocation status (Success | Failed | Cancelled). */
107
- status: string;
108
- /** Remote process exit code, or null when SSM reported none. */
109
- exitCode: number | null;
110
- stdout: string;
111
- stderr: string;
112
- /** True when SSM clipped stdout/stderr at its inline output limit. */
113
- truncated: boolean;
114
- }
115
- /**
116
- * Run a one-shot shell command on the caller's Outpost via `POST /outpost/exec`
117
- * (server-brokered SSM — no SSH). Throws `OutpostHttpError` on a non-2xx, whose
118
- * `step` distinguishes not-ready / platform-unsupported / timeout / ssm.
119
- */
120
- export declare function execOutpost(token: string, command: string, outpostId?: string): Promise<OutpostExecResult>;
121
- /** Presigned input-upload details from `mode: "stage"`. */
122
- export interface OutpostExecStage {
123
- ok: true;
124
- userId: string;
125
- outpostId: string;
126
- key: string;
127
- putUrl: string;
128
- getUrl: string;
129
- expiresInSeconds: number;
130
- }
131
- /** Asynchronous SSM command details from `mode: "submit"`. */
132
- export interface OutpostExecSubmission {
133
- ok: true;
134
- userId: string;
135
- outpostId: string;
136
- instanceId: string;
137
- commandId: string;
138
- outputPrefix: string;
139
- /** Shell budget applied server-side (AWS-RunShellScript executionTimeout). */
140
- executionTimeoutSeconds?: number;
141
- }
142
- /** Poll response from `mode: "result"`; streams arrive only when terminal. */
143
- export interface OutpostExecAsyncResult {
144
- ok: true;
145
- userId: string;
146
- outpostId: string;
147
- status: string;
148
- done: boolean;
149
- exitCode?: number | null;
150
- stdout?: string;
151
- stderr?: string;
152
- truncated?: boolean;
153
- }
154
- export declare function stageExecInput(token: string, outpostId?: string): Promise<OutpostExecStage>;
155
- export declare function submitExec(token: string, command: string, outpostId?: string, timeoutSeconds?: number): Promise<OutpostExecSubmission>;
156
- export declare function fetchExecResult(token: string, commandId: string, outpostId?: string): Promise<OutpostExecAsyncResult>;
157
- /** Preserve a single command string; safely join argv when Commander split it. */
158
- export declare function joinCommandParts(commandParts: string[]): string;
159
- /**
160
- * Prefix that best-effort `cd`s into the box's HQ checkout before running the
161
- * caller's command. `exec` runs over two transports with two different default
162
- * working directories — SSM runs as root with no `$HOME` (cwd `/usr/bin`) and
163
- * SSH lands in the login user's home — so without this, `hq outposts exec -- pwd`
164
- * printed an unhelpful, transport-dependent directory. Initialize a real root
165
- * home for the SSM case before resolving the HQ folder: tools run by the caller
166
- * (notably `gh`) otherwise treat the HQ checkout as their home and can create
167
- * root-owned machine state inside it. The trailing `|| true` keeps the command
168
- * running from the default directory when no HQ checkout is present, so exec
169
- * never fails merely because the box has no HQ folder.
170
- */
171
- export declare const REMOTE_HQ_DIR_PREFIX = "export HOME=\"${HOME:-/root}\"; cd \"$HOME/hq\" 2>/dev/null || cd ~ec2-user/hq 2>/dev/null || true";
172
- /** Wrap `command` so it runs from the box's HQ folder (see REMOTE_HQ_DIR_PREFIX). */
173
- export declare function withRemoteHqDir(command: string): string;
174
- /** SSH connection details vended by `POST /outpost/ssh-access`. */
175
- export interface OutpostSshAccess {
176
- outpostId: string;
177
- platform: "ec2" | "lightsail";
178
- host: string;
179
- port: number;
180
- username: string;
181
- /** PEM private key for the box. SENSITIVE — never printed or logged. */
182
- privateKey: string;
183
- }
184
- /**
185
- * Fetch SSH connection info + key for the caller's Outpost and open the caller's
186
- * IP on the box's SSH port. Used to reach a Lightsail box (no SSM). Throws
187
- * `OutpostHttpError` on a non-2xx.
12
+ * `vaultApiFetch` satisfies hq-cloud's `OutpostTransport` structurally — the
13
+ * option bags match field-for-field so it is passed straight through with no
14
+ * adapter shim.
188
15
  */
189
- export declare function getOutpostSshAccess(token: string, outpostId?: string): Promise<OutpostSshAccess>;
16
+ import type { Command } from "commander";
17
+ import type { ReplicaSyncDependencies } from "@indigoai-us/hq-cloud/outposts/node";
190
18
  /**
191
- * Run `command` on the box over SSH using vended access details. Writes the
192
- * private key to a locked-down temp file, runs a non-interactive `ssh`, and
193
- * returns stdout/stderr/exitCode. The key file + a throwaway known_hosts file
194
- * are always cleaned up; the key is never printed. `exitCode` is `null` only
195
- * when `ssh` itself couldn't run (e.g. binary missing) — surfaced via `error`.
196
- */
197
- export declare function execViaSsh(access: OutpostSshAccess, command: string): {
198
- stdout: string;
199
- stderr: string;
200
- exitCode: number | null;
201
- error?: string;
202
- };
203
- /** The small local-environment surface used by `outposts self-deploy`. */
204
- export interface SelfDeployDependencies {
205
- spawnSync: (command: string, args: string[], options?: Parameters<typeof spawnSync>[2]) => ReturnType<typeof spawnSync>;
206
- readTextFile: (file: string) => string;
207
- loadCachedTokens: () => {
208
- refreshToken?: string;
209
- idToken?: string;
210
- } | undefined;
211
- getUid: () => number | undefined;
212
- isStdinTty: () => boolean;
213
- confirm: () => Promise<boolean>;
214
- defaultHqRoot: () => string;
215
- invokingUser: () => string;
216
- }
217
- /**
218
- * The extra local surface `replica-sync` needs on top of the self-deploy set:
219
- * two filesystem probes for the repos clone. Kept as an extension so the
220
- * `self-deploy` dependency surface stays minimal.
221
- */
222
- export interface ReplicaSyncDependencies extends SelfDeployDependencies {
223
- pathExists: (p: string) => boolean;
224
- mkdirp: (p: string) => void;
225
- }
226
- /** One repo entry resolved from `personal/data/repos.yaml`. */
227
- export interface ReplicaRepo {
228
- url: string;
229
- visibility: "public" | "private";
230
- name: string;
231
- }
232
- /**
233
- * Parse `personal/data/repos.yaml` into a clone list. Pure + exported for
234
- * tests. Mirrors setup.sh's `clone_repos`: `visibility` defaults to public,
235
- * `name` defaults to the URL basename. Malformed entries are skipped, never
236
- * thrown.
19
+ * Register the `hq outposts` command group.
20
+ *
21
+ * `selfDeployOverrides` lets a test replace the box-side host-environment probes
22
+ * (spawnSync, /etc/os-release reads, uid checks) without a real EC2 host; it is
23
+ * unused in production.
237
24
  */
238
- export declare function parseReposManifest(yamlText: string): ReplicaRepo[];
239
25
  export declare function registerOutpostsCommand(program: Command, selfDeployOverrides?: Partial<ReplicaSyncDependencies>): void;
240
26
  //# sourceMappingURL=outposts.d.ts.map