@h-rig/provider-plugin 0.0.6-alpha.157 → 0.0.6-alpha.158
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/dist/bin/rig-agent-dispatch.d.ts +2 -0
- package/dist/bin/rig-agent-dispatch.js +863 -0
- package/dist/src/agent-harness/agent-mode.d.ts +1 -0
- package/dist/src/agent-harness/agent-mode.js +48 -0
- package/dist/src/agent-harness/agent-wrapper.d.ts +53 -0
- package/dist/src/agent-harness/agent-wrapper.js +916 -0
- package/dist/src/agent-harness/controlled-bash.d.ts +3 -0
- package/dist/src/agent-harness/controlled-bash.js +45 -0
- package/dist/src/agent-harness/git-ops.d.ts +2 -0
- package/dist/src/agent-harness/git-ops.js +27 -0
- package/dist/src/agent-harness/repo-ops.d.ts +8 -0
- package/dist/src/agent-harness/repo-ops.js +471 -0
- package/dist/src/agent-harness/rig-agent-entrypoint.d.ts +1 -0
- package/dist/src/agent-harness/rig-agent-entrypoint.js +1277 -0
- package/dist/src/agent-harness/rig-agent.d.ts +2 -0
- package/dist/src/agent-harness/rig-agent.js +1244 -0
- package/dist/src/agent-harness/runtime-snapshot-config.d.ts +2 -0
- package/dist/src/agent-harness/runtime-snapshot-config.js +25 -0
- package/dist/src/agent-harness/task-data.d.ts +2 -0
- package/dist/src/agent-harness/task-data.js +12 -0
- package/dist/src/agent-harness/task-ops.d.ts +10 -0
- package/dist/src/agent-harness/task-ops.js +53 -0
- package/dist/src/index.js +3366 -16
- package/dist/src/pi-settings-materializer.d.ts +10 -0
- package/dist/src/pi-settings-materializer.js +52 -0
- package/dist/src/plugin.d.ts +9 -2
- package/dist/src/plugin.js +3360 -8
- package/dist/src/service.d.ts +1 -1
- package/dist/src/session-asset-materializer-service.d.ts +13 -0
- package/dist/src/session-asset-materializer-service.js +124 -0
- package/dist/src/skill-materializer.d.ts +25 -0
- package/dist/src/skill-materializer.js +46 -0
- package/dist/src/tooling/binary-build-worker.d.ts +1 -0
- package/dist/src/tooling/binary-build-worker.js +323 -0
- package/dist/src/tooling/browser-tool-entrypoint.d.ts +2 -0
- package/dist/src/tooling/browser-tool-entrypoint.js +125 -0
- package/dist/src/tooling/browser-tools.d.ts +3 -0
- package/dist/src/tooling/browser-tools.js +27 -0
- package/dist/src/tooling/claude-router-binary.d.ts +3 -0
- package/dist/src/tooling/claude-router-binary.js +381 -0
- package/dist/src/tooling/claude-router.d.ts +22 -0
- package/dist/src/tooling/claude-router.js +524 -0
- package/dist/src/tooling/embedded-native-assets.d.ts +7 -0
- package/dist/src/tooling/embedded-native-assets.js +6 -0
- package/dist/src/tooling/file-tools.d.ts +5 -0
- package/dist/src/tooling/file-tools.js +224 -0
- package/dist/src/tooling/gateway.d.ts +4 -0
- package/dist/src/tooling/gateway.js +430 -0
- package/dist/src/tooling/native-extract.d.ts +2 -0
- package/dist/src/tooling/native-extract.js +44 -0
- package/dist/src/tooling/runtime-binary-build.d.ts +88 -0
- package/dist/src/tooling/runtime-binary-build.js +480 -0
- package/dist/src/tooling/shell-tools.d.ts +5 -0
- package/dist/src/tooling/shell-tools.js +217 -0
- package/native/darwin-arm64/rig-shell +0 -0
- package/native/darwin-arm64/rig-shell.build-manifest.json +4 -0
- package/native/darwin-arm64/rig-tools +0 -0
- package/native/darwin-arm64/rig-tools.build-manifest.json +4 -0
- package/native/darwin-x64/rig-shell +0 -0
- package/native/darwin-x64/rig-tools +0 -0
- package/native/linux-arm64/rig-shell +0 -0
- package/native/linux-arm64/rig-tools +0 -0
- package/native/linux-x64/rig-shell +0 -0
- package/native/linux-x64/rig-tools +0 -0
- package/native/win32-x64/rig-shell.exe +0 -0
- package/native/win32-x64/rig-tools.exe +0 -0
- package/package.json +54 -5
- package/dist/src/claude-stream-records.d.ts +0 -24
- package/dist/src/claude-stream-records.js +0 -158
- package/dist/src/codex-app-server.d.ts +0 -16
- package/dist/src/codex-app-server.js +0 -548
- package/dist/src/codex-exec-records.d.ts +0 -27
- package/dist/src/codex-exec-records.js +0 -203
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/provider-plugin/src/agent-harness/controlled-bash.ts
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
import { resolveRigLayout } from "@rig/core/layout";
|
|
6
|
+
function controlledBashCandidates(projectRoot) {
|
|
7
|
+
const layout = resolveRigLayout(projectRoot);
|
|
8
|
+
const candidates = [
|
|
9
|
+
process.env.RIG_CONTROLLED_BASH_BIN?.trim() || "",
|
|
10
|
+
resolve(layout.binDir, "controlled-bash"),
|
|
11
|
+
Bun.which("controlled-bash") || ""
|
|
12
|
+
];
|
|
13
|
+
return candidates.filter((candidate) => Boolean(candidate));
|
|
14
|
+
}
|
|
15
|
+
function resolveControlledBash(projectRoot) {
|
|
16
|
+
for (const candidate of controlledBashCandidates(projectRoot)) {
|
|
17
|
+
if (existsSync(candidate))
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
async function runControlledBash(args, options) {
|
|
23
|
+
const projectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || process.env.PROJECT_RIG_ROOT?.trim() || process.env.RIG_TASK_WORKSPACE?.trim() || process.cwd() || options.projectRootFallbackDir;
|
|
24
|
+
const controlled = resolveControlledBash(projectRoot);
|
|
25
|
+
if (!controlled) {
|
|
26
|
+
console.error("[rig-agent] controlled-bash entrypoint unavailable; refusing to run an unguarded shell.");
|
|
27
|
+
return 126;
|
|
28
|
+
}
|
|
29
|
+
const command = [controlled, ...args];
|
|
30
|
+
const child = Bun.spawn(command, {
|
|
31
|
+
stdin: "inherit",
|
|
32
|
+
stdout: "inherit",
|
|
33
|
+
stderr: "inherit",
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
env: {
|
|
36
|
+
...process.env,
|
|
37
|
+
PROJECT_RIG_ROOT: projectRoot,
|
|
38
|
+
RIG_BASH_ACTIVE: "1"
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return await child.exited;
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
runControlledBash
|
|
45
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/provider-plugin/src/agent-harness/git-ops.ts
|
|
3
|
+
import { LIFECYCLE_GIT_AGENT } from "@rig/contracts";
|
|
4
|
+
import { defineCapability } from "@rig/core/capability";
|
|
5
|
+
import { buildPluginHostContext } from "@rig/core/plugin-host-context";
|
|
6
|
+
import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
7
|
+
var LifecycleGitAgentCap = defineCapability(LIFECYCLE_GIT_AGENT);
|
|
8
|
+
var hostContextByRoot = new Map;
|
|
9
|
+
async function ensureHostContext(projectRoot) {
|
|
10
|
+
let cached = hostContextByRoot.get(projectRoot);
|
|
11
|
+
if (!cached) {
|
|
12
|
+
cached = buildPluginHostContext(projectRoot);
|
|
13
|
+
hostContextByRoot.set(projectRoot, cached);
|
|
14
|
+
}
|
|
15
|
+
await cached;
|
|
16
|
+
}
|
|
17
|
+
async function loadLifecycleGit(projectRoot) {
|
|
18
|
+
await ensureHostContext(projectRoot);
|
|
19
|
+
const git = await loadCapabilityForRoot(projectRoot, LifecycleGitAgentCap);
|
|
20
|
+
if (!git) {
|
|
21
|
+
throw new Error("lifecycle git capability unavailable: load @rig/bundle-default-lifecycle (default bundle) before running rig-agent git commands.");
|
|
22
|
+
}
|
|
23
|
+
return git;
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
loadLifecycleGit
|
|
27
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type RepoPins = Record<string, string>;
|
|
2
|
+
export declare function repoEnsure(projectRoot: string, taskId?: string): void;
|
|
3
|
+
export declare function repoPins(projectRoot: string, taskId?: string): RepoPins;
|
|
4
|
+
export declare function repoVerify(projectRoot: string, taskId?: string): boolean;
|
|
5
|
+
export declare function repoDiscover(projectRoot: string, taskId?: string): RepoPins;
|
|
6
|
+
export declare function repoBaseline(projectRoot: string, refresh?: boolean): RepoPins;
|
|
7
|
+
export declare function resetBaseline(projectRoot: string, keepTaskStatus: boolean): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/provider-plugin/src/agent-harness/repo-ops.ts
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
4
|
+
import { basename, dirname, resolve } from "path";
|
|
5
|
+
import { loadRuntimeContext } from "@rig/core/runtime-context";
|
|
6
|
+
import {
|
|
7
|
+
MANAGED_REPO_SERVICE_CAPABILITY,
|
|
8
|
+
TASK_DATA_SERVICE_CAPABILITY
|
|
9
|
+
} from "@rig/contracts";
|
|
10
|
+
import { defineCapability } from "@rig/core/capability";
|
|
11
|
+
import { getInstalledCapability, requireInstalledCapability } from "@rig/core/capability-loaders";
|
|
12
|
+
import { resolveHarnessPaths } from "@rig/core/harness-paths";
|
|
13
|
+
var ManagedRepoCap = defineCapability(MANAGED_REPO_SERVICE_CAPABILITY);
|
|
14
|
+
var getManagedRepoService = () => getInstalledCapability(ManagedRepoCap);
|
|
15
|
+
var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
|
|
16
|
+
var taskData = () => requireInstalledCapability(TaskDataCap, "task-data capability unavailable: load @rig/task-sources-plugin (default bundle) before resolving task repos.");
|
|
17
|
+
function runCapture(command, cwd, env) {
|
|
18
|
+
const result = Bun.spawnSync(command, {
|
|
19
|
+
cwd,
|
|
20
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
21
|
+
stdout: "pipe",
|
|
22
|
+
stderr: "pipe",
|
|
23
|
+
stdin: "ignore"
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
exitCode: result.exitCode,
|
|
27
|
+
stdout: result.stdout.toString(),
|
|
28
|
+
stderr: result.stderr.toString()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function readJsonFile(path, fallback) {
|
|
32
|
+
if (!existsSync(path))
|
|
33
|
+
return fallback;
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
36
|
+
} catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function nowIso() {
|
|
41
|
+
return new Date().toISOString();
|
|
42
|
+
}
|
|
43
|
+
function managedRepoEntries() {
|
|
44
|
+
return getManagedRepoService()?.listManagedRepoEntries() ?? [];
|
|
45
|
+
}
|
|
46
|
+
function requireManagedRepoService() {
|
|
47
|
+
const service = getManagedRepoService();
|
|
48
|
+
if (!service) {
|
|
49
|
+
throw new Error("managed-repo service not installed: configure @rig/repos-plugin in rig.config to use managed-repo operations.");
|
|
50
|
+
}
|
|
51
|
+
return service;
|
|
52
|
+
}
|
|
53
|
+
function primaryManagedRepoId() {
|
|
54
|
+
const entries = managedRepoEntries();
|
|
55
|
+
return entries.length > 0 ? entries[0].id : null;
|
|
56
|
+
}
|
|
57
|
+
function primaryManagedRepoAlias() {
|
|
58
|
+
const entries = managedRepoEntries();
|
|
59
|
+
return entries.length > 0 ? entries[0].alias : null;
|
|
60
|
+
}
|
|
61
|
+
function repoEnsure(projectRoot, taskId) {
|
|
62
|
+
const monorepo = ensureMonorepoReady(projectRoot);
|
|
63
|
+
const resolvedTask = taskId || taskData().currentTaskId(projectRoot);
|
|
64
|
+
if (!resolvedTask) {
|
|
65
|
+
if (monorepo) {
|
|
66
|
+
const alias = primaryManagedRepoAlias();
|
|
67
|
+
if (alias) {
|
|
68
|
+
persistBaselinePins(projectRoot, { [alias]: monorepo.headCommit });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
console.log("No active task. Refreshed baseline repo pins.");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const pins = resolvedPins(projectRoot, resolvedTask);
|
|
75
|
+
applyPins(projectRoot, pins);
|
|
76
|
+
verifyPins(projectRoot, pins);
|
|
77
|
+
}
|
|
78
|
+
function repoPins(projectRoot, taskId) {
|
|
79
|
+
const resolvedTask = taskId || taskData().currentTaskId(projectRoot);
|
|
80
|
+
if (!resolvedTask) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
return resolvedPins(projectRoot, resolvedTask);
|
|
84
|
+
}
|
|
85
|
+
function repoVerify(projectRoot, taskId) {
|
|
86
|
+
const resolvedTask = taskId || taskData().currentTaskId(projectRoot);
|
|
87
|
+
if (!resolvedTask) {
|
|
88
|
+
console.log("No active task. Nothing to verify.");
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
const pins = resolvedPins(projectRoot, resolvedTask);
|
|
92
|
+
return verifyPins(projectRoot, pins);
|
|
93
|
+
}
|
|
94
|
+
function repoDiscover(projectRoot, taskId) {
|
|
95
|
+
const resolvedTask = taskId || taskData().currentTaskId(projectRoot);
|
|
96
|
+
if (!resolvedTask) {
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
const explicit = explicitPins(projectRoot, resolvedTask);
|
|
100
|
+
if (Object.keys(explicit).length > 0) {
|
|
101
|
+
return explicit;
|
|
102
|
+
}
|
|
103
|
+
return discoverPins(projectRoot, resolvedTask);
|
|
104
|
+
}
|
|
105
|
+
function repoBaseline(projectRoot, refresh = false) {
|
|
106
|
+
const paths = resolveRepoDiscoveryPaths(projectRoot);
|
|
107
|
+
if (!refresh && existsSync(paths.baseRepoPinsPath)) {
|
|
108
|
+
const baseline = readJsonFile(paths.baseRepoPinsPath, { recorded_at: nowIso(), repos: {} });
|
|
109
|
+
return baseline.repos || {};
|
|
110
|
+
}
|
|
111
|
+
const id = primaryManagedRepoId();
|
|
112
|
+
if (!id) {
|
|
113
|
+
return persistBaselinePins(projectRoot, {});
|
|
114
|
+
}
|
|
115
|
+
const synced = requireManagedRepoService().syncManagedRepo(projectRoot, id);
|
|
116
|
+
const alias = primaryManagedRepoAlias() ?? id;
|
|
117
|
+
return persistBaselinePins(projectRoot, { [alias]: synced.headCommit });
|
|
118
|
+
}
|
|
119
|
+
function resetBaseline(projectRoot, keepTaskStatus) {
|
|
120
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
121
|
+
const label = primaryManagedRepoAlias() ?? "monorepo";
|
|
122
|
+
resetRepoToOriginMain(projectRoot, paths.monorepoRoot, label);
|
|
123
|
+
deleteBranches(projectRoot, paths.monorepoRoot, label, "rig/*");
|
|
124
|
+
deleteBranches(projectRoot, projectRoot, "project-rig", "codex/*");
|
|
125
|
+
repoBaseline(projectRoot, true);
|
|
126
|
+
console.log("OK: refreshed baseline repo pins");
|
|
127
|
+
if (!keepTaskStatus) {
|
|
128
|
+
reopenClosedTasks(projectRoot);
|
|
129
|
+
} else {
|
|
130
|
+
console.log("INFO: task status unchanged (--keep-task-status)");
|
|
131
|
+
}
|
|
132
|
+
clearDirectory(paths.artifactsDir, ".gitkeep");
|
|
133
|
+
clearDirectory(paths.logsDir, ".gitkeep");
|
|
134
|
+
for (const stateFile of ["hook_trips.log", "task-repo-commits.json", "current-task-id.txt"]) {
|
|
135
|
+
rmSync(resolve(paths.stateDir, stateFile), { force: true });
|
|
136
|
+
}
|
|
137
|
+
console.log("OK: cleared harness artifacts/logs/runtime state");
|
|
138
|
+
console.log("");
|
|
139
|
+
console.log("Baseline reset complete.");
|
|
140
|
+
}
|
|
141
|
+
function ensureMonorepoReady(projectRoot) {
|
|
142
|
+
const id = primaryManagedRepoId();
|
|
143
|
+
if (!id) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const synced = requireManagedRepoService().syncManagedRepo(projectRoot, id);
|
|
147
|
+
const sha = synced.headCommit.slice(0, 7);
|
|
148
|
+
console.log(`Monorepo ready: ${synced.layout.alias}@${sha}`);
|
|
149
|
+
return synced;
|
|
150
|
+
}
|
|
151
|
+
function persistBaselinePins(projectRoot, repos) {
|
|
152
|
+
const paths = resolveRepoDiscoveryPaths(projectRoot);
|
|
153
|
+
mkdirSync(resolve(paths.baseRepoPinsPath, ".."), { recursive: true });
|
|
154
|
+
writeFileSync(paths.baseRepoPinsPath, `${JSON.stringify({ recorded_at: nowIso(), repos }, null, 2)}
|
|
155
|
+
`, "utf-8");
|
|
156
|
+
return repos;
|
|
157
|
+
}
|
|
158
|
+
function resetRepoToOriginMain(projectRoot, repoPath, label) {
|
|
159
|
+
if (!existsSync(resolve(repoPath, ".git"))) {
|
|
160
|
+
console.log(`WARN: ${label} repo missing at ${repoPath}, skipping reset.`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
runGitCapture(["git", "-C", repoPath, "fetch", "origin", "main"], projectRoot);
|
|
164
|
+
const hasMain = runGitCapture(["git", "-C", repoPath, "show-ref", "--verify", "--quiet", "refs/heads/main"], projectRoot).exitCode === 0;
|
|
165
|
+
if (hasMain) {
|
|
166
|
+
runGitCapture(["git", "-C", repoPath, "checkout", "main"], projectRoot);
|
|
167
|
+
} else {
|
|
168
|
+
runGitCapture(["git", "-C", repoPath, "checkout", "-B", "main", "origin/main"], projectRoot);
|
|
169
|
+
}
|
|
170
|
+
const resetResult = runGitCapture(["git", "-C", repoPath, "reset", "--hard", "origin/main"], projectRoot);
|
|
171
|
+
if (resetResult.exitCode !== 0) {
|
|
172
|
+
throw new Error(`Failed to reset ${label} to origin/main:
|
|
173
|
+
${resetResult.stderr || resetResult.stdout}`);
|
|
174
|
+
}
|
|
175
|
+
runGitCapture(["git", "-C", repoPath, "clean", "-fd"], projectRoot);
|
|
176
|
+
const head = runGitCapture(["git", "-C", repoPath, "rev-parse", "--short", "HEAD"], projectRoot).stdout.trim();
|
|
177
|
+
console.log(`OK: ${label} reset to origin/main (${head})`);
|
|
178
|
+
}
|
|
179
|
+
function deleteBranches(projectRoot, repoPath, label, pattern) {
|
|
180
|
+
if (!existsSync(resolve(repoPath, ".git"))) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const listed = runGitCapture(["git", "-C", repoPath, "for-each-ref", `refs/heads/${pattern}`, "--format=%(refname:short)"], projectRoot).stdout.split(/\r?\n/).filter(Boolean);
|
|
184
|
+
let deleted = 0;
|
|
185
|
+
for (const branch of listed) {
|
|
186
|
+
runGitCapture(["git", "-C", repoPath, "branch", "-D", branch], projectRoot);
|
|
187
|
+
deleted += 1;
|
|
188
|
+
}
|
|
189
|
+
console.log(`OK: ${label} deleted ${deleted} branch(es) matching ${pattern}`);
|
|
190
|
+
}
|
|
191
|
+
function reopenClosedTasks(projectRoot) {
|
|
192
|
+
const paths = resolveHarnessPaths(projectRoot);
|
|
193
|
+
const issuesPath = resolve(paths.monorepoRoot, ".beads/issues.jsonl");
|
|
194
|
+
if (!existsSync(issuesPath)) {
|
|
195
|
+
console.log("WARN: .beads/issues.jsonl not found, skipping task reopen.");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const content = readFileSync(issuesPath, "utf-8");
|
|
199
|
+
const closed = [];
|
|
200
|
+
for (const line of content.split(/\r?\n/)) {
|
|
201
|
+
const trimmed = line.trim();
|
|
202
|
+
if (!trimmed) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const issue = JSON.parse(trimmed);
|
|
207
|
+
const normalizedStatus = taskData().normalizeTaskLifecycleStatus(issue.status);
|
|
208
|
+
if (issue.issue_type === "task" && issue.id && (normalizedStatus === "completed" || normalizedStatus === "cancelled" || normalizedStatus === "blocked")) {
|
|
209
|
+
closed.push(issue.id);
|
|
210
|
+
}
|
|
211
|
+
} catch {}
|
|
212
|
+
}
|
|
213
|
+
if (closed.length === 0) {
|
|
214
|
+
console.log("OK: no terminal tasks to reopen");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
for (const id of closed) {
|
|
218
|
+
const update = runCapture(["br", "--no-db", "update", id, "--status", "open"], paths.monorepoRoot);
|
|
219
|
+
if (update.exitCode === 0) {
|
|
220
|
+
console.log(`OK: reopened task ${id}`);
|
|
221
|
+
} else {
|
|
222
|
+
console.log(`WARN: failed to reopen task ${id}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function clearDirectory(dir, keepName) {
|
|
227
|
+
if (!existsSync(dir)) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
for (const entry of readdirSync(dir)) {
|
|
231
|
+
if (entry === keepName) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
rmSync(resolve(dir, entry), { recursive: true, force: true });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function resolvedPins(projectRoot, taskId) {
|
|
238
|
+
const explicit = explicitPins(projectRoot, taskId);
|
|
239
|
+
if (Object.keys(explicit).length > 0) {
|
|
240
|
+
return explicit;
|
|
241
|
+
}
|
|
242
|
+
return discoverPins(projectRoot, taskId);
|
|
243
|
+
}
|
|
244
|
+
function explicitPins(projectRoot, taskId) {
|
|
245
|
+
const repoPins2 = readRepoDiscoveryTaskConfig(projectRoot)[taskId]?.repo_pins || {};
|
|
246
|
+
const normalized = {};
|
|
247
|
+
const validAliases = new Set(managedRepoEntries().map((e) => e.alias));
|
|
248
|
+
for (const [key, value] of Object.entries(repoPins2)) {
|
|
249
|
+
if (!value) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (validAliases.size > 0 && !validAliases.has(key)) {
|
|
253
|
+
throw new Error(`Unsupported repo pin key for ${taskId}: ${key}. Known aliases: ${[...validAliases].join(", ") || "(none registered)"}`);
|
|
254
|
+
}
|
|
255
|
+
const existing = normalized[key];
|
|
256
|
+
if (existing && existing !== value) {
|
|
257
|
+
throw new Error(`Conflicting explicit repo pins for ${key}: ${existing} vs ${value}`);
|
|
258
|
+
}
|
|
259
|
+
normalized[key] = value;
|
|
260
|
+
}
|
|
261
|
+
return normalized;
|
|
262
|
+
}
|
|
263
|
+
function taskDependencies(projectRoot, taskId) {
|
|
264
|
+
return taskData().taskDependencyIds(projectRoot, taskId);
|
|
265
|
+
}
|
|
266
|
+
function discoverPins(projectRoot, taskId) {
|
|
267
|
+
const deps = taskDependencies(projectRoot, taskId);
|
|
268
|
+
if (deps.length === 0) {
|
|
269
|
+
return repoBaseline(projectRoot, true);
|
|
270
|
+
}
|
|
271
|
+
const paths = resolveRepoDiscoveryPaths(projectRoot);
|
|
272
|
+
const state = readJsonFile(paths.taskRepoCommitsPath, {});
|
|
273
|
+
const pinKeys = managedRepoEntries().map((e) => e.alias);
|
|
274
|
+
if (pinKeys.length === 0) {
|
|
275
|
+
return {};
|
|
276
|
+
}
|
|
277
|
+
const discovered = {};
|
|
278
|
+
for (const key of pinKeys) {
|
|
279
|
+
const commits = new Set;
|
|
280
|
+
for (const dep of deps) {
|
|
281
|
+
const fromState = state[dep]?.repos?.[key];
|
|
282
|
+
if (fromState) {
|
|
283
|
+
commits.add(fromState);
|
|
284
|
+
}
|
|
285
|
+
const fromArtifact = readPinFromArtifact(projectRoot, dep, key);
|
|
286
|
+
if (fromArtifact) {
|
|
287
|
+
commits.add(fromArtifact);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (commits.size > 1) {
|
|
291
|
+
const values = [...commits].join(`
|
|
292
|
+
- `);
|
|
293
|
+
throw new Error(`Conflicting discovered pins for ${key} from dependency graph of ${taskId}:
|
|
294
|
+
- ${values}`);
|
|
295
|
+
}
|
|
296
|
+
if (commits.size === 1) {
|
|
297
|
+
discovered[key] = [...commits][0];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return discovered;
|
|
301
|
+
}
|
|
302
|
+
function readRepoDiscoveryTaskConfig(projectRoot) {
|
|
303
|
+
try {
|
|
304
|
+
return taskData().readSourceTaskConfig(projectRoot);
|
|
305
|
+
} catch {
|
|
306
|
+
return taskData().readTaskConfig(projectRoot);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function resolveRepoDiscoveryPaths(projectRoot) {
|
|
310
|
+
const monorepoRoot = requireManagedRepoService().resolveMonorepoRepoLayout(projectRoot).checkoutRoot;
|
|
311
|
+
const explicitHostProjectRoot = (process.env.RIG_HOST_PROJECT_ROOT || "").trim();
|
|
312
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
313
|
+
const hostProjectRoot = explicitHostProjectRoot && shouldUseHostProjectStateRoot(normalizedProjectRoot) ? explicitHostProjectRoot : normalizedProjectRoot;
|
|
314
|
+
const stateDir = resolve(hostProjectRoot, ".rig", "state");
|
|
315
|
+
return {
|
|
316
|
+
monorepoRoot,
|
|
317
|
+
taskRepoCommitsPath: resolve(stateDir, "task-repo-commits.json"),
|
|
318
|
+
baseRepoPinsPath: resolve(stateDir, "base-repo-pins.json")
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function shouldUseHostProjectStateRoot(projectRoot) {
|
|
322
|
+
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
323
|
+
if (runtimeWorkspace && resolve(runtimeWorkspace) === projectRoot) {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
return basename(dirname(projectRoot)) === ".worktrees";
|
|
327
|
+
}
|
|
328
|
+
function readPinFromArtifact(projectRoot, depTask, repoKey) {
|
|
329
|
+
const snapshot = resolve(taskData().artifactDirForId(projectRoot, depTask), "git-state.txt");
|
|
330
|
+
if (!existsSync(snapshot)) {
|
|
331
|
+
return "";
|
|
332
|
+
}
|
|
333
|
+
const content = readFileSync(snapshot, "utf-8");
|
|
334
|
+
const chunk = content.split(/\r?\n/);
|
|
335
|
+
let inSection = false;
|
|
336
|
+
for (const line of chunk) {
|
|
337
|
+
if (line.startsWith("## ")) {
|
|
338
|
+
inSection = line.startsWith(`## ${repoKey}`);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (!inSection) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (line.startsWith("head:")) {
|
|
345
|
+
return line.replace(/^head:\s*/, "").trim();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
function repoPath(projectRoot, key) {
|
|
351
|
+
const managed = getManagedRepoService()?.resolveManagedRepoLayoutByAlias(projectRoot, key) ?? null;
|
|
352
|
+
if (managed) {
|
|
353
|
+
return managed.checkoutRoot;
|
|
354
|
+
}
|
|
355
|
+
return key.startsWith("/") ? key : resolve(projectRoot, key);
|
|
356
|
+
}
|
|
357
|
+
function applyPins(projectRoot, pins) {
|
|
358
|
+
for (const [key, commit] of Object.entries(pins)) {
|
|
359
|
+
const path = repoPath(projectRoot, key);
|
|
360
|
+
if (!existsSync(resolve(path, ".git"))) {
|
|
361
|
+
throw new Error(`Repo for pin not available: ${key} (${path})`);
|
|
362
|
+
}
|
|
363
|
+
let hasCommit = runGitCapture(["git", "-C", path, "cat-file", "-e", `${commit}^{commit}`], projectRoot).exitCode === 0;
|
|
364
|
+
if (!hasCommit) {
|
|
365
|
+
runGitCapture(["git", "-C", path, "fetch", "--all", "--tags", "--prune"], projectRoot);
|
|
366
|
+
hasCommit = runGitCapture(["git", "-C", path, "cat-file", "-e", `${commit}^{commit}`], projectRoot).exitCode === 0;
|
|
367
|
+
}
|
|
368
|
+
if (!hasCommit) {
|
|
369
|
+
throw new Error(`Commit not found for ${key}: ${commit}`);
|
|
370
|
+
}
|
|
371
|
+
const current = runGitCapture(["git", "-C", path, "rev-parse", "HEAD"], projectRoot).stdout.trim();
|
|
372
|
+
if (current === commit) {
|
|
373
|
+
console.log(`Repo pin: ${key} already at ${commit}`);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const branch = runGitCapture(["git", "-C", path, "rev-parse", "--abbrev-ref", "HEAD"], projectRoot).stdout.trim();
|
|
377
|
+
const checkout = branch.startsWith("rig/") ? runGitCapture(["git", "-C", path, "reset", "--hard", commit], projectRoot) : runGitCapture(["git", "-C", path, "checkout", "--detach", commit], projectRoot);
|
|
378
|
+
if (checkout.exitCode !== 0) {
|
|
379
|
+
throw new Error(`Failed to apply repo pin ${key} -> ${commit}:
|
|
380
|
+
${checkout.stderr || checkout.stdout}`);
|
|
381
|
+
}
|
|
382
|
+
console.log(`Repo pin: ${key} -> ${commit}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function verifyPins(projectRoot, pins) {
|
|
386
|
+
let ok = true;
|
|
387
|
+
for (const [key, expected] of Object.entries(pins)) {
|
|
388
|
+
const path = repoPath(projectRoot, key);
|
|
389
|
+
if (!existsSync(resolve(path, ".git"))) {
|
|
390
|
+
console.error(`ERROR: repo missing during pin verification: ${key}`);
|
|
391
|
+
ok = false;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const current = runGitCapture(["git", "-C", path, "rev-parse", "HEAD"], projectRoot).stdout.trim();
|
|
395
|
+
if (current === expected) {
|
|
396
|
+
console.log(`Repo verify: ${key} at ${current}`);
|
|
397
|
+
} else {
|
|
398
|
+
console.error(`ERROR: repo pin mismatch for ${key}: expected ${expected}, got ${current}`);
|
|
399
|
+
ok = false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return ok;
|
|
403
|
+
}
|
|
404
|
+
function runGitCapture(command, projectRoot) {
|
|
405
|
+
return runCapture(command, projectRoot, resolveRuntimeGitEnv());
|
|
406
|
+
}
|
|
407
|
+
function resolveRuntimeGitEnv() {
|
|
408
|
+
if (process.env.GIT_SSH_COMMAND?.trim()) {
|
|
409
|
+
return {
|
|
410
|
+
HOME: process.env.HOME ?? "",
|
|
411
|
+
GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const runtimeRoot = process.env.RIG_RUNTIME_HOME?.trim() || (process.env.RIG_RUNTIME_CONTEXT_FILE?.trim() ? resolve(process.env.RIG_RUNTIME_CONTEXT_FILE, "..") : inferRuntimeRootFromWorkspace(process.cwd()));
|
|
415
|
+
const runtimeHome = runtimeRoot ? resolve(runtimeRoot, "home") : process.env.HOME?.trim() || "";
|
|
416
|
+
if (!runtimeHome) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const knownHostsPath = resolve(runtimeHome, ".ssh", "known_hosts");
|
|
420
|
+
if (!existsSync(knownHostsPath)) {
|
|
421
|
+
return { HOME: runtimeHome };
|
|
422
|
+
}
|
|
423
|
+
const agentSshKey = resolve(runtimeHome, ".ssh", "rig-agent-key");
|
|
424
|
+
const sshParts = [
|
|
425
|
+
"ssh",
|
|
426
|
+
`-o UserKnownHostsFile="${knownHostsPath}"`,
|
|
427
|
+
"-o StrictHostKeyChecking=yes",
|
|
428
|
+
"-F /dev/null"
|
|
429
|
+
];
|
|
430
|
+
if (existsSync(agentSshKey)) {
|
|
431
|
+
sshParts.splice(1, 0, `-i "${agentSshKey}"`, "-o IdentitiesOnly=yes");
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
HOME: runtimeHome,
|
|
435
|
+
GIT_SSH_COMMAND: sshParts.join(" ")
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function inferRuntimeRootFromWorkspace(cwd) {
|
|
439
|
+
const contextPath = findRuntimeContextFile(cwd);
|
|
440
|
+
if (!contextPath || !existsSync(contextPath)) {
|
|
441
|
+
return "";
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
loadRuntimeContext(contextPath);
|
|
445
|
+
return resolve(contextPath, "..");
|
|
446
|
+
} catch {
|
|
447
|
+
return "";
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function findRuntimeContextFile(startPath) {
|
|
451
|
+
let current = resolve(startPath);
|
|
452
|
+
while (true) {
|
|
453
|
+
const candidate = resolve(current, "runtime-context.json");
|
|
454
|
+
if (existsSync(candidate)) {
|
|
455
|
+
return candidate;
|
|
456
|
+
}
|
|
457
|
+
const parent = resolve(current, "..");
|
|
458
|
+
if (parent === current) {
|
|
459
|
+
return "";
|
|
460
|
+
}
|
|
461
|
+
current = parent;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
export {
|
|
465
|
+
resetBaseline,
|
|
466
|
+
repoVerify,
|
|
467
|
+
repoPins,
|
|
468
|
+
repoEnsure,
|
|
469
|
+
repoDiscover,
|
|
470
|
+
repoBaseline
|
|
471
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runRigAgentEntrypoint(): Promise<void>;
|