@h-rig/core 0.0.6-alpha.18 → 0.0.6-alpha.181
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/agent-role-registry.d.ts +4 -0
- package/dist/src/agent-role-registry.js +27 -0
- package/dist/src/authority-paths.d.ts +15 -0
- package/dist/src/authority-paths.js +80 -0
- package/dist/src/baked-secrets.d.ts +3 -0
- package/dist/src/baked-secrets.js +63 -0
- package/dist/src/build-time-config.d.ts +12 -0
- package/dist/src/build-time-config.js +25 -0
- package/dist/src/build-time-config.macro.d.ts +1 -0
- package/dist/src/capability-loaders.d.ts +51 -0
- package/dist/src/capability-loaders.js +870 -0
- package/dist/src/capability.d.ts +79 -0
- package/dist/src/capability.js +63 -0
- package/dist/src/checkout-root.d.ts +1 -0
- package/dist/src/checkout-root.js +30 -0
- package/dist/src/config-env.d.ts +4 -0
- package/dist/src/config-env.js +23 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.js +44 -0
- package/dist/src/declarative-config.d.ts +14 -0
- package/dist/src/declarative-config.js +85 -0
- package/dist/src/default-kernel.d.ts +1 -0
- package/dist/src/default-kernel.js +12 -0
- package/dist/src/define-config.d.ts +20 -0
- package/dist/src/define-config.js +28 -15
- package/dist/src/define-plugin.d.ts +13 -0
- package/dist/src/define-plugin.js +4 -43
- package/dist/src/embedded-plugins.d.ts +59 -0
- package/dist/src/embedded-plugins.js +22 -0
- package/dist/src/exec.d.ts +13 -0
- package/dist/src/exec.js +101 -0
- package/dist/src/harness-paths.d.ts +9 -0
- package/dist/src/harness-paths.js +126 -0
- package/dist/src/hook-materializer.d.ts +21 -0
- package/dist/src/hook-materializer.js +152 -0
- package/dist/src/hook-protocol.d.ts +2 -0
- package/dist/src/hook-protocol.js +432 -0
- package/dist/src/hook-runner.d.ts +48 -0
- package/dist/src/hook-runner.js +868 -0
- package/dist/src/hook-runtime.d.ts +52 -0
- package/dist/src/hook-runtime.js +432 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +210 -2499
- package/dist/src/json-files.d.ts +9 -0
- package/dist/src/json-files.js +124 -0
- package/dist/src/kernel-boot.d.ts +2 -0
- package/dist/src/kernel-boot.js +10 -0
- package/dist/src/kernel-entrypoint.d.ts +22 -0
- package/dist/src/kernel-entrypoint.js +660 -0
- package/dist/src/kernel-plugin-abi.d.ts +1 -0
- package/dist/src/kernel-plugin-abi.js +1 -0
- package/dist/src/kernel-resolver.d.ts +2 -0
- package/dist/src/kernel-resolver.js +6 -0
- package/dist/src/layout.d.ts +10 -0
- package/dist/src/layout.js +138 -0
- package/dist/src/load-config.d.ts +2 -0
- package/dist/src/load-config.js +535 -30
- package/dist/src/placement.d.ts +58 -0
- package/dist/src/placement.js +53 -0
- package/dist/src/plugin-host-context.d.ts +65 -0
- package/dist/src/plugin-host-context.js +1171 -0
- package/dist/src/plugin-host-registries.d.ts +31 -0
- package/dist/src/plugin-host-registries.js +79 -0
- package/dist/src/plugin-host.d.ts +77 -0
- package/dist/src/plugin-host.js +127 -63
- package/dist/src/plugin-runtime.d.ts +173 -0
- package/dist/src/project-plugins.d.ts +63 -0
- package/dist/src/project-plugins.js +905 -0
- package/dist/src/remote-config.d.ts +125 -0
- package/dist/src/remote-config.js +85 -0
- package/dist/src/root-resolver.d.ts +5 -0
- package/dist/src/root-resolver.js +68 -0
- package/dist/src/run-provisioning.d.ts +37 -0
- package/dist/src/run-provisioning.js +35 -0
- package/dist/src/runtime-context.d.ts +20 -0
- package/dist/src/runtime-context.js +257 -0
- package/dist/src/runtime-events.d.ts +44 -0
- package/dist/src/runtime-events.js +208 -0
- package/dist/src/runtime-overlay.d.ts +11 -0
- package/dist/src/runtime-overlay.js +69 -0
- package/dist/src/runtime-paths.d.ts +21 -0
- package/dist/src/runtime-paths.js +181 -0
- package/dist/src/runtime-provisioning-env.d.ts +5 -0
- package/dist/src/runtime-provisioning-env.js +217 -0
- package/dist/src/runtime-runner-context.d.ts +12 -0
- package/dist/src/runtime-runner-context.js +1 -0
- package/dist/src/safe-identifiers.d.ts +44 -0
- package/dist/src/safe-identifiers.js +96 -0
- package/dist/src/scope-rules.d.ts +4 -0
- package/dist/src/scope-rules.js +21 -0
- package/dist/src/server-paths.d.ts +22 -0
- package/dist/src/server-paths.js +219 -0
- package/dist/src/setup-version.d.ts +3 -0
- package/dist/src/setup-version.js +14 -0
- package/dist/src/task-record-reader.d.ts +3 -0
- package/dist/src/task-record-reader.js +9 -0
- package/dist/src/validator-registry.d.ts +27 -0
- package/dist/src/validator-registry.js +64 -0
- package/package.json +162 -10
- package/dist/src/engineReadModelReducer.js +0 -1780
- package/dist/src/rig-init-builder.js +0 -57
- package/dist/src/rigSelectors.js +0 -293
- package/dist/src/taskGraph.js +0 -64
- package/dist/src/taskGraphCodes.js +0 -26
- package/dist/src/taskGraphLayout.js +0 -374
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/runtime-provisioning-env.ts
|
|
3
|
+
import { delimiter, resolve as resolve2 } from "path";
|
|
4
|
+
|
|
5
|
+
// packages/core/src/runtime-paths.ts
|
|
6
|
+
import { existsSync, readdirSync, realpathSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
function uniq(values) {
|
|
9
|
+
return [...new Set(values)];
|
|
10
|
+
}
|
|
11
|
+
function resolveBunBinaryPath() {
|
|
12
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
13
|
+
if (explicit) {
|
|
14
|
+
return explicit;
|
|
15
|
+
}
|
|
16
|
+
const pathBun = normalizeExecutablePath(Bun.which("bun")?.trim());
|
|
17
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
18
|
+
return pathBun;
|
|
19
|
+
}
|
|
20
|
+
const home = process.env.HOME?.trim();
|
|
21
|
+
const fallbackCandidates = [
|
|
22
|
+
home ? resolve(home, ".bun/bin/bun") : "",
|
|
23
|
+
"/opt/homebrew/bin/bun",
|
|
24
|
+
"/usr/local/bin/bun",
|
|
25
|
+
"/usr/bin/bun"
|
|
26
|
+
];
|
|
27
|
+
for (const candidate of fallbackCandidates) {
|
|
28
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
29
|
+
if (normalized) {
|
|
30
|
+
return normalized;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
34
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
35
|
+
return execPath;
|
|
36
|
+
}
|
|
37
|
+
throw new Error("bun not found in PATH");
|
|
38
|
+
}
|
|
39
|
+
function resolveClaudeBinaryPath() {
|
|
40
|
+
const explicit = normalizeExecutablePath(process.env.RIG_CLAUDE_PATH?.trim());
|
|
41
|
+
if (explicit) {
|
|
42
|
+
return explicit;
|
|
43
|
+
}
|
|
44
|
+
const pathClaude = normalizeExecutablePath(Bun.which("claude")?.trim());
|
|
45
|
+
if (pathClaude && !looksLikeRuntimeGateway(pathClaude)) {
|
|
46
|
+
return pathClaude;
|
|
47
|
+
}
|
|
48
|
+
const home = process.env.HOME?.trim();
|
|
49
|
+
const fallbackCandidates = [
|
|
50
|
+
home ? resolve(home, ".local/bin/claude") : "",
|
|
51
|
+
home ? resolve(home, ".local/share/claude/local/claude") : "",
|
|
52
|
+
"/opt/homebrew/bin/claude",
|
|
53
|
+
"/usr/local/bin/claude",
|
|
54
|
+
"/usr/bin/claude"
|
|
55
|
+
];
|
|
56
|
+
for (const candidate of fallbackCandidates) {
|
|
57
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
58
|
+
if (normalized) {
|
|
59
|
+
return normalized;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new Error("claude not found in PATH");
|
|
63
|
+
}
|
|
64
|
+
function resolveBunInstallDir(bunBinaryPath = resolveBunBinaryPath()) {
|
|
65
|
+
return resolve(bunBinaryPath, "../..");
|
|
66
|
+
}
|
|
67
|
+
function resolveClaudeInstallDir() {
|
|
68
|
+
const realPath = resolveClaudeBinaryPath();
|
|
69
|
+
return resolve(realPath, "..");
|
|
70
|
+
}
|
|
71
|
+
function resolveNodeInstallDir() {
|
|
72
|
+
const preferredNode = resolvePreferredNodeBinary();
|
|
73
|
+
if (!preferredNode)
|
|
74
|
+
return null;
|
|
75
|
+
const explicitNode = process.env.RIG_NODE_BIN?.trim();
|
|
76
|
+
if (explicitNode && resolve(explicitNode) === resolve(preferredNode)) {
|
|
77
|
+
return preferredNode.endsWith("/bin/node") ? resolve(preferredNode, "../..") : resolve(preferredNode, "..");
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const realPath = realpathSync(preferredNode);
|
|
81
|
+
if (realPath.endsWith("/bin/node")) {
|
|
82
|
+
return resolve(realPath, "../..");
|
|
83
|
+
}
|
|
84
|
+
return resolve(realPath, "..");
|
|
85
|
+
} catch {
|
|
86
|
+
return resolve(preferredNode, "..");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function resolvePreferredNodeBinary() {
|
|
90
|
+
const candidates = [];
|
|
91
|
+
const envNode = process.env.RIG_NODE_BIN?.trim();
|
|
92
|
+
if (envNode) {
|
|
93
|
+
const explicit = resolve(envNode);
|
|
94
|
+
if (existsSync(explicit)) {
|
|
95
|
+
return explicit;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const nvmBin = process.env.NVM_BIN?.trim();
|
|
99
|
+
if (nvmBin) {
|
|
100
|
+
candidates.push(resolve(nvmBin, "node"));
|
|
101
|
+
}
|
|
102
|
+
const home = process.env.HOME?.trim();
|
|
103
|
+
if (home) {
|
|
104
|
+
const nvmVersionsDir = resolve(home, ".nvm/versions/node");
|
|
105
|
+
if (existsSync(nvmVersionsDir)) {
|
|
106
|
+
try {
|
|
107
|
+
const versionDirs = readdirSync(nvmVersionsDir).map((entry) => entry.trim()).filter((entry) => /^v\d+\.\d+\.\d+$/.test(entry)).sort((a, b) => Bun.semver.order(b.replace(/^v/, ""), a.replace(/^v/, "")));
|
|
108
|
+
for (const versionDir of versionDirs) {
|
|
109
|
+
candidates.push(resolve(nvmVersionsDir, versionDir, "bin/node"));
|
|
110
|
+
}
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const whichNode = Bun.which("node");
|
|
115
|
+
if (whichNode) {
|
|
116
|
+
candidates.push(whichNode);
|
|
117
|
+
}
|
|
118
|
+
const deduped = uniq(candidates.map((candidate) => resolve(candidate)));
|
|
119
|
+
const existing = deduped.filter((candidate) => existsSync(candidate));
|
|
120
|
+
if (existing.length === 0) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const stable = existing.find((candidate) => {
|
|
124
|
+
const major = inferNodeMajor(candidate);
|
|
125
|
+
return typeof major === "number" && major >= 18 && major <= 24;
|
|
126
|
+
});
|
|
127
|
+
if (stable) {
|
|
128
|
+
return stable;
|
|
129
|
+
}
|
|
130
|
+
return existing[0] ?? null;
|
|
131
|
+
}
|
|
132
|
+
function inferNodeMajor(nodeBinaryPath) {
|
|
133
|
+
const normalized = resolve(nodeBinaryPath).replace(/\\/g, "/");
|
|
134
|
+
const match = normalized.match(/(?:^|\/)(?:node-)?v?(\d+)\.\d+\.\d+(?:\/|$)/);
|
|
135
|
+
if (!match) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const major = Number.parseInt(match[1], 10);
|
|
139
|
+
return Number.isFinite(major) ? major : null;
|
|
140
|
+
}
|
|
141
|
+
function normalizeExecutablePath(candidate) {
|
|
142
|
+
if (!candidate) {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
const normalized = resolve(candidate);
|
|
146
|
+
if (!existsSync(normalized)) {
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
return realpathSync(normalized);
|
|
151
|
+
} catch {
|
|
152
|
+
return normalized;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
156
|
+
const normalized = resolve(candidate).replace(/\\/g, "/");
|
|
157
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// packages/core/src/runtime-provisioning-env.ts
|
|
161
|
+
function runtimeProvisioningEnv(baseEnv = process.env) {
|
|
162
|
+
const env = { ...baseEnv };
|
|
163
|
+
const realBash = baseEnv.RIG_REAL_BASH?.trim() || "/bin/bash";
|
|
164
|
+
const bunBinary = baseEnv.RIG_BUN_PATH?.trim() || resolveBunBinaryPath();
|
|
165
|
+
const bunDir = resolveBunInstallDir(bunBinary);
|
|
166
|
+
const claudeBinary = baseEnv.RIG_CLAUDE_PATH?.trim() || (() => {
|
|
167
|
+
try {
|
|
168
|
+
return resolveClaudeBinaryPath();
|
|
169
|
+
} catch {
|
|
170
|
+
return "";
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
const claudeDir = claudeBinary ? (() => {
|
|
174
|
+
try {
|
|
175
|
+
return resolveClaudeInstallDir();
|
|
176
|
+
} catch {
|
|
177
|
+
return resolve2(claudeBinary, "..");
|
|
178
|
+
}
|
|
179
|
+
})() : "";
|
|
180
|
+
const nodeDir = resolveNodeInstallDir();
|
|
181
|
+
const realHome = baseEnv.HOME?.trim();
|
|
182
|
+
const inheritedPath = (baseEnv.PATH ?? "").split(delimiter).map((entry) => entry.trim()).filter(Boolean).filter((entry) => !entry.endsWith("/.rig/bin") && !entry.endsWith("/rig/tools"));
|
|
183
|
+
const pathEntries = [
|
|
184
|
+
`${bunDir}/bin`,
|
|
185
|
+
claudeDir,
|
|
186
|
+
nodeDir ? `${nodeDir}/bin` : "",
|
|
187
|
+
realHome ? resolve2(realHome, ".local/bin") : "",
|
|
188
|
+
realHome ? resolve2(realHome, ".cargo/bin") : "",
|
|
189
|
+
...inheritedPath,
|
|
190
|
+
"/usr/local/bin",
|
|
191
|
+
"/usr/local/sbin",
|
|
192
|
+
"/opt/homebrew/bin",
|
|
193
|
+
"/opt/homebrew/sbin",
|
|
194
|
+
"/usr/bin",
|
|
195
|
+
"/bin",
|
|
196
|
+
"/usr/sbin",
|
|
197
|
+
"/sbin"
|
|
198
|
+
].filter(Boolean);
|
|
199
|
+
env.BASH = realBash;
|
|
200
|
+
env.SHELL = baseEnv.SHELL?.trim() || realBash;
|
|
201
|
+
env.PATH = [...new Set(pathEntries)].join(delimiter);
|
|
202
|
+
env.RIG_BUN_PATH = bunBinary;
|
|
203
|
+
if (claudeBinary) {
|
|
204
|
+
env.RIG_CLAUDE_PATH = claudeBinary;
|
|
205
|
+
}
|
|
206
|
+
env.PYTHON = env.PYTHON?.trim() || "python3";
|
|
207
|
+
const nodeGypPath = Bun.which("node-gyp");
|
|
208
|
+
if (nodeGypPath) {
|
|
209
|
+
env.npm_config_node_gyp = nodeGypPath;
|
|
210
|
+
}
|
|
211
|
+
delete env.RIG_BASH_ACTIVE;
|
|
212
|
+
delete env.RIG_BASH_MODE;
|
|
213
|
+
return env;
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
runtimeProvisioningEnv
|
|
217
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CommandExecutionResult, HarnessEventType, OutputMode, PolicyMode } from "@rig/contracts";
|
|
2
|
+
import type { EventBusLike } from "./runtime-events";
|
|
3
|
+
export type RunnerContext = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
outputMode: OutputMode;
|
|
7
|
+
runId: string;
|
|
8
|
+
policyMode?: PolicyMode;
|
|
9
|
+
eventBus: EventBusLike;
|
|
10
|
+
emitEvent: <TPayload extends Record<string, unknown>>(type: HarnessEventType, payload: TPayload) => Promise<void>;
|
|
11
|
+
runCommand: (parts: string[]) => Promise<CommandExecutionResult>;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type SafePathSegment = string & {
|
|
2
|
+
readonly __rigSafePathSegment: unique symbol;
|
|
3
|
+
};
|
|
4
|
+
export type SafeGitRefComponent = string & {
|
|
5
|
+
readonly __rigSafeGitRefComponent: unique symbol;
|
|
6
|
+
};
|
|
7
|
+
export type SafeRunId = string & {
|
|
8
|
+
readonly __rigSafeRunId: unique symbol;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Convert an external/display identifier into one deterministic filesystem
|
|
12
|
+
* segment. The original identifier remains available for UI/source APIs; this
|
|
13
|
+
* value is only for paths and receives a hash suffix to avoid slug collisions.
|
|
14
|
+
*/
|
|
15
|
+
export declare function safePathSegment(value: string, options?: {
|
|
16
|
+
fallback?: string;
|
|
17
|
+
maxLength?: number;
|
|
18
|
+
}): SafePathSegment;
|
|
19
|
+
/**
|
|
20
|
+
* Derive the canonical runtime id for a task. Floor-neutral because it is shared
|
|
21
|
+
* by the isolation plugin (provisioning) AND the runtime substrate (agent-wrapper,
|
|
22
|
+
* task-runtime-start hook) — a `safePathSegment`-based id derive with no domain deps.
|
|
23
|
+
*/
|
|
24
|
+
export declare function taskRuntimeId(taskId: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Mint an ephemeral, unique agent/runtime id. Floor-neutral (shared by the
|
|
27
|
+
* isolation plugin and the runtime substrate's queue) — a random suffix derive.
|
|
28
|
+
*/
|
|
29
|
+
export declare function agentId(prefix?: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Convert an arbitrary external identifier into a single git ref component.
|
|
32
|
+
* This is for constructed refs such as `rig/<component>`, not for accepting an
|
|
33
|
+
* operator-supplied complete branch name.
|
|
34
|
+
*/
|
|
35
|
+
export declare function safeGitRefComponent(value: string, options?: {
|
|
36
|
+
fallback?: string;
|
|
37
|
+
maxLength?: number;
|
|
38
|
+
}): SafeGitRefComponent;
|
|
39
|
+
export declare function isSafeRunId(value: string): boolean;
|
|
40
|
+
export declare function assertSafeRunId(value: string): SafeRunId;
|
|
41
|
+
export declare function isSafeGitBranchName(value: string): boolean;
|
|
42
|
+
export declare function assertSafeGitBranchName(value: string, label?: string): string;
|
|
43
|
+
export declare function isPathInsideRoot(root: string, candidate: string): boolean;
|
|
44
|
+
export declare function assertPathInsideRoot(root: string, candidate: string, label?: string): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/safe-identifiers.ts
|
|
3
|
+
import { createHash, randomUUID } from "crypto";
|
|
4
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
5
|
+
var MAX_SEGMENT_LENGTH = 80;
|
|
6
|
+
var MAX_RUN_ID_LENGTH = 128;
|
|
7
|
+
function hashSuffix(value, length = 10) {
|
|
8
|
+
return createHash("sha256").update(value).digest("hex").slice(0, length);
|
|
9
|
+
}
|
|
10
|
+
function asciiSlug(value, fallback) {
|
|
11
|
+
return value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/[.]{2,}/g, ".").replace(/^[.-]+|[.-]+$/g, "") || fallback;
|
|
12
|
+
}
|
|
13
|
+
function trimWithHash(base, original, maxLength) {
|
|
14
|
+
const suffix = hashSuffix(original);
|
|
15
|
+
const prefixLength = Math.max(1, maxLength - suffix.length - 1);
|
|
16
|
+
const prefix = base.slice(0, prefixLength).replace(/[.-]+$/g, "") || "id";
|
|
17
|
+
return `${prefix}-${suffix}`;
|
|
18
|
+
}
|
|
19
|
+
function safePathSegment(value, options = {}) {
|
|
20
|
+
const original = String(value);
|
|
21
|
+
const trimmed = original.trim();
|
|
22
|
+
const fallback = asciiSlug(options.fallback ?? "id", "id");
|
|
23
|
+
const slug = asciiSlug(original, fallback);
|
|
24
|
+
const maxLength = Math.max(24, Math.trunc(options.maxLength ?? MAX_SEGMENT_LENGTH));
|
|
25
|
+
if (trimmed === slug && slug.length <= maxLength && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(slug) && slug !== "." && slug !== "..") {
|
|
26
|
+
return slug;
|
|
27
|
+
}
|
|
28
|
+
return trimWithHash(slug, original, maxLength);
|
|
29
|
+
}
|
|
30
|
+
function taskRuntimeId(taskId) {
|
|
31
|
+
return `task-${safePathSegment(taskId, { fallback: "task", maxLength: 72 })}`;
|
|
32
|
+
}
|
|
33
|
+
function agentId(prefix = "agent") {
|
|
34
|
+
return `${prefix}-${randomUUID().slice(0, 8)}`;
|
|
35
|
+
}
|
|
36
|
+
function safeGitRefComponent(value, options = {}) {
|
|
37
|
+
const segment = safePathSegment(value, { fallback: options.fallback ?? "ref", maxLength: options.maxLength ?? MAX_SEGMENT_LENGTH });
|
|
38
|
+
const safe = String(segment).replace(/\.lock$/i, "-lock").replace(/@\{/g, "-").replace(/^[.-]+|[.-]+$/g, "") || `ref-${hashSuffix(value)}`;
|
|
39
|
+
return safe.slice(0, Math.max(24, Math.trunc(options.maxLength ?? MAX_SEGMENT_LENGTH)));
|
|
40
|
+
}
|
|
41
|
+
function isSafeRunId(value) {
|
|
42
|
+
const trimmed = value.trim();
|
|
43
|
+
return trimmed.length > 0 && trimmed.length <= MAX_RUN_ID_LENGTH && trimmed !== "." && trimmed !== ".." && !trimmed.startsWith("-") && !trimmed.includes("/") && !trimmed.includes("\\") && !trimmed.includes("\x00") && !/[\x00-\x1F\x7F]/.test(trimmed) && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(trimmed);
|
|
44
|
+
}
|
|
45
|
+
function assertSafeRunId(value) {
|
|
46
|
+
if (!isSafeRunId(value)) {
|
|
47
|
+
throw new Error("Invalid runId: expected a single safe identifier segment.");
|
|
48
|
+
}
|
|
49
|
+
return value.trim();
|
|
50
|
+
}
|
|
51
|
+
function isSafeGitBranchName(value) {
|
|
52
|
+
const ref = value.trim();
|
|
53
|
+
if (!ref || ref.length > 255)
|
|
54
|
+
return false;
|
|
55
|
+
if (ref.startsWith("-") || ref.startsWith("/") || ref.endsWith("/") || ref.endsWith("."))
|
|
56
|
+
return false;
|
|
57
|
+
if (ref.includes("..") || ref.includes("@{") || ref.includes("\\") || ref.includes("//"))
|
|
58
|
+
return false;
|
|
59
|
+
if (/[\x00-\x20\x7F~^:?*[\]]/.test(ref))
|
|
60
|
+
return false;
|
|
61
|
+
if (ref.split("/").some((part) => !part || part === "." || part === ".." || part.startsWith(".") || part.endsWith(".lock")))
|
|
62
|
+
return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function assertSafeGitBranchName(value, label = "git branch") {
|
|
66
|
+
const ref = value.trim();
|
|
67
|
+
if (!isSafeGitBranchName(ref)) {
|
|
68
|
+
throw new Error(`${label} is not a safe git branch name.`);
|
|
69
|
+
}
|
|
70
|
+
return ref;
|
|
71
|
+
}
|
|
72
|
+
function isPathInsideRoot(root, candidate) {
|
|
73
|
+
const safeRoot = resolve(root);
|
|
74
|
+
const resolvedCandidate = resolve(candidate);
|
|
75
|
+
const relativeToRoot = relative(safeRoot, resolvedCandidate);
|
|
76
|
+
return relativeToRoot === "" || !relativeToRoot.startsWith("..") && !isAbsolute(relativeToRoot);
|
|
77
|
+
}
|
|
78
|
+
function assertPathInsideRoot(root, candidate, label = "path") {
|
|
79
|
+
const resolvedCandidate = resolve(candidate);
|
|
80
|
+
if (!isPathInsideRoot(root, resolvedCandidate)) {
|
|
81
|
+
throw new Error(`${label} escapes root.`);
|
|
82
|
+
}
|
|
83
|
+
return resolvedCandidate;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
taskRuntimeId,
|
|
87
|
+
safePathSegment,
|
|
88
|
+
safeGitRefComponent,
|
|
89
|
+
isSafeRunId,
|
|
90
|
+
isSafeGitBranchName,
|
|
91
|
+
isPathInsideRoot,
|
|
92
|
+
assertSafeRunId,
|
|
93
|
+
assertSafeGitBranchName,
|
|
94
|
+
assertPathInsideRoot,
|
|
95
|
+
agentId
|
|
96
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ScopeNormalizationRules } from "@rig/contracts";
|
|
2
|
+
export declare function setScopeRules(rules: ScopeNormalizationRules | undefined | null): void;
|
|
3
|
+
export declare function getScopeRules(): ScopeNormalizationRules | null;
|
|
4
|
+
export declare function clearScopeRules(): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/scope-rules.ts
|
|
3
|
+
var SCOPE_RULES_KEY = Symbol.for("rig.scope-normalization-rules");
|
|
4
|
+
function scopeRulesState() {
|
|
5
|
+
const global = globalThis;
|
|
6
|
+
return global[SCOPE_RULES_KEY] ??= { rules: null };
|
|
7
|
+
}
|
|
8
|
+
function setScopeRules(rules) {
|
|
9
|
+
scopeRulesState().rules = rules ?? null;
|
|
10
|
+
}
|
|
11
|
+
function getScopeRules() {
|
|
12
|
+
return scopeRulesState().rules;
|
|
13
|
+
}
|
|
14
|
+
function clearScopeRules() {
|
|
15
|
+
scopeRulesState().rules = null;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
setScopeRules,
|
|
19
|
+
getScopeRules,
|
|
20
|
+
clearScopeRules
|
|
21
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { normalizeOptionalString, normalizeOptionalBoolean, resolveAuthorityPaths, resolveAuthorityStateRoot, resolveAuthorityStateDir, resolveAuthorityProjectStateDir, } from "./authority-paths";
|
|
2
|
+
export type { AuthorityPaths } from "./authority-paths";
|
|
3
|
+
export type RigStatePaths = {
|
|
4
|
+
readonly stateRoot: string;
|
|
5
|
+
readonly stateDir: string;
|
|
6
|
+
readonly logsDir: string;
|
|
7
|
+
readonly controlPlaneEventsFile: string;
|
|
8
|
+
readonly notificationsFile: string;
|
|
9
|
+
readonly keybindingsPath: string;
|
|
10
|
+
};
|
|
11
|
+
declare function isPathWithin(root: string, candidate: string): boolean;
|
|
12
|
+
export declare function resolveRigStatePaths(projectRoot: string): RigStatePaths;
|
|
13
|
+
export declare function resolveRigProjectRoot(input?: {
|
|
14
|
+
readonly envProjectRoot?: string | null;
|
|
15
|
+
readonly cwd?: string;
|
|
16
|
+
readonly fallbackRoot?: string;
|
|
17
|
+
}): string;
|
|
18
|
+
export declare function resolveAuthorityRuntimeDir(projectRoot: string): string;
|
|
19
|
+
export declare function listAuthorityRunRoots(projectRoot: string): string[];
|
|
20
|
+
export declare function listAuthorityRuntimeAgentsRoots(projectRoot: string): string[];
|
|
21
|
+
export declare function resolveAuthorityRunDir(projectRoot: string, runId: string): string;
|
|
22
|
+
export { isPathWithin };
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/server-paths.ts
|
|
3
|
+
import { existsSync as existsSync3 } from "fs";
|
|
4
|
+
import { dirname as dirname3, resolve as resolve3, relative } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/core/src/layout.ts
|
|
7
|
+
import {
|
|
8
|
+
RIG_DEFINITION_DIRNAME,
|
|
9
|
+
RIG_STATE_DIRNAME
|
|
10
|
+
} from "@rig/contracts";
|
|
11
|
+
|
|
12
|
+
// packages/core/src/checkout-root.ts
|
|
13
|
+
import { dirname, resolve } from "path";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
function findNearestGitCheckoutRoot(startDir) {
|
|
16
|
+
let current = resolve(startDir);
|
|
17
|
+
for (;; ) {
|
|
18
|
+
if (existsSync(resolve(current, ".git")))
|
|
19
|
+
return current;
|
|
20
|
+
const parent = dirname(current);
|
|
21
|
+
if (parent === current)
|
|
22
|
+
return null;
|
|
23
|
+
current = parent;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function resolveCheckoutRoot(projectRoot) {
|
|
27
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
28
|
+
const explicit = process.env.MONOREPO_ROOT?.trim();
|
|
29
|
+
if (explicit) {
|
|
30
|
+
const explicitRoot = resolve(explicit);
|
|
31
|
+
const gitRoot = findNearestGitCheckoutRoot(explicitRoot);
|
|
32
|
+
if (gitRoot)
|
|
33
|
+
return gitRoot;
|
|
34
|
+
throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there or above it.`);
|
|
35
|
+
}
|
|
36
|
+
return findNearestGitCheckoutRoot(normalizedProjectRoot) ?? normalizedProjectRoot;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// packages/core/src/layout.ts
|
|
40
|
+
var resolveMonorepoRoot = resolveCheckoutRoot;
|
|
41
|
+
|
|
42
|
+
// packages/core/src/safe-identifiers.ts
|
|
43
|
+
var MAX_RUN_ID_LENGTH = 128;
|
|
44
|
+
function isSafeRunId(value) {
|
|
45
|
+
const trimmed = value.trim();
|
|
46
|
+
return trimmed.length > 0 && trimmed.length <= MAX_RUN_ID_LENGTH && trimmed !== "." && trimmed !== ".." && !trimmed.startsWith("-") && !trimmed.includes("/") && !trimmed.includes("\\") && !trimmed.includes("\x00") && !/[\x00-\x1F\x7F]/.test(trimmed) && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(trimmed);
|
|
47
|
+
}
|
|
48
|
+
function assertSafeRunId(value) {
|
|
49
|
+
if (!isSafeRunId(value)) {
|
|
50
|
+
throw new Error("Invalid runId: expected a single safe identifier segment.");
|
|
51
|
+
}
|
|
52
|
+
return value.trim();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// packages/core/src/authority-paths.ts
|
|
56
|
+
import { existsSync as existsSync2 } from "fs";
|
|
57
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
58
|
+
function normalizeOptionalString(value) {
|
|
59
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
60
|
+
}
|
|
61
|
+
function normalizeOptionalBoolean(value, fallback) {
|
|
62
|
+
if (typeof value !== "string") {
|
|
63
|
+
return fallback;
|
|
64
|
+
}
|
|
65
|
+
const normalized = value.trim().toLowerCase();
|
|
66
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
function resolveAuthorityPaths(projectRoot) {
|
|
75
|
+
const normalizedRoot = resolve2(projectRoot);
|
|
76
|
+
const stateRoot = resolveAuthorityStateRoot(normalizedRoot);
|
|
77
|
+
const stateDir = resolveAuthorityStateDir(normalizedRoot);
|
|
78
|
+
return {
|
|
79
|
+
projectRoot: normalizedRoot,
|
|
80
|
+
harnessRoot: resolve2(normalizedRoot, "rig"),
|
|
81
|
+
runsDir: resolve2(stateRoot, "runs"),
|
|
82
|
+
remoteDir: resolve2(stateRoot, "remote"),
|
|
83
|
+
stateDir,
|
|
84
|
+
remoteEndpointsPath: resolve2(stateRoot, "remote", "endpoints.toml"),
|
|
85
|
+
remoteSecretsPath: resolve2(stateDir, "remote-secrets.toml")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function resolveAuthorityStateRoot(projectRoot) {
|
|
89
|
+
const normalizedRoot = resolve2(projectRoot);
|
|
90
|
+
const taskWorkspace = normalizeOptionalString(process.env.RIG_TASK_WORKSPACE);
|
|
91
|
+
if (taskWorkspace) {
|
|
92
|
+
return resolve2(taskWorkspace, ".rig");
|
|
93
|
+
}
|
|
94
|
+
const stateDir = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
95
|
+
if (stateDir) {
|
|
96
|
+
return dirname2(resolve2(stateDir));
|
|
97
|
+
}
|
|
98
|
+
const logsDir = normalizeOptionalString(process.env.RIG_LOGS_DIR);
|
|
99
|
+
if (logsDir) {
|
|
100
|
+
return dirname2(resolve2(logsDir));
|
|
101
|
+
}
|
|
102
|
+
const sessionFile = normalizeOptionalString(process.env.RIG_SESSION_FILE);
|
|
103
|
+
if (sessionFile) {
|
|
104
|
+
return dirname2(dirname2(resolve2(sessionFile)));
|
|
105
|
+
}
|
|
106
|
+
const projectStateRoot = resolve2(normalizedRoot, ".rig");
|
|
107
|
+
if (existsSync2(projectStateRoot)) {
|
|
108
|
+
return projectStateRoot;
|
|
109
|
+
}
|
|
110
|
+
return resolve2(normalizedRoot, ".rig");
|
|
111
|
+
}
|
|
112
|
+
function resolveAuthorityStateDir(projectRoot) {
|
|
113
|
+
const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
114
|
+
if (explicit) {
|
|
115
|
+
return resolve2(explicit);
|
|
116
|
+
}
|
|
117
|
+
return resolve2(resolveAuthorityStateRoot(projectRoot), "state");
|
|
118
|
+
}
|
|
119
|
+
function resolveAuthorityProjectStateDir(projectRoot) {
|
|
120
|
+
const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
121
|
+
if (explicit) {
|
|
122
|
+
return resolve2(explicit);
|
|
123
|
+
}
|
|
124
|
+
return resolve2(resolve2(projectRoot), ".rig", "state");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// packages/core/src/server-paths.ts
|
|
128
|
+
function isPathWithin(root, candidate) {
|
|
129
|
+
const relativePath = relative(root, candidate);
|
|
130
|
+
return relativePath === "" || !relativePath.startsWith("..") && !relativePath.startsWith("/") && !relativePath.startsWith("\\");
|
|
131
|
+
}
|
|
132
|
+
function uniquePaths(paths) {
|
|
133
|
+
const seen = new Set;
|
|
134
|
+
const result = [];
|
|
135
|
+
for (const value of paths) {
|
|
136
|
+
const normalized = normalizeOptionalString(value);
|
|
137
|
+
if (!normalized || seen.has(normalized))
|
|
138
|
+
continue;
|
|
139
|
+
seen.add(normalized);
|
|
140
|
+
result.push(normalized);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
function resolveRigStatePaths(projectRoot) {
|
|
145
|
+
const taskWorkspace = normalizeOptionalString(process.env.RIG_TASK_WORKSPACE);
|
|
146
|
+
const explicitStateDir = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
147
|
+
const explicitLogsDir = normalizeOptionalString(process.env.RIG_LOGS_DIR);
|
|
148
|
+
const explicitSessionFile = normalizeOptionalString(process.env.RIG_SESSION_FILE);
|
|
149
|
+
const hostStateRoot = resolve3(projectRoot, ".rig");
|
|
150
|
+
const monorepoRoot = resolveMonorepoRoot(projectRoot);
|
|
151
|
+
const monorepoStateRoot = resolve3(monorepoRoot, ".rig");
|
|
152
|
+
const stateRoot = taskWorkspace ? resolve3(taskWorkspace, ".rig") : explicitStateDir ? dirname3(resolve3(explicitStateDir)) : explicitLogsDir ? dirname3(resolve3(explicitLogsDir)) : explicitSessionFile ? dirname3(dirname3(resolve3(explicitSessionFile))) : existsSync3(hostStateRoot) ? hostStateRoot : monorepoStateRoot;
|
|
153
|
+
const stateDir = explicitStateDir ? resolve3(explicitStateDir) : resolve3(stateRoot, "state");
|
|
154
|
+
const logsDir = explicitLogsDir ? resolve3(explicitLogsDir) : resolve3(stateRoot, "logs");
|
|
155
|
+
return {
|
|
156
|
+
stateRoot,
|
|
157
|
+
stateDir,
|
|
158
|
+
logsDir,
|
|
159
|
+
controlPlaneEventsFile: resolve3(logsDir, "control-plane.events.jsonl"),
|
|
160
|
+
notificationsFile: resolve3(projectRoot, "rig", "notifications", "targets.json"),
|
|
161
|
+
keybindingsPath: resolve3(projectRoot, "rig", "keybindings.json")
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function resolveRigProjectRoot(input) {
|
|
165
|
+
if (input?.envProjectRoot) {
|
|
166
|
+
return input.envProjectRoot;
|
|
167
|
+
}
|
|
168
|
+
const cwd = input?.cwd ?? process.cwd();
|
|
169
|
+
const fallbackRoot = input?.fallbackRoot ?? cwd;
|
|
170
|
+
const candidates = [cwd, fallbackRoot];
|
|
171
|
+
for (const candidate of candidates) {
|
|
172
|
+
if (existsSync3(resolve3(candidate, RIG_DEFINITION_DIRNAME))) {
|
|
173
|
+
return candidate;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return fallbackRoot;
|
|
177
|
+
}
|
|
178
|
+
function resolveAuthorityRuntimeDir(projectRoot) {
|
|
179
|
+
return resolve3(resolveAuthorityStateRoot(projectRoot), "runtime");
|
|
180
|
+
}
|
|
181
|
+
function listAuthorityRunRoots(projectRoot) {
|
|
182
|
+
const normalizedRoot = resolve3(projectRoot);
|
|
183
|
+
const monorepoRoot = resolveMonorepoRoot(normalizedRoot);
|
|
184
|
+
return uniquePaths([
|
|
185
|
+
resolve3(resolveAuthorityStateRoot(normalizedRoot), "runs"),
|
|
186
|
+
resolve3(monorepoRoot, ".rig", "runs")
|
|
187
|
+
]);
|
|
188
|
+
}
|
|
189
|
+
function listAuthorityRuntimeAgentsRoots(projectRoot) {
|
|
190
|
+
return uniquePaths([
|
|
191
|
+
resolve3(resolveAuthorityRuntimeDir(projectRoot), "agents")
|
|
192
|
+
]);
|
|
193
|
+
}
|
|
194
|
+
function resolveAuthorityRunDir(projectRoot, runId) {
|
|
195
|
+
const safeRunId = assertSafeRunId(runId);
|
|
196
|
+
const roots = listAuthorityRunRoots(projectRoot);
|
|
197
|
+
for (const runsDir of roots) {
|
|
198
|
+
const candidate = resolve3(runsDir, safeRunId);
|
|
199
|
+
if (existsSync3(candidate)) {
|
|
200
|
+
return candidate;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return resolve3(roots[0] ?? resolveAuthorityPaths(projectRoot).runsDir, safeRunId);
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
resolveRigStatePaths,
|
|
207
|
+
resolveRigProjectRoot,
|
|
208
|
+
resolveAuthorityStateRoot,
|
|
209
|
+
resolveAuthorityStateDir,
|
|
210
|
+
resolveAuthorityRuntimeDir,
|
|
211
|
+
resolveAuthorityRunDir,
|
|
212
|
+
resolveAuthorityProjectStateDir,
|
|
213
|
+
resolveAuthorityPaths,
|
|
214
|
+
normalizeOptionalString,
|
|
215
|
+
normalizeOptionalBoolean,
|
|
216
|
+
listAuthorityRuntimeAgentsRoots,
|
|
217
|
+
listAuthorityRunRoots,
|
|
218
|
+
isPathWithin
|
|
219
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/setup-version.ts
|
|
3
|
+
var MIN_SUPPORTED_BUN_VERSION = "1.3.11";
|
|
4
|
+
function normalizeSemverCore(version) {
|
|
5
|
+
return version.replace(/^v/, "").split("-")[0]?.split("+")[0] ?? version;
|
|
6
|
+
}
|
|
7
|
+
function isSupportedBunVersion(version) {
|
|
8
|
+
return Bun.semver.order(normalizeSemverCore(version), MIN_SUPPORTED_BUN_VERSION) >= 0;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
normalizeSemverCore,
|
|
12
|
+
isSupportedBunVersion,
|
|
13
|
+
MIN_SUPPORTED_BUN_VERSION
|
|
14
|
+
};
|