@h-rig/cli-surface-plugin 0.0.6-alpha.156 → 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
|
@@ -1,32 +1,661 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli-surface-plugin/src/commands/inspect.ts
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
taskArtifacts,
|
|
7
|
-
taskDeps
|
|
8
|
-
} from "@rig/runtime/control-plane/native/task-ops";
|
|
9
|
-
import {
|
|
10
|
-
extractRunLogs,
|
|
11
|
-
getRun,
|
|
12
|
-
listRuns,
|
|
13
|
-
runsForTask,
|
|
14
|
-
summarizeRunFailures
|
|
15
|
-
} from "@rig/run-worker/runs";
|
|
3
|
+
import { RUN_READ_MODEL, TASK_DATA_SERVICE_CAPABILITY } from "@rig/contracts";
|
|
4
|
+
import { defineCapability } from "@rig/core/capability";
|
|
5
|
+
import { requireCapabilityForRoot, requireInstalledCapability } from "@rig/core/capability-loaders";
|
|
16
6
|
|
|
17
7
|
// packages/cli-surface-plugin/src/runner.ts
|
|
18
|
-
import { EventBus } from "@rig/runtime
|
|
19
|
-
import { CliError as RuntimeCliError } from "@rig/
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
import { EventBus } from "@rig/core/runtime-events";
|
|
9
|
+
import { CliError as RuntimeCliError } from "@rig/contracts";
|
|
10
|
+
|
|
11
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
12
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
13
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
14
|
+
import { resolve } from "path";
|
|
15
|
+
|
|
16
|
+
// packages/cli-surface-plugin/src/control-plane/scope.ts
|
|
17
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
18
|
+
var scopeRegexCache = new Map;
|
|
19
|
+
function unique(values) {
|
|
20
|
+
return [...new Set(values)];
|
|
21
|
+
}
|
|
22
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
23
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
24
|
+
const rules = getScopeRules();
|
|
25
|
+
if (rules?.stripPrefixes) {
|
|
26
|
+
for (const prefix of rules.stripPrefixes) {
|
|
27
|
+
if (normalized.startsWith(prefix)) {
|
|
28
|
+
normalized = normalized.slice(prefix.length);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
35
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
36
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
37
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
38
|
+
}
|
|
39
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
40
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
41
|
+
}
|
|
42
|
+
return normalizeRelativeScopePath(normalized);
|
|
43
|
+
}
|
|
44
|
+
function scopeGlobToRegex(glob) {
|
|
45
|
+
const cached = scopeRegexCache.get(glob);
|
|
46
|
+
if (cached) {
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
|
|
50
|
+
const compiled = new RegExp(`^${escaped}$`);
|
|
51
|
+
scopeRegexCache.set(glob, compiled);
|
|
52
|
+
return compiled;
|
|
53
|
+
}
|
|
54
|
+
function scopeMatches(path, scopes) {
|
|
55
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
56
|
+
for (const scope of scopes) {
|
|
57
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
58
|
+
for (const candidatePath of pathVariants) {
|
|
59
|
+
for (const candidateScope of scopeVariants) {
|
|
60
|
+
if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
22
68
|
|
|
69
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
70
|
+
import {
|
|
71
|
+
POLICY_VERSION
|
|
72
|
+
} from "@rig/contracts";
|
|
73
|
+
var DEFAULT_SCOPE = {
|
|
74
|
+
fail_closed: true,
|
|
75
|
+
harness_paths_exempt: true,
|
|
76
|
+
runtime_paths_exempt: true
|
|
77
|
+
};
|
|
78
|
+
var DEFAULT_SANDBOX = {
|
|
79
|
+
mode: "enforce",
|
|
80
|
+
network: true,
|
|
81
|
+
read_deny: [],
|
|
82
|
+
write_allow_from_runtime: true
|
|
83
|
+
};
|
|
84
|
+
var DEFAULT_ISOLATION = {
|
|
85
|
+
default_mode: "worktree",
|
|
86
|
+
repo_symlink_fallback: false,
|
|
87
|
+
strict_provisioning: true,
|
|
88
|
+
fail_closed_on_provision_error: true
|
|
89
|
+
};
|
|
90
|
+
var DEFAULT_COMPLETION = {
|
|
91
|
+
derive_checks_from_scope: true,
|
|
92
|
+
checks: [],
|
|
93
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
94
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
95
|
+
};
|
|
96
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
97
|
+
deps: {
|
|
98
|
+
monorepo_install: false,
|
|
99
|
+
hp_next_install: false
|
|
100
|
+
},
|
|
101
|
+
plugins_require_binaries: true
|
|
102
|
+
};
|
|
103
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
104
|
+
enabled: true
|
|
105
|
+
};
|
|
106
|
+
function defaultPolicy() {
|
|
107
|
+
return {
|
|
108
|
+
version: POLICY_VERSION,
|
|
109
|
+
mode: "enforce",
|
|
110
|
+
scope: { ...DEFAULT_SCOPE },
|
|
111
|
+
rules: [],
|
|
112
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
113
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
114
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
115
|
+
runtime_image: {
|
|
116
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
117
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
118
|
+
},
|
|
119
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
var policyCache = null;
|
|
123
|
+
var policyCachePath = null;
|
|
124
|
+
var seededPolicyConfig = null;
|
|
125
|
+
var compiledRegexCache = new Map;
|
|
126
|
+
function loadPolicy(projectRoot) {
|
|
127
|
+
if (seededPolicyConfig) {
|
|
128
|
+
return seededPolicyConfig;
|
|
129
|
+
}
|
|
130
|
+
const configPath = resolve(projectRoot, "rig/policy/policy.json");
|
|
131
|
+
if (!existsSync(configPath)) {
|
|
132
|
+
return defaultPolicy();
|
|
133
|
+
}
|
|
134
|
+
let mtimeMs;
|
|
135
|
+
try {
|
|
136
|
+
mtimeMs = statSync(configPath).mtimeMs;
|
|
137
|
+
} catch {
|
|
138
|
+
return defaultPolicy();
|
|
139
|
+
}
|
|
140
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
141
|
+
return policyCache.config;
|
|
142
|
+
}
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
146
|
+
} catch {
|
|
147
|
+
return defaultPolicy();
|
|
148
|
+
}
|
|
149
|
+
const config = mergeWithDefaults(parsed);
|
|
150
|
+
policyCache = { mtimeMs, config };
|
|
151
|
+
policyCachePath = configPath;
|
|
152
|
+
return config;
|
|
153
|
+
}
|
|
154
|
+
function mergeWithDefaults(parsed) {
|
|
155
|
+
const base = defaultPolicy();
|
|
156
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
157
|
+
base.mode = parsed.mode;
|
|
158
|
+
}
|
|
159
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
160
|
+
const s = parsed.scope;
|
|
161
|
+
if (typeof s.fail_closed === "boolean")
|
|
162
|
+
base.scope.fail_closed = s.fail_closed;
|
|
163
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
164
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
165
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
166
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
167
|
+
}
|
|
168
|
+
if (Array.isArray(parsed.rules)) {
|
|
169
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
172
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
173
|
+
}
|
|
174
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
175
|
+
const sb = parsed.sandbox;
|
|
176
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
177
|
+
base.sandbox.mode = sb.mode;
|
|
178
|
+
if (typeof sb.network === "boolean")
|
|
179
|
+
base.sandbox.network = sb.network;
|
|
180
|
+
if (Array.isArray(sb.read_deny))
|
|
181
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
182
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
183
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
184
|
+
}
|
|
185
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
186
|
+
const iso = parsed.isolation;
|
|
187
|
+
if (iso.default_mode === "worktree")
|
|
188
|
+
base.isolation.default_mode = iso.default_mode;
|
|
189
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
190
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
191
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
192
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
193
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
194
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
195
|
+
}
|
|
196
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
197
|
+
const comp = parsed.completion;
|
|
198
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
199
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
200
|
+
if (Array.isArray(comp.checks))
|
|
201
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
202
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
203
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
204
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
205
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
206
|
+
}
|
|
207
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
208
|
+
const runtimeImage = parsed.runtime_image;
|
|
209
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
210
|
+
const deps = runtimeImage.deps;
|
|
211
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
212
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
213
|
+
}
|
|
214
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
215
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
219
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
223
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
224
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
225
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return base;
|
|
229
|
+
}
|
|
230
|
+
function isValidMode(value) {
|
|
231
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
232
|
+
}
|
|
233
|
+
function isValidRule(value) {
|
|
234
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
235
|
+
return false;
|
|
236
|
+
const r = value;
|
|
237
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
238
|
+
}
|
|
239
|
+
function migrateLegacyDeny(deny) {
|
|
240
|
+
const rules = [];
|
|
241
|
+
for (const entry of deny) {
|
|
242
|
+
if (typeof entry.id !== "string")
|
|
243
|
+
continue;
|
|
244
|
+
const match = {};
|
|
245
|
+
if (typeof entry.pattern === "string")
|
|
246
|
+
match.pattern = entry.pattern;
|
|
247
|
+
if (typeof entry.regex === "string")
|
|
248
|
+
match.regex = entry.regex;
|
|
249
|
+
if (!match.pattern && !match.regex)
|
|
250
|
+
continue;
|
|
251
|
+
rules.push({
|
|
252
|
+
id: entry.id,
|
|
253
|
+
category: "command",
|
|
254
|
+
match,
|
|
255
|
+
action: "block",
|
|
256
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return rules;
|
|
260
|
+
}
|
|
261
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
262
|
+
return rules.map((rule) => {
|
|
263
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
264
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
265
|
+
return {
|
|
266
|
+
...rule,
|
|
267
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
268
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function getRegexUnsafeReason(pattern) {
|
|
273
|
+
if (pattern.length > 512) {
|
|
274
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
275
|
+
}
|
|
276
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
277
|
+
return "pattern uses backreferences";
|
|
278
|
+
}
|
|
279
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
280
|
+
return "pattern contains nested quantifiers";
|
|
281
|
+
}
|
|
282
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
283
|
+
return "pattern contains nested broad quantifiers";
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
288
|
+
const cached = compiledRegexCache.get(pattern);
|
|
289
|
+
if (cached !== undefined) {
|
|
290
|
+
return cached ?? undefined;
|
|
291
|
+
}
|
|
292
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
293
|
+
if (unsafeReason) {
|
|
294
|
+
if (logOnFailure) {
|
|
295
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
296
|
+
}
|
|
297
|
+
compiledRegexCache.set(pattern, null);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const compiled = new RegExp(pattern);
|
|
302
|
+
compiledRegexCache.set(pattern, compiled);
|
|
303
|
+
return compiled;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (logOnFailure) {
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
308
|
+
}
|
|
309
|
+
compiledRegexCache.set(pattern, null);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function matchRule(rule, input) {
|
|
314
|
+
const { match } = rule;
|
|
315
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
if (match.regex) {
|
|
319
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
320
|
+
if (!compiled) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
return compiled.test(input);
|
|
325
|
+
} catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
332
|
+
if (!rule.unless)
|
|
333
|
+
return false;
|
|
334
|
+
if (rule.unless.regex) {
|
|
335
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
336
|
+
if (!compiled) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
if (compiled.test(command))
|
|
341
|
+
return true;
|
|
342
|
+
} catch {}
|
|
343
|
+
}
|
|
344
|
+
if (rule.unless.task_in && taskId) {
|
|
345
|
+
if (rule.unless.task_in.includes(taskId))
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
function resolveAction(mode, matched) {
|
|
351
|
+
if (matched.length === 0)
|
|
352
|
+
return "allow";
|
|
353
|
+
if (mode === "off")
|
|
354
|
+
return "allow";
|
|
355
|
+
if (mode === "observe")
|
|
356
|
+
return "warn";
|
|
357
|
+
return "block";
|
|
358
|
+
}
|
|
359
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
360
|
+
if (rawPath.startsWith("/"))
|
|
361
|
+
return resolve(rawPath);
|
|
362
|
+
return resolve(projectRoot, rawPath);
|
|
363
|
+
}
|
|
364
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
365
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
366
|
+
const managedRoots = [
|
|
367
|
+
resolve(projectRoot, "rig"),
|
|
368
|
+
resolve(projectRoot, ".rig"),
|
|
369
|
+
resolve(projectRoot, "artifacts")
|
|
370
|
+
];
|
|
371
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
372
|
+
}
|
|
373
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
374
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
375
|
+
if (taskWorkspace) {
|
|
376
|
+
const workspaceRigRoot = resolve(taskWorkspace, ".rig");
|
|
377
|
+
const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
|
|
378
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
|
|
383
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
384
|
+
}
|
|
385
|
+
function isTestFile(path) {
|
|
386
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
387
|
+
}
|
|
388
|
+
function evaluate(context) {
|
|
389
|
+
const policy = loadPolicy(context.projectRoot);
|
|
390
|
+
switch (context.evaluation.type) {
|
|
391
|
+
case "tool-call":
|
|
392
|
+
return evaluateToolCall(policy, context);
|
|
393
|
+
case "command":
|
|
394
|
+
return evaluateCommand(policy, context);
|
|
395
|
+
case "content-write":
|
|
396
|
+
return evaluateContent(policy, context);
|
|
397
|
+
case "file-access":
|
|
398
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
402
|
+
const allowed = () => ({
|
|
403
|
+
allowed: true,
|
|
404
|
+
matchedRules: [],
|
|
405
|
+
action: "allow",
|
|
406
|
+
failClosed: false
|
|
407
|
+
});
|
|
408
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
409
|
+
return allowed();
|
|
410
|
+
}
|
|
411
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
412
|
+
return allowed();
|
|
413
|
+
}
|
|
414
|
+
if (!context.taskId) {
|
|
415
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
416
|
+
return {
|
|
417
|
+
allowed: false,
|
|
418
|
+
matchedRules: [],
|
|
419
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
420
|
+
failClosed: true
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
return allowed();
|
|
424
|
+
}
|
|
425
|
+
const scopes = context.taskScopes || [];
|
|
426
|
+
if (scopes.length === 0) {
|
|
427
|
+
return allowed();
|
|
428
|
+
}
|
|
429
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
430
|
+
const absPath = resolve(filePath);
|
|
431
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
432
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
433
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
434
|
+
return {
|
|
435
|
+
allowed: policy.mode !== "enforce",
|
|
436
|
+
matchedRules: matched2,
|
|
437
|
+
action: resolveAction(policy.mode, matched2),
|
|
438
|
+
failClosed: false
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
443
|
+
let normalizedPath = filePath;
|
|
444
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
445
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
446
|
+
}
|
|
447
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
448
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
449
|
+
return allowed();
|
|
450
|
+
}
|
|
451
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
452
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
453
|
+
return {
|
|
454
|
+
allowed: policy.mode !== "enforce",
|
|
455
|
+
matchedRules: matched,
|
|
456
|
+
action: resolveAction(policy.mode, matched),
|
|
457
|
+
failClosed: false
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function evaluateCommand(policy, context) {
|
|
461
|
+
const evaluation = context.evaluation;
|
|
462
|
+
if (evaluation.type !== "command") {
|
|
463
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
464
|
+
}
|
|
465
|
+
const command = evaluation.command;
|
|
466
|
+
const matchedRules = [];
|
|
467
|
+
for (const rule of policy.rules) {
|
|
468
|
+
if (rule.category !== "command")
|
|
469
|
+
continue;
|
|
470
|
+
if (!matchRule(rule, command))
|
|
471
|
+
continue;
|
|
472
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
473
|
+
continue;
|
|
474
|
+
matchedRules.push({
|
|
475
|
+
id: rule.id,
|
|
476
|
+
category: rule.category,
|
|
477
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
478
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
const writeTarget = extractWriteTarget(command);
|
|
482
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
483
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
484
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
485
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
489
|
+
return {
|
|
490
|
+
allowed: action !== "block",
|
|
491
|
+
matchedRules,
|
|
492
|
+
action,
|
|
493
|
+
failClosed: false
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function extractWriteTarget(command) {
|
|
497
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
498
|
+
if (redirect?.[1])
|
|
499
|
+
return redirect[1];
|
|
500
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
501
|
+
if (tee?.[2])
|
|
502
|
+
return tee[2];
|
|
503
|
+
return "";
|
|
504
|
+
}
|
|
505
|
+
function evaluateContent(policy, context) {
|
|
506
|
+
const evaluation = context.evaluation;
|
|
507
|
+
if (evaluation.type !== "content-write") {
|
|
508
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
509
|
+
}
|
|
510
|
+
const { content, file_path } = evaluation;
|
|
511
|
+
const matchedRules = [];
|
|
512
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
513
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
514
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
515
|
+
}
|
|
516
|
+
for (const rule of policy.rules) {
|
|
517
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
518
|
+
continue;
|
|
519
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
520
|
+
continue;
|
|
521
|
+
if (!matchRule(rule, content))
|
|
522
|
+
continue;
|
|
523
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
524
|
+
continue;
|
|
525
|
+
matchedRules.push({
|
|
526
|
+
id: rule.id,
|
|
527
|
+
category: rule.category,
|
|
528
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
529
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
533
|
+
return {
|
|
534
|
+
allowed: action !== "block",
|
|
535
|
+
matchedRules,
|
|
536
|
+
action,
|
|
537
|
+
failClosed: false
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
function evaluateToolCall(policy, context) {
|
|
541
|
+
const evaluation = context.evaluation;
|
|
542
|
+
if (evaluation.type !== "tool-call") {
|
|
543
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
544
|
+
}
|
|
545
|
+
const { tool_name, tool_input } = evaluation;
|
|
546
|
+
const allMatched = [];
|
|
547
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
548
|
+
for (const fp of filePaths) {
|
|
549
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
550
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
551
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
552
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const content = extractContentFromToolInput(tool_input);
|
|
556
|
+
if (content) {
|
|
557
|
+
const filePath = filePaths[0] || "";
|
|
558
|
+
const contentContext = {
|
|
559
|
+
...context,
|
|
560
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
561
|
+
};
|
|
562
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
563
|
+
for (const rule of contentPolicy.rules) {
|
|
564
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
565
|
+
continue;
|
|
566
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
567
|
+
continue;
|
|
568
|
+
if (!matchRule(rule, content))
|
|
569
|
+
continue;
|
|
570
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
571
|
+
continue;
|
|
572
|
+
allMatched.push({
|
|
573
|
+
id: rule.id,
|
|
574
|
+
category: rule.category,
|
|
575
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
576
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (tool_name === "Bash") {
|
|
581
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
582
|
+
if (command) {
|
|
583
|
+
const cmdContext = {
|
|
584
|
+
...context,
|
|
585
|
+
evaluation: { type: "command", command }
|
|
586
|
+
};
|
|
587
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
588
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
589
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const seen = new Set;
|
|
594
|
+
const deduplicated = [];
|
|
595
|
+
for (const rule of allMatched) {
|
|
596
|
+
if (!seen.has(rule.id)) {
|
|
597
|
+
seen.add(rule.id);
|
|
598
|
+
deduplicated.push(rule);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
602
|
+
return {
|
|
603
|
+
allowed: action !== "block",
|
|
604
|
+
matchedRules: deduplicated,
|
|
605
|
+
action,
|
|
606
|
+
failClosed: false
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function isWriteTool(toolName) {
|
|
610
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
611
|
+
}
|
|
612
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
613
|
+
const paths = [];
|
|
614
|
+
const add = (value) => {
|
|
615
|
+
if (typeof value === "string" && value.trim()) {
|
|
616
|
+
paths.push(value.trim());
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
620
|
+
add(input.file_path);
|
|
621
|
+
add(input.path);
|
|
622
|
+
} else if (toolName === "Glob") {
|
|
623
|
+
add(input.path);
|
|
624
|
+
} else if (toolName === "Grep") {
|
|
625
|
+
add(input.path);
|
|
626
|
+
} else {
|
|
627
|
+
add(input.file_path);
|
|
628
|
+
add(input.path);
|
|
629
|
+
}
|
|
630
|
+
return paths;
|
|
631
|
+
}
|
|
632
|
+
function extractContentFromToolInput(input) {
|
|
633
|
+
if (typeof input.content === "string")
|
|
634
|
+
return input.content;
|
|
635
|
+
if (typeof input.new_string === "string")
|
|
636
|
+
return input.new_string;
|
|
637
|
+
return "";
|
|
638
|
+
}
|
|
639
|
+
var guardHotPathPrimed = false;
|
|
640
|
+
function primeGuardHotPaths() {
|
|
641
|
+
if (guardHotPathPrimed) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
guardHotPathPrimed = true;
|
|
645
|
+
try {
|
|
646
|
+
optimizeNextInvocation(matchRule);
|
|
647
|
+
optimizeNextInvocation(evaluate);
|
|
648
|
+
} catch {}
|
|
649
|
+
}
|
|
650
|
+
primeGuardHotPaths();
|
|
651
|
+
|
|
652
|
+
// packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
|
|
653
|
+
import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
|
|
654
|
+
|
|
655
|
+
// packages/cli-surface-plugin/src/runner.ts
|
|
23
656
|
class CliError extends RuntimeCliError {
|
|
24
|
-
hint;
|
|
25
657
|
constructor(message, exitCode = 1, options = {}) {
|
|
26
|
-
super(message, exitCode);
|
|
27
|
-
if (options.hint?.trim()) {
|
|
28
|
-
this.hint = options.hint.trim();
|
|
29
|
-
}
|
|
658
|
+
super(message, exitCode, options);
|
|
30
659
|
}
|
|
31
660
|
}
|
|
32
661
|
function takeOption(args, option) {
|
|
@@ -65,10 +694,13 @@ Usage: ${usage}`);
|
|
|
65
694
|
|
|
66
695
|
// packages/cli-surface-plugin/src/commands/_cli-format.ts
|
|
67
696
|
import pc from "picocolors";
|
|
68
|
-
import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/run-worker/runs";
|
|
69
697
|
var dim = pc.dim;
|
|
70
698
|
var faintBar = pc.dim("\u2502");
|
|
71
699
|
var accent = pc.cyan;
|
|
700
|
+
function numberField(record, key) {
|
|
701
|
+
const value = record[key];
|
|
702
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
703
|
+
}
|
|
72
704
|
function truncate(value, width) {
|
|
73
705
|
return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
74
706
|
}
|
|
@@ -90,6 +722,52 @@ function colorForRole(role) {
|
|
|
90
722
|
return pc.dim;
|
|
91
723
|
}
|
|
92
724
|
}
|
|
725
|
+
function normalizeStatus(status) {
|
|
726
|
+
const normalized = String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
727
|
+
return normalized === "waiting-input" ? "waiting-user-input" : normalized;
|
|
728
|
+
}
|
|
729
|
+
function statusColorRole(status) {
|
|
730
|
+
switch (normalizeStatus(status)) {
|
|
731
|
+
case "done":
|
|
732
|
+
case "completed":
|
|
733
|
+
case "ready":
|
|
734
|
+
case "healthy":
|
|
735
|
+
case "approved":
|
|
736
|
+
case "merged":
|
|
737
|
+
return "success";
|
|
738
|
+
case "needs-attention":
|
|
739
|
+
case "waiting-approval":
|
|
740
|
+
case "waiting-user-input":
|
|
741
|
+
case "blocked":
|
|
742
|
+
case "paused":
|
|
743
|
+
return "action-yellow";
|
|
744
|
+
case "running":
|
|
745
|
+
case "adopted":
|
|
746
|
+
case "preparing":
|
|
747
|
+
case "created":
|
|
748
|
+
case "queued":
|
|
749
|
+
case "starting":
|
|
750
|
+
case "pending":
|
|
751
|
+
case "in-progress":
|
|
752
|
+
case "active":
|
|
753
|
+
case "booting":
|
|
754
|
+
case "validating":
|
|
755
|
+
case "reviewing":
|
|
756
|
+
case "closing-out":
|
|
757
|
+
return "active-cyan";
|
|
758
|
+
case "failed":
|
|
759
|
+
case "error":
|
|
760
|
+
case "rejected":
|
|
761
|
+
return "failure";
|
|
762
|
+
case "stopped":
|
|
763
|
+
case "cancelled":
|
|
764
|
+
case "canceled":
|
|
765
|
+
case "stale":
|
|
766
|
+
return "muted";
|
|
767
|
+
default:
|
|
768
|
+
return "neutral";
|
|
769
|
+
}
|
|
770
|
+
}
|
|
93
771
|
function firstString(record, keys, fallback = "") {
|
|
94
772
|
for (const key of keys) {
|
|
95
773
|
const value = record[key];
|
|
@@ -108,10 +786,20 @@ function runTitleOf(run) {
|
|
|
108
786
|
return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
|
|
109
787
|
}
|
|
110
788
|
function runLikeStatusText(run) {
|
|
111
|
-
|
|
789
|
+
const record = run;
|
|
790
|
+
const normalized = normalizeStatus(record.status);
|
|
791
|
+
return normalized || (record.live === true && record.stale !== true ? "starting" : "unknown");
|
|
112
792
|
}
|
|
113
793
|
function runLikeStatusColor(run) {
|
|
114
|
-
|
|
794
|
+
const record = run;
|
|
795
|
+
const pendingApprovals = numberField(record, "pendingApprovals") ?? 0;
|
|
796
|
+
const pendingInputs = numberField(record, "pendingInputs") ?? 0;
|
|
797
|
+
const stallCount = numberField(record, "stallCount") ?? 0;
|
|
798
|
+
const status = runLikeStatusText(run);
|
|
799
|
+
if (status !== "completed" && status !== "failed" && status !== "stopped" && (status === "needs-attention" || pendingApprovals + pendingInputs > 0 || stallCount > 0)) {
|
|
800
|
+
return colorForRole("action-yellow");
|
|
801
|
+
}
|
|
802
|
+
return colorForRole(statusColorRole(status));
|
|
115
803
|
}
|
|
116
804
|
function printFormattedOutput(message) {
|
|
117
805
|
console.log(message);
|
|
@@ -148,6 +836,9 @@ function formatRunList(runs, options = {}) {
|
|
|
148
836
|
}
|
|
149
837
|
|
|
150
838
|
// packages/cli-surface-plugin/src/commands/inspect.ts
|
|
839
|
+
var RunReadModelCap = defineCapability(RUN_READ_MODEL);
|
|
840
|
+
var TaskDataCap = defineCapability(TASK_DATA_SERVICE_CAPABILITY);
|
|
841
|
+
var taskData = () => requireInstalledCapability(TaskDataCap, "task-data capability unavailable: load @rig/task-sources-plugin (default bundle) before inspecting task artifacts.");
|
|
151
842
|
var INSPECT_USAGE = "rig inspect <logs|artifact|artifacts|run-logs|runs|failures|graph|diff>";
|
|
152
843
|
function inspectHelpText() {
|
|
153
844
|
return [
|
|
@@ -194,18 +885,41 @@ function requireRunId(value, usage) {
|
|
|
194
885
|
throw new CliError(`${usage} requires a run id.`, 2, { hint: "Run `rig inspect runs` to find run ids." });
|
|
195
886
|
return value.trim();
|
|
196
887
|
}
|
|
888
|
+
async function loadRunReadModel(projectRoot) {
|
|
889
|
+
return requireCapabilityForRoot(projectRoot, RunReadModelCap, "No run-worker plugin provides run read model for this project root.");
|
|
890
|
+
}
|
|
891
|
+
async function listRunRecords(projectRoot, deps) {
|
|
892
|
+
return deps.listRunProjections ? deps.listRunProjections(projectRoot) : (await loadRunReadModel(projectRoot)).listRuns({ projectRoot });
|
|
893
|
+
}
|
|
894
|
+
async function getRunRecord(projectRoot, runId, deps) {
|
|
895
|
+
return deps.getRunProjection ? deps.getRunProjection(projectRoot, runId) : (await loadRunReadModel(projectRoot)).getRun({ projectRoot, selector: { id: runId, kind: "any" } });
|
|
896
|
+
}
|
|
897
|
+
async function getRunDetails(projectRoot, runId, deps, options = {}) {
|
|
898
|
+
return deps.getRunDetails ? deps.getRunDetails(projectRoot, runId, options) : (await loadRunReadModel(projectRoot)).getRunDetails({ projectRoot, selector: { id: runId, kind: "any" }, ...options.includeLogs === undefined ? {} : { includeLogs: options.includeLogs } });
|
|
899
|
+
}
|
|
900
|
+
async function runsForTaskId(projectRoot, taskId, deps) {
|
|
901
|
+
try {
|
|
902
|
+
const runs = await listRunRecords(projectRoot, deps);
|
|
903
|
+
const filtered = runs.filter((run2) => run2.taskId === taskId || run2.runId === taskId);
|
|
904
|
+
if (filtered.length > 0)
|
|
905
|
+
return filtered;
|
|
906
|
+
} catch {}
|
|
907
|
+
const run = await getRunRecord(projectRoot, taskId, deps);
|
|
908
|
+
return run ? [run] : [];
|
|
909
|
+
}
|
|
910
|
+
function failureLine(failure) {
|
|
911
|
+
return failure.detail ? `${failure.summary} \u2014 ${failure.detail}` : failure.summary;
|
|
912
|
+
}
|
|
197
913
|
async function executeLogs(context, args, deps) {
|
|
198
914
|
const taskResult = takeOption(args, "--task");
|
|
199
915
|
requireNoExtraArgs(taskResult.rest, "rig inspect logs --task <id>");
|
|
200
916
|
const taskId = requireTask(taskResult.value, "rig inspect logs --task <id>");
|
|
201
|
-
const runs = await
|
|
202
|
-
listRuns: deps.listRunProjections ?? listRuns,
|
|
203
|
-
getRun: deps.getRunProjection ?? getRun
|
|
204
|
-
});
|
|
917
|
+
const runs = await runsForTaskId(context.projectRoot, taskId, deps);
|
|
205
918
|
const projection = runs[0];
|
|
206
919
|
if (!projection)
|
|
207
920
|
throw new CliError(`No projected runs found for task ${taskId}.`, 1, { hint: "Run `rig run list` to confirm the run journal is discoverable." });
|
|
208
|
-
const
|
|
921
|
+
const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
|
|
922
|
+
const lines = [...details?.logs ?? []];
|
|
209
923
|
if (context.outputMode === "text") {
|
|
210
924
|
if (lines.length === 0)
|
|
211
925
|
console.log(`No log entries found for task ${taskId}.`);
|
|
@@ -223,7 +937,7 @@ async function executeArtifact(context, args, deps) {
|
|
|
223
937
|
const filename = fileResult.value?.trim();
|
|
224
938
|
if (!filename)
|
|
225
939
|
throw new CliError("rig inspect artifact --task <id> --file <name> requires an artifact file name.", 2);
|
|
226
|
-
const preview = await (deps.taskArtifactRead ?? taskArtifactRead)(context.projectRoot, filename, { taskId, maxBytes: 64 * 1024 });
|
|
940
|
+
const preview = await (deps.taskArtifactRead ?? taskData().taskArtifactRead)(context.projectRoot, filename, { taskId, maxBytes: 64 * 1024 });
|
|
227
941
|
if (context.outputMode === "text") {
|
|
228
942
|
console.log(preview.contents);
|
|
229
943
|
if (preview.truncated)
|
|
@@ -236,10 +950,11 @@ async function executeRunLogs(context, args, deps) {
|
|
|
236
950
|
const runResult = takeOption(args, "--run");
|
|
237
951
|
requireNoExtraArgs(runResult.rest, "rig inspect run-logs --run <id>");
|
|
238
952
|
const runId = requireRunId(runResult.value, "rig inspect run-logs --run <id>");
|
|
239
|
-
const projection = await (
|
|
953
|
+
const projection = await getRunRecord(context.projectRoot, runId, deps);
|
|
240
954
|
if (!projection)
|
|
241
955
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig inspect runs` to see runs." });
|
|
242
|
-
const
|
|
956
|
+
const details = await getRunDetails(context.projectRoot, projection.runId, deps, { includeLogs: true });
|
|
957
|
+
const lines = [...details?.logs ?? []];
|
|
243
958
|
if (context.outputMode === "text") {
|
|
244
959
|
if (lines.length === 0)
|
|
245
960
|
console.log(`No log entries found for run ${projection.runId}.`);
|
|
@@ -251,7 +966,7 @@ async function executeRunLogs(context, args, deps) {
|
|
|
251
966
|
}
|
|
252
967
|
async function executeRuns(context, args, deps) {
|
|
253
968
|
requireNoExtraArgs(args, "rig inspect runs");
|
|
254
|
-
const runs = await (
|
|
969
|
+
const runs = await listRunRecords(context.projectRoot, deps);
|
|
255
970
|
const formatted = runs.map(toRunLike);
|
|
256
971
|
if (context.outputMode === "text")
|
|
257
972
|
printFormattedOutput(formatRunList(formatted));
|
|
@@ -262,14 +977,14 @@ async function executeArtifacts(context, args, deps) {
|
|
|
262
977
|
requireNoExtraArgs(taskResult.rest, "rig inspect artifacts --task <id>");
|
|
263
978
|
const taskId = requireTask(taskResult.value, "rig inspect artifacts --task <id>");
|
|
264
979
|
if (context.outputMode === "text")
|
|
265
|
-
await (deps.taskArtifacts ?? taskArtifacts)(context.projectRoot, taskId);
|
|
980
|
+
await (deps.taskArtifacts ?? taskData().taskArtifacts)(context.projectRoot, taskId);
|
|
266
981
|
return { ok: true, group: "inspect", command: "artifacts", details: { taskId, printed: context.outputMode === "text" } };
|
|
267
982
|
}
|
|
268
983
|
async function executeDiff(context, args, deps) {
|
|
269
984
|
const taskResult = takeOption(args, "--task");
|
|
270
985
|
requireNoExtraArgs(taskResult.rest, "rig inspect diff --task <id>");
|
|
271
986
|
const taskId = requireTask(taskResult.value, "rig inspect diff --task <id>");
|
|
272
|
-
const files = (deps.changedFilesForTask ?? changedFilesForTask)(context.projectRoot, taskId, true);
|
|
987
|
+
const files = (deps.changedFilesForTask ?? taskData().changedFilesForTask)(context.projectRoot, taskId, true);
|
|
273
988
|
if (context.outputMode === "text")
|
|
274
989
|
for (const file of files)
|
|
275
990
|
console.log(file);
|
|
@@ -279,18 +994,15 @@ async function executeGraph(context, args, deps) {
|
|
|
279
994
|
const taskResult = takeOption(args, "--task");
|
|
280
995
|
requireNoExtraArgs(taskResult.rest, "rig inspect graph [--task <id>]");
|
|
281
996
|
if (context.outputMode === "text")
|
|
282
|
-
await (deps.taskDeps ?? taskDeps)(context.projectRoot, taskResult.value);
|
|
997
|
+
await (deps.taskDeps ?? taskData().taskDeps)(context.projectRoot, taskResult.value);
|
|
283
998
|
return { ok: true, group: "inspect", command: "graph", details: { taskId: taskResult.value ?? null, printed: context.outputMode === "text" } };
|
|
284
999
|
}
|
|
285
1000
|
async function executeFailures(context, args, deps) {
|
|
286
1001
|
const taskResult = takeOption(args, "--task");
|
|
287
1002
|
requireNoExtraArgs(taskResult.rest, "rig inspect failures --task <id>");
|
|
288
1003
|
const taskId = requireTask(taskResult.value, "rig inspect failures --task <id>");
|
|
289
|
-
const runs = await
|
|
290
|
-
|
|
291
|
-
getRun: deps.getRunProjection ?? getRun
|
|
292
|
-
});
|
|
293
|
-
const failures = runs.flatMap(summarizeRunFailures);
|
|
1004
|
+
const runs = await runsForTaskId(context.projectRoot, taskId, deps);
|
|
1005
|
+
const failures = (await Promise.all(runs.map(async (run) => (await getRunDetails(context.projectRoot, run.runId, deps))?.failures ?? []))).flat().map(failureLine);
|
|
294
1006
|
if (context.outputMode === "text") {
|
|
295
1007
|
if (failures.length === 0)
|
|
296
1008
|
console.log(`No failures recorded for task ${taskId}.`);
|