agent-hitch 0.1.1 → 0.2.1
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 +235 -132
- package/README.zh-CN.md +335 -0
- package/dist/bin/hitch.js +9 -0
- package/dist/bin/hitch.js.map +1 -0
- package/dist/scripts/check-architecture.js +159 -0
- package/dist/scripts/check-architecture.js.map +1 -0
- package/dist/scripts/check-release.js +25 -0
- package/dist/scripts/check-release.js.map +1 -0
- package/dist/scripts/check-syntax.js +32 -0
- package/dist/scripts/check-syntax.js.map +1 -0
- package/dist/src/adapters/catalog.js +55 -0
- package/dist/src/adapters/catalog.js.map +1 -0
- package/dist/src/adapters/contract.js +2 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/discovery.js +22 -0
- package/dist/src/adapters/discovery.js.map +1 -0
- package/dist/src/adapters/index.js +3 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/adapters/providers/claude.js +69 -0
- package/dist/src/adapters/providers/claude.js.map +1 -0
- package/dist/src/adapters/providers/codex.js +74 -0
- package/dist/src/adapters/providers/codex.js.map +1 -0
- package/dist/src/adapters/providers/deepseek.js +51 -0
- package/dist/src/adapters/providers/deepseek.js.map +1 -0
- package/dist/src/adapters/providers/opencode.js +73 -0
- package/dist/src/adapters/providers/opencode.js.map +1 -0
- package/dist/src/adapters/providers/pi.js +82 -0
- package/dist/src/adapters/providers/pi.js.map +1 -0
- package/dist/src/adapters/providers/shared.js +82 -0
- package/dist/src/adapters/providers/shared.js.map +1 -0
- package/dist/src/artifacts/index.js +3 -0
- package/dist/src/artifacts/index.js.map +1 -0
- package/dist/src/artifacts/integrity.js +188 -0
- package/dist/src/artifacts/integrity.js.map +1 -0
- package/dist/src/artifacts/preparer.js +379 -0
- package/dist/src/artifacts/preparer.js.map +1 -0
- package/dist/src/artifacts/store.js +87 -0
- package/dist/src/artifacts/store.js.map +1 -0
- package/dist/src/artifacts/types.js +2 -0
- package/dist/src/artifacts/types.js.map +1 -0
- package/dist/src/backends/contract.js +2 -0
- package/dist/src/backends/contract.js.map +1 -0
- package/dist/src/backends/harbor/backend.js +305 -0
- package/dist/src/backends/harbor/backend.js.map +1 -0
- package/dist/src/backends/harbor/index.js +4 -0
- package/dist/src/backends/harbor/index.js.map +1 -0
- package/dist/src/backends/harbor/local-git-transport.js +447 -0
- package/dist/src/backends/harbor/local-git-transport.js.map +1 -0
- package/dist/src/backends/harbor/tools.js +314 -0
- package/dist/src/backends/harbor/tools.js.map +1 -0
- package/dist/src/backends/index.js +4 -0
- package/dist/src/backends/index.js.map +1 -0
- package/dist/src/cli/arguments.js +123 -0
- package/dist/src/cli/arguments.js.map +1 -0
- package/dist/src/cli/commands/compare.js +55 -0
- package/dist/src/cli/commands/compare.js.map +1 -0
- package/dist/src/cli/commands/daemon.js +106 -0
- package/dist/src/cli/commands/daemon.js.map +1 -0
- package/dist/src/cli/commands/eval.js +126 -0
- package/dist/src/cli/commands/eval.js.map +1 -0
- package/dist/src/cli/commands/feedback.js +84 -0
- package/dist/src/cli/commands/feedback.js.map +1 -0
- package/dist/src/cli/commands/inspect.js +20 -0
- package/dist/src/cli/commands/inspect.js.map +1 -0
- package/dist/src/cli/commands/list.js +17 -0
- package/dist/src/cli/commands/list.js.map +1 -0
- package/dist/src/cli/commands/prepare.js +21 -0
- package/dist/src/cli/commands/prepare.js.map +1 -0
- package/dist/src/cli/commands/resolve.js +17 -0
- package/dist/src/cli/commands/resolve.js.map +1 -0
- package/dist/src/cli/commands/run.js +83 -0
- package/dist/src/cli/commands/run.js.map +1 -0
- package/dist/src/cli/commands/runs.js +45 -0
- package/dist/src/cli/commands/runs.js.map +1 -0
- package/dist/src/cli/commands/trajectory.js +31 -0
- package/dist/src/cli/commands/trajectory.js.map +1 -0
- package/dist/src/cli/commands/workspace.js +44 -0
- package/dist/src/cli/commands/workspace.js.map +1 -0
- package/dist/src/cli/index.js +2 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/main.js +48 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/output.js +96 -0
- package/dist/src/cli/output.js.map +1 -0
- package/dist/src/controller-runtime/hash.js +353 -0
- package/dist/src/controller-runtime/hash.js.map +1 -0
- package/dist/src/controller-runtime/index.js +3 -0
- package/dist/src/controller-runtime/index.js.map +1 -0
- package/dist/src/controller-runtime/store.js +318 -0
- package/dist/src/controller-runtime/store.js.map +1 -0
- package/dist/src/daemon/auth.js +108 -0
- package/dist/src/daemon/auth.js.map +1 -0
- package/dist/src/daemon/client.js +76 -0
- package/dist/src/daemon/client.js.map +1 -0
- package/dist/src/daemon/index.js +5 -0
- package/dist/src/daemon/index.js.map +1 -0
- package/dist/src/daemon/launcher.js +24 -0
- package/dist/src/daemon/launcher.js.map +1 -0
- package/dist/src/daemon/scheduler.js +161 -0
- package/dist/src/daemon/scheduler.js.map +1 -0
- package/dist/src/daemon/server.js +316 -0
- package/dist/src/daemon/server.js.map +1 -0
- package/dist/src/domain/artifacts.js +2 -0
- package/dist/src/domain/artifacts.js.map +1 -0
- package/dist/src/domain/controller-runtime.js +2 -0
- package/dist/src/domain/controller-runtime.js.map +1 -0
- package/dist/src/domain/eval-records.js +2 -0
- package/dist/src/domain/eval-records.js.map +1 -0
- package/dist/src/domain/evals.js +2 -0
- package/dist/src/domain/evals.js.map +1 -0
- package/dist/src/domain/feedback.js +2 -0
- package/dist/src/domain/feedback.js.map +1 -0
- package/dist/src/domain/ids.js +10 -0
- package/dist/src/domain/ids.js.map +1 -0
- package/dist/src/domain/index.js +11 -0
- package/dist/src/domain/index.js.map +1 -0
- package/dist/src/domain/revisions.js +2 -0
- package/dist/src/domain/revisions.js.map +1 -0
- package/dist/src/domain/runs.js +3 -0
- package/dist/src/domain/runs.js.map +1 -0
- package/dist/src/domain/trajectories.js +2 -0
- package/dist/src/domain/trajectories.js.map +1 -0
- package/dist/src/domain/validation.js +429 -0
- package/dist/src/domain/validation.js.map +1 -0
- package/dist/src/domain/workspaces.js +2 -0
- package/dist/src/domain/workspaces.js.map +1 -0
- package/dist/src/evals/events.js +67 -0
- package/dist/src/evals/events.js.map +1 -0
- package/dist/src/evals/index.js +5 -0
- package/dist/src/evals/index.js.map +1 -0
- package/dist/src/evals/records.js +55 -0
- package/dist/src/evals/records.js.map +1 -0
- package/dist/src/evals/request.js +109 -0
- package/dist/src/evals/request.js.map +1 -0
- package/dist/src/evals/service.js +227 -0
- package/dist/src/evals/service.js.map +1 -0
- package/dist/src/evals/trial-import.js +394 -0
- package/dist/src/evals/trial-import.js.map +1 -0
- package/dist/src/feedback/index.js +2 -0
- package/dist/src/feedback/index.js.map +1 -0
- package/dist/src/feedback/service.js +314 -0
- package/dist/src/feedback/service.js.map +1 -0
- package/dist/src/foundation/config.js +54 -0
- package/dist/src/foundation/config.js.map +1 -0
- package/dist/src/foundation/errors.js +14 -0
- package/dist/src/foundation/errors.js.map +1 -0
- package/dist/src/foundation/executable.js +135 -0
- package/dist/src/foundation/executable.js.map +1 -0
- package/dist/src/foundation/fs.js +45 -0
- package/dist/src/foundation/fs.js.map +1 -0
- package/dist/src/foundation/hash.js +24 -0
- package/dist/src/foundation/hash.js.map +1 -0
- package/dist/src/foundation/index.js +11 -0
- package/dist/src/foundation/index.js.map +1 -0
- package/dist/src/foundation/line-stream.js +20 -0
- package/dist/src/foundation/line-stream.js.map +1 -0
- package/dist/src/foundation/locks.js +114 -0
- package/dist/src/foundation/locks.js.map +1 -0
- package/dist/src/foundation/package-root.js +41 -0
- package/dist/src/foundation/package-root.js.map +1 -0
- package/dist/src/foundation/process.js +50 -0
- package/dist/src/foundation/process.js.map +1 -0
- package/dist/src/revisions/index.js +4 -0
- package/dist/src/revisions/index.js.map +1 -0
- package/dist/src/revisions/reference.js +97 -0
- package/dist/src/revisions/reference.js.map +1 -0
- package/dist/src/revisions/resolver.js +25 -0
- package/dist/src/revisions/resolver.js.map +1 -0
- package/dist/src/revisions/sources/git.js +180 -0
- package/dist/src/revisions/sources/git.js.map +1 -0
- package/dist/src/revisions/sources/installed.js +45 -0
- package/dist/src/revisions/sources/installed.js.map +1 -0
- package/dist/src/revisions/sources/npm.js +97 -0
- package/dist/src/revisions/sources/npm.js.map +1 -0
- package/dist/src/runs/compare.js +132 -0
- package/dist/src/runs/compare.js.map +1 -0
- package/dist/src/runs/events.js +79 -0
- package/dist/src/runs/events.js.map +1 -0
- package/dist/src/runs/executor.js +480 -0
- package/dist/src/runs/executor.js.map +1 -0
- package/dist/src/runs/finalizer.js +22 -0
- package/dist/src/runs/finalizer.js.map +1 -0
- package/dist/src/runs/identity.js +35 -0
- package/dist/src/runs/identity.js.map +1 -0
- package/dist/src/runs/index.js +11 -0
- package/dist/src/runs/index.js.map +1 -0
- package/dist/src/runs/manifest.js +70 -0
- package/dist/src/runs/manifest.js.map +1 -0
- package/dist/src/runs/outcome.js +49 -0
- package/dist/src/runs/outcome.js.map +1 -0
- package/dist/src/runs/query.js +104 -0
- package/dist/src/runs/query.js.map +1 -0
- package/dist/src/runs/queued.js +54 -0
- package/dist/src/runs/queued.js.map +1 -0
- package/dist/src/runs/records.js +288 -0
- package/dist/src/runs/records.js.map +1 -0
- package/dist/src/runs/request.js +169 -0
- package/dist/src/runs/request.js.map +1 -0
- package/dist/src/trajectories/contract.js +17 -0
- package/dist/src/trajectories/contract.js.map +1 -0
- package/dist/src/trajectories/format.js +127 -0
- package/dist/src/trajectories/format.js.map +1 -0
- package/dist/src/trajectories/index.js +7 -0
- package/dist/src/trajectories/index.js.map +1 -0
- package/dist/src/trajectories/projector.js +385 -0
- package/dist/src/trajectories/projector.js.map +1 -0
- package/dist/src/trajectories/provider-capture.js +133 -0
- package/dist/src/trajectories/provider-capture.js.map +1 -0
- package/dist/src/trajectories/providers/deepseek.js +179 -0
- package/dist/src/trajectories/providers/deepseek.js.map +1 -0
- package/dist/src/trajectories/store.js +421 -0
- package/dist/src/trajectories/store.js.map +1 -0
- package/dist/src/workspaces/copy.js +142 -0
- package/dist/src/workspaces/copy.js.map +1 -0
- package/dist/src/workspaces/digest.js +47 -0
- package/dist/src/workspaces/digest.js.map +1 -0
- package/dist/src/workspaces/git.js +164 -0
- package/dist/src/workspaces/git.js.map +1 -0
- package/dist/src/workspaces/index.js +6 -0
- package/dist/src/workspaces/index.js.map +1 -0
- package/dist/src/workspaces/lifecycle.js +313 -0
- package/dist/src/workspaces/lifecycle.js.map +1 -0
- package/dist/src/workspaces/planner.js +80 -0
- package/dist/src/workspaces/planner.js.map +1 -0
- package/dist/src/workspaces/store.js +25 -0
- package/dist/src/workspaces/store.js.map +1 -0
- package/dist/src/workspaces/types.js +4 -0
- package/dist/src/workspaces/types.js.map +1 -0
- package/dist/src/workspaces/utils.js +56 -0
- package/dist/src/workspaces/utils.js.map +1 -0
- package/docs/schemas/controller-runtime-manifest.schema.json +57 -0
- package/docs/schemas/controller-runtime-ref.schema.json +16 -0
- package/docs/schemas/eval-request.schema.json +2 -0
- package/docs/schemas/eval-result.schema.json +36 -1
- package/docs/schemas/local-git-transport-manifest.schema.json +34 -0
- package/docs/schemas/message-feedback.schema.json +37 -0
- package/docs/schemas/result.schema.json +9 -0
- package/docs/schemas/run-context.schema.json +39 -0
- package/docs/schemas/run-manifest.schema.json +81 -0
- package/docs/schemas/run-request.schema.json +16 -1
- package/docs/schemas/trajectory-ref.schema.json +73 -0
- package/integrations/harbor/hitch_harbor_agent.py +551 -5
- package/package.json +37 -11
- package/bin/hitch.js +0 -9
- package/src/adapters.js +0 -435
- package/src/artifacts.js +0 -949
- package/src/cli.js +0 -505
- package/src/config.js +0 -51
- package/src/daemon.js +0 -416
- package/src/engine.js +0 -400
- package/src/errors.js +0 -12
- package/src/eval-tools.js +0 -334
- package/src/evals.js +0 -276
- package/src/events.js +0 -69
- package/src/fs.js +0 -43
- package/src/harbor-backend.js +0 -285
- package/src/harness-reference.js +0 -78
- package/src/line-stream.js +0 -17
- package/src/locks.js +0 -33
- package/src/process.js +0 -49
- package/src/registry.js +0 -84
- package/src/scheduler.js +0 -156
- package/src/workspaces.js +0 -893
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { chmod, mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { HitchError, atomicWriteJSON, invalidInput, readJSON, statePaths } from "../foundation/index.js";
|
|
4
|
+
import { prepareCopy } from "./copy.js";
|
|
5
|
+
import { workspaceDigest } from "./digest.js";
|
|
6
|
+
import { isolatedGitMetadataDigest, prepareWorktree, registeredWorktree, runGit, tryGitOutput } from "./git.js";
|
|
7
|
+
import { withWorkspaceLock, workspaceRecordPath } from "./store.js";
|
|
8
|
+
import { ACTIVE_WORKSPACE_STATES } from "./types.js";
|
|
9
|
+
import { assertRunId, canonicalizePotentialPath, isWithin, pathExists, throwIfAborted } from "./utils.js";
|
|
10
|
+
export async function prepareWorkspace(plan, { recordPath, signal } = {}) {
|
|
11
|
+
if (plan.mode === "shared") {
|
|
12
|
+
const ready = {
|
|
13
|
+
...plan,
|
|
14
|
+
status: "ready",
|
|
15
|
+
prepared_at: new Date().toISOString(),
|
|
16
|
+
};
|
|
17
|
+
if (recordPath)
|
|
18
|
+
await atomicWriteJSON(recordPath, ready);
|
|
19
|
+
return ready;
|
|
20
|
+
}
|
|
21
|
+
const provisioning = { ...plan, status: "provisioning", provisioning_at: new Date().toISOString() };
|
|
22
|
+
if (recordPath)
|
|
23
|
+
await atomicWriteJSON(recordPath, provisioning);
|
|
24
|
+
let ownsManagedDirectory = false;
|
|
25
|
+
try {
|
|
26
|
+
throwIfAborted(signal);
|
|
27
|
+
await assertManagedPlan(plan);
|
|
28
|
+
await mkdir(path.dirname(plan.managed_directory), { recursive: true, mode: 0o700 });
|
|
29
|
+
await chmod(path.dirname(plan.managed_directory), 0o700);
|
|
30
|
+
if (await pathExists(plan.managed_directory)) {
|
|
31
|
+
throw new HitchError(`managed workspace already exists for ${plan.run_id}`, {
|
|
32
|
+
code: "workspace_already_exists",
|
|
33
|
+
exitCode: 11,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
await mkdir(plan.managed_directory, { mode: 0o700 });
|
|
37
|
+
ownsManagedDirectory = true;
|
|
38
|
+
const preparation = plan.mode === "worktree"
|
|
39
|
+
? await prepareWorktree(plan, { signal })
|
|
40
|
+
: await prepareCopy(plan, { signal });
|
|
41
|
+
await chmod(plan.managed_directory, 0o700);
|
|
42
|
+
await chmod(plan.execution_root, 0o700);
|
|
43
|
+
const baselineDigest = preparation?.baseline_digest || await workspaceDigest(plan.execution_root, {
|
|
44
|
+
signal,
|
|
45
|
+
excludedTopLevel: gitMetadataExclusion(plan),
|
|
46
|
+
});
|
|
47
|
+
const baselineGitMetadataDigest = plan.mode === "copy" && plan.git
|
|
48
|
+
? await isolatedGitMetadataDigest(plan.execution_root, { signal })
|
|
49
|
+
: null;
|
|
50
|
+
const preparedAt = new Date().toISOString();
|
|
51
|
+
const ready = {
|
|
52
|
+
...provisioning,
|
|
53
|
+
status: "ready",
|
|
54
|
+
baseline_digest: baselineDigest,
|
|
55
|
+
baseline_git_metadata_digest: baselineGitMetadataDigest,
|
|
56
|
+
snapshot: { ...plan.snapshot, content_digest: baselineDigest, captured_at: preparedAt },
|
|
57
|
+
retained: true,
|
|
58
|
+
prepared_at: preparedAt,
|
|
59
|
+
};
|
|
60
|
+
if (recordPath)
|
|
61
|
+
await atomicWriteJSON(recordPath, ready);
|
|
62
|
+
return ready;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (ownsManagedDirectory)
|
|
66
|
+
await cleanupProvisioning(plan).catch(() => { });
|
|
67
|
+
const errno = error;
|
|
68
|
+
const workspaceError = errno?.code === "EEXIST"
|
|
69
|
+
? new HitchError(`managed workspace already exists for ${plan.run_id}`, {
|
|
70
|
+
code: "workspace_already_exists",
|
|
71
|
+
exitCode: 11,
|
|
72
|
+
cause: error,
|
|
73
|
+
})
|
|
74
|
+
: error;
|
|
75
|
+
const hitchWorkspaceError = workspaceError;
|
|
76
|
+
const failed = {
|
|
77
|
+
...provisioning,
|
|
78
|
+
status: workspaceError instanceof Error && workspaceError.name === "HitchError" && hitchWorkspaceError.code === "cancelled" ? "cancelled" : "failed",
|
|
79
|
+
retained: false,
|
|
80
|
+
error: { code: hitchWorkspaceError.code || "workspace_provision_failed", message: workspaceError?.message || String(workspaceError) },
|
|
81
|
+
completed_at: new Date().toISOString(),
|
|
82
|
+
};
|
|
83
|
+
if (recordPath)
|
|
84
|
+
await atomicWriteJSON(recordPath, failed);
|
|
85
|
+
if (workspaceError instanceof HitchError)
|
|
86
|
+
throw workspaceError;
|
|
87
|
+
throw new HitchError(`failed to provision isolated workspace: ${workspaceError.message}`, {
|
|
88
|
+
code: "workspace_provision_failed",
|
|
89
|
+
exitCode: 5,
|
|
90
|
+
cause: workspaceError,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export async function markWorkspaceRunning(workspace, { recordPath } = {}) {
|
|
95
|
+
const running = { ...workspace, status: "running", started_at: new Date().toISOString() };
|
|
96
|
+
if (recordPath)
|
|
97
|
+
await atomicWriteJSON(recordPath, running);
|
|
98
|
+
return running;
|
|
99
|
+
}
|
|
100
|
+
export async function finalizeWorkspace(workspace, { recordPath } = {}) {
|
|
101
|
+
if (!workspace)
|
|
102
|
+
return null;
|
|
103
|
+
if (workspace.mode === "shared") {
|
|
104
|
+
const released = {
|
|
105
|
+
...workspace,
|
|
106
|
+
status: "released",
|
|
107
|
+
retained: false,
|
|
108
|
+
changed: null,
|
|
109
|
+
finalized_at: new Date().toISOString(),
|
|
110
|
+
};
|
|
111
|
+
if (recordPath)
|
|
112
|
+
await atomicWriteJSON(recordPath, released);
|
|
113
|
+
return released;
|
|
114
|
+
}
|
|
115
|
+
const finalDigest = await workspaceDigest(workspace.execution_root, {
|
|
116
|
+
excludedTopLevel: gitMetadataExclusion(workspace),
|
|
117
|
+
});
|
|
118
|
+
let headAfter = null;
|
|
119
|
+
let gitStatus = null;
|
|
120
|
+
let finalGitMetadataDigest = null;
|
|
121
|
+
if (workspace.git) {
|
|
122
|
+
headAfter = await tryGitOutput(["-C", workspace.execution_root, "rev-parse", "--verify", "HEAD^{commit}"]);
|
|
123
|
+
gitStatus = await tryGitOutput(["-C", workspace.execution_root, "status", "--porcelain=v1", "--untracked-files=all"]);
|
|
124
|
+
if (workspace.mode === "copy")
|
|
125
|
+
finalGitMetadataDigest = await isolatedGitMetadataDigest(workspace.execution_root);
|
|
126
|
+
}
|
|
127
|
+
const gitStateChanged = workspace.git
|
|
128
|
+
? gitStatus === null
|
|
129
|
+
|| (workspace.git.head ? !headAfter || headAfter !== workspace.git.head : Boolean(headAfter))
|
|
130
|
+
|| (workspace.mode === "copy" && finalGitMetadataDigest !== workspace.baseline_git_metadata_digest)
|
|
131
|
+
: false;
|
|
132
|
+
const retained = {
|
|
133
|
+
...workspace,
|
|
134
|
+
status: "retained",
|
|
135
|
+
retained: true,
|
|
136
|
+
changed: finalDigest !== workspace.baseline_digest || gitStateChanged,
|
|
137
|
+
final_digest: finalDigest,
|
|
138
|
+
...(finalGitMetadataDigest ? { final_git_metadata_digest: finalGitMetadataDigest } : {}),
|
|
139
|
+
...(headAfter ? { head_after: headAfter } : {}),
|
|
140
|
+
...(gitStatus !== null ? { git_dirty: Boolean(gitStatus) } : {}),
|
|
141
|
+
finalized_at: new Date().toISOString(),
|
|
142
|
+
};
|
|
143
|
+
if (recordPath)
|
|
144
|
+
await atomicWriteJSON(recordPath, retained);
|
|
145
|
+
return retained;
|
|
146
|
+
}
|
|
147
|
+
export async function markWorkspaceFinalizationFailed(workspace, { recordPath, warning } = {}) {
|
|
148
|
+
if (!workspace)
|
|
149
|
+
return null;
|
|
150
|
+
const terminal = {
|
|
151
|
+
...workspace,
|
|
152
|
+
status: workspace.mode === "shared" ? "released" : "orphaned",
|
|
153
|
+
retained: workspace.mode !== "shared",
|
|
154
|
+
changed: null,
|
|
155
|
+
warning: warning || {
|
|
156
|
+
code: "workspace_finalization_failed",
|
|
157
|
+
message: "workspace finalization failed",
|
|
158
|
+
},
|
|
159
|
+
finalized_at: new Date().toISOString(),
|
|
160
|
+
};
|
|
161
|
+
if (recordPath)
|
|
162
|
+
await atomicWriteJSON(recordPath, terminal);
|
|
163
|
+
return terminal;
|
|
164
|
+
}
|
|
165
|
+
export async function cancelPlannedWorkspace({ root, runId }) {
|
|
166
|
+
return abandonPlannedWorkspace({ root, runId, status: "cancelled" });
|
|
167
|
+
}
|
|
168
|
+
export async function abandonPlannedWorkspace({ root, runId, status = "unused" }) {
|
|
169
|
+
const recordPath = workspaceRecordPath(root, runId);
|
|
170
|
+
const workspace = await readJSON(recordPath, null);
|
|
171
|
+
if (!workspace || workspace.status !== "planned")
|
|
172
|
+
return workspace;
|
|
173
|
+
const completed = { ...workspace, status, completed_at: new Date().toISOString() };
|
|
174
|
+
await atomicWriteJSON(recordPath, completed);
|
|
175
|
+
return completed;
|
|
176
|
+
}
|
|
177
|
+
export async function recoverInterruptedWorkspace({ root, runId }) {
|
|
178
|
+
const recordPath = workspaceRecordPath(root, runId);
|
|
179
|
+
const workspace = await readJSON(recordPath, null);
|
|
180
|
+
if (!workspace)
|
|
181
|
+
return null;
|
|
182
|
+
if (workspace.status === "provisioning") {
|
|
183
|
+
await cleanupProvisioning(workspace).catch(() => { });
|
|
184
|
+
const failed = {
|
|
185
|
+
...workspace,
|
|
186
|
+
status: "failed",
|
|
187
|
+
retained: false,
|
|
188
|
+
error: { code: "daemon_restarted", message: "daemon stopped while provisioning the workspace" },
|
|
189
|
+
completed_at: new Date().toISOString(),
|
|
190
|
+
};
|
|
191
|
+
await atomicWriteJSON(recordPath, failed);
|
|
192
|
+
return failed;
|
|
193
|
+
}
|
|
194
|
+
if (["ready", "running"].includes(workspace.status)) {
|
|
195
|
+
const orphaned = {
|
|
196
|
+
...workspace,
|
|
197
|
+
status: workspace.mode === "shared" ? "released" : "orphaned",
|
|
198
|
+
retained: workspace.mode !== "shared",
|
|
199
|
+
completed_at: new Date().toISOString(),
|
|
200
|
+
};
|
|
201
|
+
await atomicWriteJSON(recordPath, orphaned);
|
|
202
|
+
return orphaned;
|
|
203
|
+
}
|
|
204
|
+
if (workspace.status === "planned")
|
|
205
|
+
return cancelPlannedWorkspace({ root, runId });
|
|
206
|
+
return workspace;
|
|
207
|
+
}
|
|
208
|
+
export async function inspectWorkspace({ root, runId }) {
|
|
209
|
+
assertRunId(runId);
|
|
210
|
+
return readJSON(workspaceRecordPath(root, runId), null);
|
|
211
|
+
}
|
|
212
|
+
export async function removeWorkspace({ root, runId, force = false }) {
|
|
213
|
+
assertRunId(runId);
|
|
214
|
+
const recordPath = workspaceRecordPath(root, runId);
|
|
215
|
+
const workspace = await readJSON(recordPath, null);
|
|
216
|
+
if (!workspace) {
|
|
217
|
+
throw new HitchError(`workspace record not found: ${runId}`, { code: "workspace_not_found", exitCode: 3 });
|
|
218
|
+
}
|
|
219
|
+
if (workspace.mode === "shared") {
|
|
220
|
+
throw invalidInput(`run ${runId} used a shared workspace that Hitch does not own`);
|
|
221
|
+
}
|
|
222
|
+
if (ACTIVE_WORKSPACE_STATES.has(workspace.status)) {
|
|
223
|
+
throw new HitchError(`workspace is still in use by ${runId}`, { code: "workspace_in_use", exitCode: 11 });
|
|
224
|
+
}
|
|
225
|
+
if (!force && workspace.changed !== false) {
|
|
226
|
+
throw new HitchError("workspace may contain changes; pass --force to remove it", {
|
|
227
|
+
code: "workspace_has_changes",
|
|
228
|
+
exitCode: 11,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const expectedDirectory = path.join(await canonicalizePotentialPath(statePaths(root).workspaces), runId);
|
|
232
|
+
const expectedExecutionRoot = path.join(expectedDirectory, "root");
|
|
233
|
+
const recordedDirectory = workspace.managed_directory
|
|
234
|
+
? await canonicalizePotentialPath(workspace.managed_directory)
|
|
235
|
+
: null;
|
|
236
|
+
const recordedExecutionRoot = workspace.execution_root
|
|
237
|
+
? await canonicalizePotentialPath(workspace.execution_root)
|
|
238
|
+
: null;
|
|
239
|
+
if (!recordedDirectory
|
|
240
|
+
|| !recordedExecutionRoot
|
|
241
|
+
|| path.resolve(recordedDirectory) !== path.resolve(expectedDirectory)
|
|
242
|
+
|| path.resolve(recordedExecutionRoot) !== path.resolve(expectedExecutionRoot)) {
|
|
243
|
+
throw new HitchError("workspace record points outside the managed workspace root", {
|
|
244
|
+
code: "workspace_cleanup_rejected",
|
|
245
|
+
exitCode: 11,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (workspace.mode === "worktree" && workspace.git?.common_dir) {
|
|
249
|
+
const git = workspace.git;
|
|
250
|
+
await withWorkspaceLock(root, git.common_dir, async () => {
|
|
251
|
+
if (await pathExists(git.common_dir) && await registeredWorktree(git.common_dir, workspace.execution_root)) {
|
|
252
|
+
await runGit(["--git-dir", git.common_dir, "worktree", "remove", "--force", workspace.execution_root], {
|
|
253
|
+
failureCode: "workspace_cleanup_failed",
|
|
254
|
+
failureExitCode: 12,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
await rm(expectedDirectory, { recursive: true, force: true });
|
|
260
|
+
const removed = {
|
|
261
|
+
...workspace,
|
|
262
|
+
status: "removed",
|
|
263
|
+
retained: false,
|
|
264
|
+
removed_at: new Date().toISOString(),
|
|
265
|
+
};
|
|
266
|
+
await atomicWriteJSON(recordPath, removed);
|
|
267
|
+
return removed;
|
|
268
|
+
}
|
|
269
|
+
async function cleanupProvisioning(workspace) {
|
|
270
|
+
await assertManagedPlan(workspace);
|
|
271
|
+
const git = workspace.git;
|
|
272
|
+
if (workspace.mode === "worktree"
|
|
273
|
+
&& git?.common_dir
|
|
274
|
+
&& await pathExists(git.common_dir)
|
|
275
|
+
&& await registeredWorktree(git.common_dir, workspace.execution_root)) {
|
|
276
|
+
await withWorkspaceLock(workspace.state_root, git.common_dir, () => runGit([
|
|
277
|
+
"--git-dir",
|
|
278
|
+
git.common_dir,
|
|
279
|
+
"worktree",
|
|
280
|
+
"remove",
|
|
281
|
+
"--force",
|
|
282
|
+
workspace.execution_root,
|
|
283
|
+
], {
|
|
284
|
+
failureCode: "workspace_cleanup_failed",
|
|
285
|
+
failureExitCode: 12,
|
|
286
|
+
})).catch(() => { });
|
|
287
|
+
}
|
|
288
|
+
if (workspace.managed_directory)
|
|
289
|
+
await rm(workspace.managed_directory, { recursive: true, force: true });
|
|
290
|
+
}
|
|
291
|
+
async function assertManagedPlan(workspace) {
|
|
292
|
+
assertRunId(workspace.run_id);
|
|
293
|
+
if (!workspace.state_root) {
|
|
294
|
+
throw new HitchError("workspace plan is missing its state root", {
|
|
295
|
+
code: "workspace_cleanup_rejected",
|
|
296
|
+
exitCode: 11,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
const expectedDirectory = path.join(await canonicalizePotentialPath(statePaths(workspace.state_root).workspaces), workspace.run_id);
|
|
300
|
+
const expectedRoot = path.join(expectedDirectory, "root");
|
|
301
|
+
if (path.resolve(workspace.managed_directory || "") !== path.resolve(expectedDirectory)
|
|
302
|
+
|| path.resolve(workspace.execution_root || "") !== path.resolve(expectedRoot)
|
|
303
|
+
|| !isWithin(expectedRoot, path.resolve(workspace.execution_workspace || ""))) {
|
|
304
|
+
throw new HitchError("workspace plan points outside the managed workspace root", {
|
|
305
|
+
code: "workspace_cleanup_rejected",
|
|
306
|
+
exitCode: 11,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function gitMetadataExclusion(workspace) {
|
|
311
|
+
return workspace.git ? new Set([".git"]) : new Set();
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../src/workspaces/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzG,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAChH,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,yBAAyB,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1G,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAmB,EAAE,EAAE,UAAU,EAAE,MAAM,KAAgE,EAAE;IAChJ,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAkB;YAC3B,GAAG,IAAI;YACP,MAAM,EAAE,OAAO;YACf,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACtC,CAAC;QACF,IAAI,UAAU;YAAE,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,YAAY,GAAkB,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IACnH,IAAI,UAAU;QAAE,MAAM,eAAe,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAChE,IAAI,oBAAoB,GAAG,KAAK,CAAC;IAEjC,IAAI,CAAC;QACH,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,iBAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9F,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,iBAA2B,CAAC,EAAE,KAAK,CAAC,CAAC;QACnE,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,iBAA2B,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,UAAU,CAAC,wCAAwC,IAAI,CAAC,MAAM,EAAE,EAAE;gBAC1E,IAAI,EAAE,0BAA0B;gBAChC,QAAQ,EAAE,EAAE;aACb,CAAC,CAAC;QACL,CAAC;QACD,MAAM,KAAK,CAAC,IAAI,CAAC,iBAA2B,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,oBAAoB,GAAG,IAAI,CAAC;QAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,KAAK,UAAU;YAC1C,CAAC,CAAC,MAAM,eAAe,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;YACzC,CAAC,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,CAAC,IAAI,CAAC,iBAA2B,EAAE,KAAK,CAAC,CAAC;QACrD,MAAM,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,cAAc,GAAG,WAAW,EAAE,eAAe,IAAI,MAAM,eAAe,CAAC,IAAI,CAAC,cAAc,EAAE;YAChG,MAAM;YACN,gBAAgB,EAAE,oBAAoB,CAAC,IAAI,CAAC;SAC7C,CAAC,CAAC;QACH,MAAM,yBAAyB,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG;YAChE,CAAC,CAAC,MAAM,yBAAyB,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC;YAClE,CAAC,CAAC,IAAI,CAAC;QACT,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAkB;YAC3B,GAAG,YAAY;YACf,MAAM,EAAE,OAAO;YACf,eAAe,EAAE,cAAc;YAC/B,4BAA4B,EAAE,yBAAyB;YACvD,QAAQ,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE;YACvF,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,UAAU;SACxB,CAAC;QACF,IAAI,UAAU;YAAE,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,oBAAoB;YAAE,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAG,KAA8B,CAAC;QAC7C,MAAM,cAAc,GAAG,KAAK,EAAE,IAAI,KAAK,QAAQ;YAC7C,CAAC,CAAC,IAAI,UAAU,CAAC,wCAAwC,IAAI,CAAC,MAAM,EAAE,EAAE;gBACpE,IAAI,EAAE,0BAA0B;gBAChC,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,KAAK;aACb,CAAC;YACJ,CAAC,CAAC,KAAK,CAAC;QACV,MAAM,mBAAmB,GAAG,cAAqC,CAAC;QAClE,MAAM,MAAM,GAAkB;YAC5B,GAAG,YAAY;YACf,MAAM,EAAE,cAAc,YAAY,KAAK,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY,IAAI,mBAAmB,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;YACpJ,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,CAAC,IAAI,IAAI,4BAA4B,EAAE,OAAO,EAAG,cAAwB,EAAE,OAAO,IAAI,MAAM,CAAC,cAAc,CAAC,EAAE;YAChJ,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC,CAAC;QACF,IAAI,UAAU;YAAE,MAAM,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,cAAc,YAAY,UAAU;YAAE,MAAM,cAAc,CAAC;QAC/D,MAAM,IAAI,UAAU,CAAC,2CAA4C,cAAwB,CAAC,OAAO,EAAE,EAAE;YACnG,IAAI,EAAE,4BAA4B;YAClC,QAAQ,EAAE,CAAC;YACX,KAAK,EAAE,cAAc;SACtB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAwB,EAAE,EAAE,UAAU,KAA8B,EAAE;IAC/G,MAAM,OAAO,GAAkB,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IACzG,IAAI,UAAU;QAAE,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAA2C,EAAE,EAAE,UAAU,KAA8B,EAAE;IAC/H,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAkB;YAC9B,GAAG,SAAS;YACZ,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC,CAAC;QACF,IAAI,UAAU;YAAE,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC5D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,cAAc,EAAE;QAClE,gBAAgB,EAAE,oBAAoB,CAAC,SAAS,CAAC;KAClD,CAAC,CAAC;IACH,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,sBAAsB,GAAkB,IAAI,CAAC;IACjD,IAAI,SAAS,CAAC,GAAG,EAAE,CAAC;QAClB,SAAS,GAAG,MAAM,YAAY,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;QAC3G,SAAS,GAAG,MAAM,YAAY,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,uBAAuB,CAAC,CAAC,CAAC;QACtH,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM;YAAE,sBAAsB,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACpH,CAAC;IACD,MAAM,eAAe,GAAG,SAAS,CAAC,GAAG;QACnC,CAAC,CAAC,SAAS,KAAK,IAAI;eACf,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,KAAK,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;eAC1F,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,sBAAsB,KAAK,SAAS,CAAC,4BAA4B,CAAC;QACrG,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,QAAQ,GAAkB;QAC9B,GAAG,SAAS;QACZ,MAAM,EAAE,UAAU;QAClB,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,WAAW,KAAK,SAAS,CAAC,eAAe,IAAI,eAAe;QACrE,YAAY,EAAE,WAAW;QACzB,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IACF,IAAI,UAAU;QAAE,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,SAA2C,EAC3C,EAAE,UAAU,EAAE,OAAO,KAA2E,EAAE;IAElG,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,QAAQ,GAAkB;QAC9B,GAAG,SAAS;QACZ,MAAM,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;QAC7D,QAAQ,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ;QACrC,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,OAAO,IAAI;YAClB,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,+BAA+B;SACzC;QACD,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IACF,IAAI,UAAU;QAAE,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAkC;IAC1F,OAAO,uBAAuB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAmE;IAEnG,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAuB,UAAU,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACnE,MAAM,SAAS,GAAkB,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAClG,MAAM,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC7C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,EAAE,IAAI,EAAE,KAAK,EAAkC;IAC/F,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAuB,UAAU,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,SAAS,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACxC,MAAM,mBAAmB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrD,MAAM,MAAM,GAAkB;YAC5B,GAAG,SAAS;YACZ,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,iDAAiD,EAAE;YAC/F,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC,CAAC;QACF,MAAM,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1C,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAkB;YAC9B,GAAG,SAAS;YACZ,MAAM,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;YAC7D,QAAQ,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ;YACrC,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACvC,CAAC;QACF,MAAM,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,sBAAsB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAkC;IACpF,WAAW,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,QAAQ,CAAuB,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAmD;IACnH,WAAW,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAuB,UAAU,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAAC,+BAA+B,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,YAAY,CAAC,OAAO,KAAK,kDAAkD,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,uBAAuB,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,UAAU,CAAC,gCAAgC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,IAAI,UAAU,CAAC,0DAA0D,EAAE;YAC/E,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,yBAAyB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;IACzG,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,iBAAiB,GAAG,SAAS,CAAC,iBAAiB;QACnD,CAAC,CAAC,MAAM,yBAAyB,CAAC,SAAS,CAAC,iBAAiB,CAAC;QAC9D,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,qBAAqB,GAAG,SAAS,CAAC,cAAc;QACpD,CAAC,CAAC,MAAM,yBAAyB,CAAC,SAAS,CAAC,cAAc,CAAC;QAC3D,CAAC,CAAC,IAAI,CAAC;IACT,IACE,CAAC,iBAAiB;WACf,CAAC,qBAAqB;WACtB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;WACnE,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,EAC9E,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,4DAA4D,EAAE;YACjF,IAAI,EAAE,4BAA4B;YAClC,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IAED,IAAI,SAAS,CAAC,IAAI,KAAK,UAAU,IAAI,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC;QAC/D,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC;QAC1B,MAAM,iBAAiB,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACvD,IAAI,MAAM,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC3G,MAAM,MAAM,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,EAAE;oBACrG,WAAW,EAAE,0BAA0B;oBACvC,eAAe,EAAE,EAAE;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,EAAE,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAkB;QAC7B,GAAG,SAAS;QACZ,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IACF,MAAM,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,SAAwB;IACzD,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC;IAC1B,IACE,SAAS,CAAC,IAAI,KAAK,UAAU;WAC1B,GAAG,EAAE,UAAU;WACf,MAAM,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC;WAChC,MAAM,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,cAAc,CAAC,EACrE,CAAC;QACD,MAAM,iBAAiB,CAAC,SAAS,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC;YACzE,WAAW;YACX,GAAG,CAAC,UAAU;YACd,UAAU;YACV,QAAQ;YACR,SAAS;YACT,SAAS,CAAC,cAAc;SACzB,EAAE;YACD,WAAW,EAAE,0BAA0B;YACvC,eAAe,EAAE,EAAE;SACpB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,SAAS,CAAC,iBAAiB;QAAE,MAAM,EAAE,CAAC,SAAS,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3G,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,SAAwB;IACvD,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC1B,MAAM,IAAI,UAAU,CAAC,0CAA0C,EAAE;YAC/D,IAAI,EAAE,4BAA4B;YAClC,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IACD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,yBAAyB,CAAC,UAAU,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACpI,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC1D,IACE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,iBAAiB,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;WAChF,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;WAC3E,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,EAC7E,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,0DAA0D,EAAE;YAC/E,IAAI,EAAE,4BAA4B;YAClC,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,SAAwB;IACpD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { realpath, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { HitchError, SCHEMA_VERSION, atomicWriteJSON, invalidInput, statePaths } from "../foundation/index.js";
|
|
4
|
+
import { inspectGitWorkspace } from "./git.js";
|
|
5
|
+
import { WORKSPACE_MODES } from "./types.js";
|
|
6
|
+
import { assertNoWorkspaceOverlap, assertRunId, canonicalizePotentialPath, isWithin } from "./utils.js";
|
|
7
|
+
export async function planWorkspace({ runId, sourceCwd, mode = "shared", root, recordPath }) {
|
|
8
|
+
assertRunId(runId);
|
|
9
|
+
if (!WORKSPACE_MODES.has(mode)) {
|
|
10
|
+
throw invalidInput(`workspace_mode must be one of: ${[...WORKSPACE_MODES].join(", ")}`);
|
|
11
|
+
}
|
|
12
|
+
let sourceRealPath;
|
|
13
|
+
let sourceInfo;
|
|
14
|
+
try {
|
|
15
|
+
sourceRealPath = await realpath(sourceCwd);
|
|
16
|
+
sourceInfo = await stat(sourceRealPath);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw invalidInput(`workspace does not exist: ${sourceCwd}`, { cause: error });
|
|
20
|
+
}
|
|
21
|
+
if (!sourceInfo.isDirectory())
|
|
22
|
+
throw invalidInput(`workspace is not a directory: ${sourceCwd}`);
|
|
23
|
+
const paths = statePaths(root);
|
|
24
|
+
const managedDirectory = path.join(await canonicalizePotentialPath(paths.workspaces), runId);
|
|
25
|
+
let git = null;
|
|
26
|
+
let sourceBase = sourceRealPath;
|
|
27
|
+
let sourceSubdirectory = ".";
|
|
28
|
+
if (mode !== "shared") {
|
|
29
|
+
git = await inspectGitWorkspace(sourceRealPath, { required: mode === "worktree" });
|
|
30
|
+
if (git) {
|
|
31
|
+
sourceBase = git.repository_root;
|
|
32
|
+
sourceSubdirectory = path.relative(sourceBase, sourceRealPath) || ".";
|
|
33
|
+
}
|
|
34
|
+
const stateRoot = await canonicalizePotentialPath(paths.root);
|
|
35
|
+
if (isWithin(sourceBase, stateRoot)) {
|
|
36
|
+
throw new HitchError("the Hitch state root is inside the source workspace; use a --root outside the source repository", { code: "workspace_root_overlap", exitCode: 2 });
|
|
37
|
+
}
|
|
38
|
+
assertNoWorkspaceOverlap(sourceBase, managedDirectory);
|
|
39
|
+
}
|
|
40
|
+
if (mode === "worktree") {
|
|
41
|
+
if (git?.status) {
|
|
42
|
+
throw new HitchError("worktree isolation requires a clean Git workspace; use workspace_mode=copy to preserve local changes", { code: "workspace_dirty", exitCode: 2 });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const executionRoot = mode === "shared" ? sourceRealPath : path.join(managedDirectory, "root");
|
|
46
|
+
const executionWorkspace = sourceSubdirectory === "."
|
|
47
|
+
? executionRoot
|
|
48
|
+
: path.join(executionRoot, sourceSubdirectory);
|
|
49
|
+
const snapshot = mode === "worktree"
|
|
50
|
+
? { consistency: "git_commit", ...(git?.head ? { commit: git.head } : {}), source_subdirectory: sourceSubdirectory }
|
|
51
|
+
: mode === "copy"
|
|
52
|
+
? {
|
|
53
|
+
consistency: "best_effort",
|
|
54
|
+
...(git?.head ? { commit: git.head } : {}),
|
|
55
|
+
source_subdirectory: sourceSubdirectory,
|
|
56
|
+
}
|
|
57
|
+
: { consistency: "none", source_subdirectory: "." };
|
|
58
|
+
const plan = {
|
|
59
|
+
schema_version: SCHEMA_VERSION,
|
|
60
|
+
run_id: runId,
|
|
61
|
+
state_root: path.resolve(root),
|
|
62
|
+
mode: mode,
|
|
63
|
+
status: "planned",
|
|
64
|
+
source_workspace: path.resolve(sourceCwd),
|
|
65
|
+
source_realpath: sourceRealPath,
|
|
66
|
+
source_base: sourceBase,
|
|
67
|
+
source_subdirectory: sourceSubdirectory,
|
|
68
|
+
execution_root: executionRoot,
|
|
69
|
+
execution_workspace: executionWorkspace,
|
|
70
|
+
managed_directory: mode === "shared" ? null : managedDirectory,
|
|
71
|
+
snapshot,
|
|
72
|
+
git,
|
|
73
|
+
retained: false,
|
|
74
|
+
planned_at: new Date().toISOString(),
|
|
75
|
+
};
|
|
76
|
+
if (recordPath)
|
|
77
|
+
await atomicWriteJSON(recordPath, plan);
|
|
78
|
+
return plan;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/workspaces/planner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { sha256Bytes, statePaths, withFileLock } from "../foundation/index.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function workspaceRecordPath(root, runId) {
|
|
4
|
+
return path.join(statePaths(root).runs, runId, "workspace.json");
|
|
5
|
+
}
|
|
6
|
+
export function workspaceManifestFields(workspace) {
|
|
7
|
+
if (!workspace)
|
|
8
|
+
return {};
|
|
9
|
+
return {
|
|
10
|
+
workspace_mode: workspace.mode,
|
|
11
|
+
source_workspace: workspace.source_workspace,
|
|
12
|
+
execution_workspace: workspace.execution_workspace,
|
|
13
|
+
workspace_snapshot: workspace.snapshot,
|
|
14
|
+
workspace_retained: Boolean(workspace.retained),
|
|
15
|
+
...(workspace.changed !== undefined ? { workspace_changed: workspace.changed } : {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export async function withWorkspaceLock(root, identity, operation, { signal } = {}) {
|
|
19
|
+
return withFileLock(statePaths(root).workspaceLocks, sha256Bytes(identity), operation, {
|
|
20
|
+
timeoutCode: "workspace_locked",
|
|
21
|
+
timeoutExitCode: 5,
|
|
22
|
+
...(signal ? { signal } : {}),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/workspaces/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE/E,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAY;IAC5D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAA2C;IACjF,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO;QACL,cAAc,EAAE,SAAS,CAAC,IAAI;QAC9B,gBAAgB,EAAE,SAAS,CAAC,gBAAgB;QAC5C,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;QAClD,kBAAkB,EAAE,SAAS,CAAC,QAAQ;QACtC,kBAAkB,EAAE,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC;QAC/C,GAAG,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAI,IAAY,EAAE,QAAgB,EAAE,SAA2B,EAAE,EAAE,MAAM,KAA2C,EAAE;IAC3J,OAAO,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE;QACrF,WAAW,EAAE,kBAAkB;QAC/B,eAAe,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9B,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/workspaces/types.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { HitchError, invalidInput } from "../foundation/index.js";
|
|
4
|
+
export function assertNoWorkspaceOverlap(source, managedDirectory) {
|
|
5
|
+
const sourcePath = path.resolve(source);
|
|
6
|
+
const managedPath = path.resolve(managedDirectory);
|
|
7
|
+
if (isWithin(sourcePath, managedPath) || isWithin(managedPath, sourcePath)) {
|
|
8
|
+
throw new HitchError("managed workspace storage overlaps the source workspace; use a Hitch --root outside the source repository", { code: "workspace_root_overlap", exitCode: 2 });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function isWithin(parent, candidate) {
|
|
12
|
+
const relative = path.relative(parent, candidate);
|
|
13
|
+
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== "..");
|
|
14
|
+
}
|
|
15
|
+
export async function pathExists(file) {
|
|
16
|
+
try {
|
|
17
|
+
await lstat(file);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error?.code === "ENOENT")
|
|
22
|
+
return false;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function canonicalizePotentialPath(file) {
|
|
27
|
+
const missing = [];
|
|
28
|
+
let current = path.resolve(file);
|
|
29
|
+
for (;;) {
|
|
30
|
+
try {
|
|
31
|
+
const resolved = await realpath(current);
|
|
32
|
+
return path.join(resolved, ...missing.reverse());
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error?.code !== "ENOENT")
|
|
36
|
+
throw error;
|
|
37
|
+
const parent = path.dirname(current);
|
|
38
|
+
if (parent === current)
|
|
39
|
+
throw error;
|
|
40
|
+
missing.push(path.basename(current));
|
|
41
|
+
current = parent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function assertRunId(runId) {
|
|
46
|
+
if (!/^run_[a-f0-9]{32}$/.test(runId || ""))
|
|
47
|
+
throw invalidInput(`invalid run ID: ${runId || ""}`);
|
|
48
|
+
}
|
|
49
|
+
export function throwIfAborted(signal) {
|
|
50
|
+
if (signal?.aborted)
|
|
51
|
+
throw cancelledError();
|
|
52
|
+
}
|
|
53
|
+
export function cancelledError() {
|
|
54
|
+
return new HitchError("workspace preparation cancelled", { code: "cancelled", exitCode: 9 });
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/workspaces/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAElE,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,gBAAwB;IAC/E,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnD,IAAI,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,UAAU,CAClB,2GAA2G,EAC3G,EAAE,IAAI,EAAE,wBAAwB,EAAE,QAAQ,EAAE,CAAC,EAAE,CAChD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,SAAiB;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,EAAE,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACtE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,IAAY;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,SAAS,CAAC;QACR,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;YACzC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,EAAE,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACrE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,MAAM,KAAK,OAAO;gBAAE,MAAM,KAAK,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACrC,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAAE,MAAM,YAAY,CAAC,mBAAmB,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAA+B;IAC5D,IAAI,MAAM,EAAE,OAAO;QAAE,MAAM,cAAc,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,UAAU,CAAC,iCAAiC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/rsi-gear/agent-hitch/docs/schemas/controller-runtime-manifest.schema.json",
|
|
4
|
+
"title": "Controller runtime manifest",
|
|
5
|
+
"description": "Content-addressed manifest for a shared read-only Hitch controller runtime bundle (spec §4.3). The runtime_id is the SHA-256 of the canonical encoding of { schema_version, node_range, entrypoints, files }; created_at is descriptive and excluded from the identity. The declared CLI entrypoint participates in the identity hash and is executed by the Harbor bridge relative to the upload root (/opt/hitch).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "runtime_id", "node_range", "entrypoints", "files", "created_at"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": { "const": "2" },
|
|
11
|
+
"runtime_id": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
14
|
+
},
|
|
15
|
+
"node_range": { "const": ">=22" },
|
|
16
|
+
"entrypoints": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": ["cli"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"cli": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"required": ["path", "launcher"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"path": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "CLI entrypoint relative to the upload root (/opt/hitch). Must be one of the declared files; no absolute paths or traversal.",
|
|
29
|
+
"minLength": 1
|
|
30
|
+
},
|
|
31
|
+
"launcher": { "const": "node" }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"required": ["path", "size", "executable", "sha256"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"path": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Normalized NFC path with / separators, relative to the payload root.",
|
|
46
|
+
"minLength": 1
|
|
47
|
+
},
|
|
48
|
+
"size": { "type": "integer", "minimum": 0 },
|
|
49
|
+
"executable": { "type": "boolean" },
|
|
50
|
+
"sha256": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"uniqueItems": true
|
|
54
|
+
},
|
|
55
|
+
"created_at": { "type": "string", "format": "date-time" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/rsi-gear/agent-hitch/docs/schemas/controller-runtime-ref.schema.json",
|
|
4
|
+
"title": "Controller runtime reference",
|
|
5
|
+
"description": "Durable portable reference to a shared controller runtime bundle stored inside an eval record (spec §4.2, §4.7). The local absolute cache path in Harbor's generated job.json is diagnostic machine-local state and is not identity.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "storage", "runtime_id", "manifest_digest", "created_at"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": { "const": "1" },
|
|
11
|
+
"storage": { "const": "controller-runtime-ref-v1" },
|
|
12
|
+
"runtime_id": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
13
|
+
"manifest_digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
14
|
+
"created_at": { "type": "string", "format": "date-time" }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"schema_version": { "const": "1" },
|
|
10
10
|
"backend": { "const": "harbor" },
|
|
11
11
|
"dataset": { "type": "string", "minLength": 1 },
|
|
12
|
+
"benchmark_id": { "type": "string", "minLength": 1 },
|
|
13
|
+
"benchmark_revision": { "type": "string", "minLength": 1 },
|
|
12
14
|
"harness_ref": { "type": "string", "minLength": 1 },
|
|
13
15
|
"model": { "type": "string" },
|
|
14
16
|
"attempts": { "type": "integer", "minimum": 1 },
|