@h-rig/cli-surface-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/src/app/drone-ui.d.ts +0 -11
- package/dist/src/app/drone-ui.js +0 -114
- package/dist/src/commands/_async-ui.d.ts +1 -1
- package/dist/src/commands/_cli-format.d.ts +0 -29
- package/dist/src/commands/_cli-format.js +59 -113
- package/dist/src/commands/_connection-state.d.ts +6 -33
- package/dist/src/commands/_connection-state.js +654 -138
- package/dist/src/commands/_doctor-checks.d.ts +2 -5
- package/dist/src/commands/_doctor-checks.js +10 -9
- package/dist/src/commands/_help-catalog.d.ts +2 -1
- package/dist/src/commands/_help-catalog.js +654 -7
- package/dist/src/commands/_inprocess-services.d.ts +5 -5
- package/dist/src/commands/_inprocess-services.js +1 -1
- package/dist/src/commands/_parsers.js +651 -12
- package/dist/src/commands/_paths.d.ts +0 -2
- package/dist/src/commands/_paths.js +2 -10
- package/dist/src/commands/_pi-install.d.ts +2 -12
- package/dist/src/commands/_pi-install.js +3 -36
- package/dist/src/commands/_policy.js +659 -20
- package/dist/src/commands/agent.d.ts +1 -1
- package/dist/src/commands/agent.js +675 -24
- package/dist/src/commands/config.d.ts +1 -1
- package/dist/src/commands/config.js +656 -21
- package/dist/src/commands/dist.d.ts +1 -1
- package/dist/src/commands/dist.js +828 -102
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +658 -12
- package/dist/src/commands/github.d.ts +1 -1
- package/dist/src/commands/github.js +658 -19
- package/dist/src/commands/inbox.d.ts +12 -8
- package/dist/src/commands/inbox.js +741 -22
- package/dist/src/commands/init.d.ts +17 -19
- package/dist/src/commands/init.js +836 -306
- package/dist/src/commands/inspect.d.ts +5 -6
- package/dist/src/commands/inspect.js +754 -42
- package/dist/src/commands/pi.d.ts +1 -1
- package/dist/src/commands/pi.js +655 -16
- package/dist/src/commands/plugin.d.ts +9 -9
- package/dist/src/commands/plugin.js +652 -13
- package/dist/src/commands/profile-and-review.d.ts +1 -1
- package/dist/src/commands/profile-and-review.js +655 -16
- package/dist/src/commands/queue.d.ts +1 -1
- package/dist/src/commands/queue.js +871 -12
- package/dist/src/commands/remote-client.d.ts +152 -0
- package/dist/src/commands/remote-client.js +475 -0
- package/dist/src/commands/remote.d.ts +1 -1
- package/dist/src/commands/remote.js +1100 -29
- package/dist/src/commands/repo-git-harness.d.ts +1 -1
- package/dist/src/commands/repo-git-harness.js +2321 -47
- package/dist/src/commands/run.d.ts +10 -6
- package/dist/src/commands/run.js +830 -50
- package/dist/src/commands/server.d.ts +1 -1
- package/dist/src/commands/server.js +649 -11
- package/dist/src/commands/setup.d.ts +2 -2
- package/dist/src/commands/setup.js +829 -18
- package/dist/src/commands/stats.d.ts +2 -4
- package/dist/src/commands/stats.js +1299 -20
- package/dist/src/commands/test.d.ts +1 -1
- package/dist/src/commands/test.js +648 -9
- package/dist/src/commands/triage.d.ts +2 -3
- package/dist/src/commands/triage.js +657 -11
- package/dist/src/commands/workspace.d.ts +1 -1
- package/dist/src/commands/workspace.js +1280 -15
- package/dist/src/control-plane/agent-binary-build.d.ts +9 -0
- package/dist/src/control-plane/agent-binary-build.js +88 -0
- package/dist/src/control-plane/embedded-native-assets.d.ts +7 -0
- package/dist/src/control-plane/embedded-native-assets.js +6 -0
- package/dist/src/control-plane/guard.d.ts +17 -0
- package/dist/src/control-plane/guard.js +684 -0
- package/dist/src/control-plane/harness-cli.d.ts +12 -0
- package/dist/src/control-plane/harness-cli.js +1623 -0
- package/dist/src/control-plane/native/git-ops.d.ts +67 -0
- package/dist/src/control-plane/native/git-ops.js +1381 -0
- package/dist/src/control-plane/native/github-auth-env.d.ts +1 -0
- package/dist/src/control-plane/native/github-auth-env.js +21 -0
- package/dist/src/control-plane/native/host-git.d.ts +4 -0
- package/dist/src/control-plane/native/host-git.js +51 -0
- package/dist/src/control-plane/priority-queue.d.ts +22 -0
- package/dist/src/control-plane/priority-queue.js +212 -0
- package/dist/src/control-plane/rigfig.d.ts +9 -0
- package/dist/src/control-plane/rigfig.js +70 -0
- package/dist/src/control-plane/scope.d.ts +3 -0
- package/dist/src/control-plane/scope.js +58 -0
- package/dist/src/control-plane/setup-status.d.ts +44 -0
- package/dist/src/control-plane/setup-status.js +164 -0
- package/dist/src/control-plane/task-data.d.ts +2 -0
- package/dist/src/control-plane/task-data.js +12 -0
- package/dist/src/control-plane/workspace-ops.d.ts +79 -0
- package/dist/src/control-plane/workspace-ops.js +639 -0
- package/dist/src/help-catalog-data.d.ts +7 -0
- package/dist/src/help-catalog-data.js +660 -0
- package/dist/src/kernel-dispatch.js +1 -3
- package/dist/src/plugin.js +10072 -30
- package/dist/src/runner.d.ts +7 -9
- package/dist/src/runner.js +750 -30
- package/package.json +12 -13
- package/dist/src/commands/_json-output.d.ts +0 -11
- package/dist/src/commands/_json-output.js +0 -54
- package/dist/src/commands/_pi-frontend.d.ts +0 -35
- package/dist/src/commands/_pi-frontend.js +0 -64
- package/dist/src/commands/_run-driver-helpers.d.ts +0 -26
- package/dist/src/commands/_run-driver-helpers.js +0 -132
- package/dist/src/commands/task-run-driver.d.ts +0 -93
- package/dist/src/commands/task-run-driver.js +0 -136
- package/dist/src/commands/task.d.ts +0 -46
- package/dist/src/commands/task.js +0 -555
- package/dist/src/provider-model.d.ts +0 -34
- package/dist/src/provider-model.js +0 -56
- package/dist/src/rig-config-package-deps.d.ts +0 -10
- package/dist/src/rig-config-package-deps.js +0 -272
- package/dist/src/version.d.ts +0 -8
- package/dist/src/version.js +0 -47
|
@@ -15,83 +15,13 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
|
|
18
|
-
// packages/cli-surface-plugin/src/commands/_spinner.ts
|
|
19
|
-
function createTtySpinner(input) {
|
|
20
|
-
const output = input.output ?? process.stdout;
|
|
21
|
-
const isTty = output.isTTY === true;
|
|
22
|
-
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
23
|
-
let label = input.label;
|
|
24
|
-
let frame = 0;
|
|
25
|
-
let paused = false;
|
|
26
|
-
let stopped = false;
|
|
27
|
-
let lastPrintedLabel = "";
|
|
28
|
-
const render = () => {
|
|
29
|
-
if (stopped || paused)
|
|
30
|
-
return;
|
|
31
|
-
if (!isTty) {
|
|
32
|
-
if (label !== lastPrintedLabel) {
|
|
33
|
-
output.write(`${label}
|
|
34
|
-
`);
|
|
35
|
-
lastPrintedLabel = label;
|
|
36
|
-
}
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
frame = (frame + 1) % frames.length;
|
|
40
|
-
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
41
|
-
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
42
|
-
};
|
|
43
|
-
const clearLine = () => {
|
|
44
|
-
if (isTty)
|
|
45
|
-
output.write("\r\x1B[2K");
|
|
46
|
-
};
|
|
47
|
-
render();
|
|
48
|
-
const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
|
|
49
|
-
return {
|
|
50
|
-
setLabel(next) {
|
|
51
|
-
label = next;
|
|
52
|
-
render();
|
|
53
|
-
},
|
|
54
|
-
pause() {
|
|
55
|
-
paused = true;
|
|
56
|
-
clearLine();
|
|
57
|
-
},
|
|
58
|
-
resume() {
|
|
59
|
-
if (stopped)
|
|
60
|
-
return;
|
|
61
|
-
paused = false;
|
|
62
|
-
render();
|
|
63
|
-
},
|
|
64
|
-
stop(finalLine) {
|
|
65
|
-
if (stopped)
|
|
66
|
-
return;
|
|
67
|
-
stopped = true;
|
|
68
|
-
if (timer)
|
|
69
|
-
clearInterval(timer);
|
|
70
|
-
clearLine();
|
|
71
|
-
if (finalLine)
|
|
72
|
-
output.write(`${finalLine}
|
|
73
|
-
`);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
var SPINNER_FRAMES;
|
|
78
|
-
var init__spinner = __esm(() => {
|
|
79
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
80
|
-
});
|
|
81
|
-
|
|
82
18
|
// packages/cli-surface-plugin/src/app/drone-ui.ts
|
|
83
19
|
var exports_drone_ui = {};
|
|
84
20
|
__export(exports_drone_ui, {
|
|
85
|
-
droneWarn: () => droneWarn,
|
|
86
21
|
droneText: () => droneText,
|
|
87
|
-
droneStep: () => droneStep,
|
|
88
|
-
droneSpinner: () => droneSpinner,
|
|
89
22
|
droneSelect: () => droneSelect,
|
|
90
23
|
droneOutro: () => droneOutro,
|
|
91
|
-
droneNote: () => droneNote,
|
|
92
24
|
droneIntro: () => droneIntro,
|
|
93
|
-
droneInfo: () => droneInfo,
|
|
94
|
-
droneError: () => droneError,
|
|
95
25
|
droneConfirm: () => droneConfirm,
|
|
96
26
|
droneCancel: () => droneCancel
|
|
97
27
|
});
|
|
@@ -111,12 +41,6 @@ function fg(hex) {
|
|
|
111
41
|
function bold(text) {
|
|
112
42
|
return `\x1B[1m${text}\x1B[22m`;
|
|
113
43
|
}
|
|
114
|
-
function renderMicroDroneFrame(frame) {
|
|
115
|
-
const tick = Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame));
|
|
116
|
-
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
117
|
-
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
118
|
-
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
119
|
-
}
|
|
120
44
|
function hairline(width = Math.min(process.stdout.columns ?? 80, 100)) {
|
|
121
45
|
return ink4("\u2500".repeat(Math.max(10, width)));
|
|
122
46
|
}
|
|
@@ -130,58 +54,19 @@ function droneOutro(text) {
|
|
|
130
54
|
console.log(` ${accent("\u25C6")} ${ink2(text)}`);
|
|
131
55
|
console.log("");
|
|
132
56
|
}
|
|
133
|
-
function droneNote(message, title) {
|
|
134
|
-
if (title)
|
|
135
|
-
console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
|
|
136
|
-
for (const line of message.split(`
|
|
137
|
-
`))
|
|
138
|
-
console.log(` ${ink4("\u2502")} ${line}`);
|
|
139
|
-
}
|
|
140
|
-
function droneStep(text) {
|
|
141
|
-
console.log(` ${accent("\u203A")} ${ink(text)}`);
|
|
142
|
-
}
|
|
143
|
-
function droneInfo(text) {
|
|
144
|
-
console.log(` ${cyan("\xB7")} ${ink2(text)}`);
|
|
145
|
-
}
|
|
146
|
-
function droneWarn(text) {
|
|
147
|
-
console.log(` ${yellow("\u25B2")} ${ink2(text)}`);
|
|
148
|
-
}
|
|
149
|
-
function droneError(text) {
|
|
150
|
-
console.log(` ${red("\u2716")} ${ink2(text)}`);
|
|
151
|
-
}
|
|
152
57
|
function droneCancel(text) {
|
|
153
58
|
console.log(` ${red("\u2716")} ${ink3(text)}`);
|
|
154
59
|
}
|
|
155
|
-
function droneSpinner() {
|
|
156
|
-
let active = null;
|
|
157
|
-
return {
|
|
158
|
-
start(message) {
|
|
159
|
-
active = createTtySpinner({
|
|
160
|
-
label: message,
|
|
161
|
-
frames: MICRO_DRONE_FRAMES,
|
|
162
|
-
styleFrame: renderMicroDroneFrame
|
|
163
|
-
});
|
|
164
|
-
},
|
|
165
|
-
stop(message) {
|
|
166
|
-
active?.stop(message ? ` ${accent("\u25C6")} ${ink2(message)}` : undefined);
|
|
167
|
-
active = null;
|
|
168
|
-
},
|
|
169
|
-
error(message) {
|
|
170
|
-
active?.stop(message ? ` ${red("\u2716")} ${ink2(message)}` : undefined);
|
|
171
|
-
active = null;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
60
|
async function runMiniTui(build) {
|
|
176
61
|
const tui = new TUI(new ProcessTerminal);
|
|
177
62
|
let settled = false;
|
|
178
|
-
return await new Promise((
|
|
63
|
+
return await new Promise((resolve6) => {
|
|
179
64
|
const finish = (result) => {
|
|
180
65
|
if (settled)
|
|
181
66
|
return;
|
|
182
67
|
settled = true;
|
|
183
68
|
tui.stop();
|
|
184
|
-
|
|
69
|
+
resolve6(result);
|
|
185
70
|
};
|
|
186
71
|
build(tui, finish);
|
|
187
72
|
tui.start();
|
|
@@ -250,7 +135,6 @@ async function droneConfirm(input) {
|
|
|
250
135
|
}
|
|
251
136
|
var PALETTE, ink, ink2, ink3, ink4, accent, accentDim, cyan, red, yellow, MICRO_BLADES, EYE_FRAMES, MICRO_DRONE_FRAMES, DRONE_SYMBOLS, DRONE_SELECT_THEME;
|
|
252
137
|
var init_drone_ui = __esm(() => {
|
|
253
|
-
init__spinner();
|
|
254
138
|
PALETTE = {
|
|
255
139
|
ink: "#f2f3f6",
|
|
256
140
|
ink2: "#aeb0ba",
|
|
@@ -300,23 +184,662 @@ var init_drone_ui = __esm(() => {
|
|
|
300
184
|
});
|
|
301
185
|
|
|
302
186
|
// packages/cli-surface-plugin/src/commands/init.ts
|
|
303
|
-
import { appendFileSync, existsSync as
|
|
187
|
+
import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
304
188
|
import { spawnSync } from "child_process";
|
|
305
|
-
import {
|
|
189
|
+
import { resolve as resolve6 } from "path";
|
|
306
190
|
|
|
307
191
|
// packages/cli-surface-plugin/src/runner.ts
|
|
308
|
-
import { EventBus } from "@rig/runtime
|
|
309
|
-
import { CliError as RuntimeCliError } from "@rig/
|
|
310
|
-
|
|
311
|
-
|
|
192
|
+
import { EventBus } from "@rig/core/runtime-events";
|
|
193
|
+
import { CliError as RuntimeCliError } from "@rig/contracts";
|
|
194
|
+
|
|
195
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
196
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
197
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
198
|
+
import { resolve } from "path";
|
|
199
|
+
|
|
200
|
+
// packages/cli-surface-plugin/src/control-plane/scope.ts
|
|
201
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
202
|
+
var scopeRegexCache = new Map;
|
|
203
|
+
function unique(values) {
|
|
204
|
+
return [...new Set(values)];
|
|
205
|
+
}
|
|
206
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
207
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
208
|
+
const rules = getScopeRules();
|
|
209
|
+
if (rules?.stripPrefixes) {
|
|
210
|
+
for (const prefix of rules.stripPrefixes) {
|
|
211
|
+
if (normalized.startsWith(prefix)) {
|
|
212
|
+
normalized = normalized.slice(prefix.length);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return normalized;
|
|
217
|
+
}
|
|
218
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
219
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
220
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
221
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
222
|
+
}
|
|
223
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
224
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
225
|
+
}
|
|
226
|
+
return normalizeRelativeScopePath(normalized);
|
|
227
|
+
}
|
|
228
|
+
function scopeGlobToRegex(glob) {
|
|
229
|
+
const cached = scopeRegexCache.get(glob);
|
|
230
|
+
if (cached) {
|
|
231
|
+
return cached;
|
|
232
|
+
}
|
|
233
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
|
|
234
|
+
const compiled = new RegExp(`^${escaped}$`);
|
|
235
|
+
scopeRegexCache.set(glob, compiled);
|
|
236
|
+
return compiled;
|
|
237
|
+
}
|
|
238
|
+
function scopeMatches(path, scopes) {
|
|
239
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
240
|
+
for (const scope of scopes) {
|
|
241
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
242
|
+
for (const candidatePath of pathVariants) {
|
|
243
|
+
for (const candidateScope of scopeVariants) {
|
|
244
|
+
if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
254
|
+
import {
|
|
255
|
+
POLICY_VERSION
|
|
256
|
+
} from "@rig/contracts";
|
|
257
|
+
var DEFAULT_SCOPE = {
|
|
258
|
+
fail_closed: true,
|
|
259
|
+
harness_paths_exempt: true,
|
|
260
|
+
runtime_paths_exempt: true
|
|
261
|
+
};
|
|
262
|
+
var DEFAULT_SANDBOX = {
|
|
263
|
+
mode: "enforce",
|
|
264
|
+
network: true,
|
|
265
|
+
read_deny: [],
|
|
266
|
+
write_allow_from_runtime: true
|
|
267
|
+
};
|
|
268
|
+
var DEFAULT_ISOLATION = {
|
|
269
|
+
default_mode: "worktree",
|
|
270
|
+
repo_symlink_fallback: false,
|
|
271
|
+
strict_provisioning: true,
|
|
272
|
+
fail_closed_on_provision_error: true
|
|
273
|
+
};
|
|
274
|
+
var DEFAULT_COMPLETION = {
|
|
275
|
+
derive_checks_from_scope: true,
|
|
276
|
+
checks: [],
|
|
277
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
278
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
279
|
+
};
|
|
280
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
281
|
+
deps: {
|
|
282
|
+
monorepo_install: false,
|
|
283
|
+
hp_next_install: false
|
|
284
|
+
},
|
|
285
|
+
plugins_require_binaries: true
|
|
286
|
+
};
|
|
287
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
288
|
+
enabled: true
|
|
289
|
+
};
|
|
290
|
+
function defaultPolicy() {
|
|
291
|
+
return {
|
|
292
|
+
version: POLICY_VERSION,
|
|
293
|
+
mode: "enforce",
|
|
294
|
+
scope: { ...DEFAULT_SCOPE },
|
|
295
|
+
rules: [],
|
|
296
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
297
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
298
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
299
|
+
runtime_image: {
|
|
300
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
301
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
302
|
+
},
|
|
303
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
var policyCache = null;
|
|
307
|
+
var policyCachePath = null;
|
|
308
|
+
var seededPolicyConfig = null;
|
|
309
|
+
var compiledRegexCache = new Map;
|
|
310
|
+
function loadPolicy(projectRoot) {
|
|
311
|
+
if (seededPolicyConfig) {
|
|
312
|
+
return seededPolicyConfig;
|
|
313
|
+
}
|
|
314
|
+
const configPath = resolve(projectRoot, "rig/policy/policy.json");
|
|
315
|
+
if (!existsSync(configPath)) {
|
|
316
|
+
return defaultPolicy();
|
|
317
|
+
}
|
|
318
|
+
let mtimeMs;
|
|
319
|
+
try {
|
|
320
|
+
mtimeMs = statSync(configPath).mtimeMs;
|
|
321
|
+
} catch {
|
|
322
|
+
return defaultPolicy();
|
|
323
|
+
}
|
|
324
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
325
|
+
return policyCache.config;
|
|
326
|
+
}
|
|
327
|
+
let parsed;
|
|
328
|
+
try {
|
|
329
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
330
|
+
} catch {
|
|
331
|
+
return defaultPolicy();
|
|
332
|
+
}
|
|
333
|
+
const config = mergeWithDefaults(parsed);
|
|
334
|
+
policyCache = { mtimeMs, config };
|
|
335
|
+
policyCachePath = configPath;
|
|
336
|
+
return config;
|
|
337
|
+
}
|
|
338
|
+
function mergeWithDefaults(parsed) {
|
|
339
|
+
const base = defaultPolicy();
|
|
340
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
341
|
+
base.mode = parsed.mode;
|
|
342
|
+
}
|
|
343
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
344
|
+
const s = parsed.scope;
|
|
345
|
+
if (typeof s.fail_closed === "boolean")
|
|
346
|
+
base.scope.fail_closed = s.fail_closed;
|
|
347
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
348
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
349
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
350
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
351
|
+
}
|
|
352
|
+
if (Array.isArray(parsed.rules)) {
|
|
353
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
354
|
+
}
|
|
355
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
356
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
357
|
+
}
|
|
358
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
359
|
+
const sb = parsed.sandbox;
|
|
360
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
361
|
+
base.sandbox.mode = sb.mode;
|
|
362
|
+
if (typeof sb.network === "boolean")
|
|
363
|
+
base.sandbox.network = sb.network;
|
|
364
|
+
if (Array.isArray(sb.read_deny))
|
|
365
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
366
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
367
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
368
|
+
}
|
|
369
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
370
|
+
const iso = parsed.isolation;
|
|
371
|
+
if (iso.default_mode === "worktree")
|
|
372
|
+
base.isolation.default_mode = iso.default_mode;
|
|
373
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
374
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
375
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
376
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
377
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
378
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
379
|
+
}
|
|
380
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
381
|
+
const comp = parsed.completion;
|
|
382
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
383
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
384
|
+
if (Array.isArray(comp.checks))
|
|
385
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
386
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
387
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
388
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
389
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
390
|
+
}
|
|
391
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
392
|
+
const runtimeImage = parsed.runtime_image;
|
|
393
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
394
|
+
const deps = runtimeImage.deps;
|
|
395
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
396
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
397
|
+
}
|
|
398
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
399
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
403
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
407
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
408
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
409
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return base;
|
|
413
|
+
}
|
|
414
|
+
function isValidMode(value) {
|
|
415
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
416
|
+
}
|
|
417
|
+
function isValidRule(value) {
|
|
418
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
419
|
+
return false;
|
|
420
|
+
const r = value;
|
|
421
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
422
|
+
}
|
|
423
|
+
function migrateLegacyDeny(deny) {
|
|
424
|
+
const rules = [];
|
|
425
|
+
for (const entry of deny) {
|
|
426
|
+
if (typeof entry.id !== "string")
|
|
427
|
+
continue;
|
|
428
|
+
const match = {};
|
|
429
|
+
if (typeof entry.pattern === "string")
|
|
430
|
+
match.pattern = entry.pattern;
|
|
431
|
+
if (typeof entry.regex === "string")
|
|
432
|
+
match.regex = entry.regex;
|
|
433
|
+
if (!match.pattern && !match.regex)
|
|
434
|
+
continue;
|
|
435
|
+
rules.push({
|
|
436
|
+
id: entry.id,
|
|
437
|
+
category: "command",
|
|
438
|
+
match,
|
|
439
|
+
action: "block",
|
|
440
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
return rules;
|
|
444
|
+
}
|
|
445
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
446
|
+
return rules.map((rule) => {
|
|
447
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
448
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
449
|
+
return {
|
|
450
|
+
...rule,
|
|
451
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
452
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function getRegexUnsafeReason(pattern) {
|
|
457
|
+
if (pattern.length > 512) {
|
|
458
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
459
|
+
}
|
|
460
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
461
|
+
return "pattern uses backreferences";
|
|
462
|
+
}
|
|
463
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
464
|
+
return "pattern contains nested quantifiers";
|
|
465
|
+
}
|
|
466
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
467
|
+
return "pattern contains nested broad quantifiers";
|
|
468
|
+
}
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
472
|
+
const cached = compiledRegexCache.get(pattern);
|
|
473
|
+
if (cached !== undefined) {
|
|
474
|
+
return cached ?? undefined;
|
|
475
|
+
}
|
|
476
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
477
|
+
if (unsafeReason) {
|
|
478
|
+
if (logOnFailure) {
|
|
479
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
480
|
+
}
|
|
481
|
+
compiledRegexCache.set(pattern, null);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const compiled = new RegExp(pattern);
|
|
486
|
+
compiledRegexCache.set(pattern, compiled);
|
|
487
|
+
return compiled;
|
|
488
|
+
} catch (error) {
|
|
489
|
+
if (logOnFailure) {
|
|
490
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
491
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
492
|
+
}
|
|
493
|
+
compiledRegexCache.set(pattern, null);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function matchRule(rule, input) {
|
|
498
|
+
const { match } = rule;
|
|
499
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
if (match.regex) {
|
|
503
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
504
|
+
if (!compiled) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
return compiled.test(input);
|
|
509
|
+
} catch {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
516
|
+
if (!rule.unless)
|
|
517
|
+
return false;
|
|
518
|
+
if (rule.unless.regex) {
|
|
519
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
520
|
+
if (!compiled) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
if (compiled.test(command))
|
|
525
|
+
return true;
|
|
526
|
+
} catch {}
|
|
527
|
+
}
|
|
528
|
+
if (rule.unless.task_in && taskId) {
|
|
529
|
+
if (rule.unless.task_in.includes(taskId))
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
function resolveAction(mode, matched) {
|
|
535
|
+
if (matched.length === 0)
|
|
536
|
+
return "allow";
|
|
537
|
+
if (mode === "off")
|
|
538
|
+
return "allow";
|
|
539
|
+
if (mode === "observe")
|
|
540
|
+
return "warn";
|
|
541
|
+
return "block";
|
|
542
|
+
}
|
|
543
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
544
|
+
if (rawPath.startsWith("/"))
|
|
545
|
+
return resolve(rawPath);
|
|
546
|
+
return resolve(projectRoot, rawPath);
|
|
547
|
+
}
|
|
548
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
549
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
550
|
+
const managedRoots = [
|
|
551
|
+
resolve(projectRoot, "rig"),
|
|
552
|
+
resolve(projectRoot, ".rig"),
|
|
553
|
+
resolve(projectRoot, "artifacts")
|
|
554
|
+
];
|
|
555
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
556
|
+
}
|
|
557
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
558
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
559
|
+
if (taskWorkspace) {
|
|
560
|
+
const workspaceRigRoot = resolve(taskWorkspace, ".rig");
|
|
561
|
+
const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
|
|
562
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
|
|
567
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
568
|
+
}
|
|
569
|
+
function isTestFile(path) {
|
|
570
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
571
|
+
}
|
|
572
|
+
function evaluate(context) {
|
|
573
|
+
const policy = loadPolicy(context.projectRoot);
|
|
574
|
+
switch (context.evaluation.type) {
|
|
575
|
+
case "tool-call":
|
|
576
|
+
return evaluateToolCall(policy, context);
|
|
577
|
+
case "command":
|
|
578
|
+
return evaluateCommand(policy, context);
|
|
579
|
+
case "content-write":
|
|
580
|
+
return evaluateContent(policy, context);
|
|
581
|
+
case "file-access":
|
|
582
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
586
|
+
const allowed = () => ({
|
|
587
|
+
allowed: true,
|
|
588
|
+
matchedRules: [],
|
|
589
|
+
action: "allow",
|
|
590
|
+
failClosed: false
|
|
591
|
+
});
|
|
592
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
593
|
+
return allowed();
|
|
594
|
+
}
|
|
595
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
596
|
+
return allowed();
|
|
597
|
+
}
|
|
598
|
+
if (!context.taskId) {
|
|
599
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
600
|
+
return {
|
|
601
|
+
allowed: false,
|
|
602
|
+
matchedRules: [],
|
|
603
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
604
|
+
failClosed: true
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
return allowed();
|
|
608
|
+
}
|
|
609
|
+
const scopes = context.taskScopes || [];
|
|
610
|
+
if (scopes.length === 0) {
|
|
611
|
+
return allowed();
|
|
612
|
+
}
|
|
613
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
614
|
+
const absPath = resolve(filePath);
|
|
615
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
616
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
617
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
618
|
+
return {
|
|
619
|
+
allowed: policy.mode !== "enforce",
|
|
620
|
+
matchedRules: matched2,
|
|
621
|
+
action: resolveAction(policy.mode, matched2),
|
|
622
|
+
failClosed: false
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
627
|
+
let normalizedPath = filePath;
|
|
628
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
629
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
630
|
+
}
|
|
631
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
632
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
633
|
+
return allowed();
|
|
634
|
+
}
|
|
635
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
636
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
637
|
+
return {
|
|
638
|
+
allowed: policy.mode !== "enforce",
|
|
639
|
+
matchedRules: matched,
|
|
640
|
+
action: resolveAction(policy.mode, matched),
|
|
641
|
+
failClosed: false
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function evaluateCommand(policy, context) {
|
|
645
|
+
const evaluation = context.evaluation;
|
|
646
|
+
if (evaluation.type !== "command") {
|
|
647
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
648
|
+
}
|
|
649
|
+
const command = evaluation.command;
|
|
650
|
+
const matchedRules = [];
|
|
651
|
+
for (const rule of policy.rules) {
|
|
652
|
+
if (rule.category !== "command")
|
|
653
|
+
continue;
|
|
654
|
+
if (!matchRule(rule, command))
|
|
655
|
+
continue;
|
|
656
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
657
|
+
continue;
|
|
658
|
+
matchedRules.push({
|
|
659
|
+
id: rule.id,
|
|
660
|
+
category: rule.category,
|
|
661
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
662
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
const writeTarget = extractWriteTarget(command);
|
|
666
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
667
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
668
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
669
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
673
|
+
return {
|
|
674
|
+
allowed: action !== "block",
|
|
675
|
+
matchedRules,
|
|
676
|
+
action,
|
|
677
|
+
failClosed: false
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function extractWriteTarget(command) {
|
|
681
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
682
|
+
if (redirect?.[1])
|
|
683
|
+
return redirect[1];
|
|
684
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
685
|
+
if (tee?.[2])
|
|
686
|
+
return tee[2];
|
|
687
|
+
return "";
|
|
688
|
+
}
|
|
689
|
+
function evaluateContent(policy, context) {
|
|
690
|
+
const evaluation = context.evaluation;
|
|
691
|
+
if (evaluation.type !== "content-write") {
|
|
692
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
693
|
+
}
|
|
694
|
+
const { content, file_path } = evaluation;
|
|
695
|
+
const matchedRules = [];
|
|
696
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
697
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
698
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
699
|
+
}
|
|
700
|
+
for (const rule of policy.rules) {
|
|
701
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
702
|
+
continue;
|
|
703
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
704
|
+
continue;
|
|
705
|
+
if (!matchRule(rule, content))
|
|
706
|
+
continue;
|
|
707
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
708
|
+
continue;
|
|
709
|
+
matchedRules.push({
|
|
710
|
+
id: rule.id,
|
|
711
|
+
category: rule.category,
|
|
712
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
713
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
717
|
+
return {
|
|
718
|
+
allowed: action !== "block",
|
|
719
|
+
matchedRules,
|
|
720
|
+
action,
|
|
721
|
+
failClosed: false
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function evaluateToolCall(policy, context) {
|
|
725
|
+
const evaluation = context.evaluation;
|
|
726
|
+
if (evaluation.type !== "tool-call") {
|
|
727
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
728
|
+
}
|
|
729
|
+
const { tool_name, tool_input } = evaluation;
|
|
730
|
+
const allMatched = [];
|
|
731
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
732
|
+
for (const fp of filePaths) {
|
|
733
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
734
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
735
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
736
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const content = extractContentFromToolInput(tool_input);
|
|
740
|
+
if (content) {
|
|
741
|
+
const filePath = filePaths[0] || "";
|
|
742
|
+
const contentContext = {
|
|
743
|
+
...context,
|
|
744
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
745
|
+
};
|
|
746
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
747
|
+
for (const rule of contentPolicy.rules) {
|
|
748
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
749
|
+
continue;
|
|
750
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
751
|
+
continue;
|
|
752
|
+
if (!matchRule(rule, content))
|
|
753
|
+
continue;
|
|
754
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
755
|
+
continue;
|
|
756
|
+
allMatched.push({
|
|
757
|
+
id: rule.id,
|
|
758
|
+
category: rule.category,
|
|
759
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
760
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (tool_name === "Bash") {
|
|
765
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
766
|
+
if (command) {
|
|
767
|
+
const cmdContext = {
|
|
768
|
+
...context,
|
|
769
|
+
evaluation: { type: "command", command }
|
|
770
|
+
};
|
|
771
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
772
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
773
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const seen = new Set;
|
|
778
|
+
const deduplicated = [];
|
|
779
|
+
for (const rule of allMatched) {
|
|
780
|
+
if (!seen.has(rule.id)) {
|
|
781
|
+
seen.add(rule.id);
|
|
782
|
+
deduplicated.push(rule);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
786
|
+
return {
|
|
787
|
+
allowed: action !== "block",
|
|
788
|
+
matchedRules: deduplicated,
|
|
789
|
+
action,
|
|
790
|
+
failClosed: false
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function isWriteTool(toolName) {
|
|
794
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
795
|
+
}
|
|
796
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
797
|
+
const paths = [];
|
|
798
|
+
const add = (value) => {
|
|
799
|
+
if (typeof value === "string" && value.trim()) {
|
|
800
|
+
paths.push(value.trim());
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
804
|
+
add(input.file_path);
|
|
805
|
+
add(input.path);
|
|
806
|
+
} else if (toolName === "Glob") {
|
|
807
|
+
add(input.path);
|
|
808
|
+
} else if (toolName === "Grep") {
|
|
809
|
+
add(input.path);
|
|
810
|
+
} else {
|
|
811
|
+
add(input.file_path);
|
|
812
|
+
add(input.path);
|
|
813
|
+
}
|
|
814
|
+
return paths;
|
|
815
|
+
}
|
|
816
|
+
function extractContentFromToolInput(input) {
|
|
817
|
+
if (typeof input.content === "string")
|
|
818
|
+
return input.content;
|
|
819
|
+
if (typeof input.new_string === "string")
|
|
820
|
+
return input.new_string;
|
|
821
|
+
return "";
|
|
822
|
+
}
|
|
823
|
+
var guardHotPathPrimed = false;
|
|
824
|
+
function primeGuardHotPaths() {
|
|
825
|
+
if (guardHotPathPrimed) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
guardHotPathPrimed = true;
|
|
829
|
+
try {
|
|
830
|
+
optimizeNextInvocation(matchRule);
|
|
831
|
+
optimizeNextInvocation(evaluate);
|
|
832
|
+
} catch {}
|
|
833
|
+
}
|
|
834
|
+
primeGuardHotPaths();
|
|
312
835
|
|
|
836
|
+
// packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
|
|
837
|
+
import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
|
|
838
|
+
|
|
839
|
+
// packages/cli-surface-plugin/src/runner.ts
|
|
313
840
|
class CliError extends RuntimeCliError {
|
|
314
|
-
hint;
|
|
315
841
|
constructor(message, exitCode = 1, options = {}) {
|
|
316
|
-
super(message, exitCode);
|
|
317
|
-
if (options.hint?.trim()) {
|
|
318
|
-
this.hint = options.hint.trim();
|
|
319
|
-
}
|
|
842
|
+
super(message, exitCode, options);
|
|
320
843
|
}
|
|
321
844
|
}
|
|
322
845
|
function takeFlag(args, flag) {
|
|
@@ -352,27 +875,84 @@ function takeOption(args, option) {
|
|
|
352
875
|
return { value, rest };
|
|
353
876
|
}
|
|
354
877
|
|
|
355
|
-
// packages/cli-surface-plugin/src/
|
|
356
|
-
import {
|
|
878
|
+
// packages/cli-surface-plugin/src/control-plane/rigfig.ts
|
|
879
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
|
|
880
|
+
import { resolve as resolve2 } from "path";
|
|
881
|
+
import { stringify as stringifyToml } from "smol-toml";
|
|
882
|
+
import { getStandardPluginsResolver } from "@rig/core/embedded-plugins";
|
|
883
|
+
function rigfigConfigPath(projectRoot) {
|
|
884
|
+
return resolve2(projectRoot, ".rig", "rigfig.toml");
|
|
885
|
+
}
|
|
886
|
+
function isPlainObject(value) {
|
|
887
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
888
|
+
}
|
|
889
|
+
function deepMerge(base, overlay) {
|
|
890
|
+
const out = { ...base };
|
|
891
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
892
|
+
const previous = out[key];
|
|
893
|
+
out[key] = isPlainObject(previous) && isPlainObject(value) ? deepMerge(previous, value) : value;
|
|
894
|
+
}
|
|
895
|
+
return out;
|
|
896
|
+
}
|
|
897
|
+
function composeDeclarativeConfig(plugins, context) {
|
|
898
|
+
let merged = {};
|
|
899
|
+
for (const plugin of plugins) {
|
|
900
|
+
const fragment = plugin.contributes?.config?.defaults?.(context);
|
|
901
|
+
if (isPlainObject(fragment))
|
|
902
|
+
merged = deepMerge(merged, fragment);
|
|
903
|
+
}
|
|
904
|
+
return merged;
|
|
905
|
+
}
|
|
906
|
+
var HEADER = [
|
|
907
|
+
"# Declarative rig configuration \u2014 the happy path.",
|
|
908
|
+
"# Plain DATA, composed from the standard plugins' defaults. No top-level",
|
|
909
|
+
"# rig.config.ts and no @h-rig/* install: the global rig binary resolves",
|
|
910
|
+
"# plugins from its embedded standard collection. rig created this for you;",
|
|
911
|
+
"# edit it freely (each plugin owns the section it needs).",
|
|
912
|
+
"",
|
|
913
|
+
""
|
|
914
|
+
].join(`
|
|
915
|
+
`);
|
|
916
|
+
function writeRigfigConfig(projectRoot, config) {
|
|
917
|
+
mkdirSync(resolve2(projectRoot, ".rig"), { recursive: true });
|
|
918
|
+
const path = rigfigConfigPath(projectRoot);
|
|
919
|
+
writeFileSync(path, `${HEADER}${stringifyToml(config)}
|
|
920
|
+
`, "utf-8");
|
|
921
|
+
return path;
|
|
922
|
+
}
|
|
923
|
+
function composeAndWriteRigfig(projectRoot, options = {}) {
|
|
924
|
+
const resolver = getStandardPluginsResolver();
|
|
925
|
+
if (!resolver) {
|
|
926
|
+
throw new Error("Cannot write rig config: embedded standard plugins are not registered (seed wiring error).");
|
|
927
|
+
}
|
|
928
|
+
const context = options.repoSlug ? { projectRoot, repoSlug: options.repoSlug } : { projectRoot };
|
|
929
|
+
let composed = composeDeclarativeConfig(resolver(), context);
|
|
930
|
+
if (options.overlay) {
|
|
931
|
+
if (isPlainObject(options.overlay.taskSource) && "taskSource" in composed)
|
|
932
|
+
delete composed.taskSource;
|
|
933
|
+
composed = deepMerge(composed, options.overlay);
|
|
934
|
+
}
|
|
935
|
+
return writeRigfigConfig(projectRoot, composed);
|
|
936
|
+
}
|
|
357
937
|
|
|
358
938
|
// packages/cli-surface-plugin/src/commands/_connection-state.ts
|
|
359
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
360
|
-
import { dirname, resolve } from "path";
|
|
939
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
940
|
+
import { dirname, resolve as resolve3 } from "path";
|
|
361
941
|
function resolveRepoConnectionPath(projectRoot) {
|
|
362
|
-
return
|
|
942
|
+
return resolve3(projectRoot, ".rig", "state", "connection.json");
|
|
363
943
|
}
|
|
364
944
|
function readJsonFile(path) {
|
|
365
|
-
if (!
|
|
945
|
+
if (!existsSync3(path))
|
|
366
946
|
return null;
|
|
367
947
|
try {
|
|
368
|
-
return JSON.parse(
|
|
948
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
369
949
|
} catch (error) {
|
|
370
950
|
throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
|
|
371
951
|
}
|
|
372
952
|
}
|
|
373
953
|
function writeJsonFile(path, value) {
|
|
374
|
-
|
|
375
|
-
|
|
954
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
955
|
+
writeFileSync2(path, `${JSON.stringify(value, null, 2)}
|
|
376
956
|
`, "utf8");
|
|
377
957
|
}
|
|
378
958
|
function readRepoConnection(projectRoot) {
|
|
@@ -397,11 +977,11 @@ function writeRepoConnection(projectRoot, state) {
|
|
|
397
977
|
}
|
|
398
978
|
|
|
399
979
|
// packages/cli-surface-plugin/src/commands/init.ts
|
|
400
|
-
import { upsertManagedRemoteEndpoint } from "@rig/
|
|
980
|
+
import { upsertManagedRemoteEndpoint } from "@rig/core/remote-config";
|
|
401
981
|
import { loadConfig } from "@rig/core/load-config";
|
|
402
982
|
|
|
403
983
|
// packages/cli-surface-plugin/src/commands/_inprocess-services.ts
|
|
404
|
-
import { resolve as
|
|
984
|
+
import { resolve as resolve4 } from "path";
|
|
405
985
|
import {
|
|
406
986
|
beginGitHubDeviceFlow,
|
|
407
987
|
checkGitHubRepoPermissions,
|
|
@@ -411,7 +991,7 @@ import {
|
|
|
411
991
|
resolveGitHubAuthStatus,
|
|
412
992
|
resolveProjectStatusField,
|
|
413
993
|
saveGitHubTokenForProject
|
|
414
|
-
} from "@rig/github-
|
|
994
|
+
} from "@rig/github-lib";
|
|
415
995
|
var scopedGitHubBearerTokens = new Map;
|
|
416
996
|
function cleanToken(value) {
|
|
417
997
|
const trimmed = value?.trim();
|
|
@@ -424,11 +1004,11 @@ function oauthClientId() {
|
|
|
424
1004
|
return cleanToken(process.env.RIG_GITHUB_OAUTH_CLIENT_ID);
|
|
425
1005
|
}
|
|
426
1006
|
function tokenForProject(projectRoot, override) {
|
|
427
|
-
const scoped = scopedGitHubBearerTokens.get(
|
|
1007
|
+
const scoped = scopedGitHubBearerTokens.get(resolve4(projectRoot));
|
|
428
1008
|
return cleanToken(override) ?? scoped ?? createGitHubAuthStore(projectRoot).readToken() ?? cleanToken(process.env.RIG_GITHUB_TOKEN);
|
|
429
1009
|
}
|
|
430
1010
|
function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
431
|
-
scopedGitHubBearerTokens.set(
|
|
1011
|
+
scopedGitHubBearerTokens.set(resolve4(projectRoot ?? process.cwd()), cleanToken(token));
|
|
432
1012
|
}
|
|
433
1013
|
async function getGitHubAuthStatusInProcess(context) {
|
|
434
1014
|
return { ok: true, ...resolveGitHubAuthStatus({ projectRoot: context.projectRoot, oauthConfigured: Boolean(oauthClientId()) }) };
|
|
@@ -495,12 +1075,12 @@ async function requestGitHubAuthJsonInProcess(context, pathname, init = {}) {
|
|
|
495
1075
|
}
|
|
496
1076
|
|
|
497
1077
|
// packages/cli-surface-plugin/src/commands/_pi-install.ts
|
|
498
|
-
import { existsSync as
|
|
1078
|
+
import { existsSync as existsSync4 } from "fs";
|
|
499
1079
|
import { homedir } from "os";
|
|
500
|
-
import { resolve as
|
|
1080
|
+
import { resolve as resolve5 } from "path";
|
|
501
1081
|
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
502
1082
|
async function defaultCommandRunner(command, options = {}) {
|
|
503
|
-
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
1083
|
+
const proc = Bun.spawn(command, { ...options.cwd !== undefined ? { cwd: options.cwd } : {}, stdout: "pipe", stderr: "pipe" });
|
|
504
1084
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
505
1085
|
new Response(proc.stdout).text(),
|
|
506
1086
|
new Response(proc.stderr).text(),
|
|
@@ -509,11 +1089,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
509
1089
|
return { exitCode, stdout, stderr };
|
|
510
1090
|
}
|
|
511
1091
|
function resolvePiRigExtensionPath(homeDir) {
|
|
512
|
-
return
|
|
1092
|
+
return resolve5(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
513
1093
|
}
|
|
514
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
515
|
-
const localPackage =
|
|
516
|
-
if (exists(
|
|
1094
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync4) {
|
|
1095
|
+
const localPackage = resolve5(projectRoot, "packages", "pi-rig");
|
|
1096
|
+
if (exists(resolve5(localPackage, "package.json")))
|
|
517
1097
|
return localPackage;
|
|
518
1098
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
519
1099
|
}
|
|
@@ -585,7 +1165,7 @@ async function checkPiRigInstall(input = {}) {
|
|
|
585
1165
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
586
1166
|
const hasPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
587
1167
|
${piListResult.stderr}`);
|
|
588
|
-
const hasLegacyBridgeScaffold = !hasPiRig && (input.exists ??
|
|
1168
|
+
const hasLegacyBridgeScaffold = !hasPiRig && (input.exists ?? existsSync4)(extensionPath);
|
|
589
1169
|
return {
|
|
590
1170
|
extensionPath,
|
|
591
1171
|
pi: {
|
|
@@ -605,7 +1185,7 @@ ${piListResult.stderr}`);
|
|
|
605
1185
|
async function ensurePiRigInstalled(input) {
|
|
606
1186
|
const home = resolvePiHomeDir(input.homeDir);
|
|
607
1187
|
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
608
|
-
const status = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
1188
|
+
const status = await checkPiRigInstall({ homeDir: home, ...input.commandRunner ? { commandRunner: input.commandRunner } : {} });
|
|
609
1189
|
return { ...status, installedPath: status.extensionPath };
|
|
610
1190
|
}
|
|
611
1191
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
@@ -623,10 +1203,17 @@ async function ensurePiRigInstalled(input) {
|
|
|
623
1203
|
}
|
|
624
1204
|
|
|
625
1205
|
// packages/cli-surface-plugin/src/commands/_doctor-checks.ts
|
|
626
|
-
import {
|
|
627
|
-
import {
|
|
1206
|
+
import { DOCTOR } from "@rig/contracts";
|
|
1207
|
+
import { defineCapability } from "@rig/core/capability";
|
|
1208
|
+
import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
1209
|
+
function doctorLevelToStatus(level) {
|
|
1210
|
+
return level === "ok" ? "pass" : level;
|
|
1211
|
+
}
|
|
1212
|
+
function countDoctorFailures(checks) {
|
|
1213
|
+
return checks.filter((entry) => (entry.status ?? doctorLevelToStatus(entry.level ?? "warn")) === "fail").length;
|
|
1214
|
+
}
|
|
628
1215
|
async function runRigDoctorChecks(options) {
|
|
629
|
-
const service = await
|
|
1216
|
+
const service = await loadCapabilityForRoot(options.projectRoot, defineCapability(DOCTOR));
|
|
630
1217
|
if (!service) {
|
|
631
1218
|
return [
|
|
632
1219
|
{
|
|
@@ -642,64 +1229,6 @@ async function runRigDoctorChecks(options) {
|
|
|
642
1229
|
return service.runDoctorChecks(options);
|
|
643
1230
|
}
|
|
644
1231
|
|
|
645
|
-
// packages/cli-surface-plugin/src/version.ts
|
|
646
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
647
|
-
import { dirname as dirname2, resolve as resolve4 } from "path";
|
|
648
|
-
import { fileURLToPath } from "url";
|
|
649
|
-
import { readBuildConfig } from "@rig/runtime/build-time-config";
|
|
650
|
-
var SOURCE_CLI_VERSION = "0.0.0-alpha.1";
|
|
651
|
-
var DEV_CLI_VERSION = "0.0.0-dev";
|
|
652
|
-
function resolveInstalledCliVersion() {
|
|
653
|
-
const buildConfig = readBuildConfig();
|
|
654
|
-
const envVersion = process.env.RIG_CLI_VERSION?.trim();
|
|
655
|
-
const buildVersion = buildConfig.RIG_CLI_VERSION?.trim();
|
|
656
|
-
let version = envVersion || buildVersion || "";
|
|
657
|
-
if (!version) {
|
|
658
|
-
try {
|
|
659
|
-
const execPath = process.execPath || "";
|
|
660
|
-
const moduleDir = dirname2(fileURLToPath(import.meta.url));
|
|
661
|
-
const candidates = [
|
|
662
|
-
execPath ? resolve4(dirname2(execPath), "..", "manifest.json") : "",
|
|
663
|
-
resolve4(moduleDir, "..", "package.json"),
|
|
664
|
-
resolve4(moduleDir, "..", "..", "package.json"),
|
|
665
|
-
resolve4(process.cwd(), "packages/cli/package.json")
|
|
666
|
-
].filter(Boolean);
|
|
667
|
-
for (const candidate of candidates) {
|
|
668
|
-
if (!existsSync3(candidate))
|
|
669
|
-
continue;
|
|
670
|
-
const parsed = JSON.parse(readFileSync2(candidate, "utf-8"));
|
|
671
|
-
const candidateVersion = parsed.version?.trim();
|
|
672
|
-
if (candidateVersion) {
|
|
673
|
-
version = candidateVersion;
|
|
674
|
-
break;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
} catch {}
|
|
678
|
-
}
|
|
679
|
-
return version || DEV_CLI_VERSION;
|
|
680
|
-
}
|
|
681
|
-
function isPublishedCliVersion(version) {
|
|
682
|
-
const normalized = version.trim();
|
|
683
|
-
return normalized.length > 0 && normalized !== SOURCE_CLI_VERSION && normalized !== DEV_CLI_VERSION;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// packages/cli-surface-plugin/src/rig-config-package-deps.ts
|
|
687
|
-
var REQUIRED_RIG_CONFIG_PACKAGE_NAMES = ["core", "standard-plugin"];
|
|
688
|
-
function resolveRigConfigPackageVersion() {
|
|
689
|
-
const explicit = process.env.RIG_CONFIG_PACKAGE_VERSION?.trim();
|
|
690
|
-
if (explicit)
|
|
691
|
-
return explicit;
|
|
692
|
-
const installed = resolveInstalledCliVersion();
|
|
693
|
-
return isPublishedCliVersion(installed) ? installed : "latest";
|
|
694
|
-
}
|
|
695
|
-
function rigConfigDevDependencies() {
|
|
696
|
-
const version = resolveRigConfigPackageVersion();
|
|
697
|
-
return Object.fromEntries(REQUIRED_RIG_CONFIG_PACKAGE_NAMES.map((name) => [`@rig/${name}`, rigPackageSpec(name, version)]));
|
|
698
|
-
}
|
|
699
|
-
function rigPackageSpec(packageName, version = resolveRigConfigPackageVersion()) {
|
|
700
|
-
return `npm:@h-rig/${packageName}@${version}`;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
1232
|
// packages/cli-surface-plugin/src/commands/init.ts
|
|
704
1233
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
705
1234
|
const trimmed = remoteUrl.trim();
|
|
@@ -719,20 +1248,20 @@ function parseRepoSlug(value) {
|
|
|
719
1248
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
720
1249
|
}
|
|
721
1250
|
function ensureRigPrivateDirs(projectRoot) {
|
|
722
|
-
const rigDir =
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const taskConfigPath =
|
|
729
|
-
if (!
|
|
730
|
-
|
|
1251
|
+
const rigDir = resolve6(projectRoot, ".rig");
|
|
1252
|
+
mkdirSync3(resolve6(rigDir, "state"), { recursive: true });
|
|
1253
|
+
mkdirSync3(resolve6(rigDir, "logs"), { recursive: true });
|
|
1254
|
+
mkdirSync3(resolve6(rigDir, "runs"), { recursive: true });
|
|
1255
|
+
mkdirSync3(resolve6(rigDir, "tmp"), { recursive: true });
|
|
1256
|
+
mkdirSync3(resolve6(projectRoot, "artifacts"), { recursive: true });
|
|
1257
|
+
const taskConfigPath = resolve6(rigDir, "task-config.json");
|
|
1258
|
+
if (!existsSync5(taskConfigPath))
|
|
1259
|
+
writeFileSync3(taskConfigPath, `{}
|
|
731
1260
|
`, "utf-8");
|
|
732
1261
|
}
|
|
733
1262
|
function ensureGitignoreEntries(projectRoot) {
|
|
734
|
-
const path =
|
|
735
|
-
const existing =
|
|
1263
|
+
const path = resolve6(projectRoot, ".gitignore");
|
|
1264
|
+
const existing = existsSync5(path) ? readFileSync3(path, "utf8") : "";
|
|
736
1265
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
737
1266
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
738
1267
|
if (missing.length === 0)
|
|
@@ -744,36 +1273,53 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
744
1273
|
`)}
|
|
745
1274
|
`, "utf8");
|
|
746
1275
|
}
|
|
747
|
-
function
|
|
748
|
-
const path = resolve5(projectRoot, "package.json");
|
|
749
|
-
const existing = existsSync4(path) ? JSON.parse(readFileSync3(path, "utf8")) : {};
|
|
750
|
-
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
751
|
-
for (const [name, spec] of Object.entries(rigConfigDevDependencies())) {
|
|
752
|
-
devDependencies[name] = spec;
|
|
753
|
-
}
|
|
754
|
-
const next = {
|
|
755
|
-
...existsSync4(path) ? existing : { name: "rig-project", private: true },
|
|
756
|
-
devDependencies
|
|
757
|
-
};
|
|
758
|
-
writeFileSync2(path, `${JSON.stringify(next, null, 2)}
|
|
759
|
-
`, "utf8");
|
|
760
|
-
}
|
|
761
|
-
function applyGitHubProjectConfig(source, options) {
|
|
1276
|
+
function buildGitHubProjectsOverlay(options) {
|
|
762
1277
|
if (!options.githubProject || options.githubProject === "off")
|
|
763
|
-
return
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1278
|
+
return null;
|
|
1279
|
+
const projects = {
|
|
1280
|
+
enabled: true,
|
|
1281
|
+
projectId: options.githubProject,
|
|
1282
|
+
statusFieldId: options.githubProjectStatusField ?? "Status"
|
|
1283
|
+
};
|
|
1284
|
+
if (options.githubProjectStatuses && Object.keys(options.githubProjectStatuses).length > 0) {
|
|
1285
|
+
projects.statuses = options.githubProjectStatuses;
|
|
1286
|
+
}
|
|
1287
|
+
return { github: { projects } };
|
|
1288
|
+
}
|
|
1289
|
+
function mergeOverlay(base, overlay) {
|
|
1290
|
+
if (!overlay)
|
|
1291
|
+
return base;
|
|
1292
|
+
const out = { ...base };
|
|
1293
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
1294
|
+
const prev = out[key];
|
|
1295
|
+
out[key] = prev && typeof prev === "object" && !Array.isArray(prev) && value && typeof value === "object" && !Array.isArray(value) ? mergeOverlay(prev, value) : value;
|
|
1296
|
+
}
|
|
1297
|
+
return out;
|
|
1298
|
+
}
|
|
1299
|
+
function configAlreadyExists(projectRoot) {
|
|
1300
|
+
return existsSync5(resolve6(projectRoot, "rig.config.ts")) || existsSync5(resolve6(projectRoot, "rig.config.mts")) || existsSync5(resolve6(projectRoot, "rig.config.json")) || existsSync5(rigfigConfigPath(projectRoot));
|
|
1301
|
+
}
|
|
1302
|
+
function buildWizardOverlay(input) {
|
|
1303
|
+
let overlay = {};
|
|
1304
|
+
if (input.taskSource.kind === "github-issues") {
|
|
1305
|
+
const taskSource = {
|
|
1306
|
+
kind: "github-issues",
|
|
1307
|
+
owner: input.taskSource.owner,
|
|
1308
|
+
repo: input.taskSource.repo,
|
|
1309
|
+
state: "open"
|
|
1310
|
+
};
|
|
1311
|
+
if (input.taskSource.assignee?.trim())
|
|
1312
|
+
taskSource.options = { assignee: input.taskSource.assignee.trim() };
|
|
1313
|
+
overlay.taskSource = taskSource;
|
|
1314
|
+
} else {
|
|
1315
|
+
overlay.taskSource = { kind: "files", path: input.taskSource.path };
|
|
1316
|
+
}
|
|
1317
|
+
if (input.sshTarget?.trim()) {
|
|
1318
|
+
overlay = mergeOverlay(overlay, { runtime: { server: { sshTarget: input.sshTarget.trim() } } });
|
|
1319
|
+
}
|
|
1320
|
+
if (input.projects)
|
|
1321
|
+
overlay = mergeOverlay(overlay, input.projects);
|
|
1322
|
+
return overlay;
|
|
777
1323
|
}
|
|
778
1324
|
function checkoutForInit(projectRoot, options) {
|
|
779
1325
|
if (options.server === "remote") {
|
|
@@ -964,7 +1510,7 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
|
|
|
964
1510
|
...projects.map((project) => ({
|
|
965
1511
|
value: String(project.id),
|
|
966
1512
|
label: `${String(project.title ?? "Untitled project")} (#${String(project.number ?? "?")})`,
|
|
967
|
-
|
|
1513
|
+
...typeof project.url === "string" ? { hint: project.url } : {}
|
|
968
1514
|
})),
|
|
969
1515
|
{ value: "manual", label: "Enter ProjectV2 id manually" }
|
|
970
1516
|
]
|
|
@@ -995,12 +1541,12 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
|
|
|
995
1541
|
return {
|
|
996
1542
|
githubProject: projectId,
|
|
997
1543
|
githubProjectStatusField: fieldId,
|
|
998
|
-
|
|
1544
|
+
...Object.keys(statuses).length > 0 ? { githubProjectStatuses: statuses } : {},
|
|
999
1545
|
...activeToken ? { githubToken: activeToken } : {}
|
|
1000
1546
|
};
|
|
1001
1547
|
}
|
|
1002
1548
|
function sleep(ms) {
|
|
1003
|
-
return new Promise((
|
|
1549
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
1004
1550
|
}
|
|
1005
1551
|
function positiveIntFromEnv(name, fallback) {
|
|
1006
1552
|
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
@@ -1042,24 +1588,18 @@ function runLocalFilesInit(context, options) {
|
|
|
1042
1588
|
ensureRigPrivateDirs(projectRoot);
|
|
1043
1589
|
ensureGitignoreEntries(projectRoot);
|
|
1044
1590
|
writeRepoConnection(projectRoot, { selected: "local", linkedAt: new Date().toISOString() });
|
|
1045
|
-
|
|
1046
|
-
const configExists = existsSync4(configTsPath) || existsSync4(resolve5(projectRoot, "rig.config.json"));
|
|
1047
|
-
if (configExists && !options.repair) {
|
|
1591
|
+
if (configAlreadyExists(projectRoot) && !options.repair) {
|
|
1048
1592
|
if (context.outputMode !== "json")
|
|
1049
|
-
console.log("rig
|
|
1593
|
+
console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it.");
|
|
1050
1594
|
} else {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
const tasksDir = resolve5(projectRoot, "tasks");
|
|
1060
|
-
if (!existsSync4(tasksDir)) {
|
|
1061
|
-
mkdirSync2(tasksDir, { recursive: true });
|
|
1062
|
-
writeFileSync2(resolve5(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
|
|
1595
|
+
composeAndWriteRigfig(projectRoot, {
|
|
1596
|
+
overlay: buildWizardOverlay({ taskSource: { kind: "files", path: "tasks" } })
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
const tasksDir = resolve6(projectRoot, "tasks");
|
|
1600
|
+
if (!existsSync5(tasksDir)) {
|
|
1601
|
+
mkdirSync3(tasksDir, { recursive: true });
|
|
1602
|
+
writeFileSync3(resolve6(tasksDir, "T-1.json"), `${JSON.stringify({ id: "T-1", title: "My first Rig task", body: "Describe the change you want an agent to make." }, null, 2)}
|
|
1063
1603
|
`, "utf-8");
|
|
1064
1604
|
}
|
|
1065
1605
|
if (context.outputMode !== "json") {
|
|
@@ -1114,37 +1654,32 @@ function runDemoInit(context, options) {
|
|
|
1114
1654
|
ensureRigPrivateDirs(projectRoot);
|
|
1115
1655
|
ensureGitignoreEntries(projectRoot);
|
|
1116
1656
|
writeRepoConnection(projectRoot, { selected: "local", linkedAt: new Date().toISOString() });
|
|
1117
|
-
const configTsPath = resolve5(projectRoot, "rig.config.ts");
|
|
1118
|
-
const configExists = existsSync4(configTsPath) || existsSync4(resolve5(projectRoot, "rig.config.json"));
|
|
1119
1657
|
let configWritten = false;
|
|
1120
|
-
if (
|
|
1658
|
+
if (configAlreadyExists(projectRoot) && !options.repair) {
|
|
1121
1659
|
if (context.outputMode !== "json") {
|
|
1122
|
-
console.log("rig
|
|
1660
|
+
console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it for the demo.");
|
|
1123
1661
|
}
|
|
1124
1662
|
} else {
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
useStandardPlugin: true
|
|
1129
|
-
}), "utf-8");
|
|
1663
|
+
composeAndWriteRigfig(projectRoot, {
|
|
1664
|
+
overlay: buildWizardOverlay({ taskSource: { kind: "files", path: DEMO_TASKS_RELATIVE_DIR } })
|
|
1665
|
+
});
|
|
1130
1666
|
configWritten = true;
|
|
1131
1667
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
mkdirSync2(demoTasksDir, { recursive: true });
|
|
1668
|
+
const demoTasksDir = resolve6(projectRoot, DEMO_TASKS_RELATIVE_DIR);
|
|
1669
|
+
mkdirSync3(demoTasksDir, { recursive: true });
|
|
1135
1670
|
const taskIds = [];
|
|
1136
1671
|
for (const task of DEMO_TASKS) {
|
|
1137
1672
|
const id = String(task.id);
|
|
1138
1673
|
taskIds.push(id);
|
|
1139
|
-
const taskPath =
|
|
1140
|
-
if (!
|
|
1141
|
-
|
|
1674
|
+
const taskPath = resolve6(demoTasksDir, `${id}.json`);
|
|
1675
|
+
if (!existsSync5(taskPath)) {
|
|
1676
|
+
writeFileSync3(taskPath, `${JSON.stringify(task, null, 2)}
|
|
1142
1677
|
`, "utf-8");
|
|
1143
1678
|
}
|
|
1144
1679
|
}
|
|
1145
1680
|
if (context.outputMode !== "json") {
|
|
1146
1681
|
console.log(`Demo Rig project ready (offline, no GitHub).`);
|
|
1147
|
-
console.log(` config: rig.
|
|
1682
|
+
console.log(` config: .rig/rigfig.toml (files task source -> ${DEMO_TASKS_RELATIVE_DIR}/)`);
|
|
1148
1683
|
console.log(` tasks: ${taskIds.join(", ")}`);
|
|
1149
1684
|
console.log("Next steps:");
|
|
1150
1685
|
console.log(" 1. run bare `rig`");
|
|
@@ -1198,25 +1733,21 @@ async function runControlPlaneInit(context, options) {
|
|
|
1198
1733
|
});
|
|
1199
1734
|
ensureRigPrivateDirs(projectRoot);
|
|
1200
1735
|
ensureGitignoreEntries(projectRoot);
|
|
1201
|
-
const configTsPath = resolve5(projectRoot, "rig.config.ts");
|
|
1202
|
-
const configJsonPath = resolve5(projectRoot, "rig.config.json");
|
|
1203
|
-
const configExists = existsSync4(configTsPath) || existsSync4(configJsonPath);
|
|
1204
1736
|
if (!options.privateStateOnly) {
|
|
1205
|
-
if (
|
|
1737
|
+
if (configAlreadyExists(projectRoot) && !options.repair) {
|
|
1206
1738
|
if (context.outputMode !== "json")
|
|
1207
|
-
console.log("rig
|
|
1739
|
+
console.log("rig config already exists; leaving it unchanged. Pass --repair to rewrite it.");
|
|
1208
1740
|
} else {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1741
|
+
composeAndWriteRigfig(projectRoot, {
|
|
1742
|
+
repoSlug: repo.slug,
|
|
1743
|
+
overlay: buildWizardOverlay({
|
|
1744
|
+
taskSource: { kind: "github-issues", owner: repo.owner, repo: repo.repo },
|
|
1745
|
+
projects: buildGitHubProjectsOverlay(options)
|
|
1746
|
+
})
|
|
1747
|
+
});
|
|
1216
1748
|
}
|
|
1217
|
-
ensureRigConfigPackageDependencies(projectRoot);
|
|
1218
1749
|
}
|
|
1219
|
-
|
|
1750
|
+
writeFileSync3(resolve6(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
1220
1751
|
`, "utf8");
|
|
1221
1752
|
const checkout = checkoutForInit(projectRoot, options);
|
|
1222
1753
|
let githubAuth = null;
|
|
@@ -1255,7 +1786,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
1255
1786
|
skipped: true,
|
|
1256
1787
|
reason: "retired-server-label-bootstrap"
|
|
1257
1788
|
};
|
|
1258
|
-
const pi = await ensurePiRigInstalled({ projectRoot, homeDir: process.env.RIG_PI_HOME_DIR }).catch((error) => ({
|
|
1789
|
+
const pi = await ensurePiRigInstalled({ projectRoot, ...process.env.RIG_PI_HOME_DIR !== undefined ? { homeDir: process.env.RIG_PI_HOME_DIR } : {} }).catch((error) => ({
|
|
1259
1790
|
pi: { ok: false, label: "pi", hint: error instanceof Error ? error.message : String(error) },
|
|
1260
1791
|
piRig: { ok: false, label: "pi-rig global extension", hint: "Local pi-rig installation failed." },
|
|
1261
1792
|
extensionPath: null,
|
|
@@ -1346,7 +1877,7 @@ function parseInitOptions(args) {
|
|
|
1346
1877
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
1347
1878
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
1348
1879
|
const projectRoot = context.projectRoot;
|
|
1349
|
-
const existingConfig =
|
|
1880
|
+
const existingConfig = configAlreadyExists(projectRoot);
|
|
1350
1881
|
let repair = false;
|
|
1351
1882
|
let privateStateOnly = false;
|
|
1352
1883
|
if (existingConfig) {
|
|
@@ -1370,7 +1901,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
1370
1901
|
const repoSlug = await promptRequiredText(prompts, {
|
|
1371
1902
|
message: "GitHub repo slug",
|
|
1372
1903
|
placeholder: "owner/repo",
|
|
1373
|
-
defaultValue: detectedRepo
|
|
1904
|
+
...detectedRepo !== undefined ? { defaultValue: detectedRepo } : {}
|
|
1374
1905
|
});
|
|
1375
1906
|
const placement = await promptSelect(prompts, {
|
|
1376
1907
|
message: "Execution placement",
|
|
@@ -1459,7 +1990,6 @@ export {
|
|
|
1459
1990
|
runInteractiveControlPlaneInit,
|
|
1460
1991
|
runDemoInit,
|
|
1461
1992
|
executeInit,
|
|
1462
|
-
buildRigInitConfigSource,
|
|
1463
1993
|
DEMO_TASKS_RELATIVE_DIR,
|
|
1464
1994
|
DEMO_TASKS
|
|
1465
1995
|
};
|