@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,145 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/layout.ts
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { basename, dirname, resolve } from "path";
|
|
5
|
+
var RIG_DEFINITION_DIRNAME = "rig";
|
|
6
|
+
var RIG_STATE_DIRNAME = ".rig";
|
|
7
|
+
var RIG_ARTIFACTS_DIRNAME = "artifacts";
|
|
8
|
+
function resolveNearestRigProjectRoot(startDir) {
|
|
9
|
+
let current = resolve(startDir);
|
|
10
|
+
let fallbackCandidate = null;
|
|
11
|
+
let projectCandidate = null;
|
|
12
|
+
for (;; ) {
|
|
13
|
+
const hasDefinition = existsSync(resolve(current, RIG_DEFINITION_DIRNAME));
|
|
14
|
+
const hasState = existsSync(resolve(current, RIG_STATE_DIRNAME));
|
|
15
|
+
const hasConfig = existsSync(resolve(current, "rig.config.ts")) || existsSync(resolve(current, "rig.config.json"));
|
|
16
|
+
const hasControlPlane = existsSync(resolve(current, "packages", "cli", "bin", "rig.ts")) || existsSync(resolve(current, "packages", "server"));
|
|
17
|
+
if (hasDefinition || hasState || hasConfig) {
|
|
18
|
+
fallbackCandidate = current;
|
|
19
|
+
if (hasControlPlane) {
|
|
20
|
+
projectCandidate = current;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const parent = resolve(current, "..");
|
|
24
|
+
if (parent === current) {
|
|
25
|
+
return projectCandidate ?? fallbackCandidate ?? resolve(startDir);
|
|
26
|
+
}
|
|
27
|
+
current = parent;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function resolveMonorepoRoot(projectRoot) {
|
|
31
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
32
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
33
|
+
if (explicit) {
|
|
34
|
+
const explicitRoot = resolve(explicit);
|
|
35
|
+
const explicitParent = dirname(explicitRoot);
|
|
36
|
+
if (basename(explicitParent) === ".worktrees") {
|
|
37
|
+
const owner = dirname(explicitParent);
|
|
38
|
+
const ownerHasGit = existsSync(resolve(owner, ".git"));
|
|
39
|
+
const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
|
|
40
|
+
const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
|
|
41
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
42
|
+
return owner;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
45
|
+
}
|
|
46
|
+
if (!existsSync(resolve(explicitRoot, ".git"))) {
|
|
47
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
48
|
+
}
|
|
49
|
+
const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
|
|
50
|
+
const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
|
|
51
|
+
if (!hasTaskConfig && !hasRigConfig) {
|
|
52
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
53
|
+
}
|
|
54
|
+
return explicitRoot;
|
|
55
|
+
}
|
|
56
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
57
|
+
if (basename(projectParent) === ".worktrees") {
|
|
58
|
+
const worktreeOwner = dirname(projectParent);
|
|
59
|
+
const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
|
|
60
|
+
const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
|
|
61
|
+
const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
|
|
62
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
63
|
+
return worktreeOwner;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return normalizedProjectRoot;
|
|
67
|
+
}
|
|
68
|
+
function resolveRigDataRoot(projectRoot) {
|
|
69
|
+
return resolveMonorepoRoot(projectRoot);
|
|
70
|
+
}
|
|
71
|
+
function resolveRuntimeWorkspaceLayout(workspaceDir) {
|
|
72
|
+
const root = resolve(workspaceDir);
|
|
73
|
+
const rigRoot = resolve(root, ".rig");
|
|
74
|
+
const logsDir = resolve(rigRoot, "logs");
|
|
75
|
+
const stateDir = resolve(rigRoot, "state");
|
|
76
|
+
const runtimeDir = resolve(rigRoot, "runtime");
|
|
77
|
+
const binDir = resolve(rigRoot, "bin");
|
|
78
|
+
return {
|
|
79
|
+
workspaceDir: root,
|
|
80
|
+
rigRoot,
|
|
81
|
+
stateDir,
|
|
82
|
+
logsDir,
|
|
83
|
+
artifactsRoot: resolve(root, RIG_ARTIFACTS_DIRNAME),
|
|
84
|
+
runtimeDir,
|
|
85
|
+
homeDir: resolve(rigRoot, "home"),
|
|
86
|
+
tmpDir: resolve(rigRoot, "tmp"),
|
|
87
|
+
cacheDir: resolve(rigRoot, "cache"),
|
|
88
|
+
sessionDir: resolve(rigRoot, "session"),
|
|
89
|
+
binDir,
|
|
90
|
+
distDir: resolve(rigRoot, "dist"),
|
|
91
|
+
pluginBinDir: resolve(binDir, "plugins"),
|
|
92
|
+
contextPath: resolve(rigRoot, "runtime-context.json"),
|
|
93
|
+
controlPlaneEventsFile: resolve(logsDir, "control-plane.events.jsonl")
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
|
|
97
|
+
const explicit = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
98
|
+
if (!explicit) {
|
|
99
|
+
throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
|
|
100
|
+
}
|
|
101
|
+
return resolve(explicit);
|
|
102
|
+
}
|
|
103
|
+
function resolveRigLayout(projectRoot) {
|
|
104
|
+
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
105
|
+
const definitionRoot = resolve(projectRoot, RIG_DEFINITION_DIRNAME);
|
|
106
|
+
const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
|
|
107
|
+
const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
|
|
108
|
+
const policyDir = resolve(definitionRoot, "policy");
|
|
109
|
+
return {
|
|
110
|
+
projectRoot,
|
|
111
|
+
monorepoRoot,
|
|
112
|
+
definitionRoot,
|
|
113
|
+
runtimeWorkspaceRoot,
|
|
114
|
+
stateRoot: runtimeLayout.rigRoot,
|
|
115
|
+
artifactsRoot: runtimeLayout.artifactsRoot,
|
|
116
|
+
configPath: resolve(definitionRoot, "config.sh"),
|
|
117
|
+
taskConfigPath: resolve(runtimeWorkspaceRoot, ".rig", "task-config.json"),
|
|
118
|
+
policyDir,
|
|
119
|
+
policyFile: resolve(policyDir, "policy.json"),
|
|
120
|
+
pluginsDir: resolve(definitionRoot, "plugins"),
|
|
121
|
+
hooksDir: resolve(definitionRoot, "hooks"),
|
|
122
|
+
toolsDir: resolve(definitionRoot, "tools"),
|
|
123
|
+
templatesDir: resolve(definitionRoot, "templates"),
|
|
124
|
+
validationDir: resolve(definitionRoot, "validation"),
|
|
125
|
+
stateDir: runtimeLayout.stateDir,
|
|
126
|
+
logsDir: runtimeLayout.logsDir,
|
|
127
|
+
notificationsDir: resolve(definitionRoot, "notifications"),
|
|
128
|
+
runtimeDir: runtimeLayout.runtimeDir,
|
|
129
|
+
distDir: runtimeLayout.distDir,
|
|
130
|
+
binDir: runtimeLayout.binDir,
|
|
131
|
+
pluginBinDir: runtimeLayout.pluginBinDir,
|
|
132
|
+
keybindingsPath: resolve(definitionRoot, "keybindings.json"),
|
|
133
|
+
controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
resolveRuntimeWorkspaceLayout,
|
|
138
|
+
resolveRigLayout,
|
|
139
|
+
resolveRigDataRoot,
|
|
140
|
+
resolveNearestRigProjectRoot,
|
|
141
|
+
resolveMonorepoRoot,
|
|
142
|
+
RIG_STATE_DIRNAME,
|
|
143
|
+
RIG_DEFINITION_DIRNAME,
|
|
144
|
+
RIG_ARTIFACTS_DIRNAME
|
|
145
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/local-server.ts
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import { closeSync, existsSync as existsSync2, mkdirSync, openSync, readFileSync, unlinkSync } from "fs";
|
|
5
|
+
import { resolve as resolve2 } from "path";
|
|
6
|
+
|
|
7
|
+
// packages/runtime/src/layout.ts
|
|
8
|
+
import { existsSync } from "fs";
|
|
9
|
+
import { basename, dirname, resolve } from "path";
|
|
10
|
+
function resolveMonorepoRoot(projectRoot) {
|
|
11
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
12
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
13
|
+
if (explicit) {
|
|
14
|
+
const explicitRoot = resolve(explicit);
|
|
15
|
+
const explicitParent = dirname(explicitRoot);
|
|
16
|
+
if (basename(explicitParent) === ".worktrees") {
|
|
17
|
+
const owner = dirname(explicitParent);
|
|
18
|
+
const ownerHasGit = existsSync(resolve(owner, ".git"));
|
|
19
|
+
const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
|
|
20
|
+
const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
|
|
21
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
22
|
+
return owner;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
|
|
25
|
+
}
|
|
26
|
+
if (!existsSync(resolve(explicitRoot, ".git"))) {
|
|
27
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
|
|
28
|
+
}
|
|
29
|
+
const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
|
|
30
|
+
const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
|
|
31
|
+
if (!hasTaskConfig && !hasRigConfig) {
|
|
32
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
|
|
33
|
+
}
|
|
34
|
+
return explicitRoot;
|
|
35
|
+
}
|
|
36
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
37
|
+
if (basename(projectParent) === ".worktrees") {
|
|
38
|
+
const worktreeOwner = dirname(projectParent);
|
|
39
|
+
const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
|
|
40
|
+
const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
|
|
41
|
+
const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
|
|
42
|
+
if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
|
|
43
|
+
return worktreeOwner;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return normalizedProjectRoot;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// packages/runtime/src/local-server.ts
|
|
50
|
+
function parsePublishedRigServerState(value) {
|
|
51
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const candidate = value;
|
|
55
|
+
if (typeof candidate.pid !== "number" || !Number.isInteger(candidate.pid) || candidate.pid <= 0 || typeof candidate.host !== "string" || candidate.host.trim().length === 0 || typeof candidate.port !== "number" || !Number.isInteger(candidate.port) || candidate.port < 1 || candidate.port > 65535 || candidate.authToken !== null && candidate.authToken !== undefined && typeof candidate.authToken !== "string" || typeof candidate.startedAt !== "string" || candidate.startedAt.trim().length === 0) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
pid: candidate.pid,
|
|
60
|
+
host: candidate.host,
|
|
61
|
+
port: candidate.port,
|
|
62
|
+
authToken: candidate.authToken ?? null,
|
|
63
|
+
startedAt: candidate.startedAt
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function resolveRigServerLogPath(projectRoot) {
|
|
67
|
+
return resolve2(resolveRigServerPaths(projectRoot).logsDir, "rig-server.bootstrap.log");
|
|
68
|
+
}
|
|
69
|
+
function resolvePublishedRigServerStatePath(projectRoot) {
|
|
70
|
+
return resolve2(resolveRigServerPaths(projectRoot).stateDir, "rig-server.json");
|
|
71
|
+
}
|
|
72
|
+
function resolveRigServerPaths(projectRoot) {
|
|
73
|
+
const taskWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
74
|
+
const explicitStateDir = process.env.RIG_STATE_DIR?.trim();
|
|
75
|
+
const explicitLogsDir = process.env.RIG_LOGS_DIR?.trim();
|
|
76
|
+
const explicitSessionFile = process.env.RIG_SESSION_FILE?.trim();
|
|
77
|
+
const hostStateRoot = resolve2(projectRoot, ".rig");
|
|
78
|
+
const monorepoStateRoot = resolve2(resolveMonorepoRoot(projectRoot), ".rig");
|
|
79
|
+
const stateRoot = taskWorkspace ? resolve2(taskWorkspace, ".rig") : explicitStateDir ? resolve2(explicitStateDir, "..") : explicitLogsDir ? resolve2(explicitLogsDir, "..") : explicitSessionFile ? resolve2(explicitSessionFile, "..", "..") : existsSync2(hostStateRoot) ? hostStateRoot : monorepoStateRoot;
|
|
80
|
+
return {
|
|
81
|
+
stateDir: explicitStateDir ? resolve2(explicitStateDir) : resolve2(stateRoot, "state"),
|
|
82
|
+
logsDir: explicitLogsDir ? resolve2(explicitLogsDir) : resolve2(stateRoot, "logs")
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function readPublishedRigServerState(projectRoot) {
|
|
86
|
+
return readPublishedRigServerStateSync(projectRoot);
|
|
87
|
+
}
|
|
88
|
+
function readPublishedRigServerStateSync(projectRoot) {
|
|
89
|
+
const filePath = resolvePublishedRigServerStatePath(projectRoot);
|
|
90
|
+
if (!existsSync2(filePath)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const rawState = readFileSync(filePath, "utf8");
|
|
95
|
+
return parsePublishedRigServerState(JSON.parse(rawState));
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function clearPublishedRigServerState(projectRoot) {
|
|
101
|
+
const filePath = resolvePublishedRigServerStatePath(projectRoot);
|
|
102
|
+
try {
|
|
103
|
+
unlinkSync(filePath);
|
|
104
|
+
} catch {}
|
|
105
|
+
}
|
|
106
|
+
function toLocalServerConnection(published) {
|
|
107
|
+
const baseUrl = `http://${published.host}:${published.port}`;
|
|
108
|
+
const wsUrl = new URL(`ws://${published.host}:${published.port}`);
|
|
109
|
+
if (published.authToken) {
|
|
110
|
+
wsUrl.searchParams.set("token", published.authToken);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
baseUrl,
|
|
114
|
+
wsUrl: wsUrl.toString(),
|
|
115
|
+
host: published.host,
|
|
116
|
+
port: published.port,
|
|
117
|
+
authToken: published.authToken
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async function waitForServerHealthy(baseUrl, authToken, timeoutMs) {
|
|
121
|
+
const deadline = Date.now() + timeoutMs;
|
|
122
|
+
const healthUrl = new URL(`${baseUrl}/health`);
|
|
123
|
+
if (authToken !== null) {
|
|
124
|
+
healthUrl.searchParams.set("token", authToken);
|
|
125
|
+
}
|
|
126
|
+
while (Date.now() < deadline) {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(healthUrl.toString());
|
|
129
|
+
if (response.ok) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
} catch {}
|
|
133
|
+
await Bun.sleep(150);
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
async function waitForPublishedServer(projectRoot, authToken, timeoutMs) {
|
|
138
|
+
const deadline = Date.now() + timeoutMs;
|
|
139
|
+
while (Date.now() < deadline) {
|
|
140
|
+
const published = await readPublishedRigServerState(projectRoot);
|
|
141
|
+
if (published && published.authToken === authToken) {
|
|
142
|
+
const connection = toLocalServerConnection(published);
|
|
143
|
+
if (await waitForServerHealthy(connection.baseUrl, connection.authToken, 500)) {
|
|
144
|
+
return published;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
await Bun.sleep(250);
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
async function ensureLocalRigServerConnection(projectRoot, options = {}) {
|
|
152
|
+
const published = await readPublishedRigServerState(projectRoot);
|
|
153
|
+
if (published) {
|
|
154
|
+
const connection = toLocalServerConnection(published);
|
|
155
|
+
if (await waitForServerHealthy(connection.baseUrl, connection.authToken, 1500)) {
|
|
156
|
+
return connection;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const host = options.host ?? "127.0.0.1";
|
|
160
|
+
const startupTimeoutMs = options.startupTimeoutMs ?? 15000;
|
|
161
|
+
const authToken = Buffer.from(crypto.getRandomValues(new Uint8Array(24))).toString("hex");
|
|
162
|
+
const workspaceRoot = resolve2(import.meta.dir, "../../..");
|
|
163
|
+
const bootstrapLogPath = resolveRigServerLogPath(projectRoot);
|
|
164
|
+
mkdirSync(resolveRigServerPaths(projectRoot).logsDir, { recursive: true });
|
|
165
|
+
const bootstrapLogFd = openSync(bootstrapLogPath, "w");
|
|
166
|
+
clearPublishedRigServerState(projectRoot);
|
|
167
|
+
const child = spawn("bun", [
|
|
168
|
+
"run",
|
|
169
|
+
"packages/server/src/server.ts",
|
|
170
|
+
"start",
|
|
171
|
+
"--host",
|
|
172
|
+
host,
|
|
173
|
+
"--port",
|
|
174
|
+
"0",
|
|
175
|
+
"--auth-token",
|
|
176
|
+
authToken
|
|
177
|
+
], {
|
|
178
|
+
cwd: workspaceRoot,
|
|
179
|
+
env: {
|
|
180
|
+
...process.env,
|
|
181
|
+
PROJECT_RIG_ROOT: projectRoot
|
|
182
|
+
},
|
|
183
|
+
detached: true,
|
|
184
|
+
stdio: ["ignore", bootstrapLogFd, bootstrapLogFd]
|
|
185
|
+
});
|
|
186
|
+
closeSync(bootstrapLogFd);
|
|
187
|
+
child.unref();
|
|
188
|
+
const ready = await waitForPublishedServer(projectRoot, authToken, startupTimeoutMs);
|
|
189
|
+
if (!ready) {
|
|
190
|
+
child.kill();
|
|
191
|
+
const bootstrapLog = existsSync2(bootstrapLogPath) ? readFileSync(bootstrapLogPath, "utf8").trim() : "";
|
|
192
|
+
const diagnostics = bootstrapLog ? ` Startup log (${bootstrapLogPath}):
|
|
193
|
+
${bootstrapLog}` : ` No bootstrap log was written.`;
|
|
194
|
+
throw new Error(`Timed out waiting for the local Rig server to start.${diagnostics}`);
|
|
195
|
+
}
|
|
196
|
+
return toLocalServerConnection(ready);
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
readPublishedRigServerStateSync,
|
|
200
|
+
readPublishedRigServerState,
|
|
201
|
+
ensureLocalRigServerConnection
|
|
202
|
+
};
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/plugins.ts
|
|
3
|
+
import { existsSync, readdirSync } from "fs";
|
|
4
|
+
import { basename, resolve } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/runtime/src/binary-run.ts
|
|
7
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
8
|
+
var runtimeBinaryBuildQueue = Promise.resolve();
|
|
9
|
+
async function launchRuntimeBinary(request) {
|
|
10
|
+
const options = {
|
|
11
|
+
cwd: request.cwd,
|
|
12
|
+
env: request.env ? { ...process.env, ...request.env } : process.env
|
|
13
|
+
};
|
|
14
|
+
if (request.timeoutMs !== undefined) {
|
|
15
|
+
options.timeoutMs = request.timeoutMs;
|
|
16
|
+
}
|
|
17
|
+
return spawnProcess(request.binaryPath, request.args, options);
|
|
18
|
+
}
|
|
19
|
+
async function spawnProcess(command, args, options) {
|
|
20
|
+
const child = nodeSpawn(command, args, {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
env: options.env,
|
|
23
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
24
|
+
});
|
|
25
|
+
let stdout = "";
|
|
26
|
+
let stderr = "";
|
|
27
|
+
child.stdout?.setEncoding("utf8");
|
|
28
|
+
child.stderr?.setEncoding("utf8");
|
|
29
|
+
child.stdout?.on("data", (chunk) => {
|
|
30
|
+
stdout += chunk;
|
|
31
|
+
});
|
|
32
|
+
child.stderr?.on("data", (chunk) => {
|
|
33
|
+
stderr += chunk;
|
|
34
|
+
});
|
|
35
|
+
let timedOut = false;
|
|
36
|
+
let timer;
|
|
37
|
+
const exitCode = await new Promise((resolveExit, rejectExit) => {
|
|
38
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
39
|
+
timer = setTimeout(() => {
|
|
40
|
+
timedOut = true;
|
|
41
|
+
child.kill();
|
|
42
|
+
}, options.timeoutMs);
|
|
43
|
+
}
|
|
44
|
+
child.once("error", rejectExit);
|
|
45
|
+
child.once("close", (code) => resolveExit(code ?? (timedOut ? 1 : 0)));
|
|
46
|
+
}).finally(() => {
|
|
47
|
+
if (timer) {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return { exitCode, stdout, stderr, timedOut };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// packages/runtime/src/control-plane/runtime/plugin-mode.ts
|
|
55
|
+
var LEGACY_PLUGIN_SCAN_ENV = "RIG_LEGACY_PLUGIN_SCAN";
|
|
56
|
+
function isLegacyPluginScanEnabled(env = process.env) {
|
|
57
|
+
const value = env[LEGACY_PLUGIN_SCAN_ENV]?.trim().toLowerCase();
|
|
58
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// packages/runtime/src/plugins.ts
|
|
62
|
+
var runtimeHookPhases = ["beforeCommand", "afterCommand", "onEvent"];
|
|
63
|
+
var PLUGIN_SOURCE_PATTERN = /\.plugin\.(ts|js|mjs|cjs)$/;
|
|
64
|
+
function inferRuntimePluginName(fileName) {
|
|
65
|
+
return fileName.replace(PLUGIN_SOURCE_PATTERN, "");
|
|
66
|
+
}
|
|
67
|
+
function describeRuntimePluginFiles(filePaths) {
|
|
68
|
+
return filePaths.filter((filePath) => PLUGIN_SOURCE_PATTERN.test(filePath)).map((filePath) => {
|
|
69
|
+
const fileName = filePath.split(/[\\/]/).at(-1) ?? filePath;
|
|
70
|
+
const name = inferRuntimePluginName(fileName);
|
|
71
|
+
return { name, sourcePath: filePath, binaryName: name };
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class PluginManager {
|
|
76
|
+
eventBus;
|
|
77
|
+
context;
|
|
78
|
+
pluginDir;
|
|
79
|
+
pluginFiles;
|
|
80
|
+
pluginNames;
|
|
81
|
+
localBinDir;
|
|
82
|
+
imageBinDir;
|
|
83
|
+
pluginsRequireBinaries;
|
|
84
|
+
ensureImageBinDir;
|
|
85
|
+
plugins = null;
|
|
86
|
+
loadPromise = null;
|
|
87
|
+
constructor(options) {
|
|
88
|
+
this.eventBus = options.eventBus;
|
|
89
|
+
this.context = options.context;
|
|
90
|
+
this.pluginDir = options.pluginDir;
|
|
91
|
+
this.pluginFiles = options.pluginFiles;
|
|
92
|
+
this.pluginNames = options.pluginNames;
|
|
93
|
+
this.localBinDir = options.localBinDir;
|
|
94
|
+
this.imageBinDir = options.imageBinDir;
|
|
95
|
+
this.pluginsRequireBinaries = options.pluginsRequireBinaries;
|
|
96
|
+
this.ensureImageBinDir = options.ensureImageBinDir;
|
|
97
|
+
}
|
|
98
|
+
static async load(options) {
|
|
99
|
+
const pluginDir = options.pluginDir ?? resolve(options.projectRoot, "rig", "plugins");
|
|
100
|
+
const localBinDir = options.localBinDir ?? resolve(options.projectRoot, "rig", "plugins");
|
|
101
|
+
const imageBinDir = options.imageBinDir ?? (options.runtimeContext ? resolve(options.runtimeContext.binDir, "plugins") : "");
|
|
102
|
+
const legacyPluginScan = options.legacyPluginScan ?? isLegacyPluginScanEnabled(options.env);
|
|
103
|
+
const pluginFiles = legacyPluginScan ? safeReadDir(pluginDir).filter((entry) => PLUGIN_SOURCE_PATTERN.test(entry)) : [];
|
|
104
|
+
const pluginNames = pluginFiles.map((file) => inferRuntimePluginName(basename(file)));
|
|
105
|
+
const context = {
|
|
106
|
+
projectRoot: options.projectRoot,
|
|
107
|
+
runId: options.runId,
|
|
108
|
+
eventBus: options.eventBus
|
|
109
|
+
};
|
|
110
|
+
return new PluginManager({
|
|
111
|
+
eventBus: options.eventBus,
|
|
112
|
+
context,
|
|
113
|
+
pluginDir,
|
|
114
|
+
pluginFiles,
|
|
115
|
+
pluginNames,
|
|
116
|
+
localBinDir,
|
|
117
|
+
imageBinDir,
|
|
118
|
+
pluginsRequireBinaries: options.pluginsRequireBinaries ?? true,
|
|
119
|
+
ensureImageBinDir: options.ensureImageBinDir
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
list() {
|
|
123
|
+
if (this.plugins) {
|
|
124
|
+
return this.plugins.map((plugin) => ({
|
|
125
|
+
name: plugin.name,
|
|
126
|
+
validators: plugin.validators?.map((validator) => validator.id) ?? []
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
return this.pluginNames.map((name) => ({ name, validators: [] }));
|
|
130
|
+
}
|
|
131
|
+
async beforeCommand(ctx) {
|
|
132
|
+
const plugins = await this.ensureLoaded();
|
|
133
|
+
for (const plugin of plugins) {
|
|
134
|
+
if (!plugin.beforeCommand)
|
|
135
|
+
continue;
|
|
136
|
+
await this.safeInvoke(plugin.name, "beforeCommand", () => plugin.beforeCommand?.(ctx, this.context));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async afterCommand(result) {
|
|
140
|
+
const plugins = await this.ensureLoaded();
|
|
141
|
+
for (const plugin of plugins) {
|
|
142
|
+
if (!plugin.afterCommand)
|
|
143
|
+
continue;
|
|
144
|
+
await this.safeInvoke(plugin.name, "afterCommand", () => plugin.afterCommand?.(result, this.context));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async onEvent(event) {
|
|
148
|
+
const plugins = this.plugins;
|
|
149
|
+
if (!plugins)
|
|
150
|
+
return;
|
|
151
|
+
for (const plugin of plugins) {
|
|
152
|
+
if (!plugin.onEvent)
|
|
153
|
+
continue;
|
|
154
|
+
await this.safeInvoke(plugin.name, "onEvent", () => plugin.onEvent?.(event, this.context));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async runValidators(taskId) {
|
|
158
|
+
const plugins = await this.ensureLoaded();
|
|
159
|
+
const results = [];
|
|
160
|
+
for (const plugin of plugins) {
|
|
161
|
+
for (const validator of plugin.validators ?? []) {
|
|
162
|
+
await this.eventBus.emit("validator.started", {
|
|
163
|
+
plugin: plugin.name,
|
|
164
|
+
validator: validator.id,
|
|
165
|
+
taskId
|
|
166
|
+
});
|
|
167
|
+
try {
|
|
168
|
+
const result = await validator.run({ taskId, projectRoot: this.context.projectRoot }, this.context);
|
|
169
|
+
results.push(result);
|
|
170
|
+
await this.eventBus.emit("validator.finished", {
|
|
171
|
+
plugin: plugin.name,
|
|
172
|
+
validator: validator.id,
|
|
173
|
+
taskId,
|
|
174
|
+
passed: result.passed,
|
|
175
|
+
summary: result.summary
|
|
176
|
+
});
|
|
177
|
+
} catch (error) {
|
|
178
|
+
const failed = {
|
|
179
|
+
id: validator.id,
|
|
180
|
+
passed: false,
|
|
181
|
+
summary: `${plugin.name}/${validator.id} failed unexpectedly`,
|
|
182
|
+
details: `${error}`
|
|
183
|
+
};
|
|
184
|
+
results.push(failed);
|
|
185
|
+
await this.eventBus.emit("validator.finished", {
|
|
186
|
+
plugin: plugin.name,
|
|
187
|
+
validator: validator.id,
|
|
188
|
+
taskId,
|
|
189
|
+
passed: false,
|
|
190
|
+
summary: failed.summary,
|
|
191
|
+
details: failed.details
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return results;
|
|
197
|
+
}
|
|
198
|
+
async ensureLoaded() {
|
|
199
|
+
if (this.plugins)
|
|
200
|
+
return this.plugins;
|
|
201
|
+
if (this.loadPromise)
|
|
202
|
+
return this.loadPromise;
|
|
203
|
+
this.loadPromise = this.loadCompiledPlugins();
|
|
204
|
+
try {
|
|
205
|
+
this.plugins = await this.loadPromise;
|
|
206
|
+
return this.plugins;
|
|
207
|
+
} finally {
|
|
208
|
+
this.loadPromise = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
resolveBinPath(binName) {
|
|
212
|
+
const candidates = [this.imageBinDir, this.localBinDir].filter(Boolean).map((dir) => resolve(dir, binName));
|
|
213
|
+
return candidates.find((candidate) => existsSync(candidate));
|
|
214
|
+
}
|
|
215
|
+
async loadCompiledPlugins() {
|
|
216
|
+
const plugins = [];
|
|
217
|
+
for (const file of this.pluginFiles) {
|
|
218
|
+
const binName = inferRuntimePluginName(basename(file));
|
|
219
|
+
let binPath = this.resolveBinPath(binName);
|
|
220
|
+
if (!binPath && !this.imageBinDir && this.pluginsRequireBinaries && this.ensureImageBinDir) {
|
|
221
|
+
try {
|
|
222
|
+
this.imageBinDir = await this.ensureImageBinDir(this.context.projectRoot);
|
|
223
|
+
binPath = this.resolveBinPath(binName);
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
if (!binPath) {
|
|
227
|
+
const triedPaths = [this.imageBinDir, this.localBinDir].filter(Boolean).map((dir) => resolve(dir, binName));
|
|
228
|
+
const missingError = `Compiled plugin binary not found for '${binName}'. Tried: ${triedPaths.join(", ")}`;
|
|
229
|
+
await this.eventBus.emit("plugin.error", {
|
|
230
|
+
file: resolve(this.pluginDir, file),
|
|
231
|
+
phase: "load",
|
|
232
|
+
error: missingError
|
|
233
|
+
});
|
|
234
|
+
if (this.pluginsRequireBinaries) {
|
|
235
|
+
throw new Error(missingError);
|
|
236
|
+
}
|
|
237
|
+
plugins.push({ name: binName, validators: [] });
|
|
238
|
+
await this.eventBus.emit("plugin.loaded", {
|
|
239
|
+
plugin: binName,
|
|
240
|
+
file: resolve(this.pluginDir, file),
|
|
241
|
+
source: "metadata-only"
|
|
242
|
+
});
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const wrapper = createBinaryPluginWrapper(binName, binPath, this.context.projectRoot);
|
|
246
|
+
plugins.push(wrapper);
|
|
247
|
+
await this.eventBus.emit("plugin.loaded", {
|
|
248
|
+
plugin: wrapper.name,
|
|
249
|
+
file: binPath,
|
|
250
|
+
source: "compiled-binary"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return plugins;
|
|
254
|
+
}
|
|
255
|
+
async safeInvoke(pluginName, hook, call) {
|
|
256
|
+
try {
|
|
257
|
+
await call();
|
|
258
|
+
} catch (error) {
|
|
259
|
+
await this.eventBus.emit("plugin.error", {
|
|
260
|
+
plugin: pluginName,
|
|
261
|
+
phase: hook,
|
|
262
|
+
error: `${error}`
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function createBinaryPluginWrapper(name, binPath, projectRoot) {
|
|
268
|
+
return {
|
|
269
|
+
name,
|
|
270
|
+
validators: [
|
|
271
|
+
{
|
|
272
|
+
id: `${name}:compiled`,
|
|
273
|
+
async run(ctx) {
|
|
274
|
+
const { exitCode, stdout, stderr } = await launchRuntimeBinary({
|
|
275
|
+
binaryPath: binPath,
|
|
276
|
+
args: ["--validate", ctx.taskId, ctx.projectRoot],
|
|
277
|
+
cwd: projectRoot
|
|
278
|
+
});
|
|
279
|
+
if (exitCode !== 0) {
|
|
280
|
+
return {
|
|
281
|
+
id: `${name}:compiled`,
|
|
282
|
+
passed: false,
|
|
283
|
+
summary: `Plugin binary ${name} exited with code ${exitCode}`,
|
|
284
|
+
details: stderr || stdout
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
const results = JSON.parse(stdout.trim());
|
|
289
|
+
const failed = results.filter((result) => !result.passed);
|
|
290
|
+
if (failed.length > 0) {
|
|
291
|
+
return {
|
|
292
|
+
id: `${name}:compiled`,
|
|
293
|
+
passed: false,
|
|
294
|
+
summary: `${failed.length} of ${results.length} validator(s) failed`,
|
|
295
|
+
details: failed.map((result) => `${result.id}: ${result.summary}`).join(`
|
|
296
|
+
`)
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
id: `${name}:compiled`,
|
|
301
|
+
passed: true,
|
|
302
|
+
summary: `All ${results.length} validator(s) passed`
|
|
303
|
+
};
|
|
304
|
+
} catch {
|
|
305
|
+
return {
|
|
306
|
+
id: `${name}:compiled`,
|
|
307
|
+
passed: false,
|
|
308
|
+
summary: `Failed to parse output from compiled plugin ${name}`,
|
|
309
|
+
details: stdout.slice(0, 500)
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function safeReadDir(path) {
|
|
318
|
+
try {
|
|
319
|
+
return readdirSync(path, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).sort();
|
|
320
|
+
} catch {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
export {
|
|
325
|
+
runtimeHookPhases,
|
|
326
|
+
inferRuntimePluginName,
|
|
327
|
+
describeRuntimePluginFiles,
|
|
328
|
+
PluginManager
|
|
329
|
+
};
|