@indigoai-us/hq-cli 5.70.0 → 5.72.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/bin/hq-auth-refresh.js +1 -4
  3. package/dist/cli-version.js +1 -4
  4. package/dist/commands/__fixtures__/make-tar.js +1 -4
  5. package/dist/commands/_patterns.js +1 -4
  6. package/dist/commands/add.js +1 -4
  7. package/dist/commands/agents.js +1 -4
  8. package/dist/commands/api-keys.js +1 -4
  9. package/dist/commands/auth.js +1 -4
  10. package/dist/commands/billing.js +1 -4
  11. package/dist/commands/channels.js +1 -4
  12. package/dist/commands/cloud-demote.js +1 -4
  13. package/dist/commands/cloud-provision.js +1 -4
  14. package/dist/commands/cloud.js +1 -4
  15. package/dist/commands/company.js +1 -4
  16. package/dist/commands/creators.js +1 -4
  17. package/dist/commands/crm.js +1 -4
  18. package/dist/commands/db-migrate.js +1 -4
  19. package/dist/commands/db-provision.js +1 -4
  20. package/dist/commands/db-sql.js +1 -4
  21. package/dist/commands/db-status.js +1 -4
  22. package/dist/commands/db.js +1 -4
  23. package/dist/commands/dm.js +1 -4
  24. package/dist/commands/feedback.js +1 -4
  25. package/dist/commands/files-browse.js +1 -4
  26. package/dist/commands/files.js +1 -4
  27. package/dist/commands/group-grants.js +1 -4
  28. package/dist/commands/groups.js +1 -4
  29. package/dist/commands/integrations.js +1 -4
  30. package/dist/commands/list.js +1 -4
  31. package/dist/commands/login.js +1 -4
  32. package/dist/commands/logout.js +1 -4
  33. package/dist/commands/mcp-registration.js +1 -4
  34. package/dist/commands/mcp-status.js +1 -4
  35. package/dist/commands/meetings.js +1 -4
  36. package/dist/commands/members.js +1 -4
  37. package/dist/commands/onboard-identity-guard.js +1 -4
  38. package/dist/commands/onboard-join.js +1 -4
  39. package/dist/commands/onboard-warning.js +1 -4
  40. package/dist/commands/onboard.js +1 -4
  41. package/dist/commands/outposts.d.ts +59 -1
  42. package/dist/commands/outposts.js +319 -10
  43. package/dist/commands/pack-install.js +1 -4
  44. package/dist/commands/packs.js +1 -4
  45. package/dist/commands/people.js +1 -4
  46. package/dist/commands/pkg-install.js +1 -4
  47. package/dist/commands/pkg-list.js +1 -4
  48. package/dist/commands/pkg-remove.js +1 -4
  49. package/dist/commands/pkg-update.js +1 -4
  50. package/dist/commands/publish.js +1 -4
  51. package/dist/commands/reindex.js +1 -4
  52. package/dist/commands/rescue.js +1 -4
  53. package/dist/commands/run.js +1 -4
  54. package/dist/commands/safe-extract.js +1 -4
  55. package/dist/commands/secrets-scope.js +1 -4
  56. package/dist/commands/secrets.js +1 -4
  57. package/dist/commands/signals.js +1 -4
  58. package/dist/commands/skill.js +1 -4
  59. package/dist/commands/sources.js +1 -4
  60. package/dist/commands/sync-mode.js +1 -4
  61. package/dist/commands/sync-narrow.js +1 -4
  62. package/dist/commands/sync.js +1 -4
  63. package/dist/commands/team-sync.js +1 -4
  64. package/dist/commands/update.js +1 -4
  65. package/dist/commands/whoami.js +1 -4
  66. package/dist/commands/workers.js +1 -4
  67. package/dist/index.js +1 -4
  68. package/dist/lib/db/control-plane.js +1 -4
  69. package/dist/lib/db/local.js +1 -4
  70. package/dist/lib/db/migrate.js +1 -4
  71. package/dist/lib/db/paths.js +1 -4
  72. package/dist/lib/db/remote-engine.js +1 -4
  73. package/dist/lib/db/remote-sql.js +1 -4
  74. package/dist/lib/db/sql.js +1 -4
  75. package/dist/lib/local-tree-diff.js +1 -4
  76. package/dist/lib/narrow-hint-banner.js +1 -4
  77. package/dist/main.js +1 -4
  78. package/dist/node-preflight.js +1 -4
  79. package/dist/run/discover-schemas.js +1 -4
  80. package/dist/run/hq-plugin.js +1 -4
  81. package/dist/sentry-before-send.js +1 -4
  82. package/dist/sentry-dsn.generated.js +1 -4
  83. package/dist/sentry.js +1 -4
  84. package/dist/strategies/link.js +1 -4
  85. package/dist/strategies/merge.js +1 -4
  86. package/dist/types.js +1 -4
  87. package/dist/utils/billing-gate.js +1 -4
  88. package/dist/utils/breadcrumb-buffer.js +1 -4
  89. package/dist/utils/cli-telemetry.js +1 -4
  90. package/dist/utils/cognito-session.js +1 -4
  91. package/dist/utils/contribution-table.js +1 -4
  92. package/dist/utils/environmental-error.js +1 -4
  93. package/dist/utils/epipe.js +1 -4
  94. package/dist/utils/feedback-diagnostics.js +1 -4
  95. package/dist/utils/feedback-screenshots.js +1 -4
  96. package/dist/utils/feedback-versions.js +1 -4
  97. package/dist/utils/git.js +1 -4
  98. package/dist/utils/integrity.js +1 -4
  99. package/dist/utils/intercepted-process-exit.js +1 -4
  100. package/dist/utils/login-provider.js +1 -4
  101. package/dist/utils/manifest.js +1 -4
  102. package/dist/utils/pack-contributions.js +1 -4
  103. package/dist/utils/people.js +1 -4
  104. package/dist/utils/registry-client.js +1 -4
  105. package/dist/utils/registry.js +1 -4
  106. package/dist/utils/sandbox-runner-client.js +1 -4
  107. package/dist/utils/secrets-cache.js +1 -4
  108. package/dist/utils/sentry-identity.js +1 -4
  109. package/dist/utils/vault-api.js +1 -4
  110. package/dist/utils/version-check.js +1 -4
  111. package/dist/utils/version-gate.js +1 -4
  112. package/package.json +3 -2
  113. package/scripts/chmod-bins.mjs +49 -0
  114. package/src/commands/outposts-replica-sync.test.ts +272 -0
  115. package/src/commands/outposts.test.ts +252 -4
  116. package/src/commands/outposts.ts +469 -7
  117. package/src/packaging.test.ts +55 -0
@@ -18,8 +18,6 @@
18
18
  * If the cached token is missing or expired beyond the refresh window, the
19
19
  * browser-OAuth flow opens automatically.
20
20
  */
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]="235a4623-0834-5428-957c-0c8bbe96d089")}catch(e){}}();
23
21
  import chalk from "chalk";
24
22
  import { runOnboardCli, readCheckpoint } from "@indigoai-us/hq-onboarding";
25
23
  import { DEFAULT_HQ_ROOT, DEFAULT_VAULT_API_URL, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
@@ -197,5 +195,4 @@ export function registerOnboardCommand(program) {
197
195
  }
198
196
  });
199
197
  }
200
- //# sourceMappingURL=onboard.js.map
201
- //# debugId=235a4623-0834-5428-957c-0c8bbe96d089
198
+ //# sourceMappingURL=onboard.js.map
@@ -95,6 +95,42 @@ export interface OutpostExecResult {
95
95
  * `step` distinguishes not-ready / platform-unsupported / timeout / ssm.
96
96
  */
97
97
  export declare function execOutpost(token: string, command: string, outpostId?: string): Promise<OutpostExecResult>;
98
+ /** Presigned input-upload details from `mode: "stage"`. */
99
+ export interface OutpostExecStage {
100
+ ok: true;
101
+ userId: string;
102
+ outpostId: string;
103
+ key: string;
104
+ putUrl: string;
105
+ getUrl: string;
106
+ expiresInSeconds: number;
107
+ }
108
+ /** Asynchronous SSM command details from `mode: "submit"`. */
109
+ export interface OutpostExecSubmission {
110
+ ok: true;
111
+ userId: string;
112
+ outpostId: string;
113
+ instanceId: string;
114
+ commandId: string;
115
+ outputPrefix: string;
116
+ }
117
+ /** Poll response from `mode: "result"`; streams arrive only when terminal. */
118
+ export interface OutpostExecAsyncResult {
119
+ ok: true;
120
+ userId: string;
121
+ outpostId: string;
122
+ status: string;
123
+ done: boolean;
124
+ exitCode?: number | null;
125
+ stdout?: string;
126
+ stderr?: string;
127
+ truncated?: boolean;
128
+ }
129
+ export declare function stageExecInput(token: string, outpostId?: string): Promise<OutpostExecStage>;
130
+ export declare function submitExec(token: string, command: string, outpostId?: string): Promise<OutpostExecSubmission>;
131
+ export declare function fetchExecResult(token: string, commandId: string, outpostId?: string): Promise<OutpostExecAsyncResult>;
132
+ /** Preserve a single command string; safely join argv when Commander split it. */
133
+ export declare function joinCommandParts(commandParts: string[]): string;
98
134
  /**
99
135
  * Prefix that best-effort `cd`s into the box's HQ checkout before running the
100
136
  * caller's command. `exec` runs over two transports with two different default
@@ -152,5 +188,27 @@ export interface SelfDeployDependencies {
152
188
  defaultHqRoot: () => string;
153
189
  invokingUser: () => string;
154
190
  }
155
- export declare function registerOutpostsCommand(program: Command, selfDeployOverrides?: Partial<SelfDeployDependencies>): void;
191
+ /**
192
+ * The extra local surface `replica-sync` needs on top of the self-deploy set:
193
+ * two filesystem probes for the repos clone. Kept as an extension so the
194
+ * `self-deploy` dependency surface stays minimal.
195
+ */
196
+ export interface ReplicaSyncDependencies extends SelfDeployDependencies {
197
+ pathExists: (p: string) => boolean;
198
+ mkdirp: (p: string) => void;
199
+ }
200
+ /** One repo entry resolved from `personal/data/repos.yaml`. */
201
+ export interface ReplicaRepo {
202
+ url: string;
203
+ visibility: "public" | "private";
204
+ name: string;
205
+ }
206
+ /**
207
+ * Parse `personal/data/repos.yaml` into a clone list. Pure + exported for
208
+ * tests. Mirrors setup.sh's `clone_repos`: `visibility` defaults to public,
209
+ * `name` defaults to the URL basename. Malformed entries are skipped, never
210
+ * thrown.
211
+ */
212
+ export declare function parseReposManifest(yamlText: string): ReplicaRepo[];
213
+ export declare function registerOutpostsCommand(program: Command, selfDeployOverrides?: Partial<ReplicaSyncDependencies>): void;
156
214
  //# sourceMappingURL=outposts.d.ts.map
@@ -20,8 +20,6 @@
20
20
  * web console can't rename them either), so this CLI wraps only the lifecycle
21
21
  * and status routes that exist. Renaming an Outpost is not a backend capability.
22
22
  */
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]="ff8d1d83-feab-5f6f-bf48-e4fde1a8d449")}catch(e){}}();
25
23
  import chalk from "chalk";
26
24
  import { spawnSync } from "node:child_process";
27
25
  import * as fs from "node:fs";
@@ -29,6 +27,7 @@ import * as os from "node:os";
29
27
  import * as path from "node:path";
30
28
  import * as readline from "node:readline";
31
29
  import { randomBytes } from "node:crypto";
30
+ import * as yaml from "js-yaml";
32
31
  import { loadCachedTokens } from "@indigoai-us/hq-cloud";
33
32
  import { ensureCognitoToken } from "../utils/cognito-session.js";
34
33
  import { vaultApiFetch } from "../utils/vault-api.js";
@@ -139,6 +138,63 @@ export async function execOutpost(token, command, outpostId) {
139
138
  query: outpostId ? { outpostId } : undefined,
140
139
  });
141
140
  }
141
+ export async function stageExecInput(token, outpostId) {
142
+ return outpostRequest({
143
+ token,
144
+ path: "/outpost/exec",
145
+ method: "POST",
146
+ body: { mode: "stage" },
147
+ query: outpostId ? { outpostId } : undefined,
148
+ });
149
+ }
150
+ export async function submitExec(token, command, outpostId) {
151
+ return outpostRequest({
152
+ token,
153
+ path: "/outpost/exec",
154
+ method: "POST",
155
+ body: { mode: "submit", command },
156
+ query: outpostId ? { outpostId } : undefined,
157
+ });
158
+ }
159
+ export async function fetchExecResult(token, commandId, outpostId) {
160
+ return outpostRequest({
161
+ token,
162
+ path: "/outpost/exec",
163
+ method: "POST",
164
+ body: { mode: "result", commandId },
165
+ query: outpostId ? { outpostId } : undefined,
166
+ });
167
+ }
168
+ function shellQuote(part) {
169
+ return `'${part.replace(/'/g, `'\\''`)}'`;
170
+ }
171
+ /** Preserve a single command string; safely join argv when Commander split it. */
172
+ export function joinCommandParts(commandParts) {
173
+ if (commandParts.length === 1)
174
+ return commandParts[0];
175
+ return commandParts.map(shellQuote).join(" ");
176
+ }
177
+ const EXEC_RESULT_INITIAL_POLL_MS = 500;
178
+ const EXEC_RESULT_MAX_POLL_MS = 5_000;
179
+ function sleep(ms) {
180
+ return new Promise((resolve) => setTimeout(resolve, ms));
181
+ }
182
+ async function waitForExecResult(token, commandId, outpostId) {
183
+ let delayMs = EXEC_RESULT_INITIAL_POLL_MS;
184
+ while (true) {
185
+ try {
186
+ const result = await fetchExecResult(token, commandId, outpostId);
187
+ if (result.done)
188
+ return result;
189
+ }
190
+ catch (err) {
191
+ if (!(err instanceof OutpostHttpError) || err.status !== 429)
192
+ throw err;
193
+ }
194
+ await sleep(delayMs);
195
+ delayMs = Math.min(delayMs * 2, EXEC_RESULT_MAX_POLL_MS);
196
+ }
197
+ }
142
198
  /**
143
199
  * Prefix that best-effort `cd`s into the box's HQ checkout before running the
144
200
  * caller's command. `exec` runs over two transports with two different default
@@ -449,9 +505,161 @@ async function selfDeployOutpost(opts, deps) {
449
505
  console.log(chalk.dim("Check it with: systemctl status outpost-sync.service"));
450
506
  console.log(chalk.dim("It is unregistered and unmanaged by hq-pro (no console entry or remote management)."));
451
507
  }
508
+ const defaultReplicaSyncDependencies = {
509
+ ...defaultSelfDeployDependencies,
510
+ pathExists: (p) => fs.existsSync(p),
511
+ mkdirp: (p) => {
512
+ fs.mkdirSync(p, { recursive: true });
513
+ },
514
+ };
515
+ function repoNameFromUrl(url) {
516
+ const tail = url.split("/").pop() ?? "";
517
+ return tail.replace(/\.git$/, "").trim();
518
+ }
519
+ /**
520
+ * A repo `name` becomes a path segment under repos/<visibility>/. A synced
521
+ * repos.yaml is operator data, but a malformed or hostile name must never
522
+ * escape that directory, so constrain it to a single, non-traversal component.
523
+ */
524
+ function isSafePathComponent(value) {
525
+ return (value.length > 0 &&
526
+ !value.includes("/") &&
527
+ !value.includes("\\") &&
528
+ value !== "." &&
529
+ value !== "..");
530
+ }
531
+ /**
532
+ * Parse `personal/data/repos.yaml` into a clone list. Pure + exported for
533
+ * tests. Mirrors setup.sh's `clone_repos`: `visibility` defaults to public,
534
+ * `name` defaults to the URL basename. Malformed entries are skipped, never
535
+ * thrown.
536
+ */
537
+ export function parseReposManifest(yamlText) {
538
+ let doc;
539
+ try {
540
+ doc = yaml.load(yamlText);
541
+ }
542
+ catch {
543
+ return [];
544
+ }
545
+ const rawRepos = doc?.repos;
546
+ if (!Array.isArray(rawRepos))
547
+ return [];
548
+ const out = [];
549
+ for (const entry of rawRepos) {
550
+ if (!entry || typeof entry !== "object")
551
+ continue;
552
+ const e = entry;
553
+ const url = typeof e.url === "string" ? e.url.trim() : "";
554
+ if (!url)
555
+ continue;
556
+ const visibility = e.visibility === "private" ? "private" : "public";
557
+ const name = typeof e.name === "string" && e.name.trim()
558
+ ? e.name.trim()
559
+ : repoNameFromUrl(url);
560
+ if (!isSafePathComponent(name))
561
+ continue;
562
+ out.push({ url, visibility, name });
563
+ }
564
+ return out;
565
+ }
566
+ /**
567
+ * Best-effort spawn: log a one-line outcome and carry on. NEVER throws and
568
+ * NEVER echoes child output — the same secret-safety stance as `runChecked`,
569
+ * since these children (auth refresh, secrets exec) touch credentials.
570
+ */
571
+ function runBestEffort(deps, command, args, description, options) {
572
+ let result;
573
+ try {
574
+ result = deps.spawnSync(command, args, options ?? { stdio: "inherit" });
575
+ }
576
+ catch {
577
+ console.warn(`replica-sync: ${description} could not run — continuing.`);
578
+ return false;
579
+ }
580
+ if (!result || result.error || result.status !== 0) {
581
+ console.warn(`replica-sync: ${description} did not complete cleanly — continuing.`);
582
+ return false;
583
+ }
584
+ return true;
585
+ }
586
+ /**
587
+ * Authenticate `gh` for private clones by streaming GITHUB_TOKEN through the
588
+ * vault into the child's ENV ONLY, unsetting it in-shell, and piping it to
589
+ * `gh auth login --with-token` over stdin. The token never touches argv or
590
+ * this process's logs. Mirrors personal/scripts/setup.sh's `configure_gh`.
591
+ */
592
+ function authGitHubViaVault(deps) {
593
+ const ghLogin = [
594
+ 'TOKEN="$GITHUB_TOKEN"',
595
+ "unset GITHUB_TOKEN",
596
+ 'printf "%s" "$TOKEN" | gh auth login --with-token',
597
+ ].join("\n");
598
+ const authed = runBestEffort(deps, "hq", ["secrets", "--personal", "exec", "--only", "GITHUB_TOKEN", "--", "bash", "-c", ghLogin], "GitHub auth via vault");
599
+ if (authed) {
600
+ // Let plain `git clone https://github.com/...` reuse gh's token so private
601
+ // repos don't prompt for a username/password.
602
+ runBestEffort(deps, "gh", ["auth", "setup-git"], "gh auth setup-git");
603
+ }
604
+ }
605
+ /** Clone any repos from `personal/data/repos.yaml` that aren't on disk yet. */
606
+ function replicateRepos(deps, hqRoot) {
607
+ const manifestPath = path.join(hqRoot, "personal", "data", "repos.yaml");
608
+ let manifestText;
609
+ try {
610
+ manifestText = deps.readTextFile(manifestPath);
611
+ }
612
+ catch {
613
+ console.log("replica-sync: no personal/data/repos.yaml — skipping repo clone.");
614
+ return;
615
+ }
616
+ const repos = parseReposManifest(manifestText);
617
+ if (repos.length === 0) {
618
+ console.log("replica-sync: repos.yaml lists no repos — skipping.");
619
+ return;
620
+ }
621
+ const missing = repos.filter((repo) => !deps.pathExists(path.join(hqRoot, "repos", repo.visibility, repo.name, ".git")));
622
+ if (missing.length === 0) {
623
+ console.log("replica-sync: all repos already present.");
624
+ return;
625
+ }
626
+ // Private repos need gh auth first; public-only manifests skip the vault call.
627
+ if (missing.some((repo) => repo.visibility === "private")) {
628
+ authGitHubViaVault(deps);
629
+ }
630
+ for (const repo of missing) {
631
+ deps.mkdirp(path.join(hqRoot, "repos", repo.visibility));
632
+ const dest = path.join(hqRoot, "repos", repo.visibility, repo.name);
633
+ console.log(`replica-sync: cloning ${repo.visibility}/${repo.name}…`);
634
+ runBestEffort(deps, "git", ["clone", "--recurse-submodules", repo.url, dest], `clone ${repo.visibility}/${repo.name}`);
635
+ }
636
+ }
637
+ async function replicaSyncOutpost(opts, deps) {
638
+ const hqRoot = opts.hqRoot ?? deps.defaultHqRoot();
639
+ // Gate: only replicate on a box that has an HQ session. A box that was never
640
+ // signed in has nothing to pull — exit cleanly so the timer stays green.
641
+ const session = deps.loadCachedTokens();
642
+ if (!session?.refreshToken) {
643
+ console.log("replica-sync: no HQ session on this box yet — nothing to replicate.");
644
+ return;
645
+ }
646
+ // Auth refresh gates the run: if the seeded refresh token is dead, every
647
+ // authenticated step below (pull, secrets exec) would just cascade 401s.
648
+ // Stop the cycle cleanly instead — the timer retries next tick.
649
+ if (!runBestEffort(deps, "hq", ["auth", "refresh"], "HQ auth refresh")) {
650
+ console.warn("replica-sync: auth refresh failed — skipping this cycle (token likely expired).");
651
+ return;
652
+ }
653
+ runBestEffort(deps, "hq", ["sync", "pull", "--personal", "--hq-root", hqRoot, "--on-conflict", "keep"], "personal vault pull");
654
+ // rescue lays down the core kernel and expects companies/ to exist.
655
+ deps.mkdirp(path.join(hqRoot, "companies"));
656
+ runBestEffort(deps, "hq", ["rescue", "--hq-root", hqRoot, "--yes"], "HQ kernel rescue");
657
+ replicateRepos(deps, hqRoot);
658
+ console.log("replica-sync: complete.");
659
+ }
452
660
  export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
453
- const selfDeployDependencies = {
454
- ...defaultSelfDeployDependencies,
661
+ const deps = {
662
+ ...defaultReplicaSyncDependencies,
455
663
  ...selfDeployOverrides,
456
664
  };
457
665
  const outposts = program
@@ -461,15 +669,29 @@ export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
461
669
  .command("self-deploy", { hidden: true })
462
670
  .description("Configure this EC2 host as a locally self-hosted HQ outpost")
463
671
  .option("--yes", "Skip the self-hosting confirmation")
464
- .option("--hq-root <path>", "HQ root to sync", selfDeployDependencies.defaultHqRoot())
672
+ .option("--hq-root <path>", "HQ root to sync", deps.defaultHqRoot())
465
673
  .action(async (opts) => {
466
674
  try {
467
- await selfDeployOutpost(opts, selfDeployDependencies);
675
+ await selfDeployOutpost(opts, deps);
468
676
  }
469
677
  catch (err) {
470
678
  fail(err);
471
679
  }
472
680
  });
681
+ outposts
682
+ .command("replica-sync", { hidden: true })
683
+ .description("Replicate your full HQ onto this box (personal pull + core rescue + repos). Runs on a timer.")
684
+ .option("--hq-root <path>", "HQ root to replicate into", deps.defaultHqRoot())
685
+ .action(async (opts) => {
686
+ // Best-effort background job: never hard-fail the systemd unit on a
687
+ // transient step. Swallow, log, and exit 0.
688
+ try {
689
+ await replicaSyncOutpost(opts, deps);
690
+ }
691
+ catch (err) {
692
+ console.warn(`replica-sync: unexpected error — ${err instanceof Error ? err.message : String(err)}`);
693
+ }
694
+ });
473
695
  outposts
474
696
  .command("provision")
475
697
  .alias("create")
@@ -602,8 +824,8 @@ export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
602
824
  .option("--json", "Emit raw JSON")
603
825
  .action(async function (commandParts, opts) {
604
826
  try {
605
- const command = commandParts.join(" ").trim();
606
- if (!command) {
827
+ const command = joinCommandParts(commandParts);
828
+ if (!command.trim()) {
607
829
  console.error(chalk.red("No command given. Usage: hq outposts exec -- <command>"));
608
830
  process.exit(1);
609
831
  }
@@ -669,6 +891,94 @@ export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
669
891
  fail(err);
670
892
  }
671
893
  });
894
+ outposts
895
+ .command("exec-stage")
896
+ .description("Stage a file for an asynchronous Outpost command")
897
+ .requiredOption("--file <path>", "File to upload")
898
+ .option("--id <outpostId>", "Outpost id (defaults to your primary box)")
899
+ .option("--json", "Emit raw JSON")
900
+ .action(async function (opts) {
901
+ try {
902
+ const bytes = fs.readFileSync(opts.file);
903
+ const token = await ensureCognitoToken();
904
+ const staged = await stageExecInput(token, opts.id);
905
+ const upload = await fetch(staged.putUrl, {
906
+ method: "PUT",
907
+ headers: { "Content-Length": String(bytes.byteLength) },
908
+ body: bytes,
909
+ });
910
+ if (!upload.ok) {
911
+ throw new Error(`Could not upload exec input: HTTP ${upload.status} ${upload.statusText}`);
912
+ }
913
+ const output = { key: staged.key, getUrl: staged.getUrl };
914
+ if (opts.json) {
915
+ process.stdout.write(JSON.stringify(output) + "\n");
916
+ }
917
+ else {
918
+ printKeyValues(output);
919
+ }
920
+ }
921
+ catch (err) {
922
+ fail(err);
923
+ }
924
+ });
925
+ outposts
926
+ .command("exec-submit <command...>")
927
+ .description("Submit an asynchronous shell command to an Outpost")
928
+ .option("--id <outpostId>", "Outpost id (defaults to your primary box)")
929
+ .option("--json", "Emit raw JSON")
930
+ .action(async function (commandParts, opts) {
931
+ try {
932
+ const command = joinCommandParts(commandParts);
933
+ if (!command.trim()) {
934
+ console.error(chalk.red("No command given. Usage: hq outposts exec-submit -- <command>"));
935
+ process.exit(1);
936
+ }
937
+ const token = await ensureCognitoToken();
938
+ const submitted = await submitExec(token, command, opts.id);
939
+ const output = { commandId: submitted.commandId };
940
+ if (opts.json) {
941
+ process.stdout.write(JSON.stringify(output) + "\n");
942
+ }
943
+ else {
944
+ printKeyValues(output);
945
+ }
946
+ }
947
+ catch (err) {
948
+ fail(err);
949
+ }
950
+ });
951
+ outposts
952
+ .command("exec-result")
953
+ .description("Fetch the result of an asynchronous Outpost command")
954
+ .requiredOption("--command-id <commandId>", "Command id returned by exec-submit")
955
+ .option("--id <outpostId>", "Outpost id (defaults to your primary box)")
956
+ .option("--wait", "Poll until the command reaches a terminal state")
957
+ .option("--json", "Emit raw JSON")
958
+ .action(async function (opts) {
959
+ try {
960
+ const token = await ensureCognitoToken();
961
+ const result = opts.wait
962
+ ? await waitForExecResult(token, opts.commandId, opts.id)
963
+ : await fetchExecResult(token, opts.commandId, opts.id);
964
+ const output = {
965
+ done: result.done,
966
+ status: result.status,
967
+ exitCode: result.exitCode ?? null,
968
+ stdout: result.stdout ?? "",
969
+ stderr: result.stderr ?? "",
970
+ };
971
+ if (opts.json) {
972
+ process.stdout.write(JSON.stringify(output) + "\n");
973
+ }
974
+ else {
975
+ printKeyValues(output);
976
+ }
977
+ }
978
+ catch (err) {
979
+ fail(err);
980
+ }
981
+ });
672
982
  outposts
673
983
  .command("codex-enable")
674
984
  .description("Enable (or retry) Codex on an Outpost")
@@ -740,5 +1050,4 @@ export function registerOutpostsCommand(program, selfDeployOverrides = {}) {
740
1050
  }
741
1051
  });
742
1052
  }
743
- //# sourceMappingURL=outposts.js.map
744
- //# debugId=ff8d1d83-feab-5f6f-bf48-e4fde1a8d449
1053
+ //# sourceMappingURL=outposts.js.map
@@ -33,8 +33,6 @@
33
33
  * registry file under the v12 layout. (`hq update <pack>` re-resolves source
34
34
  * from each pack's package.yaml; rationale lives in the layout-fix PR.)
35
35
  */
36
-
37
- !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]="a90272ca-94a1-5905-85f8-af12a01cb58a")}catch(e){}}();
38
36
  import * as fs from 'fs';
39
37
  import * as os from 'os';
40
38
  import * as path from 'path';
@@ -1699,5 +1697,4 @@ export async function installPack(source, opts = {}) {
1699
1697
  fs.rmSync(tmpDir, { recursive: true, force: true });
1700
1698
  }
1701
1699
  }
1702
- //# sourceMappingURL=pack-install.js.map
1703
- //# debugId=a90272ca-94a1-5905-85f8-af12a01cb58a
1700
+ //# sourceMappingURL=pack-install.js.map
@@ -16,8 +16,6 @@
16
16
  *
17
17
  * Spec: knowledge/public/hq-core/package-yaml-spec.md.
18
18
  */
19
-
20
- !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]="b743f774-8d9b-594b-9ce4-e7f5a44faca0")}catch(e){}}();
21
19
  import * as fs from 'fs';
22
20
  import * as path from 'path';
23
21
  import * as readline from 'readline';
@@ -469,5 +467,4 @@ export function registerPacksCommand(parent) {
469
467
  }
470
468
  });
471
469
  }
472
- //# sourceMappingURL=packs.js.map
473
- //# debugId=b743f774-8d9b-594b-9ce4-e7f5a44faca0
470
+ //# sourceMappingURL=packs.js.map
@@ -11,8 +11,6 @@
11
11
  * roster for exactly ONE company (the active company, or the one named by
12
12
  * `--company`); nothing reads across company boundaries.
13
13
  */
14
-
15
- !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]="555055d9-98e2-5fcd-b736-4b1df12daf4d")}catch(e){}}();
16
14
  import * as fs from "fs";
17
15
  import { Option } from "commander";
18
16
  import chalk from "chalk";
@@ -292,5 +290,4 @@ export function registerPeopleCommand(program, deps = {}) {
292
290
  }
293
291
  });
294
292
  }
295
- //# sourceMappingURL=people.js.map
296
- //# debugId=555055d9-98e2-5fcd-b736-4b1df12daf4d
293
+ //# sourceMappingURL=people.js.map
@@ -12,8 +12,6 @@
12
12
  * 8. Update packages/registry.yaml
13
13
  * 9. Print next-step message
14
14
  */
15
-
16
- !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]="ccc2e70c-f577-5a47-9a29-31a206194392")}catch(e){}}();
17
15
  import * as fs from 'fs';
18
16
  import * as os from 'os';
19
17
  import * as path from 'path';
@@ -165,5 +163,4 @@ async function installPackage(slug, company) {
165
163
  }
166
164
  }
167
165
  }
168
- //# sourceMappingURL=pkg-install.js.map
169
- //# debugId=ccc2e70c-f577-5a47-9a29-31a206194392
166
+ //# sourceMappingURL=pkg-install.js.map
@@ -4,11 +4,8 @@
4
4
  * Package installs use the content-pack installer, so this command delegates
5
5
  * to the same list view as `hq packs list`.
6
6
  */
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]="96eb060c-c1d0-5874-918f-e818f92c6242")}catch(e){}}();
9
7
  import { registerPacksListCommand } from './packs.js';
10
8
  export function registerPackageListCommand(parent) {
11
9
  registerPacksListCommand(parent);
12
10
  }
13
- //# sourceMappingURL=pkg-list.js.map
14
- //# debugId=96eb060c-c1d0-5874-918f-e818f92c6242
11
+ //# sourceMappingURL=pkg-list.js.map
@@ -5,8 +5,6 @@
5
5
  * 2. Remove entry from packages/registry.yaml
6
6
  * 3. Print next-step message
7
7
  */
8
-
9
- !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]="e44da498-3e38-5643-b124-49aa2e91f42b")}catch(e){}}();
10
8
  import * as fs from 'fs';
11
9
  import * as path from 'path';
12
10
  import chalk from 'chalk';
@@ -52,5 +50,4 @@ async function removePackage(slug) {
52
50
  console.log(chalk.green(`\nRemoved ${slug}.`));
53
51
  console.log(chalk.cyan('Run /package-remove ' + slug + ' in Claude to clean up merged content.'));
54
52
  }
55
- //# sourceMappingURL=pkg-remove.js.map
56
- //# debugId=e44da498-3e38-5643-b124-49aa2e91f42b
53
+ //# sourceMappingURL=pkg-remove.js.map
@@ -4,8 +4,6 @@
4
4
  * If no slug: check all installed packages for updates.
5
5
  * If slug given: update only that package.
6
6
  */
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]="35fae6a0-f8e1-5fde-90d8-34ae4adb3c1c")}catch(e){}}();
9
7
  import * as fs from 'fs';
10
8
  import * as os from 'os';
11
9
  import * as path from 'path';
@@ -123,5 +121,4 @@ async function updatePackages(slug) {
123
121
  }
124
122
  console.log(`\n${updatedCount} package(s) updated${updatedCount > 0 ? '.' : ' — everything is current.'}`);
125
123
  }
126
- //# sourceMappingURL=pkg-update.js.map
127
- //# debugId=35fae6a0-f8e1-5fde-90d8-34ae4adb3c1c
124
+ //# sourceMappingURL=pkg-update.js.map
@@ -27,8 +27,6 @@
27
27
  * - pack-install.ts:validateManifest (the 10 validation rules)
28
28
  * - manifest.ts (modules.yaml read/write for provenance)
29
29
  */
30
-
31
- !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]="e2f6607e-188c-57f9-beba-a81488d15225")}catch(e){}}();
32
30
  import * as fs from 'fs';
33
31
  import * as os from 'os';
34
32
  import * as path from 'path';
@@ -405,5 +403,4 @@ export function registerPublishCommand(program) {
405
403
  }
406
404
  });
407
405
  }
408
- //# sourceMappingURL=publish.js.map
409
- //# debugId=e2f6607e-188c-57f9-beba-a81488d15225
406
+ //# sourceMappingURL=publish.js.map
@@ -1,5 +1,3 @@
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]="91aee9b3-dd72-5f05-99a8-d23b37f5eabe")}catch(e){}}();
3
1
  import { reindex } from '@indigoai-us/hq-cloud';
4
2
  export function registerReindexCommand(program) {
5
3
  program
@@ -34,5 +32,4 @@ export function registerReindexCommand(program) {
34
32
  process.exit(status);
35
33
  });
36
34
  }
37
- //# sourceMappingURL=reindex.js.map
38
- //# debugId=91aee9b3-dd72-5f05-99a8-d23b37f5eabe
35
+ //# sourceMappingURL=reindex.js.map
@@ -1,5 +1,3 @@
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]="e0da62c9-7fd5-5514-b2d0-1b5b76ebcd21")}catch(e){}}();
3
1
  import { spawnSync } from 'child_process';
4
2
  import * as fs from 'fs';
5
3
  import * as path from 'path';
@@ -157,5 +155,4 @@ export function registerRescueCommand(program) {
157
155
  }
158
156
  });
159
157
  }
160
- //# sourceMappingURL=rescue.js.map
161
- //# debugId=e0da62c9-7fd5-5514-b2d0-1b5b76ebcd21
158
+ //# sourceMappingURL=rescue.js.map
@@ -1,5 +1,3 @@
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]="26bb7e02-3864-5c66-8232-3abc8fc17dc1")}catch(e){}}();
3
1
  import { spawn } from 'node:child_process';
4
2
  import * as path from 'node:path';
5
3
  import * as fs from 'node:fs';
@@ -144,5 +142,4 @@ export function registerRunCommand(program) {
144
142
  }
145
143
  });
146
144
  }
147
- //# sourceMappingURL=run.js.map
148
- //# debugId=26bb7e02-3864-5c66-8232-3abc8fc17dc1
145
+ //# sourceMappingURL=run.js.map
@@ -32,8 +32,6 @@
32
32
  * actually written (we do NOT rely solely on the pre-flight scan or on the
33
33
  * tar binary's own protections).
34
34
  */
35
-
36
- !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]="372e4f76-03b2-5287-ac66-249543f88937")}catch(e){}}();
37
35
  import { execFileSync } from 'child_process';
38
36
  import * as fs from 'fs';
39
37
  import * as path from 'path';
@@ -343,5 +341,4 @@ export function safeExtractTarball(tarballPath, finalDir, opts = {}) {
343
341
  }
344
342
  }
345
343
  }
346
- //# sourceMappingURL=safe-extract.js.map
347
- //# debugId=372e4f76-03b2-5287-ac66-249543f88937
344
+ //# sourceMappingURL=safe-extract.js.map