@h-rig/cli-surface-plugin 0.0.6-alpha.157 → 0.0.6-alpha.159
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,17 +1,656 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli-surface-plugin/src/runner.ts
|
|
3
|
-
import { EventBus } from "@rig/runtime
|
|
4
|
-
import { CliError as RuntimeCliError } from "@rig/
|
|
5
|
-
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
6
|
-
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3
|
+
import { EventBus } from "@rig/core/runtime-events";
|
|
4
|
+
import { CliError as RuntimeCliError } from "@rig/contracts";
|
|
7
5
|
|
|
6
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
7
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
8
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
9
|
+
import { resolve } from "path";
|
|
10
|
+
|
|
11
|
+
// packages/cli-surface-plugin/src/control-plane/scope.ts
|
|
12
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
13
|
+
var scopeRegexCache = new Map;
|
|
14
|
+
function unique(values) {
|
|
15
|
+
return [...new Set(values)];
|
|
16
|
+
}
|
|
17
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
18
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
19
|
+
const rules = getScopeRules();
|
|
20
|
+
if (rules?.stripPrefixes) {
|
|
21
|
+
for (const prefix of rules.stripPrefixes) {
|
|
22
|
+
if (normalized.startsWith(prefix)) {
|
|
23
|
+
normalized = normalized.slice(prefix.length);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return normalized;
|
|
28
|
+
}
|
|
29
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
30
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
31
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
32
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
33
|
+
}
|
|
34
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
35
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
36
|
+
}
|
|
37
|
+
return normalizeRelativeScopePath(normalized);
|
|
38
|
+
}
|
|
39
|
+
function scopeGlobToRegex(glob) {
|
|
40
|
+
const cached = scopeRegexCache.get(glob);
|
|
41
|
+
if (cached) {
|
|
42
|
+
return cached;
|
|
43
|
+
}
|
|
44
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
|
|
45
|
+
const compiled = new RegExp(`^${escaped}$`);
|
|
46
|
+
scopeRegexCache.set(glob, compiled);
|
|
47
|
+
return compiled;
|
|
48
|
+
}
|
|
49
|
+
function scopeMatches(path, scopes) {
|
|
50
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
51
|
+
for (const scope of scopes) {
|
|
52
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
53
|
+
for (const candidatePath of pathVariants) {
|
|
54
|
+
for (const candidateScope of scopeVariants) {
|
|
55
|
+
if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
65
|
+
import {
|
|
66
|
+
POLICY_VERSION
|
|
67
|
+
} from "@rig/contracts";
|
|
68
|
+
var DEFAULT_SCOPE = {
|
|
69
|
+
fail_closed: true,
|
|
70
|
+
harness_paths_exempt: true,
|
|
71
|
+
runtime_paths_exempt: true
|
|
72
|
+
};
|
|
73
|
+
var DEFAULT_SANDBOX = {
|
|
74
|
+
mode: "enforce",
|
|
75
|
+
network: true,
|
|
76
|
+
read_deny: [],
|
|
77
|
+
write_allow_from_runtime: true
|
|
78
|
+
};
|
|
79
|
+
var DEFAULT_ISOLATION = {
|
|
80
|
+
default_mode: "worktree",
|
|
81
|
+
repo_symlink_fallback: false,
|
|
82
|
+
strict_provisioning: true,
|
|
83
|
+
fail_closed_on_provision_error: true
|
|
84
|
+
};
|
|
85
|
+
var DEFAULT_COMPLETION = {
|
|
86
|
+
derive_checks_from_scope: true,
|
|
87
|
+
checks: [],
|
|
88
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
89
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
90
|
+
};
|
|
91
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
92
|
+
deps: {
|
|
93
|
+
monorepo_install: false,
|
|
94
|
+
hp_next_install: false
|
|
95
|
+
},
|
|
96
|
+
plugins_require_binaries: true
|
|
97
|
+
};
|
|
98
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
99
|
+
enabled: true
|
|
100
|
+
};
|
|
101
|
+
function defaultPolicy() {
|
|
102
|
+
return {
|
|
103
|
+
version: POLICY_VERSION,
|
|
104
|
+
mode: "enforce",
|
|
105
|
+
scope: { ...DEFAULT_SCOPE },
|
|
106
|
+
rules: [],
|
|
107
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
108
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
109
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
110
|
+
runtime_image: {
|
|
111
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
112
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
113
|
+
},
|
|
114
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
var policyCache = null;
|
|
118
|
+
var policyCachePath = null;
|
|
119
|
+
var seededPolicyConfig = null;
|
|
120
|
+
var compiledRegexCache = new Map;
|
|
121
|
+
function loadPolicy(projectRoot) {
|
|
122
|
+
if (seededPolicyConfig) {
|
|
123
|
+
return seededPolicyConfig;
|
|
124
|
+
}
|
|
125
|
+
const configPath = resolve(projectRoot, "rig/policy/policy.json");
|
|
126
|
+
if (!existsSync(configPath)) {
|
|
127
|
+
return defaultPolicy();
|
|
128
|
+
}
|
|
129
|
+
let mtimeMs;
|
|
130
|
+
try {
|
|
131
|
+
mtimeMs = statSync(configPath).mtimeMs;
|
|
132
|
+
} catch {
|
|
133
|
+
return defaultPolicy();
|
|
134
|
+
}
|
|
135
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
136
|
+
return policyCache.config;
|
|
137
|
+
}
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
141
|
+
} catch {
|
|
142
|
+
return defaultPolicy();
|
|
143
|
+
}
|
|
144
|
+
const config = mergeWithDefaults(parsed);
|
|
145
|
+
policyCache = { mtimeMs, config };
|
|
146
|
+
policyCachePath = configPath;
|
|
147
|
+
return config;
|
|
148
|
+
}
|
|
149
|
+
function mergeWithDefaults(parsed) {
|
|
150
|
+
const base = defaultPolicy();
|
|
151
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
152
|
+
base.mode = parsed.mode;
|
|
153
|
+
}
|
|
154
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
155
|
+
const s = parsed.scope;
|
|
156
|
+
if (typeof s.fail_closed === "boolean")
|
|
157
|
+
base.scope.fail_closed = s.fail_closed;
|
|
158
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
159
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
160
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
161
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
162
|
+
}
|
|
163
|
+
if (Array.isArray(parsed.rules)) {
|
|
164
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
165
|
+
}
|
|
166
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
167
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
168
|
+
}
|
|
169
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
170
|
+
const sb = parsed.sandbox;
|
|
171
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
172
|
+
base.sandbox.mode = sb.mode;
|
|
173
|
+
if (typeof sb.network === "boolean")
|
|
174
|
+
base.sandbox.network = sb.network;
|
|
175
|
+
if (Array.isArray(sb.read_deny))
|
|
176
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
177
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
178
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
179
|
+
}
|
|
180
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
181
|
+
const iso = parsed.isolation;
|
|
182
|
+
if (iso.default_mode === "worktree")
|
|
183
|
+
base.isolation.default_mode = iso.default_mode;
|
|
184
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
185
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
186
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
187
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
188
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
189
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
190
|
+
}
|
|
191
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
192
|
+
const comp = parsed.completion;
|
|
193
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
194
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
195
|
+
if (Array.isArray(comp.checks))
|
|
196
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
197
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
198
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
199
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
200
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
201
|
+
}
|
|
202
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
203
|
+
const runtimeImage = parsed.runtime_image;
|
|
204
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
205
|
+
const deps = runtimeImage.deps;
|
|
206
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
207
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
208
|
+
}
|
|
209
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
210
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
214
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
218
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
219
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
220
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return base;
|
|
224
|
+
}
|
|
225
|
+
function isValidMode(value) {
|
|
226
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
227
|
+
}
|
|
228
|
+
function isValidRule(value) {
|
|
229
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
230
|
+
return false;
|
|
231
|
+
const r = value;
|
|
232
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
233
|
+
}
|
|
234
|
+
function migrateLegacyDeny(deny) {
|
|
235
|
+
const rules = [];
|
|
236
|
+
for (const entry of deny) {
|
|
237
|
+
if (typeof entry.id !== "string")
|
|
238
|
+
continue;
|
|
239
|
+
const match = {};
|
|
240
|
+
if (typeof entry.pattern === "string")
|
|
241
|
+
match.pattern = entry.pattern;
|
|
242
|
+
if (typeof entry.regex === "string")
|
|
243
|
+
match.regex = entry.regex;
|
|
244
|
+
if (!match.pattern && !match.regex)
|
|
245
|
+
continue;
|
|
246
|
+
rules.push({
|
|
247
|
+
id: entry.id,
|
|
248
|
+
category: "command",
|
|
249
|
+
match,
|
|
250
|
+
action: "block",
|
|
251
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return rules;
|
|
255
|
+
}
|
|
256
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
257
|
+
return rules.map((rule) => {
|
|
258
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
259
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
260
|
+
return {
|
|
261
|
+
...rule,
|
|
262
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
263
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function getRegexUnsafeReason(pattern) {
|
|
268
|
+
if (pattern.length > 512) {
|
|
269
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
270
|
+
}
|
|
271
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
272
|
+
return "pattern uses backreferences";
|
|
273
|
+
}
|
|
274
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
275
|
+
return "pattern contains nested quantifiers";
|
|
276
|
+
}
|
|
277
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
278
|
+
return "pattern contains nested broad quantifiers";
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
283
|
+
const cached = compiledRegexCache.get(pattern);
|
|
284
|
+
if (cached !== undefined) {
|
|
285
|
+
return cached ?? undefined;
|
|
286
|
+
}
|
|
287
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
288
|
+
if (unsafeReason) {
|
|
289
|
+
if (logOnFailure) {
|
|
290
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
291
|
+
}
|
|
292
|
+
compiledRegexCache.set(pattern, null);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
const compiled = new RegExp(pattern);
|
|
297
|
+
compiledRegexCache.set(pattern, compiled);
|
|
298
|
+
return compiled;
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (logOnFailure) {
|
|
301
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
302
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
303
|
+
}
|
|
304
|
+
compiledRegexCache.set(pattern, null);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function matchRule(rule, input) {
|
|
309
|
+
const { match } = rule;
|
|
310
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
if (match.regex) {
|
|
314
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
315
|
+
if (!compiled) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
return compiled.test(input);
|
|
320
|
+
} catch {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
327
|
+
if (!rule.unless)
|
|
328
|
+
return false;
|
|
329
|
+
if (rule.unless.regex) {
|
|
330
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
331
|
+
if (!compiled) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
if (compiled.test(command))
|
|
336
|
+
return true;
|
|
337
|
+
} catch {}
|
|
338
|
+
}
|
|
339
|
+
if (rule.unless.task_in && taskId) {
|
|
340
|
+
if (rule.unless.task_in.includes(taskId))
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
function resolveAction(mode, matched) {
|
|
346
|
+
if (matched.length === 0)
|
|
347
|
+
return "allow";
|
|
348
|
+
if (mode === "off")
|
|
349
|
+
return "allow";
|
|
350
|
+
if (mode === "observe")
|
|
351
|
+
return "warn";
|
|
352
|
+
return "block";
|
|
353
|
+
}
|
|
354
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
355
|
+
if (rawPath.startsWith("/"))
|
|
356
|
+
return resolve(rawPath);
|
|
357
|
+
return resolve(projectRoot, rawPath);
|
|
358
|
+
}
|
|
359
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
360
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
361
|
+
const managedRoots = [
|
|
362
|
+
resolve(projectRoot, "rig"),
|
|
363
|
+
resolve(projectRoot, ".rig"),
|
|
364
|
+
resolve(projectRoot, "artifacts")
|
|
365
|
+
];
|
|
366
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
367
|
+
}
|
|
368
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
369
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
370
|
+
if (taskWorkspace) {
|
|
371
|
+
const workspaceRigRoot = resolve(taskWorkspace, ".rig");
|
|
372
|
+
const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
|
|
373
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
|
|
378
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
379
|
+
}
|
|
380
|
+
function isTestFile(path) {
|
|
381
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
382
|
+
}
|
|
383
|
+
function evaluate(context) {
|
|
384
|
+
const policy = loadPolicy(context.projectRoot);
|
|
385
|
+
switch (context.evaluation.type) {
|
|
386
|
+
case "tool-call":
|
|
387
|
+
return evaluateToolCall(policy, context);
|
|
388
|
+
case "command":
|
|
389
|
+
return evaluateCommand(policy, context);
|
|
390
|
+
case "content-write":
|
|
391
|
+
return evaluateContent(policy, context);
|
|
392
|
+
case "file-access":
|
|
393
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
397
|
+
const allowed = () => ({
|
|
398
|
+
allowed: true,
|
|
399
|
+
matchedRules: [],
|
|
400
|
+
action: "allow",
|
|
401
|
+
failClosed: false
|
|
402
|
+
});
|
|
403
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
404
|
+
return allowed();
|
|
405
|
+
}
|
|
406
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
407
|
+
return allowed();
|
|
408
|
+
}
|
|
409
|
+
if (!context.taskId) {
|
|
410
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
411
|
+
return {
|
|
412
|
+
allowed: false,
|
|
413
|
+
matchedRules: [],
|
|
414
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
415
|
+
failClosed: true
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return allowed();
|
|
419
|
+
}
|
|
420
|
+
const scopes = context.taskScopes || [];
|
|
421
|
+
if (scopes.length === 0) {
|
|
422
|
+
return allowed();
|
|
423
|
+
}
|
|
424
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
425
|
+
const absPath = resolve(filePath);
|
|
426
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
427
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
428
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
429
|
+
return {
|
|
430
|
+
allowed: policy.mode !== "enforce",
|
|
431
|
+
matchedRules: matched2,
|
|
432
|
+
action: resolveAction(policy.mode, matched2),
|
|
433
|
+
failClosed: false
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
438
|
+
let normalizedPath = filePath;
|
|
439
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
440
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
441
|
+
}
|
|
442
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
443
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
444
|
+
return allowed();
|
|
445
|
+
}
|
|
446
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
447
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
448
|
+
return {
|
|
449
|
+
allowed: policy.mode !== "enforce",
|
|
450
|
+
matchedRules: matched,
|
|
451
|
+
action: resolveAction(policy.mode, matched),
|
|
452
|
+
failClosed: false
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function evaluateCommand(policy, context) {
|
|
456
|
+
const evaluation = context.evaluation;
|
|
457
|
+
if (evaluation.type !== "command") {
|
|
458
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
459
|
+
}
|
|
460
|
+
const command = evaluation.command;
|
|
461
|
+
const matchedRules = [];
|
|
462
|
+
for (const rule of policy.rules) {
|
|
463
|
+
if (rule.category !== "command")
|
|
464
|
+
continue;
|
|
465
|
+
if (!matchRule(rule, command))
|
|
466
|
+
continue;
|
|
467
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
468
|
+
continue;
|
|
469
|
+
matchedRules.push({
|
|
470
|
+
id: rule.id,
|
|
471
|
+
category: rule.category,
|
|
472
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
473
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const writeTarget = extractWriteTarget(command);
|
|
477
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
478
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
479
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
480
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
484
|
+
return {
|
|
485
|
+
allowed: action !== "block",
|
|
486
|
+
matchedRules,
|
|
487
|
+
action,
|
|
488
|
+
failClosed: false
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function extractWriteTarget(command) {
|
|
492
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
493
|
+
if (redirect?.[1])
|
|
494
|
+
return redirect[1];
|
|
495
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
496
|
+
if (tee?.[2])
|
|
497
|
+
return tee[2];
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
function evaluateContent(policy, context) {
|
|
501
|
+
const evaluation = context.evaluation;
|
|
502
|
+
if (evaluation.type !== "content-write") {
|
|
503
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
504
|
+
}
|
|
505
|
+
const { content, file_path } = evaluation;
|
|
506
|
+
const matchedRules = [];
|
|
507
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
508
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
509
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
510
|
+
}
|
|
511
|
+
for (const rule of policy.rules) {
|
|
512
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
513
|
+
continue;
|
|
514
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
515
|
+
continue;
|
|
516
|
+
if (!matchRule(rule, content))
|
|
517
|
+
continue;
|
|
518
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
519
|
+
continue;
|
|
520
|
+
matchedRules.push({
|
|
521
|
+
id: rule.id,
|
|
522
|
+
category: rule.category,
|
|
523
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
524
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
528
|
+
return {
|
|
529
|
+
allowed: action !== "block",
|
|
530
|
+
matchedRules,
|
|
531
|
+
action,
|
|
532
|
+
failClosed: false
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function evaluateToolCall(policy, context) {
|
|
536
|
+
const evaluation = context.evaluation;
|
|
537
|
+
if (evaluation.type !== "tool-call") {
|
|
538
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
539
|
+
}
|
|
540
|
+
const { tool_name, tool_input } = evaluation;
|
|
541
|
+
const allMatched = [];
|
|
542
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
543
|
+
for (const fp of filePaths) {
|
|
544
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
545
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
546
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
547
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const content = extractContentFromToolInput(tool_input);
|
|
551
|
+
if (content) {
|
|
552
|
+
const filePath = filePaths[0] || "";
|
|
553
|
+
const contentContext = {
|
|
554
|
+
...context,
|
|
555
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
556
|
+
};
|
|
557
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
558
|
+
for (const rule of contentPolicy.rules) {
|
|
559
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
560
|
+
continue;
|
|
561
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
562
|
+
continue;
|
|
563
|
+
if (!matchRule(rule, content))
|
|
564
|
+
continue;
|
|
565
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
566
|
+
continue;
|
|
567
|
+
allMatched.push({
|
|
568
|
+
id: rule.id,
|
|
569
|
+
category: rule.category,
|
|
570
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
571
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (tool_name === "Bash") {
|
|
576
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
577
|
+
if (command) {
|
|
578
|
+
const cmdContext = {
|
|
579
|
+
...context,
|
|
580
|
+
evaluation: { type: "command", command }
|
|
581
|
+
};
|
|
582
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
583
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
584
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const seen = new Set;
|
|
589
|
+
const deduplicated = [];
|
|
590
|
+
for (const rule of allMatched) {
|
|
591
|
+
if (!seen.has(rule.id)) {
|
|
592
|
+
seen.add(rule.id);
|
|
593
|
+
deduplicated.push(rule);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
597
|
+
return {
|
|
598
|
+
allowed: action !== "block",
|
|
599
|
+
matchedRules: deduplicated,
|
|
600
|
+
action,
|
|
601
|
+
failClosed: false
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function isWriteTool(toolName) {
|
|
605
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
606
|
+
}
|
|
607
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
608
|
+
const paths = [];
|
|
609
|
+
const add = (value) => {
|
|
610
|
+
if (typeof value === "string" && value.trim()) {
|
|
611
|
+
paths.push(value.trim());
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
615
|
+
add(input.file_path);
|
|
616
|
+
add(input.path);
|
|
617
|
+
} else if (toolName === "Glob") {
|
|
618
|
+
add(input.path);
|
|
619
|
+
} else if (toolName === "Grep") {
|
|
620
|
+
add(input.path);
|
|
621
|
+
} else {
|
|
622
|
+
add(input.file_path);
|
|
623
|
+
add(input.path);
|
|
624
|
+
}
|
|
625
|
+
return paths;
|
|
626
|
+
}
|
|
627
|
+
function extractContentFromToolInput(input) {
|
|
628
|
+
if (typeof input.content === "string")
|
|
629
|
+
return input.content;
|
|
630
|
+
if (typeof input.new_string === "string")
|
|
631
|
+
return input.new_string;
|
|
632
|
+
return "";
|
|
633
|
+
}
|
|
634
|
+
var guardHotPathPrimed = false;
|
|
635
|
+
function primeGuardHotPaths() {
|
|
636
|
+
if (guardHotPathPrimed) {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
guardHotPathPrimed = true;
|
|
640
|
+
try {
|
|
641
|
+
optimizeNextInvocation(matchRule);
|
|
642
|
+
optimizeNextInvocation(evaluate);
|
|
643
|
+
} catch {}
|
|
644
|
+
}
|
|
645
|
+
primeGuardHotPaths();
|
|
646
|
+
|
|
647
|
+
// packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
|
|
648
|
+
import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
|
|
649
|
+
|
|
650
|
+
// packages/cli-surface-plugin/src/runner.ts
|
|
8
651
|
class CliError extends RuntimeCliError {
|
|
9
|
-
hint;
|
|
10
652
|
constructor(message, exitCode = 1, options = {}) {
|
|
11
|
-
super(message, exitCode);
|
|
12
|
-
if (options.hint?.trim()) {
|
|
13
|
-
this.hint = options.hint.trim();
|
|
14
|
-
}
|
|
653
|
+
super(message, exitCode, options);
|
|
15
654
|
}
|
|
16
655
|
}
|
|
17
656
|
function takeFlag(args, flag) {
|
|
@@ -64,18 +703,446 @@ Usage: ${usage}`);
|
|
|
64
703
|
import {
|
|
65
704
|
doctorAuthorityRemoteEndpoints as doctorManagedRemoteEndpoints,
|
|
66
705
|
importLegacyRemoteEndpoints as migrateManagedRemoteEndpoints,
|
|
67
|
-
listManagedRemoteEndpoints,
|
|
706
|
+
listManagedRemoteEndpoints as listManagedRemoteEndpoints2,
|
|
707
|
+
RemoteCliError as RemoteCliError3,
|
|
708
|
+
removeManagedRemoteEndpoint as removeManagedRemoteEndpoint2,
|
|
709
|
+
updateAuthorityRemoteEndpoint as updateManagedRemoteEndpointInAuthority,
|
|
710
|
+
upsertManagedRemoteEndpoint as upsertManagedRemoteEndpoint2
|
|
711
|
+
} from "@rig/core/remote-config";
|
|
712
|
+
|
|
713
|
+
// packages/cli-surface-plugin/src/commands/remote-client.ts
|
|
714
|
+
import { dirname } from "path";
|
|
715
|
+
import { resolveAuthorityPaths } from "@rig/core/server-paths";
|
|
716
|
+
import {
|
|
717
|
+
DEFAULT_REMOTE_PORT,
|
|
718
|
+
REMOTES_CONFIG_PATH,
|
|
719
|
+
doctorAuthorityRemoteEndpoints,
|
|
720
|
+
importLegacyRemoteEndpoints,
|
|
721
|
+
listAuthorityRemoteEndpoints,
|
|
722
|
+
loadRemotesConfig,
|
|
723
|
+
markAuthorityRemoteEndpointConnected,
|
|
724
|
+
normalizeString,
|
|
68
725
|
RemoteCliError,
|
|
726
|
+
updateAuthorityRemoteEndpoint
|
|
727
|
+
} from "@rig/core/remote-config";
|
|
728
|
+
import {
|
|
729
|
+
DEFAULT_REMOTE_PORT as DEFAULT_REMOTE_PORT2,
|
|
730
|
+
REMOTES_CONFIG_PATH as REMOTES_CONFIG_PATH2,
|
|
731
|
+
listManagedRemoteEndpoints,
|
|
732
|
+
loadRemotesConfig as loadRemotesConfig2,
|
|
733
|
+
RemoteCliError as RemoteCliError2,
|
|
69
734
|
removeManagedRemoteEndpoint,
|
|
70
|
-
|
|
735
|
+
resolveOwnerNamespaceKey,
|
|
736
|
+
resolveRegistryBaseUrl as resolveRegistryBaseUrl2,
|
|
737
|
+
resolveRelayUrl as resolveRelayUrl2,
|
|
738
|
+
resolveSelectedRemote,
|
|
739
|
+
resolveSshTarget,
|
|
71
740
|
upsertManagedRemoteEndpoint
|
|
72
|
-
} from "@rig/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
741
|
+
} from "@rig/core/remote-config";
|
|
742
|
+
var DEFAULT_TIMEOUTS = {
|
|
743
|
+
connectMs: 5000,
|
|
744
|
+
authMs: 5000,
|
|
745
|
+
requestMs: 30000,
|
|
746
|
+
subscriptionMs: 1e4
|
|
747
|
+
};
|
|
748
|
+
function resolveRemoteEndpoint(options) {
|
|
749
|
+
const env = options.env ?? process.env;
|
|
750
|
+
const envHost = normalizeString(env.RIG_REMOTE_HOST);
|
|
751
|
+
const envToken = normalizeString(env.RIG_REMOTE_TOKEN);
|
|
752
|
+
const envPort = parsePort(normalizeString(env.RIG_REMOTE_PORT), "RIG_REMOTE_PORT");
|
|
753
|
+
const envAlias = normalizeString(env.RIG_REMOTE_ALIAS);
|
|
754
|
+
const explicitHost = normalizeString(options.host);
|
|
755
|
+
const explicitToken = normalizeString(options.token);
|
|
756
|
+
const explicitPort = parsePort(normalizeString(options.port), "--port");
|
|
757
|
+
const explicitProvided = Boolean(explicitHost || explicitToken || explicitPort !== null);
|
|
758
|
+
const requestedAlias = normalizeString(options.remoteAlias) || envAlias;
|
|
759
|
+
let endpoint = {
|
|
760
|
+
source: "env",
|
|
761
|
+
host: envHost || "",
|
|
762
|
+
port: envPort ?? DEFAULT_REMOTE_PORT,
|
|
763
|
+
token: envToken || "",
|
|
764
|
+
configPath: REMOTES_CONFIG_PATH
|
|
765
|
+
};
|
|
766
|
+
if (requestedAlias) {
|
|
767
|
+
if (options.projectRoot) {
|
|
768
|
+
const remote = listAuthorityRemoteEndpoints(options.projectRoot).find((entry) => entry.alias === requestedAlias);
|
|
769
|
+
if (!remote) {
|
|
770
|
+
const configPath = resolveAuthorityPaths(options.projectRoot).remoteEndpointsPath;
|
|
771
|
+
throw new RemoteCliError("RIG_REMOTE_ALIAS_NOT_FOUND", `Remote alias '${requestedAlias}' was not found in ${configPath}.`, 2, { alias: requestedAlias, configPath });
|
|
772
|
+
}
|
|
773
|
+
endpoint = {
|
|
774
|
+
source: "alias",
|
|
775
|
+
alias: requestedAlias,
|
|
776
|
+
host: remote.host,
|
|
777
|
+
port: remote.port,
|
|
778
|
+
token: remote.token,
|
|
779
|
+
configPath: resolveAuthorityPaths(options.projectRoot).remoteEndpointsPath
|
|
780
|
+
};
|
|
781
|
+
} else {
|
|
782
|
+
const config = loadRemotesConfig();
|
|
783
|
+
const remote = config.remotes?.[requestedAlias];
|
|
784
|
+
if (!remote) {
|
|
785
|
+
throw new RemoteCliError("RIG_REMOTE_ALIAS_NOT_FOUND", `Remote alias '${requestedAlias}' was not found in ${REMOTES_CONFIG_PATH}.`, 2, { alias: requestedAlias, configPath: REMOTES_CONFIG_PATH });
|
|
786
|
+
}
|
|
787
|
+
endpoint = {
|
|
788
|
+
source: "alias",
|
|
789
|
+
alias: requestedAlias,
|
|
790
|
+
host: normalizeString(remote.host) || "",
|
|
791
|
+
port: Number.isFinite(remote.port) ? Number(remote.port) : DEFAULT_REMOTE_PORT,
|
|
792
|
+
token: normalizeString(remote.token) || "",
|
|
793
|
+
configPath: REMOTES_CONFIG_PATH
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (explicitProvided) {
|
|
798
|
+
endpoint = {
|
|
799
|
+
...endpoint,
|
|
800
|
+
source: "flags",
|
|
801
|
+
host: explicitHost || endpoint.host,
|
|
802
|
+
port: explicitPort ?? endpoint.port,
|
|
803
|
+
token: explicitToken || endpoint.token
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
if (!endpoint.host) {
|
|
807
|
+
throw new RemoteCliError("RIG_REMOTE_HOST_REQUIRED", "Remote host is required. Pass --host, --remote <alias>, or set RIG_REMOTE_HOST.", 2);
|
|
808
|
+
}
|
|
809
|
+
if (!Number.isFinite(endpoint.port) || endpoint.port <= 0 || endpoint.port > 65535) {
|
|
810
|
+
throw new RemoteCliError("RIG_REMOTE_INVALID_PORT", `Invalid remote port: ${endpoint.port}.`, 2);
|
|
811
|
+
}
|
|
812
|
+
return endpoint;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
class RemoteWsClient {
|
|
816
|
+
endpoint;
|
|
817
|
+
ws = null;
|
|
818
|
+
connected = false;
|
|
819
|
+
pending = new Map;
|
|
820
|
+
onEvent;
|
|
821
|
+
requestTimeoutMs;
|
|
822
|
+
connectTimeoutMs;
|
|
823
|
+
authTimeoutMs;
|
|
824
|
+
subscriptionTimeoutMs;
|
|
825
|
+
connectionToken = null;
|
|
826
|
+
connectionTokenExpiresAt = null;
|
|
827
|
+
tokenRefreshTimer = null;
|
|
828
|
+
constructor(endpoint, options = {}) {
|
|
829
|
+
this.endpoint = endpoint;
|
|
830
|
+
this.onEvent = options.onEvent;
|
|
831
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_TIMEOUTS.connectMs;
|
|
832
|
+
this.authTimeoutMs = options.authTimeoutMs ?? DEFAULT_TIMEOUTS.authMs;
|
|
833
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUTS.requestMs;
|
|
834
|
+
this.subscriptionTimeoutMs = options.subscriptionTimeoutMs ?? DEFAULT_TIMEOUTS.subscriptionMs;
|
|
835
|
+
}
|
|
836
|
+
setEventHandler(handler) {
|
|
837
|
+
this.onEvent = handler;
|
|
838
|
+
}
|
|
839
|
+
async connect() {
|
|
840
|
+
if (this.connected) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const url = `ws://${this.endpoint.host}:${this.endpoint.port}`;
|
|
844
|
+
await new Promise((resolve2, reject) => {
|
|
845
|
+
let authResolved = false;
|
|
846
|
+
let connectTimer = setTimeout(() => {
|
|
847
|
+
connectTimer = null;
|
|
848
|
+
reject(new RemoteCliError("RIG_REMOTE_CONNECT_TIMEOUT", `Timed out connecting to ${url} after ${this.connectTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.connectTimeoutMs }));
|
|
849
|
+
}, this.connectTimeoutMs);
|
|
850
|
+
const ws = new WebSocket(url);
|
|
851
|
+
this.ws = ws;
|
|
852
|
+
ws.onopen = () => {
|
|
853
|
+
const authMessage = {
|
|
854
|
+
type: "auth",
|
|
855
|
+
id: crypto.randomUUID(),
|
|
856
|
+
timestamp: new Date().toISOString(),
|
|
857
|
+
token: this.connectionToken || this.endpoint.token,
|
|
858
|
+
tokenType: this.connectionToken ? "connection" : "server"
|
|
859
|
+
};
|
|
860
|
+
let authTimer = setTimeout(() => {
|
|
861
|
+
authTimer = null;
|
|
862
|
+
reject(new RemoteCliError("RIG_REMOTE_AUTH_TIMEOUT", `Timed out waiting for auth response from ${url} after ${this.authTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.authTimeoutMs }));
|
|
863
|
+
}, this.authTimeoutMs);
|
|
864
|
+
ws.send(JSON.stringify(authMessage));
|
|
865
|
+
const originalOnMessage = ws.onmessage;
|
|
866
|
+
ws.onmessage = (event) => {
|
|
867
|
+
const message = parseIncomingMessage(event.data);
|
|
868
|
+
if (!message) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (message.type === "auth_response" && !authResolved) {
|
|
872
|
+
authResolved = true;
|
|
873
|
+
if (connectTimer) {
|
|
874
|
+
clearTimeout(connectTimer);
|
|
875
|
+
connectTimer = null;
|
|
876
|
+
}
|
|
877
|
+
if (authTimer) {
|
|
878
|
+
clearTimeout(authTimer);
|
|
879
|
+
authTimer = null;
|
|
880
|
+
}
|
|
881
|
+
const auth = message;
|
|
882
|
+
if (!auth.success) {
|
|
883
|
+
reject(new RemoteCliError("RIG_REMOTE_AUTH_FAILED", auth.error || "Remote authentication failed.", 3, { host: this.endpoint.host, port: this.endpoint.port }));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (auth.connectionToken && auth.connectionTokenExpiresAt) {
|
|
887
|
+
this.connectionToken = auth.connectionToken;
|
|
888
|
+
this.connectionTokenExpiresAt = auth.connectionTokenExpiresAt;
|
|
889
|
+
this.scheduleTokenRefresh();
|
|
890
|
+
}
|
|
891
|
+
if (this.endpoint.configPath.endsWith("endpoints.toml")) {
|
|
892
|
+
const projectRoot = dirname(dirname(dirname(this.endpoint.configPath)));
|
|
893
|
+
try {
|
|
894
|
+
markAuthorityRemoteEndpointConnected(projectRoot, this.endpoint.alias ? listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.alias === this.endpoint.alias)?.id ?? "" : "");
|
|
895
|
+
} catch {}
|
|
896
|
+
}
|
|
897
|
+
this.connected = true;
|
|
898
|
+
ws.onmessage = (event2) => {
|
|
899
|
+
const next = parseIncomingMessage(event2.data);
|
|
900
|
+
if (!next) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
this.routeMessage(next);
|
|
904
|
+
};
|
|
905
|
+
resolve2();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (typeof originalOnMessage === "function") {
|
|
909
|
+
originalOnMessage.call(ws, event);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
};
|
|
913
|
+
ws.onerror = () => {
|
|
914
|
+
if (connectTimer) {
|
|
915
|
+
clearTimeout(connectTimer);
|
|
916
|
+
connectTimer = null;
|
|
917
|
+
}
|
|
918
|
+
if (!authResolved) {
|
|
919
|
+
reject(new RemoteCliError("RIG_REMOTE_CONNECT_FAILED", `Failed to connect to ${url}.`, 3, { host: this.endpoint.host, port: this.endpoint.port }));
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
ws.onclose = () => {
|
|
923
|
+
this.connected = false;
|
|
924
|
+
this.clearPending("Connection closed");
|
|
925
|
+
};
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
disconnect() {
|
|
929
|
+
this.connected = false;
|
|
930
|
+
this.clearTokenRefreshTimer();
|
|
931
|
+
this.clearPending("Connection closed");
|
|
932
|
+
if (this.ws) {
|
|
933
|
+
try {
|
|
934
|
+
this.ws.close();
|
|
935
|
+
} catch {}
|
|
936
|
+
this.ws = null;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
async subscribe(eventTypes = []) {
|
|
940
|
+
const message = { type: "subscribe" };
|
|
941
|
+
if (eventTypes.length > 0) {
|
|
942
|
+
message.eventTypes = eventTypes;
|
|
943
|
+
}
|
|
944
|
+
const response = await this.request(message, this.subscriptionTimeoutMs);
|
|
945
|
+
return expectType(response, "operation_result");
|
|
946
|
+
}
|
|
947
|
+
async unsubscribe() {
|
|
948
|
+
return expectType(await this.request({ type: "unsubscribe" }), "operation_result");
|
|
949
|
+
}
|
|
950
|
+
async getState() {
|
|
951
|
+
return expectType(await this.request({ type: "get_state" }), "state_response");
|
|
952
|
+
}
|
|
953
|
+
async getTasks() {
|
|
954
|
+
return expectType(await this.request({ type: "get_tasks" }), "tasks_response");
|
|
955
|
+
}
|
|
956
|
+
async pause() {
|
|
957
|
+
return expectType(await this.request({ type: "pause" }), "operation_result");
|
|
958
|
+
}
|
|
959
|
+
async resume() {
|
|
960
|
+
return expectType(await this.request({ type: "resume" }), "operation_result");
|
|
961
|
+
}
|
|
962
|
+
async interrupt() {
|
|
963
|
+
return expectType(await this.request({ type: "interrupt" }), "operation_result");
|
|
964
|
+
}
|
|
965
|
+
async continueExecution() {
|
|
966
|
+
return expectType(await this.request({ type: "continue" }), "operation_result");
|
|
967
|
+
}
|
|
968
|
+
async refreshTasks() {
|
|
969
|
+
return expectType(await this.request({ type: "refresh_tasks" }), "operation_result");
|
|
970
|
+
}
|
|
971
|
+
async addIterations(count) {
|
|
972
|
+
return expectType(await this.request({ type: "add_iterations", count }), "operation_result");
|
|
973
|
+
}
|
|
974
|
+
async removeIterations(count) {
|
|
975
|
+
return expectType(await this.request({ type: "remove_iterations", count }), "operation_result");
|
|
976
|
+
}
|
|
977
|
+
async getPromptPreview(taskId) {
|
|
978
|
+
return expectType(await this.request({ type: "get_prompt_preview", taskId }), "prompt_preview_response");
|
|
979
|
+
}
|
|
980
|
+
async getIterationOutput(taskId) {
|
|
981
|
+
return expectType(await this.request({ type: "get_iteration_output", taskId }), "iteration_output_response");
|
|
982
|
+
}
|
|
983
|
+
async startOrchestration(options) {
|
|
984
|
+
return expectType(await this.request({
|
|
985
|
+
type: "orchestrate:start",
|
|
986
|
+
maxWorkers: options.maxWorkers,
|
|
987
|
+
maxIterations: options.maxIterations,
|
|
988
|
+
directMerge: options.directMerge
|
|
989
|
+
}), "orchestrate:start_response");
|
|
990
|
+
}
|
|
991
|
+
async pauseOrchestration(orchestrationId) {
|
|
992
|
+
return expectType(await this.request({ type: "orchestrate:pause", orchestrationId }), "operation_result");
|
|
993
|
+
}
|
|
994
|
+
async resumeOrchestration(orchestrationId) {
|
|
995
|
+
return expectType(await this.request({ type: "orchestrate:resume", orchestrationId }), "operation_result");
|
|
996
|
+
}
|
|
997
|
+
async stopOrchestration(orchestrationId) {
|
|
998
|
+
return expectType(await this.request({ type: "orchestrate:stop", orchestrationId }), "operation_result");
|
|
999
|
+
}
|
|
1000
|
+
async getOrchestrationState(orchestrationId) {
|
|
1001
|
+
return expectType(await this.request({ type: "orchestrate:get_state", orchestrationId }), "orchestrate:state_response");
|
|
1002
|
+
}
|
|
1003
|
+
async ping() {
|
|
1004
|
+
return this.request({ type: "ping" });
|
|
1005
|
+
}
|
|
1006
|
+
async request(payload, timeoutMs = this.requestTimeoutMs) {
|
|
1007
|
+
if (!this.connected || !this.ws) {
|
|
1008
|
+
throw new RemoteCliError("RIG_REMOTE_NOT_CONNECTED", "Remote client is not connected.", 3);
|
|
1009
|
+
}
|
|
1010
|
+
const id = crypto.randomUUID();
|
|
1011
|
+
const message = {
|
|
1012
|
+
...payload,
|
|
1013
|
+
id,
|
|
1014
|
+
timestamp: new Date().toISOString()
|
|
1015
|
+
};
|
|
1016
|
+
return await new Promise((resolve2, reject) => {
|
|
1017
|
+
const timeout = setTimeout(() => {
|
|
1018
|
+
this.pending.delete(id);
|
|
1019
|
+
reject(new RemoteCliError("RIG_REMOTE_REQUEST_TIMEOUT", `Timed out waiting for response to '${String(payload.type)}' after ${timeoutMs}ms.`, 3, { requestType: payload.type, requestId: id, timeoutMs }));
|
|
1020
|
+
}, timeoutMs);
|
|
1021
|
+
this.pending.set(id, { resolve: resolve2, reject, timeout });
|
|
1022
|
+
this.ws?.send(JSON.stringify(message));
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
routeMessage(message) {
|
|
1026
|
+
if (message.id && this.pending.has(message.id)) {
|
|
1027
|
+
const pending = this.pending.get(message.id);
|
|
1028
|
+
if (pending) {
|
|
1029
|
+
clearTimeout(pending.timeout);
|
|
1030
|
+
this.pending.delete(message.id);
|
|
1031
|
+
pending.resolve(message);
|
|
1032
|
+
}
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
if (message.type === "token_refresh_response") {
|
|
1036
|
+
const token = normalizeString(message.connectionToken);
|
|
1037
|
+
const expiresAt = normalizeString(message.connectionTokenExpiresAt);
|
|
1038
|
+
if (token && expiresAt) {
|
|
1039
|
+
this.connectionToken = token;
|
|
1040
|
+
this.connectionTokenExpiresAt = expiresAt;
|
|
1041
|
+
this.scheduleTokenRefresh();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
this.onEvent?.({ receivedAt: new Date().toISOString(), message });
|
|
1045
|
+
}
|
|
1046
|
+
scheduleTokenRefresh() {
|
|
1047
|
+
this.clearTokenRefreshTimer();
|
|
1048
|
+
if (!this.connectionToken || !this.connectionTokenExpiresAt || !this.ws || !this.connected) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const expiresAt = new Date(this.connectionTokenExpiresAt).getTime();
|
|
1052
|
+
if (!Number.isFinite(expiresAt)) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const refreshAt = expiresAt - 60 * 60 * 1000;
|
|
1056
|
+
const delay = Math.max(0, refreshAt - Date.now());
|
|
1057
|
+
this.tokenRefreshTimer = setTimeout(() => {
|
|
1058
|
+
if (!this.connected || !this.ws || !this.connectionToken) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const refreshMessage = {
|
|
1062
|
+
type: "token_refresh",
|
|
1063
|
+
id: crypto.randomUUID(),
|
|
1064
|
+
timestamp: new Date().toISOString(),
|
|
1065
|
+
connectionToken: this.connectionToken
|
|
1066
|
+
};
|
|
1067
|
+
this.ws.send(JSON.stringify(refreshMessage));
|
|
1068
|
+
}, delay);
|
|
1069
|
+
}
|
|
1070
|
+
clearTokenRefreshTimer() {
|
|
1071
|
+
if (this.tokenRefreshTimer) {
|
|
1072
|
+
clearTimeout(this.tokenRefreshTimer);
|
|
1073
|
+
this.tokenRefreshTimer = null;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
clearPending(reason) {
|
|
1077
|
+
for (const [requestId, pending] of this.pending.entries()) {
|
|
1078
|
+
clearTimeout(pending.timeout);
|
|
1079
|
+
pending.reject(new RemoteCliError("RIG_REMOTE_REQUEST_CANCELLED", `${reason} (request ${requestId}).`, 3, { requestId }));
|
|
1080
|
+
this.pending.delete(requestId);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function matchesEventFilter(message, expectedType) {
|
|
1085
|
+
if (!expectedType) {
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
if (message.type === expectedType) {
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1091
|
+
if (message.type === "engine_event") {
|
|
1092
|
+
const eventType = normalizeString(message.event?.type);
|
|
1093
|
+
return eventType === expectedType;
|
|
1094
|
+
}
|
|
1095
|
+
if (message.type === "parallel_event") {
|
|
1096
|
+
const eventType = normalizeString(message.event?.type);
|
|
1097
|
+
return eventType === expectedType;
|
|
1098
|
+
}
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
function summarizeMessage(message) {
|
|
1102
|
+
if (message.type === "pong") {
|
|
1103
|
+
return "pong";
|
|
1104
|
+
}
|
|
1105
|
+
if (message.type === "engine_event") {
|
|
1106
|
+
const eventType = normalizeString(message.event?.type);
|
|
1107
|
+
return eventType ? `engine_event:${eventType}` : "engine_event";
|
|
1108
|
+
}
|
|
1109
|
+
if (message.type === "parallel_event") {
|
|
1110
|
+
const eventType = normalizeString(message.event?.type);
|
|
1111
|
+
const orchestrationId = normalizeString(message.orchestrationId);
|
|
1112
|
+
return orchestrationId ? `parallel_event:${eventType || "unknown"}:${orchestrationId}` : `parallel_event:${eventType || "unknown"}`;
|
|
1113
|
+
}
|
|
1114
|
+
return message.type;
|
|
1115
|
+
}
|
|
1116
|
+
function parsePort(value, label) {
|
|
1117
|
+
if (!value) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
const parsed = Number.parseInt(value, 10);
|
|
1121
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65535) {
|
|
1122
|
+
throw new RemoteCliError("RIG_REMOTE_INVALID_PORT", `Invalid port for ${label}: ${value}.`, 2, { label, value });
|
|
1123
|
+
}
|
|
1124
|
+
return parsed;
|
|
1125
|
+
}
|
|
1126
|
+
function parseIncomingMessage(raw) {
|
|
1127
|
+
if (typeof raw !== "string") {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
try {
|
|
1131
|
+
const parsed = JSON.parse(raw);
|
|
1132
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
return parsed;
|
|
1136
|
+
} catch {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function expectType(message, expectedType) {
|
|
1141
|
+
if (message.type !== expectedType) {
|
|
1142
|
+
throw new RemoteCliError("RIG_REMOTE_UNEXPECTED_RESPONSE", `Unexpected response type: ${message.type}. Expected ${expectedType}.`, 3, { expectedType, actualType: message.type, message });
|
|
1143
|
+
}
|
|
1144
|
+
return message;
|
|
1145
|
+
}
|
|
79
1146
|
|
|
80
1147
|
// packages/cli-surface-plugin/src/commands/_parsers.ts
|
|
81
1148
|
function parseOptionalPositiveInt(value, option) {
|
|
@@ -106,7 +1173,7 @@ function assertRemoteOperationSuccess(command, response) {
|
|
|
106
1173
|
}
|
|
107
1174
|
if (response.success === false) {
|
|
108
1175
|
const error = typeof response.error === "string" && response.error || `${command} failed without explicit error detail from remote server.`;
|
|
109
|
-
throw new
|
|
1176
|
+
throw new RemoteCliError3("RIG_REMOTE_OPERATION_FAILED", error, 3, { command, response: redactSecretFields(response) });
|
|
110
1177
|
}
|
|
111
1178
|
}
|
|
112
1179
|
function redactRemoteEndpoint(endpoint) {
|
|
@@ -144,7 +1211,7 @@ async function executeRemote(context, args) {
|
|
|
144
1211
|
switch (subcommand) {
|
|
145
1212
|
case "list": {
|
|
146
1213
|
requireNoExtraArgs(subRest, "rig remote endpoint list");
|
|
147
|
-
const endpoints =
|
|
1214
|
+
const endpoints = listManagedRemoteEndpoints2(undefined, context.projectRoot);
|
|
148
1215
|
const redactedEndpoints = endpoints.map((endpoint2) => redactRemoteEndpoint(endpoint2));
|
|
149
1216
|
if (context.outputMode === "text") {
|
|
150
1217
|
if (endpoints.length === 0) {
|
|
@@ -171,7 +1238,7 @@ async function executeRemote(context, args) {
|
|
|
171
1238
|
if (!alias.value || !host.value || !token.value || !port.value) {
|
|
172
1239
|
throw new CliError("remote endpoint add requires --alias, --host, --port, and --token.", 1, { hint: "Re-run as `rig remote endpoint add --alias <a> --host <h> --port <n> --token <t>`." });
|
|
173
1240
|
}
|
|
174
|
-
const created =
|
|
1241
|
+
const created = upsertManagedRemoteEndpoint2({
|
|
175
1242
|
alias: alias.value,
|
|
176
1243
|
host: host.value,
|
|
177
1244
|
port: parseRequiredPositiveInt(port.value, "--port"),
|
|
@@ -221,7 +1288,7 @@ async function executeRemote(context, args) {
|
|
|
221
1288
|
if (!alias.value) {
|
|
222
1289
|
throw new CliError("remote endpoint remove requires --alias.", 1, { hint: "Run `rig remote endpoint list` to find the alias, then `rig remote endpoint remove --alias <a>`." });
|
|
223
1290
|
}
|
|
224
|
-
const removed =
|
|
1291
|
+
const removed = removeManagedRemoteEndpoint2(alias.value, undefined, context.projectRoot);
|
|
225
1292
|
if (!removed) {
|
|
226
1293
|
throw new CliError(`Remote endpoint alias not found: ${alias.value}`, 2, { hint: "Run `rig remote endpoint list` to see configured endpoint aliases." });
|
|
227
1294
|
}
|
|
@@ -378,7 +1445,7 @@ async function executeRemote(context, args) {
|
|
|
378
1445
|
});
|
|
379
1446
|
const subscribed = await client.subscribe(eventFilter ? [eventFilter] : []);
|
|
380
1447
|
if (!subscribed.success) {
|
|
381
|
-
throw new
|
|
1448
|
+
throw new RemoteCliError3("RIG_REMOTE_SUBSCRIBE_FAILED", subscribed.error || "Failed to subscribe to remote events.", 3);
|
|
382
1449
|
}
|
|
383
1450
|
if (seconds) {
|
|
384
1451
|
await Bun.sleep(seconds * 1000);
|
|
@@ -464,11 +1531,15 @@ async function executeRemote(context, args) {
|
|
|
464
1531
|
const directMergeResult = takeFlag(orchestrationPending, "--direct-merge");
|
|
465
1532
|
orchestrationPending = directMergeResult.rest;
|
|
466
1533
|
requireNoExtraArgs(orchestrationPending, "rig remote orchestrate-start [--max-workers <n>] [--max-iterations <n>] [--direct-merge]");
|
|
467
|
-
const response = await withClient((client) => client.startOrchestration({
|
|
468
|
-
maxWorkers
|
|
469
|
-
maxIterations
|
|
470
|
-
|
|
471
|
-
|
|
1534
|
+
const response = await withClient((client) => client.startOrchestration((() => {
|
|
1535
|
+
const maxWorkers = parseOptionalPositiveInt(maxWorkersResult.value, "--max-workers");
|
|
1536
|
+
const maxIterations = parseOptionalPositiveInt(maxIterationsResult.value, "--max-iterations");
|
|
1537
|
+
return {
|
|
1538
|
+
...maxWorkers !== undefined ? { maxWorkers } : {},
|
|
1539
|
+
...maxIterations !== undefined ? { maxIterations } : {},
|
|
1540
|
+
...directMergeResult.value !== undefined ? { directMerge: directMergeResult.value } : {}
|
|
1541
|
+
};
|
|
1542
|
+
})()));
|
|
472
1543
|
assertRemoteOperationSuccess(command, response);
|
|
473
1544
|
return {
|
|
474
1545
|
ok: true,
|
|
@@ -505,7 +1576,7 @@ async function executeRemote(context, args) {
|
|
|
505
1576
|
throw new CliError(`Unknown remote command: ${command}`, 1, { hint: "Run `rig remote --help` to list remote commands." });
|
|
506
1577
|
}
|
|
507
1578
|
} catch (error) {
|
|
508
|
-
if (error instanceof
|
|
1579
|
+
if (error instanceof RemoteCliError3) {
|
|
509
1580
|
throw new CliError(`[${error.code}] ${error.message}`, error.exitCode);
|
|
510
1581
|
}
|
|
511
1582
|
throw error;
|