@h-rig/core 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/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 +6 -0
- package/dist/src/baked-secrets.js +121 -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/{task-io.js → capability-loaders.js} +238 -196
- 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 +1 -1
- package/dist/src/config.js +8 -5
- package/dist/src/declarative-config.d.ts +14 -0
- package/dist/src/declarative-config.js +82 -0
- package/dist/src/define-config.d.ts +2 -1
- package/dist/src/define-config.js +8 -5
- 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 +18 -0
- package/dist/src/harness-paths.js +141 -0
- package/dist/src/hook-materializer.d.ts +72 -0
- package/dist/src/hook-materializer.js +274 -0
- package/dist/src/hook-runner.d.ts +48 -0
- package/dist/src/hook-runner.js +752 -0
- package/dist/src/hook-runtime.d.ts +11 -0
- package/dist/src/hook-runtime.js +307 -0
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +120 -6
- package/dist/src/json-files.d.ts +9 -0
- package/dist/src/json-files.js +125 -0
- package/dist/src/kernel-entrypoint.d.ts +22 -0
- package/dist/src/kernel-entrypoint.js +537 -0
- package/dist/src/layout.d.ts +10 -0
- package/dist/src/layout.js +144 -0
- package/dist/src/load-config.js +150 -51
- package/dist/src/placement.d.ts +50 -0
- package/dist/src/placement.js +990 -0
- package/dist/src/plugin-host-context.d.ts +66 -0
- package/dist/src/plugin-host-context.js +1270 -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 +6 -1
- package/dist/src/plugin-host.js +51 -0
- package/dist/src/plugin-runtime.d.ts +45 -6
- package/dist/src/profile-ops.d.ts +9 -0
- package/dist/src/profile-ops.js +252 -0
- package/dist/src/project-plugins.d.ts +9 -12
- package/dist/src/project-plugins.js +249 -62
- package/dist/src/remote-config.d.ts +183 -0
- package/dist/src/remote-config.js +568 -0
- package/dist/src/root-resolver.d.ts +5 -0
- package/dist/src/root-resolver.js +69 -0
- package/dist/src/run-provisioning.d.ts +58 -0
- package/dist/src/run-provisioning.js +128 -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 +212 -0
- package/dist/src/runtime-overlay.d.ts +11 -0
- package/dist/src/runtime-overlay.js +71 -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 +26 -0
- package/dist/src/server-paths.js +308 -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 +136 -6
- package/dist/src/task-io.d.ts +0 -54
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HookEvent, type HookImplementation } from "@rig/contracts";
|
|
2
|
+
export interface TypedHookOptions {
|
|
3
|
+
readonly event: HookEvent;
|
|
4
|
+
readonly projectRoot?: string;
|
|
5
|
+
readonly taskId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveBunCliInvocation(): {
|
|
8
|
+
command: string;
|
|
9
|
+
env: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
export declare function runTypedHook(fn: HookImplementation, opts: TypedHookOptions): Promise<never>;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/hook-runtime.ts
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, writeSync } from "fs";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME } from "@rig/contracts";
|
|
6
|
+
function normalizeBuildConfig(value) {
|
|
7
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
11
|
+
}
|
|
12
|
+
function readBuildConfigForCoreHooks() {
|
|
13
|
+
if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
|
|
14
|
+
return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
|
|
15
|
+
}
|
|
16
|
+
const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
|
|
17
|
+
if (!raw) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return normalizeBuildConfig(JSON.parse(raw));
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var BUILD_CONFIG = readBuildConfigForCoreHooks();
|
|
27
|
+
var BAKED_PROJECT_ROOT = BUILD_CONFIG.AGENT_PROJECT_ROOT ?? "";
|
|
28
|
+
var BAKED_TASK_ID = BUILD_CONFIG.AGENT_TASK_ID ?? "";
|
|
29
|
+
var BAKED_BUN_PATH = BUILD_CONFIG.AGENT_BUN_PATH ?? "";
|
|
30
|
+
var RUNTIME_CONTEXT_ENV = "RIG_RUNTIME_CONTEXT_FILE";
|
|
31
|
+
function isPlainObject(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
function isStringArray(value) {
|
|
35
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
36
|
+
}
|
|
37
|
+
function loadHookContextFromEnv() {
|
|
38
|
+
const contextFile = process.env[RUNTIME_CONTEXT_ENV]?.trim();
|
|
39
|
+
let filePath = contextFile || "";
|
|
40
|
+
if (!filePath) {
|
|
41
|
+
let current = resolve(process.cwd());
|
|
42
|
+
while (true) {
|
|
43
|
+
const candidate = resolve(current, "runtime-context.json");
|
|
44
|
+
if (existsSync(candidate)) {
|
|
45
|
+
filePath = candidate;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const parent = resolve(current, "..");
|
|
49
|
+
if (parent === current) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
current = parent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!filePath || !existsSync(filePath)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
60
|
+
if (!isPlainObject(raw)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const taskId = typeof raw.taskId === "string" ? raw.taskId : "";
|
|
64
|
+
const role = typeof raw.role === "string" ? raw.role : "";
|
|
65
|
+
const scopes = isStringArray(raw.scopes) ? raw.scopes : [];
|
|
66
|
+
const validation = isStringArray(raw.validation) ? raw.validation : [];
|
|
67
|
+
const hostProjectRoot = typeof raw.hostProjectRoot === "string" ? raw.hostProjectRoot : undefined;
|
|
68
|
+
const monorepoMainRoot = typeof raw.monorepoMainRoot === "string" ? raw.monorepoMainRoot : undefined;
|
|
69
|
+
const stateDir = typeof raw.stateDir === "string" ? raw.stateDir : "";
|
|
70
|
+
const policyFile = typeof raw.policyFile === "string" ? raw.policyFile : "";
|
|
71
|
+
if (!taskId || !stateDir) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
taskId,
|
|
76
|
+
role,
|
|
77
|
+
scopes,
|
|
78
|
+
validation,
|
|
79
|
+
hostProjectRoot,
|
|
80
|
+
monorepoMainRoot,
|
|
81
|
+
stateDir,
|
|
82
|
+
policyFile
|
|
83
|
+
};
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
var cachedContext;
|
|
89
|
+
function getContext() {
|
|
90
|
+
if (cachedContext !== undefined) {
|
|
91
|
+
return cachedContext;
|
|
92
|
+
}
|
|
93
|
+
cachedContext = loadHookContextFromEnv();
|
|
94
|
+
return cachedContext;
|
|
95
|
+
}
|
|
96
|
+
function resolveProjectRoot() {
|
|
97
|
+
const ctx = getContext();
|
|
98
|
+
if (ctx?.hostProjectRoot)
|
|
99
|
+
return ctx.hostProjectRoot;
|
|
100
|
+
if (BAKED_PROJECT_ROOT) {
|
|
101
|
+
return BAKED_PROJECT_ROOT;
|
|
102
|
+
}
|
|
103
|
+
if (process.env.PROJECT_RIG_ROOT) {
|
|
104
|
+
return process.env.PROJECT_RIG_ROOT;
|
|
105
|
+
}
|
|
106
|
+
const candidates = [process.cwd(), resolve(import.meta.dirname, "../..")];
|
|
107
|
+
for (const candidate of candidates) {
|
|
108
|
+
if (existsSync(resolve(candidate, RIG_DEFINITION_DIRNAME)) || existsSync(resolve(candidate, RIG_STATE_DIRNAME))) {
|
|
109
|
+
return candidate;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return resolve(import.meta.dirname, "../..");
|
|
113
|
+
}
|
|
114
|
+
function resolveTaskIdForHook(_projectRoot) {
|
|
115
|
+
const ctx = getContext();
|
|
116
|
+
if (ctx)
|
|
117
|
+
return ctx.taskId;
|
|
118
|
+
if (BAKED_TASK_ID) {
|
|
119
|
+
return BAKED_TASK_ID;
|
|
120
|
+
}
|
|
121
|
+
const fromEnv = (process.env.RIG_TASK_ID || "").trim();
|
|
122
|
+
if (fromEnv) {
|
|
123
|
+
return fromEnv;
|
|
124
|
+
}
|
|
125
|
+
const runtimeId = (process.env.RIG_TASK_RUNTIME_ID || "").trim();
|
|
126
|
+
if (runtimeId.startsWith("task-") && runtimeId.length > "task-".length) {
|
|
127
|
+
return runtimeId.slice("task-".length);
|
|
128
|
+
}
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
async function readHookInput() {
|
|
132
|
+
let text = "";
|
|
133
|
+
const inputFile = process.env.RIG_HOOK_INPUT_FILE?.trim();
|
|
134
|
+
if (inputFile) {
|
|
135
|
+
text = readFileSync(inputFile, "utf-8");
|
|
136
|
+
} else {
|
|
137
|
+
try {
|
|
138
|
+
text = readFileSync("/dev/stdin", "utf-8");
|
|
139
|
+
} catch {
|
|
140
|
+
text = readFileSync(0, "utf-8");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!text.trim()) {
|
|
144
|
+
return { input: {}, valid: true, hadPayload: false };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return {
|
|
148
|
+
input: JSON.parse(text),
|
|
149
|
+
valid: true,
|
|
150
|
+
hadPayload: true
|
|
151
|
+
};
|
|
152
|
+
} catch {
|
|
153
|
+
return { input: {}, valid: false, hadPayload: true };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function extractToolFilePaths(toolName, input) {
|
|
157
|
+
const paths = [];
|
|
158
|
+
const add = (value) => {
|
|
159
|
+
if (typeof value === "string" && value.trim()) {
|
|
160
|
+
paths.push(value.trim());
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
164
|
+
add(input.file_path);
|
|
165
|
+
add(input.path);
|
|
166
|
+
} else if (toolName === "Glob") {
|
|
167
|
+
add(input.path);
|
|
168
|
+
} else if (toolName === "Grep") {
|
|
169
|
+
add(input.path);
|
|
170
|
+
} else {
|
|
171
|
+
add(input.file_path);
|
|
172
|
+
add(input.path);
|
|
173
|
+
}
|
|
174
|
+
return paths;
|
|
175
|
+
}
|
|
176
|
+
function buildPluginHookContext(parsed, opts) {
|
|
177
|
+
const toolName = parsed.input.tool_name;
|
|
178
|
+
const toolInput = parsed.input.tool_input ?? {};
|
|
179
|
+
return {
|
|
180
|
+
event: opts.event,
|
|
181
|
+
toolName,
|
|
182
|
+
toolInput,
|
|
183
|
+
filePaths: toolName ? extractToolFilePaths(toolName, toolInput) : [],
|
|
184
|
+
projectRoot: opts.projectRoot,
|
|
185
|
+
taskId: opts.taskId
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function hookResultToProtocol(result) {
|
|
189
|
+
if (result.decision === "block") {
|
|
190
|
+
const lines = [`BLOCKED: ${result.reason ?? "blocked by plugin hook"}`];
|
|
191
|
+
if (result.systemMessage) {
|
|
192
|
+
lines.push(result.systemMessage);
|
|
193
|
+
}
|
|
194
|
+
return { exitCode: 1, stdout: `${lines.join(`
|
|
195
|
+
`)}
|
|
196
|
+
` };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
exitCode: 0,
|
|
200
|
+
stdout: result.systemMessage ? `${result.systemMessage}
|
|
201
|
+
` : ""
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function normalizeExecutablePath(candidate) {
|
|
205
|
+
if (!candidate) {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
const normalized = resolve(candidate);
|
|
209
|
+
if (!existsSync(normalized)) {
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
return realpathSync(normalized);
|
|
214
|
+
} catch {
|
|
215
|
+
return normalized;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function looksLikeRuntimeGateway(candidate) {
|
|
219
|
+
const normalized = resolve(candidate).replace(/\\/g, "/");
|
|
220
|
+
return normalized.includes("/.rig/bin/") || normalized.endsWith("/rig-shell") || normalized.endsWith("/rig-agent");
|
|
221
|
+
}
|
|
222
|
+
function resolveBunBinaryPath() {
|
|
223
|
+
const explicit = normalizeExecutablePath(process.env.RIG_BUN_PATH?.trim());
|
|
224
|
+
if (explicit) {
|
|
225
|
+
return explicit;
|
|
226
|
+
}
|
|
227
|
+
const bunGlobal = globalThis.Bun;
|
|
228
|
+
const bunWhich = bunGlobal?.which?.("bun");
|
|
229
|
+
const pathBun = normalizeExecutablePath(bunWhich?.trim());
|
|
230
|
+
if (pathBun && !looksLikeRuntimeGateway(pathBun)) {
|
|
231
|
+
return pathBun;
|
|
232
|
+
}
|
|
233
|
+
const home = process.env.HOME?.trim();
|
|
234
|
+
const fallbackCandidates = [
|
|
235
|
+
home ? resolve(home, ".bun/bin/bun") : "",
|
|
236
|
+
"/opt/homebrew/bin/bun",
|
|
237
|
+
"/usr/local/bin/bun",
|
|
238
|
+
"/usr/bin/bun"
|
|
239
|
+
];
|
|
240
|
+
for (const candidate of fallbackCandidates) {
|
|
241
|
+
const normalized = normalizeExecutablePath(candidate);
|
|
242
|
+
if (normalized) {
|
|
243
|
+
return normalized;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const execPath = normalizeExecutablePath(process.execPath?.trim());
|
|
247
|
+
if (execPath && !looksLikeRuntimeGateway(execPath)) {
|
|
248
|
+
return execPath;
|
|
249
|
+
}
|
|
250
|
+
throw new Error("bun not found in PATH");
|
|
251
|
+
}
|
|
252
|
+
function resolveBunCliInvocation() {
|
|
253
|
+
if (BAKED_BUN_PATH) {
|
|
254
|
+
return {
|
|
255
|
+
command: BAKED_BUN_PATH,
|
|
256
|
+
env: {}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (process.env.RIG_BUN_PATH?.trim()) {
|
|
260
|
+
return {
|
|
261
|
+
command: process.env.RIG_BUN_PATH.trim(),
|
|
262
|
+
env: {}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const systemBun = resolveBunBinaryPath();
|
|
267
|
+
return {
|
|
268
|
+
command: systemBun,
|
|
269
|
+
env: {}
|
|
270
|
+
};
|
|
271
|
+
} catch {}
|
|
272
|
+
if (process.execPath?.trim()) {
|
|
273
|
+
return {
|
|
274
|
+
command: process.execPath,
|
|
275
|
+
env: { BUN_BE_BUN: "1" }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return { command: "bun", env: {} };
|
|
279
|
+
}
|
|
280
|
+
async function runTypedHook(fn, opts) {
|
|
281
|
+
const parsed = await readHookInput();
|
|
282
|
+
const projectRoot = opts.projectRoot ?? resolveProjectRoot();
|
|
283
|
+
const taskId = opts.taskId ?? resolveTaskIdForHook(projectRoot);
|
|
284
|
+
const ctx = buildPluginHookContext(parsed, {
|
|
285
|
+
event: opts.event,
|
|
286
|
+
projectRoot,
|
|
287
|
+
taskId
|
|
288
|
+
});
|
|
289
|
+
let result;
|
|
290
|
+
try {
|
|
291
|
+
result = await fn(ctx);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
294
|
+
writeSync(2, `[rig hook] typed hook threw: ${message}
|
|
295
|
+
`);
|
|
296
|
+
process.exit(0);
|
|
297
|
+
}
|
|
298
|
+
const { exitCode, stdout } = hookResultToProtocol(result);
|
|
299
|
+
if (stdout) {
|
|
300
|
+
writeSync(1, stdout);
|
|
301
|
+
}
|
|
302
|
+
process.exit(exitCode);
|
|
303
|
+
}
|
|
304
|
+
export {
|
|
305
|
+
runTypedHook,
|
|
306
|
+
resolveBunCliInvocation
|
|
307
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ export { definePlugin } from "./define-plugin";
|
|
|
3
3
|
export { defineConfig } from "./define-config";
|
|
4
4
|
export { createPluginHost } from "./plugin-host";
|
|
5
5
|
export type { PluginHost } from "./plugin-host";
|
|
6
|
-
export
|
|
6
|
+
export { defineCapability, defineServiceCapability, CapabilityProviderMissingError } from "./capability";
|
|
7
|
+
export type { Capability, CapabilityImpl, CapabilityProvideOptions, ServiceCapability, ServiceCapabilityImpl, } from "./capability";
|
|
8
|
+
export type { RigPlugin, PluginContributes, Validator, TaskSource, FeatureCapability, Panel, BlockerClassifierEntry, CliCommand, Hook, StageEntry, SessionExtensionEntry, SessionExtensionInstall, SeedEntrypoint, SeedEntrypointContext, SeedEntrypointRunResult, TaskSourceFactoryContext, RuntimeCliContext, ValidatorContext, ValidatorResult, } from "./plugin-runtime";
|
package/dist/src/index.js
CHANGED
|
@@ -10,10 +10,13 @@ function definePlugin(plugin) {
|
|
|
10
10
|
import { Schema as Schema2 } from "effect";
|
|
11
11
|
import { RigConfig } from "@rig/contracts";
|
|
12
12
|
function normalizeWorkspaceConfig(raw) {
|
|
13
|
-
const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
workspace.
|
|
13
|
+
const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {};
|
|
14
|
+
if (workspace.mainRepo === undefined)
|
|
15
|
+
workspace.mainRepo = ".";
|
|
16
|
+
if (workspace.checkout === undefined && workspace.isolation !== undefined)
|
|
17
|
+
workspace.checkout = workspace.isolation;
|
|
18
|
+
if (workspace.isolation === undefined && workspace.checkout !== undefined)
|
|
19
|
+
workspace.isolation = workspace.checkout;
|
|
17
20
|
return workspace;
|
|
18
21
|
}
|
|
19
22
|
function applyConfigDefaults(raw) {
|
|
@@ -22,7 +25,7 @@ function applyConfigDefaults(raw) {
|
|
|
22
25
|
const record = raw;
|
|
23
26
|
return {
|
|
24
27
|
...record,
|
|
25
|
-
plugins: Array.isArray(record.plugins) ? record.plugins : [],
|
|
28
|
+
plugins: Array.isArray(record.plugins) ? record.plugins.flat() : [],
|
|
26
29
|
workspace: normalizeWorkspaceConfig(record.workspace)
|
|
27
30
|
};
|
|
28
31
|
}
|
|
@@ -71,6 +74,7 @@ function createPluginHost(plugins) {
|
|
|
71
74
|
const panels = [];
|
|
72
75
|
const blockerClassifiers = [];
|
|
73
76
|
const sessionExtensions = [];
|
|
77
|
+
const seedEntrypoints = [];
|
|
74
78
|
const stages = [];
|
|
75
79
|
const stageMutations = [];
|
|
76
80
|
const stageExecutors = {};
|
|
@@ -103,6 +107,8 @@ function createPluginHost(plugins) {
|
|
|
103
107
|
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
104
108
|
if (c.sessionExtensions)
|
|
105
109
|
sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
|
|
110
|
+
if (c.seedEntrypoints)
|
|
111
|
+
seedEntrypoints.push(...c.seedEntrypoints.map((item) => ({ item, pluginName })));
|
|
106
112
|
if (c.stageMutations)
|
|
107
113
|
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
108
114
|
if (c.stages) {
|
|
@@ -125,6 +131,7 @@ function createPluginHost(plugins) {
|
|
|
125
131
|
const panelMap = indexById(panels, "panel");
|
|
126
132
|
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
127
133
|
indexById(sessionExtensions, "sessionExtension");
|
|
134
|
+
indexById(seedEntrypoints, "seedEntrypoint");
|
|
128
135
|
const taskSourceFactoryByKind = new Map;
|
|
129
136
|
const taskSourceKindRegistrant = new Map;
|
|
130
137
|
for (const { item, pluginName } of taskSources) {
|
|
@@ -134,6 +141,38 @@ function createPluginHost(plugins) {
|
|
|
134
141
|
taskSourceFactoryByKind.set(item.kind, item);
|
|
135
142
|
taskSourceKindRegistrant.set(item.kind, pluginName);
|
|
136
143
|
}
|
|
144
|
+
const seedEntrypointByBasename = new Map;
|
|
145
|
+
const seedEntrypointByWorkerArg = new Map;
|
|
146
|
+
let insidePtySeedEntrypoint;
|
|
147
|
+
const registerSeedEntrypointSelector = (map, selectorKind, selector, contribution) => {
|
|
148
|
+
const existing = map.get(selector);
|
|
149
|
+
if (existing) {
|
|
150
|
+
throw new Error(`duplicate seed entrypoint ${selectorKind} "${selector}": registered by entrypoint "${existing.item.id}" from plugin "${existing.pluginName}" and entrypoint "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
151
|
+
}
|
|
152
|
+
map.set(selector, contribution);
|
|
153
|
+
};
|
|
154
|
+
for (const contribution of seedEntrypoints) {
|
|
155
|
+
const entrypoint = contribution.item;
|
|
156
|
+
let selectorCount = 0;
|
|
157
|
+
if (entrypoint.basename) {
|
|
158
|
+
selectorCount += 1;
|
|
159
|
+
registerSeedEntrypointSelector(seedEntrypointByBasename, "basename", entrypoint.basename, contribution);
|
|
160
|
+
}
|
|
161
|
+
if (entrypoint.workerArg) {
|
|
162
|
+
selectorCount += 1;
|
|
163
|
+
registerSeedEntrypointSelector(seedEntrypointByWorkerArg, "workerArg", entrypoint.workerArg, contribution);
|
|
164
|
+
}
|
|
165
|
+
if (entrypoint.insidePty) {
|
|
166
|
+
selectorCount += 1;
|
|
167
|
+
if (insidePtySeedEntrypoint) {
|
|
168
|
+
throw new Error(`duplicate seed entrypoint inside-pty selector: registered by entrypoint "${insidePtySeedEntrypoint.item.id}" from plugin "${insidePtySeedEntrypoint.pluginName}" and entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}"`);
|
|
169
|
+
}
|
|
170
|
+
insidePtySeedEntrypoint = contribution;
|
|
171
|
+
}
|
|
172
|
+
if (selectorCount === 0) {
|
|
173
|
+
throw new Error(`seed entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}" must declare at least one selector (basename, workerArg, or insidePty)`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
137
176
|
const allValidators = validators.map((c) => c.item);
|
|
138
177
|
const allHooks = hooks.map((c) => c.item);
|
|
139
178
|
const allSkills = skills.map((c) => c.item);
|
|
@@ -148,6 +187,7 @@ function createPluginHost(plugins) {
|
|
|
148
187
|
const allPanels = panels.map((c) => c.item);
|
|
149
188
|
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
150
189
|
const allSessionExtensions = sessionExtensions.map((c) => c.item);
|
|
190
|
+
const allSeedEntrypoints = seedEntrypoints.map((c) => c.item);
|
|
151
191
|
const executableCliCommandByName = new Map;
|
|
152
192
|
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
153
193
|
const existing = executableCliCommandByName.get(selector);
|
|
@@ -165,6 +205,15 @@ function createPluginHost(plugins) {
|
|
|
165
205
|
registerExecutableCliCommandSelector(alias, contribution);
|
|
166
206
|
}
|
|
167
207
|
}
|
|
208
|
+
let defaultRootCliCommand;
|
|
209
|
+
for (const contribution of cliCommands) {
|
|
210
|
+
if (!contribution.item.rootDefault)
|
|
211
|
+
continue;
|
|
212
|
+
if (defaultRootCliCommand) {
|
|
213
|
+
throw new Error(`duplicate default root CLI command registered by command "${defaultRootCliCommand.item.id}" from plugin "${defaultRootCliCommand.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
214
|
+
}
|
|
215
|
+
defaultRootCliCommand = contribution;
|
|
216
|
+
}
|
|
168
217
|
return {
|
|
169
218
|
getValidator: (id) => validatorMap.get(id),
|
|
170
219
|
getHook: (id) => hookMap.get(id),
|
|
@@ -198,16 +247,81 @@ function createPluginHost(plugins) {
|
|
|
198
247
|
listExecutableBlockerClassifiers: () => allBlockerClassifiers,
|
|
199
248
|
listExecutableCliCommands: () => allCliCommands,
|
|
200
249
|
listSessionExtensions: () => allSessionExtensions,
|
|
250
|
+
listSeedEntrypoints: () => allSeedEntrypoints,
|
|
251
|
+
resolveSeedEntrypointByBasename: (value) => seedEntrypointByBasename.get(value)?.item,
|
|
252
|
+
resolveSeedEntrypointByWorkerArg: (value) => seedEntrypointByWorkerArg.get(value)?.item,
|
|
253
|
+
resolveInsidePtySeedEntrypoint: () => insidePtySeedEntrypoint?.item,
|
|
201
254
|
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
255
|
+
resolveDefaultRootCliCommand: () => defaultRootCliCommand?.item,
|
|
202
256
|
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
203
257
|
};
|
|
204
258
|
}
|
|
259
|
+
// packages/core/src/capability.ts
|
|
260
|
+
import { resolveCapability } from "@rig/kernel-seed";
|
|
261
|
+
|
|
262
|
+
class CapabilityProviderMissingError extends Error {
|
|
263
|
+
capabilityId;
|
|
264
|
+
name = "CapabilityProviderMissingError";
|
|
265
|
+
constructor(capabilityId) {
|
|
266
|
+
super(`No provider resolved for capability "${capabilityId}"`);
|
|
267
|
+
this.capabilityId = capabilityId;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function isFactory(impl) {
|
|
271
|
+
return typeof impl === "function";
|
|
272
|
+
}
|
|
273
|
+
function defineCapability(id) {
|
|
274
|
+
const idString = String(id);
|
|
275
|
+
const tag = idString;
|
|
276
|
+
async function resolve(host) {
|
|
277
|
+
const providers = host.listExecutableCapabilities().filter((c) => c.id === idString && typeof c.run === "function").map((c) => ({
|
|
278
|
+
name: c.id,
|
|
279
|
+
provides: [c.id],
|
|
280
|
+
capabilityProviders: { [c.id]: c.run }
|
|
281
|
+
}));
|
|
282
|
+
const resolution = resolveCapability(providers, tag);
|
|
283
|
+
const run = resolution?.value;
|
|
284
|
+
if (!run)
|
|
285
|
+
return null;
|
|
286
|
+
return await run(undefined);
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
id,
|
|
290
|
+
provide(impl, opts) {
|
|
291
|
+
return {
|
|
292
|
+
id: idString,
|
|
293
|
+
title: opts?.title ?? idString,
|
|
294
|
+
...opts?.description !== undefined ? { description: opts.description } : {},
|
|
295
|
+
run: isFactory(impl) ? () => impl() : () => impl
|
|
296
|
+
};
|
|
297
|
+
},
|
|
298
|
+
resolve,
|
|
299
|
+
async require(host) {
|
|
300
|
+
const value = await resolve(host);
|
|
301
|
+
if (value === null)
|
|
302
|
+
throw new CapabilityProviderMissingError(idString);
|
|
303
|
+
return value;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function defineServiceCapability(id) {
|
|
308
|
+
const capability = defineCapability(id);
|
|
309
|
+
return {
|
|
310
|
+
...capability,
|
|
311
|
+
provideService: capability.provide,
|
|
312
|
+
resolveService: capability.resolve,
|
|
313
|
+
requireService: capability.require
|
|
314
|
+
};
|
|
315
|
+
}
|
|
205
316
|
|
|
206
317
|
// packages/core/src/index.ts
|
|
207
318
|
var RIG_CORE_PACKAGE = "@rig/core";
|
|
208
319
|
export {
|
|
320
|
+
defineServiceCapability,
|
|
209
321
|
definePlugin,
|
|
210
322
|
defineConfig,
|
|
323
|
+
defineCapability,
|
|
211
324
|
createPluginHost,
|
|
212
|
-
RIG_CORE_PACKAGE
|
|
325
|
+
RIG_CORE_PACKAGE,
|
|
326
|
+
CapabilityProviderMissingError
|
|
213
327
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function appendJsonlRecord(path: string, value: unknown): void;
|
|
2
|
+
export declare function writeJsonFile(path: string, value: unknown): void;
|
|
3
|
+
export declare function readJsonFile<T>(path: string, fallback: T): T;
|
|
4
|
+
export declare function readAuthorityStateJson<T>(projectRoot: string, relativePath: string, fallback: T): T;
|
|
5
|
+
export declare function writeAuthorityStateJson(projectRoot: string, relativePath: string, value: unknown): string;
|
|
6
|
+
export declare function readAuthorityProjectStateJson<T>(projectRoot: string, relativePath: string, fallback: T): T;
|
|
7
|
+
export declare function writeAuthorityProjectStateJson(projectRoot: string, relativePath: string, value: unknown): string;
|
|
8
|
+
export declare function readJsonlFile(path: string): unknown[];
|
|
9
|
+
export declare function stripAuthorityRunLiveCollabFields<T extends Record<string, unknown>>(record: T): T;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/json-files.ts
|
|
3
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/core/src/layout.ts
|
|
7
|
+
import {
|
|
8
|
+
RIG_ARTIFACTS_DIRNAME,
|
|
9
|
+
RIG_DEFINITION_DIRNAME,
|
|
10
|
+
RIG_STATE_DIRNAME
|
|
11
|
+
} from "@rig/contracts";
|
|
12
|
+
|
|
13
|
+
// packages/core/src/authority-paths.ts
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
import { dirname, resolve } from "path";
|
|
16
|
+
function normalizeOptionalString(value) {
|
|
17
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
18
|
+
}
|
|
19
|
+
function resolveAuthorityStateRoot(projectRoot) {
|
|
20
|
+
const normalizedRoot = resolve(projectRoot);
|
|
21
|
+
const taskWorkspace = normalizeOptionalString(process.env.RIG_TASK_WORKSPACE);
|
|
22
|
+
if (taskWorkspace) {
|
|
23
|
+
return resolve(taskWorkspace, ".rig");
|
|
24
|
+
}
|
|
25
|
+
const stateDir = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
26
|
+
if (stateDir) {
|
|
27
|
+
return dirname(resolve(stateDir));
|
|
28
|
+
}
|
|
29
|
+
const logsDir = normalizeOptionalString(process.env.RIG_LOGS_DIR);
|
|
30
|
+
if (logsDir) {
|
|
31
|
+
return dirname(resolve(logsDir));
|
|
32
|
+
}
|
|
33
|
+
const sessionFile = normalizeOptionalString(process.env.RIG_SESSION_FILE);
|
|
34
|
+
if (sessionFile) {
|
|
35
|
+
return dirname(dirname(resolve(sessionFile)));
|
|
36
|
+
}
|
|
37
|
+
const projectStateRoot = resolve(normalizedRoot, ".rig");
|
|
38
|
+
if (existsSync(projectStateRoot)) {
|
|
39
|
+
return projectStateRoot;
|
|
40
|
+
}
|
|
41
|
+
return resolve(normalizedRoot, ".rig");
|
|
42
|
+
}
|
|
43
|
+
function resolveAuthorityStateDir(projectRoot) {
|
|
44
|
+
const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
45
|
+
if (explicit) {
|
|
46
|
+
return resolve(explicit);
|
|
47
|
+
}
|
|
48
|
+
return resolve(resolveAuthorityStateRoot(projectRoot), "state");
|
|
49
|
+
}
|
|
50
|
+
function resolveAuthorityProjectStateDir(projectRoot) {
|
|
51
|
+
const explicit = normalizeOptionalString(process.env.RIG_STATE_DIR);
|
|
52
|
+
if (explicit) {
|
|
53
|
+
return resolve(explicit);
|
|
54
|
+
}
|
|
55
|
+
return resolve(resolve(projectRoot), ".rig", "state");
|
|
56
|
+
}
|
|
57
|
+
// packages/core/src/json-files.ts
|
|
58
|
+
function appendJsonlRecord(path, value) {
|
|
59
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
60
|
+
appendFileSync(path, `${JSON.stringify(value)}
|
|
61
|
+
`, "utf8");
|
|
62
|
+
}
|
|
63
|
+
function writeJsonFile(path, value) {
|
|
64
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
65
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
66
|
+
`, "utf8");
|
|
67
|
+
}
|
|
68
|
+
function readJsonFile(path, fallback) {
|
|
69
|
+
if (!existsSync2(path))
|
|
70
|
+
return fallback;
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
73
|
+
} catch {
|
|
74
|
+
return fallback;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function readAuthorityStateJson(projectRoot, relativePath, fallback) {
|
|
78
|
+
return readJsonFile(resolve2(resolveAuthorityStateDir(projectRoot), relativePath), fallback);
|
|
79
|
+
}
|
|
80
|
+
function writeAuthorityStateJson(projectRoot, relativePath, value) {
|
|
81
|
+
const path = resolve2(resolveAuthorityStateDir(projectRoot), relativePath);
|
|
82
|
+
writeJsonFile(path, value);
|
|
83
|
+
return path;
|
|
84
|
+
}
|
|
85
|
+
function readAuthorityProjectStateJson(projectRoot, relativePath, fallback) {
|
|
86
|
+
return readJsonFile(resolve2(resolveAuthorityProjectStateDir(projectRoot), relativePath), fallback);
|
|
87
|
+
}
|
|
88
|
+
function writeAuthorityProjectStateJson(projectRoot, relativePath, value) {
|
|
89
|
+
const path = resolve2(resolveAuthorityProjectStateDir(projectRoot), relativePath);
|
|
90
|
+
writeJsonFile(path, value);
|
|
91
|
+
return path;
|
|
92
|
+
}
|
|
93
|
+
function readJsonlFile(path) {
|
|
94
|
+
if (!existsSync2(path))
|
|
95
|
+
return [];
|
|
96
|
+
return readFileSync(path, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).flatMap((line) => {
|
|
97
|
+
try {
|
|
98
|
+
return [JSON.parse(line)];
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function stripAuthorityRunLiveCollabFields(record) {
|
|
105
|
+
const {
|
|
106
|
+
collabLink: _collabLink,
|
|
107
|
+
collabWebLink: _collabWebLink,
|
|
108
|
+
collabRoomId: _collabRoomId,
|
|
109
|
+
collabRelayUrl: _collabRelayUrl,
|
|
110
|
+
collabToken: _collabToken,
|
|
111
|
+
...rest
|
|
112
|
+
} = record;
|
|
113
|
+
return rest;
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
writeJsonFile,
|
|
117
|
+
writeAuthorityStateJson,
|
|
118
|
+
writeAuthorityProjectStateJson,
|
|
119
|
+
stripAuthorityRunLiveCollabFields,
|
|
120
|
+
readJsonlFile,
|
|
121
|
+
readJsonFile,
|
|
122
|
+
readAuthorityStateJson,
|
|
123
|
+
readAuthorityProjectStateJson,
|
|
124
|
+
appendJsonlRecord
|
|
125
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JournalCapability, TransportCapability } from "@rig/contracts";
|
|
2
|
+
import { type DefaultKernelBootRecord } from "@rig/kernel-seed/boot-default";
|
|
3
|
+
import type { CapabilityProviderPlugin } from "@rig/kernel-seed/plugin-abi";
|
|
4
|
+
export type HydrationResult = {
|
|
5
|
+
readonly projectRoot: string;
|
|
6
|
+
readonly dotenvLoaded: boolean;
|
|
7
|
+
readonly configLoaded: boolean;
|
|
8
|
+
readonly errors: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
export type HydrateProjectProcessEnvOptions = {
|
|
11
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
12
|
+
readonly dotenvPath?: string;
|
|
13
|
+
};
|
|
14
|
+
export type AdoptKernelOptions = HydrateProjectProcessEnvOptions & {
|
|
15
|
+
readonly entrypoint?: string;
|
|
16
|
+
readonly journal?: JournalCapability;
|
|
17
|
+
readonly transport?: TransportCapability;
|
|
18
|
+
readonly hydrateEnv?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function createPlacementKernelTransportPlugin(transport: TransportCapability): CapabilityProviderPlugin;
|
|
21
|
+
export declare function hydrateProjectProcessEnv(projectRoot: string, options?: HydrateProjectProcessEnvOptions): Promise<HydrationResult>;
|
|
22
|
+
export declare function adopt(root?: string, options?: AdoptKernelOptions): Promise<DefaultKernelBootRecord>;
|