@indigoai-us/hq-cli 5.345.49 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/command-catalog.generated.d.ts +56 -11
- package/dist/command-catalog.generated.js +67 -11
- package/dist/commands/db-destroy.js +13 -11
- package/dist/commands/db-dump.js +3 -3
- package/dist/commands/db-remote-deps.d.ts +3 -0
- package/dist/commands/db-remote-deps.js +13 -0
- package/dist/commands/db-sql.js +4 -3
- package/dist/commands/db-usage.js +4 -3
- package/dist/commands/dm-forward-flag.d.ts +4 -0
- package/dist/commands/dm-forward-flag.js +7 -0
- package/dist/commands/dm.d.ts +8 -0
- package/dist/commands/dm.js +219 -27
- package/dist/lib/daemon/hq-cloud.d.ts +2 -2
- package/dist/lib/daemon/hq-cloud.js +38 -9
- package/dist/lib/daemon/run.js +45 -19
- package/dist/lib/db/app-ref.d.ts +21 -0
- package/dist/lib/db/app-ref.js +45 -0
- package/dist/lib/lanes/keep-alive-audit.d.ts +5 -1
- package/dist/lib/lanes/keep-alive-audit.js +85 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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
|
+
|
|
5
12
|
## [5.345.49] — 2026-10-08
|
|
6
13
|
- Persist terminal lane envelopes and story metadata when a final report arrives after the lane has already stopped.
|
|
7
14
|
|
|
@@ -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
|
|
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 <
|
|
2033
|
-
readonly description: "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.";
|
|
@@ -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
|
|
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 <
|
|
2438
|
-
"description": "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.",
|
|
@@ -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
|
|
65
|
-
.option("--yes", "Skip the dump question; the
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ${
|
|
137
|
+
console.log(`Destroyed the database for app ${label}: ${what}.`);
|
|
136
138
|
}
|
|
137
139
|
catch (error) {
|
|
138
140
|
if (isPlanGateError(error))
|
package/dist/commands/db-dump.js
CHANGED
|
@@ -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) {
|
package/dist/commands/db-sql.js
CHANGED
|
@@ -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
|
|
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
|
|
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 <
|
|
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
|
-
|
|
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,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
|
package/dist/commands/dm.d.ts
CHANGED
|
@@ -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
|