harnery 0.41.0 → 0.42.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/commander.js +3 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +9 -1
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +263 -51
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -1
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -9
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- package/dist/core/adapters/profiles.d.ts +9 -0
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +22 -1
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +60 -0
- package/dist/core/artifacts/delivery-card.js +9 -2
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +522 -49
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +128 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +2 -2
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +16 -0
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/hooks/adapter/events.d.ts +17 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +5 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +37 -7
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +62 -4
- package/dist/core/hooks/cli.js +45 -6
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +4 -1
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/gate.js +20 -1
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- package/package.json +6 -1
- package/schemas/config.schema.json +91 -0
- package/src/commander.ts +19 -0
- package/src/commands/agents.ts +14 -0
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse.ts +400 -57
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/disk.ts +114 -0
- package/src/commands/doctor.ts +52 -1
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +55 -5
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- package/src/core/adapters/profiles.ts +22 -1
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/render/session-context.ts +1 -0
- package/src/core/agents/state/live-coordination-view.ts +73 -0
- package/src/core/artifacts/delivery-card.ts +10 -2
- package/src/core/artifacts/index.ts +697 -44
- package/src/core/config.ts +183 -4
- package/src/core/diagnostics/bundle.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +16 -0
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/hooks/adapter/events.ts +19 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +116 -8
- package/src/core/hooks/cli.ts +47 -6
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +4 -1
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/gate.ts +22 -1
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { lstatSync, readdirSync } from "node:fs";
|
|
3
|
+
import { dirname, extname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
+
export const DISK_GIT_STATES = [
|
|
5
|
+
"tracked",
|
|
6
|
+
"untracked",
|
|
7
|
+
"ignored",
|
|
8
|
+
"git-metadata",
|
|
9
|
+
"non-repository",
|
|
10
|
+
"unknown",
|
|
11
|
+
];
|
|
12
|
+
const TYPES = {
|
|
13
|
+
video: new Set(["mp4", "mov", "mkv", "webm", "avi", "m4v", "mxf"]),
|
|
14
|
+
audio: new Set(["mp3", "wav", "m4a", "aac", "flac", "ogg", "aiff"]),
|
|
15
|
+
image: new Set([
|
|
16
|
+
"png",
|
|
17
|
+
"jpg",
|
|
18
|
+
"jpeg",
|
|
19
|
+
"webp",
|
|
20
|
+
"gif",
|
|
21
|
+
"tif",
|
|
22
|
+
"tiff",
|
|
23
|
+
"svg",
|
|
24
|
+
"avif",
|
|
25
|
+
"heic",
|
|
26
|
+
"bmp",
|
|
27
|
+
"psd",
|
|
28
|
+
]),
|
|
29
|
+
archive: new Set(["zip", "gz", "tgz", "tar", "bz2", "xz", "7z", "rar"]),
|
|
30
|
+
};
|
|
31
|
+
TYPES.media = new Set([...TYPES.video, ...TYPES.audio, ...TYPES.image]);
|
|
32
|
+
const portable = (path) => path.split(sep).join("/");
|
|
33
|
+
const empty = (allocated) => ({
|
|
34
|
+
files: 0,
|
|
35
|
+
apparent_bytes: 0,
|
|
36
|
+
allocated_bytes: allocated ? 0 : null,
|
|
37
|
+
});
|
|
38
|
+
export function parseDiskSize(value) {
|
|
39
|
+
const match = /^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB|KiB|MiB|GiB|TiB)?$/i.exec(value);
|
|
40
|
+
if (!match)
|
|
41
|
+
throw new Error("--min-size requires bytes or a size such as 10MB or 1GiB");
|
|
42
|
+
const units = {
|
|
43
|
+
b: 1,
|
|
44
|
+
kb: 1e3,
|
|
45
|
+
mb: 1e6,
|
|
46
|
+
gb: 1e9,
|
|
47
|
+
tb: 1e12,
|
|
48
|
+
kib: 1024,
|
|
49
|
+
mib: 1024 ** 2,
|
|
50
|
+
gib: 1024 ** 3,
|
|
51
|
+
tib: 1024 ** 4,
|
|
52
|
+
};
|
|
53
|
+
const bytes = Number(match[1]) * units[(match[2] ?? "b").toLowerCase()];
|
|
54
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0)
|
|
55
|
+
throw new Error("--min-size must resolve to a non-negative whole byte count");
|
|
56
|
+
return bytes;
|
|
57
|
+
}
|
|
58
|
+
/** Inspect regular files only. No bodies are read and no symlinks are followed. */
|
|
59
|
+
export function diskUsage(input = ".", options = {}) {
|
|
60
|
+
const root = resolve(input);
|
|
61
|
+
const rootStat = lstatSync(root);
|
|
62
|
+
if (rootStat.isSymbolicLink() || (!rootStat.isDirectory() && !rootStat.isFile())) {
|
|
63
|
+
throw new Error("disk requires a regular file or directory, not a symlink or special file");
|
|
64
|
+
}
|
|
65
|
+
const group = options.group ?? "directory";
|
|
66
|
+
if (!["directory", "repository", "extension", "git", "files"].includes(group))
|
|
67
|
+
throw new Error("invalid --group; use directory, repository, extension, git, or files");
|
|
68
|
+
const depth = options.depth ?? 1;
|
|
69
|
+
const top = options.top ?? 20;
|
|
70
|
+
if (!Number.isSafeInteger(depth) || depth < 1)
|
|
71
|
+
throw new Error("--depth must be a positive integer");
|
|
72
|
+
if (!Number.isSafeInteger(top) || top < 1)
|
|
73
|
+
throw new Error("--top must be a positive integer");
|
|
74
|
+
if (options.minSize !== undefined &&
|
|
75
|
+
(!Number.isSafeInteger(options.minSize) || options.minSize < 0))
|
|
76
|
+
throw new Error("invalid --min-size");
|
|
77
|
+
const states = options.git ? new Set(options.git.split(",")) : null;
|
|
78
|
+
if (states && [...states].some((s) => !DISK_GIT_STATES.includes(s)))
|
|
79
|
+
throw new Error(`invalid --git; use ${DISK_GIT_STATES.join(", ")}`);
|
|
80
|
+
const extensions = options.type
|
|
81
|
+
? new Set(options.type
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.split(",")
|
|
84
|
+
.flatMap((type) => [...(TYPES[type] ?? new Set([type.replace(/^\./, "")]))]))
|
|
85
|
+
: null;
|
|
86
|
+
const excludes = (options.exclude ?? [])
|
|
87
|
+
.flatMap((value) => value.split(","))
|
|
88
|
+
.map((value) => value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, ""));
|
|
89
|
+
if (excludes.some((value) => !value || isAbsolute(value) || value.split("/").includes("..")))
|
|
90
|
+
throw new Error("--exclude requires directory names or paths relative to the scan root");
|
|
91
|
+
let allocatedAvailable = process.platform !== "win32" && Number.isFinite(rootStat.blocks);
|
|
92
|
+
const report = {
|
|
93
|
+
schema: "harnery.disk-usage/v1",
|
|
94
|
+
root,
|
|
95
|
+
measurement: options.apparent
|
|
96
|
+
? "apparent"
|
|
97
|
+
: allocatedAvailable
|
|
98
|
+
? "allocated"
|
|
99
|
+
: "apparent-fallback",
|
|
100
|
+
complete: true,
|
|
101
|
+
filters: { ...options, group, depth, top },
|
|
102
|
+
totals: empty(allocatedAvailable),
|
|
103
|
+
git: Object.fromEntries(DISK_GIT_STATES.map((state) => [state, empty(allocatedAvailable)])),
|
|
104
|
+
groups: [],
|
|
105
|
+
group_count: 0,
|
|
106
|
+
largest_files: [],
|
|
107
|
+
repositories: [],
|
|
108
|
+
skipped: { symlinks: 0, special_files: 0, excluded: 0 },
|
|
109
|
+
issues: [],
|
|
110
|
+
};
|
|
111
|
+
const groups = new Map();
|
|
112
|
+
const repositories = new Map();
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
const issue = (path, error) => {
|
|
115
|
+
report.complete = false;
|
|
116
|
+
report.issues.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
117
|
+
};
|
|
118
|
+
const git = (cwd, args) => spawnSync("git", ["-C", cwd, "-c", "core.fsmonitor=false", ...args], {
|
|
119
|
+
encoding: "utf8",
|
|
120
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
121
|
+
timeout: 60_000,
|
|
122
|
+
windowsHide: true,
|
|
123
|
+
});
|
|
124
|
+
const loadRepo = (repoRoot) => {
|
|
125
|
+
const cached = repositories.get(repoRoot);
|
|
126
|
+
if (cached)
|
|
127
|
+
return cached;
|
|
128
|
+
const repo = {
|
|
129
|
+
root: repoRoot,
|
|
130
|
+
tracked: new Set(),
|
|
131
|
+
untracked: new Set(),
|
|
132
|
+
valid: false,
|
|
133
|
+
};
|
|
134
|
+
repositories.set(repoRoot, repo);
|
|
135
|
+
for (const [args, target] of [
|
|
136
|
+
[["ls-files", "--cached", "-z"], repo.tracked],
|
|
137
|
+
[["ls-files", "--others", "--exclude-standard", "-z"], repo.untracked],
|
|
138
|
+
]) {
|
|
139
|
+
const result = git(repoRoot, [...args]);
|
|
140
|
+
if (result.status !== 0) {
|
|
141
|
+
issue(portable(relative(root, repoRoot)) || ".", result.error ?? new Error(`Git classification failed: ${result.stderr.trim()}`));
|
|
142
|
+
return repo;
|
|
143
|
+
}
|
|
144
|
+
for (const path of result.stdout.split("\0"))
|
|
145
|
+
if (path)
|
|
146
|
+
target.add(path);
|
|
147
|
+
}
|
|
148
|
+
repo.valid = true;
|
|
149
|
+
return repo;
|
|
150
|
+
};
|
|
151
|
+
const startDir = rootStat.isDirectory() ? root : dirname(root);
|
|
152
|
+
const detected = git(startDir, ["rev-parse", "--show-toplevel"]);
|
|
153
|
+
if (detected.error)
|
|
154
|
+
issue(".", detected.error);
|
|
155
|
+
const initialRepo = detected.status === 0 ? loadRepo(resolve(detected.stdout.trim())) : null;
|
|
156
|
+
const repoLabel = (repo) => {
|
|
157
|
+
if (!repo)
|
|
158
|
+
return null;
|
|
159
|
+
const label = portable(relative(root, repo.root));
|
|
160
|
+
return !label || label === ".." || label.startsWith("../") ? "." : label;
|
|
161
|
+
};
|
|
162
|
+
const metric = (row) => options.apparent || row.allocated_bytes === null ? row.apparent_bytes : row.allocated_bytes;
|
|
163
|
+
const compare = (a, b) => metric(b) - metric(a) || a.path.localeCompare(b.path);
|
|
164
|
+
const add = (totals, size, allocated) => {
|
|
165
|
+
totals.files++;
|
|
166
|
+
totals.apparent_bytes += size;
|
|
167
|
+
if (totals.allocated_bytes !== null)
|
|
168
|
+
totals.allocated_bytes += allocated;
|
|
169
|
+
};
|
|
170
|
+
const excluded = (path) => excludes.some((value) => value.includes("/")
|
|
171
|
+
? path === value || path.startsWith(`${value}/`)
|
|
172
|
+
: path.split("/").includes(value));
|
|
173
|
+
const file = (path, metadata, repo, inGit) => {
|
|
174
|
+
const rel = portable(relative(root, path)) || ".";
|
|
175
|
+
const extension = extname(path).slice(1).toLowerCase();
|
|
176
|
+
const repoPath = repo ? portable(relative(repo.root, path)) : "";
|
|
177
|
+
const state = inGit
|
|
178
|
+
? "git-metadata"
|
|
179
|
+
: !repo
|
|
180
|
+
? "non-repository"
|
|
181
|
+
: !repo.valid
|
|
182
|
+
? "unknown"
|
|
183
|
+
: repo.tracked.has(repoPath)
|
|
184
|
+
? "tracked"
|
|
185
|
+
: repo.untracked.has(repoPath)
|
|
186
|
+
? "untracked"
|
|
187
|
+
: "ignored";
|
|
188
|
+
if ((states && !states.has(state)) || (extensions && !extensions.has(extension)))
|
|
189
|
+
return;
|
|
190
|
+
const blocksAvailable = process.platform !== "win32" && Number.isFinite(metadata.blocks);
|
|
191
|
+
if (!blocksAvailable)
|
|
192
|
+
allocatedAvailable = false;
|
|
193
|
+
const allocation = blocksAvailable ? metadata.blocks * 512 : metadata.size;
|
|
194
|
+
if ((options.apparent || !blocksAvailable ? metadata.size : allocation) < (options.minSize ?? 0))
|
|
195
|
+
return;
|
|
196
|
+
const identity = metadata.ino ? `${metadata.dev}:${metadata.ino}` : path;
|
|
197
|
+
const allocated = seen.has(identity) ? 0 : allocation;
|
|
198
|
+
seen.add(identity);
|
|
199
|
+
add(report.totals, metadata.size, allocated);
|
|
200
|
+
add(report.git[state], metadata.size, allocated);
|
|
201
|
+
const row = {
|
|
202
|
+
path: rel,
|
|
203
|
+
files: 1,
|
|
204
|
+
apparent_bytes: metadata.size,
|
|
205
|
+
allocated_bytes: blocksAvailable ? allocation : null,
|
|
206
|
+
git: state,
|
|
207
|
+
repository: repoLabel(repo),
|
|
208
|
+
};
|
|
209
|
+
report.largest_files.push(row);
|
|
210
|
+
report.largest_files.sort(compare);
|
|
211
|
+
if (report.largest_files.length > top)
|
|
212
|
+
report.largest_files.pop();
|
|
213
|
+
if (group === "files")
|
|
214
|
+
return;
|
|
215
|
+
const parts = rel.split("/");
|
|
216
|
+
const label = group === "git"
|
|
217
|
+
? state
|
|
218
|
+
: group === "repository"
|
|
219
|
+
? (repoLabel(repo) ?? "(non-repository)")
|
|
220
|
+
: group === "extension"
|
|
221
|
+
? extension
|
|
222
|
+
? `.${extension}`
|
|
223
|
+
: "(no extension)"
|
|
224
|
+
: parts.length > 1
|
|
225
|
+
? parts.slice(0, Math.min(depth, parts.length - 1)).join("/")
|
|
226
|
+
: ".";
|
|
227
|
+
let bucket = groups.get(label);
|
|
228
|
+
if (!bucket) {
|
|
229
|
+
bucket = { path: label, ...empty(allocatedAvailable) };
|
|
230
|
+
groups.set(label, bucket);
|
|
231
|
+
}
|
|
232
|
+
add(bucket, metadata.size, allocated);
|
|
233
|
+
};
|
|
234
|
+
const walk = (dir, parentRepo, inGit) => {
|
|
235
|
+
let entries;
|
|
236
|
+
try {
|
|
237
|
+
// A directory can turn into a link between reading its parent and visiting it.
|
|
238
|
+
if (lstatSync(dir).isSymbolicLink()) {
|
|
239
|
+
report.skipped.symlinks++;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
issue(portable(relative(root, dir)) || ".", error);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const marker = entries.find((entry) => entry.name === ".git");
|
|
249
|
+
const repo = !inGit && marker && !marker.isSymbolicLink() ? loadRepo(dir) : parentRepo;
|
|
250
|
+
for (const entry of entries) {
|
|
251
|
+
const path = resolve(dir, entry.name);
|
|
252
|
+
const rel = portable(relative(root, path));
|
|
253
|
+
if (excluded(rel)) {
|
|
254
|
+
report.skipped.excluded++;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (entry.isSymbolicLink()) {
|
|
258
|
+
report.skipped.symlinks++;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const gitMetadata = inGit || entry.name === ".git";
|
|
262
|
+
if (entry.isDirectory()) {
|
|
263
|
+
if (gitMetadata && states && !states.has("git-metadata"))
|
|
264
|
+
continue;
|
|
265
|
+
walk(path, repo, gitMetadata);
|
|
266
|
+
}
|
|
267
|
+
else if (entry.isFile()) {
|
|
268
|
+
try {
|
|
269
|
+
const metadata = lstatSync(path);
|
|
270
|
+
if (metadata.isSymbolicLink())
|
|
271
|
+
report.skipped.symlinks++;
|
|
272
|
+
else if (metadata.isFile())
|
|
273
|
+
file(path, metadata, repo, gitMetadata);
|
|
274
|
+
else
|
|
275
|
+
report.skipped.special_files++;
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
issue(rel, error);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else
|
|
282
|
+
report.skipped.special_files++;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const inMetadata = startDir.split(sep).includes(".git");
|
|
286
|
+
if (rootStat.isDirectory())
|
|
287
|
+
walk(root, initialRepo, inMetadata);
|
|
288
|
+
else if (!excluded("."))
|
|
289
|
+
file(root, rootStat, initialRepo, inMetadata || root.endsWith(`${sep}.git`));
|
|
290
|
+
if (!allocatedAvailable) {
|
|
291
|
+
report.totals.allocated_bytes = null;
|
|
292
|
+
for (const totals of Object.values(report.git))
|
|
293
|
+
totals.allocated_bytes = null;
|
|
294
|
+
for (const row of groups.values())
|
|
295
|
+
row.allocated_bytes = null;
|
|
296
|
+
report.measurement = options.apparent ? "apparent" : "apparent-fallback";
|
|
297
|
+
}
|
|
298
|
+
report.repositories = [...repositories.values()].map((repo) => repoLabel(repo)).sort();
|
|
299
|
+
report.group_count = group === "files" ? report.totals.files : groups.size;
|
|
300
|
+
report.groups =
|
|
301
|
+
group === "files" ? report.largest_files : [...groups.values()].sort(compare).slice(0, top);
|
|
302
|
+
return report;
|
|
303
|
+
}
|
package/dist/lib/docs-lint.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface LintOpts {
|
|
|
25
25
|
repo?: string;
|
|
26
26
|
source?: DocsRepositorySource;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Detect whether a file declares itself an intentional monolith. The banner
|
|
30
|
+
* must sit in the first 10 lines of the body; leading YAML frontmatter does
|
|
31
|
+
* not count toward those lines, since a v2 header alone can fill them.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isDeclaredMonolith(content: string): boolean;
|
|
28
34
|
/** Detect whether a file carries lifecycle status in leading YAML frontmatter. */
|
|
29
35
|
export declare function hasStatusHeader(path: string): boolean;
|
|
30
36
|
export declare function runLint(opts: LintOpts): Promise<Violation[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs-lint.d.ts","sourceRoot":"","sources":["../../src/lib/docs-lint.ts"],"names":[],"mappings":"AAiBA,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,IAAI,CAMP;AAYD,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2BAA2B,CAAC;AAEnC;;;;;;GAMG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"docs-lint.d.ts","sourceRoot":"","sources":["../../src/lib/docs-lint.ts"],"names":[],"mappings":"AAiBA,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,qBAAqB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,IAAI,CAMP;AAYD,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,2BAA2B,CAAC;AAEnC;;;;;;GAMG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAoGD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI3D;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrD;AA8ND,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAkClE"}
|
package/dist/lib/docs-lint.js
CHANGED
|
@@ -114,9 +114,14 @@ function findMarkdownFiles(view) {
|
|
|
114
114
|
!EXCLUDED_PREFIXES.some((p) => f.startsWith(p) || f.includes(`/${p}`)) &&
|
|
115
115
|
!EXTRA_EXCLUDED_PREFIXES.some((p) => f.startsWith(p) || f.includes(`/${p}`)));
|
|
116
116
|
}
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Detect whether a file declares itself an intentional monolith. The banner
|
|
119
|
+
* must sit in the first 10 lines of the body; leading YAML frontmatter does
|
|
120
|
+
* not count toward those lines, since a v2 header alone can fill them.
|
|
121
|
+
*/
|
|
122
|
+
export function isDeclaredMonolith(content) {
|
|
123
|
+
const body = content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "");
|
|
124
|
+
const head = body.split("\n").slice(0, 10).join("\n");
|
|
120
125
|
return /INTENTIONAL-MONOLITH/i.test(head);
|
|
121
126
|
}
|
|
122
127
|
/** Detect whether a file carries lifecycle status in leading YAML frontmatter. */
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface RemoveOptions {
|
|
2
|
+
root: string;
|
|
3
|
+
recursive?: boolean;
|
|
4
|
+
yes?: boolean;
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Protected paths (Git metadata, credentials, Harnery state, SSH or GnuPG
|
|
8
|
+
* keys, a bare repository) the caller names for removal, one exact path
|
|
9
|
+
* each. A protected path that is not named is still refused.
|
|
10
|
+
*/
|
|
11
|
+
allowProtected?: string[];
|
|
12
|
+
/** Directories whose own Git metadata and Harnery state may never be allowed (the live workspace). */
|
|
13
|
+
liveRoots?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface RemovalLink {
|
|
16
|
+
path: string;
|
|
17
|
+
/** What the link points to. It is removed as a link and never followed. */
|
|
18
|
+
target: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RemovalReport {
|
|
21
|
+
schema: "harnery.removal/v1";
|
|
22
|
+
applied: boolean;
|
|
23
|
+
root: string;
|
|
24
|
+
targets: string[];
|
|
25
|
+
entries: number;
|
|
26
|
+
bytes: number;
|
|
27
|
+
/** Protected paths removed because the caller named them with allowProtected. */
|
|
28
|
+
protected: string[];
|
|
29
|
+
/** Symbolic links inside recursive targets, removed as links. */
|
|
30
|
+
links: RemovalLink[];
|
|
31
|
+
}
|
|
32
|
+
/** Permanent removal is deliberately limited to inspected, untracked local outputs. */
|
|
33
|
+
export declare function guardedRemove(paths: string[], options: RemoveOptions, checkClaims?: (targets: string[]) => void): RemovalReport;
|
|
34
|
+
//# sourceMappingURL=guarded-remove.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guarded-remove.d.ts","sourceRoot":"","sources":["../../src/lib/guarded-remove.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sGAAsG;IACtG,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iEAAiE;IACjE,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAKD,uFAAuF;AACvF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,aAAa,EACtB,WAAW,GAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAe,GAClD,aAAa,CAyJf"}
|