@indigoai-us/hq-cli 5.345.80 → 5.345.81

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.
@@ -0,0 +1,199 @@
1
+ /**
2
+ * `hq storage` — how much space HQ backups use, and pruning of old ones.
3
+ *
4
+ * Two stores:
5
+ * - Local autosave history (the HQ root git repo). Measured and pruned by the
6
+ * hq-core script `core/scripts/hq-storage.sh`; older HQ installs lack it and
7
+ * report `{ available: false, reason: "update HQ" }`.
8
+ * - Cloud file history: S3 object versioning on company vault buckets. Read
9
+ * with ListObjectVersions and pruned with batched DeleteObjects, using
10
+ * membership-scoped credentials from `/sts/vend`.
11
+ *
12
+ * Tenant isolation: cloud work only ever targets companies in the caller's own
13
+ * active memberships (`/membership/me`). `--company` narrows that set; a slug
14
+ * outside it is rejected before any credentials are vended. Deleting cloud
15
+ * history is for owners and admins only: prune checks the membership role
16
+ * before vending, and `status` marks each company with `can_delete`.
17
+ *
18
+ * `offload` and `fetch` (big files to the personal vault) live in
19
+ * `storage-offload.ts`; this file registers them and adds the offload section
20
+ * to `status`.
21
+ */
22
+ import { Command } from "commander";
23
+ import { type ReclaimReport } from "./storage-core.js";
24
+ import { ListObjectVersionsCommand, DeleteObjectsCommand, type ListObjectVersionsCommandOutput, type DeleteObjectsCommandOutput } from "@aws-sdk/client-s3";
25
+ import { type OffloadResult, type OffloadStatus } from "./storage-offload.js";
26
+ export type TrancheId = "7d" | "30d" | "90d" | "365d" | "older";
27
+ export declare const TRANCHES: readonly {
28
+ id: TrancheId;
29
+ label: string;
30
+ maxAgeDays: number;
31
+ }[];
32
+ export declare function trancheFor(lastModified: Date, now: Date): TrancheId;
33
+ export type LocalReport = Record<string, unknown> & {
34
+ available?: boolean;
35
+ reason?: string;
36
+ };
37
+ export interface CloudTranche {
38
+ id: TrancheId;
39
+ label: string;
40
+ count: number;
41
+ bytes: number;
42
+ }
43
+ export interface CloudCompanyStatus {
44
+ company: string;
45
+ available: boolean;
46
+ /** Whether the caller may delete this company's old versions (owner/admin). */
47
+ can_delete: boolean;
48
+ current_bytes: number;
49
+ noncurrent_bytes: number;
50
+ noncurrent_count: number;
51
+ delete_markers: number;
52
+ tranches: CloudTranche[];
53
+ error?: string;
54
+ }
55
+ export interface StorageStatus {
56
+ local: LocalReport;
57
+ cloud: CloudCompanyStatus[];
58
+ /** Set when cloud history could not be read at all, e.g. not signed in. */
59
+ cloud_error?: string;
60
+ offload?: OffloadStatus;
61
+ generated_at: string;
62
+ }
63
+ export interface CloudPruneResult {
64
+ company: string;
65
+ deleted_count: number;
66
+ deleted_bytes: number;
67
+ errors: string[];
68
+ /** Set when the company was not touched, e.g. the caller is not an owner/admin. */
69
+ skipped_reason?: string;
70
+ /** True when S3 refused the delete for this company (role not granted). */
71
+ access_denied?: boolean;
72
+ }
73
+ export interface StoragePruneResult {
74
+ local: LocalReport | null;
75
+ cloud: CloudPruneResult[];
76
+ dry_run: boolean;
77
+ /** Expired backups dropped before a real local prune; absent otherwise. */
78
+ reclaim?: ReclaimReport;
79
+ }
80
+ /** Subset of S3Client this command uses. Injectable for tests. */
81
+ export interface StorageS3Client {
82
+ send(cmd: ListObjectVersionsCommand): Promise<ListObjectVersionsCommandOutput>;
83
+ send(cmd: DeleteObjectsCommand): Promise<DeleteObjectsCommandOutput>;
84
+ }
85
+ export interface CompanyRef {
86
+ uid: string;
87
+ slug: string;
88
+ /** Membership role from `/membership/me`: owner, admin, member or guest. */
89
+ role?: string;
90
+ }
91
+ /** Cloud history delete is owners and admins only. A missing role is not enough. */
92
+ export declare function canDeleteCloudHistory(company: CompanyRef): boolean;
93
+ export declare const DELETE_ROLE_REASON = "Only company owners and admins can delete cloud file history.";
94
+ export interface CompanyStore {
95
+ bucket: string;
96
+ s3: StorageS3Client;
97
+ }
98
+ export type ScriptRunner = (scriptPath: string, args: string[]) => Promise<{
99
+ code: number;
100
+ stdout: string;
101
+ stderr: string;
102
+ }>;
103
+ export interface StorageDeps {
104
+ hqRoot: string;
105
+ now: () => Date;
106
+ /** The caller's active company memberships. */
107
+ listCompanies: () => Promise<CompanyRef[]>;
108
+ /** Vend credentials and resolve the bucket for one member company. */
109
+ openCompany: (company: CompanyRef) => Promise<CompanyStore>;
110
+ runScript: ScriptRunner;
111
+ confirm: (message: string) => Promise<boolean>;
112
+ log: (line: string) => void;
113
+ /** Offload section for `status`. Absent in deps that only prune. */
114
+ getOffload?: () => Promise<OffloadStatus>;
115
+ }
116
+ export declare function localScriptPath(hqRoot: string): string;
117
+ /**
118
+ * Core `drop-backups --expired` right before a real local prune; undefined
119
+ * when HQ lacks it. Status, dry runs and fetch never call it.
120
+ */
121
+ export declare function dropExpiredLocal(deps: StorageDeps): Promise<ReclaimReport | undefined>;
122
+ export declare function getLocalReport(deps: StorageDeps): Promise<LocalReport>;
123
+ export declare function selectCompanies(memberships: CompanyRef[], requested: string | undefined): CompanyRef[];
124
+ /**
125
+ * Active memberships from a `/membership/me` body, one per company. A company
126
+ * listed more than once keeps its highest-privilege role.
127
+ */
128
+ export declare function companiesFromMemberships(body: unknown): CompanyRef[];
129
+ export interface VersionEntry {
130
+ key: string;
131
+ versionId: string;
132
+ isLatest: boolean;
133
+ lastModified: Date;
134
+ size: number;
135
+ isDeleteMarker: boolean;
136
+ /**
137
+ * When this version stopped being current: the LastModified of the next
138
+ * newer version or delete marker of the same key. Unset for current entries.
139
+ */
140
+ noncurrentSince?: Date;
141
+ }
142
+ /**
143
+ * Fill `noncurrentSince` on every noncurrent entry. A noncurrent version's
144
+ * age is how long it has been a backup, not when it was written: a file
145
+ * written two years ago and overwritten yesterday became an undo copy
146
+ * yesterday. Within a key the current entry comes first, then newest to
147
+ * oldest. A noncurrent entry with no newer entry in the listing falls back
148
+ * to its own LastModified.
149
+ */
150
+ export declare function annotateNoncurrentSince(entries: VersionEntry[]): VersionEntry[];
151
+ /** Plain message when S3 refuses a version delete for one company. */
152
+ export declare function deleteDeniedMessage(slug: string): string;
153
+ export declare function describeCloudError(err: unknown, action: "list" | "delete", slug?: string): string;
154
+ export declare function getCloudStatus(company: CompanyRef, deps: StorageDeps): Promise<CloudCompanyStatus>;
155
+ /**
156
+ * Versions eligible for prune: noncurrent content versions and noncurrent
157
+ * delete markers that stopped being current before the cutoff (see
158
+ * annotateNoncurrentSince). The current version of a key (content or delete
159
+ * marker) is never selected.
160
+ */
161
+ export declare function selectPrunable(versions: VersionEntry[], cutoff: Date): VersionEntry[];
162
+ export declare function pruneCloud(company: CompanyRef, cutoff: Date, dryRun: boolean, deps: StorageDeps): Promise<CloudPruneResult>;
163
+ export declare function runStorageStatus(opts: {
164
+ company?: string;
165
+ }, deps: StorageDeps): Promise<StorageStatus>;
166
+ export declare function parseCutoff(value: string, flag: string): Date;
167
+ export declare function runStoragePrune(opts: {
168
+ localBefore?: string;
169
+ cloudBefore?: string;
170
+ company?: string;
171
+ dryRun: boolean;
172
+ yes: boolean;
173
+ }, deps: StorageDeps): Promise<StoragePruneResult | undefined>;
174
+ export declare function formatBytes(bytes: number): string;
175
+ export declare function formatStorageStatus(status: StorageStatus): string;
176
+ export declare function formatOffloadStatus(o: OffloadStatus): string[];
177
+ export declare function formatOffload(r: OffloadResult): string;
178
+ /** One line on when backed-up space comes back, or nothing. */
179
+ export declare function reclaimLine(r: Record<string, unknown>): string | undefined;
180
+ /** Readable name for a ref that keeps old history: "branch wip", "tag v1". */
181
+ export declare function holderName(ref: string): string;
182
+ /** Plain refusal text when branches or tags keep the history prune would remove. */
183
+ export declare function formatHoldingRefusal(local: LocalReport): string[];
184
+ /** Exit code for a prune result: 4 for nothing to free, 5 for a precondition, else 0/1. */
185
+ export declare function pruneExitCode(result: StoragePruneResult): number;
186
+ /** Plain lines for a local prune that was not refused (dry or real). */
187
+ export declare function formatLocalPrune(local: LocalReport, dryRun: boolean): string[];
188
+ export declare function formatStoragePrune(result: StoragePruneResult): string;
189
+ export declare const defaultRunScript: ScriptRunner;
190
+ export declare const NEEDS_YES = "No terminal to confirm in. Re-run with --yes to go ahead, or --dry-run to preview.";
191
+ /**
192
+ * Ask y/N on a terminal. Without a TTY the question could never be answered,
193
+ * so it throws and the command exits non-zero instead of hanging or exiting 0.
194
+ */
195
+ export declare function confirmOnTty(message: string, input?: NodeJS.ReadableStream & {
196
+ isTTY?: boolean;
197
+ }, output?: NodeJS.WritableStream): Promise<boolean>;
198
+ export declare function registerStorageCommand(program: Command): Command;
199
+ //# sourceMappingURL=storage.d.ts.map