@indigoai-us/hq-cli 5.25.0 → 5.26.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.
@@ -80,9 +80,29 @@ jobs:
80
80
  echo "published=true" >> "$GITHUB_OUTPUT"
81
81
  fi
82
82
 
83
+ # Sentry sourcemap upload runs on every tag push regardless of whether
84
+ # this run was the one that actually published to npm. The previous
85
+ # `if: steps.publish.outputs.published == 'true'` gate made re-runs
86
+ # useless when only the Sentry upload failed — the publish step's
87
+ # short-circuit (`npm view @ver already published`) zeroed out the
88
+ # output, so the upload step was skipped on every re-run. dist/ is
89
+ # rebuilt by the Build + Inject steps each run, so the upload is
90
+ # always operating on fresh, debug-id-stamped artifacts.
83
91
  - name: Upload sourcemaps to Sentry
84
- if: steps.publish.outputs.published == 'true'
85
92
  run: |
93
+ # Defang paste-time whitespace in SENTRY_AUTH_TOKEN. Sentry's
94
+ # auth-header parser returns HTTP 401 "Token string should not
95
+ # contain spaces" when the secret has a trailing \n or any
96
+ # embedded whitespace — observed on the v5.25.0 release after a
97
+ # copy-paste into the GH repo secret carried a literal \n. `tr -d`
98
+ # is the cheap structural guard so the workflow doesn't blow up
99
+ # on the next dirty paste.
100
+ SENTRY_AUTH_TOKEN=$(printf %s "$SENTRY_AUTH_TOKEN" | tr -d '[:space:]')
101
+ if [ -z "$SENTRY_AUTH_TOKEN" ]; then
102
+ echo "::error::SENTRY_AUTH_TOKEN secret is empty (or all whitespace) after sanitization — cannot upload sourcemaps"
103
+ exit 1
104
+ fi
105
+ export SENTRY_AUTH_TOKEN
86
106
  VER=$(jq -r .version package.json)
87
107
  npx -y @sentry/cli@^2 sourcemaps upload --release "hq-cli@$VER" dist/
88
108
  env:
@@ -0,0 +1,41 @@
1
+ import { Command } from "commander";
2
+ export interface DmRecipient {
3
+ toEmail?: string;
4
+ toPersonUid?: string;
5
+ }
6
+ /**
7
+ * Classify a recipient arg as an email or a personUid. Mirrors the
8
+ * email/prs_ heuristic used by `hq members`. Returns null for neither.
9
+ */
10
+ export declare function detectRecipient(recipient: string): DmRecipient | null;
11
+ /**
12
+ * Parse a relative duration like `30s`, `10m`, `2h`, `1d` into milliseconds.
13
+ * Returns null on anything that doesn't match. Pure → unit-testable.
14
+ */
15
+ export declare function parseDuration(input: string): number | null;
16
+ export interface DmSendBody {
17
+ toEmail?: string;
18
+ toPersonUid?: string;
19
+ body: string;
20
+ prompt?: string;
21
+ details?: string;
22
+ deliverAt?: string;
23
+ }
24
+ /**
25
+ * Build the POST /v1/notify/dm request body from CLI inputs. Pure (no I/O,
26
+ * no clock) so the option-resolution logic is unit-testable; the caller
27
+ * supplies `now` for the `--in` relative-delay computation.
28
+ *
29
+ * Throws Error with a user-facing message on invalid input.
30
+ */
31
+ export declare function buildDmBody(args: {
32
+ recipient: string;
33
+ message: string;
34
+ prompt?: string;
35
+ details?: string;
36
+ at?: string;
37
+ inDelay?: string;
38
+ now: number;
39
+ }): DmSendBody;
40
+ export declare function registerDmCommand(program: Command): void;
41
+ //# sourceMappingURL=dm.d.ts.map
@@ -0,0 +1,148 @@
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]="69d48194-d5ae-508e-b614-68f2066db6ee")}catch(e){}}();
3
+ import chalk from "chalk";
4
+ import { readFileSync } from "node:fs";
5
+ import { ensureCognitoToken } from "../utils/cognito-session.js";
6
+ import { vaultApiFetch } from "../utils/vault-api.js";
7
+ const EMAIL_PATTERN = /^[^\s]+@[^\s]+$/;
8
+ const PERSON_UID_PATTERN = /^prs_[A-Za-z0-9_-]+$/;
9
+ /**
10
+ * Classify a recipient arg as an email or a personUid. Mirrors the
11
+ * email/prs_ heuristic used by `hq members`. Returns null for neither.
12
+ */
13
+ export function detectRecipient(recipient) {
14
+ const r = recipient.trim();
15
+ if (EMAIL_PATTERN.test(r))
16
+ return { toEmail: r.toLowerCase() };
17
+ if (PERSON_UID_PATTERN.test(r))
18
+ return { toPersonUid: r };
19
+ return null;
20
+ }
21
+ /**
22
+ * Parse a relative duration like `30s`, `10m`, `2h`, `1d` into milliseconds.
23
+ * Returns null on anything that doesn't match. Pure → unit-testable.
24
+ */
25
+ export function parseDuration(input) {
26
+ const m = /^(\d+)\s*(s|m|h|d)$/.exec(input.trim());
27
+ if (!m)
28
+ return null;
29
+ const n = parseInt(m[1], 10);
30
+ const mult = {
31
+ s: 1000,
32
+ m: 60_000,
33
+ h: 3_600_000,
34
+ d: 86_400_000,
35
+ };
36
+ return n * mult[m[2]];
37
+ }
38
+ /**
39
+ * Build the POST /v1/notify/dm request body from CLI inputs. Pure (no I/O,
40
+ * no clock) so the option-resolution logic is unit-testable; the caller
41
+ * supplies `now` for the `--in` relative-delay computation.
42
+ *
43
+ * Throws Error with a user-facing message on invalid input.
44
+ */
45
+ export function buildDmBody(args) {
46
+ const rcpt = detectRecipient(args.recipient);
47
+ if (!rcpt) {
48
+ throw new Error(`Invalid recipient '${args.recipient}': must be an email address or a personUid (prs_…).`);
49
+ }
50
+ const body = (args.message ?? "").trim();
51
+ if (!body) {
52
+ throw new Error("A message body is required: hq dm <recipient> <message>");
53
+ }
54
+ if (args.at && args.inDelay) {
55
+ throw new Error("Use only one of --at or --in, not both.");
56
+ }
57
+ let deliverAt;
58
+ if (args.at) {
59
+ const when = new Date(args.at);
60
+ if (isNaN(when.getTime())) {
61
+ throw new Error(`Invalid --at '${args.at}': must be an ISO8601 date.`);
62
+ }
63
+ deliverAt = when.toISOString();
64
+ }
65
+ else if (args.inDelay) {
66
+ const ms = parseDuration(args.inDelay);
67
+ if (ms === null) {
68
+ throw new Error(`Invalid --in '${args.inDelay}': use a relative delay like 30s, 10m, 2h, 1d.`);
69
+ }
70
+ deliverAt = new Date(args.now + ms).toISOString();
71
+ }
72
+ const prompt = args.prompt?.trim();
73
+ const details = args.details?.trim();
74
+ return {
75
+ ...rcpt,
76
+ body,
77
+ ...(prompt ? { prompt } : {}),
78
+ ...(details ? { details } : {}),
79
+ ...(deliverAt ? { deliverAt } : {}),
80
+ };
81
+ }
82
+ function friendlyDmError(status, code, fallback) {
83
+ if (status === 401)
84
+ return "Not authenticated — run `hq login` and try again.";
85
+ if (status === 404 || code === "RECIPIENT_NOT_FOUND") {
86
+ return "Recipient not found or not reachable — you can only DM someone you share an active company with.";
87
+ }
88
+ if (status >= 500)
89
+ return `Server error: ${fallback}`;
90
+ return fallback;
91
+ }
92
+ export function registerDmCommand(program) {
93
+ program
94
+ .command("dm <recipient> [message]")
95
+ .description("Send a direct message to a teammate (email or personUid). They receive it as an HQ Sync notification.")
96
+ .option("--prompt <text>", "Agent-context prompt the recipient can one-click copy into their agent")
97
+ .option("--prompt-file <path>", "Read the agent prompt from a file")
98
+ .option("--details <text>", "Longer detail shown in the recipient's DM detail window")
99
+ .option("--details-file <path>", "Read the details from a file")
100
+ .option("--at <iso>", "Schedule delivery at an ISO8601 time (store-and-forward; delivered within ~60s of the time)")
101
+ .option("--in <duration>", "Schedule delivery after a relative delay: 30s, 10m, 2h, 1d")
102
+ .action(async (recipient, message, opts) => {
103
+ try {
104
+ // Resolve prompt/details from inline text or a file.
105
+ let prompt = opts.prompt;
106
+ if (opts.promptFile)
107
+ prompt = readFileSync(opts.promptFile, "utf8");
108
+ let details = opts.details;
109
+ if (opts.detailsFile)
110
+ details = readFileSync(opts.detailsFile, "utf8");
111
+ const reqBody = buildDmBody({
112
+ recipient,
113
+ message: message ?? "",
114
+ prompt,
115
+ details,
116
+ at: opts.at,
117
+ inDelay: opts.in,
118
+ now: Date.now(),
119
+ });
120
+ const token = await ensureCognitoToken();
121
+ const res = await vaultApiFetch({
122
+ token,
123
+ path: "/v1/notify/dm",
124
+ method: "POST",
125
+ body: reqBody,
126
+ });
127
+ if (!res.ok) {
128
+ const err = (await res.json().catch(() => ({})));
129
+ console.error(chalk.red(friendlyDmError(res.status, err.code, err.error ?? err.message ?? res.statusText)));
130
+ process.exit(1);
131
+ }
132
+ const data = (await res.json());
133
+ if (data.scheduled) {
134
+ console.log(chalk.green(`Scheduled DM to ${recipient} for ${data.deliverAt} (eventId ${data.eventId}).`));
135
+ console.log(chalk.dim("It delivers within ~60s of that time, even if you're offline."));
136
+ }
137
+ else {
138
+ console.log(chalk.green(`DM sent to ${recipient} (eventId ${data.eventId}).`));
139
+ }
140
+ }
141
+ catch (err) {
142
+ console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
143
+ process.exit(1);
144
+ }
145
+ });
146
+ }
147
+ //# sourceMappingURL=dm.js.map
148
+ //# debugId=69d48194-d5ae-508e-b614-68f2066db6ee
@@ -13,7 +13,7 @@
13
13
  * 9. Print next-step message
14
14
  */
15
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]="874fe8a4-2348-5f03-8600-c545cac13fc9")}catch(e){}}();
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]="c6ab4668-f147-5612-bbfc-e1e80b2f018a")}catch(e){}}();
17
17
  import * as fs from 'fs';
18
18
  import * as os from 'os';
19
19
  import * as path from 'path';
@@ -21,7 +21,7 @@ import * as yaml from 'js-yaml';
21
21
  import { execSync } from 'child_process';
22
22
  import chalk from 'chalk';
23
23
  import { ensureCognitoToken } from '../utils/cognito-session.js';
24
- import { findHqRoot } from '../utils/hq-root.js';
24
+ import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
25
25
  import { getRegistryUrl, RegistryClient, } from '../utils/registry-client.js';
26
26
  import { verifySha256, verifyRsaSignature } from '../utils/integrity.js';
27
27
  import { addToRegistry } from '../utils/registry.js';
@@ -93,7 +93,7 @@ async function installPackage(slug, company) {
93
93
  }
94
94
  }
95
95
  // 7. Extract
96
- const hqRoot = findHqRoot();
96
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
97
97
  const installDir = path.resolve(hqRoot, 'packages', 'installed', slug);
98
98
  // Clean existing installation
99
99
  if (fs.existsSync(installDir)) {
@@ -156,4 +156,4 @@ async function installPackage(slug, company) {
156
156
  }
157
157
  }
158
158
  //# sourceMappingURL=pkg-install.js.map
159
- //# debugId=874fe8a4-2348-5f03-8600-c545cac13fc9
159
+ //# debugId=c6ab4668-f147-5612-bbfc-e1e80b2f018a
@@ -4,9 +4,9 @@
4
4
  * Graceful offline: if registry is unreachable, show cached data with a note.
5
5
  */
6
6
 
7
- !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]="ce4886ce-109a-5ace-b8d9-4e5bed61978a")}catch(e){}}();
7
+ !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]="2ca922cd-1115-5059-850d-3927226eb9ee")}catch(e){}}();
8
8
  import chalk from 'chalk';
9
- import { findHqRoot } from '../utils/hq-root.js';
9
+ import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
10
10
  import { readRegistry } from '../utils/registry.js';
11
11
  import { loadCachedTokens, isExpiring } from '@indigoai-us/hq-cloud';
12
12
  import { getRegistryUrl, RegistryClient, } from '../utils/registry-client.js';
@@ -26,7 +26,7 @@ export function registerPackageListCommand(parent) {
26
26
  });
27
27
  }
28
28
  async function listPackages() {
29
- const hqRoot = findHqRoot();
29
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
30
30
  const installed = readRegistry(hqRoot);
31
31
  // Print installed packages
32
32
  if (installed.length > 0) {
@@ -70,4 +70,4 @@ async function listPackages() {
70
70
  }
71
71
  }
72
72
  //# sourceMappingURL=pkg-list.js.map
73
- //# debugId=ce4886ce-109a-5ace-b8d9-4e5bed61978a
73
+ //# debugId=2ca922cd-1115-5059-850d-3927226eb9ee
@@ -6,11 +6,11 @@
6
6
  * 3. Print next-step message
7
7
  */
8
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]="b54529d6-de12-595e-b1c7-ab50b9f22eb5")}catch(e){}}();
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
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import chalk from 'chalk';
13
- import { findHqRoot } from '../utils/hq-root.js';
13
+ import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
14
14
  import { removeFromRegistry, readRegistry } from '../utils/registry.js';
15
15
  export function registerPackageRemoveCommand(parent) {
16
16
  parent
@@ -27,7 +27,7 @@ export function registerPackageRemoveCommand(parent) {
27
27
  });
28
28
  }
29
29
  async function removePackage(slug) {
30
- const hqRoot = findHqRoot();
30
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
31
31
  const installDir = path.resolve(hqRoot, 'packages', 'installed', slug);
32
32
  // Verify it is actually installed
33
33
  const entries = readRegistry(hqRoot);
@@ -53,4 +53,4 @@ async function removePackage(slug) {
53
53
  console.log(chalk.cyan('Run /package-remove ' + slug + ' in Claude to clean up merged content.'));
54
54
  }
55
55
  //# sourceMappingURL=pkg-remove.js.map
56
- //# debugId=b54529d6-de12-595e-b1c7-ab50b9f22eb5
56
+ //# debugId=e44da498-3e38-5643-b124-49aa2e91f42b
@@ -5,7 +5,7 @@
5
5
  * If slug given: update only that package.
6
6
  */
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]="3ef2968d-eb65-594b-a506-4314b70dd3dc")}catch(e){}}();
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
9
  import * as fs from 'fs';
10
10
  import * as os from 'os';
11
11
  import * as path from 'path';
@@ -13,7 +13,7 @@ import * as yaml from 'js-yaml';
13
13
  import { execSync } from 'child_process';
14
14
  import chalk from 'chalk';
15
15
  import { ensureCognitoToken } from '../utils/cognito-session.js';
16
- import { findHqRoot } from '../utils/hq-root.js';
16
+ import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
17
17
  import { getRegistryUrl, RegistryClient, } from '../utils/registry-client.js';
18
18
  import { verifySha256, verifyRsaSignature } from '../utils/integrity.js';
19
19
  import { readRegistry, addToRegistry, } from '../utils/registry.js';
@@ -32,7 +32,7 @@ export function registerPackageUpdateCommand(parent) {
32
32
  });
33
33
  }
34
34
  async function updatePackages(slug) {
35
- const hqRoot = findHqRoot();
35
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
36
36
  const entries = readRegistry(hqRoot);
37
37
  if (entries.length === 0) {
38
38
  console.log('No packages installed. Use "hq packages install <slug>" to install one.');
@@ -124,4 +124,4 @@ async function updatePackages(slug) {
124
124
  console.log(`\n${updatedCount} package(s) updated${updatedCount > 0 ? '.' : ' — everything is current.'}`);
125
125
  }
126
126
  //# sourceMappingURL=pkg-update.js.map
127
- //# debugId=3ef2968d-eb65-594b-a506-4314b70dd3dc
127
+ //# debugId=35fae6a0-f8e1-5fde-90d8-34ae4adb3c1c
@@ -10,13 +10,13 @@
10
10
  * 6. Gracefully handle conflicts (warn, don't force overwrite)
11
11
  */
12
12
 
13
- !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]="4c9a650e-05ed-5e33-b5de-b6d521ecdf4f")}catch(e){}}();
13
+ !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]="2b2504c1-06d5-59f8-a9b3-41d1af7f745f")}catch(e){}}();
14
14
  import * as fs from 'fs';
15
15
  import * as path from 'path';
16
16
  import { execSync } from 'child_process';
17
17
  import chalk from 'chalk';
18
18
  import simpleGit from 'simple-git';
19
- import { findHqRoot } from '../utils/hq-root.js';
19
+ import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
20
20
  import { ensureCognitoToken } from '../utils/cognito-session.js';
21
21
  // ─── API helpers ────────────────────────────────────────────────────────────
22
22
  const API_BASE = 'https://example.com/api';
@@ -308,7 +308,7 @@ export function registerTeamSyncCommand(program) {
308
308
  .option('--dry-run', 'Show what would be synced without making changes')
309
309
  .action(async (options) => {
310
310
  try {
311
- const hqRoot = findHqRoot();
311
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
312
312
  // 1. Discover team directories
313
313
  let teamDirs = discoverTeamDirs(hqRoot);
314
314
  if (teamDirs.length === 0) {
@@ -422,4 +422,4 @@ export function registerTeamSyncCommand(program) {
422
422
  });
423
423
  }
424
424
  //# sourceMappingURL=team-sync.js.map
425
- //# debugId=4c9a650e-05ed-5e33-b5de-b6d521ecdf4f
425
+ //# debugId=2b2504c1-06d5-59f8-a9b3-41d1af7f745f
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * HQ CLI - Module management, package management, and cloud sync for HQ
4
4
  */
5
5
 
6
- !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]="a9b69c08-39a6-58c3-a4b6-a309d0ab8254")}catch(e){}}();
6
+ !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]="d7693093-4011-58fa-b5be-805b5f9f5421")}catch(e){}}();
7
7
  import { Command } from "commander";
8
8
  import { initSentry, Sentry } from "./sentry.js";
9
9
  import { registerAddCommand } from "./commands/add.js";
@@ -31,12 +31,14 @@ import { registerGroupsCommand } from "./commands/groups.js";
31
31
  import { registerFilesCommand } from "./commands/files.js";
32
32
  import { registerFilesBrowseCommands } from "./commands/files-browse.js";
33
33
  import { registerMembersCommand } from "./commands/members.js";
34
+ import { registerDmCommand } from "./commands/dm.js";
34
35
  import { registerFeedbackCommand } from "./commands/feedback.js";
35
36
  import { registerMeetingsCommand } from "./commands/meetings.js";
36
37
  import { registerSourcesCommand } from "./commands/sources.js";
37
38
  import { registerSignalsCommand } from "./commands/signals.js";
38
39
  import { sanitizeArgv } from "./utils/feedback-diagnostics.js";
39
40
  import { maybeWarnNewVersion, refreshVersionCache, } from "./utils/version-check.js";
41
+ import { enforceVersionGate, shouldSkipGate, } from "./utils/version-gate.js";
40
42
  import { CLI_VERSION } from "./cli-version.js";
41
43
  // Swallow EPIPE when a downstream reader (e.g. `source <(…)`, `| head`) closes the pipe early.
42
44
  const onPipeError = (err) => {
@@ -109,6 +111,7 @@ const filesCmd = registerFilesCommand(program);
109
111
  registerFilesBrowseCommands(filesCmd);
110
112
  // Membership management (subcommand group — hq members invite|list|revoke)
111
113
  registerMembersCommand(program);
114
+ registerDmCommand(program);
112
115
  // Onboarding (top-level — Cognito + vault-service provisioning)
113
116
  registerOnboardCommand(program);
114
117
  // Feedback (subcommand group — hq feedback bug|feature)
@@ -126,6 +129,14 @@ registerSignalsCommand(program);
126
129
  message: sanitizeArgv(process.argv.slice(2)).join(" "),
127
130
  level: "info",
128
131
  });
132
+ // Hard version gate: ask hq-pro whether this CLI is below the floor and
133
+ // auto-update if so (exits the process on update). Skipped for inspection
134
+ // flags (`--version`, `--help`) so users debugging a broken install can
135
+ // still introspect what they have. Silent on any failure — never blocks
136
+ // the CLI on a flaky network or hq-pro hiccup. See `utils/version-gate.ts`.
137
+ if (!shouldSkipGate(process.argv)) {
138
+ await enforceVersionGate();
139
+ }
129
140
  await program.parseAsync();
130
141
  }
131
142
  catch (err) {
@@ -137,4 +148,4 @@ registerSignalsCommand(program);
137
148
  }
138
149
  })();
139
150
  //# sourceMappingURL=index.js.map
140
- //# debugId=a9b69c08-39a6-58c3-a4b6-a309d0ab8254
151
+ //# debugId=d7693093-4011-58fa-b5be-805b5f9f5421
@@ -45,7 +45,28 @@ export declare const DEFAULT_VAULT_API_URL: string;
45
45
  * value at registration time, which matches the user's actual cwd at process
46
46
  * start. Re-importable as a function for tests and command-time resolution.
47
47
  */
48
- export declare function resolveDefaultHqRoot(): string;
48
+ /**
49
+ * Resolve the HQ root directory.
50
+ *
51
+ * Resolution order:
52
+ * 1. $HQ_ROOT env var (treated as an explicit assertion by the caller)
53
+ * 2. Walk up from cwd looking for `core.yaml` AND `companies/` siblings
54
+ * 3. Fall back to `~/hq` (or throw, per `opts.onMissing`)
55
+ *
56
+ * `opts.onMissing` controls the third arm:
57
+ * - `'fallback'` (default) — return `~/hq` if no HQ root is found above cwd.
58
+ * This preserves the module-load contract of `DEFAULT_HQ_ROOT`, which
59
+ * several commander.js `.option()` callers pin at registration time.
60
+ * - `'throw'` — throw with a user-actionable error. Used by module-management
61
+ * commands (pkg-install, pkg-remove, pkg-list, pkg-update, team-sync) where
62
+ * a silent default-path miss would silently target the wrong directory.
63
+ *
64
+ * `$HQ_ROOT` short-circuits both arms — if the env var is set, it's used
65
+ * as-is regardless of `onMissing`.
66
+ */
67
+ export declare function resolveDefaultHqRoot(opts?: {
68
+ onMissing?: "throw" | "fallback";
69
+ }): string;
49
70
  export declare const DEFAULT_HQ_ROOT: string;
50
71
  /**
51
72
  * Return a non-expired Cognito access token, refreshing or browser-logging-in
@@ -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]="d16ef3c7-eead-5307-aaa7-ca5fc828102b")}catch(e){}}();
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]="cd9690e0-c7b0-5e26-a3ee-7f7bc98a0fe2")}catch(e){}}();
23
23
  import * as fs from "fs";
24
24
  import * as os from "os";
25
25
  import * as path from "path";
@@ -67,7 +67,26 @@ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hq
67
67
  * value at registration time, which matches the user's actual cwd at process
68
68
  * start. Re-importable as a function for tests and command-time resolution.
69
69
  */
70
- export function resolveDefaultHqRoot() {
70
+ /**
71
+ * Resolve the HQ root directory.
72
+ *
73
+ * Resolution order:
74
+ * 1. $HQ_ROOT env var (treated as an explicit assertion by the caller)
75
+ * 2. Walk up from cwd looking for `core.yaml` AND `companies/` siblings
76
+ * 3. Fall back to `~/hq` (or throw, per `opts.onMissing`)
77
+ *
78
+ * `opts.onMissing` controls the third arm:
79
+ * - `'fallback'` (default) — return `~/hq` if no HQ root is found above cwd.
80
+ * This preserves the module-load contract of `DEFAULT_HQ_ROOT`, which
81
+ * several commander.js `.option()` callers pin at registration time.
82
+ * - `'throw'` — throw with a user-actionable error. Used by module-management
83
+ * commands (pkg-install, pkg-remove, pkg-list, pkg-update, team-sync) where
84
+ * a silent default-path miss would silently target the wrong directory.
85
+ *
86
+ * `$HQ_ROOT` short-circuits both arms — if the env var is set, it's used
87
+ * as-is regardless of `onMissing`.
88
+ */
89
+ export function resolveDefaultHqRoot(opts = {}) {
71
90
  if (process.env.HQ_ROOT)
72
91
  return path.resolve(process.env.HQ_ROOT);
73
92
  let cur = path.resolve(process.cwd());
@@ -76,6 +95,10 @@ export function resolveDefaultHqRoot() {
76
95
  return cur;
77
96
  cur = path.dirname(cur);
78
97
  }
98
+ if (opts.onMissing === "throw") {
99
+ throw new Error("Could not find HQ root. Run this command from within your HQ directory " +
100
+ "(must contain core.yaml AND a companies/ subdirectory), or set $HQ_ROOT.");
101
+ }
79
102
  return path.join(os.homedir(), "hq");
80
103
  }
81
104
  /** True iff `dir` looks like an HQ root (has core.yaml + companies/ dir). */
@@ -170,4 +193,4 @@ export async function refreshCachedSession() {
170
193
  }
171
194
  }
172
195
  //# sourceMappingURL=cognito-session.js.map
173
- //# debugId=d16ef3c7-eead-5307-aaa7-ca5fc828102b
196
+ //# debugId=cd9690e0-c7b0-5e26-a3ee-7f7bc98a0fe2
@@ -2,11 +2,11 @@
2
2
  * Integrity verification — SHA256 hash and RSA signature checks (US-005)
3
3
  */
4
4
 
5
- !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]="19765c32-6c3d-5477-bf80-0f7d08197d29")}catch(e){}}();
5
+ !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]="820f12b3-4caf-5f08-9add-621628715744")}catch(e){}}();
6
6
  import * as crypto from 'crypto';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
- import { findHqRoot } from './hq-root.js';
9
+ import { resolveDefaultHqRoot } from './cognito-session.js';
10
10
  /**
11
11
  * Verify a file's SHA256 hash matches the expected value.
12
12
  */
@@ -29,7 +29,7 @@ export async function verifySha256(filePath, expectedHash) {
29
29
  */
30
30
  export function verifyRsaSignature(sha256Hash, signature, publicKeyPath) {
31
31
  const keyPath = publicKeyPath ??
32
- path.resolve(findHqRoot(), 'packages', '.keys', 'registry-public.pem');
32
+ path.resolve(resolveDefaultHqRoot({ onMissing: 'throw' }), 'packages', '.keys', 'registry-public.pem');
33
33
  if (!fs.existsSync(keyPath)) {
34
34
  return false;
35
35
  }
@@ -40,4 +40,4 @@ export function verifyRsaSignature(sha256Hash, signature, publicKeyPath) {
40
40
  return verifier.verify(publicKey, Buffer.from(signature, 'base64'));
41
41
  }
42
42
  //# sourceMappingURL=integrity.js.map
43
- //# debugId=19765c32-6c3d-5477-bf80-0f7d08197d29
43
+ //# debugId=820f12b3-4caf-5f08-9add-621628715744
@@ -5,11 +5,11 @@
5
5
  * Auth tokens are NEVER written to stdout or logs.
6
6
  */
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]="ffe2dc58-fb20-5566-a56c-1b1c26999bba")}catch(e){}}();
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]="43d1600f-1bf7-5a52-819b-83e71ba30c73")}catch(e){}}();
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as yaml from 'js-yaml';
12
- import { findHqRoot } from './hq-root.js';
12
+ import { resolveDefaultHqRoot } from './cognito-session.js';
13
13
  // ---------------------------------------------------------------------------
14
14
  // URL helper (unchanged from US-004)
15
15
  // ---------------------------------------------------------------------------
@@ -19,7 +19,7 @@ import { findHqRoot } from './hq-root.js';
19
19
  * Throws if sources.yaml is missing or has no sources.
20
20
  */
21
21
  export function getRegistryUrl() {
22
- const hqRoot = findHqRoot();
22
+ const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
23
23
  const sourcesPath = path.join(hqRoot, 'packages', 'sources.yaml');
24
24
  if (!fs.existsSync(sourcesPath)) {
25
25
  throw new Error(`No packages/sources.yaml found at ${sourcesPath}. Is your HQ packages directory set up?`);
@@ -102,4 +102,4 @@ export class RegistryClient {
102
102
  }
103
103
  }
104
104
  //# sourceMappingURL=registry-client.js.map
105
- //# debugId=ffe2dc58-fb20-5566-a56c-1b1c26999bba
105
+ //# debugId=43d1600f-1bf7-5a52-819b-83e71ba30c73
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Hard version-gate: ask hq-pro whether the current CLI is below the minimum
3
+ * acceptable version and, if so, run `npm install -g …@latest` synchronously
4
+ * before any commander parsing happens. Distinct from the existing
5
+ * `version-check.ts` which is a passive (cached, opt-in) stderr nag against
6
+ * the npm registry.
7
+ *
8
+ * Why both?
9
+ * - `version-check.ts` answers "is there something newer?" by polling npm
10
+ * directly. It's a soft hint, lives on a 24h cache, and never blocks.
11
+ * - `version-gate.ts` answers "is the team currently allowing your version
12
+ * to run?" via an authoritative hq-pro endpoint. The server can yank a
13
+ * known-bad release without waiting for the npm `latest` dist-tag move.
14
+ *
15
+ * The endpoint is reusable across clients (hq-sync, hq-installer, create-hq).
16
+ * See `apps/hq-pro/src/vault-service/handlers/client-version-check.ts` for the
17
+ * source-of-truth table.
18
+ *
19
+ * Trust model: anonymous. The CLI may be running pre-login (e.g. fresh
20
+ * install) so we never send credentials. The endpoint identifies the client
21
+ * by `clientId` + `currentVersion`.
22
+ *
23
+ * Failure mode: silent. Network down, hq-pro returning 5xx, malformed body —
24
+ * the gate must never break the CLI for a user who's otherwise fine. We log
25
+ * to Sentry as a breadcrumb (best-effort) and return.
26
+ *
27
+ * Opt-out: `HQ_NO_UPDATE_CHECK=1` (same env as `version-check.ts` — one knob
28
+ * to silence both check + gate).
29
+ */
30
+ /**
31
+ * Run the upgrade command in a blocking subprocess. Inherits stdio so the
32
+ * user sees the npm progress. We do NOT auto-rerun the CLI on completion —
33
+ * forcing a re-invocation would run twice on the same process and feel
34
+ * janky; instead we print a clear "rerun your command" message and exit.
35
+ */
36
+ declare function performUpdate(command: string): {
37
+ ok: boolean;
38
+ detail?: string;
39
+ };
40
+ /**
41
+ * Public entry point. Call before commander parses argv. Blocks the CLI on
42
+ * network IO for up to FETCH_TIMEOUT_MS — acceptable because the alternative
43
+ * (a fire-and-forget background check) gives the user no chance to bail out
44
+ * of a known-bad version before it does damage.
45
+ *
46
+ * `--version` / `-v` callers MUST skip the gate (the user is debugging a
47
+ * broken install and shouldn't be force-upgraded mid-investigation). Caller
48
+ * is responsible for checking argv before invoking us — see index.ts.
49
+ */
50
+ export declare function enforceVersionGate(): Promise<void>;
51
+ /**
52
+ * Cheap argv pre-check: skip the gate for `--version` / `-V` so users
53
+ * inspecting a broken install can still see what they have without being
54
+ * force-upgraded.
55
+ */
56
+ export declare function shouldSkipGate(argv: readonly string[]): boolean;
57
+ export declare const __test__: {
58
+ CLIENT_ID: string;
59
+ ENDPOINT_PATH: string;
60
+ FETCH_TIMEOUT_MS: number;
61
+ performUpdate: typeof performUpdate;
62
+ };
63
+ export {};
64
+ //# sourceMappingURL=version-gate.d.ts.map