@h-rig/cli-surface-plugin 0.0.6-alpha.156 → 0.0.6-alpha.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/app/drone-ui.d.ts +0 -11
- package/dist/src/app/drone-ui.js +0 -114
- package/dist/src/commands/_async-ui.d.ts +1 -1
- package/dist/src/commands/_cli-format.d.ts +0 -29
- package/dist/src/commands/_cli-format.js +59 -113
- package/dist/src/commands/_connection-state.d.ts +6 -33
- package/dist/src/commands/_connection-state.js +654 -138
- package/dist/src/commands/_doctor-checks.d.ts +2 -5
- package/dist/src/commands/_doctor-checks.js +10 -9
- package/dist/src/commands/_help-catalog.d.ts +2 -1
- package/dist/src/commands/_help-catalog.js +654 -7
- package/dist/src/commands/_inprocess-services.d.ts +5 -5
- package/dist/src/commands/_inprocess-services.js +1 -1
- package/dist/src/commands/_parsers.js +651 -12
- package/dist/src/commands/_paths.d.ts +0 -2
- package/dist/src/commands/_paths.js +2 -10
- package/dist/src/commands/_pi-install.d.ts +2 -12
- package/dist/src/commands/_pi-install.js +3 -36
- package/dist/src/commands/_policy.js +659 -20
- package/dist/src/commands/agent.d.ts +1 -1
- package/dist/src/commands/agent.js +675 -24
- package/dist/src/commands/config.d.ts +1 -1
- package/dist/src/commands/config.js +656 -21
- package/dist/src/commands/dist.d.ts +1 -1
- package/dist/src/commands/dist.js +828 -102
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +658 -12
- package/dist/src/commands/github.d.ts +1 -1
- package/dist/src/commands/github.js +658 -19
- package/dist/src/commands/inbox.d.ts +12 -8
- package/dist/src/commands/inbox.js +741 -22
- package/dist/src/commands/init.d.ts +17 -19
- package/dist/src/commands/init.js +836 -306
- package/dist/src/commands/inspect.d.ts +5 -6
- package/dist/src/commands/inspect.js +754 -42
- package/dist/src/commands/pi.d.ts +1 -1
- package/dist/src/commands/pi.js +655 -16
- package/dist/src/commands/plugin.d.ts +9 -9
- package/dist/src/commands/plugin.js +652 -13
- package/dist/src/commands/profile-and-review.d.ts +1 -1
- package/dist/src/commands/profile-and-review.js +655 -16
- package/dist/src/commands/queue.d.ts +1 -1
- package/dist/src/commands/queue.js +871 -12
- package/dist/src/commands/remote-client.d.ts +152 -0
- package/dist/src/commands/remote-client.js +475 -0
- package/dist/src/commands/remote.d.ts +1 -1
- package/dist/src/commands/remote.js +1100 -29
- package/dist/src/commands/repo-git-harness.d.ts +1 -1
- package/dist/src/commands/repo-git-harness.js +2321 -47
- package/dist/src/commands/run.d.ts +10 -6
- package/dist/src/commands/run.js +830 -50
- package/dist/src/commands/server.d.ts +1 -1
- package/dist/src/commands/server.js +649 -11
- package/dist/src/commands/setup.d.ts +2 -2
- package/dist/src/commands/setup.js +829 -18
- package/dist/src/commands/stats.d.ts +2 -4
- package/dist/src/commands/stats.js +1299 -20
- package/dist/src/commands/test.d.ts +1 -1
- package/dist/src/commands/test.js +648 -9
- package/dist/src/commands/triage.d.ts +2 -3
- package/dist/src/commands/triage.js +657 -11
- package/dist/src/commands/workspace.d.ts +1 -1
- package/dist/src/commands/workspace.js +1280 -15
- package/dist/src/control-plane/agent-binary-build.d.ts +9 -0
- package/dist/src/control-plane/agent-binary-build.js +88 -0
- package/dist/src/control-plane/embedded-native-assets.d.ts +7 -0
- package/dist/src/control-plane/embedded-native-assets.js +6 -0
- package/dist/src/control-plane/guard.d.ts +17 -0
- package/dist/src/control-plane/guard.js +684 -0
- package/dist/src/control-plane/harness-cli.d.ts +12 -0
- package/dist/src/control-plane/harness-cli.js +1623 -0
- package/dist/src/control-plane/native/git-ops.d.ts +67 -0
- package/dist/src/control-plane/native/git-ops.js +1381 -0
- package/dist/src/control-plane/native/github-auth-env.d.ts +1 -0
- package/dist/src/control-plane/native/github-auth-env.js +21 -0
- package/dist/src/control-plane/native/host-git.d.ts +4 -0
- package/dist/src/control-plane/native/host-git.js +51 -0
- package/dist/src/control-plane/priority-queue.d.ts +22 -0
- package/dist/src/control-plane/priority-queue.js +212 -0
- package/dist/src/control-plane/rigfig.d.ts +9 -0
- package/dist/src/control-plane/rigfig.js +70 -0
- package/dist/src/control-plane/scope.d.ts +3 -0
- package/dist/src/control-plane/scope.js +58 -0
- package/dist/src/control-plane/setup-status.d.ts +44 -0
- package/dist/src/control-plane/setup-status.js +164 -0
- package/dist/src/control-plane/task-data.d.ts +2 -0
- package/dist/src/control-plane/task-data.js +12 -0
- package/dist/src/control-plane/workspace-ops.d.ts +79 -0
- package/dist/src/control-plane/workspace-ops.js +639 -0
- package/dist/src/help-catalog-data.d.ts +7 -0
- package/dist/src/help-catalog-data.js +660 -0
- package/dist/src/kernel-dispatch.js +1 -3
- package/dist/src/plugin.js +10072 -30
- package/dist/src/runner.d.ts +7 -9
- package/dist/src/runner.js +750 -30
- package/package.json +12 -13
- package/dist/src/commands/_json-output.d.ts +0 -11
- package/dist/src/commands/_json-output.js +0 -54
- package/dist/src/commands/_pi-frontend.d.ts +0 -35
- package/dist/src/commands/_pi-frontend.js +0 -64
- package/dist/src/commands/_run-driver-helpers.d.ts +0 -26
- package/dist/src/commands/_run-driver-helpers.js +0 -132
- package/dist/src/commands/task-run-driver.d.ts +0 -93
- package/dist/src/commands/task-run-driver.js +0 -136
- package/dist/src/commands/task.d.ts +0 -46
- package/dist/src/commands/task.js +0 -555
- package/dist/src/provider-model.d.ts +0 -34
- package/dist/src/provider-model.js +0 -56
- package/dist/src/rig-config-package-deps.d.ts +0 -10
- package/dist/src/rig-config-package-deps.js +0 -272
- package/dist/src/version.d.ts +0 -8
- package/dist/src/version.js +0 -47
|
@@ -1,33 +1,753 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli-surface-plugin/src/commands/dist.ts
|
|
3
3
|
import {
|
|
4
|
-
chmodSync,
|
|
5
|
-
copyFileSync,
|
|
6
|
-
existsSync,
|
|
7
|
-
mkdirSync,
|
|
4
|
+
chmodSync as chmodSync2,
|
|
5
|
+
copyFileSync as copyFileSync2,
|
|
6
|
+
existsSync as existsSync2,
|
|
7
|
+
mkdirSync as mkdirSync2,
|
|
8
8
|
readdirSync,
|
|
9
9
|
readlinkSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
statSync,
|
|
10
|
+
rmSync as rmSync2,
|
|
11
|
+
statSync as statSync2,
|
|
12
12
|
symlinkSync,
|
|
13
13
|
unlinkSync
|
|
14
14
|
} from "fs";
|
|
15
15
|
import { homedir as homedir2 } from "os";
|
|
16
|
-
import { resolve as
|
|
16
|
+
import { resolve as resolve5 } from "path";
|
|
17
17
|
|
|
18
18
|
// packages/cli-surface-plugin/src/runner.ts
|
|
19
|
-
import { EventBus } from "@rig/runtime
|
|
20
|
-
import { CliError as RuntimeCliError } from "@rig/
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
import { EventBus } from "@rig/core/runtime-events";
|
|
20
|
+
import { CliError as RuntimeCliError } from "@rig/contracts";
|
|
21
|
+
|
|
22
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
23
|
+
import { optimizeNextInvocation } from "bun:jsc";
|
|
24
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
25
|
+
import { resolve } from "path";
|
|
26
|
+
|
|
27
|
+
// packages/cli-surface-plugin/src/control-plane/scope.ts
|
|
28
|
+
import { getScopeRules } from "@rig/core/scope-rules";
|
|
29
|
+
var scopeRegexCache = new Map;
|
|
30
|
+
function unique(values) {
|
|
31
|
+
return [...new Set(values)];
|
|
32
|
+
}
|
|
33
|
+
function normalizeRelativeScopePath(inputPath) {
|
|
34
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
35
|
+
const rules = getScopeRules();
|
|
36
|
+
if (rules?.stripPrefixes) {
|
|
37
|
+
for (const prefix of rules.stripPrefixes) {
|
|
38
|
+
if (normalized.startsWith(prefix)) {
|
|
39
|
+
normalized = normalized.slice(prefix.length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
|
|
46
|
+
let normalized = inputPath.replace(/^\.\//, "");
|
|
47
|
+
if (normalized.startsWith(projectRoot + "/")) {
|
|
48
|
+
normalized = normalized.slice(projectRoot.length + 1);
|
|
49
|
+
}
|
|
50
|
+
if (normalized.startsWith(monorepoRoot + "/")) {
|
|
51
|
+
normalized = normalized.slice(monorepoRoot.length + 1);
|
|
52
|
+
}
|
|
53
|
+
return normalizeRelativeScopePath(normalized);
|
|
54
|
+
}
|
|
55
|
+
function scopeGlobToRegex(glob) {
|
|
56
|
+
const cached = scopeRegexCache.get(glob);
|
|
57
|
+
if (cached) {
|
|
58
|
+
return cached;
|
|
59
|
+
}
|
|
60
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
|
|
61
|
+
const compiled = new RegExp(`^${escaped}$`);
|
|
62
|
+
scopeRegexCache.set(glob, compiled);
|
|
63
|
+
return compiled;
|
|
64
|
+
}
|
|
65
|
+
function scopeMatches(path, scopes) {
|
|
66
|
+
const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
|
|
67
|
+
for (const scope of scopes) {
|
|
68
|
+
const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
|
|
69
|
+
for (const candidatePath of pathVariants) {
|
|
70
|
+
for (const candidateScope of scopeVariants) {
|
|
71
|
+
if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
23
79
|
|
|
80
|
+
// packages/cli-surface-plugin/src/control-plane/guard.ts
|
|
81
|
+
import {
|
|
82
|
+
POLICY_VERSION
|
|
83
|
+
} from "@rig/contracts";
|
|
84
|
+
var DEFAULT_SCOPE = {
|
|
85
|
+
fail_closed: true,
|
|
86
|
+
harness_paths_exempt: true,
|
|
87
|
+
runtime_paths_exempt: true
|
|
88
|
+
};
|
|
89
|
+
var DEFAULT_SANDBOX = {
|
|
90
|
+
mode: "enforce",
|
|
91
|
+
network: true,
|
|
92
|
+
read_deny: [],
|
|
93
|
+
write_allow_from_runtime: true
|
|
94
|
+
};
|
|
95
|
+
var DEFAULT_ISOLATION = {
|
|
96
|
+
default_mode: "worktree",
|
|
97
|
+
repo_symlink_fallback: false,
|
|
98
|
+
strict_provisioning: true,
|
|
99
|
+
fail_closed_on_provision_error: true
|
|
100
|
+
};
|
|
101
|
+
var DEFAULT_COMPLETION = {
|
|
102
|
+
derive_checks_from_scope: true,
|
|
103
|
+
checks: [],
|
|
104
|
+
typescript_config_probe: ["tsconfig.json"],
|
|
105
|
+
eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
|
|
106
|
+
};
|
|
107
|
+
var DEFAULT_RUNTIME_IMAGE = {
|
|
108
|
+
deps: {
|
|
109
|
+
monorepo_install: false,
|
|
110
|
+
hp_next_install: false
|
|
111
|
+
},
|
|
112
|
+
plugins_require_binaries: true
|
|
113
|
+
};
|
|
114
|
+
var DEFAULT_RUNTIME_SNAPSHOT = {
|
|
115
|
+
enabled: true
|
|
116
|
+
};
|
|
117
|
+
function defaultPolicy() {
|
|
118
|
+
return {
|
|
119
|
+
version: POLICY_VERSION,
|
|
120
|
+
mode: "enforce",
|
|
121
|
+
scope: { ...DEFAULT_SCOPE },
|
|
122
|
+
rules: [],
|
|
123
|
+
sandbox: { ...DEFAULT_SANDBOX },
|
|
124
|
+
isolation: { ...DEFAULT_ISOLATION },
|
|
125
|
+
completion: { ...DEFAULT_COMPLETION },
|
|
126
|
+
runtime_image: {
|
|
127
|
+
deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
|
|
128
|
+
plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
|
|
129
|
+
},
|
|
130
|
+
runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
var policyCache = null;
|
|
134
|
+
var policyCachePath = null;
|
|
135
|
+
var seededPolicyConfig = null;
|
|
136
|
+
var compiledRegexCache = new Map;
|
|
137
|
+
function loadPolicy(projectRoot) {
|
|
138
|
+
if (seededPolicyConfig) {
|
|
139
|
+
return seededPolicyConfig;
|
|
140
|
+
}
|
|
141
|
+
const configPath = resolve(projectRoot, "rig/policy/policy.json");
|
|
142
|
+
if (!existsSync(configPath)) {
|
|
143
|
+
return defaultPolicy();
|
|
144
|
+
}
|
|
145
|
+
let mtimeMs;
|
|
146
|
+
try {
|
|
147
|
+
mtimeMs = statSync(configPath).mtimeMs;
|
|
148
|
+
} catch {
|
|
149
|
+
return defaultPolicy();
|
|
150
|
+
}
|
|
151
|
+
if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
|
|
152
|
+
return policyCache.config;
|
|
153
|
+
}
|
|
154
|
+
let parsed;
|
|
155
|
+
try {
|
|
156
|
+
parsed = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
157
|
+
} catch {
|
|
158
|
+
return defaultPolicy();
|
|
159
|
+
}
|
|
160
|
+
const config = mergeWithDefaults(parsed);
|
|
161
|
+
policyCache = { mtimeMs, config };
|
|
162
|
+
policyCachePath = configPath;
|
|
163
|
+
return config;
|
|
164
|
+
}
|
|
165
|
+
function mergeWithDefaults(parsed) {
|
|
166
|
+
const base = defaultPolicy();
|
|
167
|
+
if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
|
|
168
|
+
base.mode = parsed.mode;
|
|
169
|
+
}
|
|
170
|
+
if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
|
|
171
|
+
const s = parsed.scope;
|
|
172
|
+
if (typeof s.fail_closed === "boolean")
|
|
173
|
+
base.scope.fail_closed = s.fail_closed;
|
|
174
|
+
if (typeof s.harness_paths_exempt === "boolean")
|
|
175
|
+
base.scope.harness_paths_exempt = s.harness_paths_exempt;
|
|
176
|
+
if (typeof s.runtime_paths_exempt === "boolean")
|
|
177
|
+
base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(parsed.rules)) {
|
|
180
|
+
base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
|
|
181
|
+
}
|
|
182
|
+
if (Array.isArray(parsed.deny) && base.rules.length === 0) {
|
|
183
|
+
base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
|
|
184
|
+
}
|
|
185
|
+
if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
|
|
186
|
+
const sb = parsed.sandbox;
|
|
187
|
+
if (typeof sb.mode === "string" && isValidMode(sb.mode))
|
|
188
|
+
base.sandbox.mode = sb.mode;
|
|
189
|
+
if (typeof sb.network === "boolean")
|
|
190
|
+
base.sandbox.network = sb.network;
|
|
191
|
+
if (Array.isArray(sb.read_deny))
|
|
192
|
+
base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
|
|
193
|
+
if (typeof sb.write_allow_from_runtime === "boolean")
|
|
194
|
+
base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
|
|
195
|
+
}
|
|
196
|
+
if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
|
|
197
|
+
const iso = parsed.isolation;
|
|
198
|
+
if (iso.default_mode === "worktree")
|
|
199
|
+
base.isolation.default_mode = iso.default_mode;
|
|
200
|
+
if (typeof iso.repo_symlink_fallback === "boolean")
|
|
201
|
+
base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
|
|
202
|
+
if (typeof iso.strict_provisioning === "boolean")
|
|
203
|
+
base.isolation.strict_provisioning = iso.strict_provisioning;
|
|
204
|
+
if (typeof iso.fail_closed_on_provision_error === "boolean")
|
|
205
|
+
base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
|
|
206
|
+
}
|
|
207
|
+
if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
|
|
208
|
+
const comp = parsed.completion;
|
|
209
|
+
if (typeof comp.derive_checks_from_scope === "boolean")
|
|
210
|
+
base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
|
|
211
|
+
if (Array.isArray(comp.checks))
|
|
212
|
+
base.completion.checks = comp.checks.filter((v) => typeof v === "string");
|
|
213
|
+
if (Array.isArray(comp.typescript_config_probe))
|
|
214
|
+
base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
|
|
215
|
+
if (Array.isArray(comp.eslint_config_probe))
|
|
216
|
+
base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
|
|
217
|
+
}
|
|
218
|
+
if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
|
|
219
|
+
const runtimeImage = parsed.runtime_image;
|
|
220
|
+
if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
|
|
221
|
+
const deps = runtimeImage.deps;
|
|
222
|
+
if (typeof deps.monorepo_install === "boolean") {
|
|
223
|
+
base.runtime_image.deps.monorepo_install = deps.monorepo_install;
|
|
224
|
+
}
|
|
225
|
+
if (typeof deps.hp_next_install === "boolean") {
|
|
226
|
+
base.runtime_image.deps.hp_next_install = deps.hp_next_install;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (typeof runtimeImage.plugins_require_binaries === "boolean") {
|
|
230
|
+
base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
|
|
234
|
+
const runtimeSnapshot = parsed.runtime_snapshot;
|
|
235
|
+
if (typeof runtimeSnapshot.enabled === "boolean") {
|
|
236
|
+
base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return base;
|
|
240
|
+
}
|
|
241
|
+
function isValidMode(value) {
|
|
242
|
+
return value === "off" || value === "observe" || value === "enforce";
|
|
243
|
+
}
|
|
244
|
+
function isValidRule(value) {
|
|
245
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
246
|
+
return false;
|
|
247
|
+
const r = value;
|
|
248
|
+
return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
|
|
249
|
+
}
|
|
250
|
+
function migrateLegacyDeny(deny) {
|
|
251
|
+
const rules = [];
|
|
252
|
+
for (const entry of deny) {
|
|
253
|
+
if (typeof entry.id !== "string")
|
|
254
|
+
continue;
|
|
255
|
+
const match = {};
|
|
256
|
+
if (typeof entry.pattern === "string")
|
|
257
|
+
match.pattern = entry.pattern;
|
|
258
|
+
if (typeof entry.regex === "string")
|
|
259
|
+
match.regex = entry.regex;
|
|
260
|
+
if (!match.pattern && !match.regex)
|
|
261
|
+
continue;
|
|
262
|
+
rules.push({
|
|
263
|
+
id: entry.id,
|
|
264
|
+
category: "command",
|
|
265
|
+
match,
|
|
266
|
+
action: "block",
|
|
267
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return rules;
|
|
271
|
+
}
|
|
272
|
+
function precompilePolicyRuleRegexes(rules) {
|
|
273
|
+
return rules.map((rule) => {
|
|
274
|
+
const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
|
|
275
|
+
const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
|
|
276
|
+
return {
|
|
277
|
+
...rule,
|
|
278
|
+
...compiledRegex ? { compiledRegex } : {},
|
|
279
|
+
...compiledUnlessRegex ? { compiledUnlessRegex } : {}
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function getRegexUnsafeReason(pattern) {
|
|
284
|
+
if (pattern.length > 512) {
|
|
285
|
+
return "pattern exceeds max safe length (512 chars)";
|
|
286
|
+
}
|
|
287
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
288
|
+
return "pattern uses backreferences";
|
|
289
|
+
}
|
|
290
|
+
if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
291
|
+
return "pattern contains nested quantifiers";
|
|
292
|
+
}
|
|
293
|
+
if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
|
|
294
|
+
return "pattern contains nested broad quantifiers";
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
|
|
299
|
+
const cached = compiledRegexCache.get(pattern);
|
|
300
|
+
if (cached !== undefined) {
|
|
301
|
+
return cached ?? undefined;
|
|
302
|
+
}
|
|
303
|
+
const unsafeReason = getRegexUnsafeReason(pattern);
|
|
304
|
+
if (unsafeReason) {
|
|
305
|
+
if (logOnFailure) {
|
|
306
|
+
console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
|
|
307
|
+
}
|
|
308
|
+
compiledRegexCache.set(pattern, null);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const compiled = new RegExp(pattern);
|
|
313
|
+
compiledRegexCache.set(pattern, compiled);
|
|
314
|
+
return compiled;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (logOnFailure) {
|
|
317
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
318
|
+
console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
|
|
319
|
+
}
|
|
320
|
+
compiledRegexCache.set(pattern, null);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function matchRule(rule, input) {
|
|
325
|
+
const { match } = rule;
|
|
326
|
+
if (match.pattern && input.includes(match.pattern)) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
if (match.regex) {
|
|
330
|
+
const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
|
|
331
|
+
if (!compiled) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
return compiled.test(input);
|
|
336
|
+
} catch {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
function matchRuleUnless(rule, command, taskId) {
|
|
343
|
+
if (!rule.unless)
|
|
344
|
+
return false;
|
|
345
|
+
if (rule.unless.regex) {
|
|
346
|
+
const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
|
|
347
|
+
if (!compiled) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
if (compiled.test(command))
|
|
352
|
+
return true;
|
|
353
|
+
} catch {}
|
|
354
|
+
}
|
|
355
|
+
if (rule.unless.task_in && taskId) {
|
|
356
|
+
if (rule.unless.task_in.includes(taskId))
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
function resolveAction(mode, matched) {
|
|
362
|
+
if (matched.length === 0)
|
|
363
|
+
return "allow";
|
|
364
|
+
if (mode === "off")
|
|
365
|
+
return "allow";
|
|
366
|
+
if (mode === "observe")
|
|
367
|
+
return "warn";
|
|
368
|
+
return "block";
|
|
369
|
+
}
|
|
370
|
+
function resolveAbsolutePath(projectRoot, rawPath) {
|
|
371
|
+
if (rawPath.startsWith("/"))
|
|
372
|
+
return resolve(rawPath);
|
|
373
|
+
return resolve(projectRoot, rawPath);
|
|
374
|
+
}
|
|
375
|
+
function isHarnessPath(projectRoot, rawPath) {
|
|
376
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
377
|
+
const managedRoots = [
|
|
378
|
+
resolve(projectRoot, "rig"),
|
|
379
|
+
resolve(projectRoot, ".rig"),
|
|
380
|
+
resolve(projectRoot, "artifacts")
|
|
381
|
+
];
|
|
382
|
+
return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
|
|
383
|
+
}
|
|
384
|
+
function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
|
|
385
|
+
const absPath = resolveAbsolutePath(projectRoot, rawPath);
|
|
386
|
+
if (taskWorkspace) {
|
|
387
|
+
const workspaceRigRoot = resolve(taskWorkspace, ".rig");
|
|
388
|
+
const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
|
|
389
|
+
if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
|
|
394
|
+
return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
|
|
395
|
+
}
|
|
396
|
+
function isTestFile(path) {
|
|
397
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
|
|
398
|
+
}
|
|
399
|
+
function evaluate(context) {
|
|
400
|
+
const policy = loadPolicy(context.projectRoot);
|
|
401
|
+
switch (context.evaluation.type) {
|
|
402
|
+
case "tool-call":
|
|
403
|
+
return evaluateToolCall(policy, context);
|
|
404
|
+
case "command":
|
|
405
|
+
return evaluateCommand(policy, context);
|
|
406
|
+
case "content-write":
|
|
407
|
+
return evaluateContent(policy, context);
|
|
408
|
+
case "file-access":
|
|
409
|
+
return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function evaluateScope(policy, context, filePath, access) {
|
|
413
|
+
const allowed = () => ({
|
|
414
|
+
allowed: true,
|
|
415
|
+
matchedRules: [],
|
|
416
|
+
action: "allow",
|
|
417
|
+
failClosed: false
|
|
418
|
+
});
|
|
419
|
+
if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
|
|
420
|
+
return allowed();
|
|
421
|
+
}
|
|
422
|
+
if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
|
|
423
|
+
return allowed();
|
|
424
|
+
}
|
|
425
|
+
if (!context.taskId) {
|
|
426
|
+
if (access === "write" && policy.scope.fail_closed) {
|
|
427
|
+
return {
|
|
428
|
+
allowed: false,
|
|
429
|
+
matchedRules: [],
|
|
430
|
+
action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
|
|
431
|
+
failClosed: true
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return allowed();
|
|
435
|
+
}
|
|
436
|
+
const scopes = context.taskScopes || [];
|
|
437
|
+
if (scopes.length === 0) {
|
|
438
|
+
return allowed();
|
|
439
|
+
}
|
|
440
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
|
|
441
|
+
const absPath = resolve(filePath);
|
|
442
|
+
if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
|
|
443
|
+
const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
|
|
444
|
+
const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
|
|
445
|
+
return {
|
|
446
|
+
allowed: policy.mode !== "enforce",
|
|
447
|
+
matchedRules: matched2,
|
|
448
|
+
action: resolveAction(policy.mode, matched2),
|
|
449
|
+
failClosed: false
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
|
|
454
|
+
let normalizedPath = filePath;
|
|
455
|
+
if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
|
|
456
|
+
normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
|
|
457
|
+
}
|
|
458
|
+
normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
|
|
459
|
+
if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
|
|
460
|
+
return allowed();
|
|
461
|
+
}
|
|
462
|
+
const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
|
|
463
|
+
const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
|
|
464
|
+
return {
|
|
465
|
+
allowed: policy.mode !== "enforce",
|
|
466
|
+
matchedRules: matched,
|
|
467
|
+
action: resolveAction(policy.mode, matched),
|
|
468
|
+
failClosed: false
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function evaluateCommand(policy, context) {
|
|
472
|
+
const evaluation = context.evaluation;
|
|
473
|
+
if (evaluation.type !== "command") {
|
|
474
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
475
|
+
}
|
|
476
|
+
const command = evaluation.command;
|
|
477
|
+
const matchedRules = [];
|
|
478
|
+
for (const rule of policy.rules) {
|
|
479
|
+
if (rule.category !== "command")
|
|
480
|
+
continue;
|
|
481
|
+
if (!matchRule(rule, command))
|
|
482
|
+
continue;
|
|
483
|
+
if (matchRuleUnless(rule, command, context.taskId))
|
|
484
|
+
continue;
|
|
485
|
+
matchedRules.push({
|
|
486
|
+
id: rule.id,
|
|
487
|
+
category: rule.category,
|
|
488
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
489
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
const writeTarget = extractWriteTarget(command);
|
|
493
|
+
if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
|
|
494
|
+
const scopeResult = evaluateScope(policy, context, writeTarget, "write");
|
|
495
|
+
if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
|
|
496
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
500
|
+
return {
|
|
501
|
+
allowed: action !== "block",
|
|
502
|
+
matchedRules,
|
|
503
|
+
action,
|
|
504
|
+
failClosed: false
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function extractWriteTarget(command) {
|
|
508
|
+
const redirect = command.match(/>>?\s+([^\s;|&]+)/);
|
|
509
|
+
if (redirect?.[1])
|
|
510
|
+
return redirect[1];
|
|
511
|
+
const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
|
|
512
|
+
if (tee?.[2])
|
|
513
|
+
return tee[2];
|
|
514
|
+
return "";
|
|
515
|
+
}
|
|
516
|
+
function evaluateContent(policy, context) {
|
|
517
|
+
const evaluation = context.evaluation;
|
|
518
|
+
if (evaluation.type !== "content-write") {
|
|
519
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
520
|
+
}
|
|
521
|
+
const { content, file_path } = evaluation;
|
|
522
|
+
const matchedRules = [];
|
|
523
|
+
const scopeResult = evaluateScope(policy, context, file_path, "write");
|
|
524
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
525
|
+
matchedRules.push(...scopeResult.matchedRules);
|
|
526
|
+
}
|
|
527
|
+
for (const rule of policy.rules) {
|
|
528
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
529
|
+
continue;
|
|
530
|
+
if (rule.applies_to === "test-files" && !isTestFile(file_path))
|
|
531
|
+
continue;
|
|
532
|
+
if (!matchRule(rule, content))
|
|
533
|
+
continue;
|
|
534
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
535
|
+
continue;
|
|
536
|
+
matchedRules.push({
|
|
537
|
+
id: rule.id,
|
|
538
|
+
category: rule.category,
|
|
539
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
540
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
const action = resolveAction(policy.mode, matchedRules);
|
|
544
|
+
return {
|
|
545
|
+
allowed: action !== "block",
|
|
546
|
+
matchedRules,
|
|
547
|
+
action,
|
|
548
|
+
failClosed: false
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function evaluateToolCall(policy, context) {
|
|
552
|
+
const evaluation = context.evaluation;
|
|
553
|
+
if (evaluation.type !== "tool-call") {
|
|
554
|
+
return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
|
|
555
|
+
}
|
|
556
|
+
const { tool_name, tool_input } = evaluation;
|
|
557
|
+
const allMatched = [];
|
|
558
|
+
const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
|
|
559
|
+
for (const fp of filePaths) {
|
|
560
|
+
const access = isWriteTool(tool_name) ? "write" : "read";
|
|
561
|
+
const scopeResult = evaluateScope(policy, context, fp, access);
|
|
562
|
+
if (scopeResult.matchedRules.length > 0) {
|
|
563
|
+
allMatched.push(...scopeResult.matchedRules);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const content = extractContentFromToolInput(tool_input);
|
|
567
|
+
if (content) {
|
|
568
|
+
const filePath = filePaths[0] || "";
|
|
569
|
+
const contentContext = {
|
|
570
|
+
...context,
|
|
571
|
+
evaluation: { type: "content-write", file_path: filePath, content }
|
|
572
|
+
};
|
|
573
|
+
const contentPolicy = loadPolicy(context.projectRoot);
|
|
574
|
+
for (const rule of contentPolicy.rules) {
|
|
575
|
+
if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
|
|
576
|
+
continue;
|
|
577
|
+
if (rule.applies_to === "test-files" && !isTestFile(filePath))
|
|
578
|
+
continue;
|
|
579
|
+
if (!matchRule(rule, content))
|
|
580
|
+
continue;
|
|
581
|
+
if (matchRuleUnless(rule, content, context.taskId))
|
|
582
|
+
continue;
|
|
583
|
+
allMatched.push({
|
|
584
|
+
id: rule.id,
|
|
585
|
+
category: rule.category,
|
|
586
|
+
...rule.description !== undefined ? { description: rule.description } : {},
|
|
587
|
+
reason: rule.description || `Matched rule ${rule.id}`
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (tool_name === "Bash") {
|
|
592
|
+
const command = String(tool_input.command || tool_input.cmd || "");
|
|
593
|
+
if (command) {
|
|
594
|
+
const cmdContext = {
|
|
595
|
+
...context,
|
|
596
|
+
evaluation: { type: "command", command }
|
|
597
|
+
};
|
|
598
|
+
const cmdResult = evaluateCommand(policy, cmdContext);
|
|
599
|
+
if (cmdResult.matchedRules.length > 0) {
|
|
600
|
+
allMatched.push(...cmdResult.matchedRules);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const seen = new Set;
|
|
605
|
+
const deduplicated = [];
|
|
606
|
+
for (const rule of allMatched) {
|
|
607
|
+
if (!seen.has(rule.id)) {
|
|
608
|
+
seen.add(rule.id);
|
|
609
|
+
deduplicated.push(rule);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const action = resolveAction(policy.mode, deduplicated);
|
|
613
|
+
return {
|
|
614
|
+
allowed: action !== "block",
|
|
615
|
+
matchedRules: deduplicated,
|
|
616
|
+
action,
|
|
617
|
+
failClosed: false
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function isWriteTool(toolName) {
|
|
621
|
+
return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
|
|
622
|
+
}
|
|
623
|
+
function extractFilePathsFromToolInput(toolName, input) {
|
|
624
|
+
const paths = [];
|
|
625
|
+
const add = (value) => {
|
|
626
|
+
if (typeof value === "string" && value.trim()) {
|
|
627
|
+
paths.push(value.trim());
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
631
|
+
add(input.file_path);
|
|
632
|
+
add(input.path);
|
|
633
|
+
} else if (toolName === "Glob") {
|
|
634
|
+
add(input.path);
|
|
635
|
+
} else if (toolName === "Grep") {
|
|
636
|
+
add(input.path);
|
|
637
|
+
} else {
|
|
638
|
+
add(input.file_path);
|
|
639
|
+
add(input.path);
|
|
640
|
+
}
|
|
641
|
+
return paths;
|
|
642
|
+
}
|
|
643
|
+
function extractContentFromToolInput(input) {
|
|
644
|
+
if (typeof input.content === "string")
|
|
645
|
+
return input.content;
|
|
646
|
+
if (typeof input.new_string === "string")
|
|
647
|
+
return input.new_string;
|
|
648
|
+
return "";
|
|
649
|
+
}
|
|
650
|
+
var guardHotPathPrimed = false;
|
|
651
|
+
function primeGuardHotPaths() {
|
|
652
|
+
if (guardHotPathPrimed) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
guardHotPathPrimed = true;
|
|
656
|
+
try {
|
|
657
|
+
optimizeNextInvocation(matchRule);
|
|
658
|
+
optimizeNextInvocation(evaluate);
|
|
659
|
+
} catch {}
|
|
660
|
+
}
|
|
661
|
+
primeGuardHotPaths();
|
|
662
|
+
|
|
663
|
+
// packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
|
|
664
|
+
import { chmodSync, copyFileSync, linkSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
665
|
+
import { basename, dirname, resolve as resolve2 } from "path";
|
|
666
|
+
import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
|
|
667
|
+
|
|
668
|
+
// packages/cli-surface-plugin/src/control-plane/embedded-native-assets.ts
|
|
669
|
+
var embeddedNatives = null;
|
|
670
|
+
|
|
671
|
+
// packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
|
|
672
|
+
function materializeSelfExecRole(outputPath, define) {
|
|
673
|
+
const selfPath = process.execPath;
|
|
674
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
675
|
+
rmSync(outputPath, { force: true });
|
|
676
|
+
try {
|
|
677
|
+
linkSync(selfPath, outputPath);
|
|
678
|
+
} catch {
|
|
679
|
+
copyFileSync(selfPath, outputPath);
|
|
680
|
+
}
|
|
681
|
+
chmodSync(outputPath, 493);
|
|
682
|
+
const role = basename(outputPath).replace(/\.exe$/i, "");
|
|
683
|
+
writeFileSync(`${outputPath}.rig-runconfig.json`, `${JSON.stringify({ role, define: define ?? {} }, null, 2)}
|
|
684
|
+
`, { mode: 384 });
|
|
685
|
+
}
|
|
686
|
+
function hasEmbeddedNatives() {
|
|
687
|
+
return embeddedNatives !== null;
|
|
688
|
+
}
|
|
689
|
+
async function buildAgentBinary(entrypoint, outputPath, cwd, defines) {
|
|
690
|
+
if (hasEmbeddedNatives()) {
|
|
691
|
+
materializeSelfExecRole(outputPath, defines);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
await buildRuntimeBinary({
|
|
695
|
+
entrypoint,
|
|
696
|
+
outputPath,
|
|
697
|
+
cwd,
|
|
698
|
+
...defines ? { define: defines } : {},
|
|
699
|
+
env: runtimeProvisioningEnv()
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
async function buildRuntimeBinary(options) {
|
|
703
|
+
mkdirSync(dirname(options.outputPath), { recursive: true });
|
|
704
|
+
await withTemporaryEnv({
|
|
705
|
+
...options.env,
|
|
706
|
+
...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
|
|
707
|
+
}, async () => {
|
|
708
|
+
const result = await Bun.build({
|
|
709
|
+
entrypoints: [resolve2(options.cwd, options.entrypoint)],
|
|
710
|
+
compile: { outfile: options.outputPath },
|
|
711
|
+
target: "bun",
|
|
712
|
+
format: "esm",
|
|
713
|
+
minify: true,
|
|
714
|
+
bytecode: true,
|
|
715
|
+
...options.define ? { define: { __RIG_BUILD_CONFIG__: JSON.stringify(options.define) } } : {}
|
|
716
|
+
});
|
|
717
|
+
if (!result.success) {
|
|
718
|
+
throw new Error(result.logs.map((log) => log.message).join(`
|
|
719
|
+
`) || `Failed to compile ${options.entrypoint}`);
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
chmodSync(options.outputPath, 493);
|
|
723
|
+
}
|
|
724
|
+
async function withTemporaryEnv(env, callback) {
|
|
725
|
+
const previous = {};
|
|
726
|
+
for (const [key, value] of Object.entries(env)) {
|
|
727
|
+
previous[key] = process.env[key];
|
|
728
|
+
if (value === undefined) {
|
|
729
|
+
delete process.env[key];
|
|
730
|
+
} else {
|
|
731
|
+
process.env[key] = value;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
return await callback();
|
|
736
|
+
} finally {
|
|
737
|
+
for (const [key, value] of Object.entries(previous)) {
|
|
738
|
+
if (value === undefined) {
|
|
739
|
+
delete process.env[key];
|
|
740
|
+
} else {
|
|
741
|
+
process.env[key] = value;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// packages/cli-surface-plugin/src/runner.ts
|
|
24
748
|
class CliError extends RuntimeCliError {
|
|
25
|
-
hint;
|
|
26
749
|
constructor(message, exitCode = 1, options = {}) {
|
|
27
|
-
super(message, exitCode);
|
|
28
|
-
if (options.hint?.trim()) {
|
|
29
|
-
this.hint = options.hint.trim();
|
|
30
|
-
}
|
|
750
|
+
super(message, exitCode, options);
|
|
31
751
|
}
|
|
32
752
|
}
|
|
33
753
|
function takeOption(args, option) {
|
|
@@ -58,15 +778,13 @@ Usage: ${usage}`);
|
|
|
58
778
|
}
|
|
59
779
|
|
|
60
780
|
// packages/cli-surface-plugin/src/commands/dist.ts
|
|
61
|
-
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
} from "@rig/runtime/control-plane/runtime/image/index";
|
|
65
|
-
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
781
|
+
import { GUARD_TOOLCHAIN_SOURCES, ISOLATION_BACKEND, LIFECYCLE_TOOLCHAIN_SOURCES } from "@rig/contracts";
|
|
782
|
+
import { defineCapability } from "@rig/core/capability";
|
|
783
|
+
import { loadCapabilityForRoot } from "@rig/core/capability-loaders";
|
|
66
784
|
|
|
67
785
|
// packages/cli-surface-plugin/src/commands/_parsers.ts
|
|
68
786
|
import { homedir } from "os";
|
|
69
|
-
import { resolve } from "path";
|
|
787
|
+
import { resolve as resolve3 } from "path";
|
|
70
788
|
function parseInstallScope(value) {
|
|
71
789
|
if (!value || value === "user") {
|
|
72
790
|
return "user";
|
|
@@ -78,34 +796,34 @@ function parseInstallScope(value) {
|
|
|
78
796
|
}
|
|
79
797
|
function resolveInstallDir(scope, explicitPath) {
|
|
80
798
|
if (explicitPath) {
|
|
81
|
-
return
|
|
799
|
+
return resolve3(explicitPath);
|
|
82
800
|
}
|
|
83
801
|
if (scope === "system") {
|
|
84
802
|
return "/usr/local/bin";
|
|
85
803
|
}
|
|
86
|
-
return
|
|
804
|
+
return resolve3(homedir(), ".local/bin");
|
|
87
805
|
}
|
|
88
806
|
|
|
89
807
|
// packages/cli-surface-plugin/src/commands/_paths.ts
|
|
90
|
-
import { resolve as
|
|
91
|
-
import { resolveMonorepoRoot } from "@rig/
|
|
808
|
+
import { resolve as resolve4 } from "path";
|
|
809
|
+
import { resolveMonorepoRoot } from "@rig/core/layout";
|
|
92
810
|
function resolveControlPlaneMonorepoRoot(projectRoot) {
|
|
93
811
|
return resolveMonorepoRoot(projectRoot);
|
|
94
812
|
}
|
|
95
813
|
function resolveControlPlaneHostStateRoot(projectRoot) {
|
|
96
|
-
return
|
|
814
|
+
return resolve4(projectRoot, ".rig");
|
|
97
815
|
}
|
|
98
816
|
function resolveControlPlaneHostBinDir(projectRoot) {
|
|
99
|
-
return
|
|
817
|
+
return resolve4(resolveControlPlaneHostStateRoot(projectRoot), "bin");
|
|
100
818
|
}
|
|
101
819
|
function resolveControlPlaneHostDistDir(projectRoot) {
|
|
102
|
-
return
|
|
820
|
+
return resolve4(resolveControlPlaneHostStateRoot(projectRoot), "dist");
|
|
103
821
|
}
|
|
104
822
|
function resolveControlPlaneMonorepoStateRoot(projectRoot) {
|
|
105
|
-
return
|
|
823
|
+
return resolve4(resolveControlPlaneMonorepoRoot(projectRoot), ".rig");
|
|
106
824
|
}
|
|
107
825
|
function resolveControlPlaneMonorepoRuntimeDir(projectRoot) {
|
|
108
|
-
return
|
|
826
|
+
return resolve4(resolveControlPlaneMonorepoStateRoot(projectRoot), "runtime");
|
|
109
827
|
}
|
|
110
828
|
|
|
111
829
|
// packages/cli-surface-plugin/src/commands/_probes.ts
|
|
@@ -119,15 +837,29 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
119
837
|
}
|
|
120
838
|
|
|
121
839
|
// packages/cli-surface-plugin/src/commands/dist.ts
|
|
840
|
+
var GuardToolchainSourcesCap = defineCapability(GUARD_TOOLCHAIN_SOURCES);
|
|
841
|
+
var LifecycleToolchainSourcesCap = defineCapability(LIFECYCLE_TOOLCHAIN_SOURCES);
|
|
842
|
+
async function resolveToolchainBuildTargets(projectRoot) {
|
|
843
|
+
const contributions = [];
|
|
844
|
+
for (const cap of [GuardToolchainSourcesCap, LifecycleToolchainSourcesCap]) {
|
|
845
|
+
const contribution = await loadCapabilityForRoot(projectRoot, cap);
|
|
846
|
+
if (contribution)
|
|
847
|
+
contributions.push(contribution);
|
|
848
|
+
}
|
|
849
|
+
return contributions.flatMap((contribution) => [
|
|
850
|
+
...Object.entries(contribution.namedSources ?? {}),
|
|
851
|
+
...(contribution.hookBinaries ?? []).map((hook) => [hook.name, hook.source])
|
|
852
|
+
]);
|
|
853
|
+
}
|
|
122
854
|
function collectRigValidatorBuildTargets(input) {
|
|
123
|
-
const validatorsRoot =
|
|
124
|
-
if (!
|
|
855
|
+
const validatorsRoot = resolve5(input.hostProjectRoot, "packages/validator-kit/src/validators");
|
|
856
|
+
if (!existsSync2(validatorsRoot))
|
|
125
857
|
return [];
|
|
126
858
|
const targets = [];
|
|
127
859
|
for (const category of readdirSync(validatorsRoot, { withFileTypes: true })) {
|
|
128
860
|
if (!category.isDirectory())
|
|
129
861
|
continue;
|
|
130
|
-
const categoryDir =
|
|
862
|
+
const categoryDir = resolve5(validatorsRoot, category.name);
|
|
131
863
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
132
864
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
133
865
|
continue;
|
|
@@ -136,8 +868,8 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
136
868
|
continue;
|
|
137
869
|
const validatorName = `${category.name}-${check}`;
|
|
138
870
|
targets.push({
|
|
139
|
-
source: `packages/
|
|
140
|
-
dest:
|
|
871
|
+
source: `packages/validator-kit/src/validators/${category.name}/${entry.name}`,
|
|
872
|
+
dest: resolve5(input.imageDir, `bin/validators/${validatorName}`),
|
|
141
873
|
cwd: input.hostProjectRoot
|
|
142
874
|
});
|
|
143
875
|
}
|
|
@@ -146,17 +878,17 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
146
878
|
}
|
|
147
879
|
async function findLatestDistBinary(projectRoot) {
|
|
148
880
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
149
|
-
if (!
|
|
881
|
+
if (!existsSync2(distRoot)) {
|
|
150
882
|
return null;
|
|
151
883
|
}
|
|
152
884
|
const candidates = [];
|
|
153
885
|
for (const entry of readdirSync(distRoot, { withFileTypes: true })) {
|
|
154
886
|
if (!entry.name.startsWith("rig-") && entry.name !== "rig")
|
|
155
887
|
continue;
|
|
156
|
-
const entryPath =
|
|
157
|
-
const binary = entry.isDirectory() ?
|
|
888
|
+
const entryPath = resolve5(distRoot, entry.name);
|
|
889
|
+
const binary = entry.isDirectory() ? resolve5(entryPath, "bin", "rig") : entryPath;
|
|
158
890
|
try {
|
|
159
|
-
const stat =
|
|
891
|
+
const stat = statSync2(binary);
|
|
160
892
|
if (stat.isFile())
|
|
161
893
|
candidates.push({ path: binary, mtimeMs: stat.mtimeMs });
|
|
162
894
|
} catch {}
|
|
@@ -174,7 +906,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
174
906
|
async function runDistDoctor(projectRoot) {
|
|
175
907
|
const bunPath = Bun.which("bun");
|
|
176
908
|
const rigPath = Bun.which("rig");
|
|
177
|
-
const userBinDir =
|
|
909
|
+
const userBinDir = resolve5(homedir2(), ".local/bin");
|
|
178
910
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
179
911
|
let rigRunnable = false;
|
|
180
912
|
if (rigPath) {
|
|
@@ -201,16 +933,16 @@ function rigRunSiblingOf(binaryPath) {
|
|
|
201
933
|
const suffix = name.toLowerCase().endsWith(".exe") ? ".exe" : "";
|
|
202
934
|
const stem = suffix ? name.slice(0, -suffix.length) : name;
|
|
203
935
|
const sibling = stem === "rig" ? `rig-run${suffix}` : stem.startsWith("rig-") ? `${stem.replace(/^rig/, "rig-run")}${suffix}` : `rig-run${suffix}`;
|
|
204
|
-
return
|
|
936
|
+
return resolve5(binaryPath, "..", sibling);
|
|
205
937
|
}
|
|
206
938
|
function installExecutable(source, target) {
|
|
207
|
-
if (
|
|
208
|
-
if (
|
|
939
|
+
if (resolve5(source) !== resolve5(target)) {
|
|
940
|
+
if (existsSync2(target)) {
|
|
209
941
|
unlinkSync(target);
|
|
210
942
|
}
|
|
211
|
-
|
|
943
|
+
copyFileSync2(source, target);
|
|
212
944
|
}
|
|
213
|
-
|
|
945
|
+
chmodSync2(target, 493);
|
|
214
946
|
}
|
|
215
947
|
async function executeDist(context, args) {
|
|
216
948
|
const [command = "build", ...rest] = args;
|
|
@@ -233,22 +965,22 @@ async function executeDist(context, args) {
|
|
|
233
965
|
requireNoExtraArgs(pending, "rig dist install [--scope user|system] [--dir <dir>]");
|
|
234
966
|
const scope = parseInstallScope(scopeResult.value);
|
|
235
967
|
const installDir = resolveInstallDir(scope, pathResult.value);
|
|
236
|
-
|
|
968
|
+
mkdirSync2(installDir, { recursive: true });
|
|
237
969
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
238
970
|
let buildDir = null;
|
|
239
971
|
if (!source) {
|
|
240
|
-
buildDir =
|
|
972
|
+
buildDir = resolve5(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
241
973
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
242
|
-
source =
|
|
974
|
+
source = resolve5(buildDir, "bin", "rig");
|
|
243
975
|
}
|
|
244
|
-
if (!
|
|
976
|
+
if (!existsSync2(source)) {
|
|
245
977
|
throw new CliError(`Unable to locate rig binary at ${source}.`, 2, { hint: "Build it first with `rig dist build`, then re-run `rig dist install`." });
|
|
246
978
|
}
|
|
247
|
-
const installedPath =
|
|
979
|
+
const installedPath = resolve5(installDir, "rig");
|
|
248
980
|
const installedRigRunPath = rigRunSiblingOf(installedPath);
|
|
249
981
|
const sourceRigRun = rigRunSiblingOf(source);
|
|
250
982
|
installExecutable(source, installedPath);
|
|
251
|
-
installExecutable(
|
|
983
|
+
installExecutable(existsSync2(sourceRigRun) ? sourceRigRun : source, installedRigRunPath);
|
|
252
984
|
const pathEntries = (process.env.PATH || "").split(":").filter(Boolean);
|
|
253
985
|
const inPath = pathEntries.includes(installDir);
|
|
254
986
|
if (context.outputMode === "text") {
|
|
@@ -268,7 +1000,7 @@ async function executeDist(context, args) {
|
|
|
268
1000
|
installedPath,
|
|
269
1001
|
installedRigRunPath,
|
|
270
1002
|
sourcePath: source,
|
|
271
|
-
sourceRigRunPath:
|
|
1003
|
+
sourceRigRunPath: existsSync2(sourceRigRun) ? sourceRigRun : null,
|
|
272
1004
|
builtNow: Boolean(buildDir),
|
|
273
1005
|
inPath
|
|
274
1006
|
}
|
|
@@ -287,56 +1019,50 @@ async function executeDist(context, args) {
|
|
|
287
1019
|
}
|
|
288
1020
|
case "rebuild-agent": {
|
|
289
1021
|
requireNoExtraArgs(rest, "rig dist rebuild-agent");
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
1022
|
+
const isolationBackend = await loadCapabilityForRoot(context.projectRoot, defineCapability(ISOLATION_BACKEND));
|
|
1023
|
+
if (!isolationBackend) {
|
|
1024
|
+
throw new CliError("Isolation backend capability unavailable: ensure @rig/isolation-plugin is installed in rig.config.", 1, { hint: "Run `rig dist rebuild-agent` from a project with a valid rig.config." });
|
|
1025
|
+
}
|
|
1026
|
+
const fp = await isolationBackend.computeRuntimeImageFingerprint(context.projectRoot);
|
|
1027
|
+
const currentId = isolationBackend.computeRuntimeImageId(fp);
|
|
1028
|
+
const imagesDir = resolve5(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
1029
|
+
mkdirSync2(imagesDir, { recursive: true });
|
|
294
1030
|
let pruned = 0;
|
|
295
1031
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
296
1032
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
297
|
-
|
|
1033
|
+
rmSync2(resolve5(imagesDir, entry.name), { recursive: true, force: true });
|
|
298
1034
|
pruned++;
|
|
299
1035
|
}
|
|
300
1036
|
}
|
|
301
1037
|
if (pruned > 0 && context.outputMode === "text") {
|
|
302
1038
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
303
1039
|
}
|
|
304
|
-
const imageDir =
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const hookNames = [
|
|
309
|
-
"scope-guard",
|
|
310
|
-
"import-guard",
|
|
311
|
-
"safety-guard",
|
|
312
|
-
"test-integrity-guard",
|
|
313
|
-
"audit-trail",
|
|
314
|
-
"post-edit-lint",
|
|
315
|
-
"completion-verification",
|
|
316
|
-
"inject-context",
|
|
317
|
-
"task-runtime-start"
|
|
318
|
-
];
|
|
1040
|
+
const imageDir = resolve5(imagesDir, currentId);
|
|
1041
|
+
mkdirSync2(resolve5(imageDir, "bin/hooks"), { recursive: true });
|
|
1042
|
+
mkdirSync2(resolve5(imageDir, "bin/plugins"), { recursive: true });
|
|
1043
|
+
mkdirSync2(resolve5(imageDir, "bin/validators"), { recursive: true });
|
|
319
1044
|
const targets = [];
|
|
320
1045
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
targets.push({ source: src, dest:
|
|
1046
|
+
for (const [name, src] of await resolveToolchainBuildTargets(context.projectRoot)) {
|
|
1047
|
+
const subdir = name === "controlled-bash" ? "bin" : "bin/hooks";
|
|
1048
|
+
const hostSubdir = name === "controlled-bash" ? "" : "hooks";
|
|
1049
|
+
if (!existsSync2(resolve5(hostProjectRoot, src)))
|
|
1050
|
+
continue;
|
|
1051
|
+
targets.push({ source: src, dest: resolve5(imageDir, subdir, name), cwd: hostProjectRoot });
|
|
1052
|
+
targets.push({ source: src, dest: resolve5(resolveControlPlaneHostBinDir(context.projectRoot), hostSubdir, name), cwd: hostProjectRoot });
|
|
327
1053
|
}
|
|
328
|
-
const pluginsDir =
|
|
329
|
-
const binPluginsDir =
|
|
330
|
-
const validatorsRoot =
|
|
331
|
-
const binValidatorsDir =
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (
|
|
1054
|
+
const pluginsDir = resolve5(context.projectRoot, "rig/plugins");
|
|
1055
|
+
const binPluginsDir = resolve5(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
1056
|
+
const validatorsRoot = resolve5(hostProjectRoot, "packages/validator-kit/src/validators");
|
|
1057
|
+
const binValidatorsDir = resolve5(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
1058
|
+
mkdirSync2(binPluginsDir, { recursive: true });
|
|
1059
|
+
mkdirSync2(binValidatorsDir, { recursive: true });
|
|
1060
|
+
if (existsSync2(pluginsDir)) {
|
|
335
1061
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
336
1062
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
337
1063
|
if (!m)
|
|
338
1064
|
continue;
|
|
339
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
1065
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve5(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
340
1066
|
}
|
|
341
1067
|
}
|
|
342
1068
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -345,19 +1071,19 @@ async function executeDist(context, args) {
|
|
|
345
1071
|
console.log(`Building: ${dest}`);
|
|
346
1072
|
}
|
|
347
1073
|
const isValidator = dest.includes("/bin/validators/");
|
|
348
|
-
await
|
|
1074
|
+
await buildAgentBinary(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
349
1075
|
}
|
|
350
|
-
if (
|
|
1076
|
+
if (existsSync2(pluginsDir)) {
|
|
351
1077
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
352
1078
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
353
1079
|
if (!m)
|
|
354
1080
|
continue;
|
|
355
1081
|
const pluginName = m[1];
|
|
356
|
-
const imageBin =
|
|
1082
|
+
const imageBin = resolve5(imageDir, `bin/plugins/${pluginName}`);
|
|
357
1083
|
if (!pluginName)
|
|
358
1084
|
continue;
|
|
359
|
-
const symlinkPath =
|
|
360
|
-
if (
|
|
1085
|
+
const symlinkPath = resolve5(binPluginsDir, pluginName);
|
|
1086
|
+
if (existsSync2(imageBin)) {
|
|
361
1087
|
try {
|
|
362
1088
|
unlinkSync(symlinkPath);
|
|
363
1089
|
} catch {}
|
|
@@ -365,10 +1091,10 @@ async function executeDist(context, args) {
|
|
|
365
1091
|
}
|
|
366
1092
|
}
|
|
367
1093
|
}
|
|
368
|
-
if (
|
|
1094
|
+
if (existsSync2(validatorsRoot)) {
|
|
369
1095
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
370
1096
|
for (const category of categories) {
|
|
371
|
-
const categoryDir =
|
|
1097
|
+
const categoryDir = resolve5(validatorsRoot, category.name);
|
|
372
1098
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
373
1099
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
374
1100
|
continue;
|
|
@@ -376,9 +1102,9 @@ async function executeDist(context, args) {
|
|
|
376
1102
|
if (!check || check === "index" || check === "shared")
|
|
377
1103
|
continue;
|
|
378
1104
|
const validatorName = `${category.name}-${check}`;
|
|
379
|
-
const imageBin =
|
|
380
|
-
const symlinkPath =
|
|
381
|
-
if (
|
|
1105
|
+
const imageBin = resolve5(imageDir, `bin/validators/${validatorName}`);
|
|
1106
|
+
const symlinkPath = resolve5(binValidatorsDir, validatorName);
|
|
1107
|
+
if (existsSync2(imageBin)) {
|
|
382
1108
|
try {
|
|
383
1109
|
unlinkSync(symlinkPath);
|
|
384
1110
|
} catch {}
|
|
@@ -387,18 +1113,18 @@ async function executeDist(context, args) {
|
|
|
387
1113
|
}
|
|
388
1114
|
}
|
|
389
1115
|
}
|
|
390
|
-
const agentsDir =
|
|
391
|
-
if (
|
|
1116
|
+
const agentsDir = resolve5(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
1117
|
+
if (existsSync2(agentsDir)) {
|
|
392
1118
|
let relinkCount = 0;
|
|
393
1119
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
394
1120
|
if (!agentEntry.isDirectory())
|
|
395
1121
|
continue;
|
|
396
|
-
const agentBinDir =
|
|
397
|
-
if (!
|
|
1122
|
+
const agentBinDir = resolve5(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
1123
|
+
if (!existsSync2(agentBinDir))
|
|
398
1124
|
continue;
|
|
399
1125
|
const walkDir = (dir) => {
|
|
400
1126
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
401
|
-
const fullPath =
|
|
1127
|
+
const fullPath = resolve5(dir, entry.name);
|
|
402
1128
|
if (entry.isDirectory()) {
|
|
403
1129
|
walkDir(fullPath);
|
|
404
1130
|
} else if (entry.isSymbolicLink()) {
|