harnery 0.43.0 → 0.44.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/README.md +3 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +16 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/workflow.js +24 -30
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +8 -12
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +7 -30
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +33 -5
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +9 -1
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +47 -27
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/servers/index.d.ts +24 -13
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +132 -92
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +3 -9
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +9 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/run-state.d.ts +3 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +31 -27
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +270 -6
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +21 -13
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +7 -1
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +16 -4
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/workflow.ts +30 -30
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +57 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +33 -5
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +71 -24
- package/src/core/resources/status.ts +57 -8
- package/src/core/servers/index.ts +177 -91
- package/src/core/servers/tunnels.ts +3 -11
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +88 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/run-state.ts +31 -29
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +315 -5
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +58 -0
- package/src/core/workflow/workspaces/recovery.ts +25 -13
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/zip/index.ts +483 -0
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { lstatSync, readdirSync, readlinkSync, realpathSync, rmSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { basename, dirname, isAbsolute, parse, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
5
5
|
const protectedNames = new Set([".git", ".harnery", ".credentials", ".ssh", ".gnupg"]);
|
|
6
6
|
const MAX_ENTRIES = 10_000;
|
|
7
|
+
const MAX_OWNED_ARTIFACT_ENTRIES = 100_000;
|
|
7
8
|
/** Permanent removal is deliberately limited to inspected, untracked local outputs. */
|
|
8
9
|
export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
9
10
|
const cwd = realpathSync(process.cwd());
|
|
10
11
|
const root = checkedPath(options.root, new Set(), true);
|
|
11
|
-
|
|
12
|
+
if (options.cleanClone) {
|
|
13
|
+
if (!options.recursive)
|
|
14
|
+
throw new Error("Clean clone removal requires --recursive");
|
|
15
|
+
if (options.allowProtected?.length)
|
|
16
|
+
throw new Error("Clean clone removal cannot be combined with --allow-protected");
|
|
17
|
+
if (!options.assertOwnedArtifact)
|
|
18
|
+
throw new Error("Clean clone removal requires one current caller-owned managed artifact");
|
|
19
|
+
options.assertOwnedArtifact(root);
|
|
20
|
+
}
|
|
21
|
+
const entryLimit = options.entryLimit ?? MAX_ENTRIES;
|
|
22
|
+
if (!Number.isSafeInteger(entryLimit) ||
|
|
23
|
+
entryLimit < 1 ||
|
|
24
|
+
entryLimit > MAX_OWNED_ARTIFACT_ENTRIES)
|
|
25
|
+
throw new Error(`The entry limit must be an integer from 1 to ${MAX_OWNED_ARTIFACT_ENTRIES}`);
|
|
26
|
+
const assertEntryLimitAuthority = () => {
|
|
27
|
+
if (entryLimit <= MAX_ENTRIES)
|
|
28
|
+
return;
|
|
29
|
+
if (!options.assertOwnedArtifact)
|
|
30
|
+
throw new Error("An entry limit above 10000 requires one current caller-owned managed artifact");
|
|
31
|
+
options.assertOwnedArtifact(root);
|
|
32
|
+
};
|
|
33
|
+
const allowed = allowedProtected(options.allowProtected ?? [], root, options.liveRoots ?? [], options.assertOwnedArtifact);
|
|
12
34
|
const rootStat = lstatSync(root);
|
|
13
35
|
const rootFingerprint = fingerprint(rootStat);
|
|
14
36
|
if (!rootStat.isDirectory())
|
|
15
37
|
throw new Error("--root must be a directory");
|
|
16
38
|
if (root === parse(root).root || root === realpathSync(homedir()))
|
|
17
39
|
throw new Error("--root cannot be a filesystem root or home directory");
|
|
40
|
+
assertEntryLimitAuthority();
|
|
18
41
|
if (paths.length === 0 || paths.length > 100)
|
|
19
42
|
throw new Error("Supply 1-100 explicit targets");
|
|
20
43
|
const targets = paths.map((path) => checkedPath(path, allowed));
|
|
@@ -22,6 +45,7 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
22
45
|
const removedProtected = new Set();
|
|
23
46
|
const links = [];
|
|
24
47
|
const gitDirs = new Set();
|
|
48
|
+
const cleanClones = new Set();
|
|
25
49
|
let bytes = 0;
|
|
26
50
|
const devices = new Set();
|
|
27
51
|
for (const target of targets) {
|
|
@@ -64,6 +88,10 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
64
88
|
if (stat.isDirectory() && !options.recursive)
|
|
65
89
|
throw new Error(`Directory removal requires --recursive: ${target}`);
|
|
66
90
|
const device = stat.dev;
|
|
91
|
+
if (options.cleanClone) {
|
|
92
|
+
assertCleanClone(target);
|
|
93
|
+
cleanClones.add(target);
|
|
94
|
+
}
|
|
67
95
|
devices.add(device);
|
|
68
96
|
const pending = [target];
|
|
69
97
|
while (pending.length) {
|
|
@@ -72,6 +100,12 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
72
100
|
if (basename(path) === ARTIFACT_RECORD && insideArtifactWorkspace(path, null))
|
|
73
101
|
throw new Error(`An artifact's record is managed by the artifact commands: ${path}`);
|
|
74
102
|
if (protectedNames.has(basename(path).toLowerCase())) {
|
|
103
|
+
if (options.cleanClone && basename(path) === ".git") {
|
|
104
|
+
const clone = dirname(path);
|
|
105
|
+
assertCleanClone(clone);
|
|
106
|
+
cleanClones.add(clone);
|
|
107
|
+
allowed.add(path);
|
|
108
|
+
}
|
|
75
109
|
if (!allowed.has(path))
|
|
76
110
|
throw new Error(protectedMessage(path));
|
|
77
111
|
removedProtected.add(path);
|
|
@@ -82,16 +116,16 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
82
116
|
// were already refused if they were links, in checkedPath.)
|
|
83
117
|
if (path === target)
|
|
84
118
|
throw new Error(`Path contains a symlink: ${path}`);
|
|
85
|
-
if (snapshots.size >=
|
|
86
|
-
throw new Error(`Removal exceeds the ${
|
|
119
|
+
if (snapshots.size >= entryLimit)
|
|
120
|
+
throw new Error(`Removal exceeds the ${entryLimit}-entry limit; use the owning cleanup command`);
|
|
87
121
|
snapshots.set(path, fingerprint(info));
|
|
88
122
|
links.push({ path, target: readlinkSync(path) });
|
|
89
123
|
continue;
|
|
90
124
|
}
|
|
91
125
|
if (info.dev !== device || (!info.isDirectory() && !info.isFile()))
|
|
92
126
|
throw new Error(`Mount boundary or special file cannot be removed: ${path}`);
|
|
93
|
-
if (snapshots.size >=
|
|
94
|
-
throw new Error(`Removal exceeds the ${
|
|
127
|
+
if (snapshots.size >= entryLimit)
|
|
128
|
+
throw new Error(`Removal exceeds the ${entryLimit}-entry limit; use the owning cleanup command`);
|
|
95
129
|
snapshots.set(path, fingerprint(info));
|
|
96
130
|
if (info.isFile())
|
|
97
131
|
bytes += info.size;
|
|
@@ -126,6 +160,7 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
126
160
|
root,
|
|
127
161
|
targets,
|
|
128
162
|
entries: snapshots.size,
|
|
163
|
+
entry_limit: entryLimit,
|
|
129
164
|
bytes,
|
|
130
165
|
protected: [...removedProtected].sort(),
|
|
131
166
|
links,
|
|
@@ -137,12 +172,19 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
137
172
|
if (checkedPath(options.root, new Set(), true) !== root ||
|
|
138
173
|
fingerprint(lstatSync(root)) !== rootFingerprint)
|
|
139
174
|
throw new Error("Allowed root changed during inspection");
|
|
175
|
+
assertEntryLimitAuthority();
|
|
176
|
+
if (options.cleanClone)
|
|
177
|
+
options.assertOwnedArtifact(root);
|
|
178
|
+
// Ownership may have been released or changed while the contents were inspected.
|
|
179
|
+
allowedProtected(options.allowProtected ?? [], root, options.liveRoots ?? [], options.assertOwnedArtifact);
|
|
140
180
|
for (const target of targets) {
|
|
141
181
|
checkedPath(target, allowed);
|
|
142
182
|
assertUntracked(target);
|
|
143
183
|
}
|
|
144
184
|
for (const dir of gitDirs)
|
|
145
185
|
assertNoUnpushedHistory(dir);
|
|
186
|
+
for (const clone of cleanClones)
|
|
187
|
+
assertCleanClone(clone);
|
|
146
188
|
checkClaims(targets);
|
|
147
189
|
for (const [path, previous] of snapshots) {
|
|
148
190
|
if (fingerprint(lstatSync(path)) !== previous)
|
|
@@ -152,6 +194,44 @@ export function guardedRemove(paths, options, checkClaims = () => { }) {
|
|
|
152
194
|
rmSync(target, { recursive: Boolean(options.recursive), force: false });
|
|
153
195
|
return { ...report, applied: true };
|
|
154
196
|
}
|
|
197
|
+
/** A disposable clone must not be the only copy of work or a registered worktree. */
|
|
198
|
+
function assertCleanClone(clone) {
|
|
199
|
+
const metadata = join(clone, ".git");
|
|
200
|
+
const stat = lstatSync(metadata);
|
|
201
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
202
|
+
throw new Error(`Clean clone removal requires a standalone .git directory: ${clone}`);
|
|
203
|
+
const git = (args) => {
|
|
204
|
+
const result = spawnSync("git", ["-c", "core.fsmonitor=false", "-C", clone, ...args], {
|
|
205
|
+
encoding: "utf8",
|
|
206
|
+
timeout: 10_000,
|
|
207
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
208
|
+
env: {
|
|
209
|
+
...process.env,
|
|
210
|
+
LC_ALL: "C",
|
|
211
|
+
GIT_DIR: undefined,
|
|
212
|
+
GIT_COMMON_DIR: undefined,
|
|
213
|
+
GIT_WORK_TREE: undefined,
|
|
214
|
+
GIT_INDEX_FILE: undefined,
|
|
215
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
if (result.error || result.status !== 0)
|
|
219
|
+
throw new Error(`Cannot verify clean clone ${clone}: git ${args[0]} failed`);
|
|
220
|
+
return result.stdout;
|
|
221
|
+
};
|
|
222
|
+
if (realpathSync(git(["rev-parse", "--show-toplevel"]).trim()) !== clone ||
|
|
223
|
+
realpathSync(resolve(clone, git(["rev-parse", "--git-common-dir"]).trim())) !== metadata)
|
|
224
|
+
throw new Error(`Clean clone removal refuses shared repository metadata: ${clone}`);
|
|
225
|
+
const worktrees = git(["worktree", "list", "--porcelain", "-z"])
|
|
226
|
+
.split("\0")
|
|
227
|
+
.filter((entry) => entry.startsWith("worktree "));
|
|
228
|
+
if (worktrees.length !== 1 || worktrees[0] !== `worktree ${clone}`)
|
|
229
|
+
throw new Error(`Clean clone removal refuses registered or linked worktrees: ${clone}`);
|
|
230
|
+
if (git(["status", "--porcelain=v1", "--untracked-files=all"]).trim())
|
|
231
|
+
throw new Error(`Clean clone has uncommitted or untracked work: ${clone}`);
|
|
232
|
+
if (git(["rev-list", "--max-count=1", "HEAD", "--all", "--reflog", "--not", "--remotes"]).trim())
|
|
233
|
+
throw new Error(`Clean clone has HEAD, ref or reflog commits not on any remote: ${clone}`);
|
|
234
|
+
}
|
|
155
235
|
/** The metadata file the artifact commands keep in each artifact workspace. */
|
|
156
236
|
const ARTIFACT_RECORD = ".harnery-artifact.json";
|
|
157
237
|
/**
|
|
@@ -188,9 +268,9 @@ function insideAllowed(path, allowed) {
|
|
|
188
268
|
* protected path strictly inside the root, reached without symlinks, and
|
|
189
269
|
* never the Git metadata or Harnery state of a live workspace root.
|
|
190
270
|
*/
|
|
191
|
-
function allowedProtected(paths, root, liveRoots) {
|
|
271
|
+
function allowedProtected(paths, root, liveRoots, assertOwnedArtifact) {
|
|
192
272
|
const allowed = new Set();
|
|
193
|
-
const live = liveRoots.map((dir) =>
|
|
273
|
+
const live = liveRoots.map((dir) => realpathSync(dir));
|
|
194
274
|
for (const input of paths) {
|
|
195
275
|
if (!input?.trim() || /[\0\r\n*?[\]$]/.test(input))
|
|
196
276
|
throw new Error("Use a nonempty explicit --allow-protected path without wildcards");
|
|
@@ -213,11 +293,25 @@ function allowedProtected(paths, root, liveRoots) {
|
|
|
213
293
|
if (!named && !(info.isDirectory() && isRepositoryMetadata(path)))
|
|
214
294
|
throw new Error(`--allow-protected must name a protected path itself: ${path}`);
|
|
215
295
|
for (const dir of live) {
|
|
216
|
-
|
|
217
|
-
if (path === dir ||
|
|
218
|
-
strictlyInside(path, dir) ||
|
|
219
|
-
own.some((state) => path === state || strictlyInside(state, path)))
|
|
296
|
+
if (path === dir || strictlyInside(path, dir))
|
|
220
297
|
throw new Error(`The live workspace's own state cannot be allowed: ${path}`);
|
|
298
|
+
for (const name of [".git", ".harnery"]) {
|
|
299
|
+
const state = resolve(dir, name);
|
|
300
|
+
if (path !== state && !strictlyInside(state, path))
|
|
301
|
+
continue;
|
|
302
|
+
// Only an explicitly named copied state directory inside one owned
|
|
303
|
+
// artifact may cross the outer live-state ancestor. Other live roots
|
|
304
|
+
// (including a checkout inside that artifact) still veto the path.
|
|
305
|
+
if (name === ".harnery" &&
|
|
306
|
+
basename(path) === ".harnery" &&
|
|
307
|
+
info.isDirectory() &&
|
|
308
|
+
dirname(root) === resolve(state, "artifacts") &&
|
|
309
|
+
assertOwnedArtifact) {
|
|
310
|
+
assertOwnedArtifact(root);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
throw new Error(`The live workspace's own state cannot be allowed: ${path}`);
|
|
314
|
+
}
|
|
221
315
|
}
|
|
222
316
|
allowed.add(realpathSync(path));
|
|
223
317
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assume.d.ts","sourceRoot":"","sources":["../../../src/lib/identities/assume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA0BH,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf,iBAAiB,GACjB,sBAAsB,GACtB,oBAAoB,GACpB,kBAAkB,GAClB,oBAAoB,GACpB,aAAa,GACb,mBAAmB,CAAC;AAExB,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,IAAI,EAAE,uBAAuB;gBAA7B,IAAI,EAAE,uBAAuB,EAC7C,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+DAA+D;IAC/D,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAOD;+EAC+E;AAC/E,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,SAAa,EAClB,mBAAmB,GAAE,WAAW,CAAC,MAAM,CAAa,GACnD,gBAAgB,GAAG,IAAI,CA6BzB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,
|
|
1
|
+
{"version":3,"file":"assume.d.ts","sourceRoot":"","sources":["../../../src/lib/identities/assume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA0BH,MAAM,MAAM,uBAAuB,GAC/B,eAAe,GACf,iBAAiB,GACjB,sBAAsB,GACtB,oBAAoB,GACpB,kBAAkB,GAClB,oBAAoB,GACpB,aAAa,GACb,mBAAmB,CAAC;AAExB,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,IAAI,EAAE,uBAAuB;gBAA7B,IAAI,EAAE,uBAAuB,EAC7C,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+DAA+D;IAC/D,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAOD;+EAC+E;AAC/E,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,SAAa,EAClB,mBAAmB,GAAE,WAAW,CAAC,MAAM,CAAa,GACnD,gBAAgB,GAAG,IAAI,CA6BzB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CA6CT;AAuED,gEAAgE;AAChE,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,oBAAoB,CAyHtB"}
|
|
@@ -85,6 +85,7 @@ export function reclaimAbandonedLocalConflict(coordRoot, conflict) {
|
|
|
85
85
|
let sessionId = conflict.instance_id;
|
|
86
86
|
let ageSecs;
|
|
87
87
|
const liveRow = readLiveCoordinationRow(coordRoot, conflict.instance_id);
|
|
88
|
+
let expectedGenerationId = liveRow?.v3_generation_id ?? null;
|
|
88
89
|
if (liveRow) {
|
|
89
90
|
adapter = adapterOf(liveRow.platform);
|
|
90
91
|
sessionId = liveRow.session_id || conflict.instance_id;
|
|
@@ -99,8 +100,10 @@ export function reclaimAbandonedLocalConflict(coordRoot, conflict) {
|
|
|
99
100
|
// It is required to join the exact producer state when recording the sweep.
|
|
100
101
|
adapter = adapterOf(hb.platform);
|
|
101
102
|
sessionId = hb.session_id || conflict.instance_id;
|
|
103
|
+
expectedGenerationId = hb.v3_generation_id ?? null;
|
|
102
104
|
}
|
|
103
105
|
catch {
|
|
106
|
+
expectedGenerationId = null;
|
|
104
107
|
/* content unreadable — still reclaim the file */
|
|
105
108
|
}
|
|
106
109
|
try {
|
|
@@ -116,6 +119,7 @@ export function reclaimAbandonedLocalConflict(coordRoot, conflict) {
|
|
|
116
119
|
owner: conflict.instance_id,
|
|
117
120
|
nativeSessionId: sessionId,
|
|
118
121
|
adapter,
|
|
122
|
+
expectedGenerationId,
|
|
119
123
|
observation: "stale_heartbeat",
|
|
120
124
|
ageMs: Math.max(0, (ageSecs ?? 0) * 1_000),
|
|
121
125
|
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface ZipLimits {
|
|
2
|
+
archiveBytes: number;
|
|
3
|
+
entryBytes: number;
|
|
4
|
+
totalBytes: number;
|
|
5
|
+
entries: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_ZIP_LIMITS: Readonly<ZipLimits>;
|
|
8
|
+
export interface ZipEntryInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
size: number;
|
|
11
|
+
compressedSize: number;
|
|
12
|
+
directory: boolean;
|
|
13
|
+
crc32: number;
|
|
14
|
+
}
|
|
15
|
+
export type ZipSource = string | Uint8Array;
|
|
16
|
+
export interface ReadOptions {
|
|
17
|
+
limits?: Partial<ZipLimits>;
|
|
18
|
+
}
|
|
19
|
+
export declare function listZip(source: ZipSource, options?: ReadOptions): ZipEntryInfo[];
|
|
20
|
+
export declare function readZipEntries(source: ZipSource, keep?: (name: string) => boolean, options?: ReadOptions): Map<string, Uint8Array>;
|
|
21
|
+
export declare function readZipEntry(source: ZipSource, name: string, options?: ReadOptions): Uint8Array | undefined;
|
|
22
|
+
export declare function readZipText(source: ZipSource, name: string, options?: ReadOptions): string | undefined;
|
|
23
|
+
/** Portable names exclude traversal, device names, streams and ambiguous aliases. */
|
|
24
|
+
export declare function safeEntryPath(root: string, name: string): string;
|
|
25
|
+
export interface ExtractOptions extends ReadOptions {
|
|
26
|
+
overwrite?: boolean;
|
|
27
|
+
filter?: (name: string) => boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Preflight all selected names, collisions and existing filesystem paths before writing. */
|
|
30
|
+
export declare function extractZip(source: ZipSource, dest: string, options?: ExtractOptions): string[];
|
|
31
|
+
export interface CreateOptions extends ReadOptions {
|
|
32
|
+
base?: string;
|
|
33
|
+
level?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
34
|
+
overwrite?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function buildZip(inputs: readonly string[], options?: CreateOptions): {
|
|
37
|
+
bytes: Uint8Array;
|
|
38
|
+
entries: string[];
|
|
39
|
+
};
|
|
40
|
+
export declare function createZip(archive: string, inputs: readonly string[], options?: CreateOptions): string[];
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/zip/index.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,SAAS,CAKjD,CAAC;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAC5C,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;CAC7B;AA+JD,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB,GAAG,YAAY,EAAE,CAKpF;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAoB,EAC5C,OAAO,GAAE,WAAgB,GACxB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAoBzB;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,UAAU,GAAG,SAAS,CAExB;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,MAAM,GAAG,SAAS,CAGpB;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAyBhE;AAsFD,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACpC;AAED,6FAA6F;AAC7F,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAmB,GAC3B,MAAM,EAAE,CAsCV;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,QAAQ,CACtB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,OAAO,GAAE,aAAkB,GAC1B;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA8C1C;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,OAAO,GAAE,aAAkB,GAC1B,MAAM,EAAE,CAQV"}
|