harnery 0.43.0 → 0.45.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/commander.js +1 -0
- 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/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +7 -4
- 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 +26 -17
- 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/tunnel.d.ts +15 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +453 -153
- package/dist/commands/turn-rollback.d.ts +5 -0
- package/dist/commands/turn-rollback.d.ts.map +1 -0
- package/dist/commands/turn-rollback.js +76 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +68 -30
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +18 -0
- 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 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +46 -1
- package/dist/core/agents/state/process-handle.d.ts +26 -0
- package/dist/core/agents/state/process-handle.d.ts.map +1 -0
- package/dist/core/agents/state/process-handle.js +121 -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/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +2 -1
- 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 +7 -13
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +34 -91
- 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/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +2 -9
- package/dist/core/servers/index.d.ts +34 -22
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +297 -144
- package/dist/core/servers/tunnels.d.ts +7 -7
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +47 -10
- 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 +7 -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/turn-rollback/content.d.ts +53 -0
- package/dist/core/turn-rollback/content.d.ts.map +1 -0
- package/dist/core/turn-rollback/content.js +396 -0
- package/dist/core/turn-rollback/index.d.ts +72 -0
- package/dist/core/turn-rollback/index.d.ts.map +1 -0
- package/dist/core/turn-rollback/index.js +443 -0
- 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 +5 -3
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +3 -2
- 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/recovery-audit.d.ts +30 -0
- package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
- package/dist/core/workflow/recovery-audit.js +116 -0
- package/dist/core/workflow/run-state.d.ts +20 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +141 -32
- 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 +328 -8
- 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 +40 -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 +2 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +42 -10
- 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/tunnel/state.d.ts +13 -6
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +58 -12
- 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 +12 -1
- package/src/commander.ts +10 -0
- 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/files.ts +13 -5
- 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 +27 -26
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/tunnel.ts +533 -166
- package/src/commands/turn-rollback.ts +111 -0
- package/src/commands/workflow.ts +89 -30
- package/src/core/agents/coord-client.ts +20 -0
- 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 +62 -2
- package/src/core/agents/state/process-handle.ts +165 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/artifacts/delivery-card.ts +7 -1
- 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 +57 -95
- package/src/core/resources/status.ts +57 -8
- package/src/core/semantic/service.ts +2 -8
- package/src/core/servers/index.ts +376 -141
- package/src/core/servers/tunnels.ts +57 -13
- 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 +87 -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/turn-rollback/content.ts +499 -0
- package/src/core/turn-rollback/index.ts +580 -0
- 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 +12 -2
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/recovery-audit.ts +181 -0
- package/src/core/workflow/run-state.ts +165 -34
- 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 +388 -7
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +62 -0
- package/src/core/workflow/workspaces/recovery.ts +45 -10
- 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/tunnel/state.ts +70 -15
- 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
|
});
|
|
@@ -32,10 +32,12 @@ export interface TunnelState {
|
|
|
32
32
|
provider: TunnelProvider;
|
|
33
33
|
url: string;
|
|
34
34
|
gate_pid: number;
|
|
35
|
+
gate_start_token?: string;
|
|
36
|
+
cloudflared_start_token?: string;
|
|
37
|
+
registration_id?: string;
|
|
38
|
+
provider_state?: "pending" | "ready";
|
|
35
39
|
/** Present for Cloudflare quick tunnels; absent for Tailscale Serve/Funnel. */
|
|
36
40
|
cloudflared_pid?: number;
|
|
37
|
-
/** Optional provider-side process when a provider owns one. */
|
|
38
|
-
provider_pid?: number;
|
|
39
41
|
started_at: string;
|
|
40
42
|
target: string;
|
|
41
43
|
vhost: string;
|
|
@@ -57,9 +59,14 @@ export declare function clearState(name?: string, root?: string): void;
|
|
|
57
59
|
*/
|
|
58
60
|
export declare function listStates(root?: string): TunnelState[];
|
|
59
61
|
export declare function isProcessAlive(pid: number): boolean;
|
|
60
|
-
export type
|
|
61
|
-
|
|
62
|
-
export
|
|
62
|
+
export type TunnelReadiness = "ready" | "dead" | "unknown";
|
|
63
|
+
export type TunnelProcessObservation = "same" | "absent" | "different" | "unknown";
|
|
64
|
+
export interface TunnelReadinessVerifier {
|
|
65
|
+
observeProcess(pid: number, originalToken: string): TunnelProcessObservation;
|
|
66
|
+
withRegistration(state: TunnelState, inspect: () => TunnelReadiness): TunnelReadiness;
|
|
67
|
+
}
|
|
68
|
+
/** Classify saved original witnesses under the verifier's registration custody. */
|
|
69
|
+
export declare function tunnelReadiness(state: TunnelState, verifier: TunnelReadinessVerifier): TunnelReadiness;
|
|
63
70
|
export declare function tunnelTargetPort(state: TunnelState): number | null;
|
|
64
71
|
/**
|
|
65
72
|
* Newest live tunnel that serves `vhost` on the requested local web port.
|
|
@@ -72,7 +79,7 @@ export declare function tunnelTargetPort(state: TunnelState): number | null;
|
|
|
72
79
|
* link 400s. Matching the vhost too keeps the returned tunnel the one that
|
|
73
80
|
* actually serves the caller's origin.
|
|
74
81
|
*/
|
|
75
|
-
export declare function findLiveTunnelForOrigin(port: number, vhost: string, root
|
|
82
|
+
export declare function findLiveTunnelForOrigin(port: number, vhost: string, root: string, verifier: TunnelReadinessVerifier): TunnelState | null;
|
|
76
83
|
/** True when the tunnel's path scope forwards every one of `paths`. */
|
|
77
84
|
export declare function tunnelServesPaths(state: TunnelState, paths: readonly string[]): boolean;
|
|
78
85
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/state.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/tunnel/state.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Tunnel config + state persistence + provider helpers. Commands default to
|
|
2
2
|
// <cwd>/.cache/tunnel/; gitignored, so the allowlist is per-machine.
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
|
-
import {
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { accessSync, chmodSync, constants, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
import { delimiter, join, resolve } from "node:path";
|
|
7
8
|
import { isPathAllowed } from "./path-scope.js";
|
|
@@ -104,7 +105,17 @@ export function readState(name = DEFAULT_INSTANCE, root = process.cwd()) {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
export function writeState(state, root = process.cwd()) {
|
|
107
|
-
|
|
108
|
+
const path = cachePath(stateFile(state.name), root);
|
|
109
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
110
|
+
try {
|
|
111
|
+
writeFileSync(temporary, JSON.stringify(state, null, 2), { flag: "wx", mode: 0o600 });
|
|
112
|
+
renameSync(temporary, path);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (existsSync(temporary))
|
|
116
|
+
unlinkSync(temporary);
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
108
119
|
}
|
|
109
120
|
export function clearState(name = DEFAULT_INSTANCE, root = process.cwd()) {
|
|
110
121
|
const p = cachePath(stateFile(name), root);
|
|
@@ -139,14 +150,49 @@ export function isProcessAlive(pid) {
|
|
|
139
150
|
return false;
|
|
140
151
|
}
|
|
141
152
|
}
|
|
142
|
-
/**
|
|
143
|
-
export function
|
|
144
|
-
if (!
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
/** Classify saved original witnesses under the verifier's registration custody. */
|
|
154
|
+
export function tunnelReadiness(state, verifier) {
|
|
155
|
+
if (!state ||
|
|
156
|
+
typeof state !== "object" ||
|
|
157
|
+
Object.hasOwn(state, "provider_pid") ||
|
|
158
|
+
Object.hasOwn(state, "provider_start_token") ||
|
|
159
|
+
typeof state.registration_id !== "string" ||
|
|
160
|
+
!state.registration_id ||
|
|
161
|
+
!Number.isSafeInteger(state.gate_pid) ||
|
|
162
|
+
state.gate_pid < 1 ||
|
|
163
|
+
typeof state.gate_start_token !== "string" ||
|
|
164
|
+
!state.gate_start_token)
|
|
165
|
+
return "unknown";
|
|
166
|
+
const targets = [{ pid: state.gate_pid, token: state.gate_start_token }];
|
|
167
|
+
if (state.provider === "tailscale") {
|
|
168
|
+
if (state.provider_state !== "ready" ||
|
|
169
|
+
state.cloudflared_pid !== undefined ||
|
|
170
|
+
state.cloudflared_start_token !== undefined)
|
|
171
|
+
return "unknown";
|
|
172
|
+
}
|
|
173
|
+
else if (state.provider === "cloudflare") {
|
|
174
|
+
if (!Number.isSafeInteger(state.cloudflared_pid) ||
|
|
175
|
+
state.cloudflared_pid < 1 ||
|
|
176
|
+
typeof state.cloudflared_start_token !== "string" ||
|
|
177
|
+
!state.cloudflared_start_token)
|
|
178
|
+
return "unknown";
|
|
179
|
+
targets.push({ pid: state.cloudflared_pid, token: state.cloudflared_start_token });
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
return "unknown";
|
|
183
|
+
try {
|
|
184
|
+
return verifier.withRegistration(state, () => {
|
|
185
|
+
const observed = targets.map(({ pid, token }) => verifier.observeProcess(pid, token));
|
|
186
|
+
if (observed.every((value) => value === "absent"))
|
|
187
|
+
return "dead";
|
|
188
|
+
if (observed.every((value) => value === "same"))
|
|
189
|
+
return "ready";
|
|
190
|
+
return "unknown";
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return "unknown";
|
|
195
|
+
}
|
|
150
196
|
}
|
|
151
197
|
export function tunnelTargetPort(state) {
|
|
152
198
|
const value = /^[a-z][a-z\d+.-]*:\/\//i.test(state.target)
|
|
@@ -173,10 +219,10 @@ export function tunnelTargetPort(state) {
|
|
|
173
219
|
* link 400s. Matching the vhost too keeps the returned tunnel the one that
|
|
174
220
|
* actually serves the caller's origin.
|
|
175
221
|
*/
|
|
176
|
-
export function findLiveTunnelForOrigin(port, vhost, root
|
|
222
|
+
export function findLiveTunnelForOrigin(port, vhost, root, verifier) {
|
|
177
223
|
return (listStates(root).find((state) => tunnelTargetPort(state) === port &&
|
|
178
224
|
state.vhost === vhost &&
|
|
179
|
-
|
|
225
|
+
tunnelReadiness(state, verifier) === "ready") ?? null);
|
|
180
226
|
}
|
|
181
227
|
/** True when the tunnel's path scope forwards every one of `paths`. */
|
|
182
228
|
export function tunnelServesPaths(state, paths) {
|
|
@@ -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"}
|