@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,2651 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toCommonJS = (from) => {
|
|
10
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
11
|
+
if (entry)
|
|
12
|
+
return entry;
|
|
13
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (var key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(entry, key))
|
|
17
|
+
__defProp(entry, key, {
|
|
18
|
+
get: __accessProp.bind(from, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
__moduleCache.set(from, entry);
|
|
23
|
+
return entry;
|
|
24
|
+
};
|
|
25
|
+
var __moduleCache;
|
|
26
|
+
var __returnValue = (v) => v;
|
|
27
|
+
function __exportSetter(name, newValue) {
|
|
28
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
29
|
+
}
|
|
30
|
+
var __export = (target, all) => {
|
|
31
|
+
for (var name in all)
|
|
32
|
+
__defProp(target, name, {
|
|
33
|
+
get: all[name],
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
set: __exportSetter.bind(all, name)
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
40
|
+
var __require = import.meta.require;
|
|
41
|
+
|
|
42
|
+
// packages/runtime/src/layout.ts
|
|
43
|
+
import { existsSync } from "fs";
|
|
44
|
+
import { basename, dirname, resolve } from "path";
|
|
45
|
+
function resolveMonorepoRoot(projectRoot) {
|
|
46
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
47
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
48
|
+
if (explicit) {
|
|
49
|
+
const explicitRoot = resolve(explicit);
|
|
50
|
+
const explicitParent = dirname(explicitRoot);
|
|
51
|
+
if (basename(explicitParent) === ".worktrees") {
|
|
52
|
+
const owner = dirname(explicitParent);
|
|
53
|
+
const ownerHasGit = existsSync(resolve(owner, ".git"));
|
|
54
|
+
const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
|
|
55
|
+
const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
|
|
56
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
57
|
+
return owner;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
60
|
+
}
|
|
61
|
+
if (!existsSync(resolve(explicitRoot, ".git"))) {
|
|
62
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
63
|
+
}
|
|
64
|
+
const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
|
|
65
|
+
const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
|
|
66
|
+
if (!hasTaskConfig && !hasRigConfig) {
|
|
67
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
68
|
+
}
|
|
69
|
+
return explicitRoot;
|
|
70
|
+
}
|
|
71
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
72
|
+
if (basename(projectParent) === ".worktrees") {
|
|
73
|
+
const worktreeOwner = dirname(projectParent);
|
|
74
|
+
const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
|
|
75
|
+
const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
|
|
76
|
+
const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
|
|
77
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
78
|
+
return worktreeOwner;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return normalizedProjectRoot;
|
|
82
|
+
}
|
|
83
|
+
function resolveRuntimeWorkspaceLayout(workspaceDir) {
|
|
84
|
+
const root = resolve(workspaceDir);
|
|
85
|
+
const rigRoot = resolve(root, ".rig");
|
|
86
|
+
const logsDir = resolve(rigRoot, "logs");
|
|
87
|
+
const stateDir = resolve(rigRoot, "state");
|
|
88
|
+
const runtimeDir = resolve(rigRoot, "runtime");
|
|
89
|
+
const binDir = resolve(rigRoot, "bin");
|
|
90
|
+
return {
|
|
91
|
+
workspaceDir: root,
|
|
92
|
+
rigRoot,
|
|
93
|
+
stateDir,
|
|
94
|
+
logsDir,
|
|
95
|
+
artifactsRoot: resolve(root, RIG_ARTIFACTS_DIRNAME),
|
|
96
|
+
runtimeDir,
|
|
97
|
+
homeDir: resolve(rigRoot, "home"),
|
|
98
|
+
tmpDir: resolve(rigRoot, "tmp"),
|
|
99
|
+
cacheDir: resolve(rigRoot, "cache"),
|
|
100
|
+
sessionDir: resolve(rigRoot, "session"),
|
|
101
|
+
binDir,
|
|
102
|
+
distDir: resolve(rigRoot, "dist"),
|
|
103
|
+
pluginBinDir: resolve(binDir, "plugins"),
|
|
104
|
+
contextPath: resolve(rigRoot, "runtime-context.json"),
|
|
105
|
+
controlPlaneEventsFile: resolve(logsDir, "control-plane.events.jsonl")
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
var RIG_ARTIFACTS_DIRNAME = "artifacts";
|
|
109
|
+
var init_layout = () => {};
|
|
110
|
+
|
|
111
|
+
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
112
|
+
import { existsSync as existsSync4, readdirSync, realpathSync } from "fs";
|
|
113
|
+
import { resolve as resolve4 } from "path";
|
|
114
|
+
function toRealPath(path) {
|
|
115
|
+
if (!existsSync4(path)) {
|
|
116
|
+
return resolve4(path);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return realpathSync.native(path);
|
|
120
|
+
} catch {
|
|
121
|
+
return resolve4(path);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function resolveHostGitMetadataPaths(projectRoot, workspaceDir) {
|
|
125
|
+
const candidates = new Set;
|
|
126
|
+
const addPath = (candidate) => {
|
|
127
|
+
if (existsSync4(candidate)) {
|
|
128
|
+
candidates.add(toRealPath(candidate));
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
addPath(resolve4(projectRoot, ".git"));
|
|
132
|
+
addPath(resolve4(workspaceDir, "..", "..", ".git"));
|
|
133
|
+
for (const repoRoot of resolveHostRepoRootPaths(projectRoot)) {
|
|
134
|
+
addPath(resolve4(repoRoot, ".git"));
|
|
135
|
+
}
|
|
136
|
+
const workspaceGit = resolve4(workspaceDir, ".git");
|
|
137
|
+
if (existsSync4(workspaceGit)) {
|
|
138
|
+
addPath(workspaceGit);
|
|
139
|
+
}
|
|
140
|
+
return [...candidates];
|
|
141
|
+
}
|
|
142
|
+
function resolveHostRepoRootPaths(projectRoot) {
|
|
143
|
+
const candidates = new Set;
|
|
144
|
+
const addPath = (candidate) => {
|
|
145
|
+
if (existsSync4(candidate)) {
|
|
146
|
+
candidates.add(toRealPath(candidate));
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
try {
|
|
150
|
+
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
151
|
+
if (toRealPath(monorepoRoot) !== toRealPath(projectRoot)) {
|
|
152
|
+
addPath(monorepoRoot);
|
|
153
|
+
}
|
|
154
|
+
} catch {}
|
|
155
|
+
const reposDir = resolve4(projectRoot, "repos");
|
|
156
|
+
if (existsSync4(reposDir)) {
|
|
157
|
+
for (const entry of readdirSync(reposDir, { withFileTypes: true })) {
|
|
158
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
159
|
+
addPath(resolve4(reposDir, entry.name));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return [...candidates];
|
|
164
|
+
}
|
|
165
|
+
function resolveNetworkWithPolicy(sandboxConfig, envOverride) {
|
|
166
|
+
if (envOverride) {
|
|
167
|
+
const envValue = parseBooleanEnv(envOverride, sandboxConfig.network);
|
|
168
|
+
if (envValue !== sandboxConfig.network) {
|
|
169
|
+
console.warn(`[sandbox] RIG_RUNTIME_SANDBOX_NETWORK=${envOverride} overrides policy sandbox.network=${sandboxConfig.network}`);
|
|
170
|
+
}
|
|
171
|
+
return envValue;
|
|
172
|
+
}
|
|
173
|
+
return sandboxConfig.network;
|
|
174
|
+
}
|
|
175
|
+
function parseBooleanEnv(raw, fallback) {
|
|
176
|
+
if (!raw) {
|
|
177
|
+
return fallback;
|
|
178
|
+
}
|
|
179
|
+
const normalized = raw.trim().toLowerCase();
|
|
180
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return fallback;
|
|
187
|
+
}
|
|
188
|
+
function uniq(values) {
|
|
189
|
+
return [...new Set(values)];
|
|
190
|
+
}
|
|
191
|
+
function seatbeltString(value) {
|
|
192
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
193
|
+
}
|
|
194
|
+
var init_utils = __esm(() => {
|
|
195
|
+
init_layout();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend-seatbelt.ts
|
|
199
|
+
var exports_backend_seatbelt = {};
|
|
200
|
+
__export(exports_backend_seatbelt, {
|
|
201
|
+
SeatbeltBackend: () => SeatbeltBackend
|
|
202
|
+
});
|
|
203
|
+
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
204
|
+
import { resolve as resolve6 } from "path";
|
|
205
|
+
|
|
206
|
+
class SeatbeltBackend {
|
|
207
|
+
kind = "macos-seatbelt";
|
|
208
|
+
binaryPath;
|
|
209
|
+
config;
|
|
210
|
+
ctx;
|
|
211
|
+
resolvedPaths;
|
|
212
|
+
constructor(binaryPath, config, ctx, resolvedPaths) {
|
|
213
|
+
this.binaryPath = binaryPath;
|
|
214
|
+
this.config = config;
|
|
215
|
+
this.ctx = ctx;
|
|
216
|
+
this.resolvedPaths = resolvedPaths;
|
|
217
|
+
}
|
|
218
|
+
wrap(options) {
|
|
219
|
+
const profilePath = this.writeSeatbeltProfile(options);
|
|
220
|
+
return {
|
|
221
|
+
command: [this.binaryPath, "-f", profilePath, ...options.command],
|
|
222
|
+
enabled: true,
|
|
223
|
+
backend: "macos-seatbelt",
|
|
224
|
+
profilePath
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
writeSeatbeltProfile(options) {
|
|
228
|
+
const sandboxDir = resolve6(options.runtime.rootDir, "sandbox");
|
|
229
|
+
mkdirSync2(sandboxDir, { recursive: true });
|
|
230
|
+
const profilePath = resolve6(sandboxDir, "seatbelt.sb");
|
|
231
|
+
const profile = this.renderProfile(options);
|
|
232
|
+
writeFileSync(profilePath, `${profile}
|
|
233
|
+
`, "utf-8");
|
|
234
|
+
return profilePath;
|
|
235
|
+
}
|
|
236
|
+
renderProfile(options) {
|
|
237
|
+
const { runtime, projectRoot } = options;
|
|
238
|
+
const { ctx, resolvedPaths, config } = this;
|
|
239
|
+
const workspaceReal = ctx.realPath(runtime.workspaceDir);
|
|
240
|
+
const runtimeRootReal = ctx.realPath(runtime.rootDir);
|
|
241
|
+
const homeReal = ctx.realPath(runtime.homeDir);
|
|
242
|
+
const tmpReal = ctx.realPath(runtime.tmpDir);
|
|
243
|
+
const cacheReal = ctx.realPath(runtime.cacheDir);
|
|
244
|
+
const hostGitDirs = resolveHostGitMetadataPaths(projectRoot, runtime.workspaceDir);
|
|
245
|
+
const hostRepoRoots = resolveHostRepoRootPaths(projectRoot).map((repoRoot) => ctx.realPath(repoRoot));
|
|
246
|
+
const bunDir = ctx.realPath(resolvedPaths.bunDir);
|
|
247
|
+
const claudeDir = resolvedPaths.claudeDir ? ctx.realPath(resolvedPaths.claudeDir) : null;
|
|
248
|
+
const allowNetwork = resolveNetworkWithPolicy(config, process.env.RIG_RUNTIME_SANDBOX_NETWORK);
|
|
249
|
+
const lines = [
|
|
250
|
+
"(version 1)",
|
|
251
|
+
"(deny default)",
|
|
252
|
+
'(import "system.sb")',
|
|
253
|
+
"(allow process*)",
|
|
254
|
+
"(allow process-info*)",
|
|
255
|
+
"(allow signal)",
|
|
256
|
+
"(allow sysctl-read)",
|
|
257
|
+
"(allow file-read-metadata)"
|
|
258
|
+
];
|
|
259
|
+
if (allowNetwork) {
|
|
260
|
+
lines.push("(allow network*)");
|
|
261
|
+
}
|
|
262
|
+
for (const sysPath of [
|
|
263
|
+
"/usr/lib",
|
|
264
|
+
"/usr/bin",
|
|
265
|
+
"/usr/sbin",
|
|
266
|
+
"/usr/share",
|
|
267
|
+
"/bin",
|
|
268
|
+
"/sbin",
|
|
269
|
+
"/System",
|
|
270
|
+
"/Library",
|
|
271
|
+
"/Library/Frameworks",
|
|
272
|
+
"/Library/Developer",
|
|
273
|
+
"/Library/Apple",
|
|
274
|
+
"/Applications",
|
|
275
|
+
"/private/var/db",
|
|
276
|
+
"/opt/homebrew"
|
|
277
|
+
]) {
|
|
278
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(sysPath)}))`);
|
|
279
|
+
}
|
|
280
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(bunDir)}))`);
|
|
281
|
+
if (claudeDir) {
|
|
282
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(claudeDir)}))`);
|
|
283
|
+
}
|
|
284
|
+
if (resolvedPaths.nodeDir) {
|
|
285
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(resolvedPaths.nodeDir)}))`);
|
|
286
|
+
}
|
|
287
|
+
for (const depPath of resolvedPaths.depRoots) {
|
|
288
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(depPath)}))`);
|
|
289
|
+
}
|
|
290
|
+
for (const rwPath of uniq([
|
|
291
|
+
workspaceReal,
|
|
292
|
+
runtimeRootReal,
|
|
293
|
+
homeReal,
|
|
294
|
+
tmpReal,
|
|
295
|
+
cacheReal
|
|
296
|
+
])) {
|
|
297
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(rwPath)}))`);
|
|
298
|
+
lines.push(`(allow file-write* (subpath ${seatbeltString(rwPath)}))`);
|
|
299
|
+
}
|
|
300
|
+
for (const gitPath of hostGitDirs) {
|
|
301
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(gitPath)}))`);
|
|
302
|
+
lines.push(`(allow file-write* (subpath ${seatbeltString(gitPath)}))`);
|
|
303
|
+
}
|
|
304
|
+
const projectRootReal = ctx.realPath(projectRoot);
|
|
305
|
+
if (projectRootReal !== workspaceReal && !projectRootReal.startsWith(workspaceReal + "/")) {
|
|
306
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(projectRootReal)}))`);
|
|
307
|
+
}
|
|
308
|
+
for (const repoRoot of hostRepoRoots) {
|
|
309
|
+
if (!ctx.pathExists(repoRoot) || repoRoot === workspaceReal || repoRoot.startsWith(workspaceReal + "/") || repoRoot === projectRootReal || repoRoot.startsWith(projectRootReal + "/")) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(repoRoot)}))`);
|
|
313
|
+
}
|
|
314
|
+
const realHome = process.env.HOME?.trim();
|
|
315
|
+
if (realHome) {
|
|
316
|
+
for (const binSubdir of [".local/bin", ".cargo/bin"]) {
|
|
317
|
+
const binPath = resolve6(realHome, binSubdir);
|
|
318
|
+
if (ctx.pathExists(binPath)) {
|
|
319
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(ctx.realPath(binPath))}))`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const tempPath of [
|
|
324
|
+
"/dev",
|
|
325
|
+
"/tmp",
|
|
326
|
+
"/private/tmp",
|
|
327
|
+
"/var/folders",
|
|
328
|
+
"/private/var/folders"
|
|
329
|
+
]) {
|
|
330
|
+
lines.push(`(allow file-read* (subpath ${seatbeltString(tempPath)}))`);
|
|
331
|
+
lines.push(`(allow file-write* (subpath ${seatbeltString(tempPath)}))`);
|
|
332
|
+
}
|
|
333
|
+
return lines.join(`
|
|
334
|
+
`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
var init_backend_seatbelt = __esm(() => {
|
|
338
|
+
init_utils();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend-bwrap.ts
|
|
342
|
+
var exports_backend_bwrap = {};
|
|
343
|
+
__export(exports_backend_bwrap, {
|
|
344
|
+
BwrapBackend: () => BwrapBackend
|
|
345
|
+
});
|
|
346
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
347
|
+
import { resolve as resolve7 } from "path";
|
|
348
|
+
|
|
349
|
+
class BwrapBackend {
|
|
350
|
+
kind = "linux-bwrap";
|
|
351
|
+
binaryPath;
|
|
352
|
+
config;
|
|
353
|
+
ctx;
|
|
354
|
+
resolvedPaths;
|
|
355
|
+
which;
|
|
356
|
+
constructor(binaryPath, config, ctx, resolvedPaths, which) {
|
|
357
|
+
this.binaryPath = binaryPath;
|
|
358
|
+
this.config = config;
|
|
359
|
+
this.ctx = ctx;
|
|
360
|
+
this.resolvedPaths = resolvedPaths;
|
|
361
|
+
this.which = which ?? ((bin) => Bun.which(bin));
|
|
362
|
+
}
|
|
363
|
+
wrap(options) {
|
|
364
|
+
const command = this.buildCommand(options);
|
|
365
|
+
return {
|
|
366
|
+
command,
|
|
367
|
+
enabled: true,
|
|
368
|
+
backend: "linux-bwrap"
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
buildCommand(options) {
|
|
372
|
+
const { runtime, projectRoot, command } = options;
|
|
373
|
+
const { ctx, resolvedPaths, config } = this;
|
|
374
|
+
const workspaceReal = ctx.realPath(runtime.workspaceDir);
|
|
375
|
+
const runtimeRootReal = ctx.realPath(runtime.rootDir);
|
|
376
|
+
const homeReal = ctx.realPath(runtime.homeDir);
|
|
377
|
+
const tmpReal = ctx.realPath(runtime.tmpDir);
|
|
378
|
+
const cacheReal = ctx.realPath(runtime.cacheDir);
|
|
379
|
+
const hostGitDirs = resolveHostGitMetadataPaths(projectRoot, runtime.workspaceDir);
|
|
380
|
+
const hostRepoRoots = resolveHostRepoRootPaths(projectRoot).map((repoRoot) => ctx.realPath(repoRoot));
|
|
381
|
+
const bunDir = ctx.realPath(resolvedPaths.bunDir);
|
|
382
|
+
const claudeDir = resolvedPaths.claudeDir ? ctx.realPath(resolvedPaths.claudeDir) : null;
|
|
383
|
+
const allowNetwork = resolveNetworkWithPolicy(config, process.env.RIG_RUNTIME_SANDBOX_NETWORK);
|
|
384
|
+
const args = [
|
|
385
|
+
this.binaryPath,
|
|
386
|
+
"--die-with-parent",
|
|
387
|
+
"--new-session",
|
|
388
|
+
"--unshare-pid",
|
|
389
|
+
"--tmpfs",
|
|
390
|
+
"/",
|
|
391
|
+
"--ro-bind",
|
|
392
|
+
"/usr",
|
|
393
|
+
"/usr",
|
|
394
|
+
"--ro-bind",
|
|
395
|
+
"/bin",
|
|
396
|
+
"/bin",
|
|
397
|
+
"--ro-bind",
|
|
398
|
+
"/sbin",
|
|
399
|
+
"/sbin"
|
|
400
|
+
];
|
|
401
|
+
for (const libPath of ["/lib", "/lib64"]) {
|
|
402
|
+
if (ctx.pathExists(libPath)) {
|
|
403
|
+
args.push("--ro-bind", libPath, libPath);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
for (const etcEntry of [
|
|
407
|
+
"/etc/ld.so.cache",
|
|
408
|
+
"/etc/ld.so.conf",
|
|
409
|
+
"/etc/ld.so.conf.d",
|
|
410
|
+
"/etc/resolv.conf",
|
|
411
|
+
"/etc/hosts",
|
|
412
|
+
"/etc/nsswitch.conf",
|
|
413
|
+
"/etc/gai.conf",
|
|
414
|
+
"/etc/host.conf",
|
|
415
|
+
"/etc/ssl",
|
|
416
|
+
"/etc/ca-certificates",
|
|
417
|
+
"/etc/pki",
|
|
418
|
+
"/etc/passwd",
|
|
419
|
+
"/etc/group",
|
|
420
|
+
"/etc/localtime",
|
|
421
|
+
"/etc/timezone",
|
|
422
|
+
"/etc/locale.conf",
|
|
423
|
+
"/etc/default/locale"
|
|
424
|
+
]) {
|
|
425
|
+
if (ctx.pathExists(etcEntry)) {
|
|
426
|
+
args.push("--ro-bind", etcEntry, etcEntry);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (ctx.pathExists("/run/systemd/resolve")) {
|
|
430
|
+
args.push("--ro-bind", "/run/systemd/resolve", "/run/systemd/resolve");
|
|
431
|
+
}
|
|
432
|
+
if (ctx.pathExists("/run/nscd")) {
|
|
433
|
+
args.push("--ro-bind", "/run/nscd", "/run/nscd");
|
|
434
|
+
}
|
|
435
|
+
args.push("--ro-bind", bunDir, bunDir);
|
|
436
|
+
if (claudeDir) {
|
|
437
|
+
args.push("--ro-bind", claudeDir, claudeDir);
|
|
438
|
+
}
|
|
439
|
+
if (resolvedPaths.nodeDir && ctx.pathExists(resolvedPaths.nodeDir)) {
|
|
440
|
+
args.push("--ro-bind", resolvedPaths.nodeDir, resolvedPaths.nodeDir);
|
|
441
|
+
}
|
|
442
|
+
for (const depPath of resolvedPaths.depRoots) {
|
|
443
|
+
if (ctx.pathExists(depPath)) {
|
|
444
|
+
args.push("--ro-bind", depPath, depPath);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const projectRootReal = ctx.realPath(projectRoot);
|
|
448
|
+
if (projectRootReal !== workspaceReal && !projectRootReal.startsWith(workspaceReal + "/") && ctx.pathExists(projectRootReal)) {
|
|
449
|
+
args.push("--ro-bind", projectRootReal, projectRootReal);
|
|
450
|
+
}
|
|
451
|
+
for (const repoRoot of hostRepoRoots) {
|
|
452
|
+
if (!ctx.pathExists(repoRoot) || repoRoot === workspaceReal || repoRoot.startsWith(workspaceReal + "/") || repoRoot === projectRootReal || repoRoot.startsWith(projectRootReal + "/")) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
args.push("--ro-bind", repoRoot, repoRoot);
|
|
456
|
+
}
|
|
457
|
+
args.push("--bind", workspaceReal, workspaceReal);
|
|
458
|
+
args.push("--bind", runtimeRootReal, runtimeRootReal);
|
|
459
|
+
for (const rwPath of uniq([homeReal, tmpReal, cacheReal])) {
|
|
460
|
+
if (rwPath !== runtimeRootReal && !rwPath.startsWith(runtimeRootReal + "/")) {
|
|
461
|
+
args.push("--bind", rwPath, rwPath);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (const gitPath of hostGitDirs) {
|
|
465
|
+
if (!ctx.pathExists(gitPath))
|
|
466
|
+
continue;
|
|
467
|
+
if (gitPath === runtimeRootReal || gitPath.startsWith(runtimeRootReal + "/"))
|
|
468
|
+
continue;
|
|
469
|
+
if (gitPath === workspaceReal || gitPath.startsWith(workspaceReal + "/"))
|
|
470
|
+
continue;
|
|
471
|
+
args.push("--bind", gitPath, gitPath);
|
|
472
|
+
}
|
|
473
|
+
const realHome = process.env.HOME?.trim();
|
|
474
|
+
if (realHome) {
|
|
475
|
+
for (const binSubdir of [".local/bin", ".local/lib", ".cargo/bin"]) {
|
|
476
|
+
const binPath = ctx.realPath(resolve7(realHome, binSubdir));
|
|
477
|
+
if (ctx.pathExists(binPath)) {
|
|
478
|
+
args.push("--ro-bind", binPath, binPath);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const agentSshDir = resolve7(homeReal, ".ssh");
|
|
482
|
+
if (ctx.pathExists(agentSshDir)) {
|
|
483
|
+
args.push("--ro-bind", agentSshDir, agentSshDir);
|
|
484
|
+
} else {
|
|
485
|
+
const hostSshDir = resolve7(realHome, ".ssh");
|
|
486
|
+
if (ctx.pathExists(hostSshDir)) {
|
|
487
|
+
mkdirSync3(agentSshDir, { recursive: true });
|
|
488
|
+
args.push("--ro-bind", hostSshDir, agentSshDir);
|
|
489
|
+
args.push("--ro-bind", hostSshDir, hostSshDir);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
args.push("--proc", "/proc");
|
|
494
|
+
args.push("--dev", "/dev");
|
|
495
|
+
args.push("--tmpfs", "/tmp");
|
|
496
|
+
args.push("--unsetenv", "CLAUDECODE");
|
|
497
|
+
args.push("--setenv", "HOME", homeReal);
|
|
498
|
+
args.push("--setenv", "TMPDIR", "/tmp");
|
|
499
|
+
args.push("--chdir", workspaceReal);
|
|
500
|
+
if (!allowNetwork) {
|
|
501
|
+
args.push("--unshare-net");
|
|
502
|
+
}
|
|
503
|
+
args.push("--", ...this.resolveCommandBinary(command));
|
|
504
|
+
return args;
|
|
505
|
+
}
|
|
506
|
+
resolveCommandBinary(command) {
|
|
507
|
+
if (command.length === 0)
|
|
508
|
+
return command;
|
|
509
|
+
const [bin, ...rest] = command;
|
|
510
|
+
if (!bin)
|
|
511
|
+
return command;
|
|
512
|
+
const resolved = this.which(bin);
|
|
513
|
+
if (!resolved)
|
|
514
|
+
return command;
|
|
515
|
+
try {
|
|
516
|
+
const { realpathSync: realpathSync3 } = __require("fs");
|
|
517
|
+
const resolvedBinary = realpathSync3(resolved);
|
|
518
|
+
return [resolvedBinary, ...rest];
|
|
519
|
+
} catch {
|
|
520
|
+
return [resolved, ...rest];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
var init_backend_bwrap = __esm(() => {
|
|
525
|
+
init_utils();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// packages/runtime/src/control-plane/runtime/isolation/runner.ts
|
|
529
|
+
import { existsSync as existsSync10, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
530
|
+
import { basename as basename5, resolve as resolve16 } from "path";
|
|
531
|
+
|
|
532
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend.ts
|
|
533
|
+
import { existsSync as existsSync6 } from "fs";
|
|
534
|
+
|
|
535
|
+
// packages/runtime/src/control-plane/runtime/guard.ts
|
|
536
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
537
|
+
import { existsSync as existsSync3, readFileSync, statSync as statSync2 } from "fs";
|
|
538
|
+
import { resolve as resolve3 } from "path";
|
|
539
|
+
|
|
540
|
+
// packages/runtime/src/control-plane/native/utils.ts
|
|
541
|
+
import { ptr as ptr2 } from "bun:ffi";
|
|
542
|
+
init_layout();
|
|
543
|
+
|
|
544
|
+
// packages/runtime/src/control-plane/native/runtime-native.ts
|
|
545
|
+
import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
|
|
546
|
+
import { copyFileSync, existsSync as existsSync2, mkdirSync, renameSync, rmSync, statSync } from "fs";
|
|
547
|
+
import { tmpdir } from "os";
|
|
548
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
549
|
+
var sharedNativeRuntimeOutputDir = resolve2(tmpdir(), "rig-native");
|
|
550
|
+
var sharedNativeRuntimeOutputPath = resolve2(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
|
|
551
|
+
var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
|
|
552
|
+
var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
|
|
553
|
+
function requireNativeRuntimeLibrary(feature) {
|
|
554
|
+
if (!nativeRuntimeLibrary) {
|
|
555
|
+
throw new Error(`Native Zig runtime is required for ${feature}`);
|
|
556
|
+
}
|
|
557
|
+
return nativeRuntimeLibrary;
|
|
558
|
+
}
|
|
559
|
+
async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
|
|
560
|
+
if (await buildNativeRuntimeLibrary(outputPath, options)) {
|
|
561
|
+
return outputPath;
|
|
562
|
+
}
|
|
563
|
+
return !options.force && existsSync2(outputPath) ? outputPath : null;
|
|
564
|
+
}
|
|
565
|
+
async function materializeNativeRuntimeLibrary(targetDir) {
|
|
566
|
+
const sourcePath = await ensureNativeRuntimeLibraryPath();
|
|
567
|
+
if (!sourcePath) {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
const targetPath = resolve2(targetDir, colocatedNativeRuntimeFileName);
|
|
571
|
+
mkdirSync(targetDir, { recursive: true });
|
|
572
|
+
const needsCopy = !existsSync2(targetPath) || statSync(sourcePath).mtimeMs > statSync(targetPath).mtimeMs;
|
|
573
|
+
if (needsCopy) {
|
|
574
|
+
copyFileSync(sourcePath, targetPath);
|
|
575
|
+
}
|
|
576
|
+
return targetPath;
|
|
577
|
+
}
|
|
578
|
+
async function loadNativeRuntimeLibrary() {
|
|
579
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
for (const candidate of nativeRuntimeLibraryCandidates()) {
|
|
583
|
+
if (!candidate || !existsSync2(candidate)) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const loaded = tryDlopenNativeRuntimeLibrary(candidate);
|
|
587
|
+
if (loaded) {
|
|
588
|
+
return loaded;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
|
|
592
|
+
if (!builtLibraryPath) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
|
|
596
|
+
}
|
|
597
|
+
function nativePackageLibraryCandidates(fromDir, names) {
|
|
598
|
+
const candidates = [];
|
|
599
|
+
let cursor = resolve2(fromDir);
|
|
600
|
+
for (let index = 0;index < 8; index += 1) {
|
|
601
|
+
for (const name of names) {
|
|
602
|
+
candidates.push(resolve2(cursor, "native", `${process.platform}-${process.arch}`, name), resolve2(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve2(cursor, "native", name), resolve2(cursor, "native", "lib", name));
|
|
603
|
+
}
|
|
604
|
+
const parent = dirname2(cursor);
|
|
605
|
+
if (parent === cursor)
|
|
606
|
+
break;
|
|
607
|
+
cursor = parent;
|
|
608
|
+
}
|
|
609
|
+
return candidates;
|
|
610
|
+
}
|
|
611
|
+
function nativeRuntimeLibraryCandidates() {
|
|
612
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
|
|
613
|
+
const execDir = process.execPath?.trim() ? dirname2(process.execPath.trim()) : "";
|
|
614
|
+
const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
|
|
615
|
+
return [...new Set([
|
|
616
|
+
explicit,
|
|
617
|
+
...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
|
|
618
|
+
execDir ? resolve2(execDir, colocatedNativeRuntimeFileName) : "",
|
|
619
|
+
execDir ? resolve2(execDir, platformSpecific) : "",
|
|
620
|
+
execDir ? resolve2(execDir, "..", colocatedNativeRuntimeFileName) : "",
|
|
621
|
+
execDir ? resolve2(execDir, "..", platformSpecific) : "",
|
|
622
|
+
execDir ? resolve2(execDir, "lib", colocatedNativeRuntimeFileName) : "",
|
|
623
|
+
execDir ? resolve2(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
|
|
624
|
+
sharedNativeRuntimeOutputPath
|
|
625
|
+
].filter(Boolean))];
|
|
626
|
+
}
|
|
627
|
+
function resolveNativeRuntimeSourcePath() {
|
|
628
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
|
|
629
|
+
if (explicit && existsSync2(explicit)) {
|
|
630
|
+
return explicit;
|
|
631
|
+
}
|
|
632
|
+
const bundled = resolve2(import.meta.dir, "../../../native/snapshot.zig");
|
|
633
|
+
return existsSync2(bundled) ? bundled : null;
|
|
634
|
+
}
|
|
635
|
+
async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
636
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
const zigBinary = Bun.which("zig");
|
|
640
|
+
const sourcePath = resolveNativeRuntimeSourcePath();
|
|
641
|
+
if (!zigBinary || !sourcePath) {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
645
|
+
try {
|
|
646
|
+
mkdirSync(dirname2(outputPath), { recursive: true });
|
|
647
|
+
const needsBuild = options.force === true || !existsSync2(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
|
|
648
|
+
if (!needsBuild) {
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
const build = Bun.spawn([
|
|
652
|
+
zigBinary,
|
|
653
|
+
"build-lib",
|
|
654
|
+
sourcePath,
|
|
655
|
+
"-dynamic",
|
|
656
|
+
"-O",
|
|
657
|
+
"ReleaseFast",
|
|
658
|
+
`-femit-bin=${tempOutputPath}`
|
|
659
|
+
], {
|
|
660
|
+
cwd: import.meta.dir,
|
|
661
|
+
stdout: "pipe",
|
|
662
|
+
stderr: "pipe"
|
|
663
|
+
});
|
|
664
|
+
const exitCode = await build.exited;
|
|
665
|
+
if (exitCode !== 0 || !existsSync2(tempOutputPath)) {
|
|
666
|
+
rmSync(tempOutputPath, { force: true });
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
renameSync(tempOutputPath, outputPath);
|
|
670
|
+
return true;
|
|
671
|
+
} catch {
|
|
672
|
+
rmSync(tempOutputPath, { force: true });
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
function tryDlopenNativeRuntimeLibrary(outputPath) {
|
|
677
|
+
try {
|
|
678
|
+
return dlopen(outputPath, {
|
|
679
|
+
rig_scope_match: {
|
|
680
|
+
args: ["ptr", "ptr"],
|
|
681
|
+
returns: "u8"
|
|
682
|
+
},
|
|
683
|
+
snapshot_capture: {
|
|
684
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
685
|
+
returns: "ptr"
|
|
686
|
+
},
|
|
687
|
+
snapshot_delta: {
|
|
688
|
+
args: ["ptr", "ptr"],
|
|
689
|
+
returns: "ptr"
|
|
690
|
+
},
|
|
691
|
+
snapshot_store_delta: {
|
|
692
|
+
args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
693
|
+
returns: "ptr"
|
|
694
|
+
},
|
|
695
|
+
snapshot_inspect_delta: {
|
|
696
|
+
args: ["ptr", "u64"],
|
|
697
|
+
returns: "ptr"
|
|
698
|
+
},
|
|
699
|
+
snapshot_apply_delta: {
|
|
700
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
701
|
+
returns: "ptr"
|
|
702
|
+
},
|
|
703
|
+
snapshot_release: {
|
|
704
|
+
args: ["ptr"],
|
|
705
|
+
returns: "void"
|
|
706
|
+
},
|
|
707
|
+
runtime_hash_file: {
|
|
708
|
+
args: ["ptr", "u64"],
|
|
709
|
+
returns: "ptr"
|
|
710
|
+
},
|
|
711
|
+
runtime_hash_tree: {
|
|
712
|
+
args: ["ptr", "u64"],
|
|
713
|
+
returns: "ptr"
|
|
714
|
+
},
|
|
715
|
+
runtime_prepare_paths: {
|
|
716
|
+
args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
717
|
+
returns: "ptr"
|
|
718
|
+
},
|
|
719
|
+
runtime_link_dependency_layer: {
|
|
720
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
721
|
+
returns: "ptr"
|
|
722
|
+
},
|
|
723
|
+
runtime_scan_worktrees: {
|
|
724
|
+
args: ["ptr", "u64"],
|
|
725
|
+
returns: "ptr"
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
} catch {
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// packages/runtime/src/control-plane/native/scope-rules.ts
|
|
734
|
+
var activeRules = null;
|
|
735
|
+
function getScopeRules() {
|
|
736
|
+
return activeRules;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// packages/runtime/src/control-plane/native/utils.ts
|
|
740
|
+
function resolveMonorepoRoot2(projectRoot) {
|
|
741
|
+
return resolveMonorepoRoot(projectRoot);
|
|
742
|
+
}
|
|
743
|
+
var nativeScopeMatcher = null;
|
|
744
|
+
var scopeRegexCache = new Map;
|
|
745
|
+
function unique(values) {
|
|
746
|
+
return [...new Set(values)];
|
|
747
|
+
}
|
|
748
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
749
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
750
|
+
const rules = getScopeRules();
|
|
751
|
+
if (rules?.stripPrefixes) {
|
|
752
|
+
for (const prefix of rules.stripPrefixes) {
|
|
753
|
+
if (normalized.startsWith(prefix)) {
|
|
754
|
+
normalized = normalized.slice(prefix.length);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return normalized;
|
|
759
|
+
}
|
|
760
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
761
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
762
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
763
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
764
|
+
}
|
|
765
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
766
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
767
|
+
}
|
|
768
|
+
return normalizeRelativeScopePath(normalized);
|
|
769
|
+
}
|
|
770
|
+
function scopeMatches(path, scopes) {
|
|
771
|
+
const matcher = getNativeScopeMatcher();
|
|
772
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
773
|
+
for (const scope of scopes) {
|
|
774
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
775
|
+
for (const candidatePath of pathVariants) {
|
|
776
|
+
for (const candidateScope of scopeVariants) {
|
|
777
|
+
if (candidatePath === candidateScope) {
|
|
778
|
+
return true;
|
|
779
|
+
}
|
|
780
|
+
if (matcher.match(candidateScope, candidatePath)) {
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
function getNativeScopeMatcher() {
|
|
789
|
+
if (nativeScopeMatcher) {
|
|
790
|
+
return nativeScopeMatcher;
|
|
791
|
+
}
|
|
792
|
+
nativeScopeMatcher = createNativeScopeMatcher();
|
|
793
|
+
return nativeScopeMatcher;
|
|
794
|
+
}
|
|
795
|
+
function createNativeScopeMatcher() {
|
|
796
|
+
const library = requireNativeRuntimeLibrary("scope matching");
|
|
797
|
+
return {
|
|
798
|
+
match: (pattern, path) => {
|
|
799
|
+
const patternBuffer = Buffer.from(`${pattern}\x00`);
|
|
800
|
+
const pathBuffer = Buffer.from(`${path}\x00`);
|
|
801
|
+
return library.symbols.rig_scope_match(Number(ptr2(patternBuffer)), Number(ptr2(pathBuffer))) !== 0;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// packages/runtime/src/control-plane/runtime/guard-types.ts
|
|
807
|
+
var POLICY_VERSION = 1;
|
|
808
|
+
|
|
809
|
+
// packages/runtime/src/control-plane/runtime/guard.ts
|
|
810
|
+
var DEFAULT_SCOPE = {
|
|
811
|
+
fail_closed: true,
|
|
812
|
+
harness_paths_exempt: true,
|
|
813
|
+
runtime_paths_exempt: true
|
|
814
|
+
};
|
|
815
|
+
var DEFAULT_SANDBOX = {
|
|
816
|
+
mode: "enforce",
|
|
817
|
+
network: true,
|
|
818
|
+
read_deny: [],
|
|
819
|
+
write_allow_from_runtime: true
|
|
820
|
+
};
|
|
821
|
+
var DEFAULT_ISOLATION = {
|
|
822
|
+
default_mode: "worktree",
|
|
823
|
+
repo_symlink_fallback: false,
|
|
824
|
+
strict_provisioning: true,
|
|
825
|
+
fail_closed_on_provision_error: true
|
|
826
|
+
};
|
|
827
|
+
var DEFAULT_COMPLETION = {
|
|
828
|
+
derive_checks_from_scope: true,
|
|
829
|
+
checks: [],
|
|
830
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
831
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
832
|
+
};
|
|
833
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
834
|
+
deps: {
|
|
835
|
+
monorepo_install: false,
|
|
836
|
+
hp_next_install: false
|
|
837
|
+
},
|
|
838
|
+
plugins_require_binaries: true
|
|
839
|
+
};
|
|
840
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
841
|
+
enabled: true
|
|
842
|
+
};
|
|
843
|
+
function defaultPolicy() {
|
|
844
|
+
return {
|
|
845
|
+
version: POLICY_VERSION,
|
|
846
|
+
mode: "enforce",
|
|
847
|
+
scope: { ...DEFAULT_SCOPE },
|
|
848
|
+
rules: [],
|
|
849
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
850
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
851
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
852
|
+
runtime_image: {
|
|
853
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
854
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
855
|
+
},
|
|
856
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
var policyCache = null;
|
|
860
|
+
var policyCachePath = null;
|
|
861
|
+
var seededPolicyConfig = null;
|
|
862
|
+
var compiledRegexCache = new Map;
|
|
863
|
+
function loadPolicy(projectRoot) {
|
|
864
|
+
if (seededPolicyConfig) {
|
|
865
|
+
return seededPolicyConfig;
|
|
866
|
+
}
|
|
867
|
+
const configPath = resolve3(projectRoot, "rig/policy/policy.json");
|
|
868
|
+
if (!existsSync3(configPath)) {
|
|
869
|
+
return defaultPolicy();
|
|
870
|
+
}
|
|
871
|
+
let mtimeMs;
|
|
872
|
+
try {
|
|
873
|
+
mtimeMs = statSync2(configPath).mtimeMs;
|
|
874
|
+
} catch {
|
|
875
|
+
return defaultPolicy();
|
|
876
|
+
}
|
|
877
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
878
|
+
return policyCache.config;
|
|
879
|
+
}
|
|
880
|
+
let parsed;
|
|
881
|
+
try {
|
|
882
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
883
|
+
} catch {
|
|
884
|
+
return defaultPolicy();
|
|
885
|
+
}
|
|
886
|
+
const config = mergeWithDefaults(parsed);
|
|
887
|
+
policyCache = { mtimeMs, config };
|
|
888
|
+
policyCachePath = configPath;
|
|
889
|
+
return config;
|
|
890
|
+
}
|
|
891
|
+
function mergeWithDefaults(parsed) {
|
|
892
|
+
const base = defaultPolicy();
|
|
893
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
894
|
+
base.mode = parsed.mode;
|
|
895
|
+
}
|
|
896
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
897
|
+
const s = parsed.scope;
|
|
898
|
+
if (typeof s.fail_closed === "boolean")
|
|
899
|
+
base.scope.fail_closed = s.fail_closed;
|
|
900
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
901
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
902
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
903
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
904
|
+
}
|
|
905
|
+
if (Array.isArray(parsed.rules)) {
|
|
906
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
907
|
+
}
|
|
908
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
909
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
910
|
+
}
|
|
911
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
912
|
+
const sb = parsed.sandbox;
|
|
913
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
914
|
+
base.sandbox.mode = sb.mode;
|
|
915
|
+
if (typeof sb.network === "boolean")
|
|
916
|
+
base.sandbox.network = sb.network;
|
|
917
|
+
if (Array.isArray(sb.read_deny))
|
|
918
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
919
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
920
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
921
|
+
}
|
|
922
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
923
|
+
const iso = parsed.isolation;
|
|
924
|
+
if (iso.default_mode === "worktree")
|
|
925
|
+
base.isolation.default_mode = iso.default_mode;
|
|
926
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
927
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
928
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
929
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
930
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
931
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
932
|
+
}
|
|
933
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
934
|
+
const comp = parsed.completion;
|
|
935
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
936
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
937
|
+
if (Array.isArray(comp.checks))
|
|
938
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
939
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
940
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
941
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
942
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
943
|
+
}
|
|
944
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
945
|
+
const runtimeImage = parsed.runtime_image;
|
|
946
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
947
|
+
const deps = runtimeImage.deps;
|
|
948
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
949
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
950
|
+
}
|
|
951
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
952
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
956
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
960
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
961
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
962
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return base;
|
|
966
|
+
}
|
|
967
|
+
function isValidMode(value) {
|
|
968
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
969
|
+
}
|
|
970
|
+
function isValidRule(value) {
|
|
971
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
972
|
+
return false;
|
|
973
|
+
const r = value;
|
|
974
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
975
|
+
}
|
|
976
|
+
function migrateLegacyDeny(deny) {
|
|
977
|
+
const rules = [];
|
|
978
|
+
for (const entry of deny) {
|
|
979
|
+
if (typeof entry.id !== "string")
|
|
980
|
+
continue;
|
|
981
|
+
const match = {};
|
|
982
|
+
if (typeof entry.pattern === "string")
|
|
983
|
+
match.pattern = entry.pattern;
|
|
984
|
+
if (typeof entry.regex === "string")
|
|
985
|
+
match.regex = entry.regex;
|
|
986
|
+
if (!match.pattern && !match.regex)
|
|
987
|
+
continue;
|
|
988
|
+
rules.push({
|
|
989
|
+
id: entry.id,
|
|
990
|
+
category: "command",
|
|
991
|
+
match,
|
|
992
|
+
action: "block",
|
|
993
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
return rules;
|
|
997
|
+
}
|
|
998
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
999
|
+
return rules.map((rule) => {
|
|
1000
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
1001
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
1002
|
+
return {
|
|
1003
|
+
...rule,
|
|
1004
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
1005
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
1006
|
+
};
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function getRegexUnsafeReason(pattern) {
|
|
1010
|
+
if (pattern.length > 512) {
|
|
1011
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
1012
|
+
}
|
|
1013
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
1014
|
+
return "pattern uses backreferences";
|
|
1015
|
+
}
|
|
1016
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
1017
|
+
return "pattern contains nested quantifiers";
|
|
1018
|
+
}
|
|
1019
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
1020
|
+
return "pattern contains nested broad quantifiers";
|
|
1021
|
+
}
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
1025
|
+
const cached = compiledRegexCache.get(pattern);
|
|
1026
|
+
if (cached !== undefined) {
|
|
1027
|
+
return cached ?? undefined;
|
|
1028
|
+
}
|
|
1029
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
1030
|
+
if (unsafeReason) {
|
|
1031
|
+
if (logOnFailure) {
|
|
1032
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
1033
|
+
}
|
|
1034
|
+
compiledRegexCache.set(pattern, null);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const compiled = new RegExp(pattern);
|
|
1039
|
+
compiledRegexCache.set(pattern, compiled);
|
|
1040
|
+
return compiled;
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
if (logOnFailure) {
|
|
1043
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1044
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
1045
|
+
}
|
|
1046
|
+
compiledRegexCache.set(pattern, null);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function matchRule(rule, input) {
|
|
1051
|
+
const { match } = rule;
|
|
1052
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
1053
|
+
return true;
|
|
1054
|
+
}
|
|
1055
|
+
if (match.regex) {
|
|
1056
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
1057
|
+
if (!compiled) {
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
return compiled.test(input);
|
|
1062
|
+
} catch {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
1069
|
+
if (!rule.unless)
|
|
1070
|
+
return false;
|
|
1071
|
+
if (rule.unless.regex) {
|
|
1072
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
1073
|
+
if (!compiled) {
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1076
|
+
try {
|
|
1077
|
+
if (compiled.test(command))
|
|
1078
|
+
return true;
|
|
1079
|
+
} catch {}
|
|
1080
|
+
}
|
|
1081
|
+
if (rule.unless.task_in && taskId) {
|
|
1082
|
+
if (rule.unless.task_in.includes(taskId))
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
function resolveAction(mode, matched) {
|
|
1088
|
+
if (matched.length === 0)
|
|
1089
|
+
return "allow";
|
|
1090
|
+
if (mode === "off")
|
|
1091
|
+
return "allow";
|
|
1092
|
+
if (mode === "observe")
|
|
1093
|
+
return "warn";
|
|
1094
|
+
return "block";
|
|
1095
|
+
}
|
|
1096
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
1097
|
+
if (rawPath.startsWith("/"))
|
|
1098
|
+
return resolve3(rawPath);
|
|
1099
|
+
return resolve3(projectRoot, rawPath);
|
|
1100
|
+
}
|
|
1101
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
1102
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
1103
|
+
const managedRoots = [
|
|
1104
|
+
resolve3(projectRoot, "rig"),
|
|
1105
|
+
resolve3(projectRoot, ".rig"),
|
|
1106
|
+
resolve3(projectRoot, "artifacts")
|
|
1107
|
+
];
|
|
1108
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
1109
|
+
}
|
|
1110
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
1111
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
1112
|
+
if (taskWorkspace) {
|
|
1113
|
+
const workspaceRigRoot = resolve3(taskWorkspace, ".rig");
|
|
1114
|
+
const workspaceArtifactsRoot = resolve3(taskWorkspace, "artifacts");
|
|
1115
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
const runtimeRoot = resolve3(projectRoot, ".rig/runtime/agents");
|
|
1120
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
1121
|
+
}
|
|
1122
|
+
function isTestFile(path) {
|
|
1123
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
1124
|
+
}
|
|
1125
|
+
function evaluate(context) {
|
|
1126
|
+
const policy = loadPolicy(context.projectRoot);
|
|
1127
|
+
switch (context.evaluation.type) {
|
|
1128
|
+
case "tool-call":
|
|
1129
|
+
return evaluateToolCall(policy, context);
|
|
1130
|
+
case "command":
|
|
1131
|
+
return evaluateCommand(policy, context);
|
|
1132
|
+
case "content-write":
|
|
1133
|
+
return evaluateContent(policy, context);
|
|
1134
|
+
case "file-access":
|
|
1135
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
1139
|
+
const allowed = () => ({
|
|
1140
|
+
allowed: true,
|
|
1141
|
+
matchedRules: [],
|
|
1142
|
+
action: "allow",
|
|
1143
|
+
failClosed: false
|
|
1144
|
+
});
|
|
1145
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
1146
|
+
return allowed();
|
|
1147
|
+
}
|
|
1148
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
1149
|
+
return allowed();
|
|
1150
|
+
}
|
|
1151
|
+
if (!context.taskId) {
|
|
1152
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
1153
|
+
return {
|
|
1154
|
+
allowed: false,
|
|
1155
|
+
matchedRules: [],
|
|
1156
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
1157
|
+
failClosed: true
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
return allowed();
|
|
1161
|
+
}
|
|
1162
|
+
const scopes = context.taskScopes || [];
|
|
1163
|
+
if (scopes.length === 0) {
|
|
1164
|
+
return allowed();
|
|
1165
|
+
}
|
|
1166
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
1167
|
+
const absPath = resolve3(filePath);
|
|
1168
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
1169
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
1170
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
1171
|
+
return {
|
|
1172
|
+
allowed: policy.mode !== "enforce",
|
|
1173
|
+
matchedRules: matched2,
|
|
1174
|
+
action: resolveAction(policy.mode, matched2),
|
|
1175
|
+
failClosed: false
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
1180
|
+
let normalizedPath = filePath;
|
|
1181
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
1182
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
1183
|
+
}
|
|
1184
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
1185
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
1186
|
+
return allowed();
|
|
1187
|
+
}
|
|
1188
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
1189
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
1190
|
+
return {
|
|
1191
|
+
allowed: policy.mode !== "enforce",
|
|
1192
|
+
matchedRules: matched,
|
|
1193
|
+
action: resolveAction(policy.mode, matched),
|
|
1194
|
+
failClosed: false
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
function evaluateCommand(policy, context) {
|
|
1198
|
+
const evaluation = context.evaluation;
|
|
1199
|
+
if (evaluation.type !== "command") {
|
|
1200
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1201
|
+
}
|
|
1202
|
+
const command = evaluation.command;
|
|
1203
|
+
const matchedRules = [];
|
|
1204
|
+
for (const rule of policy.rules) {
|
|
1205
|
+
if (rule.category !== "command")
|
|
1206
|
+
continue;
|
|
1207
|
+
if (!matchRule(rule, command))
|
|
1208
|
+
continue;
|
|
1209
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
1210
|
+
continue;
|
|
1211
|
+
matchedRules.push({
|
|
1212
|
+
id: rule.id,
|
|
1213
|
+
category: rule.category,
|
|
1214
|
+
description: rule.description,
|
|
1215
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
const writeTarget = extractWriteTarget(command);
|
|
1219
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
1220
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
1221
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
1222
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
1226
|
+
return {
|
|
1227
|
+
allowed: action !== "block",
|
|
1228
|
+
matchedRules,
|
|
1229
|
+
action,
|
|
1230
|
+
failClosed: false
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function extractWriteTarget(command) {
|
|
1234
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
1235
|
+
if (redirect?.[1])
|
|
1236
|
+
return redirect[1];
|
|
1237
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
1238
|
+
if (tee?.[2])
|
|
1239
|
+
return tee[2];
|
|
1240
|
+
return "";
|
|
1241
|
+
}
|
|
1242
|
+
function evaluateContent(policy, context) {
|
|
1243
|
+
const evaluation = context.evaluation;
|
|
1244
|
+
if (evaluation.type !== "content-write") {
|
|
1245
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1246
|
+
}
|
|
1247
|
+
const { content, file_path } = evaluation;
|
|
1248
|
+
const matchedRules = [];
|
|
1249
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
1250
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
1251
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
1252
|
+
}
|
|
1253
|
+
for (const rule of policy.rules) {
|
|
1254
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
1255
|
+
continue;
|
|
1256
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
1257
|
+
continue;
|
|
1258
|
+
if (!matchRule(rule, content))
|
|
1259
|
+
continue;
|
|
1260
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
1261
|
+
continue;
|
|
1262
|
+
matchedRules.push({
|
|
1263
|
+
id: rule.id,
|
|
1264
|
+
category: rule.category,
|
|
1265
|
+
description: rule.description,
|
|
1266
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
1270
|
+
return {
|
|
1271
|
+
allowed: action !== "block",
|
|
1272
|
+
matchedRules,
|
|
1273
|
+
action,
|
|
1274
|
+
failClosed: false
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
function evaluateToolCall(policy, context) {
|
|
1278
|
+
const evaluation = context.evaluation;
|
|
1279
|
+
if (evaluation.type !== "tool-call") {
|
|
1280
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1281
|
+
}
|
|
1282
|
+
const { tool_name, tool_input } = evaluation;
|
|
1283
|
+
const allMatched = [];
|
|
1284
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
1285
|
+
for (const fp of filePaths) {
|
|
1286
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
1287
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
1288
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
1289
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const content = extractContentFromToolInput(tool_input);
|
|
1293
|
+
if (content) {
|
|
1294
|
+
const filePath = filePaths[0] || "";
|
|
1295
|
+
const contentContext = {
|
|
1296
|
+
...context,
|
|
1297
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
1298
|
+
};
|
|
1299
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
1300
|
+
for (const rule of contentPolicy.rules) {
|
|
1301
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
1302
|
+
continue;
|
|
1303
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
1304
|
+
continue;
|
|
1305
|
+
if (!matchRule(rule, content))
|
|
1306
|
+
continue;
|
|
1307
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
1308
|
+
continue;
|
|
1309
|
+
allMatched.push({
|
|
1310
|
+
id: rule.id,
|
|
1311
|
+
category: rule.category,
|
|
1312
|
+
description: rule.description,
|
|
1313
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (tool_name === "Bash") {
|
|
1318
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
1319
|
+
if (command) {
|
|
1320
|
+
const cmdContext = {
|
|
1321
|
+
...context,
|
|
1322
|
+
evaluation: { type: "command", command }
|
|
1323
|
+
};
|
|
1324
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
1325
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
1326
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const seen = new Set;
|
|
1331
|
+
const deduplicated = [];
|
|
1332
|
+
for (const rule of allMatched) {
|
|
1333
|
+
if (!seen.has(rule.id)) {
|
|
1334
|
+
seen.add(rule.id);
|
|
1335
|
+
deduplicated.push(rule);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
1339
|
+
return {
|
|
1340
|
+
allowed: action !== "block",
|
|
1341
|
+
matchedRules: deduplicated,
|
|
1342
|
+
action,
|
|
1343
|
+
failClosed: false
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function isWriteTool(toolName) {
|
|
1347
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
1348
|
+
}
|
|
1349
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
1350
|
+
const paths = [];
|
|
1351
|
+
const add = (value) => {
|
|
1352
|
+
if (typeof value === "string" && value.trim()) {
|
|
1353
|
+
paths.push(value.trim());
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
1357
|
+
add(input.file_path);
|
|
1358
|
+
add(input.path);
|
|
1359
|
+
} else if (toolName === "Glob") {
|
|
1360
|
+
add(input.path);
|
|
1361
|
+
} else if (toolName === "Grep") {
|
|
1362
|
+
add(input.path);
|
|
1363
|
+
} else {
|
|
1364
|
+
add(input.file_path);
|
|
1365
|
+
add(input.path);
|
|
1366
|
+
}
|
|
1367
|
+
return paths;
|
|
1368
|
+
}
|
|
1369
|
+
function extractContentFromToolInput(input) {
|
|
1370
|
+
if (typeof input.content === "string")
|
|
1371
|
+
return input.content;
|
|
1372
|
+
if (typeof input.new_string === "string")
|
|
1373
|
+
return input.new_string;
|
|
1374
|
+
return "";
|
|
1375
|
+
}
|
|
1376
|
+
function loadSandboxConfig(projectRoot) {
|
|
1377
|
+
return loadPolicy(projectRoot).sandbox;
|
|
1378
|
+
}
|
|
1379
|
+
var guardHotPathPrimed = false;
|
|
1380
|
+
function primeGuardHotPaths() {
|
|
1381
|
+
if (guardHotPathPrimed) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
guardHotPathPrimed = true;
|
|
1385
|
+
try {
|
|
1386
|
+
optimizeNextInvocation(matchRule);
|
|
1387
|
+
optimizeNextInvocation(evaluate);
|
|
1388
|
+
} catch {}
|
|
1389
|
+
}
|
|
1390
|
+
primeGuardHotPaths();
|
|
1391
|
+
|
|
1392
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
1393
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, realpathSync as realpathSync2 } from "fs";
|
|
1394
|
+
import { resolve as resolve5 } from "path";
|
|
1395
|
+
|
|
1396
|
+
// packages/runtime/src/control-plane/runtime/sandbox-utils.ts
|
|
1397
|
+
init_utils();
|
|
1398
|
+
|
|
1399
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
1400
|
+
function resolveBunBinaryPath() {
|
|
1401
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
1402
|
+
if (explicit) {
|
|
1403
|
+
return explicit;
|
|
1404
|
+
}
|
|
1405
|
+
const pathBun = normalizeExecutablePath(Bun.which("bun")?.trim());
|
|
1406
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
1407
|
+
return pathBun;
|
|
1408
|
+
}
|
|
1409
|
+
const home = process.env.HOME?.trim();
|
|
1410
|
+
const fallbackCandidates = [
|
|
1411
|
+
home ? resolve5(home, ".bun/bin/bun") : "",
|
|
1412
|
+
"/opt/homebrew/bin/bun",
|
|
1413
|
+
"/usr/local/bin/bun",
|
|
1414
|
+
"/usr/bin/bun"
|
|
1415
|
+
];
|
|
1416
|
+
for (const candidate of fallbackCandidates) {
|
|
1417
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
1418
|
+
if (normalized) {
|
|
1419
|
+
return normalized;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
1423
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
1424
|
+
return execPath;
|
|
1425
|
+
}
|
|
1426
|
+
throw new Error("bun not found in PATH");
|
|
1427
|
+
}
|
|
1428
|
+
function resolveClaudeBinaryPath() {
|
|
1429
|
+
const explicit = normalizeExecutablePath(process.env.RIG_CLAUDE_PATH?.trim());
|
|
1430
|
+
if (explicit) {
|
|
1431
|
+
return explicit;
|
|
1432
|
+
}
|
|
1433
|
+
const pathClaude = normalizeExecutablePath(Bun.which("claude")?.trim());
|
|
1434
|
+
if (pathClaude && !looksLikeRuntimeGateway(pathClaude)) {
|
|
1435
|
+
return pathClaude;
|
|
1436
|
+
}
|
|
1437
|
+
const home = process.env.HOME?.trim();
|
|
1438
|
+
const fallbackCandidates = [
|
|
1439
|
+
home ? resolve5(home, ".local/bin/claude") : "",
|
|
1440
|
+
home ? resolve5(home, ".local/share/claude/local/claude") : "",
|
|
1441
|
+
"/opt/homebrew/bin/claude",
|
|
1442
|
+
"/usr/local/bin/claude",
|
|
1443
|
+
"/usr/bin/claude"
|
|
1444
|
+
];
|
|
1445
|
+
for (const candidate of fallbackCandidates) {
|
|
1446
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
1447
|
+
if (normalized) {
|
|
1448
|
+
return normalized;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
throw new Error("claude not found in PATH");
|
|
1452
|
+
}
|
|
1453
|
+
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
1454
|
+
return resolve5(bunBinaryPath, "../..");
|
|
1455
|
+
}
|
|
1456
|
+
function resolveClaudeInstallDir() {
|
|
1457
|
+
const realPath = resolveClaudeBinaryPath();
|
|
1458
|
+
return resolve5(realPath, "..");
|
|
1459
|
+
}
|
|
1460
|
+
function resolveNodeInstallDir() {
|
|
1461
|
+
const preferredNode = resolvePreferredNodeBinary();
|
|
1462
|
+
if (!preferredNode)
|
|
1463
|
+
return null;
|
|
1464
|
+
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
1465
|
+
if (explicitNode && resolve5(explicitNode) === resolve5(preferredNode)) {
|
|
1466
|
+
return preferredNode.endsWith("/bin/node") ? resolve5(preferredNode, "../..") : resolve5(preferredNode, "..");
|
|
1467
|
+
}
|
|
1468
|
+
try {
|
|
1469
|
+
const realPath = realpathSync2(preferredNode);
|
|
1470
|
+
if (realPath.endsWith("/bin/node")) {
|
|
1471
|
+
return resolve5(realPath, "../..");
|
|
1472
|
+
}
|
|
1473
|
+
return resolve5(realPath, "..");
|
|
1474
|
+
} catch {
|
|
1475
|
+
return resolve5(preferredNode, "..");
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function resolveRuntimeDependencyRoots(runtimeDirs) {
|
|
1479
|
+
const roots = [];
|
|
1480
|
+
if (process.platform === "darwin") {
|
|
1481
|
+
for (const macPath of ["/opt/homebrew", "/opt/homebrew/opt"]) {
|
|
1482
|
+
if (existsSync5(macPath)) {
|
|
1483
|
+
roots.push(macPath);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
for (const dir of runtimeDirs) {
|
|
1488
|
+
if (dir.startsWith("/opt/homebrew/Cellar/")) {
|
|
1489
|
+
roots.push("/opt/homebrew/opt");
|
|
1490
|
+
} else if (dir.startsWith("/usr/local/Cellar/")) {
|
|
1491
|
+
roots.push("/usr/local/opt");
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return uniq(roots);
|
|
1495
|
+
}
|
|
1496
|
+
function resolvePreferredNodeBinary() {
|
|
1497
|
+
const candidates = [];
|
|
1498
|
+
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
1499
|
+
if (envNode) {
|
|
1500
|
+
const explicit = resolve5(envNode);
|
|
1501
|
+
if (existsSync5(explicit)) {
|
|
1502
|
+
return explicit;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
const nvmBin = process.env.NVM_BIN?.trim();
|
|
1506
|
+
if (nvmBin) {
|
|
1507
|
+
candidates.push(resolve5(nvmBin, "node"));
|
|
1508
|
+
}
|
|
1509
|
+
const home = process.env.HOME?.trim();
|
|
1510
|
+
if (home) {
|
|
1511
|
+
const nvmVersionsDir = resolve5(home, ".nvm/versions/node");
|
|
1512
|
+
if (existsSync5(nvmVersionsDir)) {
|
|
1513
|
+
try {
|
|
1514
|
+
const versionDirs = readdirSync2(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/, "")));
|
|
1515
|
+
for (const versionDir of versionDirs) {
|
|
1516
|
+
candidates.push(resolve5(nvmVersionsDir, versionDir, "bin/node"));
|
|
1517
|
+
}
|
|
1518
|
+
} catch {}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
const whichNode = Bun.which("node");
|
|
1522
|
+
if (whichNode) {
|
|
1523
|
+
candidates.push(whichNode);
|
|
1524
|
+
}
|
|
1525
|
+
const deduped = uniq(candidates.map((candidate) => resolve5(candidate)));
|
|
1526
|
+
const existing = deduped.filter((candidate) => existsSync5(candidate));
|
|
1527
|
+
if (existing.length === 0) {
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
const stable = existing.find((candidate) => {
|
|
1531
|
+
const major = inferNodeMajor(candidate);
|
|
1532
|
+
return typeof major === "number" && major >= 18 && major <= 24;
|
|
1533
|
+
});
|
|
1534
|
+
if (stable) {
|
|
1535
|
+
return stable;
|
|
1536
|
+
}
|
|
1537
|
+
return existing[0] ?? null;
|
|
1538
|
+
}
|
|
1539
|
+
function inferNodeMajor(nodeBinaryPath) {
|
|
1540
|
+
const normalized = resolve5(nodeBinaryPath).replace(/\\/g, "/");
|
|
1541
|
+
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
1542
|
+
if (!match) {
|
|
1543
|
+
return null;
|
|
1544
|
+
}
|
|
1545
|
+
const major = Number.parseInt(match[1], 10);
|
|
1546
|
+
return Number.isFinite(major) ? major : null;
|
|
1547
|
+
}
|
|
1548
|
+
function normalizeExecutablePath(candidate) {
|
|
1549
|
+
if (!candidate) {
|
|
1550
|
+
return "";
|
|
1551
|
+
}
|
|
1552
|
+
const normalized = resolve5(candidate);
|
|
1553
|
+
if (!existsSync5(normalized)) {
|
|
1554
|
+
return "";
|
|
1555
|
+
}
|
|
1556
|
+
try {
|
|
1557
|
+
return realpathSync2(normalized);
|
|
1558
|
+
} catch {
|
|
1559
|
+
return normalized;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
1563
|
+
const normalized = resolve5(candidate).replace(/\\/g, "/");
|
|
1564
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend.ts
|
|
1568
|
+
init_utils();
|
|
1569
|
+
|
|
1570
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend-none.ts
|
|
1571
|
+
class NoSandboxBackend {
|
|
1572
|
+
kind = "none";
|
|
1573
|
+
reason;
|
|
1574
|
+
constructor(reason) {
|
|
1575
|
+
this.reason = reason;
|
|
1576
|
+
}
|
|
1577
|
+
wrap(options) {
|
|
1578
|
+
return {
|
|
1579
|
+
command: options.command,
|
|
1580
|
+
enabled: false,
|
|
1581
|
+
backend: "none",
|
|
1582
|
+
reason: this.reason,
|
|
1583
|
+
metadata: undefined
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend.ts
|
|
1589
|
+
class SandboxError extends Error {
|
|
1590
|
+
code;
|
|
1591
|
+
constructor(code, message) {
|
|
1592
|
+
super(message);
|
|
1593
|
+
this.code = code;
|
|
1594
|
+
this.name = "SandboxError";
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
function resolveRuntimeSandboxModeWithPolicy(sandboxConfig, envOverride) {
|
|
1598
|
+
if (envOverride) {
|
|
1599
|
+
const normalized = envOverride.trim().toLowerCase();
|
|
1600
|
+
if (normalized === "off" || normalized === "auto" || normalized === "enforce") {
|
|
1601
|
+
if (normalized !== sandboxConfig.mode) {
|
|
1602
|
+
console.warn(`[sandbox] RIG_RUNTIME_SANDBOX=${normalized} overrides policy sandbox.mode=${sandboxConfig.mode}`);
|
|
1603
|
+
}
|
|
1604
|
+
return normalized;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
const policyMode = sandboxConfig.mode;
|
|
1608
|
+
if (policyMode === "off")
|
|
1609
|
+
return "off";
|
|
1610
|
+
if (policyMode === "observe")
|
|
1611
|
+
return "auto";
|
|
1612
|
+
return "enforce";
|
|
1613
|
+
}
|
|
1614
|
+
var bwrapProbeCache = null;
|
|
1615
|
+
async function probeBubblewrap(binary) {
|
|
1616
|
+
if (bwrapProbeCache !== null) {
|
|
1617
|
+
return bwrapProbeCache;
|
|
1618
|
+
}
|
|
1619
|
+
const probe = await Bun.$`${binary} ${["--ro-bind", "/", "/", "--proc", "/proc", "--dev", "/dev", "--", "true"]}`.quiet().nothrow();
|
|
1620
|
+
bwrapProbeCache = probe.exitCode === 0;
|
|
1621
|
+
return bwrapProbeCache;
|
|
1622
|
+
}
|
|
1623
|
+
async function resolveBackend(projectRoot, options) {
|
|
1624
|
+
const config = loadSandboxConfig(projectRoot);
|
|
1625
|
+
const mode = resolveRuntimeSandboxModeWithPolicy(config, options?.envOverride);
|
|
1626
|
+
const probed = [];
|
|
1627
|
+
if (mode === "off") {
|
|
1628
|
+
return {
|
|
1629
|
+
backend: new NoSandboxBackend,
|
|
1630
|
+
selectedKind: "none",
|
|
1631
|
+
probed,
|
|
1632
|
+
reason: "disabled-by-config"
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
const explicitBackend = options?.backendOverride ?? process.env.RIG_SANDBOX_BACKEND?.trim().toLowerCase();
|
|
1636
|
+
const requestedBackend = resolveExplicitBackend(explicitBackend);
|
|
1637
|
+
if (requestedBackend) {
|
|
1638
|
+
if (requestedBackend === "docker") {
|
|
1639
|
+
throw new SandboxError("backend-unavailable", `Backend "docker" is not yet implemented.`);
|
|
1640
|
+
}
|
|
1641
|
+
if (requestedBackend === "none") {
|
|
1642
|
+
return {
|
|
1643
|
+
backend: new NoSandboxBackend("explicit-none"),
|
|
1644
|
+
selectedKind: "none",
|
|
1645
|
+
probed,
|
|
1646
|
+
reason: "explicit-none"
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
const bunDir = resolveBunInstallDir();
|
|
1651
|
+
const claudeDir = (() => {
|
|
1652
|
+
try {
|
|
1653
|
+
return resolveClaudeInstallDir();
|
|
1654
|
+
} catch {
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
})();
|
|
1658
|
+
const nodeDir = resolveNodeInstallDir();
|
|
1659
|
+
const depRoots = resolveRuntimeDependencyRoots([bunDir, claudeDir, nodeDir].filter(Boolean));
|
|
1660
|
+
const resolvedPaths = {
|
|
1661
|
+
bunDir,
|
|
1662
|
+
claudeDir,
|
|
1663
|
+
nodeDir,
|
|
1664
|
+
depRoots
|
|
1665
|
+
};
|
|
1666
|
+
const fsContext = {
|
|
1667
|
+
pathExists: (p) => existsSync6(p),
|
|
1668
|
+
realPath: toRealPath
|
|
1669
|
+
};
|
|
1670
|
+
if (process.platform === "darwin" && (!requestedBackend || requestedBackend === "macos-seatbelt")) {
|
|
1671
|
+
const seatbelt = Bun.which("sandbox-exec");
|
|
1672
|
+
probed.push("sandbox-exec");
|
|
1673
|
+
if (seatbelt && existsSync6(seatbelt)) {
|
|
1674
|
+
const SeatbeltBackendClass = loadSeatbeltBackend();
|
|
1675
|
+
if (SeatbeltBackendClass) {
|
|
1676
|
+
return {
|
|
1677
|
+
backend: new SeatbeltBackendClass(seatbelt, config, fsContext, resolvedPaths),
|
|
1678
|
+
selectedKind: "macos-seatbelt",
|
|
1679
|
+
probed,
|
|
1680
|
+
reason: "detected-seatbelt"
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
return handleUnavailableBackend(mode, probed, "macos-seatbelt", "Seatbelt sandbox backend module failed to load.", "seatbelt-backend-module-unavailable", { explicit: requestedBackend === "macos-seatbelt" });
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
if (process.platform === "linux" && (!requestedBackend || requestedBackend === "linux-bwrap")) {
|
|
1687
|
+
const bwrap = Bun.which("bwrap");
|
|
1688
|
+
probed.push("bwrap");
|
|
1689
|
+
if (bwrap && await probeBubblewrap(bwrap)) {
|
|
1690
|
+
const BwrapBackendClass = loadBwrapBackend();
|
|
1691
|
+
if (BwrapBackendClass) {
|
|
1692
|
+
return {
|
|
1693
|
+
backend: new BwrapBackendClass(bwrap, config, fsContext, resolvedPaths),
|
|
1694
|
+
selectedKind: "linux-bwrap",
|
|
1695
|
+
probed,
|
|
1696
|
+
reason: "detected-bwrap"
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
return handleUnavailableBackend(mode, probed, "linux-bwrap", "Bubblewrap sandbox backend module failed to load.", "bwrap-backend-module-unavailable", { explicit: requestedBackend === "linux-bwrap" });
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
if (requestedBackend) {
|
|
1703
|
+
return handleUnavailableBackend(mode, probed, requestedBackend, `Explicit sandbox backend "${requestedBackend}" is unavailable on ${process.platform}.`, "explicit-backend-unavailable", { explicit: true });
|
|
1704
|
+
}
|
|
1705
|
+
if (mode === "enforce") {
|
|
1706
|
+
throw new SandboxError("backend-unavailable", `Runtime sandbox required (mode=enforce) but no backend available. Probed: ${probed.join(", ")}`);
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
backend: new NoSandboxBackend("sandbox-backend-unavailable"),
|
|
1710
|
+
selectedKind: "none",
|
|
1711
|
+
probed,
|
|
1712
|
+
reason: "sandbox-backend-unavailable"
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function resolveExplicitBackend(explicitBackend) {
|
|
1716
|
+
if (!explicitBackend) {
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
switch (explicitBackend) {
|
|
1720
|
+
case "none":
|
|
1721
|
+
case "macos-seatbelt":
|
|
1722
|
+
case "linux-bwrap":
|
|
1723
|
+
case "docker":
|
|
1724
|
+
return explicitBackend;
|
|
1725
|
+
default:
|
|
1726
|
+
throw new SandboxError("backend-unavailable", `Unknown sandbox backend "${explicitBackend}".`);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
function loadSeatbeltBackend() {
|
|
1730
|
+
try {
|
|
1731
|
+
const mod = (init_backend_seatbelt(), __toCommonJS(exports_backend_seatbelt));
|
|
1732
|
+
return mod.SeatbeltBackend ?? null;
|
|
1733
|
+
} catch {
|
|
1734
|
+
return null;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
function loadBwrapBackend() {
|
|
1738
|
+
try {
|
|
1739
|
+
const mod = (init_backend_bwrap(), __toCommonJS(exports_backend_bwrap));
|
|
1740
|
+
return mod.BwrapBackend ?? null;
|
|
1741
|
+
} catch {
|
|
1742
|
+
return null;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
function handleUnavailableBackend(mode, probed, detectedKind, message, reason, options) {
|
|
1746
|
+
if (mode === "enforce" || options?.explicit) {
|
|
1747
|
+
throw new SandboxError("backend-unavailable", `${message} Probed: ${probed.join(", ")}`);
|
|
1748
|
+
}
|
|
1749
|
+
return {
|
|
1750
|
+
backend: new NoSandboxBackend(reason),
|
|
1751
|
+
selectedKind: "none",
|
|
1752
|
+
probed,
|
|
1753
|
+
reason: `${reason}:${detectedKind}`
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// packages/runtime/src/control-plane/runtime/sandbox/orchestrator.ts
|
|
1758
|
+
function shouldSandboxRuntime(_runtime) {
|
|
1759
|
+
return true;
|
|
1760
|
+
}
|
|
1761
|
+
async function wrapWithRuntimeSandbox(options) {
|
|
1762
|
+
const envOverride = process.env.RIG_RUNTIME_SANDBOX;
|
|
1763
|
+
const resolution = await resolveBackend(options.projectRoot, { envOverride });
|
|
1764
|
+
if (resolution.backend.kind === "none") {
|
|
1765
|
+
const plan = resolution.backend.wrap({
|
|
1766
|
+
projectRoot: options.projectRoot,
|
|
1767
|
+
runtime: options.runtime,
|
|
1768
|
+
command: options.command
|
|
1769
|
+
});
|
|
1770
|
+
return {
|
|
1771
|
+
...plan,
|
|
1772
|
+
reason: plan.reason ?? resolution.reason
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
if (!shouldSandboxRuntime(options.runtime)) {
|
|
1776
|
+
return {
|
|
1777
|
+
command: options.command,
|
|
1778
|
+
enabled: false,
|
|
1779
|
+
backend: "none",
|
|
1780
|
+
reason: "runtime-mode-not-sandboxed"
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
return resolution.backend.wrap({
|
|
1784
|
+
projectRoot: options.projectRoot,
|
|
1785
|
+
runtime: options.runtime,
|
|
1786
|
+
command: options.command
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
// packages/runtime/src/control-plane/runtime/isolation/home.ts
|
|
1790
|
+
import {
|
|
1791
|
+
chmodSync,
|
|
1792
|
+
copyFileSync as copyFileSync2,
|
|
1793
|
+
cpSync,
|
|
1794
|
+
existsSync as existsSync9,
|
|
1795
|
+
mkdirSync as mkdirSync5,
|
|
1796
|
+
statSync as statSync3,
|
|
1797
|
+
writeFileSync as writeFileSync3
|
|
1798
|
+
} from "fs";
|
|
1799
|
+
import { mkdir } from "fs/promises";
|
|
1800
|
+
import { basename as basename2, delimiter, resolve as resolve11 } from "path";
|
|
1801
|
+
|
|
1802
|
+
// packages/runtime/src/control-plane/runtime/baked-secrets.ts
|
|
1803
|
+
var BAKED_RUNTIME_SECRETS = {
|
|
1804
|
+
ANTHROPIC_API_KEY: typeof RIG_BAKED_ANTHROPIC_API_KEY !== "undefined" ? RIG_BAKED_ANTHROPIC_API_KEY : "",
|
|
1805
|
+
OPENAI_API_KEY: typeof RIG_BAKED_OPENAI_API_KEY !== "undefined" ? RIG_BAKED_OPENAI_API_KEY : "",
|
|
1806
|
+
OPENROUTER_API_KEY: typeof RIG_BAKED_OPENROUTER_API_KEY !== "undefined" ? RIG_BAKED_OPENROUTER_API_KEY : "",
|
|
1807
|
+
AI_REVIEW_MODE: typeof RIG_BAKED_AI_REVIEW_MODE !== "undefined" ? RIG_BAKED_AI_REVIEW_MODE : "",
|
|
1808
|
+
AI_REVIEW_PROVIDER: typeof RIG_BAKED_AI_REVIEW_PROVIDER !== "undefined" ? RIG_BAKED_AI_REVIEW_PROVIDER : "",
|
|
1809
|
+
GREPTILE_API_BASE: typeof RIG_BAKED_GREPTILE_API_BASE !== "undefined" ? RIG_BAKED_GREPTILE_API_BASE : "",
|
|
1810
|
+
GREPTILE_REMOTE: typeof RIG_BAKED_GREPTILE_REMOTE !== "undefined" ? RIG_BAKED_GREPTILE_REMOTE : "",
|
|
1811
|
+
GREPTILE_REPOSITORY: typeof RIG_BAKED_GREPTILE_REPOSITORY !== "undefined" ? RIG_BAKED_GREPTILE_REPOSITORY : "",
|
|
1812
|
+
GREPTILE_CONTEXT_BRANCH: typeof RIG_BAKED_GREPTILE_CONTEXT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_CONTEXT_BRANCH : "",
|
|
1813
|
+
GREPTILE_DEFAULT_BRANCH: typeof RIG_BAKED_GREPTILE_DEFAULT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_DEFAULT_BRANCH : "",
|
|
1814
|
+
GREPTILE_API_KEY: typeof RIG_BAKED_GREPTILE_API_KEY !== "undefined" ? RIG_BAKED_GREPTILE_API_KEY : "",
|
|
1815
|
+
GREPTILE_GITHUB_TOKEN: typeof RIG_BAKED_GREPTILE_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GREPTILE_GITHUB_TOKEN : "",
|
|
1816
|
+
GREPTILE_POLL_ATTEMPTS: typeof RIG_BAKED_GREPTILE_POLL_ATTEMPTS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_ATTEMPTS : "",
|
|
1817
|
+
GREPTILE_POLL_INTERVAL_MS: typeof RIG_BAKED_GREPTILE_POLL_INTERVAL_MS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_INTERVAL_MS : "",
|
|
1818
|
+
GH_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
|
|
1819
|
+
GITHUB_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
|
|
1820
|
+
GITHUB_SSH_KEY: typeof RIG_BAKED_GITHUB_SSH_KEY !== "undefined" ? RIG_BAKED_GITHUB_SSH_KEY : "",
|
|
1821
|
+
AWS_ACCESS_KEY_ID: typeof RIG_BAKED_AWS_ACCESS_KEY_ID !== "undefined" ? RIG_BAKED_AWS_ACCESS_KEY_ID : "",
|
|
1822
|
+
AWS_SECRET_ACCESS_KEY: typeof RIG_BAKED_AWS_SECRET_ACCESS_KEY !== "undefined" ? RIG_BAKED_AWS_SECRET_ACCESS_KEY : "",
|
|
1823
|
+
AWS_REGION: typeof RIG_BAKED_AWS_REGION !== "undefined" ? RIG_BAKED_AWS_REGION : "",
|
|
1824
|
+
LINEAR_API_KEY: typeof RIG_BAKED_LINEAR_API_KEY !== "undefined" ? RIG_BAKED_LINEAR_API_KEY : "",
|
|
1825
|
+
LINEAR_WEBHOOK_SECRET: typeof RIG_BAKED_LINEAR_WEBHOOK_SECRET !== "undefined" ? RIG_BAKED_LINEAR_WEBHOOK_SECRET : ""
|
|
1826
|
+
};
|
|
1827
|
+
function resolveRuntimeSecrets(env, baked = BAKED_RUNTIME_SECRETS) {
|
|
1828
|
+
const resolved = {};
|
|
1829
|
+
const keys = new Set([
|
|
1830
|
+
...Object.keys(BAKED_RUNTIME_SECRETS),
|
|
1831
|
+
...Object.keys(baked)
|
|
1832
|
+
]);
|
|
1833
|
+
for (const key of keys) {
|
|
1834
|
+
const envValue = env[key]?.trim();
|
|
1835
|
+
const bakedValue = baked[key]?.trim();
|
|
1836
|
+
if (envValue) {
|
|
1837
|
+
resolved[key] = envValue;
|
|
1838
|
+
} else if (bakedValue) {
|
|
1839
|
+
resolved[key] = bakedValue;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
return resolved;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// packages/runtime/src/control-plane/native/git-native.ts
|
|
1846
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1847
|
+
import { dirname as dirname3, isAbsolute, resolve as resolve8 } from "path";
|
|
1848
|
+
var sharedGitNativeOutputDir = resolve8(tmpdir2(), "rig-native");
|
|
1849
|
+
var sharedGitNativeOutputPath = resolve8(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
1850
|
+
function runtimeRigGitFileName() {
|
|
1851
|
+
return `rig-git${process.platform === "win32" ? ".exe" : ""}`;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// packages/runtime/src/control-plane/browser-contract.ts
|
|
1855
|
+
function browserEnvFromContext(browser) {
|
|
1856
|
+
if (!browser?.required) {
|
|
1857
|
+
return {};
|
|
1858
|
+
}
|
|
1859
|
+
const env = {
|
|
1860
|
+
RIG_BROWSER_REQUIRED: "1",
|
|
1861
|
+
RIG_BROWSER_PRESET: browser.preset,
|
|
1862
|
+
RIG_BROWSER_MODE: browser.mode,
|
|
1863
|
+
RIG_BROWSER_STATE_DIR: browser.stateDir,
|
|
1864
|
+
RIG_BROWSER_PROFILE: browser.effectiveProfile,
|
|
1865
|
+
RIG_BROWSER_BASE_PROFILE: browser.defaultProfile,
|
|
1866
|
+
RIG_BROWSER_ATTACH_URL: browser.effectiveAttachUrl,
|
|
1867
|
+
RIG_BROWSER_DEFAULT_ATTACH_URL: browser.defaultAttachUrl,
|
|
1868
|
+
RIG_BROWSER_LAUNCH_HELPER: browser.launchHelper,
|
|
1869
|
+
RIG_BROWSER_CHECK_HELPER: browser.checkHelper,
|
|
1870
|
+
RIG_BROWSER_ATTACH_INFO_HELPER: browser.attachInfoHelper,
|
|
1871
|
+
RIG_BROWSER_E2E_HELPER: browser.e2eHelper,
|
|
1872
|
+
RIG_BROWSER_RESET_HELPER: browser.resetProfileHelper
|
|
1873
|
+
};
|
|
1874
|
+
if (browser.devCommand) {
|
|
1875
|
+
env.RIG_BROWSER_DEV_COMMAND = browser.devCommand;
|
|
1876
|
+
}
|
|
1877
|
+
if (browser.launchCommand) {
|
|
1878
|
+
env.RIG_BROWSER_LAUNCH_COMMAND = browser.launchCommand;
|
|
1879
|
+
}
|
|
1880
|
+
if (browser.checkCommand) {
|
|
1881
|
+
env.RIG_BROWSER_CHECK_COMMAND = browser.checkCommand;
|
|
1882
|
+
}
|
|
1883
|
+
if (browser.e2eCommand) {
|
|
1884
|
+
env.RIG_BROWSER_E2E_COMMAND = browser.e2eCommand;
|
|
1885
|
+
}
|
|
1886
|
+
return env;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// packages/runtime/src/control-plane/runtime/context.ts
|
|
1890
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1891
|
+
import { dirname as dirname4, resolve as resolve9 } from "path";
|
|
1892
|
+
var RUNTIME_CONTEXT_ENV = "RIG_RUNTIME_CONTEXT_FILE";
|
|
1893
|
+
var runtimeContextStringFields = [
|
|
1894
|
+
"runtimeId",
|
|
1895
|
+
"taskId",
|
|
1896
|
+
"role",
|
|
1897
|
+
"workspaceDir",
|
|
1898
|
+
"stateDir",
|
|
1899
|
+
"logsDir",
|
|
1900
|
+
"sessionDir",
|
|
1901
|
+
"sessionFile",
|
|
1902
|
+
"policyFile",
|
|
1903
|
+
"binDir",
|
|
1904
|
+
"createdAt"
|
|
1905
|
+
];
|
|
1906
|
+
var runtimeContextArrayFields = ["scopes", "validation"];
|
|
1907
|
+
var runtimeContextOptionalStringFields = [
|
|
1908
|
+
"artifactRoot",
|
|
1909
|
+
"hostProjectRoot",
|
|
1910
|
+
"monorepoMainRoot",
|
|
1911
|
+
"monorepoBaseRef",
|
|
1912
|
+
"monorepoBaseCommit"
|
|
1913
|
+
];
|
|
1914
|
+
function loadRuntimeContext(path) {
|
|
1915
|
+
const absPath = resolve9(path);
|
|
1916
|
+
if (!existsSync7(absPath)) {
|
|
1917
|
+
throw new Error(`RuntimeTaskContext file not found: ${absPath}`);
|
|
1918
|
+
}
|
|
1919
|
+
let raw;
|
|
1920
|
+
try {
|
|
1921
|
+
raw = JSON.parse(readFileSync2(absPath, "utf8"));
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
throw new Error(`Failed to parse RuntimeTaskContext at ${absPath}: ${String(err)}`);
|
|
1924
|
+
}
|
|
1925
|
+
if (typeof raw !== "object" || raw === null) {
|
|
1926
|
+
throw new Error(`RuntimeTaskContext at ${absPath} is not an object`);
|
|
1927
|
+
}
|
|
1928
|
+
const obj = raw;
|
|
1929
|
+
for (const field of runtimeContextStringFields) {
|
|
1930
|
+
if (typeof obj[field] !== "string" || obj[field].length === 0) {
|
|
1931
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a non-empty string (at ${absPath})`);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
for (const field of runtimeContextArrayFields) {
|
|
1935
|
+
if (!Array.isArray(obj[field])) {
|
|
1936
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be an array (at ${absPath})`);
|
|
1937
|
+
}
|
|
1938
|
+
if (!obj[field].every((entry) => typeof entry === "string")) {
|
|
1939
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a string[] (at ${absPath})`);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
for (const field of runtimeContextOptionalStringFields) {
|
|
1943
|
+
if (field in obj && obj[field] !== undefined && typeof obj[field] !== "string") {
|
|
1944
|
+
throw new Error(`RuntimeTaskContext field "${field}" must be a string when present (at ${absPath})`);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
if (obj.browser !== undefined) {
|
|
1948
|
+
if (typeof obj.browser !== "object" || obj.browser === null || Array.isArray(obj.browser)) {
|
|
1949
|
+
throw new Error(`RuntimeTaskContext field "browser" must be an object when present (at ${absPath})`);
|
|
1950
|
+
}
|
|
1951
|
+
const browser = obj.browser;
|
|
1952
|
+
for (const field of [
|
|
1953
|
+
"preset",
|
|
1954
|
+
"mode",
|
|
1955
|
+
"stateDir",
|
|
1956
|
+
"defaultProfile",
|
|
1957
|
+
"effectiveProfile",
|
|
1958
|
+
"defaultAttachUrl",
|
|
1959
|
+
"effectiveAttachUrl",
|
|
1960
|
+
"launchHelper",
|
|
1961
|
+
"checkHelper",
|
|
1962
|
+
"attachInfoHelper",
|
|
1963
|
+
"e2eHelper",
|
|
1964
|
+
"resetProfileHelper"
|
|
1965
|
+
]) {
|
|
1966
|
+
if (typeof browser[field] !== "string" || browser[field].length === 0) {
|
|
1967
|
+
throw new Error(`RuntimeTaskContext field "browser.${field}" must be a non-empty string (at ${absPath})`);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
for (const field of ["devCommand", "launchCommand", "checkCommand", "e2eCommand"]) {
|
|
1971
|
+
if (browser[field] !== undefined && typeof browser[field] !== "string") {
|
|
1972
|
+
throw new Error(`RuntimeTaskContext field "browser.${field}" must be a string when present (at ${absPath})`);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
if (typeof browser.required !== "boolean") {
|
|
1976
|
+
throw new Error(`RuntimeTaskContext field "browser.required" must be a boolean (at ${absPath})`);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
if (obj.memory !== undefined) {
|
|
1980
|
+
if (typeof obj.memory !== "object" || obj.memory === null || Array.isArray(obj.memory)) {
|
|
1981
|
+
throw new Error(`RuntimeTaskContext field "memory" must be an object when present (at ${absPath})`);
|
|
1982
|
+
}
|
|
1983
|
+
const memory = obj.memory;
|
|
1984
|
+
for (const field of ["canonicalPath", "canonicalRef", "canonicalBaseOid", "hydratedPath"]) {
|
|
1985
|
+
if (typeof memory[field] !== "string" || memory[field].length === 0) {
|
|
1986
|
+
throw new Error(`RuntimeTaskContext field "memory.${field}" must be a non-empty string (at ${absPath})`);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
if (typeof memory.createdFresh !== "boolean") {
|
|
1990
|
+
throw new Error(`RuntimeTaskContext field "memory.createdFresh" must be a boolean (at ${absPath})`);
|
|
1991
|
+
}
|
|
1992
|
+
if (typeof memory.retrieval !== "object" || memory.retrieval === null || Array.isArray(memory.retrieval)) {
|
|
1993
|
+
throw new Error(`RuntimeTaskContext field "memory.retrieval" must be an object (at ${absPath})`);
|
|
1994
|
+
}
|
|
1995
|
+
const retrieval = memory.retrieval;
|
|
1996
|
+
for (const field of ["topK", "lexicalWeight", "vectorWeight", "recencyWeight", "confidenceWeight"]) {
|
|
1997
|
+
if (typeof retrieval[field] !== "number" || Number.isNaN(retrieval[field])) {
|
|
1998
|
+
throw new Error(`RuntimeTaskContext field "memory.retrieval.${field}" must be a number (at ${absPath})`);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
if (obj.initialDirtyFiles !== undefined) {
|
|
2003
|
+
if (typeof obj.initialDirtyFiles !== "object" || obj.initialDirtyFiles === null || Array.isArray(obj.initialDirtyFiles)) {
|
|
2004
|
+
throw new Error(`RuntimeTaskContext field "initialDirtyFiles" must be an object when present (at ${absPath})`);
|
|
2005
|
+
}
|
|
2006
|
+
const dirtyFiles = obj.initialDirtyFiles;
|
|
2007
|
+
for (const key of ["project", "monorepo"]) {
|
|
2008
|
+
if (dirtyFiles[key] === undefined) {
|
|
2009
|
+
continue;
|
|
2010
|
+
}
|
|
2011
|
+
if (!Array.isArray(dirtyFiles[key]) || !dirtyFiles[key].every((entry) => typeof entry === "string")) {
|
|
2012
|
+
throw new Error(`RuntimeTaskContext field "initialDirtyFiles.${key}" must be a string[] when present (at ${absPath})`);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (obj.initialHeadCommits !== undefined) {
|
|
2017
|
+
if (typeof obj.initialHeadCommits !== "object" || obj.initialHeadCommits === null || Array.isArray(obj.initialHeadCommits)) {
|
|
2018
|
+
throw new Error(`RuntimeTaskContext field "initialHeadCommits" must be an object when present (at ${absPath})`);
|
|
2019
|
+
}
|
|
2020
|
+
const headCommits = obj.initialHeadCommits;
|
|
2021
|
+
for (const key of ["project", "monorepo"]) {
|
|
2022
|
+
if (headCommits[key] === undefined) {
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
if (typeof headCommits[key] !== "string") {
|
|
2026
|
+
throw new Error(`RuntimeTaskContext field "initialHeadCommits.${key}" must be a string when present (at ${absPath})`);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
return obj;
|
|
2031
|
+
}
|
|
2032
|
+
function runtimeMemoryEnvFromContext(ctx) {
|
|
2033
|
+
if (!ctx?.memory) {
|
|
2034
|
+
return {};
|
|
2035
|
+
}
|
|
2036
|
+
return {
|
|
2037
|
+
RIG_MEMORY_DB_PATH: ctx.memory.hydratedPath,
|
|
2038
|
+
RIG_MEMORY_CANONICAL_PATH: ctx.memory.canonicalPath,
|
|
2039
|
+
RIG_MEMORY_CANONICAL_REF: ctx.memory.canonicalRef,
|
|
2040
|
+
RIG_MEMORY_CANONICAL_BASE_OID: ctx.memory.canonicalBaseOid,
|
|
2041
|
+
RIG_MEMORY_CREATED_FRESH: ctx.memory.createdFresh ? "1" : "0",
|
|
2042
|
+
RIG_MEMORY_RETRIEVAL_TOP_K: String(ctx.memory.retrieval.topK),
|
|
2043
|
+
RIG_MEMORY_RETRIEVAL_LEXICAL_WEIGHT: String(ctx.memory.retrieval.lexicalWeight),
|
|
2044
|
+
RIG_MEMORY_RETRIEVAL_VECTOR_WEIGHT: String(ctx.memory.retrieval.vectorWeight),
|
|
2045
|
+
RIG_MEMORY_RETRIEVAL_RECENCY_WEIGHT: String(ctx.memory.retrieval.recencyWeight),
|
|
2046
|
+
RIG_MEMORY_RETRIEVAL_CONFIDENCE_WEIGHT: String(ctx.memory.retrieval.confidenceWeight)
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// packages/runtime/src/control-plane/runtime/isolation/shared.ts
|
|
2051
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3, rmSync as rmSync2 } from "fs";
|
|
2052
|
+
import { resolve as resolve10 } from "path";
|
|
2053
|
+
var generatedCredentialFiles = new Set;
|
|
2054
|
+
function resolveMonorepoRoot3(projectRoot) {
|
|
2055
|
+
return resolveMonorepoRoot2(projectRoot);
|
|
2056
|
+
}
|
|
2057
|
+
function isRuntimeGatewayGitPath(candidate) {
|
|
2058
|
+
return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
|
|
2059
|
+
}
|
|
2060
|
+
function resolveHostGitBinary() {
|
|
2061
|
+
const candidates = [
|
|
2062
|
+
process.env.RIG_GIT_BIN?.trim() || "",
|
|
2063
|
+
"/usr/bin/git",
|
|
2064
|
+
"/opt/homebrew/bin/git",
|
|
2065
|
+
"/usr/local/bin/git"
|
|
2066
|
+
];
|
|
2067
|
+
const bunResolved = Bun.which("git");
|
|
2068
|
+
if (bunResolved && !isRuntimeGatewayGitPath(bunResolved)) {
|
|
2069
|
+
candidates.push(bunResolved);
|
|
2070
|
+
}
|
|
2071
|
+
for (const candidate of candidates) {
|
|
2072
|
+
if (!candidate || isRuntimeGatewayGitPath(candidate)) {
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
2075
|
+
if (existsSync8(candidate)) {
|
|
2076
|
+
return candidate;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
return "git";
|
|
2080
|
+
}
|
|
2081
|
+
async function runGitCommand(repoRoot, args) {
|
|
2082
|
+
const gitBinary = resolveHostGitBinary();
|
|
2083
|
+
return Bun.$`${gitBinary} -C ${repoRoot} ${args}`.quiet().nothrow();
|
|
2084
|
+
}
|
|
2085
|
+
function sanitizeRuntimeRefSegment(value) {
|
|
2086
|
+
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2087
|
+
return (sanitized || "runtime").slice(0, 64);
|
|
2088
|
+
}
|
|
2089
|
+
function taskRuntimeId(taskId) {
|
|
2090
|
+
return `task-${taskId}`;
|
|
2091
|
+
}
|
|
2092
|
+
function resolveGithubCliBinaryPath() {
|
|
2093
|
+
const explicit = process.env.RIG_GH_BIN?.trim();
|
|
2094
|
+
if (explicit && existsSync8(explicit)) {
|
|
2095
|
+
return explicit;
|
|
2096
|
+
}
|
|
2097
|
+
const bunResolved = Bun.which("gh");
|
|
2098
|
+
if (bunResolved && existsSync8(bunResolved)) {
|
|
2099
|
+
return bunResolved;
|
|
2100
|
+
}
|
|
2101
|
+
for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
|
|
2102
|
+
if (existsSync8(candidate)) {
|
|
2103
|
+
return candidate;
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
return "";
|
|
2107
|
+
}
|
|
2108
|
+
async function resolveGithubCliAuthToken(ghBinary = "") {
|
|
2109
|
+
const gh = ghBinary || resolveGithubCliBinaryPath();
|
|
2110
|
+
if (!gh) {
|
|
2111
|
+
return "";
|
|
2112
|
+
}
|
|
2113
|
+
const auth = Bun.spawn([gh, "auth", "token"], {
|
|
2114
|
+
stdout: "pipe",
|
|
2115
|
+
stderr: "pipe"
|
|
2116
|
+
});
|
|
2117
|
+
const [exitCode, stdout] = await Promise.all([
|
|
2118
|
+
auth.exited,
|
|
2119
|
+
new Response(auth.stdout).text()
|
|
2120
|
+
]);
|
|
2121
|
+
if (exitCode !== 0) {
|
|
2122
|
+
return "";
|
|
2123
|
+
}
|
|
2124
|
+
return stdout.trim();
|
|
2125
|
+
}
|
|
2126
|
+
function resolveSystemCertBundlePath() {
|
|
2127
|
+
const candidates = [
|
|
2128
|
+
process.env.SSL_CERT_FILE?.trim(),
|
|
2129
|
+
"/etc/ssl/cert.pem",
|
|
2130
|
+
"/private/etc/ssl/cert.pem",
|
|
2131
|
+
"/opt/homebrew/etc/openssl@3/cert.pem"
|
|
2132
|
+
];
|
|
2133
|
+
for (const candidate of candidates) {
|
|
2134
|
+
if (candidate && existsSync8(candidate)) {
|
|
2135
|
+
return resolve10(candidate);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
return "";
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// packages/runtime/src/control-plane/runtime/isolation/home.ts
|
|
2142
|
+
var GITHUB_KNOWN_HOSTS = [
|
|
2143
|
+
"github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
|
|
2144
|
+
"github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
|
|
2145
|
+
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
2146
|
+
].join(`
|
|
2147
|
+
`);
|
|
2148
|
+
async function runtimeEnv(projectRoot, runtime) {
|
|
2149
|
+
const bunBinaryPath = resolveBunBinaryPath();
|
|
2150
|
+
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
2151
|
+
const claudeBinaryPath = process.env.RIG_CLAUDE_PATH?.trim() || (() => {
|
|
2152
|
+
try {
|
|
2153
|
+
return resolveClaudeBinaryPath();
|
|
2154
|
+
} catch {
|
|
2155
|
+
return "";
|
|
2156
|
+
}
|
|
2157
|
+
})();
|
|
2158
|
+
const claudeDir = claudeBinaryPath ? (() => {
|
|
2159
|
+
try {
|
|
2160
|
+
return resolveClaudeInstallDir();
|
|
2161
|
+
} catch {
|
|
2162
|
+
return resolve11(claudeBinaryPath, "..");
|
|
2163
|
+
}
|
|
2164
|
+
})() : "";
|
|
2165
|
+
const nodeDir = resolveNodeInstallDir();
|
|
2166
|
+
const hostGhBinary = resolveGithubCliBinaryPath();
|
|
2167
|
+
const runtimeCertBundlePath = await materializeRuntimeCertBundle(runtime);
|
|
2168
|
+
const monorepoMainRoot = resolveMonorepoRoot3(projectRoot);
|
|
2169
|
+
const realHome = process.env.HOME?.trim();
|
|
2170
|
+
const inheritedPath = (process.env.PATH ?? "").split(delimiter).map((entry) => entry.trim()).filter(Boolean).filter((entry) => !entry.endsWith("/.rig/bin") && !entry.endsWith("/rig/tools"));
|
|
2171
|
+
const pathEntries = [
|
|
2172
|
+
runtime.binDir,
|
|
2173
|
+
`${projectRoot}/rig/tools`,
|
|
2174
|
+
`${bunDir}/bin`,
|
|
2175
|
+
claudeDir,
|
|
2176
|
+
nodeDir ? `${nodeDir}/bin` : "",
|
|
2177
|
+
realHome ? resolve11(realHome, ".local/bin") : "",
|
|
2178
|
+
realHome ? resolve11(realHome, ".cargo/bin") : "",
|
|
2179
|
+
...inheritedPath,
|
|
2180
|
+
"/usr/local/bin",
|
|
2181
|
+
"/usr/local/sbin",
|
|
2182
|
+
"/opt/homebrew/bin",
|
|
2183
|
+
"/opt/homebrew/sbin",
|
|
2184
|
+
"/usr/bin",
|
|
2185
|
+
"/bin",
|
|
2186
|
+
"/usr/sbin",
|
|
2187
|
+
"/sbin"
|
|
2188
|
+
].filter(Boolean);
|
|
2189
|
+
const runtimeBash = resolve11(runtime.binDir, "bash");
|
|
2190
|
+
const runtimeRigGit = resolve11(runtime.binDir, runtimeRigGitFileName());
|
|
2191
|
+
const preferredShell = existsSync9(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
2192
|
+
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
2193
|
+
const env = {
|
|
2194
|
+
PROJECT_RIG_ROOT: projectRoot,
|
|
2195
|
+
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
2196
|
+
HOME: runtime.homeDir,
|
|
2197
|
+
TMPDIR: runtime.tmpDir,
|
|
2198
|
+
XDG_CACHE_HOME: runtime.cacheDir,
|
|
2199
|
+
XDG_STATE_HOME: runtime.stateDir,
|
|
2200
|
+
RIG_AGENT_ID: runtime.id,
|
|
2201
|
+
RIG_TASK_ID: runtime.taskId,
|
|
2202
|
+
RIG_TASK_RUNTIME_ID: runtime.id,
|
|
2203
|
+
RIG_TASK_WORKSPACE: runtime.workspaceDir,
|
|
2204
|
+
RIG_TASK_RUNTIME_MODE: runtime.mode,
|
|
2205
|
+
RIG_RUNTIME_MODE: runtime.mode,
|
|
2206
|
+
RIG_RUNTIME_HOME: runtime.rootDir,
|
|
2207
|
+
RIG_RUNTIME_BIN_DIR: runtime.binDir,
|
|
2208
|
+
...existsSync9(runtimeRigGit) ? { RIG_NATIVE_GIT_BIN: runtimeRigGit } : {},
|
|
2209
|
+
RIG_BUN_PATH: bunBinaryPath,
|
|
2210
|
+
...claudeBinaryPath ? { RIG_CLAUDE_PATH: claudeBinaryPath } : {},
|
|
2211
|
+
RIG_AGENT_BIN: resolve11(runtime.binDir, "rig-agent"),
|
|
2212
|
+
RIG_HOOKS_ACTIVE: "1",
|
|
2213
|
+
RIG_AUTO_PR_ON_COMPLETE: "1",
|
|
2214
|
+
RIG_POLICY_FILE: resolve11(projectRoot, "rig/policy/policy.json"),
|
|
2215
|
+
RIG_STATE_DIR: runtime.stateDir,
|
|
2216
|
+
RIG_LOGS_DIR: runtime.logsDir,
|
|
2217
|
+
RIG_SESSION_FILE: resolve11(runtime.sessionDir, "session.json"),
|
|
2218
|
+
MONOREPO_ROOT: runtime.workspaceDir,
|
|
2219
|
+
MONOREPO_MAIN_ROOT: monorepoMainRoot,
|
|
2220
|
+
TS_API_TESTS_DIR: resolve11(runtime.workspaceDir, "TSAPITests"),
|
|
2221
|
+
BASH: preferredShell,
|
|
2222
|
+
SHELL: preferredShell,
|
|
2223
|
+
PATH: [...new Set(pathEntries)].join(delimiter),
|
|
2224
|
+
LANG: process.env.LANG ?? "en_US.UTF-8",
|
|
2225
|
+
TERM: process.env.TERM ?? "xterm-256color",
|
|
2226
|
+
PYTHONDONTWRITEBYTECODE: "1",
|
|
2227
|
+
PYTHONPYCACHEPREFIX: resolve11(runtime.cacheDir, "python"),
|
|
2228
|
+
...process.env.RIG_PR_BASE_PROJECT && { RIG_PR_BASE_PROJECT: process.env.RIG_PR_BASE_PROJECT },
|
|
2229
|
+
...process.env.RIG_PR_BASE_MONOREPO && { RIG_PR_BASE_MONOREPO: process.env.RIG_PR_BASE_MONOREPO },
|
|
2230
|
+
CLAUDE_HOME: runtime.claudeHomeDir,
|
|
2231
|
+
PI_CODING_AGENT_DIR: resolve11(runtime.homeDir, ".pi", "agent"),
|
|
2232
|
+
[RUNTIME_CONTEXT_ENV]: runtime.contextFile,
|
|
2233
|
+
...nativeRuntimeLibraryPath ? { RIG_NATIVE_RUNTIME_LIB: nativeRuntimeLibraryPath } : {},
|
|
2234
|
+
...hostGhBinary ? { RIG_GH_BIN: hostGhBinary } : {},
|
|
2235
|
+
...runtimeCertBundlePath ? {
|
|
2236
|
+
SSL_CERT_FILE: runtimeCertBundlePath,
|
|
2237
|
+
CURL_CA_BUNDLE: runtimeCertBundlePath,
|
|
2238
|
+
REQUESTS_CA_BUNDLE: runtimeCertBundlePath,
|
|
2239
|
+
NODE_EXTRA_CA_CERTS: runtimeCertBundlePath
|
|
2240
|
+
} : {}
|
|
2241
|
+
};
|
|
2242
|
+
const knownHostsPath = resolve11(runtime.homeDir, ".ssh", "known_hosts");
|
|
2243
|
+
if (existsSync9(knownHostsPath)) {
|
|
2244
|
+
const agentSshKey = resolve11(runtime.homeDir, ".ssh", "rig-agent-key");
|
|
2245
|
+
const sshParts = [
|
|
2246
|
+
"ssh",
|
|
2247
|
+
`-o UserKnownHostsFile="${knownHostsPath}"`,
|
|
2248
|
+
"-o StrictHostKeyChecking=yes",
|
|
2249
|
+
"-F /dev/null"
|
|
2250
|
+
];
|
|
2251
|
+
if (existsSync9(agentSshKey)) {
|
|
2252
|
+
sshParts.splice(1, 0, `-i "${agentSshKey}"`, "-o IdentitiesOnly=yes");
|
|
2253
|
+
}
|
|
2254
|
+
env.GIT_SSH_COMMAND = sshParts.join(" ");
|
|
2255
|
+
}
|
|
2256
|
+
for (const [key, value] of Object.entries(resolveRuntimeSecrets(process.env))) {
|
|
2257
|
+
if (key === "GITHUB_SSH_KEY") {
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
if (value) {
|
|
2261
|
+
env[key] = value;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
const fallbackGithubToken = !env.GITHUB_TOKEN && !env.GH_TOKEN ? await resolveGithubCliAuthToken(hostGhBinary) : "";
|
|
2265
|
+
if (fallbackGithubToken) {
|
|
2266
|
+
env.GITHUB_TOKEN = fallbackGithubToken;
|
|
2267
|
+
}
|
|
2268
|
+
if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
|
|
2269
|
+
env.GITHUB_TOKEN = env.GH_TOKEN;
|
|
2270
|
+
}
|
|
2271
|
+
if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
|
|
2272
|
+
env.GH_TOKEN = env.GITHUB_TOKEN;
|
|
2273
|
+
}
|
|
2274
|
+
if (!env.GREPTILE_GITHUB_TOKEN && env.GITHUB_TOKEN) {
|
|
2275
|
+
env.GREPTILE_GITHUB_TOKEN = env.GITHUB_TOKEN;
|
|
2276
|
+
}
|
|
2277
|
+
if (existsSync9(runtime.contextFile)) {
|
|
2278
|
+
const runtimeContext = loadRuntimeContext(runtime.contextFile);
|
|
2279
|
+
Object.assign(env, runtimeMemoryEnvFromContext(runtimeContext));
|
|
2280
|
+
Object.assign(env, browserEnvFromContext(runtimeContext.browser));
|
|
2281
|
+
}
|
|
2282
|
+
persistRuntimeSecrets(runtime.rootDir, env);
|
|
2283
|
+
return env;
|
|
2284
|
+
}
|
|
2285
|
+
function runtimeCommandEnv(baseEnv, command) {
|
|
2286
|
+
const env = { ...baseEnv };
|
|
2287
|
+
delete env.ENV;
|
|
2288
|
+
delete env.BASH_ENV;
|
|
2289
|
+
const executable = command[0]?.trim() ?? "";
|
|
2290
|
+
const shellName = basename2(executable);
|
|
2291
|
+
const isPosixSh = executable === "/bin/sh" || shellName === "sh" || shellName === "dash";
|
|
2292
|
+
if (isPosixSh) {
|
|
2293
|
+
delete env.BASH;
|
|
2294
|
+
if (executable) {
|
|
2295
|
+
env.SHELL = executable;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
return env;
|
|
2299
|
+
}
|
|
2300
|
+
async function materializeRuntimeCertBundle(runtime) {
|
|
2301
|
+
const sourcePath = resolveSystemCertBundlePath();
|
|
2302
|
+
if (!sourcePath) {
|
|
2303
|
+
return "";
|
|
2304
|
+
}
|
|
2305
|
+
const certsDir = resolve11(runtime.rootDir, "certs");
|
|
2306
|
+
const targetPath = resolve11(certsDir, "ca-certificates.pem");
|
|
2307
|
+
await mkdir(certsDir, { recursive: true });
|
|
2308
|
+
let shouldCopy = !existsSync9(targetPath);
|
|
2309
|
+
if (!shouldCopy) {
|
|
2310
|
+
try {
|
|
2311
|
+
shouldCopy = statSync3(sourcePath).mtimeMs > statSync3(targetPath).mtimeMs;
|
|
2312
|
+
} catch {
|
|
2313
|
+
shouldCopy = true;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
if (shouldCopy) {
|
|
2317
|
+
copyFileSync2(sourcePath, targetPath);
|
|
2318
|
+
}
|
|
2319
|
+
return targetPath;
|
|
2320
|
+
}
|
|
2321
|
+
function persistRuntimeSecrets(runtimeRoot, env) {
|
|
2322
|
+
const secretsPath = resolve11(runtimeRoot, "runtime-secrets.json");
|
|
2323
|
+
const persisted = {};
|
|
2324
|
+
for (const key of [
|
|
2325
|
+
"GITHUB_TOKEN",
|
|
2326
|
+
"GH_TOKEN",
|
|
2327
|
+
"GREPTILE_GITHUB_TOKEN",
|
|
2328
|
+
"GREPTILE_API_KEY",
|
|
2329
|
+
"AI_REVIEW_MODE",
|
|
2330
|
+
"AI_REVIEW_PROVIDER"
|
|
2331
|
+
]) {
|
|
2332
|
+
const value = env[key];
|
|
2333
|
+
if (value) {
|
|
2334
|
+
persisted[key] = value;
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
if (Object.keys(persisted).length === 0) {
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
writeFileSync3(secretsPath, `${JSON.stringify(persisted, null, 2)}
|
|
2341
|
+
`, "utf-8");
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
// packages/runtime/src/control-plane/runtime/isolation/worktree.ts
|
|
2345
|
+
async function cleanupRuntimeWorktree(monorepoRoot, workspaceDir) {
|
|
2346
|
+
await runGitCommand(monorepoRoot, ["worktree", "remove", "--force", workspaceDir]);
|
|
2347
|
+
}
|
|
2348
|
+
function runtimeWorktreeNameFromRuntimeId(runtimeId) {
|
|
2349
|
+
return sanitizeRuntimeRefSegment(runtimeId.replace(/^task-/, ""));
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// packages/runtime/src/control-plane/runtime/isolation/discovery.ts
|
|
2353
|
+
init_layout();
|
|
2354
|
+
import { resolve as resolve15 } from "path";
|
|
2355
|
+
|
|
2356
|
+
// packages/runtime/src/binary-run.ts
|
|
2357
|
+
init_layout();
|
|
2358
|
+
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
2359
|
+
|
|
2360
|
+
// packages/runtime/src/control-plane/runtime/tooling/shell.ts
|
|
2361
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
2362
|
+
import { basename as basename3, dirname as dirname5, resolve as resolve12 } from "path";
|
|
2363
|
+
var sharedNativeShellOutputDir = resolve12(tmpdir3(), "rig-native");
|
|
2364
|
+
var sharedNativeShellOutputPath = resolve12(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
2365
|
+
// packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
|
|
2366
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
2367
|
+
import { basename as basename4, dirname as dirname6, resolve as resolve13 } from "path";
|
|
2368
|
+
var sharedNativeToolsOutputDir = resolve13(tmpdir4(), "rig-native");
|
|
2369
|
+
var sharedNativeToolsOutputPath = resolve13(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
2370
|
+
// packages/runtime/src/control-plane/runtime/tooling/claude-router-binary.ts
|
|
2371
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
2372
|
+
import { dirname as dirname7, resolve as resolve14 } from "path";
|
|
2373
|
+
var sharedRouterOutputDir = resolve14(tmpdir5(), "rig-native");
|
|
2374
|
+
var sharedRouterOutputPath = resolve14(sharedRouterOutputDir, `rig-tool-router-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
2375
|
+
// packages/runtime/src/control-plane/runtime/isolation/discovery.ts
|
|
2376
|
+
function runtimeRootForCleanup(projectRoot, runtimeId) {
|
|
2377
|
+
let monorepoRoot = null;
|
|
2378
|
+
try {
|
|
2379
|
+
monorepoRoot = resolveMonorepoRoot3(projectRoot);
|
|
2380
|
+
} catch {}
|
|
2381
|
+
const workspaceRootCandidate = monorepoRoot ?? projectRoot;
|
|
2382
|
+
const initialWorkspaceDir = resolve15(workspaceRootCandidate, ".worktrees", runtimeWorktreeNameFromRuntimeId(runtimeId));
|
|
2383
|
+
const runtimeRoot = resolveRuntimeWorkspaceLayout(initialWorkspaceDir).runtimeDir;
|
|
2384
|
+
return { monorepoRoot, initialWorkspaceDir, runtimeRoot };
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
// packages/runtime/src/control-plane/runtime/isolation/runner.ts
|
|
2388
|
+
init_layout();
|
|
2389
|
+
var SNAPSHOT_SIDECAR_READY_TIMEOUT_MS = 1e4;
|
|
2390
|
+
async function startRuntimeSnapshotSidecar(runtime, options = {}) {
|
|
2391
|
+
const instanceId = sanitizeRuntimeRefSegment(options.instanceId?.trim() || runtime.id);
|
|
2392
|
+
const readyFile = resolve16(runtime.stateDir, `runtime-snapshot-sidecar-${instanceId}.ready`);
|
|
2393
|
+
const requestFile = resolve16(runtime.stateDir, `runtime-snapshot-sidecar-${instanceId}.request.json`);
|
|
2394
|
+
rmSync3(readyFile, { force: true });
|
|
2395
|
+
rmSync3(requestFile, { force: true });
|
|
2396
|
+
const sidecarBinary = resolveSnapshotSidecarBinaryPath(runtime.binDir);
|
|
2397
|
+
const useCompiledSidecar = shouldUseCompiledSnapshotSidecar(sidecarBinary);
|
|
2398
|
+
const bunCli = useCompiledSidecar ? null : resolveBunCliInvocation();
|
|
2399
|
+
const command = useCompiledSidecar ? [sidecarBinary] : [bunCli.command, resolveSnapshotSidecarScriptPath()];
|
|
2400
|
+
const proc = Bun.spawn([
|
|
2401
|
+
...command,
|
|
2402
|
+
"--workspace",
|
|
2403
|
+
runtime.workspaceDir,
|
|
2404
|
+
"--task",
|
|
2405
|
+
runtime.taskId,
|
|
2406
|
+
"--runtime-id",
|
|
2407
|
+
runtime.id,
|
|
2408
|
+
"--instance-id",
|
|
2409
|
+
instanceId,
|
|
2410
|
+
"--ready-file",
|
|
2411
|
+
readyFile,
|
|
2412
|
+
"--request-file",
|
|
2413
|
+
requestFile
|
|
2414
|
+
], {
|
|
2415
|
+
cwd: runtime.workspaceDir,
|
|
2416
|
+
stdin: "ignore",
|
|
2417
|
+
stdout: "pipe",
|
|
2418
|
+
stderr: "pipe",
|
|
2419
|
+
env: {
|
|
2420
|
+
...process.env,
|
|
2421
|
+
...bunCli?.env ?? {}
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
const stdoutTextPromise = drainStream(proc.stdout);
|
|
2425
|
+
const stderrTextPromise = drainStream(proc.stderr);
|
|
2426
|
+
await waitForSnapshotSidecarReady(readyFile, proc, stdoutTextPromise, stderrTextPromise);
|
|
2427
|
+
return {
|
|
2428
|
+
cancel: async () => {
|
|
2429
|
+
try {
|
|
2430
|
+
proc.kill("SIGTERM");
|
|
2431
|
+
} catch {}
|
|
2432
|
+
await proc.exited;
|
|
2433
|
+
rmSync3(readyFile, { force: true });
|
|
2434
|
+
rmSync3(requestFile, { force: true });
|
|
2435
|
+
},
|
|
2436
|
+
finalize: async (commandParts, exitCode) => {
|
|
2437
|
+
writeFileSync4(requestFile, `${JSON.stringify({ command: commandParts, exitCode })}
|
|
2438
|
+
`, "utf-8");
|
|
2439
|
+
const [sidecarExitCode, stdout, stderr] = await Promise.all([
|
|
2440
|
+
proc.exited,
|
|
2441
|
+
stdoutTextPromise,
|
|
2442
|
+
stderrTextPromise
|
|
2443
|
+
]);
|
|
2444
|
+
rmSync3(readyFile, { force: true });
|
|
2445
|
+
rmSync3(requestFile, { force: true });
|
|
2446
|
+
if (sidecarExitCode !== 0) {
|
|
2447
|
+
throw new Error(`snapshot sidecar failed (${sidecarExitCode}): ${stderr || stdout}`);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
async function drainStream(stream) {
|
|
2453
|
+
if (!stream)
|
|
2454
|
+
return "";
|
|
2455
|
+
try {
|
|
2456
|
+
return await new Response(stream).text();
|
|
2457
|
+
} catch {
|
|
2458
|
+
return "";
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
async function runInAgentRuntime(options) {
|
|
2462
|
+
const snapshotSidecar = await startRuntimeSnapshotSidecar(options.runtime);
|
|
2463
|
+
try {
|
|
2464
|
+
const runtimeCommand = resolveInternalRuntimeCommand(options.command);
|
|
2465
|
+
const sandboxPlan = await wrapWithRuntimeSandbox({
|
|
2466
|
+
projectRoot: options.projectRoot,
|
|
2467
|
+
runtime: {
|
|
2468
|
+
id: options.runtime.id,
|
|
2469
|
+
mode: options.runtime.mode,
|
|
2470
|
+
rootDir: options.runtime.rootDir,
|
|
2471
|
+
workspaceDir: options.runtime.workspaceDir,
|
|
2472
|
+
homeDir: options.runtime.homeDir,
|
|
2473
|
+
tmpDir: options.runtime.tmpDir,
|
|
2474
|
+
cacheDir: options.runtime.cacheDir,
|
|
2475
|
+
stateDir: options.runtime.stateDir,
|
|
2476
|
+
sessionDir: options.runtime.sessionDir,
|
|
2477
|
+
claudeHomeDir: options.runtime.claudeHomeDir,
|
|
2478
|
+
binDir: options.runtime.binDir
|
|
2479
|
+
},
|
|
2480
|
+
command: runtimeCommand
|
|
2481
|
+
});
|
|
2482
|
+
const proc = Bun.spawn(sandboxPlan.command, {
|
|
2483
|
+
cwd: options.runtime.workspaceDir,
|
|
2484
|
+
env: runtimeCommandEnv(await runtimeEnv(options.projectRoot, options.runtime), sandboxPlan.command),
|
|
2485
|
+
stdin: options.inheritStdio ? "inherit" : "pipe",
|
|
2486
|
+
stdout: options.inheritStdio ? "inherit" : "pipe",
|
|
2487
|
+
stderr: options.inheritStdio ? "inherit" : "pipe"
|
|
2488
|
+
});
|
|
2489
|
+
const exitCode = await proc.exited;
|
|
2490
|
+
if (options.inheritStdio) {
|
|
2491
|
+
await snapshotSidecar.finalize(runtimeCommand, exitCode);
|
|
2492
|
+
return {
|
|
2493
|
+
exitCode,
|
|
2494
|
+
sandboxBackend: sandboxPlan.backend,
|
|
2495
|
+
sandboxEnabled: sandboxPlan.enabled
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
const stdout = await new Response(proc.stdout).text();
|
|
2499
|
+
const stderr = await new Response(proc.stderr).text();
|
|
2500
|
+
try {
|
|
2501
|
+
await Bun.write(resolve16(options.runtime.logsDir, "agent-stdout.log"), stdout);
|
|
2502
|
+
await Bun.write(resolve16(options.runtime.logsDir, "agent-stderr.log"), stderr);
|
|
2503
|
+
} catch {}
|
|
2504
|
+
await snapshotSidecar.finalize(runtimeCommand, exitCode);
|
|
2505
|
+
return {
|
|
2506
|
+
exitCode,
|
|
2507
|
+
stdout,
|
|
2508
|
+
stderr,
|
|
2509
|
+
sandboxBackend: sandboxPlan.backend,
|
|
2510
|
+
sandboxEnabled: sandboxPlan.enabled
|
|
2511
|
+
};
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
await snapshotSidecar.cancel();
|
|
2514
|
+
throw error;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function resolveInternalRuntimeCommand(command) {
|
|
2518
|
+
if (command.length === 0) {
|
|
2519
|
+
return [];
|
|
2520
|
+
}
|
|
2521
|
+
const executable = command[0]?.trim() ?? "";
|
|
2522
|
+
if (!executable) {
|
|
2523
|
+
return [...command];
|
|
2524
|
+
}
|
|
2525
|
+
if (basename5(executable) === "bun") {
|
|
2526
|
+
return [resolveBunBinaryPath(), ...command.slice(1)];
|
|
2527
|
+
}
|
|
2528
|
+
return [...command];
|
|
2529
|
+
}
|
|
2530
|
+
async function cleanupAgentRuntime(options) {
|
|
2531
|
+
const { monorepoRoot, initialWorkspaceDir, runtimeRoot } = runtimeRootForCleanup(options.projectRoot, options.id);
|
|
2532
|
+
const metadataPath = resolve16(runtimeRoot, "runtime.json");
|
|
2533
|
+
if (!existsSync10(runtimeRoot)) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
let mode = "worktree";
|
|
2537
|
+
let workspaceDir = "";
|
|
2538
|
+
const metadata = await readRuntimeMetadata(metadataPath);
|
|
2539
|
+
const metadataMatchesRequestedRuntime = metadata !== null && metadata.id === options.id && resolve16(metadata.workspaceDir) === resolve16(initialWorkspaceDir);
|
|
2540
|
+
if (metadata && metadataMatchesRequestedRuntime) {
|
|
2541
|
+
mode = metadata.mode;
|
|
2542
|
+
workspaceDir = metadata.workspaceDir;
|
|
2543
|
+
} else if (existsSync10(initialWorkspaceDir)) {
|
|
2544
|
+
workspaceDir = initialWorkspaceDir;
|
|
2545
|
+
}
|
|
2546
|
+
const preservesTaskWorktree = metadataMatchesRequestedRuntime && metadata ? options.id === taskRuntimeId(metadata.taskId) : false;
|
|
2547
|
+
if (mode === "worktree" && workspaceDir && monorepoRoot && !preservesTaskWorktree) {
|
|
2548
|
+
await cleanupRuntimeWorktree(monorepoRoot, workspaceDir);
|
|
2549
|
+
} else if (mode === "worktree" && workspaceDir && preservesTaskWorktree) {
|
|
2550
|
+
cleanupTaskRuntimeOverlay(workspaceDir);
|
|
2551
|
+
}
|
|
2552
|
+
rmSync3(runtimeRoot, { recursive: true, force: true });
|
|
2553
|
+
}
|
|
2554
|
+
function cleanupTaskRuntimeOverlay(workspaceDir) {
|
|
2555
|
+
const layout = resolveRuntimeWorkspaceLayout(workspaceDir);
|
|
2556
|
+
for (const path of [
|
|
2557
|
+
layout.homeDir,
|
|
2558
|
+
layout.tmpDir,
|
|
2559
|
+
layout.cacheDir,
|
|
2560
|
+
layout.logsDir,
|
|
2561
|
+
layout.stateDir,
|
|
2562
|
+
layout.sessionDir,
|
|
2563
|
+
layout.binDir,
|
|
2564
|
+
layout.distDir,
|
|
2565
|
+
layout.runtimeDir,
|
|
2566
|
+
layout.contextPath
|
|
2567
|
+
]) {
|
|
2568
|
+
rmSync3(path, { recursive: true, force: true });
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
function resolveSnapshotSidecarScriptPath() {
|
|
2572
|
+
return resolveRuntimeSourceScriptPath("snapshot-sidecar.ts");
|
|
2573
|
+
}
|
|
2574
|
+
function resolveSnapshotSidecarBinaryPath(binDir) {
|
|
2575
|
+
return resolve16(binDir, "snapshot-sidecar");
|
|
2576
|
+
}
|
|
2577
|
+
function shouldUseCompiledSnapshotSidecar(binaryPath) {
|
|
2578
|
+
if (!existsSync10(binaryPath)) {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
const preference = process.env.RIG_USE_COMPILED_SNAPSHOT_SIDECAR?.trim().toLowerCase();
|
|
2582
|
+
if (!preference) {
|
|
2583
|
+
return false;
|
|
2584
|
+
}
|
|
2585
|
+
return preference === "1" || preference === "true" || preference === "yes";
|
|
2586
|
+
}
|
|
2587
|
+
function resolveRuntimeSourceScriptPath(fileName) {
|
|
2588
|
+
const hostRoots = [
|
|
2589
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
2590
|
+
process.env.PROJECT_RIG_ROOT?.trim()
|
|
2591
|
+
].filter((value) => Boolean(value));
|
|
2592
|
+
for (const root of hostRoots) {
|
|
2593
|
+
const candidate = resolve16(root, "packages/runtime/src/control-plane/runtime", fileName);
|
|
2594
|
+
if (existsSync10(candidate)) {
|
|
2595
|
+
return candidate;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
return resolve16(import.meta.dir, "..", fileName);
|
|
2599
|
+
}
|
|
2600
|
+
function resolveBunCliInvocation() {
|
|
2601
|
+
if (process.env.RIG_BUN_PATH?.trim()) {
|
|
2602
|
+
return {
|
|
2603
|
+
command: process.env.RIG_BUN_PATH.trim(),
|
|
2604
|
+
env: {}
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
const systemBun = Bun.which("bun")?.trim();
|
|
2608
|
+
if (systemBun) {
|
|
2609
|
+
return {
|
|
2610
|
+
command: systemBun,
|
|
2611
|
+
env: {}
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
if (process.execPath?.trim()) {
|
|
2615
|
+
return {
|
|
2616
|
+
command: process.execPath,
|
|
2617
|
+
env: { BUN_BE_BUN: "1" }
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
return { command: "bun", env: {} };
|
|
2621
|
+
}
|
|
2622
|
+
async function waitForSnapshotSidecarReady(readyFile, proc, stdoutTextPromise, stderrTextPromise) {
|
|
2623
|
+
const deadline = Date.now() + SNAPSHOT_SIDECAR_READY_TIMEOUT_MS;
|
|
2624
|
+
while (Date.now() < deadline) {
|
|
2625
|
+
if (existsSync10(readyFile)) {
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
const exitCode = proc.exitCode;
|
|
2629
|
+
if (exitCode !== null) {
|
|
2630
|
+
const [stderr, stdout] = await Promise.all([stderrTextPromise, stdoutTextPromise]);
|
|
2631
|
+
throw new Error(`snapshot sidecar exited before ready (${exitCode}): ${stderr || stdout}`);
|
|
2632
|
+
}
|
|
2633
|
+
await Bun.sleep(25);
|
|
2634
|
+
}
|
|
2635
|
+
throw new Error(`snapshot sidecar did not become ready within ${SNAPSHOT_SIDECAR_READY_TIMEOUT_MS}ms`);
|
|
2636
|
+
}
|
|
2637
|
+
async function readRuntimeMetadata(metadataPath) {
|
|
2638
|
+
if (!existsSync10(metadataPath)) {
|
|
2639
|
+
return null;
|
|
2640
|
+
}
|
|
2641
|
+
try {
|
|
2642
|
+
return await Bun.file(metadataPath).json();
|
|
2643
|
+
} catch {
|
|
2644
|
+
return null;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
export {
|
|
2648
|
+
startRuntimeSnapshotSidecar,
|
|
2649
|
+
runInAgentRuntime,
|
|
2650
|
+
cleanupAgentRuntime
|
|
2651
|
+
};
|