@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.
Files changed (112) hide show
  1. package/dist/src/app/drone-ui.d.ts +0 -11
  2. package/dist/src/app/drone-ui.js +0 -114
  3. package/dist/src/commands/_async-ui.d.ts +1 -1
  4. package/dist/src/commands/_cli-format.d.ts +0 -29
  5. package/dist/src/commands/_cli-format.js +59 -113
  6. package/dist/src/commands/_connection-state.d.ts +6 -33
  7. package/dist/src/commands/_connection-state.js +654 -138
  8. package/dist/src/commands/_doctor-checks.d.ts +2 -5
  9. package/dist/src/commands/_doctor-checks.js +10 -9
  10. package/dist/src/commands/_help-catalog.d.ts +2 -1
  11. package/dist/src/commands/_help-catalog.js +654 -7
  12. package/dist/src/commands/_inprocess-services.d.ts +5 -5
  13. package/dist/src/commands/_inprocess-services.js +1 -1
  14. package/dist/src/commands/_parsers.js +651 -12
  15. package/dist/src/commands/_paths.d.ts +0 -2
  16. package/dist/src/commands/_paths.js +2 -10
  17. package/dist/src/commands/_pi-install.d.ts +2 -12
  18. package/dist/src/commands/_pi-install.js +3 -36
  19. package/dist/src/commands/_policy.js +659 -20
  20. package/dist/src/commands/agent.d.ts +1 -1
  21. package/dist/src/commands/agent.js +675 -24
  22. package/dist/src/commands/config.d.ts +1 -1
  23. package/dist/src/commands/config.js +656 -21
  24. package/dist/src/commands/dist.d.ts +1 -1
  25. package/dist/src/commands/dist.js +828 -102
  26. package/dist/src/commands/doctor.d.ts +1 -1
  27. package/dist/src/commands/doctor.js +658 -12
  28. package/dist/src/commands/github.d.ts +1 -1
  29. package/dist/src/commands/github.js +658 -19
  30. package/dist/src/commands/inbox.d.ts +12 -8
  31. package/dist/src/commands/inbox.js +741 -22
  32. package/dist/src/commands/init.d.ts +17 -19
  33. package/dist/src/commands/init.js +836 -306
  34. package/dist/src/commands/inspect.d.ts +5 -6
  35. package/dist/src/commands/inspect.js +754 -42
  36. package/dist/src/commands/pi.d.ts +1 -1
  37. package/dist/src/commands/pi.js +655 -16
  38. package/dist/src/commands/plugin.d.ts +9 -9
  39. package/dist/src/commands/plugin.js +652 -13
  40. package/dist/src/commands/profile-and-review.d.ts +1 -1
  41. package/dist/src/commands/profile-and-review.js +655 -16
  42. package/dist/src/commands/queue.d.ts +1 -1
  43. package/dist/src/commands/queue.js +871 -12
  44. package/dist/src/commands/remote-client.d.ts +152 -0
  45. package/dist/src/commands/remote-client.js +475 -0
  46. package/dist/src/commands/remote.d.ts +1 -1
  47. package/dist/src/commands/remote.js +1100 -29
  48. package/dist/src/commands/repo-git-harness.d.ts +1 -1
  49. package/dist/src/commands/repo-git-harness.js +2321 -47
  50. package/dist/src/commands/run.d.ts +10 -6
  51. package/dist/src/commands/run.js +830 -50
  52. package/dist/src/commands/server.d.ts +1 -1
  53. package/dist/src/commands/server.js +649 -11
  54. package/dist/src/commands/setup.d.ts +2 -2
  55. package/dist/src/commands/setup.js +829 -18
  56. package/dist/src/commands/stats.d.ts +2 -4
  57. package/dist/src/commands/stats.js +1299 -20
  58. package/dist/src/commands/test.d.ts +1 -1
  59. package/dist/src/commands/test.js +648 -9
  60. package/dist/src/commands/triage.d.ts +2 -3
  61. package/dist/src/commands/triage.js +657 -11
  62. package/dist/src/commands/workspace.d.ts +1 -1
  63. package/dist/src/commands/workspace.js +1280 -15
  64. package/dist/src/control-plane/agent-binary-build.d.ts +9 -0
  65. package/dist/src/control-plane/agent-binary-build.js +88 -0
  66. package/dist/src/control-plane/embedded-native-assets.d.ts +7 -0
  67. package/dist/src/control-plane/embedded-native-assets.js +6 -0
  68. package/dist/src/control-plane/guard.d.ts +17 -0
  69. package/dist/src/control-plane/guard.js +684 -0
  70. package/dist/src/control-plane/harness-cli.d.ts +12 -0
  71. package/dist/src/control-plane/harness-cli.js +1623 -0
  72. package/dist/src/control-plane/native/git-ops.d.ts +67 -0
  73. package/dist/src/control-plane/native/git-ops.js +1381 -0
  74. package/dist/src/control-plane/native/github-auth-env.d.ts +1 -0
  75. package/dist/src/control-plane/native/github-auth-env.js +21 -0
  76. package/dist/src/control-plane/native/host-git.d.ts +4 -0
  77. package/dist/src/control-plane/native/host-git.js +51 -0
  78. package/dist/src/control-plane/priority-queue.d.ts +22 -0
  79. package/dist/src/control-plane/priority-queue.js +212 -0
  80. package/dist/src/control-plane/rigfig.d.ts +9 -0
  81. package/dist/src/control-plane/rigfig.js +70 -0
  82. package/dist/src/control-plane/scope.d.ts +3 -0
  83. package/dist/src/control-plane/scope.js +58 -0
  84. package/dist/src/control-plane/setup-status.d.ts +44 -0
  85. package/dist/src/control-plane/setup-status.js +164 -0
  86. package/dist/src/control-plane/task-data.d.ts +2 -0
  87. package/dist/src/control-plane/task-data.js +12 -0
  88. package/dist/src/control-plane/workspace-ops.d.ts +79 -0
  89. package/dist/src/control-plane/workspace-ops.js +639 -0
  90. package/dist/src/help-catalog-data.d.ts +7 -0
  91. package/dist/src/help-catalog-data.js +660 -0
  92. package/dist/src/kernel-dispatch.js +1 -3
  93. package/dist/src/plugin.js +10072 -30
  94. package/dist/src/runner.d.ts +7 -9
  95. package/dist/src/runner.js +750 -30
  96. package/package.json +12 -13
  97. package/dist/src/commands/_json-output.d.ts +0 -11
  98. package/dist/src/commands/_json-output.js +0 -54
  99. package/dist/src/commands/_pi-frontend.d.ts +0 -35
  100. package/dist/src/commands/_pi-frontend.js +0 -64
  101. package/dist/src/commands/_run-driver-helpers.d.ts +0 -26
  102. package/dist/src/commands/_run-driver-helpers.js +0 -132
  103. package/dist/src/commands/task-run-driver.d.ts +0 -93
  104. package/dist/src/commands/task-run-driver.js +0 -136
  105. package/dist/src/commands/task.d.ts +0 -46
  106. package/dist/src/commands/task.js +0 -555
  107. package/dist/src/provider-model.d.ts +0 -34
  108. package/dist/src/provider-model.js +0 -56
  109. package/dist/src/rig-config-package-deps.d.ts +0 -10
  110. package/dist/src/rig-config-package-deps.js +0 -272
  111. package/dist/src/version.d.ts +0 -8
  112. package/dist/src/version.js +0 -47
@@ -1,22 +1,661 @@
1
1
  // @bun
2
2
  // packages/cli-surface-plugin/src/commands/inbox.ts
3
- import { Duration, Effect, Stream } from "effect";
4
- import { localRunChanges } from "@rig/runtime/control-plane/run-discovery-stream";
5
- import { listInboxRecords as clientListInboxRecords, readInboxCounts, resolveInboxRequest } from "@rig/run-worker/runs";
3
+ import { RUN_READ_MODEL } from "@rig/contracts";
4
+ import { defineCapability } from "@rig/core/capability";
5
+ import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
6
6
 
7
7
  // packages/cli-surface-plugin/src/runner.ts
8
- import { EventBus } from "@rig/runtime/control-plane/runtime/events";
9
- import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
10
- import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
11
- import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
8
+ import { EventBus } from "@rig/core/runtime-events";
9
+ import { CliError as RuntimeCliError } from "@rig/contracts";
12
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
+ }
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
13
656
  class CliError extends RuntimeCliError {
14
- hint;
15
657
  constructor(message, exitCode = 1, options = {}) {
16
- super(message, exitCode);
17
- if (options.hint?.trim()) {
18
- this.hint = options.hint.trim();
19
- }
658
+ super(message, exitCode, options);
20
659
  }
21
660
  }
22
661
  function takeOption(args, option) {
@@ -48,7 +687,6 @@ Usage: ${usage}`);
48
687
 
49
688
  // packages/cli-surface-plugin/src/commands/_cli-format.ts
50
689
  import pc from "picocolors";
51
- import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/run-worker/runs";
52
690
  var dim = pc.dim;
53
691
  var faintBar = pc.dim("\u2502");
54
692
  var accent = pc.cyan;
@@ -67,6 +705,52 @@ function colorForRole(role) {
67
705
  return pc.dim;
68
706
  }
69
707
  }
708
+ function normalizeStatus(status) {
709
+ const normalized = String(status ?? "").trim().toLowerCase().replace(/[\s_]+/g, "-");
710
+ return normalized === "waiting-input" ? "waiting-user-input" : normalized;
711
+ }
712
+ function statusColorRole(status) {
713
+ switch (normalizeStatus(status)) {
714
+ case "done":
715
+ case "completed":
716
+ case "ready":
717
+ case "healthy":
718
+ case "approved":
719
+ case "merged":
720
+ return "success";
721
+ case "needs-attention":
722
+ case "waiting-approval":
723
+ case "waiting-user-input":
724
+ case "blocked":
725
+ case "paused":
726
+ return "action-yellow";
727
+ case "running":
728
+ case "adopted":
729
+ case "preparing":
730
+ case "created":
731
+ case "queued":
732
+ case "starting":
733
+ case "pending":
734
+ case "in-progress":
735
+ case "active":
736
+ case "booting":
737
+ case "validating":
738
+ case "reviewing":
739
+ case "closing-out":
740
+ return "active-cyan";
741
+ case "failed":
742
+ case "error":
743
+ case "rejected":
744
+ return "failure";
745
+ case "stopped":
746
+ case "cancelled":
747
+ case "canceled":
748
+ case "stale":
749
+ return "muted";
750
+ default:
751
+ return "neutral";
752
+ }
753
+ }
70
754
  function statusColor(status) {
71
755
  return colorForRole(statusColorRole(status));
72
756
  }
@@ -125,15 +809,36 @@ function formatInboxList(kind, entries) {
125
809
  }
126
810
 
127
811
  // packages/cli-surface-plugin/src/commands/inbox.ts
812
+ var RunReadModelCap = defineCapability(RUN_READ_MODEL);
813
+ async function loadRunReadModel(projectRoot) {
814
+ return requireCapabilityForRoot(projectRoot, RunReadModelCap, "No run-worker plugin provides run read model for this project root.");
815
+ }
816
+ function readModelKind(kind) {
817
+ return kind === "approvals" ? "approval" : "input";
818
+ }
128
819
  async function listInboxRecords(context, kind, filters = {}, deps = {}) {
129
- const clientDeps = deps.listRuns ? { listRuns: deps.listRuns } : {};
130
- return deps.listInbox ? deps.listInbox(context.projectRoot, kind, filters) : clientListInboxRecords(context.projectRoot, kind, filters, clientDeps);
820
+ if (deps.listInbox)
821
+ return deps.listInbox(context.projectRoot, kind, filters);
822
+ const readModel = await loadRunReadModel(context.projectRoot);
823
+ return readModel.listInboxRecords({
824
+ projectRoot: context.projectRoot,
825
+ kind: readModelKind(kind),
826
+ ...filters.run ? { runId: filters.run } : {},
827
+ ...filters.task ? { taskId: filters.task } : {}
828
+ });
131
829
  }
132
830
  async function listInbox(context, kind, filters = {}, deps = {}) {
133
831
  return listInboxRecords(context, kind, filters, deps);
134
832
  }
135
- async function readPendingInboxCounts(context) {
136
- return readInboxCounts(context.projectRoot);
833
+ async function readPendingInboxCounts(context, deps = {}) {
834
+ if (deps.getInboxCounts)
835
+ return deps.getInboxCounts(context.projectRoot);
836
+ try {
837
+ const counts = await (await loadRunReadModel(context.projectRoot)).getInboxCounts({ projectRoot: context.projectRoot, kind: "all" });
838
+ return { approvals: counts.approvals, inputs: counts.inputs };
839
+ } catch {
840
+ return null;
841
+ }
137
842
  }
138
843
  function resolutionAttachError(runId, verb, error) {
139
844
  const target = runId ?? "<run-id>";
@@ -158,13 +863,26 @@ function renderInboxSnapshot(snapshot) {
158
863
  if (snapshot.approvals.length === 0 && snapshot.inputs.length === 0)
159
864
  console.log("Nothing pending.");
160
865
  }
866
+ function sleep(ms) {
867
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
868
+ setTimeout(resolve2, ms);
869
+ return promise;
870
+ }
161
871
  async function watchInbox(context, filters, deps) {
162
872
  const snapshot = await readInboxSnapshot(context, filters, deps);
163
873
  if (context.outputMode !== "text")
164
874
  return { ok: true, group: "inbox", command: "watch", details: snapshot };
165
875
  renderInboxSnapshot(snapshot);
166
- await Effect.runPromise(localRunChanges(context.projectRoot).pipe(Stream.debounce(Duration.millis(500)), Stream.runForEach(() => Effect.promise(async () => renderInboxSnapshot(await readInboxSnapshot(context, filters, deps))))));
167
- return { ok: true, group: "inbox", command: "watch", details: snapshot };
876
+ while (true) {
877
+ await sleep(500);
878
+ renderInboxSnapshot(await readInboxSnapshot(context, filters, deps));
879
+ }
880
+ }
881
+ async function deliverInboxResolution(context, runId, resolution, deps) {
882
+ const result = deps.resolveInboxRequest ? await deps.resolveInboxRequest(context.projectRoot, runId, resolution) : await (await loadRunReadModel(context.projectRoot)).resolveInboxRequest({ projectRoot: context.projectRoot, runId, resolution });
883
+ if (!result.delivered)
884
+ throw new Error(result.detail ?? `Inbox request ${resolution.requestId} was not delivered.`);
885
+ return result;
168
886
  }
169
887
  async function executeInbox(context, args, deps = {}) {
170
888
  const [command = "approvals", ...rest] = args;
@@ -192,14 +910,15 @@ async function executeInbox(context, args, deps = {}) {
192
910
  const rawDecision = (decision.value ?? "approve").trim().toLowerCase();
193
911
  if (rawDecision !== "approve" && rawDecision !== "approved" && rawDecision !== "reject" && rawDecision !== "rejected")
194
912
  throw new CliError("--decision must be approve or reject.", 2);
913
+ const normalizedDecision = rawDecision === "approved" ? "approve" : rawDecision === "rejected" ? "reject" : rawDecision;
195
914
  try {
196
- await resolveInboxRequest(context.projectRoot, run.value, request.value, { kind: "approval", decision: rawDecision });
915
+ await deliverInboxResolution(context, run.value, { kind: "approval", requestId: request.value, decision: normalizedDecision }, deps);
197
916
  } catch (error) {
198
917
  resolutionAttachError(run.value, "approve", error);
199
918
  }
200
919
  if (text)
201
920
  printFormattedOutput(`Resolved approval ${request.value} for run ${run.value}.`);
202
- return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, decision: rawDecision } };
921
+ return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, decision: normalizedDecision } };
203
922
  }
204
923
  case "respond":
205
924
  case "answer": {
@@ -214,7 +933,7 @@ async function executeInbox(context, args, deps = {}) {
214
933
  if (!run.value || !request.value || !answer)
215
934
  resolutionAttachError(run.value, "respond", new Error(`rig inbox ${command} requires --run, --request, and --text/--answer.`));
216
935
  try {
217
- await resolveInboxRequest(context.projectRoot, run.value, request.value, { kind: "input", answer });
936
+ await deliverInboxResolution(context, run.value, { kind: "input", requestId: request.value, answers: { answer } }, deps);
218
937
  } catch (error) {
219
938
  resolutionAttachError(run.value, "respond", error);
220
939
  }