@indigoai-us/hq-cli 5.345.48 → 5.345.50

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 (45) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -1
  3. package/dist/command-catalog.generated.d.ts +64 -11
  4. package/dist/command-catalog.generated.js +77 -11
  5. package/dist/commands/anywhere-runtime-flag.js +5 -3
  6. package/dist/commands/db-destroy.js +13 -11
  7. package/dist/commands/db-dump.js +3 -3
  8. package/dist/commands/db-remote-deps.d.ts +3 -0
  9. package/dist/commands/db-remote-deps.js +13 -0
  10. package/dist/commands/db-sql.js +4 -3
  11. package/dist/commands/db-usage.js +4 -3
  12. package/dist/commands/dm-forward-flag.d.ts +4 -0
  13. package/dist/commands/dm-forward-flag.js +7 -0
  14. package/dist/commands/dm.d.ts +8 -0
  15. package/dist/commands/dm.js +219 -27
  16. package/dist/commands/feedback.d.ts +10 -0
  17. package/dist/commands/feedback.js +45 -0
  18. package/dist/commands/lanes-message.js +2 -2
  19. package/dist/commands/lanes.js +2 -2
  20. package/dist/commands/mcp-serve.js +17 -4
  21. package/dist/lib/anywhere-person-setting.d.ts +1 -0
  22. package/dist/lib/anywhere-person-setting.js +12 -0
  23. package/dist/lib/anywhere-runtime-fast-flag.js +5 -3
  24. package/dist/lib/anywhere-runtime-flag-key.d.ts +3 -1
  25. package/dist/lib/anywhere-runtime-flag-key.js +6 -1
  26. package/dist/lib/daemon/flag.d.ts +1 -1
  27. package/dist/lib/daemon/flag.js +20 -9
  28. package/dist/lib/daemon/hq-cloud.d.ts +2 -2
  29. package/dist/lib/daemon/hq-cloud.js +38 -9
  30. package/dist/lib/daemon/run.js +45 -19
  31. package/dist/lib/daemon/server.d.ts +1 -1
  32. package/dist/lib/db/app-ref.d.ts +21 -0
  33. package/dist/lib/db/app-ref.js +45 -0
  34. package/dist/lib/lanes/keep-alive-audit.d.ts +5 -1
  35. package/dist/lib/lanes/keep-alive-audit.js +85 -3
  36. package/dist/lib/lanes/reconcile.js +6 -4
  37. package/dist/lib/lanes/registry-flag.d.ts +11 -1
  38. package/dist/lib/lanes/registry-flag.js +68 -29
  39. package/dist/lib/lanes/watcher.js +63 -2
  40. package/dist/utils/resident-package-preload.js +4 -1
  41. package/dist/utils/self-update.js +6 -5
  42. package/dist/utils/version-gate.d.ts +11 -4
  43. package/dist/utils/version-gate.js +132 -41
  44. package/docs/anywhere-runtime.md +5 -3
  45. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.50] — 2026-10-08
6
+ - The lane keep-alive audit now admits every `done` stop envelope and splits the unrestarted ones by keep-alive eligibility: a stop keep-alive is specified not to wake is reported as `finished_story` or `idle_no_queued_work` instead of being counted as a liveness failure. `stopped_and_not_restarted_on_their_own` keeps its name and its value on the stops the audit already reported.
7
+ - A finished standing lane that receives queued work long after it stopped now gets the normal grace period measured from when that work appeared, so a normal exit from `idle_no_queued_work` is no longer reported as a keep-alive liveness failure.
8
+ - `hq db usage`, `hq db sql`, `hq db dump` and `hq db destroy` accept the app name or subdomain in `--app`, not only the app id. Names are looked up in the given company only, and a name shared by two apps is refused with both ids.
9
+ - Pinned hq-cloud installs retry transient registry failures with bounded backoff. A cold start will not start a staged or bundled copy that misses the pin. If sync is already running on another version, it stays up while the updater retries the pinned install, and the daemon logs both versions. Updater logs also report the hq-cloud package version installed before the update.
10
+ - Add `hq dm forward` for forwarding existing DM and channel messages with optional notes and file-access choices. DM and channel transcripts identify forwarded messages, and channel posts accept `--details` and `--prompt` text or files behind the `dm.forward-cli` kill switch.
11
+
12
+ ## [5.345.49] — 2026-10-08
13
+ - Persist terminal lane envelopes and story metadata when a final report arrives after the lane has already stopped.
14
+
15
+ - Keep changelog guard coverage valid after release fragments move into CHANGELOG.md.
16
+ - The CLI now updates the pnpm-managed `hq` install that PATH resolves first, so `hq update` converges when another pnpm home shadows the running process.
17
+ - Preserve an explicit unknown state when the lanes flag registry cannot be read. The four message-delivery and session-wake gates now fail open on unknown; all other lanes gates retain their fail-closed behavior.
18
+ - `hq feedback bug` can include a fail-off, hq-flags-gated agent or human reporter mark derived from runtime context.
19
+ - HQ Anywhere remains enabled when its flag registry is unavailable, while the local kill switch still turns it off.
20
+ - Windows native-resolve cleanup accepts taskkill exit 128 only after confirming the target process has exited.
21
+
5
22
  ## [5.345.48] — 2026-10-08
6
23
  - Add default-off multi-company session locks with ordered `hq-session add/remove company` updates and scope-capability enforcement.
7
24
  - Give a sync runner that reports stopping 60 seconds to persist pending pushes before SIGKILL. The daemon shutdown deadline includes that grace; other services remain capped at 15 seconds.
package/README.md CHANGED
@@ -9,7 +9,8 @@ hq --help
9
9
 
10
10
  The global HQ runtime installs hooks for supported agent runtimes. When the
11
11
  `hq-anywhere-runtime` flag is enabled, the hooks use hqd and a per-user folder
12
- registry. The flag is off by default. See [HQ Anywhere runtime](docs/anywhere-runtime.md)
12
+ registry. The flag defaults on when its registry row cannot be read, and an
13
+ explicit false turns it off. See [HQ Anywhere runtime](docs/anywhere-runtime.md)
13
14
  for install and link commands.
14
15
 
15
16
  ## Live meeting transcripts
@@ -1932,7 +1932,7 @@ export declare const COMMAND_CATALOG: readonly [{
1932
1932
  readonly hidden: false;
1933
1933
  }, {
1934
1934
  readonly flags: "--app <app>";
1935
- readonly description: "Run against this app's remote database schema";
1935
+ readonly description: "Run against this app's remote database (app id, name or subdomain)";
1936
1936
  readonly hidden: false;
1937
1937
  }, {
1938
1938
  readonly flags: "--write";
@@ -2029,8 +2029,8 @@ export declare const COMMAND_CATALOG: readonly [{
2029
2029
  readonly description: "Company slug";
2030
2030
  readonly hidden: false;
2031
2031
  }, {
2032
- readonly flags: "--app <name>";
2033
- readonly description: "Show one app: included amounts, use, overage, ceiling and estimated charge";
2032
+ readonly flags: "--app <app>";
2033
+ readonly description: "App id, name or subdomain. Shows included amounts, use, overage, ceiling and estimated charge";
2034
2034
  readonly hidden: false;
2035
2035
  }];
2036
2036
  readonly subcommands: readonly [];
@@ -2050,7 +2050,7 @@ export declare const COMMAND_CATALOG: readonly [{
2050
2050
  readonly hidden: false;
2051
2051
  }, {
2052
2052
  readonly flags: "--app <app>";
2053
- readonly description: "App id";
2053
+ readonly description: "App id, name or subdomain";
2054
2054
  readonly hidden: false;
2055
2055
  }, {
2056
2056
  readonly flags: "--out <file>";
@@ -2077,15 +2077,15 @@ export declare const COMMAND_CATALOG: readonly [{
2077
2077
  readonly hidden: false;
2078
2078
  }, {
2079
2079
  readonly flags: "--app <app>";
2080
- readonly description: "App id";
2080
+ readonly description: "App id, name or subdomain";
2081
2081
  readonly hidden: false;
2082
2082
  }, {
2083
2083
  readonly flags: "--confirm <app>";
2084
- readonly description: "Type the app id again to confirm (required when not interactive)";
2084
+ readonly description: "Type the same --app value again to confirm (required when not interactive)";
2085
2085
  readonly hidden: false;
2086
2086
  }, {
2087
2087
  readonly flags: "--yes";
2088
- readonly description: "Skip the dump question; the app id confirmation is still required";
2088
+ readonly description: "Skip the dump question; the confirmation is still required";
2089
2089
  readonly hidden: false;
2090
2090
  }, {
2091
2091
  readonly flags: "--dump-first";
@@ -3309,19 +3309,19 @@ export declare const COMMAND_CATALOG: readonly [{
3309
3309
  readonly hidden: false;
3310
3310
  }, {
3311
3311
  readonly flags: "--prompt <text>";
3312
- readonly description: "Agent-context prompt the recipient can one-click copy into their agent (1:1 DMs only)";
3312
+ readonly description: "Agent-context prompt the recipient can one-click copy into their agent (1:1 DMs; channel sends also supported)";
3313
3313
  readonly hidden: false;
3314
3314
  }, {
3315
3315
  readonly flags: "--prompt-file <path>";
3316
- readonly description: "Read the agent prompt from a file (1:1 DMs only)";
3316
+ readonly description: "Read the agent prompt from a file (1:1 DMs; channel sends also supported)";
3317
3317
  readonly hidden: false;
3318
3318
  }, {
3319
3319
  readonly flags: "--details <text>";
3320
- readonly description: "Longer detail shown in the recipient's DM detail window (1:1 DMs only)";
3320
+ readonly description: "Longer detail shown in the recipient's DM detail window (1:1 DMs; channel sends also supported)";
3321
3321
  readonly hidden: false;
3322
3322
  }, {
3323
3323
  readonly flags: "--details-file <path>";
3324
- readonly description: "Read the details from a file (1:1 DMs only)";
3324
+ readonly description: "Read the details from a file (1:1 DMs; channel sends also supported)";
3325
3325
  readonly hidden: false;
3326
3326
  }, {
3327
3327
  readonly flags: "--at <iso>";
@@ -3337,6 +3337,51 @@ export declare const COMMAND_CATALOG: readonly [{
3337
3337
  readonly hidden: false;
3338
3338
  }];
3339
3339
  readonly subcommands: readonly [];
3340
+ }, {
3341
+ readonly name: "forward";
3342
+ readonly description: "Forward a DM or channel message through the existing send routes.";
3343
+ readonly aliases: readonly [];
3344
+ readonly hidden: false;
3345
+ readonly helpOption: true;
3346
+ readonly helpCommand: false;
3347
+ readonly usage: "[options] <eventId>";
3348
+ readonly helpText: readonly [];
3349
+ readonly arguments: readonly [{
3350
+ readonly name: "eventId";
3351
+ readonly required: true;
3352
+ readonly variadic: false;
3353
+ readonly description: "";
3354
+ }];
3355
+ readonly options: readonly [{
3356
+ readonly flags: "--from <person|channel>";
3357
+ readonly description: "Source person email/UID or channel name/ID";
3358
+ readonly hidden: false;
3359
+ }, {
3360
+ readonly flags: "--to <person|agent|channel>";
3361
+ readonly description: "Destination person email/UID, agent UID, or channel name/ID";
3362
+ readonly hidden: false;
3363
+ }, {
3364
+ readonly flags: "--note <text>";
3365
+ readonly description: "Optional note to add above the forwarded message";
3366
+ readonly hidden: false;
3367
+ }, {
3368
+ readonly flags: "--note-file <path>";
3369
+ readonly description: "Read the optional forward note from a file";
3370
+ readonly hidden: false;
3371
+ }, {
3372
+ readonly flags: "--grant-files";
3373
+ readonly description: "Grant the recipient access to files they cannot currently open";
3374
+ readonly hidden: false;
3375
+ }, {
3376
+ readonly flags: "--omit-files";
3377
+ readonly description: "Omit files the recipient cannot currently open";
3378
+ readonly hidden: false;
3379
+ }, {
3380
+ readonly flags: "--cross-company";
3381
+ readonly description: "Confirm forwarding across company boundaries";
3382
+ readonly hidden: false;
3383
+ }];
3384
+ readonly subcommands: readonly [];
3340
3385
  }, {
3341
3386
  readonly name: "inbox";
3342
3387
  readonly description: "List your recent incoming direct messages.";
@@ -3673,6 +3718,10 @@ export declare const COMMAND_CATALOG: readonly [{
3673
3718
  readonly flags: "--company <slug>";
3674
3719
  readonly description: "Company slug to associate with the report";
3675
3720
  readonly hidden: false;
3721
+ }, {
3722
+ readonly flags: "--reporter <kind>";
3723
+ readonly description: "Mark this bug report as agent-filed (only 'agent' is accepted)";
3724
+ readonly hidden: false;
3676
3725
  }, {
3677
3726
  readonly flags: "--screenshot <path>";
3678
3727
  readonly description: "Attach a screenshot (repeatable, up to 5; .png/.jpg/.jpeg/.webp/.gif)";
@@ -3706,6 +3755,10 @@ export declare const COMMAND_CATALOG: readonly [{
3706
3755
  readonly flags: "--company <slug>";
3707
3756
  readonly description: "Company slug to associate with the report";
3708
3757
  readonly hidden: false;
3758
+ }, {
3759
+ readonly flags: "--reporter <kind>";
3760
+ readonly description: "Mark this bug report as agent-filed (only 'agent' is accepted)";
3761
+ readonly hidden: false;
3709
3762
  }, {
3710
3763
  readonly flags: "--screenshot <path>";
3711
3764
  readonly description: "Attach a screenshot (repeatable, up to 5; .png/.jpg/.jpeg/.webp/.gif)";
@@ -2317,7 +2317,7 @@ export const COMMAND_CATALOG = [
2317
2317
  },
2318
2318
  {
2319
2319
  "flags": "--app <app>",
2320
- "description": "Run against this app's remote database schema",
2320
+ "description": "Run against this app's remote database (app id, name or subdomain)",
2321
2321
  "hidden": false
2322
2322
  },
2323
2323
  {
@@ -2434,8 +2434,8 @@ export const COMMAND_CATALOG = [
2434
2434
  "hidden": false
2435
2435
  },
2436
2436
  {
2437
- "flags": "--app <name>",
2438
- "description": "Show one app: included amounts, use, overage, ceiling and estimated charge",
2437
+ "flags": "--app <app>",
2438
+ "description": "App id, name or subdomain. Shows included amounts, use, overage, ceiling and estimated charge",
2439
2439
  "hidden": false
2440
2440
  }
2441
2441
  ],
@@ -2459,7 +2459,7 @@ export const COMMAND_CATALOG = [
2459
2459
  },
2460
2460
  {
2461
2461
  "flags": "--app <app>",
2462
- "description": "App id",
2462
+ "description": "App id, name or subdomain",
2463
2463
  "hidden": false
2464
2464
  },
2465
2465
  {
@@ -2493,17 +2493,17 @@ export const COMMAND_CATALOG = [
2493
2493
  },
2494
2494
  {
2495
2495
  "flags": "--app <app>",
2496
- "description": "App id",
2496
+ "description": "App id, name or subdomain",
2497
2497
  "hidden": false
2498
2498
  },
2499
2499
  {
2500
2500
  "flags": "--confirm <app>",
2501
- "description": "Type the app id again to confirm (required when not interactive)",
2501
+ "description": "Type the same --app value again to confirm (required when not interactive)",
2502
2502
  "hidden": false
2503
2503
  },
2504
2504
  {
2505
2505
  "flags": "--yes",
2506
- "description": "Skip the dump question; the app id confirmation is still required",
2506
+ "description": "Skip the dump question; the confirmation is still required",
2507
2507
  "hidden": false
2508
2508
  },
2509
2509
  {
@@ -4001,22 +4001,22 @@ export const COMMAND_CATALOG = [
4001
4001
  },
4002
4002
  {
4003
4003
  "flags": "--prompt <text>",
4004
- "description": "Agent-context prompt the recipient can one-click copy into their agent (1:1 DMs only)",
4004
+ "description": "Agent-context prompt the recipient can one-click copy into their agent (1:1 DMs; channel sends also supported)",
4005
4005
  "hidden": false
4006
4006
  },
4007
4007
  {
4008
4008
  "flags": "--prompt-file <path>",
4009
- "description": "Read the agent prompt from a file (1:1 DMs only)",
4009
+ "description": "Read the agent prompt from a file (1:1 DMs; channel sends also supported)",
4010
4010
  "hidden": false
4011
4011
  },
4012
4012
  {
4013
4013
  "flags": "--details <text>",
4014
- "description": "Longer detail shown in the recipient's DM detail window (1:1 DMs only)",
4014
+ "description": "Longer detail shown in the recipient's DM detail window (1:1 DMs; channel sends also supported)",
4015
4015
  "hidden": false
4016
4016
  },
4017
4017
  {
4018
4018
  "flags": "--details-file <path>",
4019
- "description": "Read the details from a file (1:1 DMs only)",
4019
+ "description": "Read the details from a file (1:1 DMs; channel sends also supported)",
4020
4020
  "hidden": false
4021
4021
  },
4022
4022
  {
@@ -4037,6 +4037,62 @@ export const COMMAND_CATALOG = [
4037
4037
  ],
4038
4038
  "subcommands": []
4039
4039
  },
4040
+ {
4041
+ "name": "forward",
4042
+ "description": "Forward a DM or channel message through the existing send routes.",
4043
+ "aliases": [],
4044
+ "hidden": false,
4045
+ "helpOption": true,
4046
+ "helpCommand": false,
4047
+ "usage": "[options] <eventId>",
4048
+ "helpText": [],
4049
+ "arguments": [
4050
+ {
4051
+ "name": "eventId",
4052
+ "required": true,
4053
+ "variadic": false,
4054
+ "description": ""
4055
+ }
4056
+ ],
4057
+ "options": [
4058
+ {
4059
+ "flags": "--from <person|channel>",
4060
+ "description": "Source person email/UID or channel name/ID",
4061
+ "hidden": false
4062
+ },
4063
+ {
4064
+ "flags": "--to <person|agent|channel>",
4065
+ "description": "Destination person email/UID, agent UID, or channel name/ID",
4066
+ "hidden": false
4067
+ },
4068
+ {
4069
+ "flags": "--note <text>",
4070
+ "description": "Optional note to add above the forwarded message",
4071
+ "hidden": false
4072
+ },
4073
+ {
4074
+ "flags": "--note-file <path>",
4075
+ "description": "Read the optional forward note from a file",
4076
+ "hidden": false
4077
+ },
4078
+ {
4079
+ "flags": "--grant-files",
4080
+ "description": "Grant the recipient access to files they cannot currently open",
4081
+ "hidden": false
4082
+ },
4083
+ {
4084
+ "flags": "--omit-files",
4085
+ "description": "Omit files the recipient cannot currently open",
4086
+ "hidden": false
4087
+ },
4088
+ {
4089
+ "flags": "--cross-company",
4090
+ "description": "Confirm forwarding across company boundaries",
4091
+ "hidden": false
4092
+ }
4093
+ ],
4094
+ "subcommands": []
4095
+ },
4040
4096
  {
4041
4097
  "name": "inbox",
4042
4098
  "description": "List your recent incoming direct messages.",
@@ -4442,6 +4498,11 @@ export const COMMAND_CATALOG = [
4442
4498
  "description": "Company slug to associate with the report",
4443
4499
  "hidden": false
4444
4500
  },
4501
+ {
4502
+ "flags": "--reporter <kind>",
4503
+ "description": "Mark this bug report as agent-filed (only 'agent' is accepted)",
4504
+ "hidden": false
4505
+ },
4445
4506
  {
4446
4507
  "flags": "--screenshot <path>",
4447
4508
  "description": "Attach a screenshot (repeatable, up to 5; .png/.jpg/.jpeg/.webp/.gif)",
@@ -4482,6 +4543,11 @@ export const COMMAND_CATALOG = [
4482
4543
  "description": "Company slug to associate with the report",
4483
4544
  "hidden": false
4484
4545
  },
4546
+ {
4547
+ "flags": "--reporter <kind>",
4548
+ "description": "Mark this bug report as agent-filed (only 'agent' is accepted)",
4549
+ "hidden": false
4550
+ },
4485
4551
  {
4486
4552
  "flags": "--screenshot <path>",
4487
4553
  "description": "Attach a screenshot (repeatable, up to 5; .png/.jpg/.jpeg/.webp/.gif)",
@@ -1,5 +1,5 @@
1
1
  import { resolveFlagGate, resolveProcessFlagGate } from "../lib/flag-registry.js";
2
- import { HQ_ANYWHERE_RUNTIME_FLAG } from "../lib/anywhere-runtime-flag-key.js";
2
+ import { HQ_ANYWHERE_RUNTIME_FLAG, hqAnywhereRuntimeLocallyDisabled } from "../lib/anywhere-runtime-flag-key.js";
3
3
  export { HQ_ANYWHERE_RUNTIME_FLAG };
4
4
  export class AnywhereRuntimeDisabledError extends Error {
5
5
  expected = true;
@@ -9,8 +9,10 @@ export class AnywhereRuntimeDisabledError extends Error {
9
9
  }
10
10
  }
11
11
  export function anywhereRuntimeEnabled(env = process.env) {
12
- const fallback = () => false;
13
- const lookup = { fallback: false };
12
+ if (hqAnywhereRuntimeLocallyDisabled(env))
13
+ return false;
14
+ const fallback = () => true;
15
+ const lookup = { fallback: true };
14
16
  return env === process.env
15
17
  ? resolveProcessFlagGate(HQ_ANYWHERE_RUNTIME_FLAG, lookup, fallback)
16
18
  : resolveFlagGate(undefined, HQ_ANYWHERE_RUNTIME_FLAG, lookup, fallback, env);
@@ -6,7 +6,7 @@ import * as readline from "node:readline";
6
6
  import { safeErrorMessage } from "../lib/db/redact.js";
7
7
  import { isPlanGateError } from "../utils/plan-gate-error.js";
8
8
  import { dumpAppDatabase, describeSessionGap } from "./db-dump.js";
9
- import { controlPlaneClient, defaultRemoteDeps, describeRemoteError, } from "./db-remote-deps.js";
9
+ import { controlPlaneClient, defaultRemoteDeps, describeRemoteError, resolveAppId, } from "./db-remote-deps.js";
10
10
  const NOT_ADMIN = "Only company owners and admins can destroy an app database.";
11
11
  async function defaultCheckRole(deps, companyUid) {
12
12
  if (process.env.HQ_DB_MOCK_CONTROL_PLANE === "1")
@@ -60,9 +60,9 @@ export function registerDbDestroyCommand(db, depsFactory = defaultRemoteDeps, io
60
60
  db.command("destroy")
61
61
  .description("Permanently delete an app's remote database (owners and admins only; cannot be undone)")
62
62
  .requiredOption("--company <slug>", "Company slug")
63
- .requiredOption("--app <app>", "App id")
64
- .option("--confirm <app>", "Type the app id again to confirm (required when not interactive)")
65
- .option("--yes", "Skip the dump question; the app id confirmation is still required")
63
+ .requiredOption("--app <app>", "App id, name or subdomain")
64
+ .option("--confirm <app>", "Type the same --app value again to confirm (required when not interactive)")
65
+ .option("--yes", "Skip the dump question; the confirmation is still required")
66
66
  .option("--dump-first", "Run hq db dump before destroying (non-interactive)")
67
67
  .option("--no-dump", "Do not dump before destroying (non-interactive)")
68
68
  .option("--out <file>", "Dump output file (default: <app>.sql)")
@@ -86,17 +86,19 @@ export function registerDbDestroyCommand(db, depsFactory = defaultRemoteDeps, io
86
86
  fail(NOT_ADMIN);
87
87
  return;
88
88
  }
89
- console.error(chalk.yellow(`Warning: this deletes the database for app ${app} in ${resolved.companySlug} immediately. It cannot be undone.`));
89
+ const appId = await resolveAppId(deps, resolved.companySlug, app);
90
+ const label = appId === app ? app : `${app} (${appId})`;
91
+ console.error(chalk.yellow(`Warning: this deletes the database for app ${label} in ${resolved.companySlug} immediately. It cannot be undone.`));
90
92
  let confirmed = opts.confirm;
91
93
  if (confirmed === undefined) {
92
94
  if (!interactive) {
93
95
  fail(`Confirmation required. Re-run with --confirm ${app}.`);
94
96
  return;
95
97
  }
96
- confirmed = await io.ask(`Type the app id (${app}) to confirm: `);
98
+ confirmed = await io.ask(`Type the app (${app}) to confirm: `);
97
99
  }
98
- if (confirmed.trim() !== app) {
99
- fail("The confirmation did not match the app id. Nothing was deleted.");
100
+ if (confirmed.trim() !== app && confirmed.trim() !== appId) {
101
+ fail("The confirmation did not match the app. Nothing was deleted.");
100
102
  return;
101
103
  }
102
104
  let dumpFirst = opts.dumpFirst === true;
@@ -115,7 +117,7 @@ export function registerDbDestroyCommand(db, depsFactory = defaultRemoteDeps, io
115
117
  try {
116
118
  const summary = await dumpAppDatabase(deps, {
117
119
  companyUid: resolved.companyUid,
118
- appId: app,
120
+ appId,
119
121
  out: opts.out,
120
122
  });
121
123
  console.log(`Wrote ${summary.tables} table(s) and ${summary.rows} row(s) to ${summary.outPath}.`);
@@ -129,10 +131,10 @@ export function registerDbDestroyCommand(db, depsFactory = defaultRemoteDeps, io
129
131
  }
130
132
  const result = await controlPlaneClient(deps).destroyApp({
131
133
  companyUid: resolved.companyUid,
132
- appId: app,
134
+ appId,
133
135
  });
134
136
  const what = result.mode === "schema" ? "schema and roles dropped" : "database cluster deleted";
135
- console.log(`Destroyed the database for app ${app}: ${what}.`);
137
+ console.log(`Destroyed the database for app ${label}: ${what}.`);
136
138
  }
137
139
  catch (error) {
138
140
  if (isPlanGateError(error))
@@ -6,7 +6,7 @@ import fs from "node:fs";
6
6
  import { safeErrorMessage } from "../lib/db/redact.js";
7
7
  import { dumpRemoteSchema, openRemoteAppConnection } from "../lib/db/remote-engine.js";
8
8
  import { isPlanGateError } from "../utils/plan-gate-error.js";
9
- import { defaultRemoteDeps, describeRemoteError, remoteEngineDeps, } from "./db-remote-deps.js";
9
+ import { defaultRemoteDeps, describeRemoteError, remoteEngineDeps, resolveAppId, } from "./db-remote-deps.js";
10
10
  export function describeSessionGap(error) {
11
11
  const status = error.status;
12
12
  if (status === 404 || status === 405) {
@@ -34,7 +34,7 @@ export function registerDbDumpCommand(db, depsFactory = defaultRemoteDeps) {
34
34
  db.command("dump")
35
35
  .description("Export an app's remote database schema and data to a SQL file")
36
36
  .requiredOption("--company <slug>", "Company slug")
37
- .requiredOption("--app <app>", "App id")
37
+ .requiredOption("--app <app>", "App id, name or subdomain")
38
38
  .option("--out <file>", "Output file (default: <app>.sql)")
39
39
  .action(async (opts) => {
40
40
  try {
@@ -42,7 +42,7 @@ export function registerDbDumpCommand(db, depsFactory = defaultRemoteDeps) {
42
42
  const resolved = await deps.resolveCompany(String(opts.company ?? "").trim());
43
43
  const summary = await dumpAppDatabase(deps, {
44
44
  companyUid: resolved.companyUid,
45
- appId: opts.app,
45
+ appId: await resolveAppId(deps, resolved.companySlug, String(opts.app ?? "")),
46
46
  out: opts.out,
47
47
  });
48
48
  console.log(`Wrote ${summary.tables} table(s) and ${summary.rows} row(s) to ${summary.outPath}.`);
@@ -6,11 +6,14 @@ export interface DbRemoteCommandDeps {
6
6
  companySlug: string;
7
7
  }>;
8
8
  getAccessToken: () => Promise<string>;
9
+ /** Turn an app id, name or subdomain into the app id, within one company. */
10
+ resolveApp?: (companySlug: string, ref: string) => Promise<string>;
9
11
  apiBaseUrl: string;
10
12
  fetchImpl?: typeof fetch;
11
13
  engine?: Partial<Pick<RemoteEngineDeps, "tokenFactory" | "clientFactory" | "provider">>;
12
14
  }
13
15
  export declare function defaultRemoteDeps(): DbRemoteCommandDeps;
16
+ export declare function resolveAppId(deps: DbRemoteCommandDeps, companySlug: string, ref: string): Promise<string>;
14
17
  export declare function controlPlaneClient(deps: DbRemoteCommandDeps): ControlPlaneDbClient;
15
18
  export declare function remoteEngineDeps(deps: DbRemoteCommandDeps): RemoteEngineDeps;
16
19
  /** Plain-language message for a failed remote call, or null when the error is not recognized. */
@@ -2,6 +2,7 @@ import { ControlPlaneDbClient } from "../lib/db/control-plane.js";
2
2
  import { controlPlaneCredentialProvider, } from "../lib/db/remote-engine.js";
3
3
  import { DEFAULT_VAULT_API_URL, ensureCognitoToken, } from "../utils/cognito-session.js";
4
4
  import { getCompanyUid } from "../utils/vault-api.js";
5
+ import { AppRefError, resolveAppRef } from "../lib/db/app-ref.js";
5
6
  export function defaultRemoteDeps() {
6
7
  return {
7
8
  async resolveCompany(slug) {
@@ -17,12 +18,22 @@ export function defaultRemoteDeps() {
17
18
  return "mock-token";
18
19
  return ensureCognitoToken({ interactive: false });
19
20
  },
21
+ async resolveApp(companySlug, ref) {
22
+ if (process.env.HQ_DB_MOCK_CONTROL_PLANE === "1")
23
+ return ref;
24
+ const token = await ensureCognitoToken({ interactive: false });
25
+ return resolveAppRef({ token, companySlug, ref });
26
+ },
20
27
  apiBaseUrl: process.env.HQ_VAULT_API_URL ||
21
28
  process.env.HQ_API_BASE_URL ||
22
29
  process.env.HQ_CLOUD_API_URL ||
23
30
  DEFAULT_VAULT_API_URL,
24
31
  };
25
32
  }
33
+ export async function resolveAppId(deps, companySlug, ref) {
34
+ const trimmed = ref.trim();
35
+ return deps.resolveApp ? deps.resolveApp(companySlug, trimmed) : trimmed;
36
+ }
26
37
  export function controlPlaneClient(deps) {
27
38
  return new ControlPlaneDbClient({
28
39
  baseUrl: deps.apiBaseUrl,
@@ -39,6 +50,8 @@ export function remoteEngineDeps(deps) {
39
50
  }
40
51
  /** Plain-language message for a failed remote call, or null when the error is not recognized. */
41
52
  export function describeRemoteError(error) {
53
+ if (error instanceof AppRefError)
54
+ return error.message;
42
55
  const status = error.status;
43
56
  const code = error.code;
44
57
  if (code === "REMOTE_DB_NOT_AVAILABLE" || status === 501) {
@@ -11,7 +11,7 @@ import { safeErrorMessage } from "../lib/db/redact.js";
11
11
  import { formatRemoteResults, openRemoteAppConnection, runRemoteSql, } from "../lib/db/remote-engine.js";
12
12
  import { formatSqlResult, runLocalSql } from "../lib/db/sql.js";
13
13
  import { isPlanGateError } from "../utils/plan-gate-error.js";
14
- import { defaultRemoteDeps, describeRemoteError, remoteEngineDeps, } from "./db-remote-deps.js";
14
+ import { defaultRemoteDeps, describeRemoteError, remoteEngineDeps, resolveAppId, } from "./db-remote-deps.js";
15
15
  import { describeSessionGap } from "./db-dump.js";
16
16
  function readSqlArg(sqlArgs, stdinFlag) {
17
17
  if (stdinFlag || sqlArgs.length === 0) {
@@ -26,7 +26,7 @@ export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps)
26
26
  db.command("sql")
27
27
  .description("Run SQL against the company vault DB (local) or an app's remote database (--app); read-only by default")
28
28
  .requiredOption("--company <slug>", "Company slug (tenant scope; required)")
29
- .option("--app <app>", "Run against this app's remote database schema")
29
+ .option("--app <app>", "Run against this app's remote database (app id, name or subdomain)")
30
30
  .option("--write", "Allow write/DDL statements (discouraged for ad-hoc DDL)", false)
31
31
  .addOption(new Option("--remote", "Remote SQL is not available in this CLI build yet; local queries work without --remote").hideHelp())
32
32
  .option("--db-path <path>", "Dangerous: open a non-canonical DB path (denied unless --allow-cross-company-path)")
@@ -75,9 +75,10 @@ export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps)
75
75
  }
76
76
  async function runRemote(deps, company, app, sql, write) {
77
77
  const resolved = await deps.resolveCompany(company);
78
+ const appId = await resolveAppId(deps, resolved.companySlug, app);
78
79
  const conn = await openRemoteAppConnection(remoteEngineDeps(deps), {
79
80
  companyUid: resolved.companyUid,
80
- appId: app,
81
+ appId,
81
82
  });
82
83
  try {
83
84
  console.log(formatRemoteResults(await runRemoteSql(conn, sql, write)));
@@ -5,12 +5,12 @@ import chalk from "chalk";
5
5
  import { safeErrorMessage } from "../lib/db/redact.js";
6
6
  import { formatAppUsageResponse, formatUsage } from "../lib/db/usage-format.js";
7
7
  import { isPlanGateError } from "../utils/plan-gate-error.js";
8
- import { controlPlaneClient, defaultRemoteDeps, describeRemoteError, } from "./db-remote-deps.js";
8
+ import { controlPlaneClient, defaultRemoteDeps, describeRemoteError, resolveAppId, } from "./db-remote-deps.js";
9
9
  export function registerDbUsageCommand(db, depsFactory = defaultRemoteDeps) {
10
10
  db.command("usage")
11
11
  .description("Show this month's remote database usage. With --app: included, used, overage, ceiling and the estimated charge for one app")
12
12
  .requiredOption("--company <slug>", "Company slug")
13
- .option("--app <name>", "Show one app: included amounts, use, overage, ceiling and estimated charge")
13
+ .option("--app <app>", "App id, name or subdomain. Shows included amounts, use, overage, ceiling and estimated charge")
14
14
  .action(async (opts) => {
15
15
  try {
16
16
  const deps = depsFactory();
@@ -18,7 +18,8 @@ export function registerDbUsageCommand(db, depsFactory = defaultRemoteDeps) {
18
18
  const client = controlPlaneClient(deps);
19
19
  const app = String(opts.app ?? "").trim();
20
20
  if (app) {
21
- console.log(formatAppUsageResponse(await client.appUsage(resolved.companyUid, app)));
21
+ const appId = await resolveAppId(deps, resolved.companySlug, app);
22
+ console.log(formatAppUsageResponse(await client.appUsage(resolved.companyUid, appId)));
22
23
  }
23
24
  else {
24
25
  console.log(formatUsage(await client.usage(resolved.companyUid)));
@@ -0,0 +1,4 @@
1
+ export declare const DM_FORWARD_CLI_FLAG = "dm.forward-cli";
2
+ /** Forwarding and channel details stay available unless the kill switch is off. */
3
+ export declare function dmForwardCliEnabled(): boolean;
4
+ //# sourceMappingURL=dm-forward-flag.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { resolveProcessFlagGate } from "../lib/flag-registry.js";
2
+ export const DM_FORWARD_CLI_FLAG = "dm.forward-cli";
3
+ /** Forwarding and channel details stay available unless the kill switch is off. */
4
+ export function dmForwardCliEnabled() {
5
+ return resolveProcessFlagGate(DM_FORWARD_CLI_FLAG, { fallback: true }, () => true);
6
+ }
7
+ //# sourceMappingURL=dm-forward-flag.js.map
@@ -226,6 +226,13 @@ export interface DmThreadMessage {
226
226
  audience?: DmAudience;
227
227
  attachments?: DmAttachment[];
228
228
  attachment?: DmAttachment;
229
+ forwardedFrom?: ForwardedFrom;
230
+ }
231
+ export interface ForwardedFrom {
232
+ senderUid?: string;
233
+ senderName?: string;
234
+ sourceKind?: "dm" | "group" | "channel";
235
+ originalCreatedAt?: string;
229
236
  }
230
237
  /** One channel/group message from GET /v1/notify/channels/{id}/messages. */
231
238
  export interface ChannelMessageItem {
@@ -240,6 +247,7 @@ export interface ChannelMessageItem {
240
247
  rootEventId?: string;
241
248
  attachments?: DmAttachment[];
242
249
  attachment?: DmAttachment;
250
+ forwardedFrom?: ForwardedFrom;
243
251
  }
244
252
  /**
245
253
  * One file attached to a DM (US-007 `attachments[]` on the read endpoints). The