@indigoai-us/hq-cli 5.345.99 → 5.345.101
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 +16 -0
- package/dist/command-catalog.generated.d.ts +20 -0
- package/dist/command-catalog.generated.js +24 -0
- package/dist/commands/storage.d.ts +36 -2
- package/dist/commands/storage.js +54 -6
- package/dist/lib/doctor/json-output.d.ts +9 -8
- package/dist/lib/doctor/json-output.js +12 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.101] — 2026-10-12
|
|
6
|
+
- Doctor JSON no longer counts an ordinary HQ Core upgrade warning in its
|
|
7
|
+
summary after omitting it from results. Session-start health that only saw
|
|
8
|
+
that leftover warning after `hq doctor --fix` stops treating it as an
|
|
9
|
+
unrepaired failure. `hq doctor` still prints the upgrade.
|
|
10
|
+
|
|
11
|
+
## [5.345.100] — 2026-10-12
|
|
12
|
+
- `hq storage status` no longer runs the big-file scan, so it returns the
|
|
13
|
+
local backup and cloud history figures without waiting minutes on a big HQ.
|
|
14
|
+
Its JSON adds `big_files_included: false` and leaves out `offload`.
|
|
15
|
+
- New `hq storage big-files [--json]` runs only the big-file scan and returns
|
|
16
|
+
`{ offload, generated_at }`. Read-only; a failed scan reports
|
|
17
|
+
`offload.available: false` instead of failing.
|
|
18
|
+
- `hq storage status --big-files` keeps the old single call: the scan runs and
|
|
19
|
+
`offload` is included as before (`big_files_included: true`).
|
|
20
|
+
|
|
5
21
|
## [5.345.99] — 2026-10-12
|
|
6
22
|
- Session-start health no longer files an ordinary HQ Core upgrade warning as
|
|
7
23
|
a failed doctor repair, including older hooks that only read `hq doctor --json`.
|
|
@@ -2935,6 +2935,26 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
2935
2935
|
readonly flags: "--no-cache";
|
|
2936
2936
|
readonly description: "List cloud file history live instead of using figures from the last 15 minutes";
|
|
2937
2937
|
readonly hidden: false;
|
|
2938
|
+
}, {
|
|
2939
|
+
readonly flags: "--big-files";
|
|
2940
|
+
readonly description: "Also scan for big files (slow on a big HQ); same as `hq storage big-files`";
|
|
2941
|
+
readonly hidden: false;
|
|
2942
|
+
}];
|
|
2943
|
+
readonly subcommands: readonly [];
|
|
2944
|
+
}, {
|
|
2945
|
+
readonly name: "big-files";
|
|
2946
|
+
readonly description: "Show big files that could move to HQ cloud (the slow scan `status` skips; read-only)";
|
|
2947
|
+
readonly aliases: readonly [];
|
|
2948
|
+
readonly hidden: false;
|
|
2949
|
+
readonly helpOption: true;
|
|
2950
|
+
readonly helpCommand: false;
|
|
2951
|
+
readonly usage: "[options]";
|
|
2952
|
+
readonly helpText: readonly [];
|
|
2953
|
+
readonly arguments: readonly [];
|
|
2954
|
+
readonly options: readonly [{
|
|
2955
|
+
readonly flags: "--json";
|
|
2956
|
+
readonly description: "Print JSON";
|
|
2957
|
+
readonly hidden: false;
|
|
2938
2958
|
}];
|
|
2939
2959
|
readonly subcommands: readonly [];
|
|
2940
2960
|
}, {
|
|
@@ -3538,6 +3538,30 @@ export const COMMAND_CATALOG = [
|
|
|
3538
3538
|
"flags": "--no-cache",
|
|
3539
3539
|
"description": "List cloud file history live instead of using figures from the last 15 minutes",
|
|
3540
3540
|
"hidden": false
|
|
3541
|
+
},
|
|
3542
|
+
{
|
|
3543
|
+
"flags": "--big-files",
|
|
3544
|
+
"description": "Also scan for big files (slow on a big HQ); same as `hq storage big-files`",
|
|
3545
|
+
"hidden": false
|
|
3546
|
+
}
|
|
3547
|
+
],
|
|
3548
|
+
"subcommands": []
|
|
3549
|
+
},
|
|
3550
|
+
{
|
|
3551
|
+
"name": "big-files",
|
|
3552
|
+
"description": "Show big files that could move to HQ cloud (the slow scan `status` skips; read-only)",
|
|
3553
|
+
"aliases": [],
|
|
3554
|
+
"hidden": false,
|
|
3555
|
+
"helpOption": true,
|
|
3556
|
+
"helpCommand": false,
|
|
3557
|
+
"usage": "[options]",
|
|
3558
|
+
"helpText": [],
|
|
3559
|
+
"arguments": [],
|
|
3560
|
+
"options": [
|
|
3561
|
+
{
|
|
3562
|
+
"flags": "--json",
|
|
3563
|
+
"description": "Print JSON",
|
|
3564
|
+
"hidden": false
|
|
3541
3565
|
}
|
|
3542
3566
|
],
|
|
3543
3567
|
"subcommands": []
|
|
@@ -16,8 +16,22 @@
|
|
|
16
16
|
* before vending, and `status` marks each company with `can_delete`.
|
|
17
17
|
*
|
|
18
18
|
* `offload` and `fetch` (big files to the personal vault) live in
|
|
19
|
-
* `storage-offload.ts`; this file registers them and
|
|
20
|
-
*
|
|
19
|
+
* `storage-offload.ts`; this file registers them and the big-files read.
|
|
20
|
+
*
|
|
21
|
+
* The big-file scan (`hq-storage.sh large`, minutes on a big HQ) is not part
|
|
22
|
+
* of a default `status`. JSON shapes:
|
|
23
|
+
*
|
|
24
|
+
* `hq storage status --json`
|
|
25
|
+
* { local, cloud, cloud_error?, big_files_included: false, generated_at }
|
|
26
|
+
* `hq storage status --big-files --json` (one call, as older CLIs did)
|
|
27
|
+
* { local, cloud, cloud_error?, offload, big_files_included: true, generated_at }
|
|
28
|
+
* `hq storage big-files --json`
|
|
29
|
+
* { offload, generated_at }
|
|
30
|
+
*
|
|
31
|
+
* `offload` is the OffloadStatus from `storage-offload.ts`; its fields are
|
|
32
|
+
* unchanged. A failed scan reports `offload.available: false` with a
|
|
33
|
+
* `reason` instead of failing the command. Older CLIs have no
|
|
34
|
+
* `big_files_included` and always included `offload` in status.
|
|
21
35
|
*/
|
|
22
36
|
import { Command } from "commander";
|
|
23
37
|
import { type ReclaimReport } from "./storage-core.js";
|
|
@@ -59,7 +73,19 @@ export interface StorageStatus {
|
|
|
59
73
|
cloud: CloudCompanyStatus[];
|
|
60
74
|
/** Set when cloud history could not be read at all, e.g. not signed in. */
|
|
61
75
|
cloud_error?: string;
|
|
76
|
+
/** Big files. Present only when the scan ran (`--big-files`). */
|
|
62
77
|
offload?: OffloadStatus;
|
|
78
|
+
/**
|
|
79
|
+
* Whether this status ran the big-file scan. False on a default status:
|
|
80
|
+
* get big files with `hq storage big-files`. Absent on older CLIs,
|
|
81
|
+
* which always ran it.
|
|
82
|
+
*/
|
|
83
|
+
big_files_included: boolean;
|
|
84
|
+
generated_at: string;
|
|
85
|
+
}
|
|
86
|
+
/** `hq storage big-files --json`: the big-file scan on its own. */
|
|
87
|
+
export interface StorageBigFiles {
|
|
88
|
+
offload: OffloadStatus;
|
|
63
89
|
generated_at: string;
|
|
64
90
|
}
|
|
65
91
|
export interface CloudPruneResult {
|
|
@@ -192,7 +218,15 @@ export declare function pruneCloud(company: CompanyRef, cutoff: Date, dryRun: bo
|
|
|
192
218
|
export declare function runStorageStatus(opts: {
|
|
193
219
|
company?: string;
|
|
194
220
|
noCache?: boolean;
|
|
221
|
+
bigFiles?: boolean;
|
|
195
222
|
}, deps: StorageDeps): Promise<StorageStatus>;
|
|
223
|
+
/**
|
|
224
|
+
* The big-file scan on its own (`hq storage big-files`): what a default
|
|
225
|
+
* `status` leaves out. Read-only like status: never drops backups, runs gc,
|
|
226
|
+
* prunes or touches the cloud. A failed scan is reported in the result
|
|
227
|
+
* (`offload.available: false`), not thrown.
|
|
228
|
+
*/
|
|
229
|
+
export declare function runStorageBigFiles(deps: StorageDeps): Promise<StorageBigFiles>;
|
|
196
230
|
export declare function parseCutoff(value: string, flag: string): Date;
|
|
197
231
|
export declare function runStoragePrune(opts: {
|
|
198
232
|
localBefore?: string;
|
package/dist/commands/storage.js
CHANGED
|
@@ -16,8 +16,22 @@
|
|
|
16
16
|
* before vending, and `status` marks each company with `can_delete`.
|
|
17
17
|
*
|
|
18
18
|
* `offload` and `fetch` (big files to the personal vault) live in
|
|
19
|
-
* `storage-offload.ts`; this file registers them and
|
|
20
|
-
*
|
|
19
|
+
* `storage-offload.ts`; this file registers them and the big-files read.
|
|
20
|
+
*
|
|
21
|
+
* The big-file scan (`hq-storage.sh large`, minutes on a big HQ) is not part
|
|
22
|
+
* of a default `status`. JSON shapes:
|
|
23
|
+
*
|
|
24
|
+
* `hq storage status --json`
|
|
25
|
+
* { local, cloud, cloud_error?, big_files_included: false, generated_at }
|
|
26
|
+
* `hq storage status --big-files --json` (one call, as older CLIs did)
|
|
27
|
+
* { local, cloud, cloud_error?, offload, big_files_included: true, generated_at }
|
|
28
|
+
* `hq storage big-files --json`
|
|
29
|
+
* { offload, generated_at }
|
|
30
|
+
*
|
|
31
|
+
* `offload` is the OffloadStatus from `storage-offload.ts`; its fields are
|
|
32
|
+
* unchanged. A failed scan reports `offload.available: false` with a
|
|
33
|
+
* `reason` instead of failing the command. Older CLIs have no
|
|
34
|
+
* `big_files_included` and always included `offload` in status.
|
|
21
35
|
*/
|
|
22
36
|
import chalk from "chalk";
|
|
23
37
|
import * as fs from "node:fs";
|
|
@@ -527,10 +541,13 @@ export async function pruneCloud(company, cutoff, dryRun, deps) {
|
|
|
527
541
|
// ── Orchestrators ───────────────────────────────────────────────────────────
|
|
528
542
|
export async function runStorageStatus(opts, deps) {
|
|
529
543
|
// Read-only: no backup drop, no gc. Pending backup bytes come from report.
|
|
530
|
-
// The
|
|
531
|
-
//
|
|
544
|
+
// The big-file scan is the slow part on a big HQ, so it runs only when
|
|
545
|
+
// asked for (`--big-files`); `hq storage big-files` runs it on its own.
|
|
546
|
+
// The local report, that scan and the cloud listing are independent, so
|
|
547
|
+
// they run at the same time: status takes as long as the slowest one.
|
|
548
|
+
const bigFiles = opts.bigFiles === true;
|
|
532
549
|
const localP = getLocalReport(deps);
|
|
533
|
-
const offloadP = deps.getOffload ? deps.getOffload().catch(offloadUnavailable) : Promise.resolve(undefined);
|
|
550
|
+
const offloadP = bigFiles && deps.getOffload ? deps.getOffload().catch(offloadUnavailable) : Promise.resolve(undefined);
|
|
534
551
|
const cloudP = (async () => {
|
|
535
552
|
let memberships;
|
|
536
553
|
try {
|
|
@@ -564,9 +581,22 @@ export async function runStorageStatus(opts, deps) {
|
|
|
564
581
|
cloud,
|
|
565
582
|
...(cloud_error !== undefined ? { cloud_error } : {}),
|
|
566
583
|
...(offload ? { offload } : {}),
|
|
584
|
+
big_files_included: bigFiles,
|
|
567
585
|
generated_at: deps.now().toISOString(),
|
|
568
586
|
};
|
|
569
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* The big-file scan on its own (`hq storage big-files`): what a default
|
|
590
|
+
* `status` leaves out. Read-only like status: never drops backups, runs gc,
|
|
591
|
+
* prunes or touches the cloud. A failed scan is reported in the result
|
|
592
|
+
* (`offload.available: false`), not thrown.
|
|
593
|
+
*/
|
|
594
|
+
export async function runStorageBigFiles(deps) {
|
|
595
|
+
const offload = deps.getOffload
|
|
596
|
+
? await deps.getOffload().catch(offloadUnavailable)
|
|
597
|
+
: offloadUnavailable(new Error("big-file scan not available"));
|
|
598
|
+
return { offload, generated_at: deps.now().toISOString() };
|
|
599
|
+
}
|
|
570
600
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
571
601
|
export function parseCutoff(value, flag) {
|
|
572
602
|
const d = new Date(`${value}T00:00:00Z`);
|
|
@@ -710,6 +740,9 @@ export function formatStorageStatus(status) {
|
|
|
710
740
|
}
|
|
711
741
|
if (status.offload)
|
|
712
742
|
lines.push("", ...formatOffloadStatus(status.offload));
|
|
743
|
+
else if (status.big_files_included === false) {
|
|
744
|
+
lines.push("", chalk.bold("Big files"), " Not checked. Run `hq storage big-files` to scan for them.");
|
|
745
|
+
}
|
|
713
746
|
return lines.join("\n");
|
|
714
747
|
}
|
|
715
748
|
function offloadUnavailable(err) {
|
|
@@ -1077,9 +1110,10 @@ export function registerStorageCommand(program) {
|
|
|
1077
1110
|
.option("--json", "Print JSON")
|
|
1078
1111
|
.option("--company <slug>", "Only this company (must be one of yours)")
|
|
1079
1112
|
.option("--no-cache", "List cloud file history live instead of using figures from the last 15 minutes")
|
|
1113
|
+
.option("--big-files", "Also scan for big files (slow on a big HQ); same as `hq storage big-files`")
|
|
1080
1114
|
.action(async (opts) => {
|
|
1081
1115
|
try {
|
|
1082
|
-
const status = await runStorageStatus({ company: opts.company, noCache: opts.cache === false }, await buildDeps());
|
|
1116
|
+
const status = await runStorageStatus({ company: opts.company, noCache: opts.cache === false, bigFiles: opts.bigFiles === true }, await buildDeps());
|
|
1083
1117
|
console.log(opts.json ? JSON.stringify(status, null, 2) : formatStorageStatus(status));
|
|
1084
1118
|
}
|
|
1085
1119
|
catch (err) {
|
|
@@ -1087,6 +1121,20 @@ export function registerStorageCommand(program) {
|
|
|
1087
1121
|
process.exitCode = 1;
|
|
1088
1122
|
}
|
|
1089
1123
|
});
|
|
1124
|
+
storage
|
|
1125
|
+
.command("big-files")
|
|
1126
|
+
.description("Show big files that could move to HQ cloud (the slow scan `status` skips; read-only)")
|
|
1127
|
+
.option("--json", "Print JSON")
|
|
1128
|
+
.action(async (opts) => {
|
|
1129
|
+
try {
|
|
1130
|
+
const result = await runStorageBigFiles(await buildDeps());
|
|
1131
|
+
console.log(opts.json ? JSON.stringify(result, null, 2) : formatOffloadStatus(result.offload).join("\n"));
|
|
1132
|
+
}
|
|
1133
|
+
catch (err) {
|
|
1134
|
+
console.error(chalk.red(err.message));
|
|
1135
|
+
process.exitCode = 1;
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1090
1138
|
storage
|
|
1091
1139
|
.command("prune")
|
|
1092
1140
|
.description("Delete local autosave history and old cloud file versions before a date")
|
|
@@ -107,12 +107,12 @@ export interface BuildDoctorJsonInput {
|
|
|
107
107
|
/**
|
|
108
108
|
* JSON consumed by the SessionStart health hook (`--client-health-report`).
|
|
109
109
|
* Ordinary hq-core upgrade WARNs are omitted from every JSON document
|
|
110
|
-
* (`--json` and `--client-health-report`)
|
|
111
|
-
* and both current hooks and older ones
|
|
112
|
-
* treated the leftover WARN as an
|
|
113
|
-
* still prints the upgrade. Company
|
|
114
|
-
* to refresh are omitted only from
|
|
115
|
-
* leaves them for a later session.
|
|
110
|
+
* (`--json` and `--client-health-report`), including `summary` tallies:
|
|
111
|
+
* `--fix` cannot apply a release, and both current hooks and older ones
|
|
112
|
+
* that only call `hq doctor --json` treated the leftover WARN as an
|
|
113
|
+
* unrepaired failure. Human `hq doctor` still prints the upgrade. Company
|
|
114
|
+
* journals this `--fix` pass did not try to refresh are omitted only from
|
|
115
|
+
* `--client-health-report`: the repair cap leaves them for a later session.
|
|
116
116
|
*/
|
|
117
117
|
clientHealthReport?: boolean;
|
|
118
118
|
/** Clock for the leftover-journal freshness window. Default: `Date.now`. */
|
|
@@ -121,8 +121,9 @@ export interface BuildDoctorJsonInput {
|
|
|
121
121
|
/**
|
|
122
122
|
* True when a sync finding is an ordinary, non-repairable hq-core upgrade.
|
|
123
123
|
* FAIL stays visible: that is a broken local core, not "a newer release exists".
|
|
124
|
-
* JSON omits the WARN
|
|
125
|
-
* only call `hq doctor --json`) does not file
|
|
124
|
+
* JSON omits the WARN (from `results` and `summary`) so SessionStart health
|
|
125
|
+
* (including core 15.x hooks that only call `hq doctor --json`) does not file
|
|
126
|
+
* it as a failed repair.
|
|
126
127
|
*/
|
|
127
128
|
export declare function isOrdinaryCoreUpdateWarn(result: {
|
|
128
129
|
checkId: string;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `target` and `remediation` are emitted only when present, matching the
|
|
18
18
|
* per-item result shape where both are optional.
|
|
19
19
|
*/
|
|
20
|
-
import { computeExitCode,
|
|
20
|
+
import { computeExitCode, emptyStatusCounts, UNKNOWN_PLATFORM, } from "./report.js";
|
|
21
21
|
import { deriveRemediation } from "./fix/remediation.js";
|
|
22
22
|
import { freshAttemptedCompanyJournalCheckIds, isUnreachedCompanyJournalForClientHealth, } from "./fix/refresh-sync-progress.js";
|
|
23
23
|
/**
|
|
@@ -33,12 +33,20 @@ export const DOCTOR_JSON_SCHEMA_VERSION = 2;
|
|
|
33
33
|
/**
|
|
34
34
|
* True when a sync finding is an ordinary, non-repairable hq-core upgrade.
|
|
35
35
|
* FAIL stays visible: that is a broken local core, not "a newer release exists".
|
|
36
|
-
* JSON omits the WARN
|
|
37
|
-
* only call `hq doctor --json`) does not file
|
|
36
|
+
* JSON omits the WARN (from `results` and `summary`) so SessionStart health
|
|
37
|
+
* (including core 15.x hooks that only call `hq doctor --json`) does not file
|
|
38
|
+
* it as a failed repair.
|
|
38
39
|
*/
|
|
39
40
|
export function isOrdinaryCoreUpdateWarn(result) {
|
|
40
41
|
return result.checkId === "sync.update.core" && result.status === "WARN";
|
|
41
42
|
}
|
|
43
|
+
/** Tally the results that actually ship in the JSON document. */
|
|
44
|
+
function summarizeJsonResults(results) {
|
|
45
|
+
const counts = emptyStatusCounts();
|
|
46
|
+
for (const result of results)
|
|
47
|
+
counts[result.status] += 1;
|
|
48
|
+
return counts;
|
|
49
|
+
}
|
|
42
50
|
/** Assemble the machine-readable document from a completed run. */
|
|
43
51
|
export function buildDoctorJson(input) {
|
|
44
52
|
const attemptedRefreshSyncCheckIds = input.clientHealthReport === true
|
|
@@ -61,7 +69,7 @@ export function buildDoctorJson(input) {
|
|
|
61
69
|
schemaVersion: DOCTOR_JSON_SCHEMA_VERSION,
|
|
62
70
|
platform: input.platform ?? UNKNOWN_PLATFORM,
|
|
63
71
|
hqRoot: input.hqRoot,
|
|
64
|
-
summary:
|
|
72
|
+
summary: summarizeJsonResults(results),
|
|
65
73
|
exitCode: computeExitCode(input.families),
|
|
66
74
|
...(input.scope && input.scope.length > 0 ? { scope: [...input.scope] } : {}),
|
|
67
75
|
results,
|