@git.zone/tsdisk 1.2.1 → 1.4.0
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +4 -1
- package/dist_ts/index.js +4 -2
- package/dist_ts/tsdisk.cleanup.d.ts +5 -0
- package/dist_ts/tsdisk.cleanup.js +25 -2
- package/dist_ts/tsdisk.cli.d.ts +13 -1
- package/dist_ts/tsdisk.cli.js +125 -50
- package/dist_ts/tsdisk.progress.d.ts +31 -0
- package/dist_ts/tsdisk.progress.js +64 -0
- package/dist_ts/tsdisk.scan.d.ts +3 -0
- package/dist_ts/tsdisk.scan.js +48 -19
- package/dist_ts/tsdisk.usage.d.ts +50 -0
- package/dist_ts/tsdisk.usage.js +194 -0
- package/package.json +1 -1
- package/readme.md +26 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +4 -0
- package/ts/tsdisk.cleanup.ts +26 -1
- package/ts/tsdisk.cli.ts +110 -53
- package/ts/tsdisk.progress.ts +78 -0
- package/ts/tsdisk.scan.ts +45 -18
- package/ts/tsdisk.usage.ts +199 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type IUsageEntry } from './tsdisk.usage.js';
|
|
2
|
+
import type { IToolCacheFinding } from './tsdisk.toolcaches.js';
|
|
3
|
+
export interface IReclaimEstimate {
|
|
4
|
+
candidateBytes: number;
|
|
5
|
+
unknownSizeCount: number;
|
|
6
|
+
protectedCount: number;
|
|
7
|
+
confirmedReclaimableBytes: null;
|
|
8
|
+
status: 'owner-review-required';
|
|
9
|
+
}
|
|
10
|
+
/** Do not sum parents and children, or treat a protected descendant as removable. */
|
|
11
|
+
export declare function estimateReclaimableSpace(findings: IToolCacheFinding[]): IReclaimEstimate;
|
|
12
|
+
export interface IDiskProgress {
|
|
13
|
+
stage: string;
|
|
14
|
+
cacheCount: number;
|
|
15
|
+
measuredBytes: number;
|
|
16
|
+
reclaim: IReclaimEstimate;
|
|
17
|
+
largest: IUsageEntry[];
|
|
18
|
+
}
|
|
19
|
+
export declare function formatBytes(bytes: number): string;
|
|
20
|
+
/** Timer ownership is explicit; callers stop it in finally even on failure. */
|
|
21
|
+
export declare class DiskProgressReporter {
|
|
22
|
+
private write;
|
|
23
|
+
private timer?;
|
|
24
|
+
private startedAt;
|
|
25
|
+
private state;
|
|
26
|
+
constructor(write: (line: string) => void);
|
|
27
|
+
update(update: Partial<IDiskProgress>): void;
|
|
28
|
+
start(): void;
|
|
29
|
+
private print;
|
|
30
|
+
stop(stage?: string): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { containsPath } from './tsdisk.usage.js';
|
|
2
|
+
/** Do not sum parents and children, or treat a protected descendant as removable. */
|
|
3
|
+
export function estimateReclaimableSpace(findings) {
|
|
4
|
+
const protectedPaths = findings.filter((finding) => finding.marker?.safeToPrune === false).map((finding) => finding.path);
|
|
5
|
+
const candidates = findings.filter((finding) => !protectedPaths.some((path) => containsPath(path, finding.path) || containsPath(finding.path, path)));
|
|
6
|
+
const selected = [];
|
|
7
|
+
for (const finding of [...candidates].sort((a, b) => a.path.length - b.path.length)) {
|
|
8
|
+
if (finding.bytes === undefined || selected.some((parent) => containsPath(parent.path, finding.path)))
|
|
9
|
+
continue;
|
|
10
|
+
selected.push(finding);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
candidateBytes: selected.reduce((sum, finding) => sum + (finding.bytes ?? 0), 0),
|
|
14
|
+
unknownSizeCount: candidates.filter((finding) => finding.bytes === undefined).length,
|
|
15
|
+
protectedCount: protectedPaths.length,
|
|
16
|
+
confirmedReclaimableBytes: null,
|
|
17
|
+
status: 'owner-review-required',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function formatBytes(bytes) {
|
|
21
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
22
|
+
return 'n/a';
|
|
23
|
+
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
|
24
|
+
let value = bytes;
|
|
25
|
+
let index = 0;
|
|
26
|
+
while (value >= 1024 && index < units.length - 1) {
|
|
27
|
+
value /= 1024;
|
|
28
|
+
index++;
|
|
29
|
+
}
|
|
30
|
+
return `${value.toFixed(index === 0 || value >= 100 ? 0 : value >= 10 ? 1 : 2)} ${units[index]}`;
|
|
31
|
+
}
|
|
32
|
+
/** Timer ownership is explicit; callers stop it in finally even on failure. */
|
|
33
|
+
export class DiskProgressReporter {
|
|
34
|
+
write;
|
|
35
|
+
timer;
|
|
36
|
+
startedAt = Date.now();
|
|
37
|
+
state = { stage: 'starting', cacheCount: 0, measuredBytes: 0, reclaim: estimateReclaimableSpace([]), largest: [] };
|
|
38
|
+
constructor(write) {
|
|
39
|
+
this.write = write;
|
|
40
|
+
}
|
|
41
|
+
update(update) { Object.assign(this.state, update); }
|
|
42
|
+
start() {
|
|
43
|
+
if (this.timer)
|
|
44
|
+
return;
|
|
45
|
+
this.startedAt = Date.now();
|
|
46
|
+
this.print();
|
|
47
|
+
this.timer = setInterval(() => this.print(), 5000);
|
|
48
|
+
this.timer.unref();
|
|
49
|
+
}
|
|
50
|
+
print() {
|
|
51
|
+
const state = this.state;
|
|
52
|
+
const top = state.largest.slice(0, 3).map((entry) => `${formatBytes(entry.bytes)}${entry.complete ? '' : '+'} ${JSON.stringify(entry.path)}`).join('; ');
|
|
53
|
+
this.write(`[tsdisk ${Math.floor((Date.now() - this.startedAt) / 1000)}s] ${state.stage} | measured ${formatBytes(state.measuredBytes)} | ${state.cacheCount} caches | potential cleanup ${formatBytes(state.reclaim.candidateBytes)} (owner review; ${state.reclaim.unknownSizeCount} unsized) | confirmed reclaimable: unknown${top ? ` | largest: ${top}` : ''}`);
|
|
54
|
+
}
|
|
55
|
+
stop(stage = 'finished') {
|
|
56
|
+
if (!this.timer)
|
|
57
|
+
return;
|
|
58
|
+
clearInterval(this.timer);
|
|
59
|
+
this.timer = undefined;
|
|
60
|
+
this.state.stage = stage;
|
|
61
|
+
this.print();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHNkaXNrLnByb2dyZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvdHNkaXNrLnByb2dyZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQW9CLE1BQU0sbUJBQW1CLENBQUM7QUFXbkUscUZBQXFGO0FBQ3JGLE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxRQUE2QjtJQUNwRSxNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLFdBQVcsS0FBSyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxSCxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0SixNQUFNLFFBQVEsR0FBd0IsRUFBRSxDQUFDO0lBQ3pDLEtBQUssTUFBTSxPQUFPLElBQUksQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUNwRixJQUFJLE9BQU8sQ0FBQyxLQUFLLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUFFLFNBQVM7UUFDaEgsUUFBUSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FBTztRQUNMLGNBQWMsRUFBRSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDaEYsZ0JBQWdCLEVBQUUsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssS0FBSyxTQUFTLENBQUMsQ0FBQyxNQUFNO1FBQ3BGLGNBQWMsRUFBRSxjQUFjLENBQUMsTUFBTTtRQUNyQyx5QkFBeUIsRUFBRSxJQUFJO1FBQy9CLE1BQU0sRUFBRSx1QkFBdUI7S0FDaEMsQ0FBQztBQUNKLENBQUM7QUFVRCxNQUFNLFVBQVUsV0FBVyxDQUFDLEtBQWE7SUFDdkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxHQUFHLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUN2RCxNQUFNLEtBQUssR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNoRCxJQUFJLEtBQUssR0FBRyxLQUFLLENBQUM7SUFDbEIsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsT0FBTyxLQUFLLElBQUksSUFBSSxJQUFJLEtBQUssR0FBRyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQUMsS0FBSyxJQUFJLElBQUksQ0FBQztRQUFDLEtBQUssRUFBRSxDQUFDO0lBQUMsQ0FBQztJQUM3RSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEtBQUssQ0FBQyxJQUFJLEtBQUssSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztBQUNuRyxDQUFDO0FBRUQsK0VBQStFO0FBQy9FLE1BQU0sT0FBTyxvQkFBb0I7SUFLWDtJQUpaLEtBQUssQ0FBa0M7SUFDdkMsU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN2QixLQUFLLEdBQWtCLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLGFBQWEsRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLHdCQUF3QixDQUFDLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUUxSSxZQUFvQixLQUE2QjtRQUE3QixVQUFLLEdBQUwsS0FBSyxDQUF3QjtJQUFHLENBQUM7SUFFOUMsTUFBTSxDQUFDLE1BQThCLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUU3RSxLQUFLO1FBQ1YsSUFBSSxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU87UUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDNUIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2IsSUFBSSxDQUFDLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVPLEtBQUs7UUFDWCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO1FBQ3pCLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pKLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsS0FBSyxlQUFlLFdBQVcsQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDLE1BQU0sS0FBSyxDQUFDLFVBQVUsK0JBQStCLFdBQVcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxtQkFBbUIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsNkNBQTZDLEdBQUcsQ0FBQyxDQUFDLENBQUMsZUFBZSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN2VyxDQUFDO0lBRU0sSUFBSSxDQUFDLEtBQUssR0FBRyxVQUFVO1FBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU87UUFDeEIsYUFBYSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMxQixJQUFJLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQztRQUN2QixJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDekIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2YsQ0FBQztDQUNGIn0=
|
package/dist_ts/tsdisk.scan.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface IWorkspaceScanOptions {
|
|
|
3
3
|
maxDepth: number;
|
|
4
4
|
timeoutMs: number;
|
|
5
5
|
maxEntries: number;
|
|
6
|
+
onProgress?: (result: IWorkspaceScanResult) => void;
|
|
6
7
|
}
|
|
7
8
|
export interface IScanIssue {
|
|
8
9
|
path: string;
|
|
@@ -15,5 +16,7 @@ export interface IWorkspaceScanResult {
|
|
|
15
16
|
issues: IScanIssue[];
|
|
16
17
|
complete: boolean;
|
|
17
18
|
visitedEntries: number;
|
|
19
|
+
issueCounts: Partial<Record<IScanIssue['reason'], number>>;
|
|
20
|
+
usagePriorityPaths: string[];
|
|
18
21
|
}
|
|
19
22
|
export declare function scanWorkspace(root: string, options: IWorkspaceScanOptions): Promise<IWorkspaceScanResult>;
|
package/dist_ts/tsdisk.scan.js
CHANGED
|
@@ -8,12 +8,14 @@ export async function scanWorkspace(root, options) {
|
|
|
8
8
|
throw new Error(`Invalid scan option ${name}: ${value}`);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
-
const result = { root: plugins.path.resolve(root), findings: [], issues: [], complete: true, visitedEntries: 0 };
|
|
11
|
+
const result = { root: plugins.path.resolve(root), findings: [], issues: [], issueCounts: {}, usagePriorityPaths: [], complete: true, visitedEntries: 0 };
|
|
12
12
|
const deadline = Date.now() + options.timeoutMs;
|
|
13
13
|
let stopped = false;
|
|
14
14
|
const issue = (path, reason, detail) => {
|
|
15
15
|
result.complete = false;
|
|
16
|
-
result.
|
|
16
|
+
result.issueCounts[reason] = (result.issueCounts[reason] ?? 0) + 1;
|
|
17
|
+
if (result.issues.length < 200)
|
|
18
|
+
result.issues.push({ path, reason, detail });
|
|
17
19
|
};
|
|
18
20
|
const limited = (path) => {
|
|
19
21
|
if (stopped)
|
|
@@ -39,6 +41,8 @@ export async function scanWorkspace(root, options) {
|
|
|
39
41
|
issue(result.root, 'read-error', error instanceof Error ? error.message : String(error));
|
|
40
42
|
return result;
|
|
41
43
|
}
|
|
44
|
+
const queue = [{ directory: result.root, depth: 0 }];
|
|
45
|
+
let queueLimited = false;
|
|
42
46
|
const visit = async (directory, depth) => {
|
|
43
47
|
if (limited(directory))
|
|
44
48
|
return;
|
|
@@ -53,11 +57,16 @@ export async function scanWorkspace(root, options) {
|
|
|
53
57
|
issue(directory, 'mount-boundary', 'Different filesystem skipped.');
|
|
54
58
|
return;
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
if (plugins.path.basename(directory) === '.nogit')
|
|
61
|
+
result.usagePriorityPaths.push(directory);
|
|
57
62
|
let marker;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const markerPath = plugins.path.join(directory, toolCacheMarkerFile);
|
|
64
|
+
const markerStat = await plugins.fs.lstat(markerPath).catch((error) => {
|
|
65
|
+
if (error.code === 'ENOENT')
|
|
66
|
+
return undefined;
|
|
67
|
+
throw error;
|
|
68
|
+
});
|
|
69
|
+
if (markerStat) {
|
|
61
70
|
if (!markerStat.isFile() || markerStat.size > 65536) {
|
|
62
71
|
issue(markerPath, 'invalid-marker', 'Marker must be a regular file of at most 64 KiB.');
|
|
63
72
|
}
|
|
@@ -74,20 +83,25 @@ export async function scanWorkspace(root, options) {
|
|
|
74
83
|
// Cache contents are not workspaces. Registry session directories can carry their own markers.
|
|
75
84
|
if (classification && heuristic?.kind !== 'docker-registry-cache')
|
|
76
85
|
return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
let enumerated = 0;
|
|
87
|
+
const children = await plugins.fs.opendir(directory);
|
|
88
|
+
for await (const child of children) {
|
|
89
|
+
// A huge flat directory must not consume the budget of sibling projects.
|
|
90
|
+
if (++enumerated > 4096) {
|
|
91
|
+
issue(directory, 'entry-limit', 'Directory listing stopped at 4096 entries; sibling directories are still scanned.');
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
if (excludedDirectories.has(child.name))
|
|
95
|
+
continue;
|
|
96
|
+
if (!child.isDirectory() && !child.isSymbolicLink())
|
|
97
|
+
continue;
|
|
98
|
+
if (depth >= options.maxDepth) {
|
|
80
99
|
issue(directory, 'depth-limit', `Maximum scan depth ${options.maxDepth} reached.`);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
for (const child of children) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
84
102
|
const childPath = plugins.path.join(directory, child.name);
|
|
85
103
|
if (limited(childPath))
|
|
86
104
|
return;
|
|
87
|
-
if (!child.isDirectory() && !child.isSymbolicLink()) {
|
|
88
|
-
result.visitedEntries++;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
105
|
if (child.isSymbolicLink()) {
|
|
92
106
|
result.visitedEntries++;
|
|
93
107
|
issue(childPath, 'symlink', 'Symbolic link skipped.');
|
|
@@ -105,14 +119,29 @@ export async function scanWorkspace(root, options) {
|
|
|
105
119
|
continue;
|
|
106
120
|
}
|
|
107
121
|
}
|
|
108
|
-
|
|
122
|
+
if (queue.length >= options.maxEntries) {
|
|
123
|
+
if (!queueLimited)
|
|
124
|
+
issue(directory, 'entry-limit', 'Directory queue budget exhausted; already queued siblings are still scanned.');
|
|
125
|
+
queueLimited = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
queue.push({ directory: childPath, depth: depth + 1 });
|
|
109
129
|
}
|
|
110
130
|
}
|
|
111
131
|
catch (error) {
|
|
112
132
|
issue(directory, 'read-error', error instanceof Error ? error.message : String(error));
|
|
113
133
|
}
|
|
114
134
|
};
|
|
115
|
-
|
|
135
|
+
// Breadth first: canonical projects precede deeply nested retained worktrees.
|
|
136
|
+
let lastProgress = 0;
|
|
137
|
+
for (let index = 0; index < queue.length && !stopped; index++) {
|
|
138
|
+
await visit(queue[index].directory, queue[index].depth);
|
|
139
|
+
if (Date.now() - lastProgress >= 250) {
|
|
140
|
+
options.onProgress?.(result);
|
|
141
|
+
lastProgress = Date.now();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
options.onProgress?.(result);
|
|
116
145
|
return result;
|
|
117
146
|
}
|
|
118
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
147
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHNkaXNrLnNjYW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c2Rpc2suc2Nhbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxvQkFBb0IsRUFBRSxtQkFBbUIsRUFBMEIsTUFBTSx3QkFBd0IsQ0FBQztBQXlCbEksTUFBTSxtQkFBbUIsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxjQUFjLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsYUFBYSxDQUFDLENBQUMsQ0FBQztBQUVuSCxNQUFNLENBQUMsS0FBSyxVQUFVLGFBQWEsQ0FBQyxJQUFZLEVBQUUsT0FBOEI7SUFDOUUsS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUsWUFBWSxDQUFVLEVBQUUsQ0FBQztRQUNwRSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxHQUFHLENBQUMsSUFBSSxLQUFLLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFFLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQzNELENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQXlCLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxXQUFXLEVBQUUsRUFBRSxFQUFFLGtCQUFrQixFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxF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
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface IUsageEntry {
|
|
2
|
+
path: string;
|
|
3
|
+
bytes: number;
|
|
4
|
+
complete: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface IUsageReport {
|
|
7
|
+
roots: string[];
|
|
8
|
+
measuredBytes: number;
|
|
9
|
+
entries: IUsageEntry[];
|
|
10
|
+
records: number;
|
|
11
|
+
complete: boolean;
|
|
12
|
+
code: number;
|
|
13
|
+
stderr: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function containsPath(parent: string, child: string): boolean;
|
|
16
|
+
/** Preserve explicitly selected nested mounts; subsume overlapping roots on the same device. */
|
|
17
|
+
export declare function normalizeScanRoots(roots: string[]): Promise<string[]>;
|
|
18
|
+
/** GNU du emits files immediately and inclusive directory totals on completion.
|
|
19
|
+
* Reconcile each completed directory with its observed children, rather than
|
|
20
|
+
* adding inclusive totals twice. The active ancestor chain survives a timeout.
|
|
21
|
+
*/
|
|
22
|
+
export declare class UsageAccumulator {
|
|
23
|
+
private roots;
|
|
24
|
+
private trackedPaths;
|
|
25
|
+
private priorityPaths;
|
|
26
|
+
private pending;
|
|
27
|
+
private active;
|
|
28
|
+
private largest;
|
|
29
|
+
private tracked;
|
|
30
|
+
private minimumLargestBytes;
|
|
31
|
+
private operandBytes;
|
|
32
|
+
private accountingRoots;
|
|
33
|
+
private currentRoot?;
|
|
34
|
+
measuredBytes: number;
|
|
35
|
+
records: number;
|
|
36
|
+
currentPath: string;
|
|
37
|
+
constructor(roots: string[], trackedPaths?: Set<string>, priorityPaths?: string[]);
|
|
38
|
+
consume(chunk: string): void;
|
|
39
|
+
private record;
|
|
40
|
+
snapshot(): IUsageEntry[];
|
|
41
|
+
get endedOnRecordBoundary(): boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface IUsageOptions {
|
|
44
|
+
timeoutSeconds: number;
|
|
45
|
+
trackedPaths?: string[];
|
|
46
|
+
priorityPaths?: string[];
|
|
47
|
+
onProgress?: (usage: UsageAccumulator) => void;
|
|
48
|
+
}
|
|
49
|
+
/** One streaming native pass; no output export, no symlink traversal, no raw file content reads. */
|
|
50
|
+
export declare function measureDiskUsage(roots: string[], options: IUsageOptions): Promise<IUsageReport>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as plugins from './tsdisk.plugins.js';
|
|
2
|
+
export function containsPath(parent, child) {
|
|
3
|
+
const relative = plugins.path.relative(parent, child);
|
|
4
|
+
return relative === '' || (!relative.startsWith(`..${plugins.path.sep}`) && relative !== '..' && !plugins.path.isAbsolute(relative));
|
|
5
|
+
}
|
|
6
|
+
/** Preserve explicitly selected nested mounts; subsume overlapping roots on the same device. */
|
|
7
|
+
export async function normalizeScanRoots(roots) {
|
|
8
|
+
const unique = [...new Set(roots.map((root) => plugins.path.resolve(root)))].sort((a, b) => a.length - b.length || a.localeCompare(b));
|
|
9
|
+
const accepted = [];
|
|
10
|
+
for (const path of unique) {
|
|
11
|
+
const stat = await plugins.fs.lstat(path).catch(() => undefined);
|
|
12
|
+
const real = await plugins.fs.realpath(path).catch(() => undefined);
|
|
13
|
+
const dev = stat?.isDirectory() && real === path ? stat.dev : undefined;
|
|
14
|
+
if (dev !== undefined && accepted.some((parent) => parent.dev === dev && containsPath(parent.path, path)))
|
|
15
|
+
continue;
|
|
16
|
+
accepted.push({ path, dev });
|
|
17
|
+
}
|
|
18
|
+
return accepted.map((root) => root.path);
|
|
19
|
+
}
|
|
20
|
+
/** GNU du emits files immediately and inclusive directory totals on completion.
|
|
21
|
+
* Reconcile each completed directory with its observed children, rather than
|
|
22
|
+
* adding inclusive totals twice. The active ancestor chain survives a timeout.
|
|
23
|
+
*/
|
|
24
|
+
export class UsageAccumulator {
|
|
25
|
+
roots;
|
|
26
|
+
trackedPaths;
|
|
27
|
+
priorityPaths;
|
|
28
|
+
pending = '';
|
|
29
|
+
active = new Map();
|
|
30
|
+
largest = new Map();
|
|
31
|
+
tracked = new Map();
|
|
32
|
+
minimumLargestBytes = 0;
|
|
33
|
+
operandBytes = new Map();
|
|
34
|
+
accountingRoots;
|
|
35
|
+
currentRoot;
|
|
36
|
+
measuredBytes = 0;
|
|
37
|
+
records = 0;
|
|
38
|
+
currentPath = '';
|
|
39
|
+
constructor(roots, trackedPaths = new Set(), priorityPaths = []) {
|
|
40
|
+
this.roots = roots;
|
|
41
|
+
this.trackedPaths = trackedPaths;
|
|
42
|
+
this.priorityPaths = priorityPaths;
|
|
43
|
+
this.roots = [...roots].sort((a, b) => b.length - a.length);
|
|
44
|
+
if (roots.some((root) => plugins.path.resolve(root) !== root) || priorityPaths.some((path) => !roots.some((root) => root !== path && containsPath(root, path)))) {
|
|
45
|
+
throw new Error('Usage accounting requires normalized roots and strict descendant priority paths.');
|
|
46
|
+
}
|
|
47
|
+
this.accountingRoots = [...priorityPaths, ...roots].sort((a, b) => b.length - a.length);
|
|
48
|
+
this.trackedPaths = new Set([...trackedPaths, ...roots, ...priorityPaths]);
|
|
49
|
+
}
|
|
50
|
+
consume(chunk) {
|
|
51
|
+
this.pending += chunk;
|
|
52
|
+
let start = 0;
|
|
53
|
+
for (;;) {
|
|
54
|
+
const end = this.pending.indexOf('\0', start);
|
|
55
|
+
if (end < 0)
|
|
56
|
+
break;
|
|
57
|
+
this.record(this.pending.slice(start, end));
|
|
58
|
+
start = end + 1;
|
|
59
|
+
}
|
|
60
|
+
this.pending = this.pending.slice(start);
|
|
61
|
+
if (this.pending.length > 1024 * 1024)
|
|
62
|
+
throw new Error('Invalid du record exceeds 1 MiB.');
|
|
63
|
+
}
|
|
64
|
+
record(record) {
|
|
65
|
+
const separator = record.indexOf('\t');
|
|
66
|
+
const bytes = Number(record.slice(0, separator));
|
|
67
|
+
const path = record.slice(separator + 1);
|
|
68
|
+
const matches = (candidate) => path === candidate || path.startsWith(candidate === '/' ? '/' : `${candidate}/`);
|
|
69
|
+
const root = this.currentRoot && matches(this.currentRoot) ? this.currentRoot : this.accountingRoots.find(matches);
|
|
70
|
+
if (separator < 1 || !Number.isSafeInteger(bytes) || bytes < 0 || !plugins.path.isAbsolute(path) || !root)
|
|
71
|
+
throw new Error('Invalid du output.');
|
|
72
|
+
this.records++;
|
|
73
|
+
this.currentPath = path;
|
|
74
|
+
this.currentRoot = path === root ? undefined : root;
|
|
75
|
+
const delta = bytes - (this.active.get(path) ?? 0);
|
|
76
|
+
this.active.delete(path);
|
|
77
|
+
this.measuredBytes += delta;
|
|
78
|
+
this.operandBytes.set(root, (this.operandBytes.get(root) ?? 0) + delta);
|
|
79
|
+
if (path !== root) {
|
|
80
|
+
for (let parent = plugins.path.dirname(path);; parent = plugins.path.dirname(parent)) {
|
|
81
|
+
this.active.set(parent, (this.active.get(parent) ?? 0) + delta);
|
|
82
|
+
if (parent === root)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const entry = { path, bytes, complete: true };
|
|
87
|
+
if (this.trackedPaths.has(path))
|
|
88
|
+
this.tracked.set(path, entry);
|
|
89
|
+
// Retain only the largest completed paths, not millions of file records.
|
|
90
|
+
if (this.largest.size < 100 || bytes > this.minimumLargestBytes) {
|
|
91
|
+
this.largest.set(path, entry);
|
|
92
|
+
if (this.largest.size > 100) {
|
|
93
|
+
const sorted = [...this.largest.values()].sort((a, b) => b.bytes - a.bytes);
|
|
94
|
+
this.largest = new Map(sorted.slice(0, 100).map((item) => [item.path, item]));
|
|
95
|
+
}
|
|
96
|
+
this.minimumLargestBytes = Math.min(...[...this.largest.values()].map((item) => item.bytes));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
snapshot() {
|
|
100
|
+
const entries = new Map([...this.largest, ...this.tracked].map(([path, entry]) => [path, { ...entry }]));
|
|
101
|
+
for (const [path, bytes] of this.active)
|
|
102
|
+
entries.set(path, { path, bytes, complete: false });
|
|
103
|
+
// du charges an overlapping subtree to its first operand and omits it from
|
|
104
|
+
// later ancestor totals. Restore those inclusive totals for display only;
|
|
105
|
+
// measuredBytes remains the native, deduplicated sum of operand contributions.
|
|
106
|
+
for (const priority of this.priorityPaths) {
|
|
107
|
+
const bytes = this.operandBytes.get(priority);
|
|
108
|
+
const root = this.roots.find((candidate) => containsPath(candidate, priority));
|
|
109
|
+
if (bytes === undefined || !root)
|
|
110
|
+
continue;
|
|
111
|
+
for (let parent = plugins.path.dirname(priority);; parent = plugins.path.dirname(parent)) {
|
|
112
|
+
const entry = entries.get(parent) ?? { path: parent, bytes: 0, complete: false };
|
|
113
|
+
entry.bytes += bytes;
|
|
114
|
+
entries.set(parent, entry);
|
|
115
|
+
if (parent === root)
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...entries.values()].sort((a, b) => b.bytes - a.bytes);
|
|
120
|
+
}
|
|
121
|
+
get endedOnRecordBoundary() { return this.pending.length === 0; }
|
|
122
|
+
}
|
|
123
|
+
/** One streaming native pass; no output export, no symlink traversal, no raw file content reads. */
|
|
124
|
+
export async function measureDiskUsage(roots, options) {
|
|
125
|
+
if (!Number.isFinite(options.timeoutSeconds) || options.timeoutSeconds <= 0)
|
|
126
|
+
throw new Error('Invalid usage timeout.');
|
|
127
|
+
const valid = [];
|
|
128
|
+
const devices = new Map();
|
|
129
|
+
const errors = [];
|
|
130
|
+
for (const root of await normalizeScanRoots(roots)) {
|
|
131
|
+
try {
|
|
132
|
+
const stat = await plugins.fs.lstat(root);
|
|
133
|
+
if (!stat.isDirectory() || await plugins.fs.realpath(root) !== root)
|
|
134
|
+
throw new Error('Usage roots must be real directories without symlink ancestors.');
|
|
135
|
+
valid.push(root);
|
|
136
|
+
devices.set(root, stat.dev);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
errors.push(`${root}: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const priorities = [];
|
|
143
|
+
for (const path of await normalizeScanRoots(options.priorityPaths ?? [])) {
|
|
144
|
+
const root = [...valid].reverse().find((root) => root !== path && containsPath(root, path));
|
|
145
|
+
if (!root)
|
|
146
|
+
continue;
|
|
147
|
+
const real = await plugins.fs.realpath(path).catch(() => undefined);
|
|
148
|
+
const stat = await plugins.fs.lstat(path).catch(() => undefined);
|
|
149
|
+
if (real === path && stat?.isDirectory() && stat.dev === devices.get(root))
|
|
150
|
+
priorities.push(path);
|
|
151
|
+
}
|
|
152
|
+
// Measure retained project artifacts before broad dependency/source trees.
|
|
153
|
+
// This changes order only: remaining paths are visited by the final roots.
|
|
154
|
+
priorities.sort((a, b) => b.split(plugins.path.sep).length - a.split(plugins.path.sep).length || a.localeCompare(b));
|
|
155
|
+
let argumentBytes = valid.reduce((sum, path) => sum + Buffer.byteLength(path) + 1, 0);
|
|
156
|
+
const argumentLimit = priorities.findIndex((path) => (argumentBytes += Buffer.byteLength(path) + 1) > 128 * 1024);
|
|
157
|
+
if (argumentLimit >= 0)
|
|
158
|
+
priorities.splice(argumentLimit);
|
|
159
|
+
const usage = new UsageAccumulator(valid, new Set(options.trackedPaths), priorities);
|
|
160
|
+
let stderr = errors.join('\n');
|
|
161
|
+
let malformed = false;
|
|
162
|
+
const code = valid.length ? await new Promise((resolve) => {
|
|
163
|
+
const child = plugins.spawn('timeout', ['--kill-after=5s', `${options.timeoutSeconds}s`, 'du', '-a', '-0', '-x', '-B1', '--', ...priorities, ...valid], { stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, LC_ALL: 'C' } });
|
|
164
|
+
child.stdout.setEncoding('utf8');
|
|
165
|
+
let lastNotification = 0;
|
|
166
|
+
child.stdout.on('data', (chunk) => {
|
|
167
|
+
if (malformed)
|
|
168
|
+
return;
|
|
169
|
+
try {
|
|
170
|
+
usage.consume(chunk);
|
|
171
|
+
if (Date.now() - lastNotification >= 250) {
|
|
172
|
+
options.onProgress?.(usage);
|
|
173
|
+
lastNotification = Date.now();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
malformed = true;
|
|
178
|
+
stderr += `\n${error instanceof Error ? error.message : String(error)}`;
|
|
179
|
+
// Signal timeout, which forwards termination to its managed process group.
|
|
180
|
+
child.kill('SIGTERM');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
child.stderr.setEncoding('utf8');
|
|
184
|
+
child.stderr.on('data', (chunk) => { if (stderr.length < 65536)
|
|
185
|
+
stderr += chunk.slice(0, 65536 - stderr.length); });
|
|
186
|
+
child.on('error', (error) => { stderr += `\n${error.message}`; });
|
|
187
|
+
child.on('close', (exitCode) => resolve(exitCode ?? 1));
|
|
188
|
+
}) : 1;
|
|
189
|
+
options.onProgress?.(usage);
|
|
190
|
+
const complete = code === 0 && !stderr.trim() && !malformed && usage.endedOnRecordBoundary;
|
|
191
|
+
const entries = usage.snapshot().map((entry) => ({ ...entry, complete: entry.complete && complete }));
|
|
192
|
+
return { roots: valid, measuredBytes: usage.measuredBytes, entries, records: usage.records, complete, code, stderr };
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHNkaXNrLnVzYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvdHNkaXNrLnVzYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFrQi9DLE1BQU0sVUFBVSxZQUFZLENBQUMsTUFBYyxFQUFFLEtBQWE7SUFDeEQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3RELE9BQU8sUUFBUSxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxLQUFLLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxRQUFRLEtBQUssSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUN2SSxDQUFDO0FBRUQsZ0dBQWdHO0FBQ2hHLE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQUMsS0FBZTtJQUN0RCxNQUFNLE1BQU0sR0FBRyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2SSxNQUFNLFFBQVEsR0FBMEMsRUFBRSxDQUFDO0lBQzNELEtBQUssTUFBTSxJQUFJLElBQUksTUFBTSxFQUFFLENBQUM7UUFDMUIsTUFBTSxJQUFJLEdBQUcsTUFBTSxPQUFPLENBQUMs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
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -24,7 +24,9 @@ pnpm dlx @git.zone/tsdisk
|
|
|
24
24
|
tsdisk
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
The tool prints a structured report covering filesystem usage,
|
|
27
|
+
The tool prints a structured report covering filesystem usage, large files and directories on mounted workspaces such as `/mnt/data`, known user and system cache paths, project-local tool caches, systemd journal usage, deleted open files, and Docker context storage. Both the default human report and `scan` print live progress every five seconds, including the current stage, discovered cache count, measured workspace bytes, potential cleanup bytes, and the largest paths found so far.
|
|
28
|
+
|
|
29
|
+
Potential cleanup is an estimate of measured cache candidates requiring owner review, not guaranteed free space. Explicitly protected caches and overlapping parent/child candidates are excluded from that estimate. Unknown sizes are counted separately. Hardlinks or shared filesystem extents can reduce actual savings. Backups, checkpoints, and worktrees appear as large disk consumers but are never automatically classified as removable. Confirmed reclaimable bytes remain unknown until the owning tool verifies cleanup eligibility.
|
|
28
30
|
|
|
29
31
|
It asks for `sudo` because root-owned filesystem paths and deleted open files require elevated permissions. The default diagnostic command does not delete caches or prune Docker data. The explicit `cleanup` command previews owning-tool cleanup and requires `--apply` to execute it.
|
|
30
32
|
|
|
@@ -66,11 +68,15 @@ tsdisk --version
|
|
|
66
68
|
|
|
67
69
|
`scan` and `--json` run bounded, noninteractive diagnostics without requesting sudo or installing tools. `--root` selects project-cache discovery and workspace size roots; host filesystem, home/cache, journal, deleted-open-file, and Docker diagnostics are still included. Without roots, discovery uses `/mnt/data` when present and the current directory. The original `tsdisk` human report remains available.
|
|
68
70
|
|
|
69
|
-
The JSON object includes `schemaVersion`, `generatedAt`, `roots`, `toolCaches`, `issues`, `sections`, and `complete`.
|
|
71
|
+
The JSON object includes `schemaVersion`, `generatedAt`, `roots`, `toolCaches`, `issues`, `issueCounts`, `usage`, `reclaim`, `sections`, and `complete`. Sections with `kind: "command"` record the executed command, arguments, exit code, stdout, stderr, and completeness. Workspace sections have `kind: "derived"`, `derivedFrom: "usage.entries"`, and `command: "usage-summary"`; their text is a compatibility summary of at most 100 retained paths, not native command output. Capped summaries set `result.truncated: true` and `complete: false`. Use `usage.entries` for structured paths and partial-size flags. Missing tools, command warnings, and insufficient permissions remain visible. JSON stdout contains exactly one document; five-second progress updates go to stderr.
|
|
72
|
+
|
|
73
|
+
`usage` records measured bytes, processed record count, the largest paths, cache paths, and completeness. It streams one native `du -a -0 -x -B1` pass over the selected roots, retaining partial ancestor totals even when a large directory exceeds the time budget. Discovered `.nogit` directories are measured first, so retained checkpoints and worktrees become visible before broad dependency trees. GNU du counts overlapping operands and hardlinks once; the report reconstructs inclusive ancestor totals without adding those bytes twice. `complete: false` sizes are lower bounds; human output marks them with `+`. Inclusive parent/child sizes must not be added together. File records are consumed as they arrive rather than retained in memory or exported to disk. The native pass includes dependencies, backups, and arbitrary large files. Overlapping roots on the same device are scanned once; explicitly selected nested mounts are retained.
|
|
74
|
+
|
|
75
|
+
`reclaim.candidateBytes` counts measured cache candidates without parent/child overlap; `unknownSizeCount` counts unsized candidates. `confirmedReclaimableBytes` is `null` and `status` is `owner-review-required`: discovery does not run project-installed cleanup tools or establish inactivity.
|
|
70
76
|
|
|
71
77
|
Cache discovery records read failures, invalid markers, symlinks, filesystem boundaries, and exhausted depth, entry, or time budgets. Unknown sizes stay absent rather than becoming zero. Symlink roots and roots with symlink ancestors are rejected. Standard dependency and generated-code directories are intentionally excluded. Cache byte sizes are allocated disk usage, not a promise of reclaimable space: parent/child findings and hardlinks may overlap.
|
|
72
78
|
|
|
73
|
-
|
|
79
|
+
Cache discovery visits directories breadth first, so nearby projects precede deep worktrees. It is bounded to depth 8, 50,000 directory/symlink inspections, and 45 seconds per root. Ordinary source files do not consume the directory budget; each directory listing is capped at 4,096 entries so a huge flat directory cannot starve its siblings. `issueCounts` retains totals by reason; `issues` contains at most 200 discovery examples per root, plus sizing issues. Sizing has a 45-second budget and a 500-cache limit. Workspace usage has one `DU_TIMEOUT` budget (default 300 seconds, plus five seconds for termination). Additional command diagnostics have a 120-second total budget, with at most 15 seconds per command. Discovery limits are cooperative between filesystem operations. `scan` exits `0` for a complete report, `2` for an incomplete report, or `1` for invalid input/failure. Consumers of the earlier `--json` shape can still read `toolCaches` and workspace command sections.
|
|
74
80
|
|
|
75
81
|
## Cleanup
|
|
76
82
|
|
|
@@ -91,11 +97,26 @@ Rust rechecks retention, markers, cache identity, and active use at execution ti
|
|
|
91
97
|
|
|
92
98
|
All owner previews must succeed without truncated output before any execution. `--apply` prints the plan and asks for confirmation; noninteractive or JSON execution additionally requires `--yes`. Each apply invokes the owning tool again so its safety checks run at execution time. A failed command stops subsequent actions and returns a nonzero exit code; completed actions are not rolled back. Commands time out after 120 seconds for preview or 240 seconds for apply, with a five-second termination grace period. Review owning-tool state before retrying an interrupted operation.
|
|
93
99
|
|
|
94
|
-
Cleanup runs as the project owner
|
|
100
|
+
Cleanup runs as the project owner and refuses filesystem roots and symlink project paths.
|
|
101
|
+
With project-local `@git.zone/tsdocker` 3.7.0 or later, Docker previews report whether
|
|
102
|
+
cache permissions require sudo. After successful previews and cleanup confirmation,
|
|
103
|
+
tsdisk invokes sudo's native password prompt in the terminal. The password never
|
|
104
|
+
enters tsdisk, arguments, environment variables, JSON, or log files. The owning tool
|
|
105
|
+
then elevates only approved cache file removal; project code and Docker commands
|
|
106
|
+
continue as your user. Rust and pnpm cleanup are never elevated.
|
|
107
|
+
|
|
108
|
+
Use `--no-sudo` to refuse elevated cleanup. Without a terminal, caches requiring sudo
|
|
109
|
+
block the batch unless `--sudo` is explicit and sudo can authenticate without a prompt.
|
|
110
|
+
`--yes` accepts the cleanup plan but does not supply or bypass sudo authentication.
|
|
111
|
+
JSON stdout remains one document; sudo prompts and notices go to the terminal/stderr.
|
|
112
|
+
Authentication failure or cancellation prevents all apply commands. Older Docker
|
|
113
|
+
tools retain their ordinary cleanup behavior and must be upgraded for sudo support;
|
|
114
|
+
tsdisk never reruns an old project binary as root. No plans or application state are
|
|
115
|
+
persisted. This Linux CLI uses `du`, `df`, `findmnt`, and GNU `timeout` from the host.
|
|
95
116
|
|
|
96
117
|
## Library API
|
|
97
118
|
|
|
98
|
-
`scanWorkspace(root, { maxDepth, maxEntries, timeoutMs })` returns findings, issues, completion status, and the
|
|
119
|
+
`scanWorkspace(root, { maxDepth, maxEntries, timeoutMs, onProgress? })` returns findings, issues, issue counts, usage-priority paths, completion status, and the directory/symlink inspection count. `collectProjectToolCacheReport(context, roots?)` adds sizing. `collectDiskReport(context, roots?)` adds streaming workspace usage and host command diagnostics; `context.onProgress` receives live state updates. `measureDiskUsage(roots, { timeoutSeconds, trackedPaths?, priorityPaths?, onProgress? })` performs the standalone native usage pass. Priority paths only reorder descendants on the selected filesystem; they cannot expand scan scope. `estimateReclaimableSpace(findings)` returns the conservative candidate summary. The existing classification and byte-formatting exports remain available. `parseCliOptions(args)` and `createCleanupPlan(options)` expose validated CLI options and cleanup command plans; creating a plan does not execute cleanup.
|
|
99
120
|
|
|
100
121
|
## License and Legal Information
|
|
101
122
|
|