@h-rig/runtime 0.0.6-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/bin/rig-agent-dispatch.js +9615 -0
- package/dist/bin/rig-agent.js +9512 -0
- package/dist/bin/rig-browser-tool.js +269 -0
- package/dist/src/agent-mode.js +48 -0
- package/dist/src/baked-secrets.js +121 -0
- package/dist/src/binary-build-worker.js +312 -0
- package/dist/src/binary-run.js +540 -0
- package/dist/src/boundaries.js +1 -0
- package/dist/src/build-time-config.js +25 -0
- package/dist/src/control-plane/agent-roles.js +27 -0
- package/dist/src/control-plane/agent-wrapper.js +9621 -0
- package/dist/src/control-plane/authority-files.js +582 -0
- package/dist/src/control-plane/browser-contract.js +135 -0
- package/dist/src/control-plane/controlled-bash.js +1111 -0
- package/dist/src/control-plane/errors.js +13 -0
- package/dist/src/control-plane/harness-main.js +10828 -0
- package/dist/src/control-plane/hook-materializer.js +75 -0
- package/dist/src/control-plane/hooks/audit-trail.js +353 -0
- package/dist/src/control-plane/hooks/completion-verification.js +7552 -0
- package/dist/src/control-plane/hooks/import-guard.js +890 -0
- package/dist/src/control-plane/hooks/inject-context.js +4189 -0
- package/dist/src/control-plane/hooks/post-edit-lint.js +43 -0
- package/dist/src/control-plane/hooks/safety-guard.js +910 -0
- package/dist/src/control-plane/hooks/scope-guard.js +907 -0
- package/dist/src/control-plane/hooks/shared.js +44 -0
- package/dist/src/control-plane/hooks/submodule-branch.js +7797 -0
- package/dist/src/control-plane/hooks/task-runtime-start.js +7799 -0
- package/dist/src/control-plane/hooks/test-integrity-guard.js +891 -0
- package/dist/src/control-plane/materialize-task-config.js +453 -0
- package/dist/src/control-plane/memory-sync/cli.js +2019 -0
- package/dist/src/control-plane/memory-sync/db.js +753 -0
- package/dist/src/control-plane/memory-sync/embed.js +281 -0
- package/dist/src/control-plane/memory-sync/index.js +2049 -0
- package/dist/src/control-plane/memory-sync/query.js +294 -0
- package/dist/src/control-plane/memory-sync/read.js +784 -0
- package/dist/src/control-plane/memory-sync/types.js +6 -0
- package/dist/src/control-plane/memory-sync/write.js +1547 -0
- package/dist/src/control-plane/native/git-native.js +490 -0
- package/dist/src/control-plane/native/git-ops.js +2860 -0
- package/dist/src/control-plane/native/harness-cli.js +9721 -0
- package/dist/src/control-plane/native/pr-automation.js +373 -0
- package/dist/src/control-plane/native/profile-ops.js +481 -0
- package/dist/src/control-plane/native/repo-ops.js +2342 -0
- package/dist/src/control-plane/native/root-resolver.js +66 -0
- package/dist/src/control-plane/native/run-ops.js +3281 -0
- package/dist/src/control-plane/native/runtime-native-sidecar.js +299 -0
- package/dist/src/control-plane/native/runtime-native.js +392 -0
- package/dist/src/control-plane/native/scope-rules.js +17 -0
- package/dist/src/control-plane/native/task-ops.js +6320 -0
- package/dist/src/control-plane/native/task-state.js +1512 -0
- package/dist/src/control-plane/native/utils.js +535 -0
- package/dist/src/control-plane/native/validator-binaries.js +889 -0
- package/dist/src/control-plane/native/validator.js +2197 -0
- package/dist/src/control-plane/native/verifier.js +3249 -0
- package/dist/src/control-plane/native/workspace-ops.js +1635 -0
- package/dist/src/control-plane/plugin-host-context.js +334 -0
- package/dist/src/control-plane/project-main-pre-run-sync.js +630 -0
- package/dist/src/control-plane/provider/claude-stream-records.js +158 -0
- package/dist/src/control-plane/provider/codex-app-server.js +885 -0
- package/dist/src/control-plane/provider/codex-exec-records.js +203 -0
- package/dist/src/control-plane/provider/rig-task-run-skill.js +39 -0
- package/dist/src/control-plane/provider/runtime-instructions.js +96 -0
- package/dist/src/control-plane/remote.js +854 -0
- package/dist/src/control-plane/repos/index.js +473 -0
- package/dist/src/control-plane/repos/layout.js +124 -0
- package/dist/src/control-plane/repos/mirror/bootstrap.js +268 -0
- package/dist/src/control-plane/repos/mirror/refresh.js +398 -0
- package/dist/src/control-plane/repos/mirror/state.js +167 -0
- package/dist/src/control-plane/repos/registry.js +77 -0
- package/dist/src/control-plane/repos/types.js +1 -0
- package/dist/src/control-plane/runtime/agent-mode.js +48 -0
- package/dist/src/control-plane/runtime/baked-secrets.js +120 -0
- package/dist/src/control-plane/runtime/claude-tool-router-binary.js +343 -0
- package/dist/src/control-plane/runtime/claude-tool-router.js +520 -0
- package/dist/src/control-plane/runtime/context.js +216 -0
- package/dist/src/control-plane/runtime/events.js +218 -0
- package/dist/src/control-plane/runtime/guard-types.js +6 -0
- package/dist/src/control-plane/runtime/guard.js +880 -0
- package/dist/src/control-plane/runtime/image/fingerprint-sidecar.js +1194 -0
- package/dist/src/control-plane/runtime/image/index.js +2255 -0
- package/dist/src/control-plane/runtime/image-fingerprint-sidecar.js +1191 -0
- package/dist/src/control-plane/runtime/image.js +2255 -0
- package/dist/src/control-plane/runtime/index.js +8511 -0
- package/dist/src/control-plane/runtime/isolation/discovery.js +599 -0
- package/dist/src/control-plane/runtime/isolation/home.js +1217 -0
- package/dist/src/control-plane/runtime/isolation/index.js +8193 -0
- package/dist/src/control-plane/runtime/isolation/runner.js +2651 -0
- package/dist/src/control-plane/runtime/isolation/shared.js +501 -0
- package/dist/src/control-plane/runtime/isolation/toolchain.js +1892 -0
- package/dist/src/control-plane/runtime/isolation/types.js +1 -0
- package/dist/src/control-plane/runtime/isolation/worktree.js +509 -0
- package/dist/src/control-plane/runtime/isolation.js +8193 -0
- package/dist/src/control-plane/runtime/overlay.js +67 -0
- package/dist/src/control-plane/runtime/plugin-mode.js +41 -0
- package/dist/src/control-plane/runtime/plugins.js +1131 -0
- package/dist/src/control-plane/runtime/provisioning-env.js +220 -0
- package/dist/src/control-plane/runtime/queue.js +8358 -0
- package/dist/src/control-plane/runtime/rig-shell.js +205 -0
- package/dist/src/control-plane/runtime/rig-tools.js +182 -0
- package/dist/src/control-plane/runtime/runner-context.js +1 -0
- package/dist/src/control-plane/runtime/runtime-paths.js +184 -0
- package/dist/src/control-plane/runtime/sandbox/backend-bwrap.js +311 -0
- package/dist/src/control-plane/runtime/sandbox/backend-none.js +21 -0
- package/dist/src/control-plane/runtime/sandbox/backend-seatbelt.js +268 -0
- package/dist/src/control-plane/runtime/sandbox/backend.js +1718 -0
- package/dist/src/control-plane/runtime/sandbox/orchestrator.js +1745 -0
- package/dist/src/control-plane/runtime/sandbox/utils.js +137 -0
- package/dist/src/control-plane/runtime/sandbox-backend-bwrap.js +311 -0
- package/dist/src/control-plane/runtime/sandbox-backend-none.js +21 -0
- package/dist/src/control-plane/runtime/sandbox-backend-seatbelt.js +268 -0
- package/dist/src/control-plane/runtime/sandbox-backend.js +1718 -0
- package/dist/src/control-plane/runtime/sandbox-orchestrator.js +1745 -0
- package/dist/src/control-plane/runtime/sandbox-utils.js +137 -0
- package/dist/src/control-plane/runtime/snapshot/index.js +454 -0
- package/dist/src/control-plane/runtime/snapshot/sidecar.js +502 -0
- package/dist/src/control-plane/runtime/snapshot/task-run.js +1578 -0
- package/dist/src/control-plane/runtime/snapshot-sidecar.js +498 -0
- package/dist/src/control-plane/runtime/snapshot.js +454 -0
- package/dist/src/control-plane/runtime/task-run-snapshot.js +1578 -0
- package/dist/src/control-plane/runtime/tool-gateway.js +422 -0
- package/dist/src/control-plane/runtime/tooling/browser-tools.js +32 -0
- package/dist/src/control-plane/runtime/tooling/claude-router-binary.js +343 -0
- package/dist/src/control-plane/runtime/tooling/claude-router.js +524 -0
- package/dist/src/control-plane/runtime/tooling/file-tools.js +182 -0
- package/dist/src/control-plane/runtime/tooling/gateway.js +422 -0
- package/dist/src/control-plane/runtime/tooling/index.js +1290 -0
- package/dist/src/control-plane/runtime/tooling/shell.js +205 -0
- package/dist/src/control-plane/runtime/types.js +1 -0
- package/dist/src/control-plane/setup-version.js +14 -0
- package/dist/src/control-plane/state-sync/index.js +1509 -0
- package/dist/src/control-plane/state-sync/read.js +856 -0
- package/dist/src/control-plane/state-sync/reconcile.js +260 -0
- package/dist/src/control-plane/state-sync/repo.js +302 -0
- package/dist/src/control-plane/state-sync/types.js +111 -0
- package/dist/src/control-plane/state-sync/write.js +1469 -0
- package/dist/src/control-plane/task-fields.js +38 -0
- package/dist/src/control-plane/task-source-bootstrap.js +46 -0
- package/dist/src/control-plane/task-source.js +30 -0
- package/dist/src/control-plane/tasks/legacy-task-config-source.js +130 -0
- package/dist/src/control-plane/tasks/plugin-task-source.js +103 -0
- package/dist/src/control-plane/tasks/source-aware-task-config-source.js +611 -0
- package/dist/src/control-plane/tasks/source-lifecycle.js +1093 -0
- package/dist/src/control-plane/tasks/task-record-reader.js +9 -0
- package/dist/src/control-plane/validators/boundary/public-apis.js +107 -0
- package/dist/src/control-plane/validators/integration/_shared.js +51 -0
- package/dist/src/control-plane/validators/integration/adm-audit-http.js +85 -0
- package/dist/src/control-plane/validators/integration/adm-auth-http.js +78 -0
- package/dist/src/control-plane/validators/integration/adm-issuer-http.js +80 -0
- package/dist/src/control-plane/validators/integration/adm-migration.js +78 -0
- package/dist/src/control-plane/validators/integration/adm-scaffold.js +78 -0
- package/dist/src/control-plane/validators/runtime-registration.js +64 -0
- package/dist/src/control-plane/validators/shared.js +683 -0
- package/dist/src/events.js +218 -0
- package/dist/src/execution.js +35 -0
- package/dist/src/index.js +1633 -0
- package/dist/src/layout.js +145 -0
- package/dist/src/local-server.js +202 -0
- package/dist/src/plugins.js +329 -0
- package/dist/src/remote-http.js +83 -0
- package/dist/src/runtime-context.js +216 -0
- package/dist/src/types.js +1 -0
- package/native/darwin-arm64/bin/rig-git +0 -0
- package/native/darwin-arm64/bin/rig-shell +0 -0
- package/native/darwin-arm64/bin/rig-tools +0 -0
- package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
- package/native/darwin-arm64/lib/runtime-native.dylib +0 -0
- package/native/darwin-arm64/manifest.json +1 -0
- package/native/linux-x64/bin/rig-git +0 -0
- package/native/linux-x64/bin/rig-shell +0 -0
- package/native/linux-x64/bin/rig-tools +0 -0
- package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
- package/native/linux-x64/lib/runtime-native.so +0 -0
- package/native/linux-x64/manifest.json +1 -0
- package/package.json +74 -0
- package/skills/rig-task-run.md +71 -0
|
@@ -0,0 +1,1217 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/control-plane/runtime/isolation/home.ts
|
|
3
|
+
import {
|
|
4
|
+
chmodSync,
|
|
5
|
+
copyFileSync as copyFileSync2,
|
|
6
|
+
cpSync,
|
|
7
|
+
existsSync as existsSync6,
|
|
8
|
+
mkdirSync as mkdirSync3,
|
|
9
|
+
statSync as statSync2,
|
|
10
|
+
writeFileSync as writeFileSync2
|
|
11
|
+
} from "fs";
|
|
12
|
+
import { mkdir } from "fs/promises";
|
|
13
|
+
import { basename as basename2, delimiter, resolve as resolve7 } from "path";
|
|
14
|
+
|
|
15
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
16
|
+
import { existsSync as existsSync2, readdirSync, realpathSync } from "fs";
|
|
17
|
+
import { resolve as resolve2 } from "path";
|
|
18
|
+
|
|
19
|
+
// packages/runtime/src/layout.ts
|
|
20
|
+
import { existsSync } from "fs";
|
|
21
|
+
import { basename, dirname, resolve } from "path";
|
|
22
|
+
function resolveMonorepoRoot(projectRoot) {
|
|
23
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
24
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
25
|
+
if (explicit) {
|
|
26
|
+
const explicitRoot = resolve(explicit);
|
|
27
|
+
const explicitParent = dirname(explicitRoot);
|
|
28
|
+
if (basename(explicitParent) === ".worktrees") {
|
|
29
|
+
const owner = dirname(explicitParent);
|
|
30
|
+
const ownerHasGit = existsSync(resolve(owner, ".git"));
|
|
31
|
+
const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
|
|
32
|
+
const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
|
|
33
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
34
|
+
return owner;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
37
|
+
}
|
|
38
|
+
if (!existsSync(resolve(explicitRoot, ".git"))) {
|
|
39
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
40
|
+
}
|
|
41
|
+
const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
|
|
42
|
+
const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
|
|
43
|
+
if (!hasTaskConfig && !hasRigConfig) {
|
|
44
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
45
|
+
}
|
|
46
|
+
return explicitRoot;
|
|
47
|
+
}
|
|
48
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
49
|
+
if (basename(projectParent) === ".worktrees") {
|
|
50
|
+
const worktreeOwner = dirname(projectParent);
|
|
51
|
+
const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
|
|
52
|
+
const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
|
|
53
|
+
const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
|
|
54
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
55
|
+
return worktreeOwner;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return normalizedProjectRoot;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
62
|
+
function uniq(values) {
|
|
63
|
+
return [...new Set(values)];
|
|
64
|
+
}
|
|
65
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
66
|
+
function resolveBunBinaryPath() {
|
|
67
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
68
|
+
if (explicit) {
|
|
69
|
+
return explicit;
|
|
70
|
+
}
|
|
71
|
+
const pathBun = normalizeExecutablePath(Bun.which("bun")?.trim());
|
|
72
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
73
|
+
return pathBun;
|
|
74
|
+
}
|
|
75
|
+
const home = process.env.HOME?.trim();
|
|
76
|
+
const fallbackCandidates = [
|
|
77
|
+
home ? resolve2(home, ".bun/bin/bun") : "",
|
|
78
|
+
"/opt/homebrew/bin/bun",
|
|
79
|
+
"/usr/local/bin/bun",
|
|
80
|
+
"/usr/bin/bun"
|
|
81
|
+
];
|
|
82
|
+
for (const candidate of fallbackCandidates) {
|
|
83
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
84
|
+
if (normalized) {
|
|
85
|
+
return normalized;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
89
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
90
|
+
return execPath;
|
|
91
|
+
}
|
|
92
|
+
throw new Error("bun not found in PATH");
|
|
93
|
+
}
|
|
94
|
+
function resolveClaudeBinaryPath() {
|
|
95
|
+
const explicit = normalizeExecutablePath(process.env.RIG_CLAUDE_PATH?.trim());
|
|
96
|
+
if (explicit) {
|
|
97
|
+
return explicit;
|
|
98
|
+
}
|
|
99
|
+
const pathClaude = normalizeExecutablePath(Bun.which("claude")?.trim());
|
|
100
|
+
if (pathClaude && !looksLikeRuntimeGateway(pathClaude)) {
|
|
101
|
+
return pathClaude;
|
|
102
|
+
}
|
|
103
|
+
const home = process.env.HOME?.trim();
|
|
104
|
+
const fallbackCandidates = [
|
|
105
|
+
home ? resolve2(home, ".local/bin/claude") : "",
|
|
106
|
+
home ? resolve2(home, ".local/share/claude/local/claude") : "",
|
|
107
|
+
"/opt/homebrew/bin/claude",
|
|
108
|
+
"/usr/local/bin/claude",
|
|
109
|
+
"/usr/bin/claude"
|
|
110
|
+
];
|
|
111
|
+
for (const candidate of fallbackCandidates) {
|
|
112
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
113
|
+
if (normalized) {
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw new Error("claude not found in PATH");
|
|
118
|
+
}
|
|
119
|
+
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
120
|
+
return resolve2(bunBinaryPath, "../..");
|
|
121
|
+
}
|
|
122
|
+
function resolveClaudeInstallDir() {
|
|
123
|
+
const realPath = resolveClaudeBinaryPath();
|
|
124
|
+
return resolve2(realPath, "..");
|
|
125
|
+
}
|
|
126
|
+
function resolveNodeInstallDir() {
|
|
127
|
+
const preferredNode = resolvePreferredNodeBinary();
|
|
128
|
+
if (!preferredNode)
|
|
129
|
+
return null;
|
|
130
|
+
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
131
|
+
if (explicitNode && resolve2(explicitNode) === resolve2(preferredNode)) {
|
|
132
|
+
return preferredNode.endsWith("/bin/node") ? resolve2(preferredNode, "../..") : resolve2(preferredNode, "..");
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const realPath = realpathSync(preferredNode);
|
|
136
|
+
if (realPath.endsWith("/bin/node")) {
|
|
137
|
+
return resolve2(realPath, "../..");
|
|
138
|
+
}
|
|
139
|
+
return resolve2(realPath, "..");
|
|
140
|
+
} catch {
|
|
141
|
+
return resolve2(preferredNode, "..");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function resolvePreferredNodeBinary() {
|
|
145
|
+
const candidates = [];
|
|
146
|
+
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
147
|
+
if (envNode) {
|
|
148
|
+
const explicit = resolve2(envNode);
|
|
149
|
+
if (existsSync2(explicit)) {
|
|
150
|
+
return explicit;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const nvmBin = process.env.NVM_BIN?.trim();
|
|
154
|
+
if (nvmBin) {
|
|
155
|
+
candidates.push(resolve2(nvmBin, "node"));
|
|
156
|
+
}
|
|
157
|
+
const home = process.env.HOME?.trim();
|
|
158
|
+
if (home) {
|
|
159
|
+
const nvmVersionsDir = resolve2(home, ".nvm/versions/node");
|
|
160
|
+
if (existsSync2(nvmVersionsDir)) {
|
|
161
|
+
try {
|
|
162
|
+
const versionDirs = readdirSync(nvmVersionsDir).map((entry) => entry.trim()).filter((entry) => /^v\d+\.\d+\.\d+$/.test(entry)).sort((a, b) => Bun.semver.order(b.replace(/^v/, ""), a.replace(/^v/, "")));
|
|
163
|
+
for (const versionDir of versionDirs) {
|
|
164
|
+
candidates.push(resolve2(nvmVersionsDir, versionDir, "bin/node"));
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const whichNode = Bun.which("node");
|
|
170
|
+
if (whichNode) {
|
|
171
|
+
candidates.push(whichNode);
|
|
172
|
+
}
|
|
173
|
+
const deduped = uniq(candidates.map((candidate) => resolve2(candidate)));
|
|
174
|
+
const existing = deduped.filter((candidate) => existsSync2(candidate));
|
|
175
|
+
if (existing.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const stable = existing.find((candidate) => {
|
|
179
|
+
const major = inferNodeMajor(candidate);
|
|
180
|
+
return typeof major === "number" && major >= 18 && major <= 24;
|
|
181
|
+
});
|
|
182
|
+
if (stable) {
|
|
183
|
+
return stable;
|
|
184
|
+
}
|
|
185
|
+
return existing[0] ?? null;
|
|
186
|
+
}
|
|
187
|
+
function inferNodeMajor(nodeBinaryPath) {
|
|
188
|
+
const normalized = resolve2(nodeBinaryPath).replace(/\\/g, "/");
|
|
189
|
+
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
190
|
+
if (!match) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const major = Number.parseInt(match[1], 10);
|
|
194
|
+
return Number.isFinite(major) ? major : null;
|
|
195
|
+
}
|
|
196
|
+
function normalizeExecutablePath(candidate) {
|
|
197
|
+
if (!candidate) {
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
const normalized = resolve2(candidate);
|
|
201
|
+
if (!existsSync2(normalized)) {
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
return realpathSync(normalized);
|
|
206
|
+
} catch {
|
|
207
|
+
return normalized;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
211
|
+
const normalized = resolve2(candidate).replace(/\\/g, "/");
|
|
212
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// packages/runtime/src/control-plane/runtime/baked-secrets.ts
|
|
216
|
+
var BAKED_RUNTIME_SECRETS = {
|
|
217
|
+
ANTHROPIC_API_KEY: typeof RIG_BAKED_ANTHROPIC_API_KEY !== "undefined" ? RIG_BAKED_ANTHROPIC_API_KEY : "",
|
|
218
|
+
OPENAI_API_KEY: typeof RIG_BAKED_OPENAI_API_KEY !== "undefined" ? RIG_BAKED_OPENAI_API_KEY : "",
|
|
219
|
+
OPENROUTER_API_KEY: typeof RIG_BAKED_OPENROUTER_API_KEY !== "undefined" ? RIG_BAKED_OPENROUTER_API_KEY : "",
|
|
220
|
+
AI_REVIEW_MODE: typeof RIG_BAKED_AI_REVIEW_MODE !== "undefined" ? RIG_BAKED_AI_REVIEW_MODE : "",
|
|
221
|
+
AI_REVIEW_PROVIDER: typeof RIG_BAKED_AI_REVIEW_PROVIDER !== "undefined" ? RIG_BAKED_AI_REVIEW_PROVIDER : "",
|
|
222
|
+
GREPTILE_API_BASE: typeof RIG_BAKED_GREPTILE_API_BASE !== "undefined" ? RIG_BAKED_GREPTILE_API_BASE : "",
|
|
223
|
+
GREPTILE_REMOTE: typeof RIG_BAKED_GREPTILE_REMOTE !== "undefined" ? RIG_BAKED_GREPTILE_REMOTE : "",
|
|
224
|
+
GREPTILE_REPOSITORY: typeof RIG_BAKED_GREPTILE_REPOSITORY !== "undefined" ? RIG_BAKED_GREPTILE_REPOSITORY : "",
|
|
225
|
+
GREPTILE_CONTEXT_BRANCH: typeof RIG_BAKED_GREPTILE_CONTEXT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_CONTEXT_BRANCH : "",
|
|
226
|
+
GREPTILE_DEFAULT_BRANCH: typeof RIG_BAKED_GREPTILE_DEFAULT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_DEFAULT_BRANCH : "",
|
|
227
|
+
GREPTILE_API_KEY: typeof RIG_BAKED_GREPTILE_API_KEY !== "undefined" ? RIG_BAKED_GREPTILE_API_KEY : "",
|
|
228
|
+
GREPTILE_GITHUB_TOKEN: typeof RIG_BAKED_GREPTILE_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GREPTILE_GITHUB_TOKEN : "",
|
|
229
|
+
GREPTILE_POLL_ATTEMPTS: typeof RIG_BAKED_GREPTILE_POLL_ATTEMPTS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_ATTEMPTS : "",
|
|
230
|
+
GREPTILE_POLL_INTERVAL_MS: typeof RIG_BAKED_GREPTILE_POLL_INTERVAL_MS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_INTERVAL_MS : "",
|
|
231
|
+
GH_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
|
|
232
|
+
GITHUB_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
|
|
233
|
+
GITHUB_SSH_KEY: typeof RIG_BAKED_GITHUB_SSH_KEY !== "undefined" ? RIG_BAKED_GITHUB_SSH_KEY : "",
|
|
234
|
+
AWS_ACCESS_KEY_ID: typeof RIG_BAKED_AWS_ACCESS_KEY_ID !== "undefined" ? RIG_BAKED_AWS_ACCESS_KEY_ID : "",
|
|
235
|
+
AWS_SECRET_ACCESS_KEY: typeof RIG_BAKED_AWS_SECRET_ACCESS_KEY !== "undefined" ? RIG_BAKED_AWS_SECRET_ACCESS_KEY : "",
|
|
236
|
+
AWS_REGION: typeof RIG_BAKED_AWS_REGION !== "undefined" ? RIG_BAKED_AWS_REGION : "",
|
|
237
|
+
LINEAR_API_KEY: typeof RIG_BAKED_LINEAR_API_KEY !== "undefined" ? RIG_BAKED_LINEAR_API_KEY : "",
|
|
238
|
+
LINEAR_WEBHOOK_SECRET: typeof RIG_BAKED_LINEAR_WEBHOOK_SECRET !== "undefined" ? RIG_BAKED_LINEAR_WEBHOOK_SECRET : ""
|
|
239
|
+
};
|
|
240
|
+
function resolveRuntimeSecrets(env, baked = BAKED_RUNTIME_SECRETS) {
|
|
241
|
+
const resolved = {};
|
|
242
|
+
const keys = new Set([
|
|
243
|
+
...Object.keys(BAKED_RUNTIME_SECRETS),
|
|
244
|
+
...Object.keys(baked)
|
|
245
|
+
]);
|
|
246
|
+
for (const key of keys) {
|
|
247
|
+
const envValue = env[key]?.trim();
|
|
248
|
+
const bakedValue = baked[key]?.trim();
|
|
249
|
+
if (envValue) {
|
|
250
|
+
resolved[key] = envValue;
|
|
251
|
+
} else if (bakedValue) {
|
|
252
|
+
resolved[key] = bakedValue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return resolved;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// packages/runtime/src/control-plane/native/git-native.ts
|
|
259
|
+
import { tmpdir } from "os";
|
|
260
|
+
import { dirname as dirname2, isAbsolute, resolve as resolve3 } from "path";
|
|
261
|
+
var sharedGitNativeOutputDir = resolve3(tmpdir(), "rig-native");
|
|
262
|
+
var sharedGitNativeOutputPath = resolve3(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
263
|
+
function runtimeRigGitFileName() {
|
|
264
|
+
return `rig-git${process.platform === "win32" ? ".exe" : ""}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// packages/runtime/src/control-plane/native/runtime-native.ts
|
|
268
|
+
import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
|
|
269
|
+
import { copyFileSync, existsSync as existsSync3, mkdirSync, renameSync, rmSync, statSync } from "fs";
|
|
270
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
271
|
+
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
272
|
+
var sharedNativeRuntimeOutputDir = resolve4(tmpdir2(), "rig-native");
|
|
273
|
+
var sharedNativeRuntimeOutputPath = resolve4(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
|
|
274
|
+
var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
|
|
275
|
+
var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
|
|
276
|
+
async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
|
|
277
|
+
if (await buildNativeRuntimeLibrary(outputPath, options)) {
|
|
278
|
+
return outputPath;
|
|
279
|
+
}
|
|
280
|
+
return !options.force && existsSync3(outputPath) ? outputPath : null;
|
|
281
|
+
}
|
|
282
|
+
async function materializeNativeRuntimeLibrary(targetDir) {
|
|
283
|
+
const sourcePath = await ensureNativeRuntimeLibraryPath();
|
|
284
|
+
if (!sourcePath) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const targetPath = resolve4(targetDir, colocatedNativeRuntimeFileName);
|
|
288
|
+
mkdirSync(targetDir, { recursive: true });
|
|
289
|
+
const needsCopy = !existsSync3(targetPath) || statSync(sourcePath).mtimeMs > statSync(targetPath).mtimeMs;
|
|
290
|
+
if (needsCopy) {
|
|
291
|
+
copyFileSync(sourcePath, targetPath);
|
|
292
|
+
}
|
|
293
|
+
return targetPath;
|
|
294
|
+
}
|
|
295
|
+
async function loadNativeRuntimeLibrary() {
|
|
296
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
for (const candidate of nativeRuntimeLibraryCandidates()) {
|
|
300
|
+
if (!candidate || !existsSync3(candidate)) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const loaded = tryDlopenNativeRuntimeLibrary(candidate);
|
|
304
|
+
if (loaded) {
|
|
305
|
+
return loaded;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
|
|
309
|
+
if (!builtLibraryPath) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
|
|
313
|
+
}
|
|
314
|
+
function nativePackageLibraryCandidates(fromDir, names) {
|
|
315
|
+
const candidates = [];
|
|
316
|
+
let cursor = resolve4(fromDir);
|
|
317
|
+
for (let index = 0;index < 8; index += 1) {
|
|
318
|
+
for (const name of names) {
|
|
319
|
+
candidates.push(resolve4(cursor, "native", `${process.platform}-${process.arch}`, name), resolve4(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve4(cursor, "native", name), resolve4(cursor, "native", "lib", name));
|
|
320
|
+
}
|
|
321
|
+
const parent = dirname3(cursor);
|
|
322
|
+
if (parent === cursor)
|
|
323
|
+
break;
|
|
324
|
+
cursor = parent;
|
|
325
|
+
}
|
|
326
|
+
return candidates;
|
|
327
|
+
}
|
|
328
|
+
function nativeRuntimeLibraryCandidates() {
|
|
329
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
|
|
330
|
+
const execDir = process.execPath?.trim() ? dirname3(process.execPath.trim()) : "";
|
|
331
|
+
const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
|
|
332
|
+
return [...new Set([
|
|
333
|
+
explicit,
|
|
334
|
+
...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
|
|
335
|
+
execDir ? resolve4(execDir, colocatedNativeRuntimeFileName) : "",
|
|
336
|
+
execDir ? resolve4(execDir, platformSpecific) : "",
|
|
337
|
+
execDir ? resolve4(execDir, "..", colocatedNativeRuntimeFileName) : "",
|
|
338
|
+
execDir ? resolve4(execDir, "..", platformSpecific) : "",
|
|
339
|
+
execDir ? resolve4(execDir, "lib", colocatedNativeRuntimeFileName) : "",
|
|
340
|
+
execDir ? resolve4(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
|
|
341
|
+
sharedNativeRuntimeOutputPath
|
|
342
|
+
].filter(Boolean))];
|
|
343
|
+
}
|
|
344
|
+
function resolveNativeRuntimeSourcePath() {
|
|
345
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
|
|
346
|
+
if (explicit && existsSync3(explicit)) {
|
|
347
|
+
return explicit;
|
|
348
|
+
}
|
|
349
|
+
const bundled = resolve4(import.meta.dir, "../../../native/snapshot.zig");
|
|
350
|
+
return existsSync3(bundled) ? bundled : null;
|
|
351
|
+
}
|
|
352
|
+
async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
353
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
const zigBinary = Bun.which("zig");
|
|
357
|
+
const sourcePath = resolveNativeRuntimeSourcePath();
|
|
358
|
+
if (!zigBinary || !sourcePath) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
362
|
+
try {
|
|
363
|
+
mkdirSync(dirname3(outputPath), { recursive: true });
|
|
364
|
+
const needsBuild = options.force === true || !existsSync3(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
|
|
365
|
+
if (!needsBuild) {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
const build = Bun.spawn([
|
|
369
|
+
zigBinary,
|
|
370
|
+
"build-lib",
|
|
371
|
+
sourcePath,
|
|
372
|
+
"-dynamic",
|
|
373
|
+
"-O",
|
|
374
|
+
"ReleaseFast",
|
|
375
|
+
`-femit-bin=${tempOutputPath}`
|
|
376
|
+
], {
|
|
377
|
+
cwd: import.meta.dir,
|
|
378
|
+
stdout: "pipe",
|
|
379
|
+
stderr: "pipe"
|
|
380
|
+
});
|
|
381
|
+
const exitCode = await build.exited;
|
|
382
|
+
if (exitCode !== 0 || !existsSync3(tempOutputPath)) {
|
|
383
|
+
rmSync(tempOutputPath, { force: true });
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
renameSync(tempOutputPath, outputPath);
|
|
387
|
+
return true;
|
|
388
|
+
} catch {
|
|
389
|
+
rmSync(tempOutputPath, { force: true });
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function tryDlopenNativeRuntimeLibrary(outputPath) {
|
|
394
|
+
try {
|
|
395
|
+
return dlopen(outputPath, {
|
|
396
|
+
rig_scope_match: {
|
|
397
|
+
args: ["ptr", "ptr"],
|
|
398
|
+
returns: "u8"
|
|
399
|
+
},
|
|
400
|
+
snapshot_capture: {
|
|
401
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
402
|
+
returns: "ptr"
|
|
403
|
+
},
|
|
404
|
+
snapshot_delta: {
|
|
405
|
+
args: ["ptr", "ptr"],
|
|
406
|
+
returns: "ptr"
|
|
407
|
+
},
|
|
408
|
+
snapshot_store_delta: {
|
|
409
|
+
args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
410
|
+
returns: "ptr"
|
|
411
|
+
},
|
|
412
|
+
snapshot_inspect_delta: {
|
|
413
|
+
args: ["ptr", "u64"],
|
|
414
|
+
returns: "ptr"
|
|
415
|
+
},
|
|
416
|
+
snapshot_apply_delta: {
|
|
417
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
418
|
+
returns: "ptr"
|
|
419
|
+
},
|
|
420
|
+
snapshot_release: {
|
|
421
|
+
args: ["ptr"],
|
|
422
|
+
returns: "void"
|
|
423
|
+
},
|
|
424
|
+
runtime_hash_file: {
|
|
425
|
+
args: ["ptr", "u64"],
|
|
426
|
+
returns: "ptr"
|
|
427
|
+
},
|
|
428
|
+
runtime_hash_tree: {
|
|
429
|
+
args: ["ptr", "u64"],
|
|
430
|
+
returns: "ptr"
|
|
431
|
+
},
|
|
432
|
+
runtime_prepare_paths: {
|
|
433
|
+
args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
434
|
+
returns: "ptr"
|
|
435
|
+
},
|
|
436
|
+
runtime_link_dependency_layer: {
|
|
437
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
438
|
+
returns: "ptr"
|
|
439
|
+
},
|
|
440
|
+
runtime_scan_worktrees: {
|
|
441
|
+
args: ["ptr", "u64"],
|
|
442
|
+
returns: "ptr"
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
} catch {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// packages/runtime/src/control-plane/browser-contract.ts
|
|
451
|
+
function browserEnvFromContext(browser) {
|
|
452
|
+
if (!browser?.required) {
|
|
453
|
+
return {};
|
|
454
|
+
}
|
|
455
|
+
const env = {
|
|
456
|
+
RIG_BROWSER_REQUIRED: "1",
|
|
457
|
+
RIG_BROWSER_PRESET: browser.preset,
|
|
458
|
+
RIG_BROWSER_MODE: browser.mode,
|
|
459
|
+
RIG_BROWSER_STATE_DIR: browser.stateDir,
|
|
460
|
+
RIG_BROWSER_PROFILE: browser.effectiveProfile,
|
|
461
|
+
RIG_BROWSER_BASE_PROFILE: browser.defaultProfile,
|
|
462
|
+
RIG_BROWSER_ATTACH_URL: browser.effectiveAttachUrl,
|
|
463
|
+
RIG_BROWSER_DEFAULT_ATTACH_URL: browser.defaultAttachUrl,
|
|
464
|
+
RIG_BROWSER_LAUNCH_HELPER: browser.launchHelper,
|
|
465
|
+
RIG_BROWSER_CHECK_HELPER: browser.checkHelper,
|
|
466
|
+
RIG_BROWSER_ATTACH_INFO_HELPER: browser.attachInfoHelper,
|
|
467
|
+
RIG_BROWSER_E2E_HELPER: browser.e2eHelper,
|
|
468
|
+
RIG_BROWSER_RESET_HELPER: browser.resetProfileHelper
|
|
469
|
+
};
|
|
470
|
+
if (browser.devCommand) {
|
|
471
|
+
env.RIG_BROWSER_DEV_COMMAND = browser.devCommand;
|
|
472
|
+
}
|
|
473
|
+
if (browser.launchCommand) {
|
|
474
|
+
env.RIG_BROWSER_LAUNCH_COMMAND = browser.launchCommand;
|
|
475
|
+
}
|
|
476
|
+
if (browser.checkCommand) {
|
|
477
|
+
env.RIG_BROWSER_CHECK_COMMAND = browser.checkCommand;
|
|
478
|
+
}
|
|
479
|
+
if (browser.e2eCommand) {
|
|
480
|
+
env.RIG_BROWSER_E2E_COMMAND = browser.e2eCommand;
|
|
481
|
+
}
|
|
482
|
+
return env;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// packages/runtime/src/control-plane/runtime/context.ts
|
|
486
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
487
|
+
import { dirname as dirname4, resolve as resolve5 } from "path";
|
|
488
|
+
var RUNTIME_CONTEXT_ENV = "RIG_RUNTIME_CONTEXT_FILE";
|
|
489
|
+
var runtimeContextStringFields = [
|
|
490
|
+
"runtimeId",
|
|
491
|
+
"taskId",
|
|
492
|
+
"role",
|
|
493
|
+
"workspaceDir",
|
|
494
|
+
"stateDir",
|
|
495
|
+
"logsDir",
|
|
496
|
+
"sessionDir",
|
|
497
|
+
"sessionFile",
|
|
498
|
+
"policyFile",
|
|
499
|
+
"binDir",
|
|
500
|
+
"createdAt"
|
|
501
|
+
];
|
|
502
|
+
var runtimeContextArrayFields = ["scopes", "validation"];
|
|
503
|
+
var runtimeContextOptionalStringFields = [
|
|
504
|
+
"artifactRoot",
|
|
505
|
+
"hostProjectRoot",
|
|
506
|
+
"monorepoMainRoot",
|
|
507
|
+
"monorepoBaseRef",
|
|
508
|
+
"monorepoBaseCommit"
|
|
509
|
+
];
|
|
510
|
+
function loadRuntimeContext(path) {
|
|
511
|
+
const absPath = resolve5(path);
|
|
512
|
+
if (!existsSync4(absPath)) {
|
|
513
|
+
throw new Error(`RuntimeTaskContext file not found: ${absPath}`);
|
|
514
|
+
}
|
|
515
|
+
let raw;
|
|
516
|
+
try {
|
|
517
|
+
raw = JSON.parse(readFileSync(absPath, "utf8"));
|
|
518
|
+
} catch (err) {
|
|
519
|
+
throw new Error(`Failed to parse RuntimeTaskContext at ${absPath}: ${String(err)}`);
|
|
520
|
+
}
|
|
521
|
+
if (typeof raw !== "object" || raw === null) {
|
|
522
|
+
throw new Error(`RuntimeTaskContext at ${absPath} is not an object`);
|
|
523
|
+
}
|
|
524
|
+
const obj = raw;
|
|
525
|
+
for (const field of runtimeContextStringFields) {
|
|
526
|
+
if (typeof obj[field] !== "string" || obj[field].length === 0) {
|
|
527
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a non-empty string (at ${absPath})`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
for (const field of runtimeContextArrayFields) {
|
|
531
|
+
if (!Array.isArray(obj[field])) {
|
|
532
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be an array (at ${absPath})`);
|
|
533
|
+
}
|
|
534
|
+
if (!obj[field].every((entry) => typeof entry === "string")) {
|
|
535
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a string[] (at ${absPath})`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
for (const field of runtimeContextOptionalStringFields) {
|
|
539
|
+
if (field in obj && obj[field] !== undefined && typeof obj[field] !== "string") {
|
|
540
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a string when present (at ${absPath})`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (obj.browser !== undefined) {
|
|
544
|
+
if (typeof obj.browser !== "object" || obj.browser === null || Array.isArray(obj.browser)) {
|
|
545
|
+
throw new Error(`RuntimeTaskContext field "browser" must be an object when present (at ${absPath})`);
|
|
546
|
+
}
|
|
547
|
+
const browser = obj.browser;
|
|
548
|
+
for (const field of [
|
|
549
|
+
"preset",
|
|
550
|
+
"mode",
|
|
551
|
+
"stateDir",
|
|
552
|
+
"defaultProfile",
|
|
553
|
+
"effectiveProfile",
|
|
554
|
+
"defaultAttachUrl",
|
|
555
|
+
"effectiveAttachUrl",
|
|
556
|
+
"launchHelper",
|
|
557
|
+
"checkHelper",
|
|
558
|
+
"attachInfoHelper",
|
|
559
|
+
"e2eHelper",
|
|
560
|
+
"resetProfileHelper"
|
|
561
|
+
]) {
|
|
562
|
+
if (typeof browser[field] !== "string" || browser[field].length === 0) {
|
|
563
|
+
throw new Error(`RuntimeTaskContext field "browser.${field}" must be a non-empty string (at ${absPath})`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
for (const field of ["devCommand", "launchCommand", "checkCommand", "e2eCommand"]) {
|
|
567
|
+
if (browser[field] !== undefined && typeof browser[field] !== "string") {
|
|
568
|
+
throw new Error(`RuntimeTaskContext field "browser.${field}" must be a string when present (at ${absPath})`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (typeof browser.required !== "boolean") {
|
|
572
|
+
throw new Error(`RuntimeTaskContext field "browser.required" must be a boolean (at ${absPath})`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (obj.memory !== undefined) {
|
|
576
|
+
if (typeof obj.memory !== "object" || obj.memory === null || Array.isArray(obj.memory)) {
|
|
577
|
+
throw new Error(`RuntimeTaskContext field "memory" must be an object when present (at ${absPath})`);
|
|
578
|
+
}
|
|
579
|
+
const memory = obj.memory;
|
|
580
|
+
for (const field of ["canonicalPath", "canonicalRef", "canonicalBaseOid", "hydratedPath"]) {
|
|
581
|
+
if (typeof memory[field] !== "string" || memory[field].length === 0) {
|
|
582
|
+
throw new Error(`RuntimeTaskContext field "memory.${field}" must be a non-empty string (at ${absPath})`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (typeof memory.createdFresh !== "boolean") {
|
|
586
|
+
throw new Error(`RuntimeTaskContext field "memory.createdFresh" must be a boolean (at ${absPath})`);
|
|
587
|
+
}
|
|
588
|
+
if (typeof memory.retrieval !== "object" || memory.retrieval === null || Array.isArray(memory.retrieval)) {
|
|
589
|
+
throw new Error(`RuntimeTaskContext field "memory.retrieval" must be an object (at ${absPath})`);
|
|
590
|
+
}
|
|
591
|
+
const retrieval = memory.retrieval;
|
|
592
|
+
for (const field of ["topK", "lexicalWeight", "vectorWeight", "recencyWeight", "confidenceWeight"]) {
|
|
593
|
+
if (typeof retrieval[field] !== "number" || Number.isNaN(retrieval[field])) {
|
|
594
|
+
throw new Error(`RuntimeTaskContext field "memory.retrieval.${field}" must be a number (at ${absPath})`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (obj.initialDirtyFiles !== undefined) {
|
|
599
|
+
if (typeof obj.initialDirtyFiles !== "object" || obj.initialDirtyFiles === null || Array.isArray(obj.initialDirtyFiles)) {
|
|
600
|
+
throw new Error(`RuntimeTaskContext field "initialDirtyFiles" must be an object when present (at ${absPath})`);
|
|
601
|
+
}
|
|
602
|
+
const dirtyFiles = obj.initialDirtyFiles;
|
|
603
|
+
for (const key of ["project", "monorepo"]) {
|
|
604
|
+
if (dirtyFiles[key] === undefined) {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (!Array.isArray(dirtyFiles[key]) || !dirtyFiles[key].every((entry) => typeof entry === "string")) {
|
|
608
|
+
throw new Error(`RuntimeTaskContext field "initialDirtyFiles.${key}" must be a string[] when present (at ${absPath})`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (obj.initialHeadCommits !== undefined) {
|
|
613
|
+
if (typeof obj.initialHeadCommits !== "object" || obj.initialHeadCommits === null || Array.isArray(obj.initialHeadCommits)) {
|
|
614
|
+
throw new Error(`RuntimeTaskContext field "initialHeadCommits" must be an object when present (at ${absPath})`);
|
|
615
|
+
}
|
|
616
|
+
const headCommits = obj.initialHeadCommits;
|
|
617
|
+
for (const key of ["project", "monorepo"]) {
|
|
618
|
+
if (headCommits[key] === undefined) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (typeof headCommits[key] !== "string") {
|
|
622
|
+
throw new Error(`RuntimeTaskContext field "initialHeadCommits.${key}" must be a string when present (at ${absPath})`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return obj;
|
|
627
|
+
}
|
|
628
|
+
function runtimeMemoryEnvFromContext(ctx) {
|
|
629
|
+
if (!ctx?.memory) {
|
|
630
|
+
return {};
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
RIG_MEMORY_DB_PATH: ctx.memory.hydratedPath,
|
|
634
|
+
RIG_MEMORY_CANONICAL_PATH: ctx.memory.canonicalPath,
|
|
635
|
+
RIG_MEMORY_CANONICAL_REF: ctx.memory.canonicalRef,
|
|
636
|
+
RIG_MEMORY_CANONICAL_BASE_OID: ctx.memory.canonicalBaseOid,
|
|
637
|
+
RIG_MEMORY_CREATED_FRESH: ctx.memory.createdFresh ? "1" : "0",
|
|
638
|
+
RIG_MEMORY_RETRIEVAL_TOP_K: String(ctx.memory.retrieval.topK),
|
|
639
|
+
RIG_MEMORY_RETRIEVAL_LEXICAL_WEIGHT: String(ctx.memory.retrieval.lexicalWeight),
|
|
640
|
+
RIG_MEMORY_RETRIEVAL_VECTOR_WEIGHT: String(ctx.memory.retrieval.vectorWeight),
|
|
641
|
+
RIG_MEMORY_RETRIEVAL_RECENCY_WEIGHT: String(ctx.memory.retrieval.recencyWeight),
|
|
642
|
+
RIG_MEMORY_RETRIEVAL_CONFIDENCE_WEIGHT: String(ctx.memory.retrieval.confidenceWeight)
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// packages/runtime/src/control-plane/runtime/isolation/shared.ts
|
|
647
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, rmSync as rmSync2 } from "fs";
|
|
648
|
+
import { resolve as resolve6 } from "path";
|
|
649
|
+
|
|
650
|
+
// packages/runtime/src/control-plane/native/utils.ts
|
|
651
|
+
function resolveMonorepoRoot2(projectRoot) {
|
|
652
|
+
return resolveMonorepoRoot(projectRoot);
|
|
653
|
+
}
|
|
654
|
+
var scopeRegexCache = new Map;
|
|
655
|
+
|
|
656
|
+
// packages/runtime/src/control-plane/runtime/isolation/shared.ts
|
|
657
|
+
var generatedCredentialFiles = new Set;
|
|
658
|
+
var credentialCleanupRegistered = false;
|
|
659
|
+
function resolveMonorepoRoot3(projectRoot) {
|
|
660
|
+
return resolveMonorepoRoot2(projectRoot);
|
|
661
|
+
}
|
|
662
|
+
function sha256Hex(input) {
|
|
663
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
664
|
+
hasher.update(input);
|
|
665
|
+
return hasher.digest("hex");
|
|
666
|
+
}
|
|
667
|
+
function registerCredentialCleanup(path) {
|
|
668
|
+
generatedCredentialFiles.add(path);
|
|
669
|
+
if (credentialCleanupRegistered) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
credentialCleanupRegistered = true;
|
|
673
|
+
const cleanup = () => {
|
|
674
|
+
for (const filePath of generatedCredentialFiles) {
|
|
675
|
+
try {
|
|
676
|
+
rmSync2(filePath, { force: true });
|
|
677
|
+
} catch {}
|
|
678
|
+
}
|
|
679
|
+
generatedCredentialFiles.clear();
|
|
680
|
+
};
|
|
681
|
+
process.on("exit", cleanup);
|
|
682
|
+
process.on("beforeExit", cleanup);
|
|
683
|
+
}
|
|
684
|
+
function hashProjectPath(workspaceDir) {
|
|
685
|
+
return sha256Hex(workspaceDir).slice(0, 16);
|
|
686
|
+
}
|
|
687
|
+
function resolveGithubCliBinaryPath() {
|
|
688
|
+
const explicit = process.env.RIG_GH_BIN?.trim();
|
|
689
|
+
if (explicit && existsSync5(explicit)) {
|
|
690
|
+
return explicit;
|
|
691
|
+
}
|
|
692
|
+
const bunResolved = Bun.which("gh");
|
|
693
|
+
if (bunResolved && existsSync5(bunResolved)) {
|
|
694
|
+
return bunResolved;
|
|
695
|
+
}
|
|
696
|
+
for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
|
|
697
|
+
if (existsSync5(candidate)) {
|
|
698
|
+
return candidate;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return "";
|
|
702
|
+
}
|
|
703
|
+
async function resolveGithubCliAuthToken(ghBinary = "") {
|
|
704
|
+
const gh = ghBinary || resolveGithubCliBinaryPath();
|
|
705
|
+
if (!gh) {
|
|
706
|
+
return "";
|
|
707
|
+
}
|
|
708
|
+
const auth = Bun.spawn([gh, "auth", "token"], {
|
|
709
|
+
stdout: "pipe",
|
|
710
|
+
stderr: "pipe"
|
|
711
|
+
});
|
|
712
|
+
const [exitCode, stdout] = await Promise.all([
|
|
713
|
+
auth.exited,
|
|
714
|
+
new Response(auth.stdout).text()
|
|
715
|
+
]);
|
|
716
|
+
if (exitCode !== 0) {
|
|
717
|
+
return "";
|
|
718
|
+
}
|
|
719
|
+
return stdout.trim();
|
|
720
|
+
}
|
|
721
|
+
function resolveSystemCertBundlePath() {
|
|
722
|
+
const candidates = [
|
|
723
|
+
process.env.SSL_CERT_FILE?.trim(),
|
|
724
|
+
"/etc/ssl/cert.pem",
|
|
725
|
+
"/private/etc/ssl/cert.pem",
|
|
726
|
+
"/opt/homebrew/etc/openssl@3/cert.pem"
|
|
727
|
+
];
|
|
728
|
+
for (const candidate of candidates) {
|
|
729
|
+
if (candidate && existsSync5(candidate)) {
|
|
730
|
+
return resolve6(candidate);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return "";
|
|
734
|
+
}
|
|
735
|
+
function readKnownHosts(path) {
|
|
736
|
+
if (!existsSync5(path)) {
|
|
737
|
+
return new Set;
|
|
738
|
+
}
|
|
739
|
+
return new Set(readFileSync2(path, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// packages/runtime/src/control-plane/runtime/isolation/home.ts
|
|
743
|
+
var GITHUB_SSH_KEY_PLACEHOLDER = "<base64 encoded>";
|
|
744
|
+
var GITHUB_KNOWN_HOSTS = [
|
|
745
|
+
"github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
|
|
746
|
+
"github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
|
|
747
|
+
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
748
|
+
].join(`
|
|
749
|
+
`);
|
|
750
|
+
async function runtimeEnv(projectRoot, runtime) {
|
|
751
|
+
const bunBinaryPath = resolveBunBinaryPath();
|
|
752
|
+
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
753
|
+
const claudeBinaryPath = process.env.RIG_CLAUDE_PATH?.trim() || (() => {
|
|
754
|
+
try {
|
|
755
|
+
return resolveClaudeBinaryPath();
|
|
756
|
+
} catch {
|
|
757
|
+
return "";
|
|
758
|
+
}
|
|
759
|
+
})();
|
|
760
|
+
const claudeDir = claudeBinaryPath ? (() => {
|
|
761
|
+
try {
|
|
762
|
+
return resolveClaudeInstallDir();
|
|
763
|
+
} catch {
|
|
764
|
+
return resolve7(claudeBinaryPath, "..");
|
|
765
|
+
}
|
|
766
|
+
})() : "";
|
|
767
|
+
const nodeDir = resolveNodeInstallDir();
|
|
768
|
+
const hostGhBinary = resolveGithubCliBinaryPath();
|
|
769
|
+
const runtimeCertBundlePath = await materializeRuntimeCertBundle(runtime);
|
|
770
|
+
const monorepoMainRoot = resolveMonorepoRoot3(projectRoot);
|
|
771
|
+
const realHome = process.env.HOME?.trim();
|
|
772
|
+
const inheritedPath = (process.env.PATH ?? "").split(delimiter).map((entry) => entry.trim()).filter(Boolean).filter((entry) => !entry.endsWith("/.rig/bin") && !entry.endsWith("/rig/tools"));
|
|
773
|
+
const pathEntries = [
|
|
774
|
+
runtime.binDir,
|
|
775
|
+
`${projectRoot}/rig/tools`,
|
|
776
|
+
`${bunDir}/bin`,
|
|
777
|
+
claudeDir,
|
|
778
|
+
nodeDir ? `${nodeDir}/bin` : "",
|
|
779
|
+
realHome ? resolve7(realHome, ".local/bin") : "",
|
|
780
|
+
realHome ? resolve7(realHome, ".cargo/bin") : "",
|
|
781
|
+
...inheritedPath,
|
|
782
|
+
"/usr/local/bin",
|
|
783
|
+
"/usr/local/sbin",
|
|
784
|
+
"/opt/homebrew/bin",
|
|
785
|
+
"/opt/homebrew/sbin",
|
|
786
|
+
"/usr/bin",
|
|
787
|
+
"/bin",
|
|
788
|
+
"/usr/sbin",
|
|
789
|
+
"/sbin"
|
|
790
|
+
].filter(Boolean);
|
|
791
|
+
const runtimeBash = resolve7(runtime.binDir, "bash");
|
|
792
|
+
const runtimeRigGit = resolve7(runtime.binDir, runtimeRigGitFileName());
|
|
793
|
+
const preferredShell = existsSync6(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
794
|
+
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
795
|
+
const env = {
|
|
796
|
+
PROJECT_RIG_ROOT: projectRoot,
|
|
797
|
+
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
798
|
+
HOME: runtime.homeDir,
|
|
799
|
+
TMPDIR: runtime.tmpDir,
|
|
800
|
+
XDG_CACHE_HOME: runtime.cacheDir,
|
|
801
|
+
XDG_STATE_HOME: runtime.stateDir,
|
|
802
|
+
RIG_AGENT_ID: runtime.id,
|
|
803
|
+
RIG_TASK_ID: runtime.taskId,
|
|
804
|
+
RIG_TASK_RUNTIME_ID: runtime.id,
|
|
805
|
+
RIG_TASK_WORKSPACE: runtime.workspaceDir,
|
|
806
|
+
RIG_TASK_RUNTIME_MODE: runtime.mode,
|
|
807
|
+
RIG_RUNTIME_MODE: runtime.mode,
|
|
808
|
+
RIG_RUNTIME_HOME: runtime.rootDir,
|
|
809
|
+
RIG_RUNTIME_BIN_DIR: runtime.binDir,
|
|
810
|
+
...existsSync6(runtimeRigGit) ? { RIG_NATIVE_GIT_BIN: runtimeRigGit } : {},
|
|
811
|
+
RIG_BUN_PATH: bunBinaryPath,
|
|
812
|
+
...claudeBinaryPath ? { RIG_CLAUDE_PATH: claudeBinaryPath } : {},
|
|
813
|
+
RIG_AGENT_BIN: resolve7(runtime.binDir, "rig-agent"),
|
|
814
|
+
RIG_HOOKS_ACTIVE: "1",
|
|
815
|
+
RIG_AUTO_PR_ON_COMPLETE: "1",
|
|
816
|
+
RIG_POLICY_FILE: resolve7(projectRoot, "rig/policy/policy.json"),
|
|
817
|
+
RIG_STATE_DIR: runtime.stateDir,
|
|
818
|
+
RIG_LOGS_DIR: runtime.logsDir,
|
|
819
|
+
RIG_SESSION_FILE: resolve7(runtime.sessionDir, "session.json"),
|
|
820
|
+
MONOREPO_ROOT: runtime.workspaceDir,
|
|
821
|
+
MONOREPO_MAIN_ROOT: monorepoMainRoot,
|
|
822
|
+
TS_API_TESTS_DIR: resolve7(runtime.workspaceDir, "TSAPITests"),
|
|
823
|
+
BASH: preferredShell,
|
|
824
|
+
SHELL: preferredShell,
|
|
825
|
+
PATH: [...new Set(pathEntries)].join(delimiter),
|
|
826
|
+
LANG: process.env.LANG ?? "en_US.UTF-8",
|
|
827
|
+
TERM: process.env.TERM ?? "xterm-256color",
|
|
828
|
+
PYTHONDONTWRITEBYTECODE: "1",
|
|
829
|
+
PYTHONPYCACHEPREFIX: resolve7(runtime.cacheDir, "python"),
|
|
830
|
+
...process.env.RIG_PR_BASE_PROJECT && { RIG_PR_BASE_PROJECT: process.env.RIG_PR_BASE_PROJECT },
|
|
831
|
+
...process.env.RIG_PR_BASE_MONOREPO && { RIG_PR_BASE_MONOREPO: process.env.RIG_PR_BASE_MONOREPO },
|
|
832
|
+
CLAUDE_HOME: runtime.claudeHomeDir,
|
|
833
|
+
PI_CODING_AGENT_DIR: resolve7(runtime.homeDir, ".pi", "agent"),
|
|
834
|
+
[RUNTIME_CONTEXT_ENV]: runtime.contextFile,
|
|
835
|
+
...nativeRuntimeLibraryPath ? { RIG_NATIVE_RUNTIME_LIB: nativeRuntimeLibraryPath } : {},
|
|
836
|
+
...hostGhBinary ? { RIG_GH_BIN: hostGhBinary } : {},
|
|
837
|
+
...runtimeCertBundlePath ? {
|
|
838
|
+
SSL_CERT_FILE: runtimeCertBundlePath,
|
|
839
|
+
CURL_CA_BUNDLE: runtimeCertBundlePath,
|
|
840
|
+
REQUESTS_CA_BUNDLE: runtimeCertBundlePath,
|
|
841
|
+
NODE_EXTRA_CA_CERTS: runtimeCertBundlePath
|
|
842
|
+
} : {}
|
|
843
|
+
};
|
|
844
|
+
const knownHostsPath = resolve7(runtime.homeDir, ".ssh", "known_hosts");
|
|
845
|
+
if (existsSync6(knownHostsPath)) {
|
|
846
|
+
const agentSshKey = resolve7(runtime.homeDir, ".ssh", "rig-agent-key");
|
|
847
|
+
const sshParts = [
|
|
848
|
+
"ssh",
|
|
849
|
+
`-o UserKnownHostsFile="${knownHostsPath}"`,
|
|
850
|
+
"-o StrictHostKeyChecking=yes",
|
|
851
|
+
"-F /dev/null"
|
|
852
|
+
];
|
|
853
|
+
if (existsSync6(agentSshKey)) {
|
|
854
|
+
sshParts.splice(1, 0, `-i "${agentSshKey}"`, "-o IdentitiesOnly=yes");
|
|
855
|
+
}
|
|
856
|
+
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
857
|
+
}
|
|
858
|
+
for (const [key, value] of Object.entries(resolveRuntimeSecrets(process.env))) {
|
|
859
|
+
if (key === "GITHUB_SSH_KEY") {
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
if (value) {
|
|
863
|
+
env[key] = value;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
const fallbackGithubToken = !env.GITHUB_TOKEN && !env.GH_TOKEN ? await resolveGithubCliAuthToken(hostGhBinary) : "";
|
|
867
|
+
if (fallbackGithubToken) {
|
|
868
|
+
env.GITHUB_TOKEN = fallbackGithubToken;
|
|
869
|
+
}
|
|
870
|
+
if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
|
|
871
|
+
env.GITHUB_TOKEN = env.GH_TOKEN;
|
|
872
|
+
}
|
|
873
|
+
if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
|
|
874
|
+
env.GH_TOKEN = env.GITHUB_TOKEN;
|
|
875
|
+
}
|
|
876
|
+
if (!env.GREPTILE_GITHUB_TOKEN && env.GITHUB_TOKEN) {
|
|
877
|
+
env.GREPTILE_GITHUB_TOKEN = env.GITHUB_TOKEN;
|
|
878
|
+
}
|
|
879
|
+
if (existsSync6(runtime.contextFile)) {
|
|
880
|
+
const runtimeContext = loadRuntimeContext(runtime.contextFile);
|
|
881
|
+
Object.assign(env, runtimeMemoryEnvFromContext(runtimeContext));
|
|
882
|
+
Object.assign(env, browserEnvFromContext(runtimeContext.browser));
|
|
883
|
+
}
|
|
884
|
+
persistRuntimeSecrets(runtime.rootDir, env);
|
|
885
|
+
return env;
|
|
886
|
+
}
|
|
887
|
+
function runtimeCommandEnv(baseEnv, command) {
|
|
888
|
+
const env = { ...baseEnv };
|
|
889
|
+
delete env.ENV;
|
|
890
|
+
delete env.BASH_ENV;
|
|
891
|
+
const executable = command[0]?.trim() ?? "";
|
|
892
|
+
const shellName = basename2(executable);
|
|
893
|
+
const isPosixSh = executable === "/bin/sh" || shellName === "sh" || shellName === "dash";
|
|
894
|
+
if (isPosixSh) {
|
|
895
|
+
delete env.BASH;
|
|
896
|
+
if (executable) {
|
|
897
|
+
env.SHELL = executable;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return env;
|
|
901
|
+
}
|
|
902
|
+
async function provisionRuntimeHome(runtime, options = {}) {
|
|
903
|
+
await mkdir(runtime.homeDir, { recursive: true });
|
|
904
|
+
await mkdir(runtime.tmpDir, { recursive: true });
|
|
905
|
+
await mkdir(runtime.cacheDir, { recursive: true });
|
|
906
|
+
await provisionAgentSshKey(runtime.homeDir);
|
|
907
|
+
if (options.provider === "codex") {
|
|
908
|
+
const hasCodexAuth = await injectCodexAuth(resolve7(runtime.homeDir, ".codex"));
|
|
909
|
+
if (!hasCodexAuth) {
|
|
910
|
+
console.warn("[rig] No Codex auth.json found for isolated runtime. " + "Run `codex login` in your host shell, then retry the agent run.");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (options.provider === "pi") {
|
|
914
|
+
const hasPiAuth = await injectPiAgentConfig(resolve7(runtime.homeDir, ".pi", "agent"));
|
|
915
|
+
if (!hasPiAuth) {
|
|
916
|
+
console.warn("[rig] No Pi auth.json found for isolated runtime. " + "Run `pi /login` in your host shell, then retry the agent run.");
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
async function provisionClaudeHome(config) {
|
|
921
|
+
mkdirSync3(config.claudeHomeDir, { recursive: true });
|
|
922
|
+
const workspaceSettings = resolve7(config.workspaceDir, ".claude/settings.json");
|
|
923
|
+
const hostSettings = resolve7(config.hostProjectRoot, ".claude/settings.json");
|
|
924
|
+
const projectSettings = existsSync6(workspaceSettings) ? workspaceSettings : hostSettings;
|
|
925
|
+
const runtimeSettings = await loadRuntimeClaudeSettings(projectSettings);
|
|
926
|
+
if (existsSync6(projectSettings)) {
|
|
927
|
+
writeFileSync2(resolve7(config.claudeHomeDir, "settings.local.json"), `${JSON.stringify(runtimeSettings, null, 2)}
|
|
928
|
+
`, "utf-8");
|
|
929
|
+
}
|
|
930
|
+
writeClaudeProjectSettings(config.claudeHomeDir, config.workspaceDir, runtimeSettings);
|
|
931
|
+
writeFileSync2(resolve7(config.claudeHomeDir, "settings.json"), JSON.stringify({
|
|
932
|
+
permissions: { defaultMode: "bypassPermissions" },
|
|
933
|
+
autoMemoryEnabled: false
|
|
934
|
+
}, null, 2));
|
|
935
|
+
const hasCredentials = await injectClaudeCredentials(config.claudeHomeDir);
|
|
936
|
+
if (!hasCredentials) {
|
|
937
|
+
console.warn("[rig] No Claude credentials found for isolated runtime. " + "Run `claude /login` in your host shell, then retry the agent run.");
|
|
938
|
+
}
|
|
939
|
+
const realClaudeHome = resolve7(process.env.HOME ?? "", ".claude");
|
|
940
|
+
if (process.env.HOME && existsSync6(resolve7(realClaudeHome, "CLAUDE.md"))) {
|
|
941
|
+
cpSync(resolve7(realClaudeHome, "CLAUDE.md"), resolve7(config.claudeHomeDir, "CLAUDE.md"));
|
|
942
|
+
}
|
|
943
|
+
if (process.env.HOME && existsSync6(resolve7(realClaudeHome, "agents"))) {
|
|
944
|
+
cpSync(resolve7(realClaudeHome, "agents"), resolve7(config.claudeHomeDir, "agents"), { recursive: true });
|
|
945
|
+
}
|
|
946
|
+
if (process.platform === "darwin" && process.env.HOME) {
|
|
947
|
+
writeClaudeProjectSettings(realClaudeHome, config.workspaceDir, runtimeSettings);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
async function materializeRuntimeCertBundle(runtime) {
|
|
951
|
+
const sourcePath = resolveSystemCertBundlePath();
|
|
952
|
+
if (!sourcePath) {
|
|
953
|
+
return "";
|
|
954
|
+
}
|
|
955
|
+
const certsDir = resolve7(runtime.rootDir, "certs");
|
|
956
|
+
const targetPath = resolve7(certsDir, "ca-certificates.pem");
|
|
957
|
+
await mkdir(certsDir, { recursive: true });
|
|
958
|
+
let shouldCopy = !existsSync6(targetPath);
|
|
959
|
+
if (!shouldCopy) {
|
|
960
|
+
try {
|
|
961
|
+
shouldCopy = statSync2(sourcePath).mtimeMs > statSync2(targetPath).mtimeMs;
|
|
962
|
+
} catch {
|
|
963
|
+
shouldCopy = true;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (shouldCopy) {
|
|
967
|
+
copyFileSync2(sourcePath, targetPath);
|
|
968
|
+
}
|
|
969
|
+
return targetPath;
|
|
970
|
+
}
|
|
971
|
+
function persistRuntimeSecrets(runtimeRoot, env) {
|
|
972
|
+
const secretsPath = resolve7(runtimeRoot, "runtime-secrets.json");
|
|
973
|
+
const persisted = {};
|
|
974
|
+
for (const key of [
|
|
975
|
+
"GITHUB_TOKEN",
|
|
976
|
+
"GH_TOKEN",
|
|
977
|
+
"GREPTILE_GITHUB_TOKEN",
|
|
978
|
+
"GREPTILE_API_KEY",
|
|
979
|
+
"AI_REVIEW_MODE",
|
|
980
|
+
"AI_REVIEW_PROVIDER"
|
|
981
|
+
]) {
|
|
982
|
+
const value = env[key];
|
|
983
|
+
if (value) {
|
|
984
|
+
persisted[key] = value;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (Object.keys(persisted).length === 0) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
writeFileSync2(secretsPath, `${JSON.stringify(persisted, null, 2)}
|
|
991
|
+
`, "utf-8");
|
|
992
|
+
}
|
|
993
|
+
async function provisionAgentSshKey(homeDir) {
|
|
994
|
+
const sshDir = resolve7(homeDir, ".ssh");
|
|
995
|
+
if (!existsSync6(sshDir)) {
|
|
996
|
+
await mkdir(sshDir, { recursive: true });
|
|
997
|
+
}
|
|
998
|
+
seedKnownHosts(sshDir);
|
|
999
|
+
const secrets = resolveRuntimeSecrets(process.env);
|
|
1000
|
+
const privateKey = decodeProvisionedSshKey(secrets.GITHUB_SSH_KEY);
|
|
1001
|
+
if (!privateKey) {
|
|
1002
|
+
const hostKeyPath = resolveHostSshKeyPath(process.env.HOME ?? "");
|
|
1003
|
+
if (!process.env.HOME || !existsSync6(hostKeyPath)) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const agentKeyPath2 = resolve7(sshDir, "rig-agent-key");
|
|
1007
|
+
if (!existsSync6(agentKeyPath2)) {
|
|
1008
|
+
copyFileSync2(hostKeyPath, agentKeyPath2);
|
|
1009
|
+
chmodSync(agentKeyPath2, 384);
|
|
1010
|
+
}
|
|
1011
|
+
const hostPubPath = `${hostKeyPath}.pub`;
|
|
1012
|
+
if (existsSync6(hostPubPath)) {
|
|
1013
|
+
const agentPubPath = `${agentKeyPath2}.pub`;
|
|
1014
|
+
if (!existsSync6(agentPubPath)) {
|
|
1015
|
+
copyFileSync2(hostPubPath, agentPubPath);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
writeSshConfig(sshDir, agentKeyPath2);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const agentKeyPath = resolve7(sshDir, "rig-agent-key");
|
|
1022
|
+
if (!existsSync6(agentKeyPath)) {
|
|
1023
|
+
writeFileSync2(agentKeyPath, privateKey, { mode: 384 });
|
|
1024
|
+
}
|
|
1025
|
+
writeSshConfig(sshDir, agentKeyPath);
|
|
1026
|
+
}
|
|
1027
|
+
function decodeProvisionedSshKey(encodedKey) {
|
|
1028
|
+
const trimmed = encodedKey?.trim();
|
|
1029
|
+
if (!trimmed || trimmed === GITHUB_SSH_KEY_PLACEHOLDER) {
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
const decoded = Buffer.from(trimmed, "base64").toString("utf-8").trim();
|
|
1033
|
+
if (!decoded.includes("PRIVATE KEY")) {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
return `${decoded}
|
|
1037
|
+
`;
|
|
1038
|
+
}
|
|
1039
|
+
function resolveHostSshKeyPath(homeDir) {
|
|
1040
|
+
const sshDir = resolve7(homeDir, ".ssh");
|
|
1041
|
+
const candidates = [
|
|
1042
|
+
"rig-agent-key",
|
|
1043
|
+
"id_ed25519",
|
|
1044
|
+
"id_ecdsa",
|
|
1045
|
+
"id_rsa"
|
|
1046
|
+
].map((name) => resolve7(sshDir, name));
|
|
1047
|
+
return candidates.find((candidate) => existsSync6(candidate)) ?? resolve7(sshDir, "rig-agent-key");
|
|
1048
|
+
}
|
|
1049
|
+
function writeSshConfig(sshDir, keyPath) {
|
|
1050
|
+
const configPath = resolve7(sshDir, "config");
|
|
1051
|
+
if (existsSync6(configPath)) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
const knownHostsPath = resolve7(sshDir, "known_hosts");
|
|
1055
|
+
const config = [
|
|
1056
|
+
"Host github.com",
|
|
1057
|
+
` IdentityFile ${keyPath}`,
|
|
1058
|
+
" IdentitiesOnly yes",
|
|
1059
|
+
` UserKnownHostsFile ${knownHostsPath}`,
|
|
1060
|
+
" StrictHostKeyChecking yes",
|
|
1061
|
+
""
|
|
1062
|
+
].join(`
|
|
1063
|
+
`);
|
|
1064
|
+
writeFileSync2(configPath, config, { mode: 420 });
|
|
1065
|
+
}
|
|
1066
|
+
function seedKnownHosts(sshDir) {
|
|
1067
|
+
const knownHostsPath = resolve7(sshDir, "known_hosts");
|
|
1068
|
+
const existingLines = readKnownHosts(knownHostsPath);
|
|
1069
|
+
const requiredLines = GITHUB_KNOWN_HOSTS.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1070
|
+
const missing = requiredLines.filter((line) => !existingLines.has(line));
|
|
1071
|
+
if (missing.length === 0) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
try {
|
|
1075
|
+
for (const line of missing) {
|
|
1076
|
+
existingLines.add(line);
|
|
1077
|
+
}
|
|
1078
|
+
writeFileSync2(knownHostsPath, `${Array.from(existingLines).join(`
|
|
1079
|
+
`)}
|
|
1080
|
+
`, { mode: 420 });
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
const hint = existsSync6(knownHostsPath) ? "" : " \u2014 known_hosts is missing; git SSH operations may fail";
|
|
1083
|
+
console.warn(`[rig] Could not update ${knownHostsPath}: ${err instanceof Error ? err.message : String(err)}${hint}`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
function writeClaudeProjectSettings(claudeHomeDir, workspaceDir, runtimeSettings) {
|
|
1087
|
+
const projectHash = hashProjectPath(workspaceDir);
|
|
1088
|
+
const projectDir = resolve7(claudeHomeDir, "projects", projectHash);
|
|
1089
|
+
mkdirSync3(projectDir, { recursive: true });
|
|
1090
|
+
writeFileSync2(resolve7(projectDir, "settings.json"), `${JSON.stringify(runtimeSettings, null, 2)}
|
|
1091
|
+
`, "utf-8");
|
|
1092
|
+
}
|
|
1093
|
+
async function loadRuntimeClaudeSettings(projectSettingsPath) {
|
|
1094
|
+
if (!existsSync6(projectSettingsPath)) {
|
|
1095
|
+
return {};
|
|
1096
|
+
}
|
|
1097
|
+
let parsed;
|
|
1098
|
+
try {
|
|
1099
|
+
parsed = await Bun.file(projectSettingsPath).json();
|
|
1100
|
+
} catch {
|
|
1101
|
+
return {};
|
|
1102
|
+
}
|
|
1103
|
+
const clone = JSON.parse(JSON.stringify(parsed));
|
|
1104
|
+
const hooks = clone.hooks;
|
|
1105
|
+
if (!hooks || typeof hooks !== "object") {
|
|
1106
|
+
return clone;
|
|
1107
|
+
}
|
|
1108
|
+
for (const key of ["PreToolUse", "PostToolUse", "SessionStart", "Stop"]) {
|
|
1109
|
+
const groups = hooks[key];
|
|
1110
|
+
if (!Array.isArray(groups)) {
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
for (const group of groups) {
|
|
1114
|
+
if (!group || typeof group !== "object" || Array.isArray(group)) {
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
const hookGroup = group;
|
|
1118
|
+
if (!Array.isArray(hookGroup.hooks)) {
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
for (const hook of hookGroup.hooks) {
|
|
1122
|
+
if (!hook || hook.type !== "command" || typeof hook.command !== "string") {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const shMatch = hook.command.match(/rig\/hooks\/([a-z0-9-]+)\.sh$/i);
|
|
1126
|
+
if (shMatch) {
|
|
1127
|
+
hook.command = `.rig/bin/hooks/${shMatch[1]}`;
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
const splinterMatch = hook.command.match(/\.splinter\/bin\/hooks\/([a-z0-9-]+)$/i);
|
|
1131
|
+
if (splinterMatch) {
|
|
1132
|
+
hook.command = `.rig/bin/hooks/${splinterMatch[1]}`;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return clone;
|
|
1138
|
+
}
|
|
1139
|
+
async function injectClaudeCredentials(claudeHomeDir, options = {}) {
|
|
1140
|
+
const credentialsPath = resolve7(claudeHomeDir, ".credentials.json");
|
|
1141
|
+
const platform = options.platform ?? process.platform;
|
|
1142
|
+
if (platform === "darwin") {
|
|
1143
|
+
const raw = options.loadKeychainCredentials ? await options.loadKeychainCredentials() : await (async () => {
|
|
1144
|
+
const result = await Bun.$`security find-generic-password -s "Claude Code-credentials" -w`.quiet().nothrow();
|
|
1145
|
+
return result.exitCode === 0 ? result.stdout.toString().trim() : "";
|
|
1146
|
+
})();
|
|
1147
|
+
if (raw) {
|
|
1148
|
+
try {
|
|
1149
|
+
JSON.parse(raw);
|
|
1150
|
+
writeFileSync2(credentialsPath, raw, { mode: 384 });
|
|
1151
|
+
registerCredentialCleanup(credentialsPath);
|
|
1152
|
+
return true;
|
|
1153
|
+
} catch {}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
const hostClaudeHome = options.hostClaudeHome ? resolve7(options.hostClaudeHome) : process.env.CLAUDE_HOME?.trim() ? resolve7(process.env.CLAUDE_HOME) : process.env.HOME ? resolve7(process.env.HOME, ".claude") : "";
|
|
1157
|
+
if (hostClaudeHome) {
|
|
1158
|
+
const realCredentials = resolve7(hostClaudeHome, ".credentials.json");
|
|
1159
|
+
if (existsSync6(realCredentials)) {
|
|
1160
|
+
cpSync(realCredentials, credentialsPath);
|
|
1161
|
+
return true;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
async function injectCodexAuth(codexHomeDir) {
|
|
1167
|
+
mkdirSync3(codexHomeDir, { recursive: true });
|
|
1168
|
+
const hostCodexHome = process.env.CODEX_HOME?.trim() ? resolve7(process.env.CODEX_HOME) : process.env.HOME ? resolve7(process.env.HOME, ".codex") : "";
|
|
1169
|
+
if (!hostCodexHome) {
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
const hostAuthPath = resolve7(hostCodexHome, "auth.json");
|
|
1173
|
+
if (!existsSync6(hostAuthPath)) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
const runtimeAuthPath = resolve7(codexHomeDir, "auth.json");
|
|
1177
|
+
copyFileSync2(hostAuthPath, runtimeAuthPath);
|
|
1178
|
+
chmodSync(runtimeAuthPath, 384);
|
|
1179
|
+
return true;
|
|
1180
|
+
}
|
|
1181
|
+
async function injectPiAgentConfig(piAgentDir) {
|
|
1182
|
+
mkdirSync3(piAgentDir, { recursive: true });
|
|
1183
|
+
const hostPiAgentDir = process.env.PI_CODING_AGENT_DIR?.trim() ? resolve7(process.env.PI_CODING_AGENT_DIR) : process.env.HOME ? resolve7(process.env.HOME, ".pi", "agent") : "";
|
|
1184
|
+
if (!hostPiAgentDir) {
|
|
1185
|
+
return false;
|
|
1186
|
+
}
|
|
1187
|
+
const hostAuthPath = resolve7(hostPiAgentDir, "auth.json");
|
|
1188
|
+
if (!existsSync6(hostAuthPath)) {
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
const runtimeAuthPath = resolve7(piAgentDir, "auth.json");
|
|
1192
|
+
copyFileSync2(hostAuthPath, runtimeAuthPath);
|
|
1193
|
+
chmodSync(runtimeAuthPath, 384);
|
|
1194
|
+
const hostSettingsPath = resolve7(hostPiAgentDir, "settings.json");
|
|
1195
|
+
if (existsSync6(hostSettingsPath)) {
|
|
1196
|
+
const runtimeSettingsPath = resolve7(piAgentDir, "settings.json");
|
|
1197
|
+
copyFileSync2(hostSettingsPath, runtimeSettingsPath);
|
|
1198
|
+
chmodSync(runtimeSettingsPath, 384);
|
|
1199
|
+
}
|
|
1200
|
+
return true;
|
|
1201
|
+
}
|
|
1202
|
+
function loadRuntimeTaskContext(runtime) {
|
|
1203
|
+
return loadRuntimeContext(runtime.contextFile);
|
|
1204
|
+
}
|
|
1205
|
+
var __testOnly = {
|
|
1206
|
+
injectClaudeCredentials,
|
|
1207
|
+
injectCodexAuth,
|
|
1208
|
+
injectPiAgentConfig
|
|
1209
|
+
};
|
|
1210
|
+
export {
|
|
1211
|
+
runtimeEnv,
|
|
1212
|
+
runtimeCommandEnv,
|
|
1213
|
+
provisionRuntimeHome,
|
|
1214
|
+
provisionClaudeHome,
|
|
1215
|
+
loadRuntimeTaskContext,
|
|
1216
|
+
__testOnly
|
|
1217
|
+
};
|