@h-rig/core 0.0.6-alpha.155 → 0.0.6-alpha.156
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/config.d.ts +1 -1
- package/dist/src/config.js +4 -91
- package/dist/src/define-plugin.d.ts +11 -7
- package/dist/src/define-plugin.js +4 -91
- package/dist/src/index.d.ts +1 -11
- package/dist/src/index.js +33 -3704
- package/dist/src/plugin-host.d.ts +25 -18
- package/dist/src/plugin-host.js +28 -149
- package/dist/src/plugin-runtime.d.ts +82 -51
- package/dist/src/project-plugins.d.ts +66 -0
- package/dist/src/project-plugins.js +596 -0
- package/dist/src/task-io.d.ts +54 -0
- package/dist/src/task-io.js +707 -0
- package/package.json +8 -20
- package/dist/src/dependencyGraph.d.ts +0 -43
- package/dist/src/dependencyGraph.js +0 -703
- package/dist/src/engineReadModelReducer.d.ts +0 -12
- package/dist/src/engineReadModelReducer.js +0 -1784
- package/dist/src/rig-init-builder.d.ts +0 -30
- package/dist/src/rig-init-builder.js +0 -61
- package/dist/src/rigSelectors.d.ts +0 -220
- package/dist/src/rigSelectors.js +0 -414
- package/dist/src/rollups.d.ts +0 -6
- package/dist/src/rollups.js +0 -377
- package/dist/src/stageResolve.d.ts +0 -77
- package/dist/src/stageResolve.js +0 -361
- package/dist/src/taskGraph.d.ts +0 -64
- package/dist/src/taskGraph.js +0 -377
- package/dist/src/taskGraphCodes.d.ts +0 -3
- package/dist/src/taskGraphCodes.js +0 -26
- package/dist/src/taskGraphLayout.d.ts +0 -61
- package/dist/src/taskGraphLayout.js +0 -397
- package/dist/src/taskScore.d.ts +0 -17
- package/dist/src/taskScore.js +0 -49
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/core/src/project-plugins.ts
|
|
3
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/core/src/plugin-host.ts
|
|
7
|
+
function indexById(contributions, kind) {
|
|
8
|
+
const map = new Map;
|
|
9
|
+
const registrant = new Map;
|
|
10
|
+
for (const { item, pluginName } of contributions) {
|
|
11
|
+
if (map.has(item.id)) {
|
|
12
|
+
throw new Error(`duplicate ${kind} id "${item.id}": registered by plugins "${registrant.get(item.id)}" and "${pluginName}"`);
|
|
13
|
+
}
|
|
14
|
+
map.set(item.id, item);
|
|
15
|
+
registrant.set(item.id, pluginName);
|
|
16
|
+
}
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
function assertUniquePluginNames(plugins) {
|
|
20
|
+
const seen = new Set;
|
|
21
|
+
for (const plugin of plugins) {
|
|
22
|
+
if (seen.has(plugin.name)) {
|
|
23
|
+
throw new Error(`duplicate plugin name "${plugin.name}"`);
|
|
24
|
+
}
|
|
25
|
+
seen.add(plugin.name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createPluginHost(plugins) {
|
|
29
|
+
assertUniquePluginNames(plugins);
|
|
30
|
+
const validators = [];
|
|
31
|
+
const hooks = [];
|
|
32
|
+
const skills = [];
|
|
33
|
+
const repoSources = [];
|
|
34
|
+
const agentRoles = [];
|
|
35
|
+
const taskFieldExtensions = [];
|
|
36
|
+
const taskSources = [];
|
|
37
|
+
const cliCommands = [];
|
|
38
|
+
const capabilities = [];
|
|
39
|
+
const panels = [];
|
|
40
|
+
const blockerClassifiers = [];
|
|
41
|
+
const sessionExtensions = [];
|
|
42
|
+
const stages = [];
|
|
43
|
+
const stageMutations = [];
|
|
44
|
+
const stageExecutors = {};
|
|
45
|
+
for (const plugin of plugins) {
|
|
46
|
+
const c = plugin.contributes;
|
|
47
|
+
if (!c)
|
|
48
|
+
continue;
|
|
49
|
+
const pluginName = plugin.name;
|
|
50
|
+
if (c.validators)
|
|
51
|
+
validators.push(...c.validators.map((item) => ({ item, pluginName })));
|
|
52
|
+
if (c.hooks)
|
|
53
|
+
hooks.push(...c.hooks.map((item) => ({ item, pluginName })));
|
|
54
|
+
if (c.skills)
|
|
55
|
+
skills.push(...c.skills.map((item) => ({ item, pluginName })));
|
|
56
|
+
if (c.repoSources)
|
|
57
|
+
repoSources.push(...c.repoSources.map((item) => ({ item, pluginName })));
|
|
58
|
+
if (c.agentRoles)
|
|
59
|
+
agentRoles.push(...c.agentRoles.map((item) => ({ item, pluginName })));
|
|
60
|
+
if (c.taskFieldSchemas)
|
|
61
|
+
taskFieldExtensions.push(...c.taskFieldSchemas.map((item) => ({ item, pluginName })));
|
|
62
|
+
if (c.taskSources)
|
|
63
|
+
taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
|
|
64
|
+
if (c.cliCommands)
|
|
65
|
+
cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
|
|
66
|
+
if (c.capabilities)
|
|
67
|
+
capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
|
|
68
|
+
if (c.panels)
|
|
69
|
+
panels.push(...c.panels.map((item) => ({ item, pluginName })));
|
|
70
|
+
if (c.blockerClassifiers)
|
|
71
|
+
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
72
|
+
if (c.sessionExtensions)
|
|
73
|
+
sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
|
|
74
|
+
if (c.stageMutations)
|
|
75
|
+
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
76
|
+
if (c.stages) {
|
|
77
|
+
for (const stage of c.stages) {
|
|
78
|
+
stages.push({ item: stage, pluginName });
|
|
79
|
+
if (stage.run)
|
|
80
|
+
stageExecutors[stage.id] = stage.run;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const validatorMap = indexById(validators, "validator");
|
|
85
|
+
const hookMap = indexById(hooks, "hook");
|
|
86
|
+
const skillMap = indexById(skills, "skill");
|
|
87
|
+
const repoSourceMap = indexById(repoSources, "repoSource");
|
|
88
|
+
const agentRoleMap = indexById(agentRoles, "agentRole");
|
|
89
|
+
const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
|
|
90
|
+
const taskSourceMap = indexById(taskSources, "taskSource");
|
|
91
|
+
const cliCommandMap = indexById(cliCommands, "cliCommand");
|
|
92
|
+
const capabilityMap = indexById(capabilities, "capability");
|
|
93
|
+
const panelMap = indexById(panels, "panel");
|
|
94
|
+
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
95
|
+
indexById(sessionExtensions, "sessionExtension");
|
|
96
|
+
const taskSourceFactoryByKind = new Map;
|
|
97
|
+
const taskSourceKindRegistrant = new Map;
|
|
98
|
+
for (const { item, pluginName } of taskSources) {
|
|
99
|
+
if (taskSourceFactoryByKind.has(item.kind)) {
|
|
100
|
+
throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
|
|
101
|
+
}
|
|
102
|
+
taskSourceFactoryByKind.set(item.kind, item);
|
|
103
|
+
taskSourceKindRegistrant.set(item.kind, pluginName);
|
|
104
|
+
}
|
|
105
|
+
const allValidators = validators.map((c) => c.item);
|
|
106
|
+
const allHooks = hooks.map((c) => c.item);
|
|
107
|
+
const allSkills = skills.map((c) => c.item);
|
|
108
|
+
const allRepoSources = repoSources.map((c) => c.item);
|
|
109
|
+
const allAgentRoles = agentRoles.map((c) => c.item);
|
|
110
|
+
const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
|
|
111
|
+
const allTaskSources = taskSources.map((c) => c.item);
|
|
112
|
+
const allCliCommands = cliCommands.map((c) => c.item);
|
|
113
|
+
const allStageMutations = stageMutations.map((c) => c.item);
|
|
114
|
+
const allStages = stages.map((c) => c.item);
|
|
115
|
+
const allCapabilities = capabilities.map((c) => c.item);
|
|
116
|
+
const allPanels = panels.map((c) => c.item);
|
|
117
|
+
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
118
|
+
const allSessionExtensions = sessionExtensions.map((c) => c.item);
|
|
119
|
+
const executableCliCommandByName = new Map;
|
|
120
|
+
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
121
|
+
const existing = executableCliCommandByName.get(selector);
|
|
122
|
+
if (existing && existing.item.id !== contribution.item.id) {
|
|
123
|
+
throw new Error(`duplicate executable CLI selector "${selector}" registered by command "${existing.item.id}" from plugin "${existing.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
|
|
124
|
+
}
|
|
125
|
+
executableCliCommandByName.set(selector, contribution);
|
|
126
|
+
};
|
|
127
|
+
for (const contribution of cliCommands) {
|
|
128
|
+
const command = contribution.item;
|
|
129
|
+
const family = command.family ?? command.id;
|
|
130
|
+
registerExecutableCliCommandSelector(command.id, contribution);
|
|
131
|
+
registerExecutableCliCommandSelector(family, contribution);
|
|
132
|
+
for (const alias of command.aliases ?? []) {
|
|
133
|
+
registerExecutableCliCommandSelector(alias, contribution);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
getValidator: (id) => validatorMap.get(id),
|
|
138
|
+
getHook: (id) => hookMap.get(id),
|
|
139
|
+
getSkill: (id) => skillMap.get(id),
|
|
140
|
+
getRepoSource: (id) => repoSourceMap.get(id),
|
|
141
|
+
getAgentRole: (id) => agentRoleMap.get(id),
|
|
142
|
+
getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
|
|
143
|
+
getTaskSource: (id) => taskSourceMap.get(id),
|
|
144
|
+
getCliCommand: (id) => cliCommandMap.get(id),
|
|
145
|
+
getCapability: (id) => capabilityMap.get(id),
|
|
146
|
+
getPanel: (id) => panelMap.get(id),
|
|
147
|
+
getBlockerClassifier: (id) => blockerClassifierMap.get(id),
|
|
148
|
+
listValidators: () => allValidators,
|
|
149
|
+
listHooks: () => allHooks,
|
|
150
|
+
listSkills: () => allSkills,
|
|
151
|
+
listRepoSources: () => allRepoSources,
|
|
152
|
+
listAgentRoles: () => allAgentRoles,
|
|
153
|
+
listTaskFieldExtensions: () => allTaskFieldExtensions,
|
|
154
|
+
listTaskSources: () => allTaskSources,
|
|
155
|
+
listCliCommands: () => allCliCommands,
|
|
156
|
+
listCapabilities: () => allCapabilities,
|
|
157
|
+
listPanels: () => allPanels,
|
|
158
|
+
listBlockerClassifiers: () => allBlockerClassifiers,
|
|
159
|
+
listStages: () => allStages,
|
|
160
|
+
listStageMutations: () => allStageMutations,
|
|
161
|
+
listStageExecutors: () => stageExecutors,
|
|
162
|
+
listExecutableValidators: () => allValidators.filter((v) => typeof v.run === "function"),
|
|
163
|
+
listExecutableTaskSources: () => allTaskSources,
|
|
164
|
+
listExecutableCapabilities: () => allCapabilities.filter((c) => typeof c.run === "function"),
|
|
165
|
+
listExecutablePanels: () => allPanels.filter((p) => typeof p.produce === "function"),
|
|
166
|
+
listExecutableBlockerClassifiers: () => allBlockerClassifiers,
|
|
167
|
+
listExecutableCliCommands: () => allCliCommands,
|
|
168
|
+
listSessionExtensions: () => allSessionExtensions,
|
|
169
|
+
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
170
|
+
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// packages/core/src/load-config.ts
|
|
175
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from "fs";
|
|
176
|
+
import { isBuiltin } from "module";
|
|
177
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
178
|
+
import { pathToFileURL } from "url";
|
|
179
|
+
import { Schema as Schema2 } from "effect";
|
|
180
|
+
import { RigConfig as RigConfig2 } from "@rig/contracts";
|
|
181
|
+
|
|
182
|
+
// packages/core/src/define-config.ts
|
|
183
|
+
import { Schema } from "effect";
|
|
184
|
+
import { RigConfig } from "@rig/contracts";
|
|
185
|
+
function normalizeWorkspaceConfig(raw) {
|
|
186
|
+
const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : { mainRepo: "." };
|
|
187
|
+
workspace.checkout = workspace.checkout ?? workspace.isolation ?? "worktree";
|
|
188
|
+
workspace.isolation = workspace.isolation ?? workspace.checkout;
|
|
189
|
+
workspace.sandbox = workspace.sandbox ?? "enforce";
|
|
190
|
+
return workspace;
|
|
191
|
+
}
|
|
192
|
+
function applyConfigDefaults(raw) {
|
|
193
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
194
|
+
return raw;
|
|
195
|
+
const record = raw;
|
|
196
|
+
return {
|
|
197
|
+
...record,
|
|
198
|
+
plugins: Array.isArray(record.plugins) ? record.plugins : [],
|
|
199
|
+
workspace: normalizeWorkspaceConfig(record.workspace)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// packages/core/src/load-config.ts
|
|
204
|
+
var TS_NAMES = ["rig.config.ts", "rig.config.mts"];
|
|
205
|
+
var JSON_NAMES = ["rig.config.json"];
|
|
206
|
+
function isModuleResolutionError(error) {
|
|
207
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
208
|
+
return message.includes("Cannot find module") || message.includes("Could not resolve");
|
|
209
|
+
}
|
|
210
|
+
function runningFromCompiledBinary() {
|
|
211
|
+
return import.meta.url.includes("$bunfs");
|
|
212
|
+
}
|
|
213
|
+
function packageNameAndSubpath(specifier) {
|
|
214
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || /^[a-zA-Z]+:/.test(specifier))
|
|
215
|
+
return null;
|
|
216
|
+
const parts = specifier.split("/");
|
|
217
|
+
const packageName = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
218
|
+
if (!packageName)
|
|
219
|
+
return null;
|
|
220
|
+
const rest = parts.slice(specifier.startsWith("@") ? 2 : 1).join("/");
|
|
221
|
+
return { packageName, subpath: rest ? `./${rest}` : "." };
|
|
222
|
+
}
|
|
223
|
+
function exportTargetFromEntry(entry) {
|
|
224
|
+
if (typeof entry === "string")
|
|
225
|
+
return entry;
|
|
226
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
227
|
+
const conditions = entry;
|
|
228
|
+
for (const key of ["bun", "import", "default", "require"]) {
|
|
229
|
+
if (typeof conditions[key] === "string")
|
|
230
|
+
return conditions[key];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
function patternExportTarget(record, subpath) {
|
|
236
|
+
for (const [pattern, entry] of Object.entries(record)) {
|
|
237
|
+
if (!pattern.includes("*"))
|
|
238
|
+
continue;
|
|
239
|
+
const [prefix = "", suffix = ""] = pattern.split("*");
|
|
240
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
|
|
241
|
+
continue;
|
|
242
|
+
const replacement = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
243
|
+
const target = exportTargetFromEntry(entry);
|
|
244
|
+
if (target)
|
|
245
|
+
return target.replace("*", replacement);
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
function exportTargetFromPackageJson(pkg, subpath) {
|
|
250
|
+
const exportsField = pkg.exports;
|
|
251
|
+
const target = (() => {
|
|
252
|
+
if (typeof exportsField === "string" && subpath === ".")
|
|
253
|
+
return exportsField;
|
|
254
|
+
if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField))
|
|
255
|
+
return null;
|
|
256
|
+
const record = exportsField;
|
|
257
|
+
return exportTargetFromEntry(record[subpath] ?? (subpath === "." ? record["."] : undefined)) ?? patternExportTarget(record, subpath);
|
|
258
|
+
})();
|
|
259
|
+
if (target)
|
|
260
|
+
return target;
|
|
261
|
+
return subpath === "." && typeof pkg.module === "string" ? pkg.module : subpath === "." && typeof pkg.main === "string" ? pkg.main : null;
|
|
262
|
+
}
|
|
263
|
+
function resolvePackageDirFromBunStore(packageName, nodeModulesDir) {
|
|
264
|
+
const storeDir = join(nodeModulesDir, ".bun");
|
|
265
|
+
if (!existsSync(storeDir))
|
|
266
|
+
return null;
|
|
267
|
+
const encoded = packageName.replace("/", "+");
|
|
268
|
+
try {
|
|
269
|
+
const candidates = readdirSync(storeDir).filter((entry) => entry.startsWith(`${encoded}@`)).map((entry) => {
|
|
270
|
+
const candidateDir = join(storeDir, entry, "node_modules", packageName);
|
|
271
|
+
const packageJsonPath = join(candidateDir, "package.json");
|
|
272
|
+
if (!existsSync(packageJsonPath))
|
|
273
|
+
return null;
|
|
274
|
+
try {
|
|
275
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
276
|
+
return {
|
|
277
|
+
dir: candidateDir,
|
|
278
|
+
sortKey: pkg.version?.trim() || entry
|
|
279
|
+
};
|
|
280
|
+
} catch {
|
|
281
|
+
return {
|
|
282
|
+
dir: candidateDir,
|
|
283
|
+
sortKey: entry
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}).filter((candidate) => candidate !== null).sort((a, b) => b.sortKey.localeCompare(a.sortKey, undefined, { numeric: true, sensitivity: "base" }));
|
|
287
|
+
return candidates[0]?.dir ?? null;
|
|
288
|
+
} catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function resolveDirectoryModulePath(directoryPath) {
|
|
293
|
+
const packageJsonPath = join(directoryPath, "package.json");
|
|
294
|
+
if (existsSync(packageJsonPath)) {
|
|
295
|
+
try {
|
|
296
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
297
|
+
const target = exportTargetFromPackageJson(pkg, ".");
|
|
298
|
+
if (target) {
|
|
299
|
+
const resolved = resolveModulePath(join(directoryPath, target));
|
|
300
|
+
if (resolved)
|
|
301
|
+
return resolved;
|
|
302
|
+
}
|
|
303
|
+
} catch {}
|
|
304
|
+
}
|
|
305
|
+
for (const candidate of ["index.js", "index.mjs", "index.cjs", "index.ts", "index.json"]) {
|
|
306
|
+
const resolved = resolveModulePath(join(directoryPath, candidate));
|
|
307
|
+
if (resolved)
|
|
308
|
+
return resolved;
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
function resolveModulePath(candidatePath) {
|
|
313
|
+
if (!existsSync(candidatePath)) {
|
|
314
|
+
for (const extension of [".ts", ".mts", ".tsx", ".js", ".mjs", ".cjs", ".json"]) {
|
|
315
|
+
const withExtension = `${candidatePath}${extension}`;
|
|
316
|
+
if (existsSync(withExtension))
|
|
317
|
+
return resolveModulePath(withExtension);
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const stat = statSync(candidatePath);
|
|
323
|
+
if (stat.isFile())
|
|
324
|
+
return candidatePath;
|
|
325
|
+
if (stat.isDirectory())
|
|
326
|
+
return resolveDirectoryModulePath(candidatePath);
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
function resolvePackageExportFromDir(packageDir, subpath) {
|
|
333
|
+
const packageJsonPath = join(packageDir, "package.json");
|
|
334
|
+
if (existsSync(packageJsonPath)) {
|
|
335
|
+
try {
|
|
336
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
337
|
+
const target = exportTargetFromPackageJson(pkg, subpath);
|
|
338
|
+
if (target) {
|
|
339
|
+
const resolved = resolveModulePath(join(packageDir, target));
|
|
340
|
+
if (resolved)
|
|
341
|
+
return resolved;
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (subpath !== ".") {
|
|
348
|
+
const legacySubpath = subpath.replace(/^\.\//, "");
|
|
349
|
+
for (const candidate of [
|
|
350
|
+
join(packageDir, legacySubpath),
|
|
351
|
+
join(packageDir, `${legacySubpath}.js`),
|
|
352
|
+
join(packageDir, `${legacySubpath}.mjs`),
|
|
353
|
+
join(packageDir, `${legacySubpath}.cjs`),
|
|
354
|
+
join(packageDir, `${legacySubpath}.ts`),
|
|
355
|
+
join(packageDir, `${legacySubpath}.json`)
|
|
356
|
+
]) {
|
|
357
|
+
const resolved = resolveModulePath(candidate);
|
|
358
|
+
if (resolved)
|
|
359
|
+
return resolved;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return subpath === "." ? resolveDirectoryModulePath(packageDir) : null;
|
|
363
|
+
}
|
|
364
|
+
var runtimeBundleQueue = Promise.resolve();
|
|
365
|
+
function enqueueRuntimeBundle(operation) {
|
|
366
|
+
const next = runtimeBundleQueue.then(operation, operation);
|
|
367
|
+
runtimeBundleQueue = next.then(() => {
|
|
368
|
+
return;
|
|
369
|
+
}, () => {
|
|
370
|
+
return;
|
|
371
|
+
});
|
|
372
|
+
return next;
|
|
373
|
+
}
|
|
374
|
+
function isWithinDir(candidatePath, rootPath) {
|
|
375
|
+
const rel = relative(resolve(rootPath), resolve(candidatePath));
|
|
376
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
377
|
+
}
|
|
378
|
+
function resolvedFilePath(path, rootPath) {
|
|
379
|
+
if (!path || !isAbsolute(path))
|
|
380
|
+
return null;
|
|
381
|
+
const resolved = resolveModulePath(path);
|
|
382
|
+
if (!resolved)
|
|
383
|
+
return null;
|
|
384
|
+
return rootPath && !isWithinDir(resolved, rootPath) ? null : resolved;
|
|
385
|
+
}
|
|
386
|
+
function resolveProjectPackageImport(specifier, configDir) {
|
|
387
|
+
const parsed = packageNameAndSubpath(specifier);
|
|
388
|
+
if (!parsed)
|
|
389
|
+
return null;
|
|
390
|
+
const nodeModulesDir = join(configDir, "node_modules");
|
|
391
|
+
const directPackageDir = join(nodeModulesDir, parsed.packageName);
|
|
392
|
+
const packageDir = existsSync(join(directPackageDir, "package.json")) ? directPackageDir : resolvePackageDirFromBunStore(parsed.packageName, nodeModulesDir);
|
|
393
|
+
return packageDir ? resolvePackageExportFromDir(packageDir, parsed.subpath) : null;
|
|
394
|
+
}
|
|
395
|
+
async function importConfigViaRuntimeBundleUnserialized(configPath) {
|
|
396
|
+
const bun = globalThis.Bun;
|
|
397
|
+
if (!bun?.build) {
|
|
398
|
+
throw new Error(`Failed to import ${configPath}: bare imports could not be resolved and no Bun.build runtime bundler is available.`);
|
|
399
|
+
}
|
|
400
|
+
const RUNTIME_ONLY_EXTERNAL_PACKAGES = new Set(["mupdf", "fastembed", "onnxruntime-node", "markit-ai"]);
|
|
401
|
+
const configDir = dirname(configPath);
|
|
402
|
+
const UNRESOLVED_NAMESPACE = "rig-config-unresolved";
|
|
403
|
+
const unresolvedLocalPlugin = {
|
|
404
|
+
name: "rig-config-unresolved-local",
|
|
405
|
+
setup(build) {
|
|
406
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
407
|
+
const directFilePath = resolvedFilePath(args.path, configDir);
|
|
408
|
+
if (directFilePath)
|
|
409
|
+
return { path: directFilePath };
|
|
410
|
+
const packageImport = packageNameAndSubpath(args.path);
|
|
411
|
+
if (packageImport && RUNTIME_ONLY_EXTERNAL_PACKAGES.has(packageImport.packageName))
|
|
412
|
+
return { path: args.path, external: true };
|
|
413
|
+
const projectPackagePath = resolveProjectPackageImport(args.path, configDir);
|
|
414
|
+
if (projectPackagePath)
|
|
415
|
+
return packageImport?.packageName.startsWith("@oh-my-pi/") ? { path: projectPackagePath, external: true } : { path: projectPackagePath };
|
|
416
|
+
if (/^(?:node|bun):/.test(args.path) || isBuiltin(args.path))
|
|
417
|
+
return;
|
|
418
|
+
if (packageImport)
|
|
419
|
+
return { path: args.path, external: true };
|
|
420
|
+
const parentCandidates = args.path.startsWith(".") ? [args.importer && isAbsolute(args.importer) ? dirname(args.importer) : null, configDir].filter((value) => Boolean(value)) : [args.importer && isAbsolute(args.importer) ? dirname(args.importer) : configDir];
|
|
421
|
+
for (const parent2 of parentCandidates) {
|
|
422
|
+
const filePath = resolvedFilePath(resolve(parent2, args.path), configDir);
|
|
423
|
+
if (filePath)
|
|
424
|
+
return { path: filePath };
|
|
425
|
+
}
|
|
426
|
+
const parent = parentCandidates[0] ?? configDir;
|
|
427
|
+
try {
|
|
428
|
+
const resolved = bun.resolveSync?.(args.path, parent) ?? resolve(parent, args.path);
|
|
429
|
+
const filePath = resolvedFilePath(resolved, configDir);
|
|
430
|
+
if (filePath)
|
|
431
|
+
return { path: filePath };
|
|
432
|
+
} catch {}
|
|
433
|
+
return { path: args.path, namespace: UNRESOLVED_NAMESPACE };
|
|
434
|
+
});
|
|
435
|
+
build.onLoad({ filter: /.*/, namespace: UNRESOLVED_NAMESPACE }, (args) => ({
|
|
436
|
+
loader: "js",
|
|
437
|
+
contents: `module.exports = {};
|
|
438
|
+
throw new Error(${JSON.stringify(`Failed to bundle ${configPath}: Could not resolve local import "${args.path}". Maybe you need to fix a relative import in rig.config.ts or install project deps.`)});
|
|
439
|
+
`
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const result = await bun.build({
|
|
444
|
+
entrypoints: [configPath],
|
|
445
|
+
target: "bun",
|
|
446
|
+
external: ["mupdf", "fastembed", "onnxruntime-node", "markit-ai"],
|
|
447
|
+
format: "esm",
|
|
448
|
+
throw: false,
|
|
449
|
+
packages: "bundle",
|
|
450
|
+
plugins: [unresolvedLocalPlugin]
|
|
451
|
+
});
|
|
452
|
+
if (!result.success || !result.outputs[0]) {
|
|
453
|
+
const detail = result.logs.map((log) => String(log)).join(`
|
|
454
|
+
`);
|
|
455
|
+
throw new Error(`Failed to bundle ${configPath}: ${detail || "unknown bundler error"}`);
|
|
456
|
+
}
|
|
457
|
+
const bundleParentDir = join(configDir, ".rig", "tmp");
|
|
458
|
+
mkdirSync(bundleParentDir, { recursive: true });
|
|
459
|
+
const dir = mkdtempSync(join(bundleParentDir, "rig-config-bundle-"));
|
|
460
|
+
try {
|
|
461
|
+
const bundledPath = join(dir, "rig.config.bundled.js");
|
|
462
|
+
await bun.write(bundledPath, await result.outputs[0].text());
|
|
463
|
+
return await import(pathToFileURL(bundledPath).href);
|
|
464
|
+
} finally {
|
|
465
|
+
try {
|
|
466
|
+
rmSync(dir, { recursive: true, force: true });
|
|
467
|
+
} catch {}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async function loadConfig(cwd) {
|
|
471
|
+
for (const name of TS_NAMES) {
|
|
472
|
+
const p = join(cwd, name);
|
|
473
|
+
if (existsSync(p)) {
|
|
474
|
+
const mod = await enqueueRuntimeBundle(async () => {
|
|
475
|
+
if (runningFromCompiledBinary()) {
|
|
476
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
477
|
+
}
|
|
478
|
+
const source = readFileSync(p, "utf8");
|
|
479
|
+
const importsRigHostPackages = /(?:import\s+[^;]*?from\s*|import\s*\()\s*["']@rig\//.test(source);
|
|
480
|
+
if (importsRigHostPackages) {
|
|
481
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
return await import(pathToFileURL(p).href);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
if (!isModuleResolutionError(error))
|
|
487
|
+
throw error;
|
|
488
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
const raw = mod.default ?? mod.config;
|
|
492
|
+
return decodeExplicitPluginConfig(raw);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
for (const name of JSON_NAMES) {
|
|
496
|
+
const p = join(cwd, name);
|
|
497
|
+
if (existsSync(p)) {
|
|
498
|
+
const raw = JSON.parse(readFileSync(p, "utf-8"));
|
|
499
|
+
return decodeExplicitPluginConfig(raw);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
throw new Error(`no rig.config.{ts,mts,json} found in ${cwd}`);
|
|
503
|
+
}
|
|
504
|
+
function decodeExplicitPluginConfig(raw) {
|
|
505
|
+
const withDefaults = applyConfigDefaults(raw);
|
|
506
|
+
const explicitPlugins = Array.isArray(withDefaults.plugins) ? [...withDefaults.plugins] : [];
|
|
507
|
+
const decoded = Schema2.decodeUnknownSync(RigConfig2)({
|
|
508
|
+
...withDefaults,
|
|
509
|
+
plugins: explicitPlugins
|
|
510
|
+
});
|
|
511
|
+
return { ...decoded, plugins: explicitPlugins };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// packages/core/src/project-plugins.ts
|
|
515
|
+
var RIG_CONFIG_FILENAMES = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
516
|
+
function findProjectConfigPath(projectRoot) {
|
|
517
|
+
for (const name of RIG_CONFIG_FILENAMES) {
|
|
518
|
+
const path = resolve2(projectRoot, name);
|
|
519
|
+
if (existsSync2(path))
|
|
520
|
+
return path;
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
var pluginHostCache = new Map;
|
|
525
|
+
function configFileMtimeMs(normalizedRoot) {
|
|
526
|
+
const path = findProjectConfigPath(normalizedRoot);
|
|
527
|
+
if (!path)
|
|
528
|
+
return 0;
|
|
529
|
+
try {
|
|
530
|
+
return statSync2(path).mtimeMs;
|
|
531
|
+
} catch {
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function resolvePluginHost(projectRoot, options = {}) {
|
|
536
|
+
const normalizedRoot = resolve2(projectRoot);
|
|
537
|
+
const build = (load) => load(normalizedRoot).then((config) => ({
|
|
538
|
+
host: createPluginHost(config.plugins),
|
|
539
|
+
config
|
|
540
|
+
}));
|
|
541
|
+
if (options.load)
|
|
542
|
+
return build(options.load);
|
|
543
|
+
const mtimeMs = configFileMtimeMs(normalizedRoot);
|
|
544
|
+
const cached = pluginHostCache.get(normalizedRoot);
|
|
545
|
+
if (cached && cached.mtimeMs === mtimeMs)
|
|
546
|
+
return cached.promise;
|
|
547
|
+
const promise = build(loadConfig);
|
|
548
|
+
promise.catch(() => {
|
|
549
|
+
if (pluginHostCache.get(normalizedRoot)?.promise === promise) {
|
|
550
|
+
pluginHostCache.delete(normalizedRoot);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
pluginHostCache.set(normalizedRoot, { mtimeMs, promise });
|
|
554
|
+
return promise;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// packages/core/src/task-io.ts
|
|
558
|
+
function isRecord(value) {
|
|
559
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
560
|
+
}
|
|
561
|
+
function taskText(value) {
|
|
562
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
563
|
+
}
|
|
564
|
+
function taskUrl(record) {
|
|
565
|
+
const metadata = record;
|
|
566
|
+
return taskText(metadata.url) ?? taskText(metadata.html_url) ?? taskText(metadata.webUrl);
|
|
567
|
+
}
|
|
568
|
+
function taskBody(record) {
|
|
569
|
+
const metadata = record;
|
|
570
|
+
return taskText(metadata.body) ?? taskText(metadata.description);
|
|
571
|
+
}
|
|
572
|
+
function taskTitle(record) {
|
|
573
|
+
const metadata = record;
|
|
574
|
+
return taskText(metadata.title) ?? taskText(metadata.name) ?? record.id;
|
|
575
|
+
}
|
|
576
|
+
function taskStringList(value) {
|
|
577
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
578
|
+
}
|
|
579
|
+
function taskDependencies(record) {
|
|
580
|
+
const raw = record;
|
|
581
|
+
const dependencies = taskStringList(raw.dependencies);
|
|
582
|
+
if (dependencies.length > 0)
|
|
583
|
+
return dependencies;
|
|
584
|
+
const deps = taskStringList(raw.deps);
|
|
585
|
+
return deps.length > 0 ? deps : null;
|
|
586
|
+
}
|
|
587
|
+
function taskParentChildDeps(record) {
|
|
588
|
+
const raw = record;
|
|
589
|
+
const parentChildDeps = taskStringList(raw.parentChildDeps);
|
|
590
|
+
if (parentChildDeps.length > 0)
|
|
591
|
+
return parentChildDeps;
|
|
592
|
+
const parents = taskStringList(raw.parents);
|
|
593
|
+
return parents.length > 0 ? parents : null;
|
|
594
|
+
}
|
|
595
|
+
function taskCreateInput(task) {
|
|
596
|
+
const deps = Array.isArray(task.dependencies) ? task.dependencies.filter((entry) => typeof entry === "string") : Array.isArray(task.deps) ? task.deps.filter((entry) => typeof entry === "string") : [];
|
|
597
|
+
const parents = Array.isArray(task.parents) ? task.parents.filter((entry) => typeof entry === "string") : [];
|
|
598
|
+
return {
|
|
599
|
+
title: taskText(task.title) ?? taskText(task.name) ?? "Untitled task",
|
|
600
|
+
body: taskText(task.body) ?? taskText(task.description) ?? "",
|
|
601
|
+
...deps.length > 0 ? { deps } : {},
|
|
602
|
+
...parents.length > 0 ? { parents } : {},
|
|
603
|
+
metadata: { ...task }
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function toRigTask(record, sourceKind) {
|
|
607
|
+
const dependencies = taskDependencies(record);
|
|
608
|
+
const parentChildDeps = taskParentChildDeps(record);
|
|
609
|
+
return {
|
|
610
|
+
...record,
|
|
611
|
+
id: record.id,
|
|
612
|
+
title: taskTitle(record),
|
|
613
|
+
status: typeof record.status === "string" ? record.status : null,
|
|
614
|
+
source: sourceKind,
|
|
615
|
+
url: taskUrl(record),
|
|
616
|
+
body: taskBody(record),
|
|
617
|
+
...dependencies ? { dependencies } : {},
|
|
618
|
+
...parentChildDeps ? { parentChildDeps } : {}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function normalizeTaskId(value) {
|
|
622
|
+
const trimmed = value?.trim();
|
|
623
|
+
if (!trimmed)
|
|
624
|
+
return;
|
|
625
|
+
return trimmed.startsWith("#") && /^#\d+$/.test(trimmed) ? trimmed.slice(1) : trimmed;
|
|
626
|
+
}
|
|
627
|
+
function readTaskTitle(task) {
|
|
628
|
+
if (!task)
|
|
629
|
+
return null;
|
|
630
|
+
for (const key of ["title", "summary", "name"]) {
|
|
631
|
+
const value = task[key];
|
|
632
|
+
if (typeof value === "string" && value.trim())
|
|
633
|
+
return value.trim();
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
async function loadTaskSource(projectRoot) {
|
|
638
|
+
const { config, host: pluginHost } = await resolvePluginHost(projectRoot);
|
|
639
|
+
const factory = pluginHost.resolveTaskSourceFactoryByKind(config.taskSource.kind);
|
|
640
|
+
if (!factory) {
|
|
641
|
+
const kinds = pluginHost.listExecutableTaskSources().map((entry) => entry.kind).join(", ") || "none";
|
|
642
|
+
throw new Error(`No task source factory registered for kind ${config.taskSource.kind}. Registered task sources: ${kinds}.`);
|
|
643
|
+
}
|
|
644
|
+
return { kind: config.taskSource.kind, source: factory.factory(config.taskSource, { projectRoot }) };
|
|
645
|
+
}
|
|
646
|
+
var defaultLoadTaskSource = loadTaskSource;
|
|
647
|
+
async function listTasks(projectRoot, deps = {}) {
|
|
648
|
+
const { kind, source } = await (deps.loadTaskSource ?? loadTaskSource)(projectRoot);
|
|
649
|
+
return (await source.list()).map((task) => toRigTask(task, kind));
|
|
650
|
+
}
|
|
651
|
+
var listTasksFromSource = listTasks;
|
|
652
|
+
async function getTask(projectRoot, taskId, deps = {}) {
|
|
653
|
+
const { kind, source } = await (deps.loadTaskSource ?? loadTaskSource)(projectRoot);
|
|
654
|
+
const task = source.get ? await source.get(taskId) ?? null : (await source.list()).find((entry) => entry.id === taskId) ?? null;
|
|
655
|
+
return task ? toRigTask(task, kind) : null;
|
|
656
|
+
}
|
|
657
|
+
var getTaskFromSource = getTask;
|
|
658
|
+
async function listTasksForCommand(projectRoot, deps = {}) {
|
|
659
|
+
if (deps.listTasks)
|
|
660
|
+
return deps.listTasks(projectRoot);
|
|
661
|
+
if (deps.loadTaskSource) {
|
|
662
|
+
const { kind, source } = await deps.loadTaskSource(projectRoot);
|
|
663
|
+
return (await source.list()).map((task) => ({ ...toRigTask(task, kind), source: kind }));
|
|
664
|
+
}
|
|
665
|
+
return listTasks(projectRoot);
|
|
666
|
+
}
|
|
667
|
+
async function getTaskForCommand(projectRoot, taskId, deps = {}) {
|
|
668
|
+
if (deps.getTask)
|
|
669
|
+
return deps.getTask(projectRoot, taskId);
|
|
670
|
+
if (deps.loadTaskSource) {
|
|
671
|
+
const { kind, source } = await deps.loadTaskSource(projectRoot);
|
|
672
|
+
const task = source.get ? await source.get(taskId) : (await source.list()).find((entry) => entry.id === taskId);
|
|
673
|
+
return task ? toRigTask(task, kind) : null;
|
|
674
|
+
}
|
|
675
|
+
return getTask(projectRoot, taskId);
|
|
676
|
+
}
|
|
677
|
+
function getTaskCreator(source) {
|
|
678
|
+
const record = source;
|
|
679
|
+
if (typeof record.createTask === "function")
|
|
680
|
+
return record.createTask.bind(source);
|
|
681
|
+
if (typeof record.create === "function")
|
|
682
|
+
return (task) => record.create?.(taskCreateInput(task));
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
async function createTask(projectRoot, task, deps = {}) {
|
|
686
|
+
const { kind, source } = await (deps.loadTaskSource ?? loadTaskSource)(projectRoot);
|
|
687
|
+
const creator = getTaskCreator(source);
|
|
688
|
+
if (!creator)
|
|
689
|
+
throw new Error(`The configured ${kind} task source does not expose a task creation API.`);
|
|
690
|
+
const result = await creator(task);
|
|
691
|
+
const taskId = typeof result === "string" ? result : isRecord(result) && typeof result.id === "string" ? result.id : null;
|
|
692
|
+
return { taskId, source: kind, result };
|
|
693
|
+
}
|
|
694
|
+
export {
|
|
695
|
+
readTaskTitle,
|
|
696
|
+
normalizeTaskId,
|
|
697
|
+
loadTaskSource,
|
|
698
|
+
listTasksFromSource,
|
|
699
|
+
listTasksForCommand,
|
|
700
|
+
listTasks,
|
|
701
|
+
getTaskFromSource,
|
|
702
|
+
getTaskForCommand,
|
|
703
|
+
getTaskCreator,
|
|
704
|
+
getTask,
|
|
705
|
+
defaultLoadTaskSource,
|
|
706
|
+
createTask
|
|
707
|
+
};
|