@indigoai-us/hq-cli 5.345.110 → 5.345.112

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 CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.112] — 2026-10-13
6
+ - The bundled sync engine now uses hq-cloud 6.18.71. The hq-core update no longer replaces pack-installed worker links (for example `core/workers/public/qa-tester`) with a copy of the release folder.
7
+
8
+ ## [5.345.111] — 2026-10-13
9
+ - The local bot runs a Create brief job from the desktop app in a fresh model session of its own instead of resuming the owner's whole DM conversation, and schedules it like a DM rather than behind the job slot. A resumed session that fails twice in a row with the model CLI's "An unknown error occurred (Unexpected)" is cleared so the next turn starts fresh; a single failure never clears it.
10
+
11
+ - Add `--personal` support to `hq db provision`, `status`, and `sql` for a person's remote database.
12
+ - `hq capacity` now keeps its Claude OAuth sessions awake on its own: when a
13
+ token is about to expire it refreshes in the background, with per-home
14
+ backoff so busy lanes do not thrash the auth endpoint. If a refresh is
15
+ rejected (revoked or invalid grant), the account is reported as "signed
16
+ out: sign-in revoked" and the printed fix is `hq capacity login claude
17
+ --account <id> --repair` for a managed non-default home, or `claude auth
18
+ login` for the default home.
19
+ - Refreshes fail closed when the live-lane or running-process gates cannot
20
+ be verified (unexpected `hq lanes list --json` shape, `ps` failure, or
21
+ darwin credential pre-read failure with a prior refresh token), so a
22
+ rotation never races a running Claude Code.
23
+ - Doctor's refresh error record stores only the HTTP status and a fixed
24
+ classification (`invalid_grant`, `revoked`, `server_error`, `timeout`),
25
+ never a response body.
26
+ - An expired Claude account whose newest usage reading predates the token
27
+ expiry is now flagged as stale, so the router does not trust an old
28
+ reading as live headroom.
29
+ - `hq capacity doctor` adds a per-account Claude table showing token age,
30
+ time to expiry, last refresh attempt and outcome, and any backoff
31
+ window. All strings in the report are passed through a token-redaction
32
+ scrubber before display.
33
+
5
34
  ## [5.345.110] — 2026-10-13
6
35
  - The bundled sync runner now uses hq-cloud 6.18.69, which compacts the sync-state write-ahead log into a snapshot once the log grows past a size-scaled ratio of the snapshot, so large companies stop accumulating oversized sync-state files.
7
36
  - Remove the three policy-family shell rollback copies after the native route's clean release soak. `hq core policy age-report`, `policy retire`, and `policy frontmatter` now use the native route regardless of the retained `core-native-policy` flag, which still gates `knowledge-prefs`.
@@ -1916,7 +1916,7 @@ export declare const COMMAND_CATALOG: readonly [{
1916
1916
  readonly options: readonly [];
1917
1917
  readonly subcommands: readonly [{
1918
1918
  readonly name: "status";
1919
- readonly description: "Show local (and remote when bound) vault DB status; auto-creates local SQLite if missing";
1919
+ readonly description: "Show company local/remote status or your personal database status";
1920
1920
  readonly aliases: readonly [];
1921
1921
  readonly hidden: false;
1922
1922
  readonly helpOption: true;
@@ -1926,7 +1926,11 @@ export declare const COMMAND_CATALOG: readonly [{
1926
1926
  readonly arguments: readonly [];
1927
1927
  readonly options: readonly [{
1928
1928
  readonly flags: "--company <slug>";
1929
- readonly description: "Company slug (tenant scope; required)";
1929
+ readonly description: "Company slug (tenant scope)";
1930
+ readonly hidden: false;
1931
+ }, {
1932
+ readonly flags: "--personal";
1933
+ readonly description: "Show your personal database status";
1930
1934
  readonly hidden: false;
1931
1935
  }, {
1932
1936
  readonly flags: "--home <path>";
@@ -1945,7 +1949,7 @@ export declare const COMMAND_CATALOG: readonly [{
1945
1949
  readonly subcommands: readonly [];
1946
1950
  }, {
1947
1951
  readonly name: "sql";
1948
- readonly description: "Run SQL against the company vault DB (local) or an app's remote database (--app); read-only by default";
1952
+ readonly description: "Run read-only SQL against a company or personal database; use --write for changes";
1949
1953
  readonly aliases: readonly [];
1950
1954
  readonly hidden: false;
1951
1955
  readonly helpOption: true;
@@ -1960,7 +1964,11 @@ export declare const COMMAND_CATALOG: readonly [{
1960
1964
  }];
1961
1965
  readonly options: readonly [{
1962
1966
  readonly flags: "--company <slug>";
1963
- readonly description: "Company slug (tenant scope; required)";
1967
+ readonly description: "Company slug (tenant scope)";
1968
+ readonly hidden: false;
1969
+ }, {
1970
+ readonly flags: "--personal";
1971
+ readonly description: "Run against your personal remote database";
1964
1972
  readonly hidden: false;
1965
1973
  }, {
1966
1974
  readonly flags: "--app <app>";
@@ -2027,7 +2035,7 @@ export declare const COMMAND_CATALOG: readonly [{
2027
2035
  readonly subcommands: readonly [];
2028
2036
  }, {
2029
2037
  readonly name: "provision";
2030
- readonly description: "Create the company remote database (Team plan)";
2038
+ readonly description: "Create a company remote database or your personal database";
2031
2039
  readonly aliases: readonly [];
2032
2040
  readonly hidden: false;
2033
2041
  readonly helpOption: true;
@@ -2039,6 +2047,10 @@ export declare const COMMAND_CATALOG: readonly [{
2039
2047
  readonly flags: "--company <slug>";
2040
2048
  readonly description: "Company slug";
2041
2049
  readonly hidden: false;
2050
+ }, {
2051
+ readonly flags: "--personal";
2052
+ readonly description: "Provision your personal database";
2053
+ readonly hidden: false;
2042
2054
  }, {
2043
2055
  readonly flags: "--region <region>";
2044
2056
  readonly description: "AWS region";
@@ -2298,7 +2298,7 @@ export const COMMAND_CATALOG = [
2298
2298
  "subcommands": [
2299
2299
  {
2300
2300
  "name": "status",
2301
- "description": "Show local (and remote when bound) vault DB status; auto-creates local SQLite if missing",
2301
+ "description": "Show company local/remote status or your personal database status",
2302
2302
  "aliases": [],
2303
2303
  "hidden": false,
2304
2304
  "helpOption": true,
@@ -2309,7 +2309,12 @@ export const COMMAND_CATALOG = [
2309
2309
  "options": [
2310
2310
  {
2311
2311
  "flags": "--company <slug>",
2312
- "description": "Company slug (tenant scope; required)",
2312
+ "description": "Company slug (tenant scope)",
2313
+ "hidden": false
2314
+ },
2315
+ {
2316
+ "flags": "--personal",
2317
+ "description": "Show your personal database status",
2313
2318
  "hidden": false
2314
2319
  },
2315
2320
  {
@@ -2333,7 +2338,7 @@ export const COMMAND_CATALOG = [
2333
2338
  },
2334
2339
  {
2335
2340
  "name": "sql",
2336
- "description": "Run SQL against the company vault DB (local) or an app's remote database (--app); read-only by default",
2341
+ "description": "Run read-only SQL against a company or personal database; use --write for changes",
2337
2342
  "aliases": [],
2338
2343
  "hidden": false,
2339
2344
  "helpOption": true,
@@ -2351,7 +2356,12 @@ export const COMMAND_CATALOG = [
2351
2356
  "options": [
2352
2357
  {
2353
2358
  "flags": "--company <slug>",
2354
- "description": "Company slug (tenant scope; required)",
2359
+ "description": "Company slug (tenant scope)",
2360
+ "hidden": false
2361
+ },
2362
+ {
2363
+ "flags": "--personal",
2364
+ "description": "Run against your personal remote database",
2355
2365
  "hidden": false
2356
2366
  },
2357
2367
  {
@@ -2433,7 +2443,7 @@ export const COMMAND_CATALOG = [
2433
2443
  },
2434
2444
  {
2435
2445
  "name": "provision",
2436
- "description": "Create the company remote database (Team plan)",
2446
+ "description": "Create a company remote database or your personal database",
2437
2447
  "aliases": [],
2438
2448
  "hidden": false,
2439
2449
  "helpOption": true,
@@ -2447,6 +2457,11 @@ export const COMMAND_CATALOG = [
2447
2457
  "description": "Company slug",
2448
2458
  "hidden": false
2449
2459
  },
2460
+ {
2461
+ "flags": "--personal",
2462
+ "description": "Provision your personal database",
2463
+ "hidden": false
2464
+ },
2450
2465
  {
2451
2466
  "flags": "--region <region>",
2452
2467
  "description": "AWS region",
@@ -11,6 +11,7 @@
11
11
  import { Command } from "commander";
12
12
  import { type CapacityState, type LedgerEvent, type LedgerOptions, type NowView, type ParserFailureEvent, type Reading, type ReadingSource } from "../lib/capacity/ledger.js";
13
13
  import { type CapacityAccount } from "../lib/capacity/accounts.js";
14
+ import { type ClaudeRefreshLastEntry } from "../lib/capacity/collectors/claude-oauth.js";
14
15
  export declare const CAPACITY_SCHEMA_VERSION: "hq-capacity-now-view.v1";
15
16
  export declare const DOCTOR_SCHEMA_VERSION = "hq-capacity-doctor.v1";
16
17
  export declare const PARSER_FAILURE_WINDOW_MS: number;
@@ -40,6 +41,30 @@ export interface DoctorVersion {
40
41
  installed: string | null;
41
42
  match: boolean;
42
43
  }
44
+ export interface DoctorClaudeAccount {
45
+ /** Account label from the capacity registry (eg "default"). */
46
+ label: string;
47
+ /** Resolved credential home directory. */
48
+ home: string;
49
+ /** ISO timestamp of the last refresh attempt, redacted. Null when nothing was tried for this home. */
50
+ lastRefreshAttempt: string | null;
51
+ /** Outcome of the last refresh attempt. Null when nothing was tried. */
52
+ lastRefreshOutcome: ClaudeRefreshLastEntry["outcome"] | null;
53
+ /** ms since the last refresh attempt. Null when nothing was tried. */
54
+ tokenAgeMs: number | null;
55
+ /** ms until the token's recorded expiry. Negative when past expiry. Null when the collector has not recorded one. */
56
+ timeToExpiryMs: number | null;
57
+ /** ISO expiry of the token the entry describes. Null when unknown. */
58
+ tokenExpiresAt: string | null;
59
+ /** ISO backoff window end, when the last outcome set one. Null otherwise. */
60
+ backoffUntil: string | null;
61
+ /** Short, token-free diagnostic from the last attempt. Null otherwise. Never contains a response body. */
62
+ errorMessage: string | null;
63
+ /** Fixed classification string for the last error outcome. Null otherwise. */
64
+ errorClass: ClaudeRefreshLastEntry["errorClass"] | null;
65
+ /** HTTP status from the last attempt that reached the token endpoint. Null otherwise. */
66
+ httpStatus: number | null;
67
+ }
43
68
  export interface DoctorReport {
44
69
  schemaVersion: typeof DOCTOR_SCHEMA_VERSION;
45
70
  collector: "on" | "off";
@@ -50,6 +75,7 @@ export interface DoctorReport {
50
75
  provider: string;
51
76
  state: CapacityState;
52
77
  }[];
78
+ claudeAccounts: DoctorClaudeAccount[];
53
79
  ok: boolean;
54
80
  stale: boolean;
55
81
  ageMs: number;
@@ -3,9 +3,9 @@ import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import * as readline from "node:readline";
6
- import { LedgerCorrupt, LedgerMissing, READING_SOURCES, capacityOptionsFromEnv, capacityRoot, nowViewJsonSchema, readCollectorMark, readLedgerEvents, readNow, rebuildNow, setCollectorEnabled, } from "../lib/capacity/ledger.js";
6
+ import { LedgerCorrupt, LedgerMissing, READING_SOURCES, capacityOptionsFromEnv, capacityRoot, nowViewJsonSchema, readCollectorMark, readLedgerEvents, readNow, rebuildNow, redactProviderTokens, setCollectorEnabled, } from "../lib/capacity/ledger.js";
7
7
  import { listCapacityAccounts, registryAccountIds } from "../lib/capacity/accounts.js";
8
- import { claudeIdentityKeys } from "../lib/capacity/collectors/claude-oauth.js";
8
+ import { claudeIdentityKeys, readClaudeRefreshLast } from "../lib/capacity/collectors/claude-oauth.js";
9
9
  import { projectCapacityView } from "../lib/capacity/project.js";
10
10
  import { capacityWidgetTimeZone, renderCapacityWidget } from "../lib/capacity/widget.js";
11
11
  import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
@@ -185,6 +185,7 @@ export function buildDoctorReport(options) {
185
185
  provider: provider.provider,
186
186
  state: provider.state,
187
187
  }));
188
+ const claudeAccounts = buildClaudeAccountDoctor(resolved.options, resolved.nowMs);
188
189
  const freshness = capacityFreshness(view.generatedAt, resolved.nowMs);
189
190
  return {
190
191
  schemaVersion: DOCTOR_SCHEMA_VERSION,
@@ -193,11 +194,56 @@ export function buildDoctorReport(options) {
193
194
  versions,
194
195
  parserFailures,
195
196
  providers,
197
+ claudeAccounts,
196
198
  ok: providers.every((provider) => provider.state !== "unknown"),
197
199
  stale: freshness.stale,
198
200
  ageMs: freshness.ageMs,
199
201
  };
200
202
  }
203
+ function buildClaudeAccountDoctor(options, nowMs) {
204
+ const env = options.env ?? process.env;
205
+ const home = options.home ?? env.HOME;
206
+ const root = options.root ?? capacityRoot(env, home);
207
+ const accounts = listCapacityAccounts({ env, hqRoot: env.HQ_ROOT, home })
208
+ .filter((account) => account.provider === "claude");
209
+ return accounts.map((account) => {
210
+ const entry = readClaudeRefreshLast(root, account.home);
211
+ if (!entry) {
212
+ return {
213
+ label: account.label,
214
+ home: account.home,
215
+ lastRefreshAttempt: null,
216
+ lastRefreshOutcome: null,
217
+ tokenAgeMs: null,
218
+ timeToExpiryMs: null,
219
+ tokenExpiresAt: null,
220
+ backoffUntil: null,
221
+ errorMessage: null,
222
+ errorClass: null,
223
+ httpStatus: null,
224
+ };
225
+ }
226
+ const attemptedAtMs = Date.parse(entry.attemptedAt);
227
+ const expiresAtMs = entry.tokenExpiresAt ? Date.parse(entry.tokenExpiresAt) : NaN;
228
+ const tokenAgeMs = entry.outcome === "refreshed" && Number.isFinite(attemptedAtMs)
229
+ ? nowMs - attemptedAtMs
230
+ : null;
231
+ const timeToExpiryMs = Number.isFinite(expiresAtMs) ? expiresAtMs - nowMs : null;
232
+ return {
233
+ label: account.label,
234
+ home: account.home,
235
+ lastRefreshAttempt: redactProviderTokens(entry.attemptedAt),
236
+ lastRefreshOutcome: entry.outcome,
237
+ tokenAgeMs,
238
+ timeToExpiryMs,
239
+ tokenExpiresAt: entry.tokenExpiresAt ?? null,
240
+ backoffUntil: entry.backoffUntil ?? null,
241
+ errorMessage: entry.errorMessage ? redactProviderTokens(entry.errorMessage) : null,
242
+ errorClass: entry.errorClass ?? null,
243
+ httpStatus: typeof entry.httpStatus === "number" ? entry.httpStatus : null,
244
+ };
245
+ });
246
+ }
201
247
  export function formatDoctor(report) {
202
248
  const sources = formatTable(["SOURCE", "LAST SUCCESS"], report.sources.map((source) => [source.source, source.lastSuccess ?? "—"]));
203
249
  const versions = formatTable(["PROVIDER", "PINNED", "INSTALLED", "MATCH"], report.versions.map((version) => [
@@ -212,6 +258,16 @@ export function formatDoctor(report) {
212
258
  const states = report.providers.length === 0
213
259
  ? "none"
214
260
  : report.providers.map((provider) => `${provider.provider} ${provider.state}`).join("\n");
261
+ const claudeAccounts = report.claudeAccounts.length === 0
262
+ ? "none"
263
+ : formatTable(["ACCOUNT", "LAST ATTEMPT", "OUTCOME", "TOKEN AGE", "EXPIRES IN", "BACKOFF UNTIL"], report.claudeAccounts.map((account) => [
264
+ account.label,
265
+ account.lastRefreshAttempt ?? "—",
266
+ account.lastRefreshOutcome ?? "—",
267
+ account.tokenAgeMs === null ? "—" : formatSignedSpan(account.tokenAgeMs),
268
+ account.timeToExpiryMs === null ? "—" : formatSignedSpan(account.timeToExpiryMs),
269
+ account.backoffUntil ?? "—",
270
+ ]));
215
271
  const warning = report.stale
216
272
  ? report.ageMs === 0
217
273
  ? "collector not running, numbers may be stale; last update unknown"
@@ -226,8 +282,14 @@ export function formatDoctor(report) {
226
282
  failures,
227
283
  "provider state",
228
284
  states,
285
+ "claude accounts",
286
+ claudeAccounts,
229
287
  ].join("\n");
230
288
  }
289
+ function formatSignedSpan(ms) {
290
+ const span = formatSpan(Math.floor(Math.abs(ms) / 1000));
291
+ return ms < 0 ? `-${span}` : span;
292
+ }
231
293
  export function registerCapacityCommand(program) {
232
294
  const capacity = program
233
295
  .command("capacity")
@@ -9,24 +9,43 @@ import { defaultRemoteDeps, describeRemoteError, } from "./db-remote-deps.js";
9
9
  const defaultDeps = defaultRemoteDeps;
10
10
  export function registerDbProvisionCommand(db, depsFactory = defaultDeps) {
11
11
  db.command("provision")
12
- .description("Create the company remote database (Team plan)")
13
- .requiredOption("--company <slug>", "Company slug")
12
+ .description("Create a company remote database or your personal database")
13
+ .option("--company <slug>", "Company slug")
14
+ .option("--personal", "Provision your personal database")
14
15
  .option("--region <region>", "AWS region", "us-east-1")
15
16
  .action(async (opts) => {
16
17
  try {
17
18
  const deps = depsFactory();
18
19
  const company = String(opts.company ?? "").trim();
19
- if (!company) {
20
- console.error(chalk.red("Error: --company is required"));
20
+ if (opts.personal && opts.company !== undefined) {
21
+ console.error(chalk.red("Error: --personal cannot be used with --company"));
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+ if (!opts.personal && !company) {
26
+ console.error(chalk.red("error: required option '--company <slug>' not specified"));
21
27
  process.exitCode = 1;
22
28
  return;
23
29
  }
24
- const resolved = await deps.resolveCompany(company);
25
30
  const client = new ControlPlaneDbClient({
26
31
  baseUrl: deps.apiBaseUrl,
27
32
  getAccessToken: deps.getAccessToken,
28
33
  fetchImpl: deps.fetchImpl,
29
34
  });
35
+ if (opts.personal) {
36
+ const result = await client.personalProvision({ region: opts.region });
37
+ if (result.status === "provisioning") {
38
+ console.log("Your personal database is still being set up. Run this again in a minute.");
39
+ }
40
+ else if (result.idempotent || result.status === "existing") {
41
+ console.log(`Your personal database already exists and is ready (${result.region}).`);
42
+ }
43
+ else {
44
+ console.log(`Your personal database is ready (${result.region}).`);
45
+ }
46
+ return;
47
+ }
48
+ const resolved = await deps.resolveCompany(company);
30
49
  const result = await client.provision({
31
50
  companyUid: resolved.companyUid,
32
51
  companySlug: resolved.companySlug,
@@ -50,11 +69,13 @@ export function registerDbProvisionCommand(db, depsFactory = defaultDeps) {
50
69
  const status = error.status;
51
70
  if (described) {
52
71
  console.error(chalk.red("Error:"), described);
53
- if (status === 501 || error.code === "REMOTE_DB_NOT_AVAILABLE") {
54
- console.error(chalk.dim("Local hq db status|sql|migrate still work offline."));
55
- }
56
- else {
57
- console.error(chalk.dim("Local databases still work: hq db status|sql|migrate."));
72
+ if (!opts.personal) {
73
+ if (status === 501 || error.code === "REMOTE_DB_NOT_AVAILABLE") {
74
+ console.error(chalk.dim("Local hq db status|sql|migrate still work offline."));
75
+ }
76
+ else {
77
+ console.error(chalk.dim("Local databases still work: hq db status|sql|migrate."));
78
+ }
58
79
  }
59
80
  }
60
81
  else if (status === 404) {
@@ -54,6 +54,18 @@ export function describeRemoteError(error) {
54
54
  return error.message;
55
55
  const status = error.status;
56
56
  const code = error.code;
57
+ if (code === "PERSONAL_DB_NOT_ENABLED") {
58
+ return "Personal databases are not turned on for your account.";
59
+ }
60
+ if (code === "PERSONAL_DB_NOT_PROVISIONED") {
61
+ return "You don't have a personal database yet. Run: hq db provision --personal";
62
+ }
63
+ if (code === "PERSONAL_DB_APP_LIMIT") {
64
+ return "Your personal database already holds 8 apps, which is the limit.";
65
+ }
66
+ if (status === 404 && error.personalRoute) {
67
+ return "The HQ API does not support personal databases yet. Update the CLI or try again later.";
68
+ }
57
69
  if (code === "REMOTE_DB_NOT_AVAILABLE" || status === 501) {
58
70
  const reason = error.reason;
59
71
  const message = error.message;
@@ -8,10 +8,10 @@ import { Option } from "commander";
8
8
  import chalk from "chalk";
9
9
  import fs from "node:fs";
10
10
  import { safeErrorMessage } from "../lib/db/redact.js";
11
- import { formatRemoteResults, openRemoteAppConnection, runRemoteSql, } from "../lib/db/remote-engine.js";
11
+ import { formatRemoteResults, openRemoteAppConnection, openRemoteSessionConnection, 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, resolveAppId, } from "./db-remote-deps.js";
14
+ import { defaultRemoteDeps, controlPlaneClient, 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) {
@@ -24,8 +24,9 @@ function readSqlArg(sqlArgs, stdinFlag) {
24
24
  }
25
25
  export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps) {
26
26
  db.command("sql")
27
- .description("Run SQL against the company vault DB (local) or an app's remote database (--app); read-only by default")
28
- .requiredOption("--company <slug>", "Company slug (tenant scope; required)")
27
+ .description("Run read-only SQL against a company or personal database; use --write for changes")
28
+ .option("--company <slug>", "Company slug (tenant scope)")
29
+ .option("--personal", "Run against your personal remote database")
29
30
  .option("--app <app>", "Run against this app's remote database (app id, name or subdomain)")
30
31
  .option("--write", "Allow write/DDL statements (discouraged for ad-hoc DDL)", false)
31
32
  .addOption(new Option("--remote", "Remote SQL is not available in this CLI build yet; local queries work without --remote").hideHelp())
@@ -38,8 +39,26 @@ export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps)
38
39
  .action(async (sqlParts, opts) => {
39
40
  try {
40
41
  const company = String(opts.company ?? "").trim();
41
- if (!company) {
42
- console.error(chalk.red("Error: --company is required"));
42
+ if (opts.personal && opts.company !== undefined) {
43
+ console.error(chalk.red("Error: --personal cannot be used with --company"));
44
+ process.exitCode = 1;
45
+ return;
46
+ }
47
+ if (opts.personal) {
48
+ const conflicts = [
49
+ opts.app !== undefined && "--app",
50
+ !!opts.remote && "--remote",
51
+ opts.dbPath !== undefined && "--db-path",
52
+ !!opts.allowCrossCompanyPath && "--allow-cross-company-path",
53
+ ].filter(Boolean);
54
+ if (conflicts.length > 0) {
55
+ console.error(chalk.red(`Error: --personal cannot be used with ${conflicts.join(", ")}`));
56
+ process.exitCode = 1;
57
+ return;
58
+ }
59
+ }
60
+ if (!opts.personal && !company) {
61
+ console.error(chalk.red("error: required option '--company <slug>' not specified"));
43
62
  process.exitCode = 1;
44
63
  return;
45
64
  }
@@ -50,8 +69,12 @@ export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps)
50
69
  }
51
70
  const sql = readSqlArg(sqlParts, !!opts.stdin).trim();
52
71
  const format = opts.format === "table" ? "table" : "jsonl";
72
+ if (opts.personal) {
73
+ await runPersonalRemote(remoteDepsFactory(), sql, !!opts.write, format);
74
+ return;
75
+ }
53
76
  if (opts.app) {
54
- await runRemote(remoteDepsFactory(), company, opts.app, sql, !!opts.write);
77
+ await runRemote(remoteDepsFactory(), company, opts.app, sql, !!opts.write, format);
55
78
  return;
56
79
  }
57
80
  const result = runLocalSql({
@@ -73,7 +96,17 @@ export function registerDbSqlCommand(db, remoteDepsFactory = defaultRemoteDeps)
73
96
  }
74
97
  });
75
98
  }
76
- async function runRemote(deps, company, app, sql, write) {
99
+ async function runPersonalRemote(deps, sql, write, format) {
100
+ const session = await controlPlaneClient(deps).personalSession();
101
+ const conn = await openRemoteSessionConnection(remoteEngineDeps(deps), session, (schema) => schema === "personal");
102
+ try {
103
+ console.log(formatRemoteResults(await runRemoteSql(conn, sql, write), format));
104
+ }
105
+ finally {
106
+ await conn.close();
107
+ }
108
+ }
109
+ async function runRemote(deps, company, app, sql, write, format) {
77
110
  const resolved = await deps.resolveCompany(company);
78
111
  const appId = await resolveAppId(deps, resolved.companySlug, app);
79
112
  const conn = await openRemoteAppConnection(remoteEngineDeps(deps), {
@@ -81,7 +114,7 @@ async function runRemote(deps, company, app, sql, write) {
81
114
  appId,
82
115
  });
83
116
  try {
84
- console.log(formatRemoteResults(await runRemoteSql(conn, sql, write)));
117
+ console.log(formatRemoteResults(await runRemoteSql(conn, sql, write), format));
85
118
  }
86
119
  finally {
87
120
  await conn.close();
@@ -3,5 +3,6 @@
3
3
  * Optionally reports remote tier when control plane returns a binding (US-009).
4
4
  */
5
5
  import { Command } from "commander";
6
- export declare function registerDbStatusCommand(db: Command): void;
6
+ import { type DbRemoteCommandDeps } from "./db-remote-deps.js";
7
+ export declare function registerDbStatusCommand(db: Command, depsFactory?: () => DbRemoteCommandDeps): void;
7
8
  //# sourceMappingURL=db-status.d.ts.map
@@ -5,21 +5,47 @@
5
5
  import chalk from "chalk";
6
6
  import { ControlPlaneDbClient } from "../lib/db/control-plane.js";
7
7
  import { ensureAndStatusLocalDb, formatLocalDbStatus, } from "../lib/db/local.js";
8
- export function registerDbStatusCommand(db) {
8
+ import { safeErrorMessage } from "../lib/db/redact.js";
9
+ import { controlPlaneClient, defaultRemoteDeps, describeRemoteError, } from "./db-remote-deps.js";
10
+ export function registerDbStatusCommand(db, depsFactory = defaultRemoteDeps) {
9
11
  db.command("status")
10
- .description("Show local (and remote when bound) vault DB status; auto-creates local SQLite if missing")
11
- .requiredOption("--company <slug>", "Company slug (tenant scope; required)")
12
+ .description("Show company local/remote status or your personal database status")
13
+ .option("--company <slug>", "Company slug (tenant scope)")
14
+ .option("--personal", "Show your personal database status")
12
15
  .option("--home <path>", "Override home directory for local DB root (tests / advanced only)")
13
16
  .option("--remote", "Also query control plane for remote tier status (requires auth)", false)
14
17
  .option("--company-uid <uid>", "Company uid for remote status (with --remote)")
15
18
  .action(async (opts) => {
16
19
  try {
17
20
  const company = String(opts.company ?? "").trim();
18
- if (!company) {
19
- console.error(chalk.red("Error: --company is required"));
21
+ if (opts.personal && opts.company !== undefined) {
22
+ console.error(chalk.red("Error: --personal cannot be used with --company"));
20
23
  process.exitCode = 1;
21
24
  return;
22
25
  }
26
+ if (opts.personal && (opts.remote || opts.companyUid !== undefined)) {
27
+ console.error(chalk.red("Error: --personal cannot be used with --remote or --company-uid"));
28
+ process.exitCode = 1;
29
+ return;
30
+ }
31
+ if (!opts.personal && !company) {
32
+ console.error(chalk.red("error: required option '--company <slug>' not specified"));
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ if (opts.personal) {
37
+ const remote = await controlPlaneClient(depsFactory()).personalStatus();
38
+ if (!remote.remote) {
39
+ console.log("remote: (none)");
40
+ }
41
+ else {
42
+ console.log(`remote: ${remote.remote.status}`);
43
+ console.log(`remoteEngine: ${remote.remote.engineId}`);
44
+ console.log(`remoteRegion: ${remote.remote.region}`);
45
+ console.log(`remoteResourceArn: ${remote.remote.resourceArn}`);
46
+ }
47
+ return;
48
+ }
23
49
  const status = ensureAndStatusLocalDb(company, opts.home ? { home: opts.home } : undefined);
24
50
  // Never print remote connection material.
25
51
  console.log(formatLocalDbStatus(status));
@@ -54,7 +80,7 @@ export function registerDbStatusCommand(db) {
54
80
  }
55
81
  }
56
82
  catch (e) {
57
- const msg = e instanceof Error ? e.message : "remote status failed";
83
+ const msg = describeRemoteError(e) ?? safeErrorMessage(e);
58
84
  console.log(`remote: error (${msg})`);
59
85
  }
60
86
  }
@@ -63,7 +89,7 @@ export function registerDbStatusCommand(db) {
63
89
  }
64
90
  }
65
91
  catch (error) {
66
- console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
92
+ console.error(chalk.red("Error:"), describeRemoteError(error) ?? safeErrorMessage(error));
67
93
  process.exitCode = 1;
68
94
  }
69
95
  });