@indigoai-us/hq-cli 5.68.2 → 5.69.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 +13 -0
- package/dist/commands/outposts.d.ts +16 -1
- package/dist/commands/outposts.js +230 -3
- package/dist/main.js +6 -2
- package/dist/utils/cli-telemetry.d.ts +6 -0
- package/dist/utils/cli-telemetry.js +60 -0
- package/dist/utils/cognito-session.d.ts +2 -1
- package/dist/utils/cognito-session.js +5 -2
- package/dist/utils/vault-api.d.ts +1 -0
- package/dist/utils/vault-api.js +3 -2
- package/package.json +1 -1
- package/src/commands/outposts-self-deploy.test.ts +243 -0
- package/src/commands/outposts.ts +346 -1
- package/src/main.ts +5 -0
- package/src/utils/cli-telemetry.test.ts +153 -0
- package/src/utils/cli-telemetry.ts +61 -0
- package/src/utils/cognito-session.ts +4 -0
- package/src/utils/vault-api.test.ts +18 -0
- package/src/utils/vault-api.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.69.0]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`hq outposts self-deploy` (hidden).** Run on an existing Amazon Linux 2023
|
|
10
|
+
x86_64 EC2 by a logged-in user to make that box behave like an HQ outpost
|
|
11
|
+
locally: it materializes the HQ kernel (`hq rescue`) and installs the
|
|
12
|
+
`outpost-sync.service` (`hq-sync-runner --companies --watch`) so the box
|
|
13
|
+
continuously syncs all your company vaults. It is entirely client-side — it
|
|
14
|
+
makes no calls to hq-pro, registers no outpost row, and is not reported to or
|
|
15
|
+
managed by the control plane (no console entry, no metering). Gated by a
|
|
16
|
+
preflight (OS/arch/systemd/root/login) and a confirmation prompt (`--yes`).
|
|
17
|
+
|
|
5
18
|
## [5.68.1]
|
|
6
19
|
|
|
7
20
|
### Fixed
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* and status routes that exist. Renaming an Outpost is not a backend capability.
|
|
22
22
|
*/
|
|
23
23
|
import { Command } from "commander";
|
|
24
|
+
import { spawnSync } from "node:child_process";
|
|
24
25
|
import { type BillingErrorPayload } from "../utils/billing-gate.js";
|
|
25
26
|
/** A non-2xx from the `/outpost/*` control plane. Carries status + `step`. */
|
|
26
27
|
export declare class OutpostHttpError extends Error {
|
|
@@ -137,5 +138,19 @@ export declare function execViaSsh(access: OutpostSshAccess, command: string): {
|
|
|
137
138
|
exitCode: number | null;
|
|
138
139
|
error?: string;
|
|
139
140
|
};
|
|
140
|
-
|
|
141
|
+
/** The small local-environment surface used by `outposts self-deploy`. */
|
|
142
|
+
export interface SelfDeployDependencies {
|
|
143
|
+
spawnSync: (command: string, args: string[], options?: Parameters<typeof spawnSync>[2]) => ReturnType<typeof spawnSync>;
|
|
144
|
+
readTextFile: (file: string) => string;
|
|
145
|
+
loadCachedTokens: () => {
|
|
146
|
+
refreshToken?: string;
|
|
147
|
+
idToken?: string;
|
|
148
|
+
} | undefined;
|
|
149
|
+
getUid: () => number | undefined;
|
|
150
|
+
isStdinTty: () => boolean;
|
|
151
|
+
confirm: () => Promise<boolean>;
|
|
152
|
+
defaultHqRoot: () => string;
|
|
153
|
+
invokingUser: () => string;
|
|
154
|
+
}
|
|
155
|
+
export declare function registerOutpostsCommand(program: Command, selfDeployOverrides?: Partial<SelfDeployDependencies>): void;
|
|
141
156
|
//# sourceMappingURL=outposts.d.ts.map
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
* and status routes that exist. Renaming an Outpost is not a backend capability.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
24
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ff8d1d83-feab-5f6f-bf48-e4fde1a8d449")}catch(e){}}();
|
|
25
25
|
import chalk from "chalk";
|
|
26
26
|
import { spawnSync } from "node:child_process";
|
|
27
27
|
import * as fs from "node:fs";
|
|
28
28
|
import * as os from "node:os";
|
|
29
29
|
import * as path from "node:path";
|
|
30
|
+
import * as readline from "node:readline";
|
|
30
31
|
import { randomBytes } from "node:crypto";
|
|
31
32
|
import { loadCachedTokens } from "@indigoai-us/hq-cloud";
|
|
32
33
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
@@ -239,10 +240,236 @@ function printKeyValues(obj) {
|
|
|
239
240
|
console.log(`${chalk.bold(k)}: ${rendered}`);
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
|
-
|
|
243
|
+
const defaultSelfDeployDependencies = {
|
|
244
|
+
spawnSync: (command, args, options) => spawnSync(command, args, options),
|
|
245
|
+
readTextFile: (file) => fs.readFileSync(file, "utf8"),
|
|
246
|
+
loadCachedTokens: () => loadCachedTokens() ?? undefined,
|
|
247
|
+
getUid: () => process.getuid?.(),
|
|
248
|
+
isStdinTty: () => process.stdin.isTTY === true,
|
|
249
|
+
confirm: async () => {
|
|
250
|
+
const rl = readline.createInterface({
|
|
251
|
+
input: process.stdin,
|
|
252
|
+
output: process.stdout,
|
|
253
|
+
});
|
|
254
|
+
return new Promise((resolve) => {
|
|
255
|
+
rl.question("", (answer) => {
|
|
256
|
+
rl.close();
|
|
257
|
+
resolve(/^y(es)?$/i.test(answer.trim()));
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
defaultHqRoot: () => process.env.HQ_ROOT ?? path.join(os.homedir(), "hq"),
|
|
262
|
+
invokingUser: () => process.env.SUDO_USER ?? process.env.USER ?? os.userInfo().username,
|
|
263
|
+
};
|
|
264
|
+
function selfDeployError(message) {
|
|
265
|
+
return new Error(`Self-deploy preflight failed: ${message}`);
|
|
266
|
+
}
|
|
267
|
+
function commandSucceeded(deps, command, args) {
|
|
268
|
+
try {
|
|
269
|
+
const result = deps.spawnSync(command, args, { encoding: "utf8" });
|
|
270
|
+
return !!result && !result.error && result.status === 0;
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function commandOutput(deps, command, args) {
|
|
277
|
+
try {
|
|
278
|
+
const result = deps.spawnSync(command, args, { encoding: "utf8" });
|
|
279
|
+
if (!result || result.error || result.status !== 0)
|
|
280
|
+
return undefined;
|
|
281
|
+
return String(result.stdout ?? "").trim();
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function parseOsRelease(source) {
|
|
288
|
+
const values = new Map();
|
|
289
|
+
for (const line of source.split("\n")) {
|
|
290
|
+
const match = /^([A-Z_]+)=(.*)$/.exec(line);
|
|
291
|
+
if (!match)
|
|
292
|
+
continue;
|
|
293
|
+
const [, key, rawValue] = match;
|
|
294
|
+
values.set(key, rawValue.replace(/^['"]|['"]$/g, ""));
|
|
295
|
+
}
|
|
296
|
+
return values;
|
|
297
|
+
}
|
|
298
|
+
function hqIdentityFromSession(session) {
|
|
299
|
+
if (!session.idToken)
|
|
300
|
+
return "your cached HQ session";
|
|
301
|
+
try {
|
|
302
|
+
const payload = session.idToken.split(".")[1];
|
|
303
|
+
if (!payload)
|
|
304
|
+
return "your cached HQ session";
|
|
305
|
+
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
306
|
+
for (const key of [
|
|
307
|
+
"email",
|
|
308
|
+
"preferred_username",
|
|
309
|
+
"cognito:username",
|
|
310
|
+
"username",
|
|
311
|
+
"sub",
|
|
312
|
+
]) {
|
|
313
|
+
const value = claims[key];
|
|
314
|
+
if (typeof value === "string" && value)
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// A cache that has a refresh token remains valid for this local setup. The
|
|
320
|
+
// identity banner is informational, so never expose a token parse failure.
|
|
321
|
+
}
|
|
322
|
+
return "your cached HQ session";
|
|
323
|
+
}
|
|
324
|
+
function bashSingleQuote(value) {
|
|
325
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
326
|
+
}
|
|
327
|
+
function systemdQuoted(value) {
|
|
328
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
329
|
+
}
|
|
330
|
+
function renderSelfDeploySyncScript(hqRoot) {
|
|
331
|
+
return `#!/usr/bin/env bash
|
|
332
|
+
set -u
|
|
333
|
+
# Persistent all-membership sync for a locally self-hosted HQ outpost. The
|
|
334
|
+
# watch runner event-pushes local changes and polls remote changes every minute.
|
|
335
|
+
HQ_ROOT=${bashSingleQuote(hqRoot)}
|
|
336
|
+
cd "$HQ_ROOT"
|
|
337
|
+
while true; do
|
|
338
|
+
hq auth refresh || echo "[outpost-sync] auth refresh failed, continuing"
|
|
339
|
+
npx -y --package=@indigoai-us/hq-cloud@latest hq-sync-runner \\
|
|
340
|
+
--companies \\
|
|
341
|
+
--direction both \\
|
|
342
|
+
--on-conflict keep \\
|
|
343
|
+
--hq-root "$HQ_ROOT" \\
|
|
344
|
+
--watch \\
|
|
345
|
+
--event-push \\
|
|
346
|
+
--poll-remote-ms 60000
|
|
347
|
+
echo "[outpost-sync] watch runner exited; restarting in 10 seconds"
|
|
348
|
+
sleep 10
|
|
349
|
+
done
|
|
350
|
+
`;
|
|
351
|
+
}
|
|
352
|
+
function renderSelfDeployService(hqRoot, user) {
|
|
353
|
+
return `[Unit]
|
|
354
|
+
After=network-online.target
|
|
355
|
+
Wants=network-online.target
|
|
356
|
+
|
|
357
|
+
[Service]
|
|
358
|
+
User=${user}
|
|
359
|
+
WorkingDirectory=${systemdQuoted(hqRoot)}
|
|
360
|
+
ExecStart=/usr/local/bin/outpost-sync.sh
|
|
361
|
+
Restart=always
|
|
362
|
+
RestartSec=10s
|
|
363
|
+
|
|
364
|
+
[Install]
|
|
365
|
+
WantedBy=multi-user.target
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
function runChecked(deps, command, args, description, options) {
|
|
369
|
+
let result;
|
|
370
|
+
try {
|
|
371
|
+
result = deps.spawnSync(command, args, options);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
throw new Error(`${description} could not be started.`);
|
|
375
|
+
}
|
|
376
|
+
if (!result || result.error || result.status !== 0) {
|
|
377
|
+
// Do not include child process output here: even though this command never
|
|
378
|
+
// passes credentials to children, it keeps the error path secret-safe.
|
|
379
|
+
throw new Error(`${description} failed. Resolve the error and try again.`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function runPrivileged(deps, command, args, description, options) {
|
|
383
|
+
if (deps.getUid() === 0) {
|
|
384
|
+
runChecked(deps, command, args, description, options);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
runChecked(deps, "sudo", [command, ...args], description, options);
|
|
388
|
+
}
|
|
389
|
+
function selfDeployPreflight(deps) {
|
|
390
|
+
let osRelease;
|
|
391
|
+
try {
|
|
392
|
+
osRelease = deps.readTextFile("/etc/os-release");
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
throw selfDeployError("could not read /etc/os-release. This command only supports Amazon Linux 2023 x86_64.");
|
|
396
|
+
}
|
|
397
|
+
const release = parseOsRelease(osRelease);
|
|
398
|
+
if (release.get("ID") !== "amzn" ||
|
|
399
|
+
!release.get("VERSION_ID")?.startsWith("2023")) {
|
|
400
|
+
throw selfDeployError("this command only supports Amazon Linux 2023 x86_64. Use an Amazon Linux 2023 EC2 instance.");
|
|
401
|
+
}
|
|
402
|
+
const architecture = commandOutput(deps, "uname", ["-m"]);
|
|
403
|
+
if (architecture !== "x86_64") {
|
|
404
|
+
throw selfDeployError("this command only supports x86_64 EC2 instances. Use an Amazon Linux 2023 x86_64 instance.");
|
|
405
|
+
}
|
|
406
|
+
if (!commandSucceeded(deps, "systemctl", ["--version"])) {
|
|
407
|
+
throw selfDeployError("systemd is required but `systemctl --version` failed. Run this on an Amazon Linux 2023 EC2 host with systemd.");
|
|
408
|
+
}
|
|
409
|
+
if (deps.getUid() !== 0 && !commandSucceeded(deps, "sudo", ["-n", "true"])) {
|
|
410
|
+
throw selfDeployError("root or passwordless sudo is required. Configure passwordless sudo or run this command as root.");
|
|
411
|
+
}
|
|
412
|
+
const session = deps.loadCachedTokens();
|
|
413
|
+
if (!session?.refreshToken) {
|
|
414
|
+
throw selfDeployError("no HQ login session was found. Run `hq login`, then re-run this command.");
|
|
415
|
+
}
|
|
416
|
+
return session;
|
|
417
|
+
}
|
|
418
|
+
async function selfDeployOutpost(opts, deps) {
|
|
419
|
+
const session = selfDeployPreflight(deps);
|
|
420
|
+
const hqRoot = opts.hqRoot ?? deps.defaultHqRoot();
|
|
421
|
+
const user = deps.invokingUser();
|
|
422
|
+
if (!opts.yes) {
|
|
423
|
+
console.log(`HQ identity: ${hqIdentityFromSession(session)}`);
|
|
424
|
+
console.log(chalk.yellow("This machine will run as a SELF-HOSTED HQ outpost under YOUR identity, continuously syncing ALL your company vaults. " +
|
|
425
|
+
"It is NOT registered with or managed by hq-pro (no console entry, no remote management, no metering). " +
|
|
426
|
+
"Anyone with root on this box can act as you. Continue? [y/N]"));
|
|
427
|
+
if (!deps.isStdinTty()) {
|
|
428
|
+
throw new Error("Confirmation requires a TTY. Pass --yes to continue non-interactively.");
|
|
429
|
+
}
|
|
430
|
+
if (!(await deps.confirm())) {
|
|
431
|
+
throw new Error("Self-deploy cancelled.");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
runChecked(deps, "hq", ["rescue", "--hq-root", hqRoot, "--yes"], "HQ kernel rescue", { stdio: "inherit" });
|
|
435
|
+
runPrivileged(deps, "tee", ["/usr/local/bin/outpost-sync.sh"], "Writing /usr/local/bin/outpost-sync.sh", {
|
|
436
|
+
encoding: "utf8",
|
|
437
|
+
input: renderSelfDeploySyncScript(hqRoot),
|
|
438
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
439
|
+
});
|
|
440
|
+
runPrivileged(deps, "chmod", ["+x", "/usr/local/bin/outpost-sync.sh"], "Making /usr/local/bin/outpost-sync.sh executable");
|
|
441
|
+
runPrivileged(deps, "tee", ["/etc/systemd/system/outpost-sync.service"], "Writing /etc/systemd/system/outpost-sync.service", {
|
|
442
|
+
encoding: "utf8",
|
|
443
|
+
input: renderSelfDeployService(hqRoot, user),
|
|
444
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
445
|
+
});
|
|
446
|
+
runPrivileged(deps, "systemctl", ["daemon-reload"], "Reloading systemd");
|
|
447
|
+
runPrivileged(deps, "systemctl", ["enable", "--now", "outpost-sync.service"], "Enabling outpost-sync.service");
|
|
448
|
+
console.log(chalk.green("This box is now a self-hosted HQ outpost and will sync all your company vaults continuously."));
|
|
449
|
+
console.log(chalk.dim("Check it with: systemctl status outpost-sync.service"));
|
|
450
|
+
console.log(chalk.dim("It is unregistered and unmanaged by hq-pro (no console entry or remote management)."));
|
|
451
|
+
}
|
|
452
|
+
export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
|
|
453
|
+
const selfDeployDependencies = {
|
|
454
|
+
...defaultSelfDeployDependencies,
|
|
455
|
+
...selfDeployOverrides,
|
|
456
|
+
};
|
|
243
457
|
const outposts = program
|
|
244
458
|
.command("outposts")
|
|
245
459
|
.description("Manage your personal HQ Outposts (EC2 boxes)");
|
|
460
|
+
outposts
|
|
461
|
+
.command("self-deploy", { hidden: true })
|
|
462
|
+
.description("Configure this EC2 host as a locally self-hosted HQ outpost")
|
|
463
|
+
.option("--yes", "Skip the self-hosting confirmation")
|
|
464
|
+
.option("--hq-root <path>", "HQ root to sync", selfDeployDependencies.defaultHqRoot())
|
|
465
|
+
.action(async (opts) => {
|
|
466
|
+
try {
|
|
467
|
+
await selfDeployOutpost(opts, selfDeployDependencies);
|
|
468
|
+
}
|
|
469
|
+
catch (err) {
|
|
470
|
+
fail(err);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
246
473
|
outposts
|
|
247
474
|
.command("provision")
|
|
248
475
|
.alias("create")
|
|
@@ -514,4 +741,4 @@ export function registerOutpostsCommand(program) {
|
|
|
514
741
|
});
|
|
515
742
|
}
|
|
516
743
|
//# sourceMappingURL=outposts.js.map
|
|
517
|
-
//# debugId=
|
|
744
|
+
//# debugId=ff8d1d83-feab-5f6f-bf48-e4fde1a8d449
|
package/dist/main.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// MUST be first: guard the Node version before any dependency that needs a
|
|
6
6
|
// Node 20+ API (e.g. util.styleText) or a newer native ABI is evaluated.
|
|
7
7
|
|
|
8
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
8
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d6747715-2057-5a0c-bfc5-c0a8372639d0")}catch(e){}}();
|
|
9
9
|
import "./node-preflight.js";
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { initSentry, Sentry } from "./sentry.js";
|
|
@@ -65,6 +65,7 @@ import { isInterceptedProcessExit } from "./utils/intercepted-process-exit.js";
|
|
|
65
65
|
import { maybeWarnNewVersion, refreshVersionCache, } from "./utils/version-check.js";
|
|
66
66
|
import { enforceVersionGate, shouldSkipGate, } from "./utils/version-gate.js";
|
|
67
67
|
import { CLI_VERSION } from "./cli-version.js";
|
|
68
|
+
import { emitCliSessionStarted } from "./utils/cli-telemetry.js";
|
|
68
69
|
// Swallow EPIPE when a downstream reader (e.g. `source <(…)`, `| head`) closes
|
|
69
70
|
// the pipe early. This covers the ASYNC path — an 'error' event emitted on the
|
|
70
71
|
// stream. The SYNCHRONOUS path (a `write EPIPE` thrown straight out of
|
|
@@ -214,6 +215,9 @@ registerOutpostsCommand(program);
|
|
|
214
215
|
// mint a shareable Stripe card-capture link — the client side of the paid-
|
|
215
216
|
// provisioning gate for agents & Outposts.
|
|
216
217
|
registerBillingCommand(program);
|
|
218
|
+
program.hook("preAction", async () => {
|
|
219
|
+
await emitCliSessionStarted();
|
|
220
|
+
});
|
|
217
221
|
export async function runCli() {
|
|
218
222
|
try {
|
|
219
223
|
Sentry.addBreadcrumb({
|
|
@@ -274,4 +278,4 @@ export async function runCli() {
|
|
|
274
278
|
}
|
|
275
279
|
}
|
|
276
280
|
//# sourceMappingURL=main.js.map
|
|
277
|
-
//# debugId=
|
|
281
|
+
//# debugId=d6747715-2057-5a0c-bfc5-c0a8372639d0
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b7441453-1b37-541f-becb-a3ccb3a554cf")}catch(e){}}();
|
|
3
|
+
import { CLI_VERSION } from "../cli-version.js";
|
|
4
|
+
import { isExpiring, isMachineIdentity, loadCachedTokens, } from "./cognito-session.js";
|
|
5
|
+
import { vaultApiFetch } from "./vault-api.js";
|
|
6
|
+
const TELEMETRY_TIMEOUT_MS = 1_200;
|
|
7
|
+
let cliSessionStartedPromise;
|
|
8
|
+
/**
|
|
9
|
+
* Emit the authenticated CLI session signal using an already-cached token.
|
|
10
|
+
* This deliberately never refreshes a session or opens a browser.
|
|
11
|
+
*/
|
|
12
|
+
export function emitCliSessionStarted() {
|
|
13
|
+
cliSessionStartedPromise ??= emitCachedCliSessionStarted();
|
|
14
|
+
return cliSessionStartedPromise;
|
|
15
|
+
}
|
|
16
|
+
async function emitCachedCliSessionStarted() {
|
|
17
|
+
try {
|
|
18
|
+
const cached = loadCachedTokens();
|
|
19
|
+
if (!cached || isExpiring(cached, 120))
|
|
20
|
+
return;
|
|
21
|
+
const token = isMachineIdentity() ? cached.idToken : cached.accessToken;
|
|
22
|
+
if (!token)
|
|
23
|
+
return;
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeout = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
26
|
+
try {
|
|
27
|
+
const response = await vaultApiFetch({
|
|
28
|
+
token,
|
|
29
|
+
path: "/v1/telemetry/events",
|
|
30
|
+
method: "POST",
|
|
31
|
+
body: {
|
|
32
|
+
events: [
|
|
33
|
+
{
|
|
34
|
+
eventName: "cli_session_started",
|
|
35
|
+
app: "hq-cli",
|
|
36
|
+
source: "cli",
|
|
37
|
+
occurredAt: new Date().toISOString(),
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
properties: { version: CLI_VERSION },
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
signal: controller.signal,
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok)
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Telemetry is best effort and must never affect a CLI command.
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
clearTimeout(timeout);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Cached-token reads are also best effort (for example, a malformed cache).
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=cli-telemetry.js.map
|
|
60
|
+
//# debugId=b7441453-1b37-541f-becb-a3ccb3a554cf
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* HQ_COGNITO_CALLBACK_PORT — Loopback OAuth callback port
|
|
19
19
|
* HQ_VAULT_API_URL — vault-service API Gateway URL
|
|
20
20
|
*/
|
|
21
|
-
import { type CognitoAuthConfig, type ClientInfo, type VaultServiceConfig } from "@indigoai-us/hq-cloud";
|
|
21
|
+
import { loadCachedTokens, isExpiring, isMachineIdentity, type CognitoAuthConfig, type ClientInfo, type VaultServiceConfig } from "@indigoai-us/hq-cloud";
|
|
22
|
+
export { isExpiring, isMachineIdentity, loadCachedTokens };
|
|
22
23
|
export declare const DEFAULT_COGNITO: CognitoAuthConfig;
|
|
23
24
|
export declare const DEFAULT_VAULT_API_URL: string;
|
|
24
25
|
/**
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* HQ_VAULT_API_URL — vault-service API Gateway URL
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
22
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5a2a53de-b1ef-5d31-891c-9f508b6e4d7a")}catch(e){}}();
|
|
23
23
|
import * as fs from "fs";
|
|
24
24
|
import * as os from "os";
|
|
25
25
|
import * as path from "path";
|
|
@@ -27,6 +27,9 @@ import * as yaml from "js-yaml";
|
|
|
27
27
|
import chalk from "chalk";
|
|
28
28
|
import { loadCachedTokens, isExpiring, refreshTokens, browserLogin, detectHqCoreVersion, isMachineIdentity, getValidMachineTokens, } from "@indigoai-us/hq-cloud";
|
|
29
29
|
import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
|
|
30
|
+
// Re-export the cached-session primitives for callers that must deliberately
|
|
31
|
+
// avoid the refresh/login behavior in the higher-level helpers below.
|
|
32
|
+
export { isExpiring, isMachineIdentity, loadCachedTokens };
|
|
30
33
|
export const DEFAULT_COGNITO = {
|
|
31
34
|
region: process.env.AWS_REGION ?? "us-east-1",
|
|
32
35
|
userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "vault-indigo-hq-prod",
|
|
@@ -375,4 +378,4 @@ export async function refreshCachedSession() {
|
|
|
375
378
|
}
|
|
376
379
|
}
|
|
377
380
|
//# sourceMappingURL=cognito-session.js.map
|
|
378
|
-
//# debugId=
|
|
381
|
+
//# debugId=5a2a53de-b1ef-5d31-891c-9f508b6e4d7a
|
package/dist/utils/vault-api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6f96bd3e-9a86-54ba-ad09-951c57afac62")}catch(e){}}();
|
|
3
3
|
import { DEFAULT_VAULT_API_URL } from './cognito-session.js';
|
|
4
4
|
import { Sentry } from '../sentry.js';
|
|
5
5
|
export async function vaultApiFetch(opts) {
|
|
@@ -24,6 +24,7 @@ export async function vaultApiFetch(opts) {
|
|
|
24
24
|
'Content-Type': 'application/json',
|
|
25
25
|
},
|
|
26
26
|
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
27
|
+
signal: opts.signal,
|
|
27
28
|
});
|
|
28
29
|
if (!response.ok) {
|
|
29
30
|
Sentry.addBreadcrumb({
|
|
@@ -229,4 +230,4 @@ export async function getEntityUid(token, opts) {
|
|
|
229
230
|
return getCompanyUid(token, opts.companySlug);
|
|
230
231
|
}
|
|
231
232
|
//# sourceMappingURL=vault-api.js.map
|
|
232
|
-
//# debugId=
|
|
233
|
+
//# debugId=6f96bd3e-9a86-54ba-ad09-951c57afac62
|