@indigoai-us/hq-cloud 6.15.4 → 6.15.5
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/dist/cli/push-scan-exclusions.test.d.ts +2 -0
- package/dist/cli/push-scan-exclusions.test.d.ts.map +1 -0
- package/dist/cli/push-scan-exclusions.test.js +87 -0
- package/dist/cli/push-scan-exclusions.test.js.map +1 -0
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +21 -1
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +82 -0
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +23 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +9 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/outposts/billing.d.ts +41 -0
- package/dist/outposts/billing.d.ts.map +1 -0
- package/dist/outposts/billing.js +53 -0
- package/dist/outposts/billing.js.map +1 -0
- package/dist/outposts/cli-billing.d.ts +59 -0
- package/dist/outposts/cli-billing.d.ts.map +1 -0
- package/dist/outposts/cli-billing.js +93 -0
- package/dist/outposts/cli-billing.js.map +1 -0
- package/dist/outposts/cli.d.ts +13 -0
- package/dist/outposts/cli.d.ts.map +1 -0
- package/dist/outposts/cli.js +13 -0
- package/dist/outposts/cli.js.map +1 -0
- package/dist/outposts/client.d.ts +140 -0
- package/dist/outposts/client.d.ts.map +1 -0
- package/dist/outposts/client.js +271 -0
- package/dist/outposts/client.js.map +1 -0
- package/dist/outposts/client.test.d.ts +9 -0
- package/dist/outposts/client.test.d.ts.map +1 -0
- package/dist/outposts/client.test.js +226 -0
- package/dist/outposts/client.test.js.map +1 -0
- package/dist/outposts/command.d.ts +74 -0
- package/dist/outposts/command.d.ts.map +1 -0
- package/dist/outposts/command.js +661 -0
- package/dist/outposts/command.js.map +1 -0
- package/dist/outposts/command.test.d.ts +18 -0
- package/dist/outposts/command.test.d.ts.map +1 -0
- package/dist/outposts/command.test.js +933 -0
- package/dist/outposts/command.test.js.map +1 -0
- package/dist/outposts/errors.d.ts +32 -0
- package/dist/outposts/errors.d.ts.map +1 -0
- package/dist/outposts/errors.js +72 -0
- package/dist/outposts/errors.js.map +1 -0
- package/dist/outposts/heartbeat-command.d.ts +106 -0
- package/dist/outposts/heartbeat-command.d.ts.map +1 -0
- package/dist/outposts/heartbeat-command.js +186 -0
- package/dist/outposts/heartbeat-command.js.map +1 -0
- package/dist/outposts/host-deps.d.ts +49 -0
- package/dist/outposts/host-deps.d.ts.map +1 -0
- package/dist/outposts/host-deps.js +93 -0
- package/dist/outposts/host-deps.js.map +1 -0
- package/dist/outposts/index.d.ts +36 -0
- package/dist/outposts/index.d.ts.map +1 -0
- package/dist/outposts/index.js +32 -0
- package/dist/outposts/index.js.map +1 -0
- package/dist/outposts/node.d.ts +23 -0
- package/dist/outposts/node.d.ts.map +1 -0
- package/dist/outposts/node.js +22 -0
- package/dist/outposts/node.js.map +1 -0
- package/dist/outposts/remote-command.d.ts +30 -0
- package/dist/outposts/remote-command.d.ts.map +1 -0
- package/dist/outposts/remote-command.js +39 -0
- package/dist/outposts/remote-command.js.map +1 -0
- package/dist/outposts/replica-sync.d.ts +40 -0
- package/dist/outposts/replica-sync.d.ts.map +1 -0
- package/dist/outposts/replica-sync.js +188 -0
- package/dist/outposts/replica-sync.js.map +1 -0
- package/dist/outposts/replica-sync.test.d.ts +2 -0
- package/dist/outposts/replica-sync.test.d.ts.map +1 -0
- package/dist/outposts/replica-sync.test.js +224 -0
- package/dist/outposts/replica-sync.test.js.map +1 -0
- package/dist/outposts/self-deploy.d.ts +47 -0
- package/dist/outposts/self-deploy.d.ts.map +1 -0
- package/dist/outposts/self-deploy.js +188 -0
- package/dist/outposts/self-deploy.js.map +1 -0
- package/dist/outposts/self-deploy.test.d.ts +2 -0
- package/dist/outposts/self-deploy.test.d.ts.map +1 -0
- package/dist/outposts/self-deploy.test.js +186 -0
- package/dist/outposts/self-deploy.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-bounds.test.d.ts +25 -0
- package/dist/outposts/session-heartbeat-bounds.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-bounds.test.js +156 -0
- package/dist/outposts/session-heartbeat-bounds.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-guard.test.d.ts +16 -0
- package/dist/outposts/session-heartbeat-guard.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-guard.test.js +88 -0
- package/dist/outposts/session-heartbeat-guard.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.d.ts +76 -0
- package/dist/outposts/session-heartbeat-publisher.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.js +117 -0
- package/dist/outposts/session-heartbeat-publisher.js.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.test.d.ts +10 -0
- package/dist/outposts/session-heartbeat-publisher.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.test.js +153 -0
- package/dist/outposts/session-heartbeat-publisher.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts +18 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.js +106 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.js.map +1 -0
- package/dist/outposts/session-heartbeat.d.ts +210 -0
- package/dist/outposts/session-heartbeat.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat.js +657 -0
- package/dist/outposts/session-heartbeat.js.map +1 -0
- package/dist/outposts/session-heartbeat.test.d.ts +11 -0
- package/dist/outposts/session-heartbeat.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat.test.js +370 -0
- package/dist/outposts/session-heartbeat.test.js.map +1 -0
- package/dist/outposts/ssh.d.ts +29 -0
- package/dist/outposts/ssh.d.ts.map +1 -0
- package/dist/outposts/ssh.js +66 -0
- package/dist/outposts/ssh.js.map +1 -0
- package/dist/outposts/transport.d.ts +76 -0
- package/dist/outposts/transport.d.ts.map +1 -0
- package/dist/outposts/transport.js +91 -0
- package/dist/outposts/transport.js.map +1 -0
- package/dist/outposts/transport.test.d.ts +10 -0
- package/dist/outposts/transport.test.d.ts.map +1 -0
- package/dist/outposts/transport.test.js +116 -0
- package/dist/outposts/transport.test.js.map +1 -0
- package/dist/outposts/types.d.ts +125 -0
- package/dist/outposts/types.d.ts.map +1 -0
- package/dist/outposts/types.js +13 -0
- package/dist/outposts/types.js.map +1 -0
- package/dist/personal-vault.d.ts +69 -0
- package/dist/personal-vault.d.ts.map +1 -1
- package/dist/personal-vault.js +100 -0
- package/dist/personal-vault.js.map +1 -1
- package/dist/personal-vault.test.js +140 -1
- package/dist/personal-vault.test.js.map +1 -1
- package/package.json +39 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal-facing half of the billing gate: the prose that talks to a human.
|
|
3
|
+
*
|
|
4
|
+
* Split from `./billing.ts` so a Next.js consumer can use the prices, the
|
|
5
|
+
* formatter, and the link mint without inheriting `chalk` or `process.exit`.
|
|
6
|
+
*
|
|
7
|
+
* Everything here is full prose rather than terse status output — a recurring
|
|
8
|
+
* charge and a declined card are exactly the moments where more words are the
|
|
9
|
+
* right call.
|
|
10
|
+
*/
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { formatUsd, mintPaymentLink } from "./billing.js";
|
|
13
|
+
/**
|
|
14
|
+
* Gate a paid provisioning call behind an explicit, informed `--yes`.
|
|
15
|
+
*
|
|
16
|
+
* Prints the monthly charge in full and exits non-zero when the caller has not
|
|
17
|
+
* passed `--yes`. Returns normally once approved so the caller proceeds.
|
|
18
|
+
*/
|
|
19
|
+
export function confirmChargeOrExit(opts) {
|
|
20
|
+
const quantity = opts.quantity ?? 1;
|
|
21
|
+
const total = opts.unitCents * quantity;
|
|
22
|
+
const each = quantity > 1 ? ` (${formatUsd(opts.unitCents)} each × ${quantity})` : "";
|
|
23
|
+
const noun = quantity > 1 ? `${opts.resource}s` : opts.resource;
|
|
24
|
+
const article = /^[aeiou]/i.test(opts.resource) ? "an" : "a";
|
|
25
|
+
const subject = quantity > 1 ? `${quantity} ${noun}` : `${article} ${noun}`;
|
|
26
|
+
if (!opts.yes) {
|
|
27
|
+
console.error(chalk.yellow(`Provisioning ${subject} adds a recurring charge of ` +
|
|
28
|
+
`${formatUsd(total)}/month${each} to the payer on file. This is a ` +
|
|
29
|
+
`paid resource.\n` +
|
|
30
|
+
`Re-run with --yes to confirm you want to incur this charge.`));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk.dim(`Confirmed: ${formatUsd(total)}/month for ${quantity} ${noun}. Provisioning…`));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The provision was blocked because the payer has no card on file.
|
|
37
|
+
*
|
|
38
|
+
* Mints the card-capture link the backend pointed us at and prints a plain,
|
|
39
|
+
* shareable message so the user can add a card — or hand the link to whoever owns
|
|
40
|
+
* billing — and re-run. Returns the URL, or `null` when the server gave no `setup`
|
|
41
|
+
* action to act on. Never prints tokens or secrets.
|
|
42
|
+
*/
|
|
43
|
+
export async function surfaceBillingRequired(ctx, billing) {
|
|
44
|
+
if (!billing.setup) {
|
|
45
|
+
console.error(chalk.yellow("Provisioning is blocked on billing, but the server didn't return a " +
|
|
46
|
+
"payment link. Add a card in the console billing page, then re-run."));
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const url = await mintPaymentLink({ ...ctx, setup: billing.setup });
|
|
50
|
+
console.log(chalk.yellow("No card on file — provisioning is blocked until one is added."));
|
|
51
|
+
console.log("Add a card here (safe to share with whoever owns billing):\n " +
|
|
52
|
+
chalk.cyan(url));
|
|
53
|
+
console.log(chalk.dim("Once a card is added, re-run the same command."));
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Status-aware surface for a 402 billing block.
|
|
58
|
+
*
|
|
59
|
+
* hq-pro's envelope carries two distinct remediations that must never be
|
|
60
|
+
* conflated (mirroring the server's own classification):
|
|
61
|
+
*
|
|
62
|
+
* - `payment_failed` — a card EXISTS and the charge was DECLINED. The server's
|
|
63
|
+
* `message` already carries the friendly decline copy ("Your card was
|
|
64
|
+
* declined…", "insufficient funds", …). Telling this user "No card on file"
|
|
65
|
+
* sends them down the wrong remediation path entirely (observed live
|
|
66
|
+
* 2026-07-20: a declined $80 Outpost proration surfaced as "no card",
|
|
67
|
+
* triggering a hunt for a missing card that existed). The capture link still
|
|
68
|
+
* surfaces — as the way to UPDATE the card.
|
|
69
|
+
* - anything else (`billing_required`) — genuinely no usable card on file; the
|
|
70
|
+
* add-a-card copy is correct.
|
|
71
|
+
*/
|
|
72
|
+
export async function surfaceBillingBlocked(ctx, billing, serverMessage) {
|
|
73
|
+
if (billing.status !== "payment_failed") {
|
|
74
|
+
return surfaceBillingRequired(ctx, billing);
|
|
75
|
+
}
|
|
76
|
+
console.error(chalk.yellow(serverMessage?.trim() ||
|
|
77
|
+
"Your payment failed. Try a different card or contact your bank."));
|
|
78
|
+
if (!billing.setup)
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
const url = await mintPaymentLink({ ...ctx, setup: billing.setup });
|
|
82
|
+
console.log("Update or replace the card here (safe to share with whoever owns billing):\n " +
|
|
83
|
+
chalk.cyan(url));
|
|
84
|
+
console.log(chalk.dim("Once the payment method is sorted, re-run the same command."));
|
|
85
|
+
return url;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Link minting is best-effort on the decline path — the decline reason above
|
|
89
|
+
// is the essential part, and the console billing page also works.
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=cli-billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-billing.js","sourceRoot":"","sources":["../../src/outposts/cli-billing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts/cli` — the `hq outposts` command tree.
|
|
3
|
+
*
|
|
4
|
+
* Depends on `commander` and `chalk` and calls `process.exit`, so it is only for a
|
|
5
|
+
* host CLI. hq-cli mounts it by passing its own auth and HTTP seams; nothing else
|
|
6
|
+
* should import this.
|
|
7
|
+
*/
|
|
8
|
+
export { registerOutpostsCommand } from "./command.js";
|
|
9
|
+
export type { OutpostsCommandDeps } from "./command.js";
|
|
10
|
+
export { registerHeartbeatCommand } from "./heartbeat-command.js";
|
|
11
|
+
export { confirmChargeOrExit, surfaceBillingRequired, surfaceBillingBlocked, } from "./cli-billing.js";
|
|
12
|
+
export type { BillingSurfaceContext } from "./cli-billing.js";
|
|
13
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/outposts/cli.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAIlE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts/cli` — the `hq outposts` command tree.
|
|
3
|
+
*
|
|
4
|
+
* Depends on `commander` and `chalk` and calls `process.exit`, so it is only for a
|
|
5
|
+
* host CLI. hq-cli mounts it by passing its own auth and HTTP seams; nothing else
|
|
6
|
+
* should import this.
|
|
7
|
+
*/
|
|
8
|
+
export { registerOutpostsCommand } from "./command.js";
|
|
9
|
+
export { registerHeartbeatCommand } from "./heartbeat-command.js";
|
|
10
|
+
// Shared billing prose. `hq agents` provisioning renders the same confirmation and
|
|
11
|
+
// decline copy, so it is exported rather than kept private to the outposts tree.
|
|
12
|
+
export { confirmChargeOrExit, surfaceBillingRequired, surfaceBillingBlocked, } from "./cli-billing.js";
|
|
13
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/outposts/cli.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAGvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAElE,mFAAmF;AACnF,iFAAiF;AACjF,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OutpostsClient` — typed client for the hq-pro `/outpost/*` control plane.
|
|
3
|
+
*
|
|
4
|
+
* These are the same routes hq-console's outpost panel calls and the same ones
|
|
5
|
+
* `hq outposts` drives, so this class is the single implementation of the wire
|
|
6
|
+
* contract for every first-party surface.
|
|
7
|
+
*
|
|
8
|
+
* Portable: the only things it touches are its injected transport, its injected
|
|
9
|
+
* token source, and `setTimeout`. No Node built-ins, no filesystem, no
|
|
10
|
+
* `process.exit`, no module-level side effects — safe to import from a Next.js
|
|
11
|
+
* server action, a route handler, or an edge function.
|
|
12
|
+
*
|
|
13
|
+
* Outposts are PERSONAL / caller-scoped: hq-pro keys every route on the caller's
|
|
14
|
+
* Cognito sub, so no method takes a company. `outpostId` selects a specific box;
|
|
15
|
+
* omitting it targets the caller's primary slot.
|
|
16
|
+
*
|
|
17
|
+
* Every non-2xx throws {@link OutpostHttpError} — nothing is swallowed, and the
|
|
18
|
+
* server's `step` is preserved so callers can distinguish a retryable
|
|
19
|
+
* `teardown-incomplete` from a real failure.
|
|
20
|
+
*/
|
|
21
|
+
import type { OutpostRequest, OutpostTransport } from "./transport.js";
|
|
22
|
+
import type { OutpostExecAsyncResult, OutpostExecResult, OutpostExecStage, OutpostExecSubmission, OutpostSshAccess, OutpostSummary, ProvisionOutpostInput } from "./types.js";
|
|
23
|
+
/**
|
|
24
|
+
* A bearer token, or a function that resolves one per request.
|
|
25
|
+
*
|
|
26
|
+
* The function form is the important one: hq-cli passes `ensureCognitoToken`, so
|
|
27
|
+
* a long-lived client re-resolves a refreshed token instead of pinning the one
|
|
28
|
+
* that happened to be valid at construction. A Next.js app passes a closure over
|
|
29
|
+
* its session.
|
|
30
|
+
*/
|
|
31
|
+
export type OutpostTokenSource = string | (() => string | Promise<string>);
|
|
32
|
+
export interface OutpostsClientOptions {
|
|
33
|
+
/** How requests reach the control plane. See `createOutpostTransport`. */
|
|
34
|
+
transport: OutpostTransport;
|
|
35
|
+
/** Bearer token, or a resolver called once per request. */
|
|
36
|
+
token: OutpostTokenSource;
|
|
37
|
+
/** Origin override passed through to the transport on every request. */
|
|
38
|
+
baseUrl?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Tuning for {@link OutpostsClient.waitForExecResult}. */
|
|
41
|
+
export interface WaitForExecOptions {
|
|
42
|
+
/** First poll delay in ms. Defaults to 500. */
|
|
43
|
+
initialPollMs?: number;
|
|
44
|
+
/** Ceiling for the exponential backoff in ms. Defaults to 5000. */
|
|
45
|
+
maxPollMs?: number;
|
|
46
|
+
/** Abort the wait. The in-flight poll rejects with the abort reason. */
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}
|
|
49
|
+
/** The widest AWS-RunShellScript `executionTimeout`, in seconds (48h). */
|
|
50
|
+
export declare const EXEC_MAX_TIMEOUT_SECONDS = 172800;
|
|
51
|
+
export declare class OutpostsClient {
|
|
52
|
+
private readonly transport;
|
|
53
|
+
private readonly tokenSource;
|
|
54
|
+
private readonly baseUrl?;
|
|
55
|
+
constructor(options: OutpostsClientOptions);
|
|
56
|
+
/** Resolve the bearer token for one request. */
|
|
57
|
+
private token;
|
|
58
|
+
/**
|
|
59
|
+
* Authenticated JSON round-trip. Throws {@link OutpostHttpError} on any
|
|
60
|
+
* non-2xx, decoding hq-pro's `{ error | message, step }` envelope for the
|
|
61
|
+
* reason plus the two structural envelopes (`billing`, `capped`) that carry no
|
|
62
|
+
* message at all.
|
|
63
|
+
*/
|
|
64
|
+
request<T>(options: Omit<OutpostRequest, "token" | "baseUrl">): Promise<T>;
|
|
65
|
+
/**
|
|
66
|
+
* Provision the caller's Outpost.
|
|
67
|
+
*
|
|
68
|
+
* `refreshToken` lets the box authenticate AS the caller — the same body
|
|
69
|
+
* hq-console's `provisionMyOutpost` sends. It travels over HTTPS and is never
|
|
70
|
+
* logged.
|
|
71
|
+
*
|
|
72
|
+
* No duplicate is ever created: a caller already at their per-person cap gets a
|
|
73
|
+
* `409` whose body lists their existing boxes, surfaced here as an
|
|
74
|
+
* `OutpostHttpError` carrying `capped`. hq-pro checks the cap BEFORE activation
|
|
75
|
+
* billing, so a capped call is never charged.
|
|
76
|
+
*
|
|
77
|
+
* A payer with no usable card gets a `402` carrying `billing`.
|
|
78
|
+
*/
|
|
79
|
+
provision(input: ProvisionOutpostInput): Promise<Record<string, unknown>>;
|
|
80
|
+
/** Every Outpost the caller owns. */
|
|
81
|
+
list(): Promise<OutpostSummary[]>;
|
|
82
|
+
/** Live detail for one box. */
|
|
83
|
+
status(outpostId?: string): Promise<Record<string, unknown>>;
|
|
84
|
+
/** Enable (or retry) Codex on a box. */
|
|
85
|
+
enableCodex(outpostId?: string): Promise<Record<string, unknown>>;
|
|
86
|
+
/** Ask the box to mint a fresh Claude login URL. */
|
|
87
|
+
regenerateLoginUrl(outpostId?: string): Promise<Record<string, unknown>>;
|
|
88
|
+
/**
|
|
89
|
+
* Hand the box the one-time Claude sign-in code from the login URL. hq-pro
|
|
90
|
+
* stores it as the row's pending code; the box polls for it, feeds it to
|
|
91
|
+
* `claude`, and flips itself `awaiting-claude-login → ready`.
|
|
92
|
+
*/
|
|
93
|
+
submitLoginCode(code: string, outpostId?: string): Promise<Record<string, unknown>>;
|
|
94
|
+
/**
|
|
95
|
+
* Permanently destroy a box and its cloud resources.
|
|
96
|
+
*
|
|
97
|
+
* Idempotent: a `409` / `step: "teardown-incomplete"` means the gateway's
|
|
98
|
+
* timeout fired while the Lambda kept working. The row is preserved — re-issue
|
|
99
|
+
* the same call to finish.
|
|
100
|
+
*/
|
|
101
|
+
destroy(outpostId?: string): Promise<Record<string, unknown>>;
|
|
102
|
+
/**
|
|
103
|
+
* Run a one-shot shell command via server-brokered SSM (no SSH). Synchronous,
|
|
104
|
+
* so it is bounded by the API gateway's ~20s cap — use the submit/result pair
|
|
105
|
+
* for anything longer.
|
|
106
|
+
*
|
|
107
|
+
* A `step: "platform-unsupported"` error means the box is Lightsail and has no
|
|
108
|
+
* SSM agent; fall back to SSH via {@link sshAccess}.
|
|
109
|
+
*/
|
|
110
|
+
exec(command: string, outpostId?: string): Promise<OutpostExecResult>;
|
|
111
|
+
/** Presign an input upload for an asynchronous command. */
|
|
112
|
+
stageExecInput(outpostId?: string): Promise<OutpostExecStage>;
|
|
113
|
+
/** Submit an asynchronous command; returns as soon as SSM accepts it. */
|
|
114
|
+
submitExec(command: string, outpostId?: string, timeoutSeconds?: number): Promise<OutpostExecSubmission>;
|
|
115
|
+
/**
|
|
116
|
+
* Poll an asynchronous command once. Streams arrive only once `done`.
|
|
117
|
+
*
|
|
118
|
+
* `signal` aborts the in-flight request itself — not just the caller's wait —
|
|
119
|
+
* so a slow or hung poll is cancelled rather than left pending.
|
|
120
|
+
*/
|
|
121
|
+
fetchExecResult(commandId: string, outpostId?: string, signal?: AbortSignal): Promise<OutpostExecAsyncResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Poll until an asynchronous command reaches a terminal state, backing off
|
|
124
|
+
* exponentially.
|
|
125
|
+
*
|
|
126
|
+
* A `429` is treated as back-pressure and retried — the command is still
|
|
127
|
+
* running, and giving up on a rate limit would strand a job that is fine. Any
|
|
128
|
+
* other error propagates.
|
|
129
|
+
*/
|
|
130
|
+
waitForExecResult(commandId: string, outpostId?: string, options?: WaitForExecOptions): Promise<OutpostExecAsyncResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Fetch SSH connection info + key for a box and open the caller's IP on its SSH
|
|
133
|
+
* port. The route exists to reach a Lightsail box, which has no SSM agent.
|
|
134
|
+
*
|
|
135
|
+
* The result carries a PEM private key. Treat it as a credential: never log it,
|
|
136
|
+
* never return it to a browser, and write it only to a mode-0600 file.
|
|
137
|
+
*/
|
|
138
|
+
sshAccess(outpostId?: string): Promise<OutpostSshAccess>;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/outposts/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OutpostsClient` — typed client for the hq-pro `/outpost/*` control plane.
|
|
3
|
+
*
|
|
4
|
+
* These are the same routes hq-console's outpost panel calls and the same ones
|
|
5
|
+
* `hq outposts` drives, so this class is the single implementation of the wire
|
|
6
|
+
* contract for every first-party surface.
|
|
7
|
+
*
|
|
8
|
+
* Portable: the only things it touches are its injected transport, its injected
|
|
9
|
+
* token source, and `setTimeout`. No Node built-ins, no filesystem, no
|
|
10
|
+
* `process.exit`, no module-level side effects — safe to import from a Next.js
|
|
11
|
+
* server action, a route handler, or an edge function.
|
|
12
|
+
*
|
|
13
|
+
* Outposts are PERSONAL / caller-scoped: hq-pro keys every route on the caller's
|
|
14
|
+
* Cognito sub, so no method takes a company. `outpostId` selects a specific box;
|
|
15
|
+
* omitting it targets the caller's primary slot.
|
|
16
|
+
*
|
|
17
|
+
* Every non-2xx throws {@link OutpostHttpError} — nothing is swallowed, and the
|
|
18
|
+
* server's `step` is preserved so callers can distinguish a retryable
|
|
19
|
+
* `teardown-incomplete` from a real failure.
|
|
20
|
+
*/
|
|
21
|
+
import { OutpostHttpError, parseBillingPayload, parseCappedPayload } from "./errors.js";
|
|
22
|
+
const EXEC_RESULT_INITIAL_POLL_MS = 500;
|
|
23
|
+
const EXEC_RESULT_MAX_POLL_MS = 5_000;
|
|
24
|
+
/** The widest AWS-RunShellScript `executionTimeout`, in seconds (48h). */
|
|
25
|
+
export const EXEC_MAX_TIMEOUT_SECONDS = 172_800;
|
|
26
|
+
function sleep(ms, signal) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
if (signal?.aborted) {
|
|
29
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
signal?.removeEventListener("abort", onAbort);
|
|
34
|
+
resolve();
|
|
35
|
+
}, ms);
|
|
36
|
+
// Do NOT unref this timer. The backoff IS the work while `waitForExecResult`
|
|
37
|
+
// polls: an unresolved promise alone does not keep Node's event loop alive,
|
|
38
|
+
// so unref-ing here lets `hq outposts exec --async` / `exec-result --wait`
|
|
39
|
+
// exit before the next poll ever fires — printing nothing for a command that
|
|
40
|
+
// is still running.
|
|
41
|
+
function onAbort() {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
reject(signal?.reason ?? new Error("Aborted"));
|
|
44
|
+
}
|
|
45
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export class OutpostsClient {
|
|
49
|
+
transport;
|
|
50
|
+
tokenSource;
|
|
51
|
+
baseUrl;
|
|
52
|
+
constructor(options) {
|
|
53
|
+
this.transport = options.transport;
|
|
54
|
+
this.tokenSource = options.token;
|
|
55
|
+
this.baseUrl = options.baseUrl;
|
|
56
|
+
}
|
|
57
|
+
/** Resolve the bearer token for one request. */
|
|
58
|
+
async token() {
|
|
59
|
+
return typeof this.tokenSource === "function"
|
|
60
|
+
? this.tokenSource()
|
|
61
|
+
: this.tokenSource;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Authenticated JSON round-trip. Throws {@link OutpostHttpError} on any
|
|
65
|
+
* non-2xx, decoding hq-pro's `{ error | message, step }` envelope for the
|
|
66
|
+
* reason plus the two structural envelopes (`billing`, `capped`) that carry no
|
|
67
|
+
* message at all.
|
|
68
|
+
*/
|
|
69
|
+
async request(options) {
|
|
70
|
+
const res = await this.transport({
|
|
71
|
+
...options,
|
|
72
|
+
token: await this.token(),
|
|
73
|
+
...(this.baseUrl ? { baseUrl: this.baseUrl } : {}),
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
const body = (await res.json().catch(() => ({})));
|
|
77
|
+
const message = typeof body.message === "string"
|
|
78
|
+
? body.message
|
|
79
|
+
: typeof body.error === "string"
|
|
80
|
+
? body.error
|
|
81
|
+
: res.statusText;
|
|
82
|
+
throw new OutpostHttpError(res.status, message, body.step, parseBillingPayload(body), parseCappedPayload(body));
|
|
83
|
+
}
|
|
84
|
+
return (await res.json());
|
|
85
|
+
}
|
|
86
|
+
// -- Lifecycle ------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Provision the caller's Outpost.
|
|
89
|
+
*
|
|
90
|
+
* `refreshToken` lets the box authenticate AS the caller — the same body
|
|
91
|
+
* hq-console's `provisionMyOutpost` sends. It travels over HTTPS and is never
|
|
92
|
+
* logged.
|
|
93
|
+
*
|
|
94
|
+
* No duplicate is ever created: a caller already at their per-person cap gets a
|
|
95
|
+
* `409` whose body lists their existing boxes, surfaced here as an
|
|
96
|
+
* `OutpostHttpError` carrying `capped`. hq-pro checks the cap BEFORE activation
|
|
97
|
+
* billing, so a capped call is never charged.
|
|
98
|
+
*
|
|
99
|
+
* A payer with no usable card gets a `402` carrying `billing`.
|
|
100
|
+
*/
|
|
101
|
+
async provision(input) {
|
|
102
|
+
return this.request({
|
|
103
|
+
path: "/outpost/provision",
|
|
104
|
+
method: "POST",
|
|
105
|
+
body: {
|
|
106
|
+
refreshToken: input.refreshToken,
|
|
107
|
+
...(input.clientIp ? { clientIp: input.clientIp } : {}),
|
|
108
|
+
...(input.diskSizeGb ? { diskSizeGb: input.diskSizeGb } : {}),
|
|
109
|
+
...(input.agentRuntime ? { agentRuntime: input.agentRuntime } : {}),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** Every Outpost the caller owns. */
|
|
114
|
+
async list() {
|
|
115
|
+
const data = await this.request({
|
|
116
|
+
path: "/outpost/list",
|
|
117
|
+
});
|
|
118
|
+
return data.outposts ?? [];
|
|
119
|
+
}
|
|
120
|
+
/** Live detail for one box. */
|
|
121
|
+
async status(outpostId) {
|
|
122
|
+
return this.request({
|
|
123
|
+
path: "/outpost/status",
|
|
124
|
+
query: outpostId ? { outpostId } : undefined,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/** Enable (or retry) Codex on a box. */
|
|
128
|
+
async enableCodex(outpostId) {
|
|
129
|
+
return this.request({
|
|
130
|
+
path: "/outpost/codex/enable",
|
|
131
|
+
method: "POST",
|
|
132
|
+
query: outpostId ? { outpostId } : undefined,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/** Ask the box to mint a fresh Claude login URL. */
|
|
136
|
+
async regenerateLoginUrl(outpostId) {
|
|
137
|
+
return this.request({
|
|
138
|
+
path: "/outpost/regenerate-login-url",
|
|
139
|
+
method: "POST",
|
|
140
|
+
query: outpostId ? { outpostId } : undefined,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Hand the box the one-time Claude sign-in code from the login URL. hq-pro
|
|
145
|
+
* stores it as the row's pending code; the box polls for it, feeds it to
|
|
146
|
+
* `claude`, and flips itself `awaiting-claude-login → ready`.
|
|
147
|
+
*/
|
|
148
|
+
async submitLoginCode(code, outpostId) {
|
|
149
|
+
return this.request({
|
|
150
|
+
path: "/outpost/login-code",
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: { code },
|
|
153
|
+
query: outpostId ? { outpostId } : undefined,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Permanently destroy a box and its cloud resources.
|
|
158
|
+
*
|
|
159
|
+
* Idempotent: a `409` / `step: "teardown-incomplete"` means the gateway's
|
|
160
|
+
* timeout fired while the Lambda kept working. The row is preserved — re-issue
|
|
161
|
+
* the same call to finish.
|
|
162
|
+
*/
|
|
163
|
+
async destroy(outpostId) {
|
|
164
|
+
return this.request({
|
|
165
|
+
path: "/outpost/destroy",
|
|
166
|
+
method: "POST",
|
|
167
|
+
query: outpostId ? { outpostId } : undefined,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// -- Exec -----------------------------------------------------------------
|
|
171
|
+
/**
|
|
172
|
+
* Run a one-shot shell command via server-brokered SSM (no SSH). Synchronous,
|
|
173
|
+
* so it is bounded by the API gateway's ~20s cap — use the submit/result pair
|
|
174
|
+
* for anything longer.
|
|
175
|
+
*
|
|
176
|
+
* A `step: "platform-unsupported"` error means the box is Lightsail and has no
|
|
177
|
+
* SSM agent; fall back to SSH via {@link sshAccess}.
|
|
178
|
+
*/
|
|
179
|
+
async exec(command, outpostId) {
|
|
180
|
+
return this.request({
|
|
181
|
+
path: "/outpost/exec",
|
|
182
|
+
method: "POST",
|
|
183
|
+
body: { command },
|
|
184
|
+
query: outpostId ? { outpostId } : undefined,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/** Presign an input upload for an asynchronous command. */
|
|
188
|
+
async stageExecInput(outpostId) {
|
|
189
|
+
return this.request({
|
|
190
|
+
path: "/outpost/exec",
|
|
191
|
+
method: "POST",
|
|
192
|
+
body: { mode: "stage" },
|
|
193
|
+
query: outpostId ? { outpostId } : undefined,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Submit an asynchronous command; returns as soon as SSM accepts it. */
|
|
197
|
+
async submitExec(command, outpostId, timeoutSeconds) {
|
|
198
|
+
return this.request({
|
|
199
|
+
path: "/outpost/exec",
|
|
200
|
+
method: "POST",
|
|
201
|
+
body: {
|
|
202
|
+
mode: "submit",
|
|
203
|
+
command,
|
|
204
|
+
...(timeoutSeconds !== undefined ? { timeoutSeconds } : {}),
|
|
205
|
+
},
|
|
206
|
+
query: outpostId ? { outpostId } : undefined,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Poll an asynchronous command once. Streams arrive only once `done`.
|
|
211
|
+
*
|
|
212
|
+
* `signal` aborts the in-flight request itself — not just the caller's wait —
|
|
213
|
+
* so a slow or hung poll is cancelled rather than left pending.
|
|
214
|
+
*/
|
|
215
|
+
async fetchExecResult(commandId, outpostId, signal) {
|
|
216
|
+
return this.request({
|
|
217
|
+
path: "/outpost/exec",
|
|
218
|
+
method: "POST",
|
|
219
|
+
body: { mode: "result", commandId },
|
|
220
|
+
query: outpostId ? { outpostId } : undefined,
|
|
221
|
+
...(signal ? { signal } : {}),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Poll until an asynchronous command reaches a terminal state, backing off
|
|
226
|
+
* exponentially.
|
|
227
|
+
*
|
|
228
|
+
* A `429` is treated as back-pressure and retried — the command is still
|
|
229
|
+
* running, and giving up on a rate limit would strand a job that is fine. Any
|
|
230
|
+
* other error propagates.
|
|
231
|
+
*/
|
|
232
|
+
async waitForExecResult(commandId, outpostId, options = {}) {
|
|
233
|
+
const maxPollMs = options.maxPollMs ?? EXEC_RESULT_MAX_POLL_MS;
|
|
234
|
+
let delayMs = options.initialPollMs ?? EXEC_RESULT_INITIAL_POLL_MS;
|
|
235
|
+
for (;;) {
|
|
236
|
+
// Abort BEFORE issuing another poll — a signal that fired during the last
|
|
237
|
+
// backoff must not start a fresh request.
|
|
238
|
+
if (options.signal?.aborted) {
|
|
239
|
+
throw options.signal.reason ?? new Error("Aborted");
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const result = await this.fetchExecResult(commandId, outpostId, options.signal);
|
|
243
|
+
if (result.done)
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (!(err instanceof OutpostHttpError) || err.status !== 429)
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
await sleep(delayMs, options.signal);
|
|
251
|
+
delayMs = Math.min(delayMs * 2, maxPollMs);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// -- SSH ------------------------------------------------------------------
|
|
255
|
+
/**
|
|
256
|
+
* Fetch SSH connection info + key for a box and open the caller's IP on its SSH
|
|
257
|
+
* port. The route exists to reach a Lightsail box, which has no SSM agent.
|
|
258
|
+
*
|
|
259
|
+
* The result carries a PEM private key. Treat it as a credential: never log it,
|
|
260
|
+
* never return it to a browser, and write it only to a mode-0600 file.
|
|
261
|
+
*/
|
|
262
|
+
async sshAccess(outpostId) {
|
|
263
|
+
return this.request({
|
|
264
|
+
path: "/outpost/ssh-access",
|
|
265
|
+
method: "POST",
|
|
266
|
+
body: {},
|
|
267
|
+
query: outpostId ? { outpostId } : undefined,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/outposts/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for OutpostsClient against a stub transport.
|
|
3
|
+
*
|
|
4
|
+
* The command-tree tests cover the wire shapes end-to-end; these focus on the
|
|
5
|
+
* client's own logic: error decoding for all three envelopes, the token-source
|
|
6
|
+
* resolution contract, and the exec-result polling/backoff.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/outposts/client.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|