@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,2255 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
3
|
+
|
|
4
|
+
// packages/runtime/src/layout.ts
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { basename, dirname, resolve } from "path";
|
|
7
|
+
function resolveMonorepoRoot(projectRoot) {
|
|
8
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
9
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
10
|
+
if (explicit) {
|
|
11
|
+
const explicitRoot = resolve(explicit);
|
|
12
|
+
const explicitParent = dirname(explicitRoot);
|
|
13
|
+
if (basename(explicitParent) === ".worktrees") {
|
|
14
|
+
const owner = dirname(explicitParent);
|
|
15
|
+
const ownerHasGit = existsSync(resolve(owner, ".git"));
|
|
16
|
+
const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
|
|
17
|
+
const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
|
|
18
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
19
|
+
return owner;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
22
|
+
}
|
|
23
|
+
if (!existsSync(resolve(explicitRoot, ".git"))) {
|
|
24
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
25
|
+
}
|
|
26
|
+
const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
|
|
27
|
+
const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
|
|
28
|
+
if (!hasTaskConfig && !hasRigConfig) {
|
|
29
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
30
|
+
}
|
|
31
|
+
return explicitRoot;
|
|
32
|
+
}
|
|
33
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
34
|
+
if (basename(projectParent) === ".worktrees") {
|
|
35
|
+
const worktreeOwner = dirname(projectParent);
|
|
36
|
+
const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
|
|
37
|
+
const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
|
|
38
|
+
const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
|
|
39
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
40
|
+
return worktreeOwner;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return normalizedProjectRoot;
|
|
44
|
+
}
|
|
45
|
+
function resolveRuntimeWorkspaceLayout(workspaceDir) {
|
|
46
|
+
const root = resolve(workspaceDir);
|
|
47
|
+
const rigRoot = resolve(root, ".rig");
|
|
48
|
+
const logsDir = resolve(rigRoot, "logs");
|
|
49
|
+
const stateDir = resolve(rigRoot, "state");
|
|
50
|
+
const runtimeDir = resolve(rigRoot, "runtime");
|
|
51
|
+
const binDir = resolve(rigRoot, "bin");
|
|
52
|
+
return {
|
|
53
|
+
workspaceDir: root,
|
|
54
|
+
rigRoot,
|
|
55
|
+
stateDir,
|
|
56
|
+
logsDir,
|
|
57
|
+
artifactsRoot: resolve(root, RIG_ARTIFACTS_DIRNAME),
|
|
58
|
+
runtimeDir,
|
|
59
|
+
homeDir: resolve(rigRoot, "home"),
|
|
60
|
+
tmpDir: resolve(rigRoot, "tmp"),
|
|
61
|
+
cacheDir: resolve(rigRoot, "cache"),
|
|
62
|
+
sessionDir: resolve(rigRoot, "session"),
|
|
63
|
+
binDir,
|
|
64
|
+
distDir: resolve(rigRoot, "dist"),
|
|
65
|
+
pluginBinDir: resolve(binDir, "plugins"),
|
|
66
|
+
contextPath: resolve(rigRoot, "runtime-context.json"),
|
|
67
|
+
controlPlaneEventsFile: resolve(logsDir, "control-plane.events.jsonl")
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
|
|
71
|
+
const explicit = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
72
|
+
if (!explicit) {
|
|
73
|
+
throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
|
|
74
|
+
}
|
|
75
|
+
return resolve(explicit);
|
|
76
|
+
}
|
|
77
|
+
function resolveRigLayout(projectRoot) {
|
|
78
|
+
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
79
|
+
const definitionRoot = resolve(projectRoot, RIG_DEFINITION_DIRNAME);
|
|
80
|
+
const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
|
|
81
|
+
const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
|
|
82
|
+
const policyDir = resolve(definitionRoot, "policy");
|
|
83
|
+
return {
|
|
84
|
+
projectRoot,
|
|
85
|
+
monorepoRoot,
|
|
86
|
+
definitionRoot,
|
|
87
|
+
runtimeWorkspaceRoot,
|
|
88
|
+
stateRoot: runtimeLayout.rigRoot,
|
|
89
|
+
artifactsRoot: runtimeLayout.artifactsRoot,
|
|
90
|
+
configPath: resolve(definitionRoot, "config.sh"),
|
|
91
|
+
taskConfigPath: resolve(runtimeWorkspaceRoot, ".rig", "task-config.json"),
|
|
92
|
+
policyDir,
|
|
93
|
+
policyFile: resolve(policyDir, "policy.json"),
|
|
94
|
+
pluginsDir: resolve(definitionRoot, "plugins"),
|
|
95
|
+
hooksDir: resolve(definitionRoot, "hooks"),
|
|
96
|
+
toolsDir: resolve(definitionRoot, "tools"),
|
|
97
|
+
templatesDir: resolve(definitionRoot, "templates"),
|
|
98
|
+
validationDir: resolve(definitionRoot, "validation"),
|
|
99
|
+
stateDir: runtimeLayout.stateDir,
|
|
100
|
+
logsDir: runtimeLayout.logsDir,
|
|
101
|
+
notificationsDir: resolve(definitionRoot, "notifications"),
|
|
102
|
+
runtimeDir: runtimeLayout.runtimeDir,
|
|
103
|
+
distDir: runtimeLayout.distDir,
|
|
104
|
+
binDir: runtimeLayout.binDir,
|
|
105
|
+
pluginBinDir: runtimeLayout.pluginBinDir,
|
|
106
|
+
keybindingsPath: resolve(definitionRoot, "keybindings.json"),
|
|
107
|
+
controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
var RIG_DEFINITION_DIRNAME = "rig", RIG_ARTIFACTS_DIRNAME = "artifacts";
|
|
111
|
+
var init_layout = () => {};
|
|
112
|
+
|
|
113
|
+
// packages/runtime/src/control-plane/runtime/sandbox/utils.ts
|
|
114
|
+
function uniq(values) {
|
|
115
|
+
return [...new Set(values)];
|
|
116
|
+
}
|
|
117
|
+
var init_utils = __esm(() => {
|
|
118
|
+
init_layout();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// packages/runtime/src/control-plane/runtime/image.ts
|
|
122
|
+
init_layout();
|
|
123
|
+
import { ptr as ptr3, toBuffer as toBuffer2 } from "bun:ffi";
|
|
124
|
+
import {
|
|
125
|
+
existsSync as existsSync6,
|
|
126
|
+
readFileSync as readFileSync2,
|
|
127
|
+
readdirSync as readdirSync2,
|
|
128
|
+
mkdirSync as mkdirSync3,
|
|
129
|
+
writeFileSync as writeFileSync2,
|
|
130
|
+
renameSync as renameSync3,
|
|
131
|
+
rmSync as rmSync3,
|
|
132
|
+
cpSync as cpSync2,
|
|
133
|
+
lstatSync,
|
|
134
|
+
unlinkSync,
|
|
135
|
+
symlinkSync
|
|
136
|
+
} from "fs";
|
|
137
|
+
import { resolve as resolve11 } from "path";
|
|
138
|
+
|
|
139
|
+
// packages/runtime/src/control-plane/native/runtime-native.ts
|
|
140
|
+
import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
|
|
141
|
+
import { copyFileSync, existsSync as existsSync2, mkdirSync, renameSync, rmSync, statSync } from "fs";
|
|
142
|
+
import { tmpdir } from "os";
|
|
143
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
144
|
+
var sharedNativeRuntimeOutputDir = resolve2(tmpdir(), "rig-native");
|
|
145
|
+
var sharedNativeRuntimeOutputPath = resolve2(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
|
|
146
|
+
var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
|
|
147
|
+
var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
|
|
148
|
+
function requireNativeRuntimeLibrary(feature) {
|
|
149
|
+
if (!nativeRuntimeLibrary) {
|
|
150
|
+
throw new Error(`Native Zig runtime is required for ${feature}`);
|
|
151
|
+
}
|
|
152
|
+
return nativeRuntimeLibrary;
|
|
153
|
+
}
|
|
154
|
+
async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
|
|
155
|
+
if (await buildNativeRuntimeLibrary(outputPath, options)) {
|
|
156
|
+
return outputPath;
|
|
157
|
+
}
|
|
158
|
+
return !options.force && existsSync2(outputPath) ? outputPath : null;
|
|
159
|
+
}
|
|
160
|
+
async function materializeNativeRuntimeLibrary(targetDir) {
|
|
161
|
+
const sourcePath = await ensureNativeRuntimeLibraryPath();
|
|
162
|
+
if (!sourcePath) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const targetPath = resolve2(targetDir, colocatedNativeRuntimeFileName);
|
|
166
|
+
mkdirSync(targetDir, { recursive: true });
|
|
167
|
+
const needsCopy = !existsSync2(targetPath) || statSync(sourcePath).mtimeMs > statSync(targetPath).mtimeMs;
|
|
168
|
+
if (needsCopy) {
|
|
169
|
+
copyFileSync(sourcePath, targetPath);
|
|
170
|
+
}
|
|
171
|
+
return targetPath;
|
|
172
|
+
}
|
|
173
|
+
async function loadNativeRuntimeLibrary() {
|
|
174
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
for (const candidate of nativeRuntimeLibraryCandidates()) {
|
|
178
|
+
if (!candidate || !existsSync2(candidate)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const loaded = tryDlopenNativeRuntimeLibrary(candidate);
|
|
182
|
+
if (loaded) {
|
|
183
|
+
return loaded;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
|
|
187
|
+
if (!builtLibraryPath) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
|
|
191
|
+
}
|
|
192
|
+
function nativePackageLibraryCandidates(fromDir, names) {
|
|
193
|
+
const candidates = [];
|
|
194
|
+
let cursor = resolve2(fromDir);
|
|
195
|
+
for (let index = 0;index < 8; index += 1) {
|
|
196
|
+
for (const name of names) {
|
|
197
|
+
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));
|
|
198
|
+
}
|
|
199
|
+
const parent = dirname2(cursor);
|
|
200
|
+
if (parent === cursor)
|
|
201
|
+
break;
|
|
202
|
+
cursor = parent;
|
|
203
|
+
}
|
|
204
|
+
return candidates;
|
|
205
|
+
}
|
|
206
|
+
function nativeRuntimeLibraryCandidates() {
|
|
207
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
|
|
208
|
+
const execDir = process.execPath?.trim() ? dirname2(process.execPath.trim()) : "";
|
|
209
|
+
const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
|
|
210
|
+
return [...new Set([
|
|
211
|
+
explicit,
|
|
212
|
+
...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
|
|
213
|
+
execDir ? resolve2(execDir, colocatedNativeRuntimeFileName) : "",
|
|
214
|
+
execDir ? resolve2(execDir, platformSpecific) : "",
|
|
215
|
+
execDir ? resolve2(execDir, "..", colocatedNativeRuntimeFileName) : "",
|
|
216
|
+
execDir ? resolve2(execDir, "..", platformSpecific) : "",
|
|
217
|
+
execDir ? resolve2(execDir, "lib", colocatedNativeRuntimeFileName) : "",
|
|
218
|
+
execDir ? resolve2(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
|
|
219
|
+
sharedNativeRuntimeOutputPath
|
|
220
|
+
].filter(Boolean))];
|
|
221
|
+
}
|
|
222
|
+
function resolveNativeRuntimeSourcePath() {
|
|
223
|
+
const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
|
|
224
|
+
if (explicit && existsSync2(explicit)) {
|
|
225
|
+
return explicit;
|
|
226
|
+
}
|
|
227
|
+
const bundled = resolve2(import.meta.dir, "../../../native/snapshot.zig");
|
|
228
|
+
return existsSync2(bundled) ? bundled : null;
|
|
229
|
+
}
|
|
230
|
+
async function buildNativeRuntimeLibrary(outputPath, options = {}) {
|
|
231
|
+
if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const zigBinary = Bun.which("zig");
|
|
235
|
+
const sourcePath = resolveNativeRuntimeSourcePath();
|
|
236
|
+
if (!zigBinary || !sourcePath) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
240
|
+
try {
|
|
241
|
+
mkdirSync(dirname2(outputPath), { recursive: true });
|
|
242
|
+
const needsBuild = options.force === true || !existsSync2(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
|
|
243
|
+
if (!needsBuild) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
const build = Bun.spawn([
|
|
247
|
+
zigBinary,
|
|
248
|
+
"build-lib",
|
|
249
|
+
sourcePath,
|
|
250
|
+
"-dynamic",
|
|
251
|
+
"-O",
|
|
252
|
+
"ReleaseFast",
|
|
253
|
+
`-femit-bin=${tempOutputPath}`
|
|
254
|
+
], {
|
|
255
|
+
cwd: import.meta.dir,
|
|
256
|
+
stdout: "pipe",
|
|
257
|
+
stderr: "pipe"
|
|
258
|
+
});
|
|
259
|
+
const exitCode = await build.exited;
|
|
260
|
+
if (exitCode !== 0 || !existsSync2(tempOutputPath)) {
|
|
261
|
+
rmSync(tempOutputPath, { force: true });
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
renameSync(tempOutputPath, outputPath);
|
|
265
|
+
return true;
|
|
266
|
+
} catch {
|
|
267
|
+
rmSync(tempOutputPath, { force: true });
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function tryDlopenNativeRuntimeLibrary(outputPath) {
|
|
272
|
+
try {
|
|
273
|
+
return dlopen(outputPath, {
|
|
274
|
+
rig_scope_match: {
|
|
275
|
+
args: ["ptr", "ptr"],
|
|
276
|
+
returns: "u8"
|
|
277
|
+
},
|
|
278
|
+
snapshot_capture: {
|
|
279
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
280
|
+
returns: "ptr"
|
|
281
|
+
},
|
|
282
|
+
snapshot_delta: {
|
|
283
|
+
args: ["ptr", "ptr"],
|
|
284
|
+
returns: "ptr"
|
|
285
|
+
},
|
|
286
|
+
snapshot_store_delta: {
|
|
287
|
+
args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
288
|
+
returns: "ptr"
|
|
289
|
+
},
|
|
290
|
+
snapshot_inspect_delta: {
|
|
291
|
+
args: ["ptr", "u64"],
|
|
292
|
+
returns: "ptr"
|
|
293
|
+
},
|
|
294
|
+
snapshot_apply_delta: {
|
|
295
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
296
|
+
returns: "ptr"
|
|
297
|
+
},
|
|
298
|
+
snapshot_release: {
|
|
299
|
+
args: ["ptr"],
|
|
300
|
+
returns: "void"
|
|
301
|
+
},
|
|
302
|
+
runtime_hash_file: {
|
|
303
|
+
args: ["ptr", "u64"],
|
|
304
|
+
returns: "ptr"
|
|
305
|
+
},
|
|
306
|
+
runtime_hash_tree: {
|
|
307
|
+
args: ["ptr", "u64"],
|
|
308
|
+
returns: "ptr"
|
|
309
|
+
},
|
|
310
|
+
runtime_prepare_paths: {
|
|
311
|
+
args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
|
|
312
|
+
returns: "ptr"
|
|
313
|
+
},
|
|
314
|
+
runtime_link_dependency_layer: {
|
|
315
|
+
args: ["ptr", "u64", "ptr", "u64"],
|
|
316
|
+
returns: "ptr"
|
|
317
|
+
},
|
|
318
|
+
runtime_scan_worktrees: {
|
|
319
|
+
args: ["ptr", "u64"],
|
|
320
|
+
returns: "ptr"
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// packages/runtime/src/control-plane/native/git-native.ts
|
|
329
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
330
|
+
import { dirname as dirname3, isAbsolute, resolve as resolve3 } from "path";
|
|
331
|
+
var sharedGitNativeOutputDir = resolve3(tmpdir2(), "rig-native");
|
|
332
|
+
var sharedGitNativeOutputPath = resolve3(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
333
|
+
// packages/runtime/src/control-plane/native/utils.ts
|
|
334
|
+
import { ptr as ptr2 } from "bun:ffi";
|
|
335
|
+
init_layout();
|
|
336
|
+
|
|
337
|
+
// packages/runtime/src/control-plane/native/scope-rules.ts
|
|
338
|
+
var activeRules = null;
|
|
339
|
+
function getScopeRules() {
|
|
340
|
+
return activeRules;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// packages/runtime/src/control-plane/native/utils.ts
|
|
344
|
+
var nativeScopeMatcher = null;
|
|
345
|
+
var scopeRegexCache = new Map;
|
|
346
|
+
function unique(values) {
|
|
347
|
+
return [...new Set(values)];
|
|
348
|
+
}
|
|
349
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
350
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
351
|
+
const rules = getScopeRules();
|
|
352
|
+
if (rules?.stripPrefixes) {
|
|
353
|
+
for (const prefix of rules.stripPrefixes) {
|
|
354
|
+
if (normalized.startsWith(prefix)) {
|
|
355
|
+
normalized = normalized.slice(prefix.length);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return normalized;
|
|
360
|
+
}
|
|
361
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
362
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
363
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
364
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
365
|
+
}
|
|
366
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
367
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
368
|
+
}
|
|
369
|
+
return normalizeRelativeScopePath(normalized);
|
|
370
|
+
}
|
|
371
|
+
function scopeMatches(path, scopes) {
|
|
372
|
+
const matcher = getNativeScopeMatcher();
|
|
373
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
374
|
+
for (const scope of scopes) {
|
|
375
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
376
|
+
for (const candidatePath of pathVariants) {
|
|
377
|
+
for (const candidateScope of scopeVariants) {
|
|
378
|
+
if (candidatePath === candidateScope) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
if (matcher.match(candidateScope, candidatePath)) {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
function getNativeScopeMatcher() {
|
|
390
|
+
if (nativeScopeMatcher) {
|
|
391
|
+
return nativeScopeMatcher;
|
|
392
|
+
}
|
|
393
|
+
nativeScopeMatcher = createNativeScopeMatcher();
|
|
394
|
+
return nativeScopeMatcher;
|
|
395
|
+
}
|
|
396
|
+
function createNativeScopeMatcher() {
|
|
397
|
+
const library = requireNativeRuntimeLibrary("scope matching");
|
|
398
|
+
return {
|
|
399
|
+
match: (pattern, path) => {
|
|
400
|
+
const patternBuffer = Buffer.from(`${pattern}\x00`);
|
|
401
|
+
const pathBuffer = Buffer.from(`${path}\x00`);
|
|
402
|
+
return library.symbols.rig_scope_match(Number(ptr2(patternBuffer)), Number(ptr2(pathBuffer))) !== 0;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
// packages/runtime/src/control-plane/runtime/context.ts
|
|
407
|
+
var DEFAULT_RUNTIME_MEMORY_RETRIEVAL = {
|
|
408
|
+
topK: 5,
|
|
409
|
+
lexicalWeight: 0.35,
|
|
410
|
+
vectorWeight: 0.45,
|
|
411
|
+
recencyWeight: 0.1,
|
|
412
|
+
confidenceWeight: 0.1
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// packages/runtime/src/control-plane/memory-sync/query.ts
|
|
416
|
+
var DEFAULT_RESULT_LIMIT = DEFAULT_RUNTIME_MEMORY_RETRIEVAL.topK;
|
|
417
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
418
|
+
// packages/runtime/src/build-time-config.ts
|
|
419
|
+
function normalizeBuildConfig(value) {
|
|
420
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
421
|
+
return {};
|
|
422
|
+
}
|
|
423
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
424
|
+
}
|
|
425
|
+
function readBuildConfig() {
|
|
426
|
+
if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
|
|
427
|
+
return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
|
|
428
|
+
}
|
|
429
|
+
const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
|
|
430
|
+
if (!raw) {
|
|
431
|
+
return {};
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
return normalizeBuildConfig(JSON.parse(raw));
|
|
435
|
+
} catch {
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// packages/runtime/src/control-plane/runtime/tooling/shell.ts
|
|
441
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
442
|
+
import { basename as basename2, dirname as dirname4, resolve as resolve4 } from "path";
|
|
443
|
+
var sharedNativeShellOutputDir = resolve4(tmpdir3(), "rig-native");
|
|
444
|
+
var sharedNativeShellOutputPath = resolve4(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
445
|
+
// packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
|
|
446
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
447
|
+
import { basename as basename3, dirname as dirname5, resolve as resolve5 } from "path";
|
|
448
|
+
var sharedNativeToolsOutputDir = resolve5(tmpdir4(), "rig-native");
|
|
449
|
+
var sharedNativeToolsOutputPath = resolve5(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
450
|
+
// packages/runtime/src/control-plane/plugin-host-context.ts
|
|
451
|
+
import { createPluginHost } from "@rig/core";
|
|
452
|
+
import { loadConfig } from "@rig/core/load-config";
|
|
453
|
+
|
|
454
|
+
// packages/runtime/src/control-plane/repos/registry.ts
|
|
455
|
+
var MANAGED_REPOS = new Map;
|
|
456
|
+
|
|
457
|
+
// packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
|
|
458
|
+
var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
|
|
459
|
+
|
|
460
|
+
// packages/runtime/src/control-plane/state-sync/types.ts
|
|
461
|
+
var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
|
|
462
|
+
"draft",
|
|
463
|
+
"open",
|
|
464
|
+
"ready",
|
|
465
|
+
"queued",
|
|
466
|
+
"in_progress",
|
|
467
|
+
"under_review",
|
|
468
|
+
"blocked",
|
|
469
|
+
"completed",
|
|
470
|
+
"cancelled"
|
|
471
|
+
]);
|
|
472
|
+
// packages/runtime/src/control-plane/repos/layout.ts
|
|
473
|
+
init_layout();
|
|
474
|
+
// packages/runtime/src/control-plane/state-sync/reconcile.ts
|
|
475
|
+
var STALE_CLAIM_MS = 24 * 60 * 60 * 1000;
|
|
476
|
+
// packages/runtime/src/control-plane/native/validator.ts
|
|
477
|
+
init_layout();
|
|
478
|
+
|
|
479
|
+
// packages/runtime/src/binary-run.ts
|
|
480
|
+
init_layout();
|
|
481
|
+
import { chmodSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync } from "fs";
|
|
482
|
+
import { basename as basename4, dirname as dirname6, resolve as resolve6 } from "path";
|
|
483
|
+
import { fileURLToPath } from "url";
|
|
484
|
+
import { drainMicrotasks, gcAndSweep } from "bun:jsc";
|
|
485
|
+
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
486
|
+
async function buildRuntimeBinary(options) {
|
|
487
|
+
return runSerializedRuntimeBinaryBuild(async () => {
|
|
488
|
+
const resolved = resolveRuntimeBinaryBuildOptions(options);
|
|
489
|
+
runBestEffortBuildGc();
|
|
490
|
+
const manifestPath = runtimeBinaryCacheManifestPath(resolved.outputPath);
|
|
491
|
+
const buildKey = createRuntimeBinaryBuildKey({
|
|
492
|
+
entrypoint: resolved.entrypoint,
|
|
493
|
+
define: resolved.define,
|
|
494
|
+
env: resolved.env
|
|
495
|
+
});
|
|
496
|
+
if (await isRuntimeBinaryBuildFresh({ outputPath: resolved.outputPath, manifestPath, buildKey })) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (shouldUseRuntimeBinaryBuildWorker()) {
|
|
500
|
+
await buildRuntimeBinaryViaWorker(resolved);
|
|
501
|
+
} else {
|
|
502
|
+
await buildRuntimeBinaryInProcess(resolved, { manifestPath, buildKey });
|
|
503
|
+
}
|
|
504
|
+
runBestEffortBuildGc();
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
async function buildRuntimeBinaryInProcess(options, manifest) {
|
|
508
|
+
const tempBuildDir = resolve6(dirname6(options.outputPath), `.bun-build-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
509
|
+
const tempOutputPath = resolve6(tempBuildDir, basename4(options.outputPath));
|
|
510
|
+
mkdirSync2(tempBuildDir, { recursive: true });
|
|
511
|
+
await withTemporaryEnv({
|
|
512
|
+
...options.env,
|
|
513
|
+
...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
|
|
514
|
+
}, async () => withTemporaryCwd(tempBuildDir, async () => {
|
|
515
|
+
const buildResult = await Bun.build({
|
|
516
|
+
entrypoints: [options.entrypoint],
|
|
517
|
+
compile: {
|
|
518
|
+
target: currentCompileTarget(),
|
|
519
|
+
outfile: tempOutputPath
|
|
520
|
+
},
|
|
521
|
+
target: "bun",
|
|
522
|
+
format: "esm",
|
|
523
|
+
minify: true,
|
|
524
|
+
bytecode: true,
|
|
525
|
+
metafile: true,
|
|
526
|
+
define: options.define ? {
|
|
527
|
+
__RIG_BUILD_CONFIG__: JSON.stringify(options.define)
|
|
528
|
+
} : undefined
|
|
529
|
+
});
|
|
530
|
+
if (!buildResult.success) {
|
|
531
|
+
const details = buildResult.logs.map((log) => [log.message, log.position?.file ? `${log.position.file}:${log.position.line}:${log.position.column}` : ""].filter(Boolean).join(" ")).filter(Boolean).join(`
|
|
532
|
+
`);
|
|
533
|
+
throw new Error(`Failed to build ${options.entrypoint}: ${details || "Bun.build() returned errors"}`);
|
|
534
|
+
}
|
|
535
|
+
if (!existsSync3(tempOutputPath)) {
|
|
536
|
+
const emitted = buildResult.outputs.map((output) => output.path).join(", ") || "(none)";
|
|
537
|
+
throw new Error(`Failed to build ${options.entrypoint}: Bun.build() did not emit ${tempOutputPath}. Emitted: ${emitted}`);
|
|
538
|
+
}
|
|
539
|
+
renameSync2(tempOutputPath, options.outputPath);
|
|
540
|
+
chmodSync(options.outputPath, 493);
|
|
541
|
+
if (manifest) {
|
|
542
|
+
await writeRuntimeBinaryCacheManifest({
|
|
543
|
+
manifestPath: manifest.manifestPath,
|
|
544
|
+
buildKey: manifest.buildKey,
|
|
545
|
+
cwd: tempBuildDir,
|
|
546
|
+
metafile: buildResult.metafile
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
})).finally(() => {
|
|
550
|
+
rmSync2(tempBuildDir, { recursive: true, force: true });
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
function runBestEffortBuildGc() {
|
|
554
|
+
try {
|
|
555
|
+
drainMicrotasks();
|
|
556
|
+
} catch {}
|
|
557
|
+
try {
|
|
558
|
+
gcAndSweep();
|
|
559
|
+
} catch {}
|
|
560
|
+
}
|
|
561
|
+
function runtimeBinaryCacheManifestPath(outputPath) {
|
|
562
|
+
return `${outputPath}.build-manifest.json`;
|
|
563
|
+
}
|
|
564
|
+
function resolveRuntimeBinaryBuildOptions(options) {
|
|
565
|
+
return {
|
|
566
|
+
...options,
|
|
567
|
+
entrypoint: resolve6(options.cwd, options.sourcePath),
|
|
568
|
+
outputPath: resolve6(options.outputPath)
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function shouldUseRuntimeBinaryBuildWorker() {
|
|
572
|
+
if (process.env.RIG_RUNTIME_BUILD_WORKER === "1") {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
if (process.env.RIG_RUNTIME_BUILD_IN_PROCESS === "1") {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
580
|
+
async function buildRuntimeBinaryViaWorker(options) {
|
|
581
|
+
const workerSourcePath = resolveRuntimeBinaryBuildWorkerSourcePath(options);
|
|
582
|
+
if (!workerSourcePath || !existsSync3(workerSourcePath)) {
|
|
583
|
+
await buildRuntimeBinaryInProcess(options, {
|
|
584
|
+
manifestPath: runtimeBinaryCacheManifestPath(options.outputPath),
|
|
585
|
+
buildKey: createRuntimeBinaryBuildKey({
|
|
586
|
+
entrypoint: options.entrypoint,
|
|
587
|
+
define: options.define,
|
|
588
|
+
env: options.env
|
|
589
|
+
})
|
|
590
|
+
});
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const payloadPath = createRuntimeBinaryBuildWorkerPayloadPath(options.outputPath);
|
|
594
|
+
const bunCli = resolveRuntimeBinaryBuildWorkerInvocation();
|
|
595
|
+
await Bun.write(payloadPath, `${JSON.stringify(options)}
|
|
596
|
+
`);
|
|
597
|
+
const build = Bun.spawn([bunCli.command, workerSourcePath, payloadPath], {
|
|
598
|
+
cwd: options.cwd,
|
|
599
|
+
stdout: "pipe",
|
|
600
|
+
stderr: "pipe",
|
|
601
|
+
env: {
|
|
602
|
+
...process.env,
|
|
603
|
+
...options.env,
|
|
604
|
+
...bunCli.env,
|
|
605
|
+
RIG_RUNTIME_BUILD_WORKER: "1"
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
609
|
+
build.exited,
|
|
610
|
+
new Response(build.stdout).text(),
|
|
611
|
+
new Response(build.stderr).text()
|
|
612
|
+
]);
|
|
613
|
+
rmSync2(payloadPath, { force: true });
|
|
614
|
+
if (exitCode !== 0) {
|
|
615
|
+
throw new Error(`Failed to build ${options.entrypoint}: ${(stderr || stdout || `worker exited ${exitCode}`).trim()}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function createRuntimeBinaryBuildWorkerPayloadPath(outputPath) {
|
|
619
|
+
return resolve6(dirname6(outputPath), `.bun-build-worker-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
620
|
+
}
|
|
621
|
+
function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
|
|
622
|
+
const envRoots = [
|
|
623
|
+
options.cwd?.trim(),
|
|
624
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
625
|
+
process.env.PROJECT_RIG_ROOT?.trim()
|
|
626
|
+
].filter(Boolean);
|
|
627
|
+
for (const root of envRoots) {
|
|
628
|
+
const candidate = resolve6(root, "packages/runtime/src/binary-build-worker.ts");
|
|
629
|
+
if (existsSync3(candidate)) {
|
|
630
|
+
return candidate;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
const localCandidate = resolve6(import.meta.dir, "binary-build-worker.ts");
|
|
634
|
+
return existsSync3(localCandidate) ? localCandidate : null;
|
|
635
|
+
}
|
|
636
|
+
function resolveRuntimeBinaryBuildWorkerInvocation() {
|
|
637
|
+
const bunPath = Bun.which("bun");
|
|
638
|
+
if (bunPath) {
|
|
639
|
+
return { command: bunPath, env: {} };
|
|
640
|
+
}
|
|
641
|
+
if (process.execPath?.trim()) {
|
|
642
|
+
return {
|
|
643
|
+
command: process.execPath,
|
|
644
|
+
env: { BUN_BE_BUN: "1" }
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
throw new Error("bun is required to run the runtime binary build worker.");
|
|
648
|
+
}
|
|
649
|
+
function currentCompileTarget() {
|
|
650
|
+
if (process.platform === "darwin") {
|
|
651
|
+
return process.arch === "arm64" ? "bun-darwin-arm64" : "bun-darwin-x64";
|
|
652
|
+
}
|
|
653
|
+
if (process.platform === "linux") {
|
|
654
|
+
return process.arch === "arm64" ? "bun-linux-arm64" : "bun-linux-x64";
|
|
655
|
+
}
|
|
656
|
+
return "bun-windows-x64";
|
|
657
|
+
}
|
|
658
|
+
function createRuntimeBinaryBuildKey(input) {
|
|
659
|
+
return JSON.stringify({
|
|
660
|
+
version: 1,
|
|
661
|
+
bunVersion: Bun.version,
|
|
662
|
+
platform: process.platform,
|
|
663
|
+
arch: process.arch,
|
|
664
|
+
entrypoint: input.entrypoint,
|
|
665
|
+
define: sortRecord(input.define),
|
|
666
|
+
env: sortRecord(input.env)
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
async function isRuntimeBinaryBuildFresh(input) {
|
|
670
|
+
if (!existsSync3(input.outputPath) || !existsSync3(input.manifestPath)) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
let manifest = null;
|
|
674
|
+
try {
|
|
675
|
+
manifest = await Bun.file(input.manifestPath).json();
|
|
676
|
+
} catch {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
if (!manifest || manifest.version !== 1 || manifest.buildKey !== input.buildKey) {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
for (const [filePath, expectedDigest] of Object.entries(manifest.inputs || {})) {
|
|
683
|
+
if (!existsSync3(filePath)) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
if (await sha256File(filePath) !== expectedDigest) {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
async function writeRuntimeBinaryCacheManifest(input) {
|
|
693
|
+
const inputs = {};
|
|
694
|
+
for (const inputPath of Object.keys(input.metafile?.inputs || {}).sort()) {
|
|
695
|
+
const normalized = normalizeBuildInputPath(input.cwd, inputPath);
|
|
696
|
+
if (!normalized || !existsSync3(normalized)) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
inputs[normalized] = await sha256File(normalized);
|
|
700
|
+
}
|
|
701
|
+
const manifest = {
|
|
702
|
+
version: 1,
|
|
703
|
+
buildKey: input.buildKey,
|
|
704
|
+
inputs
|
|
705
|
+
};
|
|
706
|
+
await Bun.write(input.manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
707
|
+
`);
|
|
708
|
+
}
|
|
709
|
+
function normalizeBuildInputPath(cwd, inputPath) {
|
|
710
|
+
if (!inputPath) {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
if (inputPath.startsWith("file://")) {
|
|
714
|
+
return fileURLToPath(inputPath);
|
|
715
|
+
}
|
|
716
|
+
if (inputPath.startsWith("<")) {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
return resolve6(cwd, inputPath);
|
|
720
|
+
}
|
|
721
|
+
async function sha256File(path) {
|
|
722
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
723
|
+
hasher.update(await Bun.file(path).arrayBuffer());
|
|
724
|
+
return hasher.digest("hex");
|
|
725
|
+
}
|
|
726
|
+
function sortRecord(value) {
|
|
727
|
+
if (!value) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
|
|
731
|
+
}
|
|
732
|
+
async function runSerializedRuntimeBinaryBuild(action) {
|
|
733
|
+
const previous = runtimeBinaryBuildQueue;
|
|
734
|
+
let release;
|
|
735
|
+
runtimeBinaryBuildQueue = new Promise((resolve7) => {
|
|
736
|
+
release = resolve7;
|
|
737
|
+
});
|
|
738
|
+
await previous;
|
|
739
|
+
try {
|
|
740
|
+
return await action();
|
|
741
|
+
} finally {
|
|
742
|
+
release();
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async function withTemporaryEnv(env, action) {
|
|
746
|
+
if (!env) {
|
|
747
|
+
return action();
|
|
748
|
+
}
|
|
749
|
+
const previousValues = new Map;
|
|
750
|
+
for (const [key, value] of Object.entries(env)) {
|
|
751
|
+
previousValues.set(key, process.env[key]);
|
|
752
|
+
if (value === undefined) {
|
|
753
|
+
delete process.env[key];
|
|
754
|
+
} else {
|
|
755
|
+
process.env[key] = value;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
return await action();
|
|
760
|
+
} finally {
|
|
761
|
+
for (const [key, value] of previousValues.entries()) {
|
|
762
|
+
if (value === undefined) {
|
|
763
|
+
delete process.env[key];
|
|
764
|
+
} else {
|
|
765
|
+
process.env[key] = value;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function withTemporaryCwd(cwd, action) {
|
|
771
|
+
const previousCwd = process.cwd();
|
|
772
|
+
process.chdir(cwd);
|
|
773
|
+
try {
|
|
774
|
+
return await action();
|
|
775
|
+
} finally {
|
|
776
|
+
process.chdir(previousCwd);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// packages/runtime/src/control-plane/runtime/provisioning-env.ts
|
|
781
|
+
import { delimiter, resolve as resolve8 } from "path";
|
|
782
|
+
|
|
783
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
784
|
+
import { existsSync as existsSync4, readdirSync, realpathSync } from "fs";
|
|
785
|
+
import { resolve as resolve7 } from "path";
|
|
786
|
+
|
|
787
|
+
// packages/runtime/src/control-plane/runtime/sandbox-utils.ts
|
|
788
|
+
init_utils();
|
|
789
|
+
|
|
790
|
+
// packages/runtime/src/control-plane/runtime/runtime-paths.ts
|
|
791
|
+
function resolveBunBinaryPath() {
|
|
792
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
793
|
+
if (explicit) {
|
|
794
|
+
return explicit;
|
|
795
|
+
}
|
|
796
|
+
const pathBun = normalizeExecutablePath(Bun.which("bun")?.trim());
|
|
797
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
798
|
+
return pathBun;
|
|
799
|
+
}
|
|
800
|
+
const home = process.env.HOME?.trim();
|
|
801
|
+
const fallbackCandidates = [
|
|
802
|
+
home ? resolve7(home, ".bun/bin/bun") : "",
|
|
803
|
+
"/opt/homebrew/bin/bun",
|
|
804
|
+
"/usr/local/bin/bun",
|
|
805
|
+
"/usr/bin/bun"
|
|
806
|
+
];
|
|
807
|
+
for (const candidate of fallbackCandidates) {
|
|
808
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
809
|
+
if (normalized) {
|
|
810
|
+
return normalized;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
814
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
815
|
+
return execPath;
|
|
816
|
+
}
|
|
817
|
+
throw new Error("bun not found in PATH");
|
|
818
|
+
}
|
|
819
|
+
function resolveClaudeBinaryPath() {
|
|
820
|
+
const explicit = normalizeExecutablePath(process.env.RIG_CLAUDE_PATH?.trim());
|
|
821
|
+
if (explicit) {
|
|
822
|
+
return explicit;
|
|
823
|
+
}
|
|
824
|
+
const pathClaude = normalizeExecutablePath(Bun.which("claude")?.trim());
|
|
825
|
+
if (pathClaude && !looksLikeRuntimeGateway(pathClaude)) {
|
|
826
|
+
return pathClaude;
|
|
827
|
+
}
|
|
828
|
+
const home = process.env.HOME?.trim();
|
|
829
|
+
const fallbackCandidates = [
|
|
830
|
+
home ? resolve7(home, ".local/bin/claude") : "",
|
|
831
|
+
home ? resolve7(home, ".local/share/claude/local/claude") : "",
|
|
832
|
+
"/opt/homebrew/bin/claude",
|
|
833
|
+
"/usr/local/bin/claude",
|
|
834
|
+
"/usr/bin/claude"
|
|
835
|
+
];
|
|
836
|
+
for (const candidate of fallbackCandidates) {
|
|
837
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
838
|
+
if (normalized) {
|
|
839
|
+
return normalized;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
throw new Error("claude not found in PATH");
|
|
843
|
+
}
|
|
844
|
+
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
845
|
+
return resolve7(bunBinaryPath, "../..");
|
|
846
|
+
}
|
|
847
|
+
function resolveClaudeInstallDir() {
|
|
848
|
+
const realPath = resolveClaudeBinaryPath();
|
|
849
|
+
return resolve7(realPath, "..");
|
|
850
|
+
}
|
|
851
|
+
function resolveNodeInstallDir() {
|
|
852
|
+
const preferredNode = resolvePreferredNodeBinary();
|
|
853
|
+
if (!preferredNode)
|
|
854
|
+
return null;
|
|
855
|
+
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
856
|
+
if (explicitNode && resolve7(explicitNode) === resolve7(preferredNode)) {
|
|
857
|
+
return preferredNode.endsWith("/bin/node") ? resolve7(preferredNode, "../..") : resolve7(preferredNode, "..");
|
|
858
|
+
}
|
|
859
|
+
try {
|
|
860
|
+
const realPath = realpathSync(preferredNode);
|
|
861
|
+
if (realPath.endsWith("/bin/node")) {
|
|
862
|
+
return resolve7(realPath, "../..");
|
|
863
|
+
}
|
|
864
|
+
return resolve7(realPath, "..");
|
|
865
|
+
} catch {
|
|
866
|
+
return resolve7(preferredNode, "..");
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function resolvePreferredNodeBinary() {
|
|
870
|
+
const candidates = [];
|
|
871
|
+
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
872
|
+
if (envNode) {
|
|
873
|
+
const explicit = resolve7(envNode);
|
|
874
|
+
if (existsSync4(explicit)) {
|
|
875
|
+
return explicit;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
const nvmBin = process.env.NVM_BIN?.trim();
|
|
879
|
+
if (nvmBin) {
|
|
880
|
+
candidates.push(resolve7(nvmBin, "node"));
|
|
881
|
+
}
|
|
882
|
+
const home = process.env.HOME?.trim();
|
|
883
|
+
if (home) {
|
|
884
|
+
const nvmVersionsDir = resolve7(home, ".nvm/versions/node");
|
|
885
|
+
if (existsSync4(nvmVersionsDir)) {
|
|
886
|
+
try {
|
|
887
|
+
const versionDirs = readdirSync(nvmVersionsDir).map((entry) => entry.trim()).filter((entry) => /^v\d+\.\d+\.\d+$/.test(entry)).sort((a, b) => Bun.semver.order(b.replace(/^v/, ""), a.replace(/^v/, "")));
|
|
888
|
+
for (const versionDir of versionDirs) {
|
|
889
|
+
candidates.push(resolve7(nvmVersionsDir, versionDir, "bin/node"));
|
|
890
|
+
}
|
|
891
|
+
} catch {}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
const whichNode = Bun.which("node");
|
|
895
|
+
if (whichNode) {
|
|
896
|
+
candidates.push(whichNode);
|
|
897
|
+
}
|
|
898
|
+
const deduped = uniq(candidates.map((candidate) => resolve7(candidate)));
|
|
899
|
+
const existing = deduped.filter((candidate) => existsSync4(candidate));
|
|
900
|
+
if (existing.length === 0) {
|
|
901
|
+
return null;
|
|
902
|
+
}
|
|
903
|
+
const stable = existing.find((candidate) => {
|
|
904
|
+
const major = inferNodeMajor(candidate);
|
|
905
|
+
return typeof major === "number" && major >= 18 && major <= 24;
|
|
906
|
+
});
|
|
907
|
+
if (stable) {
|
|
908
|
+
return stable;
|
|
909
|
+
}
|
|
910
|
+
return existing[0] ?? null;
|
|
911
|
+
}
|
|
912
|
+
function inferNodeMajor(nodeBinaryPath) {
|
|
913
|
+
const normalized = resolve7(nodeBinaryPath).replace(/\\/g, "/");
|
|
914
|
+
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
915
|
+
if (!match) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
const major = Number.parseInt(match[1], 10);
|
|
919
|
+
return Number.isFinite(major) ? major : null;
|
|
920
|
+
}
|
|
921
|
+
function normalizeExecutablePath(candidate) {
|
|
922
|
+
if (!candidate) {
|
|
923
|
+
return "";
|
|
924
|
+
}
|
|
925
|
+
const normalized = resolve7(candidate);
|
|
926
|
+
if (!existsSync4(normalized)) {
|
|
927
|
+
return "";
|
|
928
|
+
}
|
|
929
|
+
try {
|
|
930
|
+
return realpathSync(normalized);
|
|
931
|
+
} catch {
|
|
932
|
+
return normalized;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
936
|
+
const normalized = resolve7(candidate).replace(/\\/g, "/");
|
|
937
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// packages/runtime/src/control-plane/runtime/provisioning-env.ts
|
|
941
|
+
function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
942
|
+
const env = { ...baseEnv };
|
|
943
|
+
const realBash = baseEnv.RIG_REAL_BASH?.trim() || "/bin/bash";
|
|
944
|
+
const bunBinary = baseEnv.RIG_BUN_PATH?.trim() || resolveBunBinaryPath();
|
|
945
|
+
const bunDir = resolveBunInstallDir(bunBinary);
|
|
946
|
+
const claudeBinary = baseEnv.RIG_CLAUDE_PATH?.trim() || (() => {
|
|
947
|
+
try {
|
|
948
|
+
return resolveClaudeBinaryPath();
|
|
949
|
+
} catch {
|
|
950
|
+
return "";
|
|
951
|
+
}
|
|
952
|
+
})();
|
|
953
|
+
const claudeDir = claudeBinary ? (() => {
|
|
954
|
+
try {
|
|
955
|
+
return resolveClaudeInstallDir();
|
|
956
|
+
} catch {
|
|
957
|
+
return resolve8(claudeBinary, "..");
|
|
958
|
+
}
|
|
959
|
+
})() : "";
|
|
960
|
+
const nodeDir = resolveNodeInstallDir();
|
|
961
|
+
const realHome = baseEnv.HOME?.trim();
|
|
962
|
+
const inheritedPath = (baseEnv.PATH ?? "").split(delimiter).map((entry) => entry.trim()).filter(Boolean).filter((entry) => !entry.endsWith("/.rig/bin") && !entry.endsWith("/rig/tools"));
|
|
963
|
+
const pathEntries = [
|
|
964
|
+
`${bunDir}/bin`,
|
|
965
|
+
claudeDir,
|
|
966
|
+
nodeDir ? `${nodeDir}/bin` : "",
|
|
967
|
+
realHome ? resolve8(realHome, ".local/bin") : "",
|
|
968
|
+
realHome ? resolve8(realHome, ".cargo/bin") : "",
|
|
969
|
+
...inheritedPath,
|
|
970
|
+
"/usr/local/bin",
|
|
971
|
+
"/usr/local/sbin",
|
|
972
|
+
"/opt/homebrew/bin",
|
|
973
|
+
"/opt/homebrew/sbin",
|
|
974
|
+
"/usr/bin",
|
|
975
|
+
"/bin",
|
|
976
|
+
"/usr/sbin",
|
|
977
|
+
"/sbin"
|
|
978
|
+
].filter(Boolean);
|
|
979
|
+
env.BASH = realBash;
|
|
980
|
+
env.SHELL = baseEnv.SHELL?.trim() || realBash;
|
|
981
|
+
env.PATH = [...new Set(pathEntries)].join(delimiter);
|
|
982
|
+
env.RIG_BUN_PATH = bunBinary;
|
|
983
|
+
if (claudeBinary) {
|
|
984
|
+
env.RIG_CLAUDE_PATH = claudeBinary;
|
|
985
|
+
}
|
|
986
|
+
env.PYTHON = env.PYTHON?.trim() || "python3";
|
|
987
|
+
const nodeGypPath = Bun.which("node-gyp");
|
|
988
|
+
if (nodeGypPath) {
|
|
989
|
+
env.npm_config_node_gyp = nodeGypPath;
|
|
990
|
+
}
|
|
991
|
+
delete env.RIG_BASH_ACTIVE;
|
|
992
|
+
delete env.RIG_BASH_MODE;
|
|
993
|
+
return env;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// packages/runtime/src/control-plane/native/git-ops.ts
|
|
997
|
+
var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
|
|
998
|
+
"changed-files.txt",
|
|
999
|
+
"contract-changes.md",
|
|
1000
|
+
"decision-log.md",
|
|
1001
|
+
"git-state.txt",
|
|
1002
|
+
"next-actions.md",
|
|
1003
|
+
"pr-state.json",
|
|
1004
|
+
"task-result.json",
|
|
1005
|
+
"validation-summary.json"
|
|
1006
|
+
]);
|
|
1007
|
+
|
|
1008
|
+
// packages/runtime/src/control-plane/provider/runtime-instructions.ts
|
|
1009
|
+
var CLAUDE_ROUTER_TOOL_NAMES = [
|
|
1010
|
+
"`mcp__rig_runtime_tools__read`",
|
|
1011
|
+
"`mcp__rig_runtime_tools__write`",
|
|
1012
|
+
"`mcp__rig_runtime_tools__edit`",
|
|
1013
|
+
"`mcp__rig_runtime_tools__glob`",
|
|
1014
|
+
"`mcp__rig_runtime_tools__grep`"
|
|
1015
|
+
].join(", ");
|
|
1016
|
+
var CODEX_DYNAMIC_TOOL_NAMES = [
|
|
1017
|
+
"`shell`",
|
|
1018
|
+
"`read`",
|
|
1019
|
+
"`write`",
|
|
1020
|
+
"`edit`",
|
|
1021
|
+
"`glob`",
|
|
1022
|
+
"`grep`"
|
|
1023
|
+
].join(", ");
|
|
1024
|
+
|
|
1025
|
+
// packages/runtime/src/control-plane/native/task-ops.ts
|
|
1026
|
+
var BUILD_CONFIG = readBuildConfig();
|
|
1027
|
+
var BAKED_INFO_OUTPUT = BUILD_CONFIG.AGENT_INFO_OUTPUT ?? "";
|
|
1028
|
+
var BAKED_DEPS_OUTPUT = BUILD_CONFIG.AGENT_DEPS_OUTPUT ?? "";
|
|
1029
|
+
var BAKED_STATUS_OUTPUT = BUILD_CONFIG.AGENT_STATUS_OUTPUT ?? "";
|
|
1030
|
+
var REOPENABLE_TASK_STATUSES = new Set(["completed", "cancelled", "blocked"]);
|
|
1031
|
+
var GENERATED_TASK_ARTIFACT_FILES = new Set([
|
|
1032
|
+
"changed-files.txt",
|
|
1033
|
+
"decision-log.md",
|
|
1034
|
+
"next-actions.md",
|
|
1035
|
+
"task-result.json",
|
|
1036
|
+
"validation-summary.json",
|
|
1037
|
+
"review-feedback.md",
|
|
1038
|
+
"review-state.json",
|
|
1039
|
+
"review-status.txt",
|
|
1040
|
+
"review-greptile-raw.json",
|
|
1041
|
+
"pr-state.json",
|
|
1042
|
+
"git-state.txt"
|
|
1043
|
+
]);
|
|
1044
|
+
|
|
1045
|
+
// packages/runtime/src/control-plane/runtime/isolation/index.ts
|
|
1046
|
+
init_layout();
|
|
1047
|
+
|
|
1048
|
+
// packages/runtime/src/control-plane/runtime/overlay.ts
|
|
1049
|
+
init_layout();
|
|
1050
|
+
|
|
1051
|
+
// packages/runtime/src/control-plane/runtime/isolation/shared.ts
|
|
1052
|
+
var generatedCredentialFiles = new Set;
|
|
1053
|
+
|
|
1054
|
+
// packages/runtime/src/control-plane/runtime/isolation/home.ts
|
|
1055
|
+
var GITHUB_KNOWN_HOSTS = [
|
|
1056
|
+
"github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
|
|
1057
|
+
"github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
|
|
1058
|
+
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
1059
|
+
].join(`
|
|
1060
|
+
`);
|
|
1061
|
+
|
|
1062
|
+
// packages/runtime/src/control-plane/runtime/tooling/claude-router.ts
|
|
1063
|
+
if (false) {}
|
|
1064
|
+
// packages/runtime/src/control-plane/runtime/isolation/discovery.ts
|
|
1065
|
+
init_layout();
|
|
1066
|
+
|
|
1067
|
+
// packages/runtime/src/control-plane/runtime/tooling/claude-router-binary.ts
|
|
1068
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
1069
|
+
import { dirname as dirname7, resolve as resolve9 } from "path";
|
|
1070
|
+
var sharedRouterOutputDir = resolve9(tmpdir5(), "rig-native");
|
|
1071
|
+
var sharedRouterOutputPath = resolve9(sharedRouterOutputDir, `rig-tool-router-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
|
|
1072
|
+
// packages/runtime/src/control-plane/runtime/isolation/toolchain.ts
|
|
1073
|
+
async function buildBinary(entrypoint, outputPath, cwd, defines) {
|
|
1074
|
+
await buildRuntimeBinary({
|
|
1075
|
+
sourcePath: entrypoint,
|
|
1076
|
+
outputPath,
|
|
1077
|
+
cwd,
|
|
1078
|
+
define: defines,
|
|
1079
|
+
env: runtimeProvisioningEnv()
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// packages/runtime/src/control-plane/runtime/guard.ts
|
|
1084
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
1085
|
+
import { existsSync as existsSync5, readFileSync, statSync as statSync2 } from "fs";
|
|
1086
|
+
import { resolve as resolve10 } from "path";
|
|
1087
|
+
|
|
1088
|
+
// packages/runtime/src/control-plane/runtime/guard-types.ts
|
|
1089
|
+
var POLICY_VERSION = 1;
|
|
1090
|
+
|
|
1091
|
+
// packages/runtime/src/control-plane/runtime/guard.ts
|
|
1092
|
+
var DEFAULT_SCOPE = {
|
|
1093
|
+
fail_closed: true,
|
|
1094
|
+
harness_paths_exempt: true,
|
|
1095
|
+
runtime_paths_exempt: true
|
|
1096
|
+
};
|
|
1097
|
+
var DEFAULT_SANDBOX = {
|
|
1098
|
+
mode: "enforce",
|
|
1099
|
+
network: true,
|
|
1100
|
+
read_deny: [],
|
|
1101
|
+
write_allow_from_runtime: true
|
|
1102
|
+
};
|
|
1103
|
+
var DEFAULT_ISOLATION = {
|
|
1104
|
+
default_mode: "worktree",
|
|
1105
|
+
repo_symlink_fallback: false,
|
|
1106
|
+
strict_provisioning: true,
|
|
1107
|
+
fail_closed_on_provision_error: true
|
|
1108
|
+
};
|
|
1109
|
+
var DEFAULT_COMPLETION = {
|
|
1110
|
+
derive_checks_from_scope: true,
|
|
1111
|
+
checks: [],
|
|
1112
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
1113
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
1114
|
+
};
|
|
1115
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
1116
|
+
deps: {
|
|
1117
|
+
monorepo_install: false,
|
|
1118
|
+
hp_next_install: false
|
|
1119
|
+
},
|
|
1120
|
+
plugins_require_binaries: true
|
|
1121
|
+
};
|
|
1122
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
1123
|
+
enabled: true
|
|
1124
|
+
};
|
|
1125
|
+
function defaultPolicy() {
|
|
1126
|
+
return {
|
|
1127
|
+
version: POLICY_VERSION,
|
|
1128
|
+
mode: "enforce",
|
|
1129
|
+
scope: { ...DEFAULT_SCOPE },
|
|
1130
|
+
rules: [],
|
|
1131
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
1132
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
1133
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
1134
|
+
runtime_image: {
|
|
1135
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
1136
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
1137
|
+
},
|
|
1138
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
var policyCache = null;
|
|
1142
|
+
var policyCachePath = null;
|
|
1143
|
+
var seededPolicyConfig = null;
|
|
1144
|
+
var compiledRegexCache = new Map;
|
|
1145
|
+
function loadPolicy(projectRoot) {
|
|
1146
|
+
if (seededPolicyConfig) {
|
|
1147
|
+
return seededPolicyConfig;
|
|
1148
|
+
}
|
|
1149
|
+
const configPath = resolve10(projectRoot, "rig/policy/policy.json");
|
|
1150
|
+
if (!existsSync5(configPath)) {
|
|
1151
|
+
return defaultPolicy();
|
|
1152
|
+
}
|
|
1153
|
+
let mtimeMs;
|
|
1154
|
+
try {
|
|
1155
|
+
mtimeMs = statSync2(configPath).mtimeMs;
|
|
1156
|
+
} catch {
|
|
1157
|
+
return defaultPolicy();
|
|
1158
|
+
}
|
|
1159
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
1160
|
+
return policyCache.config;
|
|
1161
|
+
}
|
|
1162
|
+
let parsed;
|
|
1163
|
+
try {
|
|
1164
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1165
|
+
} catch {
|
|
1166
|
+
return defaultPolicy();
|
|
1167
|
+
}
|
|
1168
|
+
const config = mergeWithDefaults(parsed);
|
|
1169
|
+
policyCache = { mtimeMs, config };
|
|
1170
|
+
policyCachePath = configPath;
|
|
1171
|
+
return config;
|
|
1172
|
+
}
|
|
1173
|
+
function mergeWithDefaults(parsed) {
|
|
1174
|
+
const base = defaultPolicy();
|
|
1175
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
1176
|
+
base.mode = parsed.mode;
|
|
1177
|
+
}
|
|
1178
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
1179
|
+
const s = parsed.scope;
|
|
1180
|
+
if (typeof s.fail_closed === "boolean")
|
|
1181
|
+
base.scope.fail_closed = s.fail_closed;
|
|
1182
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
1183
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
1184
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
1185
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
1186
|
+
}
|
|
1187
|
+
if (Array.isArray(parsed.rules)) {
|
|
1188
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
1189
|
+
}
|
|
1190
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
1191
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
1192
|
+
}
|
|
1193
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
1194
|
+
const sb = parsed.sandbox;
|
|
1195
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
1196
|
+
base.sandbox.mode = sb.mode;
|
|
1197
|
+
if (typeof sb.network === "boolean")
|
|
1198
|
+
base.sandbox.network = sb.network;
|
|
1199
|
+
if (Array.isArray(sb.read_deny))
|
|
1200
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
1201
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
1202
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
1203
|
+
}
|
|
1204
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
1205
|
+
const iso = parsed.isolation;
|
|
1206
|
+
if (iso.default_mode === "worktree")
|
|
1207
|
+
base.isolation.default_mode = iso.default_mode;
|
|
1208
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
1209
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
1210
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
1211
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
1212
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
1213
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
1214
|
+
}
|
|
1215
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
1216
|
+
const comp = parsed.completion;
|
|
1217
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
1218
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
1219
|
+
if (Array.isArray(comp.checks))
|
|
1220
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
1221
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
1222
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
1223
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
1224
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
1225
|
+
}
|
|
1226
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
1227
|
+
const runtimeImage = parsed.runtime_image;
|
|
1228
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
1229
|
+
const deps = runtimeImage.deps;
|
|
1230
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
1231
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
1232
|
+
}
|
|
1233
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
1234
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
1238
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
1242
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
1243
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
1244
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return base;
|
|
1248
|
+
}
|
|
1249
|
+
function isValidMode(value) {
|
|
1250
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
1251
|
+
}
|
|
1252
|
+
function isValidRule(value) {
|
|
1253
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1254
|
+
return false;
|
|
1255
|
+
const r = value;
|
|
1256
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
1257
|
+
}
|
|
1258
|
+
function migrateLegacyDeny(deny) {
|
|
1259
|
+
const rules = [];
|
|
1260
|
+
for (const entry of deny) {
|
|
1261
|
+
if (typeof entry.id !== "string")
|
|
1262
|
+
continue;
|
|
1263
|
+
const match = {};
|
|
1264
|
+
if (typeof entry.pattern === "string")
|
|
1265
|
+
match.pattern = entry.pattern;
|
|
1266
|
+
if (typeof entry.regex === "string")
|
|
1267
|
+
match.regex = entry.regex;
|
|
1268
|
+
if (!match.pattern && !match.regex)
|
|
1269
|
+
continue;
|
|
1270
|
+
rules.push({
|
|
1271
|
+
id: entry.id,
|
|
1272
|
+
category: "command",
|
|
1273
|
+
match,
|
|
1274
|
+
action: "block",
|
|
1275
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
return rules;
|
|
1279
|
+
}
|
|
1280
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
1281
|
+
return rules.map((rule) => {
|
|
1282
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
1283
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
1284
|
+
return {
|
|
1285
|
+
...rule,
|
|
1286
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
1287
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
1288
|
+
};
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
function getRegexUnsafeReason(pattern) {
|
|
1292
|
+
if (pattern.length > 512) {
|
|
1293
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
1294
|
+
}
|
|
1295
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
1296
|
+
return "pattern uses backreferences";
|
|
1297
|
+
}
|
|
1298
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
1299
|
+
return "pattern contains nested quantifiers";
|
|
1300
|
+
}
|
|
1301
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
1302
|
+
return "pattern contains nested broad quantifiers";
|
|
1303
|
+
}
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
1307
|
+
const cached = compiledRegexCache.get(pattern);
|
|
1308
|
+
if (cached !== undefined) {
|
|
1309
|
+
return cached ?? undefined;
|
|
1310
|
+
}
|
|
1311
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
1312
|
+
if (unsafeReason) {
|
|
1313
|
+
if (logOnFailure) {
|
|
1314
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
1315
|
+
}
|
|
1316
|
+
compiledRegexCache.set(pattern, null);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
try {
|
|
1320
|
+
const compiled = new RegExp(pattern);
|
|
1321
|
+
compiledRegexCache.set(pattern, compiled);
|
|
1322
|
+
return compiled;
|
|
1323
|
+
} catch (error) {
|
|
1324
|
+
if (logOnFailure) {
|
|
1325
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1326
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
1327
|
+
}
|
|
1328
|
+
compiledRegexCache.set(pattern, null);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
function matchRule(rule, input) {
|
|
1333
|
+
const { match } = rule;
|
|
1334
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
if (match.regex) {
|
|
1338
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
1339
|
+
if (!compiled) {
|
|
1340
|
+
return false;
|
|
1341
|
+
}
|
|
1342
|
+
try {
|
|
1343
|
+
return compiled.test(input);
|
|
1344
|
+
} catch {
|
|
1345
|
+
return false;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
1351
|
+
if (!rule.unless)
|
|
1352
|
+
return false;
|
|
1353
|
+
if (rule.unless.regex) {
|
|
1354
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
1355
|
+
if (!compiled) {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
try {
|
|
1359
|
+
if (compiled.test(command))
|
|
1360
|
+
return true;
|
|
1361
|
+
} catch {}
|
|
1362
|
+
}
|
|
1363
|
+
if (rule.unless.task_in && taskId) {
|
|
1364
|
+
if (rule.unless.task_in.includes(taskId))
|
|
1365
|
+
return true;
|
|
1366
|
+
}
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
function resolveAction(mode, matched) {
|
|
1370
|
+
if (matched.length === 0)
|
|
1371
|
+
return "allow";
|
|
1372
|
+
if (mode === "off")
|
|
1373
|
+
return "allow";
|
|
1374
|
+
if (mode === "observe")
|
|
1375
|
+
return "warn";
|
|
1376
|
+
return "block";
|
|
1377
|
+
}
|
|
1378
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
1379
|
+
if (rawPath.startsWith("/"))
|
|
1380
|
+
return resolve10(rawPath);
|
|
1381
|
+
return resolve10(projectRoot, rawPath);
|
|
1382
|
+
}
|
|
1383
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
1384
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
1385
|
+
const managedRoots = [
|
|
1386
|
+
resolve10(projectRoot, "rig"),
|
|
1387
|
+
resolve10(projectRoot, ".rig"),
|
|
1388
|
+
resolve10(projectRoot, "artifacts")
|
|
1389
|
+
];
|
|
1390
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
1391
|
+
}
|
|
1392
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
1393
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
1394
|
+
if (taskWorkspace) {
|
|
1395
|
+
const workspaceRigRoot = resolve10(taskWorkspace, ".rig");
|
|
1396
|
+
const workspaceArtifactsRoot = resolve10(taskWorkspace, "artifacts");
|
|
1397
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
1398
|
+
return true;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
const runtimeRoot = resolve10(projectRoot, ".rig/runtime/agents");
|
|
1402
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
1403
|
+
}
|
|
1404
|
+
function isTestFile(path) {
|
|
1405
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
1406
|
+
}
|
|
1407
|
+
function evaluate(context) {
|
|
1408
|
+
const policy = loadPolicy(context.projectRoot);
|
|
1409
|
+
switch (context.evaluation.type) {
|
|
1410
|
+
case "tool-call":
|
|
1411
|
+
return evaluateToolCall(policy, context);
|
|
1412
|
+
case "command":
|
|
1413
|
+
return evaluateCommand(policy, context);
|
|
1414
|
+
case "content-write":
|
|
1415
|
+
return evaluateContent(policy, context);
|
|
1416
|
+
case "file-access":
|
|
1417
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
1421
|
+
const allowed = () => ({
|
|
1422
|
+
allowed: true,
|
|
1423
|
+
matchedRules: [],
|
|
1424
|
+
action: "allow",
|
|
1425
|
+
failClosed: false
|
|
1426
|
+
});
|
|
1427
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
1428
|
+
return allowed();
|
|
1429
|
+
}
|
|
1430
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
1431
|
+
return allowed();
|
|
1432
|
+
}
|
|
1433
|
+
if (!context.taskId) {
|
|
1434
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
1435
|
+
return {
|
|
1436
|
+
allowed: false,
|
|
1437
|
+
matchedRules: [],
|
|
1438
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
1439
|
+
failClosed: true
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
return allowed();
|
|
1443
|
+
}
|
|
1444
|
+
const scopes = context.taskScopes || [];
|
|
1445
|
+
if (scopes.length === 0) {
|
|
1446
|
+
return allowed();
|
|
1447
|
+
}
|
|
1448
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
1449
|
+
const absPath = resolve10(filePath);
|
|
1450
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
1451
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
1452
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
1453
|
+
return {
|
|
1454
|
+
allowed: policy.mode !== "enforce",
|
|
1455
|
+
matchedRules: matched2,
|
|
1456
|
+
action: resolveAction(policy.mode, matched2),
|
|
1457
|
+
failClosed: false
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
1462
|
+
let normalizedPath = filePath;
|
|
1463
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
1464
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
1465
|
+
}
|
|
1466
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
1467
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
1468
|
+
return allowed();
|
|
1469
|
+
}
|
|
1470
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
1471
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
1472
|
+
return {
|
|
1473
|
+
allowed: policy.mode !== "enforce",
|
|
1474
|
+
matchedRules: matched,
|
|
1475
|
+
action: resolveAction(policy.mode, matched),
|
|
1476
|
+
failClosed: false
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function evaluateCommand(policy, context) {
|
|
1480
|
+
const evaluation = context.evaluation;
|
|
1481
|
+
if (evaluation.type !== "command") {
|
|
1482
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1483
|
+
}
|
|
1484
|
+
const command = evaluation.command;
|
|
1485
|
+
const matchedRules = [];
|
|
1486
|
+
for (const rule of policy.rules) {
|
|
1487
|
+
if (rule.category !== "command")
|
|
1488
|
+
continue;
|
|
1489
|
+
if (!matchRule(rule, command))
|
|
1490
|
+
continue;
|
|
1491
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
1492
|
+
continue;
|
|
1493
|
+
matchedRules.push({
|
|
1494
|
+
id: rule.id,
|
|
1495
|
+
category: rule.category,
|
|
1496
|
+
description: rule.description,
|
|
1497
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
const writeTarget = extractWriteTarget(command);
|
|
1501
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
1502
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
1503
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
1504
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
1508
|
+
return {
|
|
1509
|
+
allowed: action !== "block",
|
|
1510
|
+
matchedRules,
|
|
1511
|
+
action,
|
|
1512
|
+
failClosed: false
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
function extractWriteTarget(command) {
|
|
1516
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
1517
|
+
if (redirect?.[1])
|
|
1518
|
+
return redirect[1];
|
|
1519
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
1520
|
+
if (tee?.[2])
|
|
1521
|
+
return tee[2];
|
|
1522
|
+
return "";
|
|
1523
|
+
}
|
|
1524
|
+
function evaluateContent(policy, context) {
|
|
1525
|
+
const evaluation = context.evaluation;
|
|
1526
|
+
if (evaluation.type !== "content-write") {
|
|
1527
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1528
|
+
}
|
|
1529
|
+
const { content, file_path } = evaluation;
|
|
1530
|
+
const matchedRules = [];
|
|
1531
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
1532
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
1533
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
1534
|
+
}
|
|
1535
|
+
for (const rule of policy.rules) {
|
|
1536
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
1537
|
+
continue;
|
|
1538
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
1539
|
+
continue;
|
|
1540
|
+
if (!matchRule(rule, content))
|
|
1541
|
+
continue;
|
|
1542
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
1543
|
+
continue;
|
|
1544
|
+
matchedRules.push({
|
|
1545
|
+
id: rule.id,
|
|
1546
|
+
category: rule.category,
|
|
1547
|
+
description: rule.description,
|
|
1548
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
1552
|
+
return {
|
|
1553
|
+
allowed: action !== "block",
|
|
1554
|
+
matchedRules,
|
|
1555
|
+
action,
|
|
1556
|
+
failClosed: false
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
function evaluateToolCall(policy, context) {
|
|
1560
|
+
const evaluation = context.evaluation;
|
|
1561
|
+
if (evaluation.type !== "tool-call") {
|
|
1562
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
1563
|
+
}
|
|
1564
|
+
const { tool_name, tool_input } = evaluation;
|
|
1565
|
+
const allMatched = [];
|
|
1566
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
1567
|
+
for (const fp of filePaths) {
|
|
1568
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
1569
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
1570
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
1571
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
const content = extractContentFromToolInput(tool_input);
|
|
1575
|
+
if (content) {
|
|
1576
|
+
const filePath = filePaths[0] || "";
|
|
1577
|
+
const contentContext = {
|
|
1578
|
+
...context,
|
|
1579
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
1580
|
+
};
|
|
1581
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
1582
|
+
for (const rule of contentPolicy.rules) {
|
|
1583
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
1584
|
+
continue;
|
|
1585
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
1586
|
+
continue;
|
|
1587
|
+
if (!matchRule(rule, content))
|
|
1588
|
+
continue;
|
|
1589
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
1590
|
+
continue;
|
|
1591
|
+
allMatched.push({
|
|
1592
|
+
id: rule.id,
|
|
1593
|
+
category: rule.category,
|
|
1594
|
+
description: rule.description,
|
|
1595
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (tool_name === "Bash") {
|
|
1600
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
1601
|
+
if (command) {
|
|
1602
|
+
const cmdContext = {
|
|
1603
|
+
...context,
|
|
1604
|
+
evaluation: { type: "command", command }
|
|
1605
|
+
};
|
|
1606
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
1607
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
1608
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
const seen = new Set;
|
|
1613
|
+
const deduplicated = [];
|
|
1614
|
+
for (const rule of allMatched) {
|
|
1615
|
+
if (!seen.has(rule.id)) {
|
|
1616
|
+
seen.add(rule.id);
|
|
1617
|
+
deduplicated.push(rule);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
1621
|
+
return {
|
|
1622
|
+
allowed: action !== "block",
|
|
1623
|
+
matchedRules: deduplicated,
|
|
1624
|
+
action,
|
|
1625
|
+
failClosed: false
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
function isWriteTool(toolName) {
|
|
1629
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
1630
|
+
}
|
|
1631
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
1632
|
+
const paths = [];
|
|
1633
|
+
const add = (value) => {
|
|
1634
|
+
if (typeof value === "string" && value.trim()) {
|
|
1635
|
+
paths.push(value.trim());
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
1639
|
+
add(input.file_path);
|
|
1640
|
+
add(input.path);
|
|
1641
|
+
} else if (toolName === "Glob") {
|
|
1642
|
+
add(input.path);
|
|
1643
|
+
} else if (toolName === "Grep") {
|
|
1644
|
+
add(input.path);
|
|
1645
|
+
} else {
|
|
1646
|
+
add(input.file_path);
|
|
1647
|
+
add(input.path);
|
|
1648
|
+
}
|
|
1649
|
+
return paths;
|
|
1650
|
+
}
|
|
1651
|
+
function extractContentFromToolInput(input) {
|
|
1652
|
+
if (typeof input.content === "string")
|
|
1653
|
+
return input.content;
|
|
1654
|
+
if (typeof input.new_string === "string")
|
|
1655
|
+
return input.new_string;
|
|
1656
|
+
return "";
|
|
1657
|
+
}
|
|
1658
|
+
function loadRuntimeImageConfig(projectRoot) {
|
|
1659
|
+
return loadPolicy(projectRoot).runtime_image ?? {
|
|
1660
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
1661
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
var guardHotPathPrimed = false;
|
|
1665
|
+
function primeGuardHotPaths() {
|
|
1666
|
+
if (guardHotPathPrimed) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
guardHotPathPrimed = true;
|
|
1670
|
+
try {
|
|
1671
|
+
optimizeNextInvocation(matchRule);
|
|
1672
|
+
optimizeNextInvocation(evaluate);
|
|
1673
|
+
} catch {}
|
|
1674
|
+
}
|
|
1675
|
+
primeGuardHotPaths();
|
|
1676
|
+
|
|
1677
|
+
// packages/runtime/src/control-plane/runtime/sandbox/backend.ts
|
|
1678
|
+
init_utils();
|
|
1679
|
+
// packages/runtime/src/control-plane/runtime/isolation/runner.ts
|
|
1680
|
+
init_layout();
|
|
1681
|
+
// packages/runtime/src/control-plane/runtime/image.ts
|
|
1682
|
+
var HASH_RESULT_SIZE = 40;
|
|
1683
|
+
var nativeRuntimeLibrary2 = null;
|
|
1684
|
+
function sha256Hex2(input) {
|
|
1685
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
1686
|
+
hasher.update(input);
|
|
1687
|
+
return hasher.digest("hex");
|
|
1688
|
+
}
|
|
1689
|
+
function hashFile(filePath) {
|
|
1690
|
+
if (!existsSync6(filePath)) {
|
|
1691
|
+
return "";
|
|
1692
|
+
}
|
|
1693
|
+
return hashPathWithNative("runtime_hash_file", filePath);
|
|
1694
|
+
}
|
|
1695
|
+
function hashDirectory(dir) {
|
|
1696
|
+
if (!existsSync6(dir)) {
|
|
1697
|
+
return sha256Hex2("(empty)");
|
|
1698
|
+
}
|
|
1699
|
+
return hashPathWithNative("runtime_hash_tree", dir);
|
|
1700
|
+
}
|
|
1701
|
+
function hashDirectories(dirs) {
|
|
1702
|
+
const combined = new Bun.CryptoHasher("sha256");
|
|
1703
|
+
for (const { label, path } of dirs) {
|
|
1704
|
+
const dirHash = hashDirectory(path);
|
|
1705
|
+
combined.update(`${label}:${dirHash}
|
|
1706
|
+
`);
|
|
1707
|
+
}
|
|
1708
|
+
return combined.digest("hex");
|
|
1709
|
+
}
|
|
1710
|
+
async function probeVersion(args) {
|
|
1711
|
+
try {
|
|
1712
|
+
const proc = Bun.spawn(args, { stdout: "pipe", stderr: "pipe" });
|
|
1713
|
+
const [exitCode, stdout] = await Promise.all([
|
|
1714
|
+
proc.exited,
|
|
1715
|
+
new Response(proc.stdout).text()
|
|
1716
|
+
]);
|
|
1717
|
+
if (exitCode === 0) {
|
|
1718
|
+
return stdout.trim().split(`
|
|
1719
|
+
`)[0] ?? "";
|
|
1720
|
+
}
|
|
1721
|
+
return "";
|
|
1722
|
+
} catch {
|
|
1723
|
+
return "";
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
async function computeRuntimeImageFingerprint(projectRoot) {
|
|
1727
|
+
return runFingerprintSidecar(projectRoot);
|
|
1728
|
+
}
|
|
1729
|
+
async function computeRuntimeImageFingerprintNative(projectRoot) {
|
|
1730
|
+
const [nodeVersion, claudeVersion] = await Promise.all([
|
|
1731
|
+
probeVersion(["node", "--version"]),
|
|
1732
|
+
probeVersion(["claude", "--version"])
|
|
1733
|
+
]);
|
|
1734
|
+
const runtimeDir = resolve11(projectRoot, "packages", "runtime");
|
|
1735
|
+
const cliDir = resolve11(projectRoot, "packages", "cli");
|
|
1736
|
+
const serverDir = resolve11(projectRoot, "packages", "server");
|
|
1737
|
+
const runtimeCodeHash = hashDirectories([
|
|
1738
|
+
{ label: "packages/runtime", path: runtimeDir },
|
|
1739
|
+
{ label: "packages/cli", path: cliDir },
|
|
1740
|
+
{ label: "packages/server", path: serverDir }
|
|
1741
|
+
]);
|
|
1742
|
+
const policyPath = resolve11(projectRoot, "rig/policy/policy.json");
|
|
1743
|
+
const policyHash = hashFile(policyPath);
|
|
1744
|
+
const pluginsDir = resolve11(projectRoot, "rig/plugins");
|
|
1745
|
+
const pluginsHash = hashDirectory(pluginsDir);
|
|
1746
|
+
const baseLockfiles = [
|
|
1747
|
+
"bun.lock",
|
|
1748
|
+
"bun.lockb",
|
|
1749
|
+
"package-lock.json",
|
|
1750
|
+
"package.json",
|
|
1751
|
+
"tsconfig.json"
|
|
1752
|
+
];
|
|
1753
|
+
const extra = process.env.RIG_RUNTIME_IMAGE_EXTRA_LOCKFILES?.trim();
|
|
1754
|
+
const lockfileCandidates = extra ? [...baseLockfiles, ...extra.split(",").map((p) => p.trim()).filter(Boolean)] : baseLockfiles;
|
|
1755
|
+
const lockfileHashes = {};
|
|
1756
|
+
for (const relPath of lockfileCandidates) {
|
|
1757
|
+
const fullPath = resolve11(projectRoot, relPath);
|
|
1758
|
+
if (existsSync6(fullPath)) {
|
|
1759
|
+
lockfileHashes[relPath] = hashFile(fullPath);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
return {
|
|
1763
|
+
platform: process.platform,
|
|
1764
|
+
bunVersion: Bun.version,
|
|
1765
|
+
nodeVersion,
|
|
1766
|
+
claudeVersion,
|
|
1767
|
+
runtimeCodeHash,
|
|
1768
|
+
policyHash,
|
|
1769
|
+
pluginsHash,
|
|
1770
|
+
lockfileHashes
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
async function runFingerprintSidecar(projectRoot) {
|
|
1774
|
+
const sidecarPath = resolveRuntimeSourceScriptPath(projectRoot, "image-fingerprint-sidecar.ts");
|
|
1775
|
+
const bunCli = resolveBunCliInvocation();
|
|
1776
|
+
const proc = Bun.spawn([bunCli.command, sidecarPath, "--project-root", projectRoot], {
|
|
1777
|
+
cwd: projectRoot,
|
|
1778
|
+
env: {
|
|
1779
|
+
...process.env,
|
|
1780
|
+
...bunCli.env
|
|
1781
|
+
},
|
|
1782
|
+
stdout: "pipe",
|
|
1783
|
+
stderr: "pipe"
|
|
1784
|
+
});
|
|
1785
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
1786
|
+
proc.exited,
|
|
1787
|
+
new Response(proc.stdout).text(),
|
|
1788
|
+
new Response(proc.stderr).text()
|
|
1789
|
+
]);
|
|
1790
|
+
if (exitCode !== 0) {
|
|
1791
|
+
throw new Error(stderr.trim() || stdout.trim() || `runtime image sidecar exited with ${exitCode}`);
|
|
1792
|
+
}
|
|
1793
|
+
return JSON.parse(stdout);
|
|
1794
|
+
}
|
|
1795
|
+
function resolveRuntimeSourceScriptPath(projectRoot, fileName) {
|
|
1796
|
+
const hostRoots = [
|
|
1797
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
1798
|
+
process.env.PROJECT_RIG_ROOT?.trim(),
|
|
1799
|
+
projectRoot.trim()
|
|
1800
|
+
].filter((value) => Boolean(value));
|
|
1801
|
+
for (const root of hostRoots) {
|
|
1802
|
+
const candidate = resolve11(root, "packages/runtime/src/control-plane/runtime", fileName);
|
|
1803
|
+
if (existsSync6(candidate)) {
|
|
1804
|
+
return candidate;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return resolve11(import.meta.dir, fileName);
|
|
1808
|
+
}
|
|
1809
|
+
function resolveBunCliInvocation() {
|
|
1810
|
+
if (process.env.RIG_BUN_PATH?.trim()) {
|
|
1811
|
+
return {
|
|
1812
|
+
command: process.env.RIG_BUN_PATH.trim(),
|
|
1813
|
+
env: {}
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
try {
|
|
1817
|
+
const systemBun = resolveBunBinaryPath();
|
|
1818
|
+
return {
|
|
1819
|
+
command: systemBun,
|
|
1820
|
+
env: {}
|
|
1821
|
+
};
|
|
1822
|
+
} catch {}
|
|
1823
|
+
if (process.execPath?.trim()) {
|
|
1824
|
+
return {
|
|
1825
|
+
command: process.execPath,
|
|
1826
|
+
env: { BUN_BE_BUN: "1" }
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
return { command: "bun", env: {} };
|
|
1830
|
+
}
|
|
1831
|
+
function hashPathWithNative(symbol, path) {
|
|
1832
|
+
const pathBuffer = Buffer.from(path, "utf8");
|
|
1833
|
+
const runtimeLibrary = getNativeRuntimeLibrary();
|
|
1834
|
+
const resultPtr = runtimeLibrary.symbols[symbol](Number(ptr3(pathBuffer)), pathBuffer.byteLength);
|
|
1835
|
+
if (!resultPtr) {
|
|
1836
|
+
throw new Error(`${symbol} returned null for ${path}`);
|
|
1837
|
+
}
|
|
1838
|
+
try {
|
|
1839
|
+
const view = viewAt(resultPtr, HASH_RESULT_SIZE);
|
|
1840
|
+
const error = readError(view, 24, 32);
|
|
1841
|
+
if (error) {
|
|
1842
|
+
throw new Error(`${symbol} failed: ${error}`);
|
|
1843
|
+
}
|
|
1844
|
+
return readString(readU64(view, 8), readU64(view, 16));
|
|
1845
|
+
} finally {
|
|
1846
|
+
runtimeLibrary.symbols.snapshot_release(resultPtr);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function getNativeRuntimeLibrary() {
|
|
1850
|
+
if (nativeRuntimeLibrary2) {
|
|
1851
|
+
return nativeRuntimeLibrary2;
|
|
1852
|
+
}
|
|
1853
|
+
nativeRuntimeLibrary2 = requireNativeRuntimeLibrary("runtime image hashing");
|
|
1854
|
+
return nativeRuntimeLibrary2;
|
|
1855
|
+
}
|
|
1856
|
+
function viewAt(pointer, size) {
|
|
1857
|
+
const buffer = toBuffer2(pointer, 0, size);
|
|
1858
|
+
return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
1859
|
+
}
|
|
1860
|
+
function readString(pointer, length) {
|
|
1861
|
+
if (!pointer || length === 0) {
|
|
1862
|
+
return "";
|
|
1863
|
+
}
|
|
1864
|
+
return Buffer.from(toBuffer2(pointer, 0, length)).toString("utf8");
|
|
1865
|
+
}
|
|
1866
|
+
function readU64(view, offset) {
|
|
1867
|
+
return Number(view.getBigUint64(offset, true));
|
|
1868
|
+
}
|
|
1869
|
+
function readError(view, pointerOffset, lengthOffset) {
|
|
1870
|
+
const errorPointer = readU64(view, pointerOffset);
|
|
1871
|
+
const errorLength = readU64(view, lengthOffset);
|
|
1872
|
+
if (!errorPointer || !errorLength) {
|
|
1873
|
+
return null;
|
|
1874
|
+
}
|
|
1875
|
+
return readString(errorPointer, errorLength);
|
|
1876
|
+
}
|
|
1877
|
+
function computeRuntimeImageId(fp) {
|
|
1878
|
+
const serialized = JSON.stringify(fp);
|
|
1879
|
+
return sha256Hex2(serialized).slice(0, 16);
|
|
1880
|
+
}
|
|
1881
|
+
var LOCK_STALE_MS = 10 * 60 * 1000;
|
|
1882
|
+
function isPidAlive(pid) {
|
|
1883
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
1884
|
+
return false;
|
|
1885
|
+
}
|
|
1886
|
+
try {
|
|
1887
|
+
process.kill(pid, 0);
|
|
1888
|
+
return true;
|
|
1889
|
+
} catch {
|
|
1890
|
+
return false;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function readRuntimeImageLockData(lockPath) {
|
|
1894
|
+
try {
|
|
1895
|
+
const stat = lstatSync(lockPath);
|
|
1896
|
+
const raw = stat.isDirectory() ? readFileSync2(resolve11(lockPath, "owner.json"), "utf-8") : readFileSync2(lockPath, "utf-8");
|
|
1897
|
+
return JSON.parse(raw);
|
|
1898
|
+
} catch {
|
|
1899
|
+
return null;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
function acquireRuntimeImageLock(lockPath) {
|
|
1903
|
+
const lockData = { pid: process.pid, timestamp: Date.now() };
|
|
1904
|
+
const lockBody = JSON.stringify(lockData);
|
|
1905
|
+
const tryExclusiveCreate = () => {
|
|
1906
|
+
try {
|
|
1907
|
+
mkdirSync3(lockPath);
|
|
1908
|
+
writeFileSync2(resolve11(lockPath, "owner.json"), lockBody, "utf-8");
|
|
1909
|
+
return true;
|
|
1910
|
+
} catch {
|
|
1911
|
+
try {
|
|
1912
|
+
if (existsSync6(lockPath) && !lstatSync(lockPath).isDirectory()) {
|
|
1913
|
+
rmSync3(lockPath, { force: true });
|
|
1914
|
+
}
|
|
1915
|
+
} catch {}
|
|
1916
|
+
return false;
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1919
|
+
if (tryExclusiveCreate()) {
|
|
1920
|
+
return true;
|
|
1921
|
+
}
|
|
1922
|
+
if (existsSync6(lockPath)) {
|
|
1923
|
+
const data = readRuntimeImageLockData(lockPath);
|
|
1924
|
+
if (data) {
|
|
1925
|
+
const age = Date.now() - data.timestamp;
|
|
1926
|
+
if (age < LOCK_STALE_MS) {
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
if (isPidAlive(data.pid)) {
|
|
1930
|
+
return false;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
rmSync3(lockPath, { recursive: true, force: true });
|
|
1934
|
+
}
|
|
1935
|
+
return tryExclusiveCreate();
|
|
1936
|
+
}
|
|
1937
|
+
function releaseRuntimeImageLock(lockPath) {
|
|
1938
|
+
try {
|
|
1939
|
+
rmSync3(lockPath, { recursive: true, force: true });
|
|
1940
|
+
} catch {}
|
|
1941
|
+
}
|
|
1942
|
+
async function waitForRuntimeImageManifest(manifestPath, timeoutMs, pollMs = 500) {
|
|
1943
|
+
const deadline = Date.now() + timeoutMs;
|
|
1944
|
+
while (!existsSync6(manifestPath) && Date.now() < deadline) {
|
|
1945
|
+
await Bun.sleep(pollMs);
|
|
1946
|
+
}
|
|
1947
|
+
return existsSync6(manifestPath);
|
|
1948
|
+
}
|
|
1949
|
+
var SHARED_HOOK_NAMES = [
|
|
1950
|
+
"scope-guard",
|
|
1951
|
+
"import-guard",
|
|
1952
|
+
"safety-guard",
|
|
1953
|
+
"test-integrity-guard",
|
|
1954
|
+
"audit-trail",
|
|
1955
|
+
"post-edit-lint",
|
|
1956
|
+
"completion-verification",
|
|
1957
|
+
"inject-context",
|
|
1958
|
+
"task-runtime-start"
|
|
1959
|
+
];
|
|
1960
|
+
function discoverValidators(projectRoot) {
|
|
1961
|
+
const validatorsRoot = resolve11(projectRoot, "packages/runtime/src/control-plane/validators");
|
|
1962
|
+
const entries = [];
|
|
1963
|
+
if (!existsSync6(validatorsRoot)) {
|
|
1964
|
+
return entries;
|
|
1965
|
+
}
|
|
1966
|
+
const categories = readdirSync2(validatorsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1967
|
+
for (const category of categories) {
|
|
1968
|
+
const categoryDir = resolve11(validatorsRoot, category);
|
|
1969
|
+
const files = readdirSync2(categoryDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".ts")).map((e) => e.name).sort();
|
|
1970
|
+
for (const file of files) {
|
|
1971
|
+
const check = file.replace(/\.ts$/, "");
|
|
1972
|
+
if (!/^[a-z][\w-]*$/.test(check)) {
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
entries.push({
|
|
1976
|
+
category,
|
|
1977
|
+
check,
|
|
1978
|
+
sourcePath: `packages/runtime/src/control-plane/validators/${category}/${check}.ts`
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return entries;
|
|
1983
|
+
}
|
|
1984
|
+
function discoverPlugins(projectRoot) {
|
|
1985
|
+
const pluginsRoot = resolve11(projectRoot, "rig/plugins");
|
|
1986
|
+
if (!existsSync6(pluginsRoot)) {
|
|
1987
|
+
return [];
|
|
1988
|
+
}
|
|
1989
|
+
return readdirSync2(pluginsRoot, { withFileTypes: true }).filter((entry) => entry.isFile() && /\.plugin\.(ts|js|mjs|cjs)$/.test(entry.name)).map((entry) => {
|
|
1990
|
+
const name = entry.name.replace(/\.plugin\.(ts|js|mjs|cjs)$/, "");
|
|
1991
|
+
return {
|
|
1992
|
+
name,
|
|
1993
|
+
sourcePath: `rig/plugins/${entry.name}`
|
|
1994
|
+
};
|
|
1995
|
+
}).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1996
|
+
}
|
|
1997
|
+
async function provisionDependencyLayer(projectRoot, tempDepsDir) {
|
|
1998
|
+
const config = loadRuntimeImageConfig(projectRoot);
|
|
1999
|
+
if (!config.deps.monorepo_install && !config.deps.hp_next_install)
|
|
2000
|
+
return;
|
|
2001
|
+
const linkNodeModules = (sourceNodeModules, targetNodeModules) => {
|
|
2002
|
+
if (existsSync6(targetNodeModules)) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
try {
|
|
2006
|
+
symlinkSync(sourceNodeModules, targetNodeModules);
|
|
2007
|
+
} catch {
|
|
2008
|
+
cpSync2(sourceNodeModules, targetNodeModules, {
|
|
2009
|
+
recursive: true,
|
|
2010
|
+
force: true,
|
|
2011
|
+
dereference: true
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
2015
|
+
const ensureInstall = async (cwd, label) => {
|
|
2016
|
+
const bunCli = resolveBunBinaryPath();
|
|
2017
|
+
const install = Bun.spawn([bunCli, "install"], {
|
|
2018
|
+
cwd,
|
|
2019
|
+
stdout: "pipe",
|
|
2020
|
+
stderr: "pipe",
|
|
2021
|
+
env: runtimeProvisioningEnv()
|
|
2022
|
+
});
|
|
2023
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
2024
|
+
install.exited,
|
|
2025
|
+
new Response(install.stdout).text(),
|
|
2026
|
+
new Response(install.stderr).text()
|
|
2027
|
+
]);
|
|
2028
|
+
if (exitCode !== 0) {
|
|
2029
|
+
throw new Error([
|
|
2030
|
+
`Failed to install shared runtime dependencies for ${label}.`,
|
|
2031
|
+
`cwd: ${cwd}`,
|
|
2032
|
+
stderr.trim() || stdout.trim()
|
|
2033
|
+
].filter(Boolean).join(`
|
|
2034
|
+
`));
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
2037
|
+
if (config.deps.monorepo_install) {
|
|
2038
|
+
const monorepoSubRel = process.env.RIG_MONOREPO_INSTALL_PATH?.trim() || "repos/spliter-monorepo/humoongate";
|
|
2039
|
+
const monorepoSubRoot = resolve11(projectRoot, monorepoSubRel);
|
|
2040
|
+
const monorepoSubPackage = resolve11(monorepoSubRoot, "package.json");
|
|
2041
|
+
if (existsSync6(monorepoSubPackage)) {
|
|
2042
|
+
const sourceNodeModules = resolve11(monorepoSubRoot, "node_modules");
|
|
2043
|
+
try {
|
|
2044
|
+
if (lstatSync(sourceNodeModules).isSymbolicLink() && !existsSync6(sourceNodeModules)) {
|
|
2045
|
+
unlinkSync(sourceNodeModules);
|
|
2046
|
+
}
|
|
2047
|
+
} catch {}
|
|
2048
|
+
if (!existsSync6(sourceNodeModules)) {
|
|
2049
|
+
await ensureInstall(monorepoSubRoot, "monorepo");
|
|
2050
|
+
}
|
|
2051
|
+
const targetRoot = resolve11(tempDepsDir, "humoongate");
|
|
2052
|
+
const targetNodeModules = resolve11(targetRoot, "node_modules");
|
|
2053
|
+
mkdirSync3(targetRoot, { recursive: true });
|
|
2054
|
+
linkNodeModules(sourceNodeModules, targetNodeModules);
|
|
2055
|
+
for (const fileName of ["package.json", "bun.lockb"]) {
|
|
2056
|
+
const source = resolve11(monorepoSubRoot, fileName);
|
|
2057
|
+
if (existsSync6(source)) {
|
|
2058
|
+
cpSync2(source, resolve11(targetRoot, fileName), { force: true });
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (config.deps.hp_next_install) {
|
|
2064
|
+
const monorepoRel = process.env.RIG_HP_NEXT_MONOREPO_PATH?.trim() || "repos/spliter-monorepo";
|
|
2065
|
+
const appRel = process.env.RIG_HP_NEXT_APP_PATH?.trim() || "microservices/hp-next-frontend/app";
|
|
2066
|
+
const monorepoRoot = resolve11(projectRoot, monorepoRel);
|
|
2067
|
+
const hpNextRoot = resolve11(monorepoRoot, appRel);
|
|
2068
|
+
const hpNextPackage = resolve11(hpNextRoot, "package.json");
|
|
2069
|
+
if (existsSync6(hpNextPackage)) {
|
|
2070
|
+
const rootNodeModules = resolve11(monorepoRoot, "node_modules");
|
|
2071
|
+
const hpNextNodeModules = resolve11(hpNextRoot, "node_modules");
|
|
2072
|
+
if (!existsSync6(rootNodeModules) && !existsSync6(hpNextNodeModules)) {
|
|
2073
|
+
await ensureInstall(monorepoRoot, "spliter-monorepo hp-next workspace");
|
|
2074
|
+
}
|
|
2075
|
+
const targetRoot = resolve11(tempDepsDir, "microservices", "hp-next-frontend", "app");
|
|
2076
|
+
mkdirSync3(targetRoot, { recursive: true });
|
|
2077
|
+
if (existsSync6(rootNodeModules)) {
|
|
2078
|
+
linkNodeModules(rootNodeModules, resolve11(targetRoot, "root-node_modules"));
|
|
2079
|
+
}
|
|
2080
|
+
if (existsSync6(hpNextNodeModules)) {
|
|
2081
|
+
linkNodeModules(hpNextNodeModules, resolve11(targetRoot, "node_modules"));
|
|
2082
|
+
}
|
|
2083
|
+
for (const fileName of ["package.json", "bun.lock", "bun.lockb", "package-lock.json"]) {
|
|
2084
|
+
const source = resolve11(hpNextRoot, fileName);
|
|
2085
|
+
if (existsSync6(source)) {
|
|
2086
|
+
cpSync2(source, resolve11(targetRoot, fileName), { force: true });
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
for (const fileName of ["package.json", "bun.lock", "bun.lockb", "package-lock.json"]) {
|
|
2090
|
+
const source = resolve11(monorepoRoot, fileName);
|
|
2091
|
+
if (existsSync6(source)) {
|
|
2092
|
+
cpSync2(source, resolve11(targetRoot, `workspace-${fileName}`), { force: true });
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
function imageFromDir(rootDir, fingerprint, id) {
|
|
2099
|
+
return {
|
|
2100
|
+
id,
|
|
2101
|
+
rootDir,
|
|
2102
|
+
binDir: resolve11(rootDir, "bin"),
|
|
2103
|
+
hooksDir: resolve11(rootDir, "bin/hooks"),
|
|
2104
|
+
pluginsDir: resolve11(rootDir, "bin/plugins"),
|
|
2105
|
+
validatorsDir: resolve11(rootDir, "bin/validators"),
|
|
2106
|
+
depsDir: resolve11(rootDir, "deps"),
|
|
2107
|
+
manifestPath: resolve11(rootDir, "image-manifest.json"),
|
|
2108
|
+
fingerprint
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
function cleanupImageBuildDirs(rootDir, currentBuildDir) {
|
|
2112
|
+
const imagesRootDir = resolve11(rootDir, "..");
|
|
2113
|
+
const buildPrefix = `${rootDir}.building-`;
|
|
2114
|
+
try {
|
|
2115
|
+
for (const entry of readdirSync2(imagesRootDir)) {
|
|
2116
|
+
const fullPath = resolve11(imagesRootDir, entry);
|
|
2117
|
+
if (fullPath.startsWith(buildPrefix) && fullPath !== currentBuildDir) {
|
|
2118
|
+
rmSync3(fullPath, { recursive: true, force: true });
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
} catch {}
|
|
2122
|
+
}
|
|
2123
|
+
async function buildImage(projectRoot, image) {
|
|
2124
|
+
const { rootDir } = image;
|
|
2125
|
+
const bunPath = resolveBunBinaryPath();
|
|
2126
|
+
const buildDir = `${rootDir}.building-${process.pid}`;
|
|
2127
|
+
cleanupImageBuildDirs(rootDir, buildDir);
|
|
2128
|
+
if (existsSync6(buildDir)) {
|
|
2129
|
+
rmSync3(buildDir, { recursive: true, force: true });
|
|
2130
|
+
}
|
|
2131
|
+
let promoted = false;
|
|
2132
|
+
try {
|
|
2133
|
+
const tempBinDir = resolve11(buildDir, "bin");
|
|
2134
|
+
const tempHooksDir = resolve11(buildDir, "bin/hooks");
|
|
2135
|
+
const tempPluginsDir = resolve11(buildDir, "bin/plugins");
|
|
2136
|
+
const tempValidatorsDir = resolve11(buildDir, "bin/validators");
|
|
2137
|
+
const tempDepsDir = resolve11(buildDir, "deps");
|
|
2138
|
+
mkdirSync3(tempBinDir, { recursive: true });
|
|
2139
|
+
mkdirSync3(tempHooksDir, { recursive: true });
|
|
2140
|
+
mkdirSync3(tempPluginsDir, { recursive: true });
|
|
2141
|
+
mkdirSync3(tempValidatorsDir, { recursive: true });
|
|
2142
|
+
mkdirSync3(tempDepsDir, { recursive: true });
|
|
2143
|
+
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(tempBinDir);
|
|
2144
|
+
if (!nativeRuntimeLibraryPath) {
|
|
2145
|
+
throw new Error("Failed to provision the native Zig runtime library for runtime images.");
|
|
2146
|
+
}
|
|
2147
|
+
await buildBinary("packages/cli/bin/rig.ts", resolve11(tempBinDir, "rig"), projectRoot);
|
|
2148
|
+
await buildBinary("packages/runtime/bin/rig-agent.ts", resolve11(tempBinDir, "rig-agent"), projectRoot);
|
|
2149
|
+
await buildBinary("packages/runtime/src/control-plane/controlled-bash.ts", resolve11(tempBinDir, "controlled-bash"), projectRoot);
|
|
2150
|
+
for (const hookName of SHARED_HOOK_NAMES) {
|
|
2151
|
+
await buildBinary(`packages/runtime/src/control-plane/hooks/${hookName}.ts`, resolve11(tempHooksDir, hookName), projectRoot);
|
|
2152
|
+
}
|
|
2153
|
+
const plugins = discoverPlugins(projectRoot);
|
|
2154
|
+
for (const plugin of plugins) {
|
|
2155
|
+
if (existsSync6(resolve11(projectRoot, plugin.sourcePath))) {
|
|
2156
|
+
await buildBinary(plugin.sourcePath, resolve11(tempPluginsDir, plugin.name), projectRoot);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
const validators = discoverValidators(projectRoot);
|
|
2160
|
+
for (const { category, check, sourcePath } of validators) {
|
|
2161
|
+
if (!existsSync6(resolve11(projectRoot, sourcePath))) {
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
await buildBinary(sourcePath, resolve11(tempValidatorsDir, `${category}-${check}`), projectRoot, {
|
|
2165
|
+
AGENT_BUN_PATH: bunPath
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
await provisionDependencyLayer(projectRoot, tempDepsDir);
|
|
2169
|
+
const manifest = {
|
|
2170
|
+
id: image.id,
|
|
2171
|
+
fingerprint: image.fingerprint,
|
|
2172
|
+
builtAt: new Date().toISOString(),
|
|
2173
|
+
pid: process.pid
|
|
2174
|
+
};
|
|
2175
|
+
writeFileSync2(resolve11(buildDir, "image-manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
2176
|
+
if (existsSync6(rootDir)) {
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
try {
|
|
2180
|
+
renameSync3(buildDir, rootDir);
|
|
2181
|
+
promoted = true;
|
|
2182
|
+
} catch {
|
|
2183
|
+
if (existsSync6(rootDir)) {
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
throw new Error(`Failed to promote runtime image build directory: ${buildDir} -> ${rootDir}`);
|
|
2187
|
+
}
|
|
2188
|
+
} finally {
|
|
2189
|
+
if (!promoted && existsSync6(buildDir)) {
|
|
2190
|
+
rmSync3(buildDir, { recursive: true, force: true });
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function isRuntimeImageHealthy(projectRoot, image) {
|
|
2195
|
+
if (!existsSync6(image.manifestPath)) {
|
|
2196
|
+
return false;
|
|
2197
|
+
}
|
|
2198
|
+
const requiredPaths = [
|
|
2199
|
+
resolve11(image.binDir, "rig"),
|
|
2200
|
+
resolve11(image.binDir, "rig-agent"),
|
|
2201
|
+
resolve11(image.binDir, "controlled-bash"),
|
|
2202
|
+
...SHARED_HOOK_NAMES.map((hookName) => resolve11(image.hooksDir, hookName)),
|
|
2203
|
+
...discoverPlugins(projectRoot).map((plugin) => resolve11(image.pluginsDir, plugin.name)),
|
|
2204
|
+
...discoverValidators(projectRoot).map(({ category, check }) => resolve11(image.validatorsDir, `${category}-${check}`))
|
|
2205
|
+
];
|
|
2206
|
+
return requiredPaths.every((path) => existsSync6(path));
|
|
2207
|
+
}
|
|
2208
|
+
function computeRuntimeImageIdFromFingerprint(fp) {
|
|
2209
|
+
return computeRuntimeImageId(fp);
|
|
2210
|
+
}
|
|
2211
|
+
async function ensureRuntimeImage(projectRoot) {
|
|
2212
|
+
const fingerprint = await computeRuntimeImageFingerprint(projectRoot);
|
|
2213
|
+
const id = computeRuntimeImageId(fingerprint);
|
|
2214
|
+
const layout = resolveRigLayout(projectRoot);
|
|
2215
|
+
const imagesRoot = resolve11(layout.runtimeDir, "images");
|
|
2216
|
+
mkdirSync3(imagesRoot, { recursive: true });
|
|
2217
|
+
const imageRootDir = resolve11(imagesRoot, id);
|
|
2218
|
+
const image = imageFromDir(imageRootDir, fingerprint, id);
|
|
2219
|
+
if (isRuntimeImageHealthy(projectRoot, image)) {
|
|
2220
|
+
return image;
|
|
2221
|
+
}
|
|
2222
|
+
const lockPath = resolve11(imagesRoot, `${id}.lock`);
|
|
2223
|
+
const acquired = acquireRuntimeImageLock(lockPath);
|
|
2224
|
+
try {
|
|
2225
|
+
if (isRuntimeImageHealthy(projectRoot, image)) {
|
|
2226
|
+
return image;
|
|
2227
|
+
}
|
|
2228
|
+
if (!acquired) {
|
|
2229
|
+
const ready = await waitForRuntimeImageManifest(image.manifestPath, LOCK_STALE_MS);
|
|
2230
|
+
if (!ready || !isRuntimeImageHealthy(projectRoot, image)) {
|
|
2231
|
+
throw new Error(`Runtime image ${id} is being built by another process and is not healthy after waiting. ` + `Lock file: ${lockPath}`);
|
|
2232
|
+
}
|
|
2233
|
+
return image;
|
|
2234
|
+
}
|
|
2235
|
+
if (existsSync6(image.rootDir)) {
|
|
2236
|
+
rmSync3(image.rootDir, { recursive: true, force: true });
|
|
2237
|
+
}
|
|
2238
|
+
await buildImage(projectRoot, image);
|
|
2239
|
+
return image;
|
|
2240
|
+
} finally {
|
|
2241
|
+
if (acquired) {
|
|
2242
|
+
releaseRuntimeImageLock(lockPath);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
export {
|
|
2247
|
+
waitForRuntimeImageManifest,
|
|
2248
|
+
releaseRuntimeImageLock,
|
|
2249
|
+
ensureRuntimeImage,
|
|
2250
|
+
computeRuntimeImageIdFromFingerprint,
|
|
2251
|
+
computeRuntimeImageId,
|
|
2252
|
+
computeRuntimeImageFingerprintNative,
|
|
2253
|
+
computeRuntimeImageFingerprint,
|
|
2254
|
+
acquireRuntimeImageLock
|
|
2255
|
+
};
|