agent-hitch 0.2.0 → 0.2.2
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 +88 -250
- package/README.zh-CN.md +84 -230
- package/dist/bin/hitch.js +1 -1
- package/dist/bin/hitch.js.map +1 -1
- package/dist/scripts/check-architecture.js +159 -0
- package/dist/scripts/check-architecture.js.map +1 -0
- package/dist/scripts/check-release.js +1 -1
- package/dist/scripts/check-release.js.map +1 -1
- 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/handoff.js +12 -0
- package/dist/src/artifacts/handoff.js.map +1 -0
- package/dist/src/artifacts/index.js +5 -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 +157 -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/{harbor-backend.js → backends/harbor/backend.js} +52 -14
- 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/{eval-tools.js → backends/harbor/tools.js} +2 -6
- 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 +145 -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/index.js +3 -0
- package/dist/src/controller-runtime/index.js.map +1 -0
- package/dist/src/controller-runtime/store.js +2 -7
- package/dist/src/controller-runtime/store.js.map +1 -1
- 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/{scheduler.js → daemon/scheduler.js} +6 -7
- package/dist/src/daemon/scheduler.js.map +1 -0
- package/dist/src/{daemon.js → daemon/server.js} +10 -155
- 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/{types.js → ids.js} +1 -1
- 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/{validate.js → validation.js} +194 -5
- 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 +280 -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 +3 -4
- package/dist/src/feedback/service.js.map +1 -1
- package/dist/src/{config.js → foundation/config.js} +1 -0
- package/dist/src/foundation/config.js.map +1 -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.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.map +1 -0
- package/dist/src/foundation/locks.js +114 -0
- package/dist/src/foundation/locks.js.map +1 -0
- package/dist/src/{package-root.js → foundation/package-root.js} +3 -2
- package/dist/src/foundation/package-root.js.map +1 -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/{harness-reference.js → revisions/reference.js} +16 -2
- 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/{events.js → runs/events.js} +1 -2
- package/dist/src/runs/events.js.map +1 -0
- package/dist/src/{engine.js → runs/executor.js} +172 -151
- 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/format.js +1 -1
- package/dist/src/trajectories/format.js.map +1 -1
- package/dist/src/trajectories/index.js +7 -0
- package/dist/src/trajectories/index.js.map +1 -0
- package/dist/src/trajectories/projector.js +1 -1
- package/dist/src/trajectories/projector.js.map +1 -1
- 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 +148 -8
- package/dist/src/trajectories/store.js.map +1 -1
- 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/eval-request.schema.json +2 -0
- package/docs/schemas/eval-result.schema.json +50 -1
- package/docs/schemas/local-git-transport-manifest.schema.json +34 -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 +66 -23
- package/integrations/harbor/hitch_harbor_agent.py +482 -10
- package/package.json +21 -3
- package/dist/src/adapters.js +0 -461
- package/dist/src/adapters.js.map +0 -1
- package/dist/src/artifacts.js +0 -1081
- package/dist/src/artifacts.js.map +0 -1
- package/dist/src/cli.js +0 -699
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.js.map +0 -1
- package/dist/src/daemon.js.map +0 -1
- package/dist/src/domain/types.js.map +0 -1
- package/dist/src/domain/validate.js.map +0 -1
- package/dist/src/engine.js.map +0 -1
- package/dist/src/errors.js.map +0 -1
- package/dist/src/eval-tools.js.map +0 -1
- package/dist/src/evals.js +0 -313
- package/dist/src/evals.js.map +0 -1
- package/dist/src/events.js.map +0 -1
- package/dist/src/fs.js.map +0 -1
- package/dist/src/harbor-backend.js.map +0 -1
- package/dist/src/harness-reference.js.map +0 -1
- package/dist/src/line-stream.js.map +0 -1
- package/dist/src/locks.js +0 -38
- package/dist/src/locks.js.map +0 -1
- package/dist/src/package-root.js.map +0 -1
- package/dist/src/process.js.map +0 -1
- package/dist/src/registry.js +0 -81
- package/dist/src/registry.js.map +0 -1
- package/dist/src/scheduler.js.map +0 -1
- package/dist/src/workspaces.js +0 -858
- package/dist/src/workspaces.js.map +0 -1
- /package/dist/src/{errors.js → foundation/errors.js} +0 -0
- /package/dist/src/{fs.js → foundation/fs.js} +0 -0
- /package/dist/src/{line-stream.js → foundation/line-stream.js} +0 -0
- /package/dist/src/{process.js → foundation/process.js} +0 -0
package/dist/src/cli.js
DELETED
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { openSync, readFileSync } from "node:fs";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
6
|
-
import { DaemonServer, daemonClient } from "./daemon.js";
|
|
7
|
-
import { defaultRoot, DEFAULT_MAX_CONCURRENT, DEFAULT_PORT, parseDuration, positiveInteger, SCHEMA_VERSION, statePaths } from "./config.js";
|
|
8
|
-
import { discoverAgents, inspectAgent } from "./registry.js";
|
|
9
|
-
import { executeRun, newRunId } from "./engine.js";
|
|
10
|
-
import { inspectEval, listEvals, runEval } from "./evals.js";
|
|
11
|
-
import { DEFAULT_HARBOR_VERSION, doctorHarbor, setupHarbor } from "./eval-tools.js";
|
|
12
|
-
import { HitchError, invalidInput } from "./errors.js";
|
|
13
|
-
import { delay } from "./process.js";
|
|
14
|
-
import { ensureDir, readJSON } from "./fs.js";
|
|
15
|
-
import { listPreparedArtifacts, prepareHarness, resolveHarness } from "./artifacts.js";
|
|
16
|
-
import { inspectWorkspace, removeWorkspace, WORKSPACE_MODES } from "./workspaces.js";
|
|
17
|
-
import { MessageFeedbackService, resolveFeedbackSession, trajectoryTargetValidator } from "./feedback/service.js";
|
|
18
|
-
import { readTrajectory, loadTrajectoryRef } from "./trajectories/store.js";
|
|
19
|
-
import { packageVersion as readPackageVersion } from "./package-root.js";
|
|
20
|
-
const executable = fileURLToPath(new URL("../bin/hitch.js", import.meta.url));
|
|
21
|
-
export async function main(argv) {
|
|
22
|
-
const args = [...argv];
|
|
23
|
-
const root = path.resolve(takeOption(args, "--root") || defaultRoot());
|
|
24
|
-
const command = args.shift();
|
|
25
|
-
switch (command) {
|
|
26
|
-
case "list": return listCommand(args);
|
|
27
|
-
case "inspect": return inspectCommand(args, root);
|
|
28
|
-
case "resolve": return resolveCommand(args, root);
|
|
29
|
-
case "prepare": return prepareCommand(args, root);
|
|
30
|
-
case "run": return runCommand(args, root);
|
|
31
|
-
case "eval": return evalCommand(args, root);
|
|
32
|
-
case "workspace": return workspaceCommand(args, root);
|
|
33
|
-
case "trajectory": return trajectoryCommand(args, root);
|
|
34
|
-
case "feedback": return feedbackCommand(args, root);
|
|
35
|
-
case "daemon": return daemonCommand(args, root);
|
|
36
|
-
case "help":
|
|
37
|
-
case "--help":
|
|
38
|
-
case "-h":
|
|
39
|
-
case undefined:
|
|
40
|
-
process.stdout.write(helpText());
|
|
41
|
-
return;
|
|
42
|
-
case "--version":
|
|
43
|
-
case "-V":
|
|
44
|
-
process.stdout.write(`hitch ${readPackageVersion()}\n`);
|
|
45
|
-
return;
|
|
46
|
-
default:
|
|
47
|
-
throw invalidInput(`unknown command: ${command}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function listCommand(args) {
|
|
51
|
-
const json = takeFlag(args, "--json");
|
|
52
|
-
assertNoArgs(args);
|
|
53
|
-
const agents = await discoverAgents();
|
|
54
|
-
if (json) {
|
|
55
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, harnesses: agents }, null, 2)}\n`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
for (const agent of agents) {
|
|
59
|
-
const detail = agent.status === "available" ? `${agent.version || "version unknown"} ${agent.executable}` : "not installed";
|
|
60
|
-
process.stdout.write(`${agent.id.padEnd(10)} ${agent.status.padEnd(11)} ${detail}\n`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async function inspectCommand(args, root) {
|
|
64
|
-
const json = takeFlag(args, "--json");
|
|
65
|
-
const id = args.shift();
|
|
66
|
-
if (!id)
|
|
67
|
-
throw invalidInput("inspect requires a harness name");
|
|
68
|
-
assertNoArgs(args);
|
|
69
|
-
const harness = {
|
|
70
|
-
...await inspectAgent(id),
|
|
71
|
-
prepared_artifacts: await listPreparedArtifacts(id, { root }),
|
|
72
|
-
};
|
|
73
|
-
if (json)
|
|
74
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, harness }, null, 2)}\n`);
|
|
75
|
-
else
|
|
76
|
-
process.stdout.write(`${harness.display_name}: ${harness.status}${harness.executable ? `\n executable: ${harness.executable}\n version: ${harness.version || "unknown"}` : ""}\n prepared artifacts: ${harness.prepared_artifacts.length}\n`);
|
|
77
|
-
}
|
|
78
|
-
async function resolveCommand(args, root) {
|
|
79
|
-
const json = takeFlag(args, "--json");
|
|
80
|
-
const reference = args.shift();
|
|
81
|
-
if (!reference)
|
|
82
|
-
throw invalidInput("resolve requires a harness reference");
|
|
83
|
-
assertNoArgs(args);
|
|
84
|
-
const resolved = await resolveHarness(reference, { root });
|
|
85
|
-
if (json)
|
|
86
|
-
process.stdout.write(`${JSON.stringify(resolved, null, 2)}\n`);
|
|
87
|
-
else
|
|
88
|
-
process.stdout.write(`${resolved.canonical_ref} -> ${revisionLabel(resolved)} (${resolved.identity})\n`);
|
|
89
|
-
}
|
|
90
|
-
async function prepareCommand(args, root) {
|
|
91
|
-
const json = takeFlag(args, "--json");
|
|
92
|
-
const reference = args.shift();
|
|
93
|
-
if (!reference)
|
|
94
|
-
throw invalidInput("prepare requires a harness reference");
|
|
95
|
-
assertNoArgs(args);
|
|
96
|
-
const resolved = await resolveHarness(reference, { root });
|
|
97
|
-
const artifact = await prepareHarness(resolved, { root });
|
|
98
|
-
if (json)
|
|
99
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, resolved_revision: resolved, artifact }, null, 2)}\n`);
|
|
100
|
-
else
|
|
101
|
-
process.stdout.write(`${artifact.cache_hit ? "Cached" : "Prepared"} ${resolved.canonical_ref} as ${artifact.artifact_id}\n`);
|
|
102
|
-
}
|
|
103
|
-
async function runCommand(args, root) {
|
|
104
|
-
const useDaemon = takeFlag(args, "--daemon");
|
|
105
|
-
const output = takeOption(args, "--output") || "jsonl";
|
|
106
|
-
const request = await parseRunRequest(args);
|
|
107
|
-
assertNoArgs(args);
|
|
108
|
-
if (!new Set(["json", "jsonl"]).has(output))
|
|
109
|
-
throw invalidInput("--output must be json or jsonl");
|
|
110
|
-
if (useDaemon) {
|
|
111
|
-
const client = await daemonClient(root);
|
|
112
|
-
const accepted = await client.request("/v1/runs", { method: "POST", body: JSON.stringify(request) });
|
|
113
|
-
const result = await waitForDaemonRun(client, accepted.run_id, output);
|
|
114
|
-
process.exitCode = result.exit_code;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const runId = newRunId();
|
|
118
|
-
const result = await executeRun({
|
|
119
|
-
runId,
|
|
120
|
-
request,
|
|
121
|
-
runsRoot: statePaths(root).runs,
|
|
122
|
-
root,
|
|
123
|
-
...(output === "jsonl" ? { onEvent: (event) => process.stdout.write(`${JSON.stringify(event)}\n`) } : {}),
|
|
124
|
-
});
|
|
125
|
-
if (output === "json")
|
|
126
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
127
|
-
process.exitCode = result.exit_code;
|
|
128
|
-
}
|
|
129
|
-
async function evalCommand(args, root) {
|
|
130
|
-
const action = args.shift();
|
|
131
|
-
switch (action) {
|
|
132
|
-
case "run": return evalRunCommand(args, root);
|
|
133
|
-
case "list": return evalListCommand(args, root);
|
|
134
|
-
case "inspect": return evalInspectCommand(args, root);
|
|
135
|
-
case "setup": return evalSetupCommand(args, root);
|
|
136
|
-
case "doctor": return evalDoctorCommand(args, root);
|
|
137
|
-
default: throw invalidInput("eval requires run, list, inspect, setup, or doctor");
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async function evalSetupCommand(args, root) {
|
|
141
|
-
const backend = args.shift();
|
|
142
|
-
if (backend !== "harbor")
|
|
143
|
-
throw invalidInput("eval setup currently supports only harbor");
|
|
144
|
-
const version = takeOption(args, "--version") || DEFAULT_HARBOR_VERSION;
|
|
145
|
-
const python = takeOption(args, "--python");
|
|
146
|
-
const force = takeFlag(args, "--force");
|
|
147
|
-
const json = takeFlag(args, "--json");
|
|
148
|
-
assertNoArgs(args);
|
|
149
|
-
const result = await setupHarbor({
|
|
150
|
-
root,
|
|
151
|
-
version,
|
|
152
|
-
...(python !== undefined ? { python } : {}),
|
|
153
|
-
force,
|
|
154
|
-
...(json ? {} : { onProgress: (message) => process.stderr.write(`${message}\n`) }),
|
|
155
|
-
});
|
|
156
|
-
if (json)
|
|
157
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
158
|
-
else
|
|
159
|
-
process.stdout.write(`${result.cache_hit ? "Using" : "Installed"} Harbor ${result.version} at ${result.executable}\n`);
|
|
160
|
-
}
|
|
161
|
-
async function evalDoctorCommand(args, root) {
|
|
162
|
-
const json = takeFlag(args, "--json");
|
|
163
|
-
const python = takeOption(args, "--python");
|
|
164
|
-
const harbor = takeOption(args, "--harbor");
|
|
165
|
-
const docker = takeOption(args, "--docker");
|
|
166
|
-
assertNoArgs(args);
|
|
167
|
-
const result = await doctorHarbor({
|
|
168
|
-
root,
|
|
169
|
-
...(python !== undefined ? { python } : {}),
|
|
170
|
-
...(harbor !== undefined ? { harbor } : {}),
|
|
171
|
-
...(docker !== undefined ? { docker } : {}),
|
|
172
|
-
});
|
|
173
|
-
if (json) {
|
|
174
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
process.stdout.write(`Harbor eval: ${result.status}\n`);
|
|
178
|
-
for (const [name, check] of Object.entries(result.checks)) {
|
|
179
|
-
const detail = check.version || check.message || (check.present?.length ? check.present.join(", ") : "");
|
|
180
|
-
process.stdout.write(` ${name.padEnd(12)} ${check.status}${detail ? ` ${detail}` : ""}\n`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (!result.ready)
|
|
184
|
-
process.exitCode = 3;
|
|
185
|
-
}
|
|
186
|
-
async function evalRunCommand(args, root) {
|
|
187
|
-
const output = takeOption(args, "--output") || "json";
|
|
188
|
-
const harborExecutable = takeOption(args, "--harbor");
|
|
189
|
-
const request = parseEvalRequest(args);
|
|
190
|
-
assertNoArgs(args);
|
|
191
|
-
if (!new Set(["json", "jsonl"]).has(output))
|
|
192
|
-
throw invalidInput("--output must be json or jsonl");
|
|
193
|
-
const controller = new AbortController();
|
|
194
|
-
const cancel = () => controller.abort();
|
|
195
|
-
process.once("SIGINT", cancel);
|
|
196
|
-
process.once("SIGTERM", cancel);
|
|
197
|
-
try {
|
|
198
|
-
const result = await runEval({
|
|
199
|
-
request,
|
|
200
|
-
root,
|
|
201
|
-
...(harborExecutable !== undefined ? { harborExecutable } : {}),
|
|
202
|
-
signal: controller.signal,
|
|
203
|
-
...(output === "jsonl" ? { onEvent: (event) => process.stdout.write(`${JSON.stringify(event)}\n`) } : {}),
|
|
204
|
-
});
|
|
205
|
-
if (output === "json")
|
|
206
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
207
|
-
process.exitCode = result.exit_code;
|
|
208
|
-
}
|
|
209
|
-
finally {
|
|
210
|
-
process.removeListener("SIGINT", cancel);
|
|
211
|
-
process.removeListener("SIGTERM", cancel);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
async function evalListCommand(args, root) {
|
|
215
|
-
const json = takeFlag(args, "--json");
|
|
216
|
-
assertNoArgs(args);
|
|
217
|
-
const evals = await listEvals({ root });
|
|
218
|
-
if (json) {
|
|
219
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, evals }, null, 2)}\n`);
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
if (evals.length === 0) {
|
|
223
|
-
process.stdout.write("No evals found\n");
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
for (const evaluation of evals) {
|
|
227
|
-
const reward = evaluation.primary_reward === null ? "-" : String(evaluation.primary_reward);
|
|
228
|
-
process.stdout.write(`${evaluation.eval_id} ${String(evaluation.status).padEnd(9)} reward=${reward} ${evaluation.harness_ref || "-"} ${evaluation.dataset || "-"}\n`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
async function evalInspectCommand(args, root) {
|
|
232
|
-
const json = takeFlag(args, "--json");
|
|
233
|
-
const evalId = args.shift();
|
|
234
|
-
if (!evalId)
|
|
235
|
-
throw invalidInput("eval inspect requires an eval ID");
|
|
236
|
-
assertNoArgs(args);
|
|
237
|
-
const evaluation = await inspectEval(evalId, { root });
|
|
238
|
-
if (json) {
|
|
239
|
-
process.stdout.write(`${JSON.stringify(evaluation, null, 2)}\n`);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
const result = evaluation.result;
|
|
243
|
-
process.stdout.write(`${evaluation.eval_id}: ${result?.status || "running"}\n`);
|
|
244
|
-
process.stdout.write(` dataset: ${evaluation.request?.dataset}\n`);
|
|
245
|
-
process.stdout.write(` harness: ${evaluation.plan?.candidate?.harness_ref || evaluation.request?.harness_ref}\n`);
|
|
246
|
-
process.stdout.write(` primary reward: ${result?.summary?.primary_reward ?? "-"}\n`);
|
|
247
|
-
process.stdout.write(` runtime storage: ${evaluation.runtime_storage}\n`);
|
|
248
|
-
process.stdout.write(` directory: ${evaluation.directory}\n`);
|
|
249
|
-
}
|
|
250
|
-
async function daemonCommand(args, root) {
|
|
251
|
-
const action = args.shift();
|
|
252
|
-
switch (action) {
|
|
253
|
-
case "serve": return daemonServe(args, root);
|
|
254
|
-
case "start": return daemonStart(args, root);
|
|
255
|
-
case "stop": return daemonStop(args, root);
|
|
256
|
-
case "status": return daemonStatus(args, root);
|
|
257
|
-
case "submit": return daemonSubmit(args, root);
|
|
258
|
-
case "cancel": return daemonCancel(args, root);
|
|
259
|
-
case "logs": return daemonLogs(args, root);
|
|
260
|
-
default: throw invalidInput("daemon requires start, serve, stop, status, submit, cancel, or logs");
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
async function workspaceCommand(args, root) {
|
|
264
|
-
const action = args.shift();
|
|
265
|
-
const runId = args.shift();
|
|
266
|
-
if (!runId)
|
|
267
|
-
throw invalidInput("workspace requires a run ID");
|
|
268
|
-
if (action === "inspect") {
|
|
269
|
-
const json = takeFlag(args, "--json");
|
|
270
|
-
assertNoArgs(args);
|
|
271
|
-
const workspace = await inspectWorkspace({ root, runId: runId });
|
|
272
|
-
if (!workspace)
|
|
273
|
-
throw new HitchError(`workspace record not found: ${runId}`, { code: "workspace_not_found", exitCode: 3 });
|
|
274
|
-
if (json)
|
|
275
|
-
process.stdout.write(`${JSON.stringify(workspace, null, 2)}\n`);
|
|
276
|
-
else
|
|
277
|
-
process.stdout.write(`${runId}: ${workspace.mode} ${workspace.status}\n source: ${workspace.source_workspace}\n execution: ${workspace.execution_workspace}\n retained: ${workspace.retained ? "yes" : "no"}${workspace.changed === undefined || workspace.changed === null ? "" : `\n changed: ${workspace.changed ? "yes" : "no"}`}\n`);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
if (action === "path") {
|
|
281
|
-
assertNoArgs(args);
|
|
282
|
-
const workspace = await inspectWorkspace({ root, runId: runId });
|
|
283
|
-
if (!workspace)
|
|
284
|
-
throw new HitchError(`workspace record not found: ${runId}`, { code: "workspace_not_found", exitCode: 3 });
|
|
285
|
-
if (!workspace.retained)
|
|
286
|
-
throw new HitchError(`run ${runId} has no retained workspace`, { code: "workspace_not_retained", exitCode: 3 });
|
|
287
|
-
process.stdout.write(`${workspace.execution_workspace}\n`);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
if (action === "remove") {
|
|
291
|
-
const force = takeFlag(args, "--force");
|
|
292
|
-
const json = takeFlag(args, "--json");
|
|
293
|
-
assertNoArgs(args);
|
|
294
|
-
const workspace = await removeWorkspace({ root, runId: runId, force });
|
|
295
|
-
if (json)
|
|
296
|
-
process.stdout.write(`${JSON.stringify(workspace, null, 2)}\n`);
|
|
297
|
-
else
|
|
298
|
-
process.stdout.write(`Removed workspace for ${runId}\n`);
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
throw invalidInput("workspace requires inspect, path, or remove");
|
|
302
|
-
}
|
|
303
|
-
async function trajectoryCommand(args, root) {
|
|
304
|
-
const action = args.shift();
|
|
305
|
-
if (action !== "inspect")
|
|
306
|
-
throw invalidInput("trajectory requires inspect");
|
|
307
|
-
const json = takeFlag(args, "--json");
|
|
308
|
-
const runId = args.shift();
|
|
309
|
-
if (!runId)
|
|
310
|
-
throw invalidInput("trajectory inspect requires a run ID");
|
|
311
|
-
assertNoArgs(args);
|
|
312
|
-
const runDirectory = path.join(statePaths(root).runs, runId);
|
|
313
|
-
const ref = await loadTrajectoryRef(runDirectory);
|
|
314
|
-
if (!ref) {
|
|
315
|
-
throw new HitchError(`run ${runId} has no canonical trajectory`, { code: "trajectory_not_found", exitCode: 3 });
|
|
316
|
-
}
|
|
317
|
-
const { header, events } = await readTrajectory(ref.path);
|
|
318
|
-
if (json) {
|
|
319
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, run_id: runId, ref, header, events }, null, 2)}\n`);
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
process.stdout.write(`${runId}: ${ref.fidelity} trajectory (${events.length} events)\n`);
|
|
323
|
-
process.stdout.write(` session: ${ref.session_id}\n`);
|
|
324
|
-
process.stdout.write(` path: ${ref.path}\n`);
|
|
325
|
-
const assistantMessages = events.filter((event) => event.type === "assistant/message");
|
|
326
|
-
const toolResults = events.filter((event) => event.type === "tool/result");
|
|
327
|
-
process.stdout.write(` assistant messages: ${assistantMessages.length}, tool results: ${toolResults.length}\n`);
|
|
328
|
-
}
|
|
329
|
-
async function feedbackCommand(args, root) {
|
|
330
|
-
const action = args.shift();
|
|
331
|
-
const runId = args.shift();
|
|
332
|
-
if (!runId)
|
|
333
|
-
throw invalidInput("feedback requires a run ID");
|
|
334
|
-
const runDirectory = path.join(statePaths(root).runs, runId);
|
|
335
|
-
const session = await resolveFeedbackSession(runDirectory);
|
|
336
|
-
if (!session) {
|
|
337
|
-
throw new HitchError(`run ${runId} has no canonical trajectory for feedback`, {
|
|
338
|
-
code: "session-not-found",
|
|
339
|
-
exitCode: 3,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
const service = new MessageFeedbackService({
|
|
343
|
-
root: runDirectory,
|
|
344
|
-
validateTarget: await trajectoryTargetValidator(runDirectory),
|
|
345
|
-
});
|
|
346
|
-
if (action === "list") {
|
|
347
|
-
const json = takeFlag(args, "--json");
|
|
348
|
-
assertNoArgs(args);
|
|
349
|
-
const items = await service.list({ sessionId: session.sessionId }, session);
|
|
350
|
-
if (json) {
|
|
351
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, run_id: runId, session_id: session.sessionId, items }, null, 2)}\n`);
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
if (items.length === 0) {
|
|
355
|
-
process.stdout.write("No feedback\n");
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
for (const item of items) {
|
|
359
|
-
process.stdout.write(`${item.messageId} ${item.rating} v${item.version}${item.note ? ` ${item.note}` : ""}\n`);
|
|
360
|
-
}
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
if (action === "put") {
|
|
364
|
-
const json = takeFlag(args, "--json");
|
|
365
|
-
const messageId = takeOption(args, "--message");
|
|
366
|
-
const ratingValue = takeOption(args, "--rating");
|
|
367
|
-
const note = takeOption(args, "--note");
|
|
368
|
-
const ifVersion = takeOption(args, "--if-version");
|
|
369
|
-
assertNoArgs(args);
|
|
370
|
-
if (!messageId)
|
|
371
|
-
throw invalidInput("feedback put requires --message <id>");
|
|
372
|
-
if (ratingValue !== "positive" && ratingValue !== "negative") {
|
|
373
|
-
throw invalidInput("feedback put requires --rating positive|negative");
|
|
374
|
-
}
|
|
375
|
-
const item = await service.put({
|
|
376
|
-
sessionId: session.sessionId,
|
|
377
|
-
messageId,
|
|
378
|
-
rating: ratingValue,
|
|
379
|
-
...(note !== undefined ? { note } : {}),
|
|
380
|
-
ifVersion: ifVersion === undefined ? null : ifVersion,
|
|
381
|
-
}, session, { messageId });
|
|
382
|
-
if (json)
|
|
383
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, item }, null, 2)}\n`);
|
|
384
|
-
else
|
|
385
|
-
process.stdout.write(`${item.messageId}: ${item.rating} (v${item.version})\n`);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
if (action === "delete") {
|
|
389
|
-
const json = takeFlag(args, "--json");
|
|
390
|
-
const messageId = takeOption(args, "--message");
|
|
391
|
-
const ifVersion = takeOption(args, "--if-version");
|
|
392
|
-
assertNoArgs(args);
|
|
393
|
-
if (!messageId)
|
|
394
|
-
throw invalidInput("feedback delete requires --message <id>");
|
|
395
|
-
await service.delete({
|
|
396
|
-
sessionId: session.sessionId,
|
|
397
|
-
messageId,
|
|
398
|
-
ifVersion: ifVersion === undefined ? null : ifVersion,
|
|
399
|
-
}, session);
|
|
400
|
-
if (json)
|
|
401
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, absent: true }, null, 2)}\n`);
|
|
402
|
-
else
|
|
403
|
-
process.stdout.write(`Deleted feedback for ${messageId}\n`);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
throw invalidInput("feedback requires list, put, or delete");
|
|
407
|
-
}
|
|
408
|
-
async function daemonServe(args, root) {
|
|
409
|
-
const port = Number(takeOption(args, "--port") || DEFAULT_PORT);
|
|
410
|
-
const maxConcurrent = positiveInteger(takeOption(args, "--max-concurrent") || DEFAULT_MAX_CONCURRENT, "--max-concurrent");
|
|
411
|
-
assertNoArgs(args);
|
|
412
|
-
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
413
|
-
throw invalidInput("--port must be between 0 and 65535");
|
|
414
|
-
const server = new DaemonServer({ root, port, maxConcurrent });
|
|
415
|
-
await server.start();
|
|
416
|
-
const shutdown = () => server.close().catch((error) => process.stderr.write(`shutdown error: ${error.message}\n`));
|
|
417
|
-
process.once("SIGINT", shutdown);
|
|
418
|
-
process.once("SIGTERM", shutdown);
|
|
419
|
-
await server.closed;
|
|
420
|
-
}
|
|
421
|
-
async function daemonStart(args, root) {
|
|
422
|
-
const foreground = takeFlag(args, "--foreground");
|
|
423
|
-
const port = Number(takeOption(args, "--port") || DEFAULT_PORT);
|
|
424
|
-
const maxConcurrent = positiveInteger(takeOption(args, "--max-concurrent") || DEFAULT_MAX_CONCURRENT, "--max-concurrent");
|
|
425
|
-
assertNoArgs(args);
|
|
426
|
-
const health = await probeHealth(root);
|
|
427
|
-
if (health?.status === "running")
|
|
428
|
-
throw new HitchError(`daemon is already running (pid ${health.pid})`, { code: "already_running", exitCode: 2 });
|
|
429
|
-
if (foreground)
|
|
430
|
-
return daemonServe(["--port", String(port), "--max-concurrent", String(maxConcurrent)], root);
|
|
431
|
-
const paths = statePaths(root);
|
|
432
|
-
await ensureDir(root);
|
|
433
|
-
const stdout = openSync(paths.log, "a", 0o600);
|
|
434
|
-
const stderr = openSync(paths.errorLog, "a", 0o600);
|
|
435
|
-
const child = spawn(process.execPath, [executable, "--root", root, "daemon", "serve", "--port", String(port), "--max-concurrent", String(maxConcurrent)], {
|
|
436
|
-
detached: true,
|
|
437
|
-
stdio: ["ignore", stdout, stderr],
|
|
438
|
-
windowsHide: true,
|
|
439
|
-
});
|
|
440
|
-
child.unref();
|
|
441
|
-
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
442
|
-
await delay(100);
|
|
443
|
-
const current = await probeHealth(root);
|
|
444
|
-
if (current?.status === "running" && current.pid === child.pid) {
|
|
445
|
-
process.stdout.write(`Hitch daemon started (pid ${current.pid}, port ${current.port})\n`);
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
throw new HitchError(`daemon did not become ready; see ${paths.errorLog}`, { code: "daemon_start_failed", exitCode: 12 });
|
|
450
|
-
}
|
|
451
|
-
async function daemonStop(args, root) {
|
|
452
|
-
assertNoArgs(args);
|
|
453
|
-
const client = await daemonClient(root);
|
|
454
|
-
const response = await client.request("/shutdown", { method: "POST" });
|
|
455
|
-
process.stdout.write(`${response.status}\n`);
|
|
456
|
-
}
|
|
457
|
-
async function daemonStatus(args, root) {
|
|
458
|
-
const json = takeFlag(args, "--json");
|
|
459
|
-
assertNoArgs(args);
|
|
460
|
-
const health = await probeHealth(root);
|
|
461
|
-
if (!health) {
|
|
462
|
-
if (json)
|
|
463
|
-
process.stdout.write(`${JSON.stringify({ schema_version: SCHEMA_VERSION, status: "stopped" })}\n`);
|
|
464
|
-
else
|
|
465
|
-
process.stdout.write("Hitch daemon is stopped\n");
|
|
466
|
-
process.exitCode = 3;
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
if (json)
|
|
470
|
-
process.stdout.write(`${JSON.stringify(health, null, 2)}\n`);
|
|
471
|
-
else
|
|
472
|
-
process.stdout.write(`Hitch daemon is ${health.status} (pid ${health.pid}, port ${health.port}, ${health.scheduler?.running} running, ${health.scheduler?.queued} queued)\n`);
|
|
473
|
-
}
|
|
474
|
-
async function daemonSubmit(args, root) {
|
|
475
|
-
const wait = takeFlag(args, "--wait");
|
|
476
|
-
const output = takeOption(args, "--output") || "json";
|
|
477
|
-
const request = await parseRunRequest(args);
|
|
478
|
-
assertNoArgs(args);
|
|
479
|
-
const client = await daemonClient(root);
|
|
480
|
-
const accepted = await client.request("/v1/runs", { method: "POST", body: JSON.stringify(request) });
|
|
481
|
-
if (!wait) {
|
|
482
|
-
process.stdout.write(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
const result = await waitForDaemonRun(client, accepted.run_id, output);
|
|
486
|
-
process.exitCode = result.exit_code;
|
|
487
|
-
}
|
|
488
|
-
async function daemonCancel(args, root) {
|
|
489
|
-
const runId = args.shift();
|
|
490
|
-
if (!runId)
|
|
491
|
-
throw invalidInput("daemon cancel requires a run ID");
|
|
492
|
-
assertNoArgs(args);
|
|
493
|
-
const client = await daemonClient(root);
|
|
494
|
-
const response = await client.request(`/v1/runs/${runId}/cancel`, { method: "POST" });
|
|
495
|
-
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
496
|
-
}
|
|
497
|
-
async function daemonLogs(args, root) {
|
|
498
|
-
const lines = positiveInteger(takeOption(args, "-n") || 50, "-n");
|
|
499
|
-
assertNoArgs(args);
|
|
500
|
-
let content = "";
|
|
501
|
-
try {
|
|
502
|
-
content = await readFile(statePaths(root).log, "utf8");
|
|
503
|
-
}
|
|
504
|
-
catch (error) {
|
|
505
|
-
if (error?.code !== "ENOENT")
|
|
506
|
-
throw error;
|
|
507
|
-
}
|
|
508
|
-
process.stdout.write(`${content.trimEnd().split(/\r?\n/).slice(-lines).join("\n")}\n`);
|
|
509
|
-
}
|
|
510
|
-
async function parseRunRequest(args) {
|
|
511
|
-
const harness = takeOption(args, "--harness");
|
|
512
|
-
const agent = takeOption(args, "--agent");
|
|
513
|
-
const model = takeOption(args, "--model") || "";
|
|
514
|
-
const cwd = takeOption(args, "--cwd") || process.cwd();
|
|
515
|
-
const workspaceMode = takeOption(args, "--workspace-mode") || "shared";
|
|
516
|
-
const promptValue = takeOption(args, "--prompt");
|
|
517
|
-
const promptFile = takeOption(args, "--prompt-file");
|
|
518
|
-
const timeout = parseDuration(takeOption(args, "--timeout") || "0");
|
|
519
|
-
const agentArgs = takeRepeatedOption(args, "--agent-arg");
|
|
520
|
-
if (harness && agent)
|
|
521
|
-
throw invalidInput("use only one of --harness and the legacy --agent option");
|
|
522
|
-
if (!harness && !agent)
|
|
523
|
-
throw invalidInput("--harness is required");
|
|
524
|
-
if (!WORKSPACE_MODES.has(workspaceMode))
|
|
525
|
-
throw invalidInput(`--workspace-mode must be one of: ${[...WORKSPACE_MODES].join(", ")}`);
|
|
526
|
-
if (agent?.includes("@"))
|
|
527
|
-
throw invalidInput("--agent accepts only a harness name; use --harness for revision selection");
|
|
528
|
-
if (promptValue !== undefined && promptFile)
|
|
529
|
-
throw invalidInput("use only one of --prompt and --prompt-file");
|
|
530
|
-
let prompt = promptValue;
|
|
531
|
-
if (promptFile)
|
|
532
|
-
prompt = await readFile(path.resolve(promptFile), "utf8");
|
|
533
|
-
if (prompt === undefined && !process.stdin.isTTY)
|
|
534
|
-
prompt = readFileSync(0, "utf8");
|
|
535
|
-
if (!prompt)
|
|
536
|
-
throw invalidInput("provide --prompt, --prompt-file, or stdin");
|
|
537
|
-
return { harness_ref: harness || `${agent}@installed`, model, cwd, workspace_mode: workspaceMode, prompt, timeout_ms: timeout, agent_args: agentArgs };
|
|
538
|
-
}
|
|
539
|
-
function parseEvalRequest(args) {
|
|
540
|
-
const backend = takeOption(args, "--backend") || "harbor";
|
|
541
|
-
const dataset = takeOption(args, "--dataset");
|
|
542
|
-
const harness = takeOption(args, "--harness");
|
|
543
|
-
const model = takeOption(args, "--model") || "";
|
|
544
|
-
const attempts = positiveInteger(takeOption(args, "--attempts") || 1, "--attempts");
|
|
545
|
-
const maxConcurrent = positiveInteger(takeOption(args, "--max-concurrent") || DEFAULT_MAX_CONCURRENT, "--max-concurrent");
|
|
546
|
-
const timeoutValue = takeOption(args, "--timeout");
|
|
547
|
-
const setupTimeoutValue = takeOption(args, "--setup-timeout");
|
|
548
|
-
const agentArgs = takeRepeatedOption(args, "--agent-arg");
|
|
549
|
-
const passEnv = takeRepeatedOption(args, "--pass-env");
|
|
550
|
-
if (!dataset)
|
|
551
|
-
throw invalidInput("--dataset is required");
|
|
552
|
-
if (!harness)
|
|
553
|
-
throw invalidInput("--harness is required");
|
|
554
|
-
return {
|
|
555
|
-
backend,
|
|
556
|
-
dataset,
|
|
557
|
-
harness_ref: harness,
|
|
558
|
-
model,
|
|
559
|
-
attempts,
|
|
560
|
-
max_concurrent: maxConcurrent,
|
|
561
|
-
timeout_ms: timeoutValue === undefined ? undefined : parseDuration(timeoutValue),
|
|
562
|
-
setup_timeout_ms: setupTimeoutValue === undefined ? undefined : parseDuration(setupTimeoutValue),
|
|
563
|
-
agent_args: agentArgs,
|
|
564
|
-
pass_env: passEnv,
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
function revisionLabel(resolved) {
|
|
568
|
-
if (resolved.revision.type === "commit")
|
|
569
|
-
return resolved.revision.commit || "";
|
|
570
|
-
if (resolved.revision.type === "version")
|
|
571
|
-
return resolved.revision.version || "";
|
|
572
|
-
return resolved.revision.version || resolved.source.integrity || "";
|
|
573
|
-
}
|
|
574
|
-
async function waitForDaemonRun(client, runId, output) {
|
|
575
|
-
let eventOffset = 0;
|
|
576
|
-
for (;;) {
|
|
577
|
-
const status = await client.request(`/v1/runs/${runId}`);
|
|
578
|
-
if (output === "jsonl") {
|
|
579
|
-
try {
|
|
580
|
-
const response = await client.requestWithMetadata(`/v1/runs/${runId}/events?offset=${eventOffset}`);
|
|
581
|
-
const raw = response.payload;
|
|
582
|
-
for (const line of String(raw).trim().split(/\r?\n/).filter(Boolean)) {
|
|
583
|
-
JSON.parse(line);
|
|
584
|
-
process.stdout.write(`${line}\n`);
|
|
585
|
-
}
|
|
586
|
-
eventOffset = Number(response.headers.get("x-hitch-next-offset") || eventOffset);
|
|
587
|
-
}
|
|
588
|
-
catch (error) {
|
|
589
|
-
if (error.status !== 404)
|
|
590
|
-
throw error;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
if (status.result) {
|
|
594
|
-
if (output === "json")
|
|
595
|
-
process.stdout.write(`${JSON.stringify(status.result, null, 2)}\n`);
|
|
596
|
-
return status.result;
|
|
597
|
-
}
|
|
598
|
-
await delay(200);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
async function probeHealth(root) {
|
|
602
|
-
const state = await readJSON(statePaths(root).daemon, null);
|
|
603
|
-
if (!state?.port)
|
|
604
|
-
return null;
|
|
605
|
-
try {
|
|
606
|
-
const response = await fetch(`http://127.0.0.1:${state.port}/health`, { signal: AbortSignal.timeout(1_000) });
|
|
607
|
-
if (!response.ok)
|
|
608
|
-
return null;
|
|
609
|
-
const health = await response.json();
|
|
610
|
-
return health.instance_id === state.instance_id ? health : null;
|
|
611
|
-
}
|
|
612
|
-
catch {
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
function takeOption(args, name) {
|
|
617
|
-
const index = args.indexOf(name);
|
|
618
|
-
if (index < 0)
|
|
619
|
-
return undefined;
|
|
620
|
-
if (index === args.length - 1)
|
|
621
|
-
throw invalidInput(`${name} requires a value`);
|
|
622
|
-
const [value] = args.splice(index + 1, 1);
|
|
623
|
-
args.splice(index, 1);
|
|
624
|
-
return value;
|
|
625
|
-
}
|
|
626
|
-
function takeRepeatedOption(args, name) {
|
|
627
|
-
const values = [];
|
|
628
|
-
for (;;) {
|
|
629
|
-
const value = takeOption(args, name);
|
|
630
|
-
if (value === undefined)
|
|
631
|
-
return values;
|
|
632
|
-
values.push(value);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
function takeFlag(args, name) {
|
|
636
|
-
const index = args.indexOf(name);
|
|
637
|
-
if (index < 0)
|
|
638
|
-
return false;
|
|
639
|
-
args.splice(index, 1);
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
function assertNoArgs(args) {
|
|
643
|
-
if (args.length > 0)
|
|
644
|
-
throw invalidInput(`unexpected argument: ${args[0]}`);
|
|
645
|
-
}
|
|
646
|
-
function helpText() {
|
|
647
|
-
return `Hitch — content-addressed version control and evidence storage for agent harnesses
|
|
648
|
-
|
|
649
|
-
Usage:
|
|
650
|
-
hitch list [--json]
|
|
651
|
-
hitch inspect <harness> [--json]
|
|
652
|
-
hitch resolve <harness-ref> [--json]
|
|
653
|
-
hitch prepare <harness-ref> [--json]
|
|
654
|
-
hitch run --harness <ref> [--model <id>] [--workspace-mode <mode>] --prompt <text> [--daemon]
|
|
655
|
-
hitch eval setup harbor [--version <version>] [--python <path>] [--force] [--json]
|
|
656
|
-
hitch eval doctor [--harbor <path>] [--python <path>] [--docker <path>] [--json]
|
|
657
|
-
hitch eval run [--backend harbor] --dataset <ref> --harness <immutable-ref> [--model <id>] [--attempts <n>]
|
|
658
|
-
hitch eval list [--json]
|
|
659
|
-
hitch eval inspect <eval-id> [--json]
|
|
660
|
-
hitch workspace inspect <run-id> [--json]
|
|
661
|
-
hitch workspace path <run-id>
|
|
662
|
-
hitch workspace remove <run-id> [--force] [--json]
|
|
663
|
-
hitch trajectory inspect <run-id> [--json]
|
|
664
|
-
hitch feedback list <run-id> [--json]
|
|
665
|
-
hitch feedback put <run-id> --message <id> --rating positive|negative [--note <text>] [--if-version <v>] [--json]
|
|
666
|
-
hitch feedback delete <run-id> --message <id> [--if-version <v>] [--json]
|
|
667
|
-
hitch daemon start [--foreground] [--port <port>] [--max-concurrent <n>]
|
|
668
|
-
hitch daemon stop | status [--json] | logs [-n <lines>]
|
|
669
|
-
hitch daemon submit --harness <ref> --prompt <text> [--workspace-mode <mode>] [--wait]
|
|
670
|
-
hitch daemon cancel <run-id>
|
|
671
|
-
|
|
672
|
-
Eval:
|
|
673
|
-
Harbor runs each task in Docker; Hitch executes the selected harness inside that task container.
|
|
674
|
-
Use 'hitch eval setup harbor' for an isolated managed install and 'hitch eval doctor' to verify it.
|
|
675
|
-
Eval accepts exact version: or registered commit: refs. Use --pass-env NAME for extra credentials.
|
|
676
|
-
Every eval references a shared read-only controller runtime bundle by SHA-256 id
|
|
677
|
-
(see 'hitch eval inspect' for the runtime storage kind).
|
|
678
|
-
|
|
679
|
-
Trajectory and feedback:
|
|
680
|
-
Every run records a DSH-compatible canonical trajectory under runs/<run>/trajectory/
|
|
681
|
-
with a trajectory.ref.json pointer. Message feedback is a lifecycle-bound sidecar.
|
|
682
|
-
|
|
683
|
-
Workspace modes:
|
|
684
|
-
shared Use the source directory directly (compatibility default)
|
|
685
|
-
worktree Create a detached worktree from a clean Git HEAD
|
|
686
|
-
copy Copy the current filesystem state into an independent workspace
|
|
687
|
-
|
|
688
|
-
Harness refs:
|
|
689
|
-
codex Installed executable (compatibility default)
|
|
690
|
-
codex@installed Installed executable
|
|
691
|
-
codex@version:0.42.1 Exact published version
|
|
692
|
-
codex@commit:abc1234 Commit from the registered Git source
|
|
693
|
-
codex@git+file:///src#abc1234 Commit from a clean local Git repository
|
|
694
|
-
|
|
695
|
-
Global:
|
|
696
|
-
--root <path> Relocate all Hitch state (default: ~/.hitch)
|
|
697
|
-
`;
|
|
698
|
-
}
|
|
699
|
-
//# sourceMappingURL=cli.js.map
|