@h-rig/core 0.0.6-alpha.135 → 0.0.6-alpha.137
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/define-plugin.js +31 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +168 -32
- package/dist/src/plugin-host.d.ts +27 -2
- package/dist/src/plugin-host.js +116 -0
- package/dist/src/plugin-runtime.d.ts +42 -1
- package/dist/src/rig-init-builder.js +12 -9
- package/dist/src/rigSelectors.d.ts +1 -1
- package/dist/src/stageResolve.d.ts +0 -7
- package/dist/src/stageResolve.js +9 -23
- package/package.json +2 -2
|
@@ -60,6 +60,37 @@ function definePlugin(meta, runtime) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
const declaredCapabilities = new Map((validated.contributes?.capabilities ?? []).map((capability) => [capability.id, capability]));
|
|
64
|
+
for (const capability of runtime.featureCapabilities ?? []) {
|
|
65
|
+
if (!declaredCapabilities.has(capability.id)) {
|
|
66
|
+
throw new Error(`definePlugin(${validated.name}): executable capability "${capability.id}" has no matching metadata entry in contributes.capabilities`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const declaredPanels = new Map((validated.contributes?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
70
|
+
for (const panel of runtime.panels ?? []) {
|
|
71
|
+
const metadata = declaredPanels.get(panel.id);
|
|
72
|
+
if (!metadata) {
|
|
73
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" has no matching metadata entry in contributes.panels`);
|
|
74
|
+
}
|
|
75
|
+
if (metadata.slot !== panel.slot) {
|
|
76
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" slot "${panel.slot}" does not match metadata slot "${metadata.slot}"`);
|
|
77
|
+
}
|
|
78
|
+
if (metadata.capabilityId !== panel.capabilityId) {
|
|
79
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" capabilityId "${panel.capabilityId ?? "(none)"}" does not match metadata capabilityId "${metadata.capabilityId ?? "(none)"}"`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const declaredBlockerClassifiers = new Map((validated.contributes?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
83
|
+
for (const classifier of runtime.blockerClassifiers ?? []) {
|
|
84
|
+
if (!declaredBlockerClassifiers.has(classifier.id)) {
|
|
85
|
+
throw new Error(`definePlugin(${validated.name}): executable blocker classifier "${classifier.id}" has no matching metadata entry in contributes.blockerClassifiers`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const declaredCliCommands = new Map((validated.contributes?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
89
|
+
for (const command of runtime.cliCommands ?? []) {
|
|
90
|
+
if (!declaredCliCommands.has(command.id)) {
|
|
91
|
+
throw new Error(`definePlugin(${validated.name}): executable cli command "${command.id}" has no matching metadata entry in contributes.cliCommands`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
63
94
|
return { ...validated, __runtime: runtime };
|
|
64
95
|
}
|
|
65
96
|
export {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export { defineConfig } from "./define-config";
|
|
|
4
4
|
export { createPluginHost } from "./plugin-host";
|
|
5
5
|
export type { PluginHost } from "./plugin-host";
|
|
6
6
|
export { buildRigInitConfigSource, type RigInitConfigInput, } from "./rig-init-builder";
|
|
7
|
-
export type { RegisteredValidator, RigPluginRuntime, RigPluginWithRuntime, TaskSourceFactoryContext, TaskSourceFactoryEntry, ValidatorContext, ValidatorResult, } from "./plugin-runtime";
|
|
7
|
+
export type { RegisteredValidator, RigPluginRuntime, RigPluginWithRuntime, TaskSourceFactoryContext, RuntimeCliCommand, RuntimeCliContext, TaskSourceFactoryEntry, ValidatorContext, ValidatorResult, } from "./plugin-runtime";
|
|
8
8
|
export { applyEngineEvent, applyEngineEvents, applyEngineEvent as applyRigEvent, pruneQueueEntries, type ApplyStatus, type EngineEventApplyResult, type EngineEventApplyResult as RigEventApplyResult, } from "./engineReadModelReducer";
|
|
9
9
|
export { pickDefaultWorkspaceId, projectRunStatusForTaskGrouping, projectTaskStatusForGrouping, projectTaskStatusWithSessions, normalizeTaskAssigneeFilter, readTaskAssigneeLogins, selectAdhocRuns, selectAdhocRunsForWorkspace, selectApprovalsForRun, selectApprovalsForWorkspace, selectGraphsForWorkspace, selectPendingApprovals, selectPrimaryWorkspace, selectQueueForWorkspace, selectRun, selectRunsByTask, selectRunsForTask, selectRunsForWorkspace, selectTask, selectTasksByStatus, selectTasksByWorkspace, selectTasksForWorkspace, selectTasksGroupedByStatus, selectTasksAssignedTo, selectTasksAssignedToMe, selectUserInputsForRun, selectUserInputsForWorkspace, selectWorkspace, selectWorkspaces, type RigTaskSessionProjection, type RigTaskStatusGroup, type RigTaskStatusGroupingInput, } from "./rigSelectors";
|
|
10
10
|
export { buildTaskReferenceIndex, computeTaskBlockingDepths, computeTaskDependencyBadges, disjointScope, isTaskTerminalStatus, rankReadyTasks, readTaskBlockingDependencyRefs, readTaskDependencyRefs, readTaskMetadataStringList, readTaskScope, readTaskSourceIssueId, resolveTaskReference, selectNextReadyTaskByPriority, selectRankedReadyTasks, type RankedReadyTask, type RankedReadyTaskOptions, type ReadyTaskSelectionMode, type TaskDependencyBadge, type TaskDependencyBadgeKind, type TaskDependencyBadgeSummary, type TaskDependencyProjection, type TaskScopeInput, } from "./taskGraph";
|
|
11
11
|
export { extractTaskCode, extractTaskGroupKey, stripTaskCode, } from "./taskGraphCodes";
|
|
12
12
|
export { buildTaskGraphLayout, type TaskGraphEdge, type TaskGraphLane, type TaskGraphLayout, type TaskGraphNode, type TaskGraphStage, } from "./taskGraphLayout";
|
|
13
|
-
export { PipelineUnresolvableError, resolveStagePipeline, type DroppedStageAnchor, type
|
|
13
|
+
export { PipelineUnresolvableError, resolveStagePipeline, type DroppedStageAnchor, type ResolvableStage, type ResolvedStagePipeline, type ResolveStagePipelineInput, type StageKind, type StageMutation, type StageMutationOp, type StageResolutionRecordEntry, type StageWrapper, } from "./stageResolve";
|
|
14
14
|
export { rankTasks, scoreTask, type RankedTask, type TaskCriticality, type TaskScoreInput, } from "./taskScore";
|
|
15
15
|
export { buildDependencyGraphModel, type BuildDependencyGraphModelOptions, type DependencyEdge, type DependencyEdgeType, type DependencyGraphModel, type DependencyNode, } from "./dependencyGraph";
|
|
16
16
|
export { rollupByAssignee, rollupByEpic, type AssigneeRollup, type BlockerClass, type EpicRollup, } from "./rollups";
|
package/dist/src/index.js
CHANGED
|
@@ -60,6 +60,37 @@ function definePlugin(meta, runtime) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
const declaredCapabilities = new Map((validated.contributes?.capabilities ?? []).map((capability) => [capability.id, capability]));
|
|
64
|
+
for (const capability of runtime.featureCapabilities ?? []) {
|
|
65
|
+
if (!declaredCapabilities.has(capability.id)) {
|
|
66
|
+
throw new Error(`definePlugin(${validated.name}): executable capability "${capability.id}" has no matching metadata entry in contributes.capabilities`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const declaredPanels = new Map((validated.contributes?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
70
|
+
for (const panel of runtime.panels ?? []) {
|
|
71
|
+
const metadata = declaredPanels.get(panel.id);
|
|
72
|
+
if (!metadata) {
|
|
73
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" has no matching metadata entry in contributes.panels`);
|
|
74
|
+
}
|
|
75
|
+
if (metadata.slot !== panel.slot) {
|
|
76
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" slot "${panel.slot}" does not match metadata slot "${metadata.slot}"`);
|
|
77
|
+
}
|
|
78
|
+
if (metadata.capabilityId !== panel.capabilityId) {
|
|
79
|
+
throw new Error(`definePlugin(${validated.name}): executable panel "${panel.id}" capabilityId "${panel.capabilityId ?? "(none)"}" does not match metadata capabilityId "${metadata.capabilityId ?? "(none)"}"`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const declaredBlockerClassifiers = new Map((validated.contributes?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
83
|
+
for (const classifier of runtime.blockerClassifiers ?? []) {
|
|
84
|
+
if (!declaredBlockerClassifiers.has(classifier.id)) {
|
|
85
|
+
throw new Error(`definePlugin(${validated.name}): executable blocker classifier "${classifier.id}" has no matching metadata entry in contributes.blockerClassifiers`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const declaredCliCommands = new Map((validated.contributes?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
89
|
+
for (const command of runtime.cliCommands ?? []) {
|
|
90
|
+
if (!declaredCliCommands.has(command.id)) {
|
|
91
|
+
throw new Error(`definePlugin(${validated.name}): executable cli command "${command.id}" has no matching metadata entry in contributes.cliCommands`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
63
94
|
return { ...validated, __runtime: runtime };
|
|
64
95
|
}
|
|
65
96
|
// packages/core/src/define-config.ts
|
|
@@ -176,6 +207,41 @@ function assertRuntimeMatchesMetadata(plugin) {
|
|
|
176
207
|
}
|
|
177
208
|
}
|
|
178
209
|
}
|
|
210
|
+
const declaredCapabilities = new Map((plugin.contributes?.capabilities ?? []).map((capability) => [capability.id, capability]));
|
|
211
|
+
const runtimeCapabilities = new Map((plugin.__runtime?.featureCapabilities ?? []).map((capability) => [capability.id, capability]));
|
|
212
|
+
for (const capability of runtimeCapabilities.values()) {
|
|
213
|
+
if (!declaredCapabilities.has(capability.id)) {
|
|
214
|
+
throw new Error(`plugin "${plugin.name}" executable capability "${capability.id}" has no matching metadata entry in contributes.capabilities`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const declaredPanels = new Map((plugin.contributes?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
218
|
+
const runtimePanels = new Map((plugin.__runtime?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
219
|
+
for (const panel of runtimePanels.values()) {
|
|
220
|
+
const metadata = declaredPanels.get(panel.id);
|
|
221
|
+
if (!metadata) {
|
|
222
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" has no matching metadata entry in contributes.panels`);
|
|
223
|
+
}
|
|
224
|
+
if (metadata.slot !== panel.slot) {
|
|
225
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" slot "${panel.slot}" does not match metadata slot "${metadata.slot}"`);
|
|
226
|
+
}
|
|
227
|
+
if (metadata.capabilityId !== panel.capabilityId) {
|
|
228
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" capabilityId "${panel.capabilityId ?? "(none)"}" does not match metadata capabilityId "${metadata.capabilityId ?? "(none)"}"`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const declaredBlockerClassifiers = new Map((plugin.contributes?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
232
|
+
const runtimeBlockerClassifiers = new Map((plugin.__runtime?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
233
|
+
for (const classifier of runtimeBlockerClassifiers.values()) {
|
|
234
|
+
if (!declaredBlockerClassifiers.has(classifier.id)) {
|
|
235
|
+
throw new Error(`plugin "${plugin.name}" executable blocker classifier "${classifier.id}" has no matching metadata entry in contributes.blockerClassifiers`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const declaredCliCommands = new Map((plugin.contributes?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
239
|
+
const runtimeCliCommands = new Map((plugin.__runtime?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
240
|
+
for (const command of runtimeCliCommands.values()) {
|
|
241
|
+
if (!declaredCliCommands.has(command.id)) {
|
|
242
|
+
throw new Error(`plugin "${plugin.name}" executable cli command "${command.id}" has no matching metadata entry in contributes.cliCommands`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
179
245
|
}
|
|
180
246
|
function createPluginHost(plugins) {
|
|
181
247
|
assertUniquePluginNames(plugins);
|
|
@@ -190,8 +256,18 @@ function createPluginHost(plugins) {
|
|
|
190
256
|
const taskFieldExtensions = [];
|
|
191
257
|
const taskSources = [];
|
|
192
258
|
const cliCommands = [];
|
|
259
|
+
const capabilities = [];
|
|
260
|
+
const panels = [];
|
|
261
|
+
const blockerClassifiers = [];
|
|
262
|
+
const stages = [];
|
|
263
|
+
const stageMutations = [];
|
|
264
|
+
const stageExecutors = {};
|
|
193
265
|
const executableValidators = [];
|
|
194
266
|
const executableTaskSources = [];
|
|
267
|
+
const executableCapabilities = [];
|
|
268
|
+
const executablePanels = [];
|
|
269
|
+
const executableBlockerClassifiers = [];
|
|
270
|
+
const executableCliCommands = [];
|
|
195
271
|
for (const plugin of plugins) {
|
|
196
272
|
const c = plugin.contributes;
|
|
197
273
|
if (!c)
|
|
@@ -203,6 +279,18 @@ function createPluginHost(plugins) {
|
|
|
203
279
|
if (plugin.__runtime?.taskSources) {
|
|
204
280
|
executableTaskSources.push(...plugin.__runtime.taskSources.map((item) => ({ item, pluginName })));
|
|
205
281
|
}
|
|
282
|
+
if (plugin.__runtime?.featureCapabilities) {
|
|
283
|
+
executableCapabilities.push(...plugin.__runtime.featureCapabilities.map((item) => ({ item, pluginName })));
|
|
284
|
+
}
|
|
285
|
+
if (plugin.__runtime?.panels) {
|
|
286
|
+
executablePanels.push(...plugin.__runtime.panels.map((item) => ({ item, pluginName })));
|
|
287
|
+
}
|
|
288
|
+
if (plugin.__runtime?.blockerClassifiers) {
|
|
289
|
+
executableBlockerClassifiers.push(...plugin.__runtime.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
290
|
+
}
|
|
291
|
+
if (plugin.__runtime?.cliCommands) {
|
|
292
|
+
executableCliCommands.push(...plugin.__runtime.cliCommands.map((item) => ({ item, pluginName })));
|
|
293
|
+
}
|
|
206
294
|
if (c.validators)
|
|
207
295
|
validators.push(...c.validators.map((item) => ({ item, pluginName })));
|
|
208
296
|
if (c.hooks)
|
|
@@ -219,9 +307,25 @@ function createPluginHost(plugins) {
|
|
|
219
307
|
taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
|
|
220
308
|
if (c.cliCommands)
|
|
221
309
|
cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
|
|
310
|
+
if (c.capabilities)
|
|
311
|
+
capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
|
|
312
|
+
if (c.panels)
|
|
313
|
+
panels.push(...c.panels.map((item) => ({ item, pluginName })));
|
|
314
|
+
if (c.blockerClassifiers)
|
|
315
|
+
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
316
|
+
if (c.stages)
|
|
317
|
+
stages.push(...c.stages.map((item) => ({ item, pluginName })));
|
|
318
|
+
if (c.stageMutations)
|
|
319
|
+
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
320
|
+
if (plugin.__runtime?.stages)
|
|
321
|
+
Object.assign(stageExecutors, plugin.__runtime.stages);
|
|
222
322
|
}
|
|
223
323
|
indexById(executableValidators, "executableValidator");
|
|
224
324
|
indexById(executableTaskSources, "executableTaskSource");
|
|
325
|
+
indexById(executableCapabilities, "executableCapability");
|
|
326
|
+
indexById(executablePanels, "executablePanel");
|
|
327
|
+
indexById(executableBlockerClassifiers, "executableBlockerClassifier");
|
|
328
|
+
indexById(executableCliCommands, "executableCliCommand");
|
|
225
329
|
const taskSourceFactoryByKind = new Map;
|
|
226
330
|
const taskSourceKindRegistrant = new Map;
|
|
227
331
|
for (const { item, pluginName } of executableTaskSources) {
|
|
@@ -239,6 +343,9 @@ function createPluginHost(plugins) {
|
|
|
239
343
|
const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
|
|
240
344
|
const taskSourceMap = indexById(taskSources, "taskSource");
|
|
241
345
|
const cliCommandMap = indexById(cliCommands, "cliCommand");
|
|
346
|
+
const capabilityMap = indexById(capabilities, "capability");
|
|
347
|
+
const panelMap = indexById(panels, "panel");
|
|
348
|
+
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
242
349
|
const allValidators = validators.map((c) => c.item);
|
|
243
350
|
const allHooks = hooks.map((c) => c.item);
|
|
244
351
|
const allSkills = skills.map((c) => c.item);
|
|
@@ -247,8 +354,34 @@ function createPluginHost(plugins) {
|
|
|
247
354
|
const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
|
|
248
355
|
const allTaskSources = taskSources.map((c) => c.item);
|
|
249
356
|
const allCliCommands = cliCommands.map((c) => c.item);
|
|
357
|
+
const allStageMutations = stageMutations.map((c) => c.item);
|
|
358
|
+
const allStages = stages.map((c) => c.item);
|
|
359
|
+
const allCapabilities = capabilities.map((c) => c.item);
|
|
360
|
+
const allPanels = panels.map((c) => c.item);
|
|
361
|
+
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
250
362
|
const allExecutableValidators = executableValidators.map((c) => c.item);
|
|
251
363
|
const allExecutableTaskSources = executableTaskSources.map((c) => c.item);
|
|
364
|
+
const allExecutableCapabilities = executableCapabilities.map((c) => c.item);
|
|
365
|
+
const allExecutablePanels = executablePanels.map((c) => c.item);
|
|
366
|
+
const allExecutableBlockerClassifiers = executableBlockerClassifiers.map((c) => c.item);
|
|
367
|
+
const allExecutableCliCommands = executableCliCommands.map((c) => c.item);
|
|
368
|
+
const executableCliCommandByName = new Map;
|
|
369
|
+
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
370
|
+
const existing = executableCliCommandByName.get(selector);
|
|
371
|
+
if (existing && existing.item.id !== contribution.item.id) {
|
|
372
|
+
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}"`);
|
|
373
|
+
}
|
|
374
|
+
executableCliCommandByName.set(selector, contribution);
|
|
375
|
+
};
|
|
376
|
+
for (const contribution of executableCliCommands) {
|
|
377
|
+
const command = contribution.item;
|
|
378
|
+
const family = command.family ?? command.id;
|
|
379
|
+
registerExecutableCliCommandSelector(command.id, contribution);
|
|
380
|
+
registerExecutableCliCommandSelector(family, contribution);
|
|
381
|
+
for (const alias of command.aliases ?? []) {
|
|
382
|
+
registerExecutableCliCommandSelector(alias, contribution);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
252
385
|
return {
|
|
253
386
|
getValidator: (id) => validatorMap.get(id),
|
|
254
387
|
getHook: (id) => hookMap.get(id),
|
|
@@ -258,6 +391,9 @@ function createPluginHost(plugins) {
|
|
|
258
391
|
getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
|
|
259
392
|
getTaskSource: (id) => taskSourceMap.get(id),
|
|
260
393
|
getCliCommand: (id) => cliCommandMap.get(id),
|
|
394
|
+
getCapability: (id) => capabilityMap.get(id),
|
|
395
|
+
getPanel: (id) => panelMap.get(id),
|
|
396
|
+
getBlockerClassifier: (id) => blockerClassifierMap.get(id),
|
|
261
397
|
listValidators: () => allValidators,
|
|
262
398
|
listHooks: () => allHooks,
|
|
263
399
|
listSkills: () => allSkills,
|
|
@@ -266,30 +402,44 @@ function createPluginHost(plugins) {
|
|
|
266
402
|
listTaskFieldExtensions: () => allTaskFieldExtensions,
|
|
267
403
|
listTaskSources: () => allTaskSources,
|
|
268
404
|
listCliCommands: () => allCliCommands,
|
|
405
|
+
listCapabilities: () => allCapabilities,
|
|
406
|
+
listPanels: () => allPanels,
|
|
407
|
+
listBlockerClassifiers: () => allBlockerClassifiers,
|
|
408
|
+
listStages: () => allStages,
|
|
409
|
+
listStageMutations: () => allStageMutations,
|
|
410
|
+
listStageExecutors: () => stageExecutors,
|
|
269
411
|
listExecutableValidators: () => allExecutableValidators,
|
|
270
412
|
listExecutableTaskSources: () => allExecutableTaskSources,
|
|
413
|
+
listExecutableCapabilities: () => allExecutableCapabilities,
|
|
414
|
+
listExecutablePanels: () => allExecutablePanels,
|
|
415
|
+
listExecutableBlockerClassifiers: () => allExecutableBlockerClassifiers,
|
|
416
|
+
listExecutableCliCommands: () => allExecutableCliCommands,
|
|
417
|
+
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
271
418
|
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
272
419
|
};
|
|
273
420
|
}
|
|
274
421
|
// packages/core/src/rig-init-builder.ts
|
|
275
422
|
function buildRigInitConfigSource(input) {
|
|
276
423
|
const lines = [`import { defineConfig } from "@rig/core";`];
|
|
277
|
-
if (input.useStandardPlugin
|
|
278
|
-
lines.push(`import
|
|
279
|
-
|
|
280
|
-
|
|
424
|
+
if (input.useStandardPlugin) {
|
|
425
|
+
lines.push(`import { standardProjectPlugins } from "@rig/standard-plugin/bundle";`);
|
|
426
|
+
if (input.taskSource.kind === "github-issues") {
|
|
427
|
+
lines.push(`import { createStateGitHubCredentialProvider } from "@rig/standard-plugin";`);
|
|
428
|
+
}
|
|
281
429
|
}
|
|
282
430
|
lines.push(``, `export default defineConfig({`);
|
|
283
431
|
const projectRepo = input.projectRepo ?? (input.taskSource.kind === "github-issues" ? `${input.taskSource.owner}/${input.taskSource.repo}` : undefined);
|
|
284
432
|
lines.push(projectRepo ? ` project: { name: ${JSON.stringify(input.projectName)}, repo: ${JSON.stringify(projectRepo)} },` : ` project: { name: ${JSON.stringify(input.projectName)} },`);
|
|
285
433
|
if (input.useStandardPlugin && input.taskSource.kind === "github-issues") {
|
|
286
|
-
lines.push(` plugins: [
|
|
287
|
-
lines.push(`
|
|
288
|
-
lines.push(`
|
|
289
|
-
lines.push(`
|
|
434
|
+
lines.push(` plugins: [...standardProjectPlugins({`);
|
|
435
|
+
lines.push(` standard: {`);
|
|
436
|
+
lines.push(` githubCredentialProvider: createStateGitHubCredentialProvider(),`);
|
|
437
|
+
lines.push(` githubWorkspaceId: ${JSON.stringify(`${input.taskSource.owner}/${input.taskSource.repo}`)},`);
|
|
438
|
+
lines.push(` githubUserId: process.env.RIG_GITHUB_USER_ID ?? "server-selected-user",`);
|
|
439
|
+
lines.push(` },`);
|
|
290
440
|
lines.push(` })],`);
|
|
291
441
|
} else {
|
|
292
|
-
lines.push(` plugins: [${input.useStandardPlugin ? "
|
|
442
|
+
lines.push(` plugins: [${input.useStandardPlugin ? "...standardProjectPlugins()" : ""}],`);
|
|
293
443
|
}
|
|
294
444
|
if (input.taskSource.kind === "github-issues") {
|
|
295
445
|
lines.push(` taskSource: {`);
|
|
@@ -3102,9 +3252,6 @@ function assertNoDuplicateMutationTargets(mutations, op) {
|
|
|
3102
3252
|
seen.set(id, contributorOf(mutation));
|
|
3103
3253
|
}
|
|
3104
3254
|
}
|
|
3105
|
-
function hasProtectedGrant(grants, stageId, pluginId, op) {
|
|
3106
|
-
return grants.some((grant) => grant.stageId === stageId && grant.pluginId === pluginId && (grant.op === undefined || grant.op === op));
|
|
3107
|
-
}
|
|
3108
3255
|
function mergeAnchors(current, incoming) {
|
|
3109
3256
|
return uniqueSorted([...current, ...incoming ?? []]);
|
|
3110
3257
|
}
|
|
@@ -3235,19 +3382,18 @@ function resolveStagePipeline(input) {
|
|
|
3235
3382
|
const mutations = [...input.mutations ?? []];
|
|
3236
3383
|
assertNoDuplicateMutationTargets(mutations, "insert");
|
|
3237
3384
|
assertNoDuplicateMutationTargets(mutations, "replace");
|
|
3238
|
-
const grants = input.protectedStageGrants ?? [];
|
|
3239
3385
|
const states = new Map;
|
|
3240
3386
|
const removedStates = new Map;
|
|
3241
3387
|
for (const [index, stage] of input.defaultStages.entries()) {
|
|
3242
3388
|
states.set(stage.id, {
|
|
3243
|
-
stage,
|
|
3389
|
+
stage: { ...stage, protected: false },
|
|
3244
3390
|
before: [...stage.before ?? []],
|
|
3245
3391
|
after: [...stage.after ?? []],
|
|
3246
3392
|
baseIndex: index,
|
|
3247
3393
|
contributedBy: "default",
|
|
3248
3394
|
wrappers: [],
|
|
3249
3395
|
droppedAnchors: [],
|
|
3250
|
-
isProtected:
|
|
3396
|
+
isProtected: false
|
|
3251
3397
|
});
|
|
3252
3398
|
}
|
|
3253
3399
|
for (const mutation of mutations.filter((entry) => entry.op === "remove").sort(stableMutationCompare)) {
|
|
@@ -3255,13 +3401,9 @@ function resolveStagePipeline(input) {
|
|
|
3255
3401
|
const contributor = contributorOf(mutation);
|
|
3256
3402
|
if (!state)
|
|
3257
3403
|
continue;
|
|
3258
|
-
if (state.isProtected && !hasProtectedGrant(grants, mutation.id, contributor, "remove")) {
|
|
3259
|
-
throw new PipelineUnresolvableError(`Protected stage ${mutation.id} cannot be removed by ${contributor} without an explicit grant`, [], [contributor]);
|
|
3260
|
-
}
|
|
3261
3404
|
const removedState = {
|
|
3262
3405
|
...state,
|
|
3263
|
-
removedBy: contributor
|
|
3264
|
-
...state.isProtected ? { grantUsedBy: contributor } : {}
|
|
3406
|
+
removedBy: contributor
|
|
3265
3407
|
};
|
|
3266
3408
|
removedStates.set(mutation.id, removedState);
|
|
3267
3409
|
states.delete(mutation.id);
|
|
@@ -3271,10 +3413,7 @@ function resolveStagePipeline(input) {
|
|
|
3271
3413
|
const contributor = contributorOf(mutation);
|
|
3272
3414
|
if (!state)
|
|
3273
3415
|
continue;
|
|
3274
|
-
|
|
3275
|
-
throw new PipelineUnresolvableError(`Protected stage ${mutation.id} cannot be replaced by ${contributor} without an explicit grant`, [], [contributor]);
|
|
3276
|
-
}
|
|
3277
|
-
const replacement = { ...mutation.stage, id: mutation.id };
|
|
3416
|
+
const replacement = { ...mutation.stage, id: mutation.id, protected: false };
|
|
3278
3417
|
states.set(mutation.id, {
|
|
3279
3418
|
...state,
|
|
3280
3419
|
stage: replacement,
|
|
@@ -3282,8 +3421,7 @@ function resolveStagePipeline(input) {
|
|
|
3282
3421
|
after: mutation.stage.after ? [...mutation.stage.after] : state.after,
|
|
3283
3422
|
replacedBy: contributor,
|
|
3284
3423
|
contributedBy: state.contributedBy,
|
|
3285
|
-
isProtected:
|
|
3286
|
-
...state.isProtected ? { grantUsedBy: contributor } : {}
|
|
3424
|
+
isProtected: false
|
|
3287
3425
|
});
|
|
3288
3426
|
}
|
|
3289
3427
|
for (const mutation of mutations.filter((entry) => entry.op === "insert").sort(stableMutationCompare)) {
|
|
@@ -3292,14 +3430,14 @@ function resolveStagePipeline(input) {
|
|
|
3292
3430
|
throw new PipelineUnresolvableError(`Inserted stage ${mutation.stage.id} conflicts with an existing stage`, [], [contributor]);
|
|
3293
3431
|
}
|
|
3294
3432
|
states.set(mutation.stage.id, {
|
|
3295
|
-
stage: mutation.stage,
|
|
3433
|
+
stage: { ...mutation.stage, protected: false },
|
|
3296
3434
|
before: [...mutation.stage.before ?? []],
|
|
3297
3435
|
after: [...mutation.stage.after ?? []],
|
|
3298
3436
|
baseIndex: null,
|
|
3299
3437
|
contributedBy: contributor,
|
|
3300
3438
|
wrappers: [],
|
|
3301
3439
|
droppedAnchors: [],
|
|
3302
|
-
isProtected:
|
|
3440
|
+
isProtected: false
|
|
3303
3441
|
});
|
|
3304
3442
|
}
|
|
3305
3443
|
for (const mutation of mutations.filter((entry) => entry.op === "reorder").sort(stableMutationCompare)) {
|
|
@@ -3398,8 +3536,7 @@ function resolveStagePipeline(input) {
|
|
|
3398
3536
|
...state.replacedBy ? { replacedBy: state.replacedBy } : {},
|
|
3399
3537
|
...wrappedBy.length > 0 ? { wrappedBy } : {},
|
|
3400
3538
|
...state.droppedAnchors.length > 0 ? { droppedAnchors: state.droppedAnchors.toSorted((left, right) => left.anchor.localeCompare(right.anchor)) } : {},
|
|
3401
|
-
isProtected: state.isProtected
|
|
3402
|
-
...state.grantUsedBy ? { grantUsedBy: state.grantUsedBy } : {}
|
|
3539
|
+
isProtected: state.isProtected
|
|
3403
3540
|
});
|
|
3404
3541
|
}
|
|
3405
3542
|
record.push(...[...removedStates.entries()].toSorted((left, right) => {
|
|
@@ -3412,8 +3549,7 @@ function resolveStagePipeline(input) {
|
|
|
3412
3549
|
stageId,
|
|
3413
3550
|
contributedBy: state.contributedBy,
|
|
3414
3551
|
...state.removedBy ? { removedBy: state.removedBy } : {},
|
|
3415
|
-
isProtected: state.isProtected
|
|
3416
|
-
...state.grantUsedBy ? { grantUsedBy: state.grantUsedBy } : {}
|
|
3552
|
+
isProtected: state.isProtected
|
|
3417
3553
|
})));
|
|
3418
3554
|
return { stages, order, record, cycles: [] };
|
|
3419
3555
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { RegisteredValidator, RigPluginWithRuntime, TaskSourceFactoryEntry } from "./plugin-runtime";
|
|
1
|
+
import type { BlockerClassifierRegistration, CliCommandRegistration, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RepoSourceRegistration, AgentRoleRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
2
|
+
import type { RegisteredValidator, RigPluginWithRuntime, RuntimeBlockerClassifier, RuntimeCliCommand, RuntimeFeatureCapability, RuntimePanelProducer, TaskSourceFactoryEntry } from "./plugin-runtime";
|
|
3
3
|
export interface PluginHost {
|
|
4
4
|
getValidator(id: string): ValidatorRegistration | undefined;
|
|
5
5
|
getHook(id: string): HookRegistration | undefined;
|
|
@@ -9,6 +9,9 @@ export interface PluginHost {
|
|
|
9
9
|
getTaskFieldExtension(id: string): TaskFieldExtension | undefined;
|
|
10
10
|
getTaskSource(id: string): TaskSourceRegistration | undefined;
|
|
11
11
|
getCliCommand(id: string): CliCommandRegistration | undefined;
|
|
12
|
+
getCapability(id: string): ProductCapabilityRegistration | undefined;
|
|
13
|
+
getPanel(id: string): PanelRegistration | undefined;
|
|
14
|
+
getBlockerClassifier(id: string): BlockerClassifierRegistration | undefined;
|
|
12
15
|
listValidators(): readonly ValidatorRegistration[];
|
|
13
16
|
listHooks(): readonly HookRegistration[];
|
|
14
17
|
listSkills(): readonly SkillRegistration[];
|
|
@@ -17,6 +20,23 @@ export interface PluginHost {
|
|
|
17
20
|
listTaskFieldExtensions(): readonly TaskFieldExtension[];
|
|
18
21
|
listTaskSources(): readonly TaskSourceRegistration[];
|
|
19
22
|
listCliCommands(): readonly CliCommandRegistration[];
|
|
23
|
+
listCapabilities(): readonly ProductCapabilityRegistration[];
|
|
24
|
+
listPanels(): readonly PanelRegistration[];
|
|
25
|
+
listBlockerClassifiers(): readonly BlockerClassifierRegistration[];
|
|
26
|
+
listStages(): readonly Stage[];
|
|
27
|
+
/**
|
|
28
|
+
* Stage mutations contributed by all loaded plugins, flattened in load order.
|
|
29
|
+
* The kernel resolves these (+ the default-lifecycle stages) into the run
|
|
30
|
+
* pipeline — this is the wire that makes "every plugin mutates the lifecycle"
|
|
31
|
+
* real (drift pre-merge gate, closure observer, reclassify observer).
|
|
32
|
+
*/
|
|
33
|
+
listStageMutations(): readonly StageMutation[];
|
|
34
|
+
/**
|
|
35
|
+
* Executable stage implementations contributed by plugins (`runtime.stages`),
|
|
36
|
+
* merged by stage id. The kernel's stage runner uses these to execute
|
|
37
|
+
* plugin-contributed stages inserted via stageMutations.
|
|
38
|
+
*/
|
|
39
|
+
listStageExecutors(): Readonly<Record<string, StageRun>>;
|
|
20
40
|
/**
|
|
21
41
|
* Executable validators contributed by plugins. Only validators whose
|
|
22
42
|
* metadata appears in `listValidators()` and whose plugin shipped a runtime
|
|
@@ -30,6 +50,11 @@ export interface PluginHost {
|
|
|
30
50
|
* source kinds (Linear, Jira, custom) declared by plugins.
|
|
31
51
|
*/
|
|
32
52
|
listExecutableTaskSources(): readonly TaskSourceFactoryEntry[];
|
|
53
|
+
listExecutableCapabilities(): readonly RuntimeFeatureCapability[];
|
|
54
|
+
listExecutablePanels(): readonly RuntimePanelProducer[];
|
|
55
|
+
listExecutableBlockerClassifiers(): readonly RuntimeBlockerClassifier[];
|
|
56
|
+
listExecutableCliCommands(): readonly RuntimeCliCommand[];
|
|
57
|
+
resolveExecutableCliCommand(requested: string): RuntimeCliCommand | undefined;
|
|
33
58
|
/**
|
|
34
59
|
* Look up an executable task source factory by its `kind` (the value in
|
|
35
60
|
* `config.taskSource.kind`). Returns undefined when no plugin provides
|
package/dist/src/plugin-host.js
CHANGED
|
@@ -76,6 +76,41 @@ function assertRuntimeMatchesMetadata(plugin) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
const declaredCapabilities = new Map((plugin.contributes?.capabilities ?? []).map((capability) => [capability.id, capability]));
|
|
80
|
+
const runtimeCapabilities = new Map((plugin.__runtime?.featureCapabilities ?? []).map((capability) => [capability.id, capability]));
|
|
81
|
+
for (const capability of runtimeCapabilities.values()) {
|
|
82
|
+
if (!declaredCapabilities.has(capability.id)) {
|
|
83
|
+
throw new Error(`plugin "${plugin.name}" executable capability "${capability.id}" has no matching metadata entry in contributes.capabilities`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const declaredPanels = new Map((plugin.contributes?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
87
|
+
const runtimePanels = new Map((plugin.__runtime?.panels ?? []).map((panel) => [panel.id, panel]));
|
|
88
|
+
for (const panel of runtimePanels.values()) {
|
|
89
|
+
const metadata = declaredPanels.get(panel.id);
|
|
90
|
+
if (!metadata) {
|
|
91
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" has no matching metadata entry in contributes.panels`);
|
|
92
|
+
}
|
|
93
|
+
if (metadata.slot !== panel.slot) {
|
|
94
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" slot "${panel.slot}" does not match metadata slot "${metadata.slot}"`);
|
|
95
|
+
}
|
|
96
|
+
if (metadata.capabilityId !== panel.capabilityId) {
|
|
97
|
+
throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" capabilityId "${panel.capabilityId ?? "(none)"}" does not match metadata capabilityId "${metadata.capabilityId ?? "(none)"}"`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const declaredBlockerClassifiers = new Map((plugin.contributes?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
101
|
+
const runtimeBlockerClassifiers = new Map((plugin.__runtime?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
|
|
102
|
+
for (const classifier of runtimeBlockerClassifiers.values()) {
|
|
103
|
+
if (!declaredBlockerClassifiers.has(classifier.id)) {
|
|
104
|
+
throw new Error(`plugin "${plugin.name}" executable blocker classifier "${classifier.id}" has no matching metadata entry in contributes.blockerClassifiers`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const declaredCliCommands = new Map((plugin.contributes?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
108
|
+
const runtimeCliCommands = new Map((plugin.__runtime?.cliCommands ?? []).map((command) => [command.id, command]));
|
|
109
|
+
for (const command of runtimeCliCommands.values()) {
|
|
110
|
+
if (!declaredCliCommands.has(command.id)) {
|
|
111
|
+
throw new Error(`plugin "${plugin.name}" executable cli command "${command.id}" has no matching metadata entry in contributes.cliCommands`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
79
114
|
}
|
|
80
115
|
function createPluginHost(plugins) {
|
|
81
116
|
assertUniquePluginNames(plugins);
|
|
@@ -90,8 +125,18 @@ function createPluginHost(plugins) {
|
|
|
90
125
|
const taskFieldExtensions = [];
|
|
91
126
|
const taskSources = [];
|
|
92
127
|
const cliCommands = [];
|
|
128
|
+
const capabilities = [];
|
|
129
|
+
const panels = [];
|
|
130
|
+
const blockerClassifiers = [];
|
|
131
|
+
const stages = [];
|
|
132
|
+
const stageMutations = [];
|
|
133
|
+
const stageExecutors = {};
|
|
93
134
|
const executableValidators = [];
|
|
94
135
|
const executableTaskSources = [];
|
|
136
|
+
const executableCapabilities = [];
|
|
137
|
+
const executablePanels = [];
|
|
138
|
+
const executableBlockerClassifiers = [];
|
|
139
|
+
const executableCliCommands = [];
|
|
95
140
|
for (const plugin of plugins) {
|
|
96
141
|
const c = plugin.contributes;
|
|
97
142
|
if (!c)
|
|
@@ -103,6 +148,18 @@ function createPluginHost(plugins) {
|
|
|
103
148
|
if (plugin.__runtime?.taskSources) {
|
|
104
149
|
executableTaskSources.push(...plugin.__runtime.taskSources.map((item) => ({ item, pluginName })));
|
|
105
150
|
}
|
|
151
|
+
if (plugin.__runtime?.featureCapabilities) {
|
|
152
|
+
executableCapabilities.push(...plugin.__runtime.featureCapabilities.map((item) => ({ item, pluginName })));
|
|
153
|
+
}
|
|
154
|
+
if (plugin.__runtime?.panels) {
|
|
155
|
+
executablePanels.push(...plugin.__runtime.panels.map((item) => ({ item, pluginName })));
|
|
156
|
+
}
|
|
157
|
+
if (plugin.__runtime?.blockerClassifiers) {
|
|
158
|
+
executableBlockerClassifiers.push(...plugin.__runtime.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
159
|
+
}
|
|
160
|
+
if (plugin.__runtime?.cliCommands) {
|
|
161
|
+
executableCliCommands.push(...plugin.__runtime.cliCommands.map((item) => ({ item, pluginName })));
|
|
162
|
+
}
|
|
106
163
|
if (c.validators)
|
|
107
164
|
validators.push(...c.validators.map((item) => ({ item, pluginName })));
|
|
108
165
|
if (c.hooks)
|
|
@@ -119,9 +176,25 @@ function createPluginHost(plugins) {
|
|
|
119
176
|
taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
|
|
120
177
|
if (c.cliCommands)
|
|
121
178
|
cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
|
|
179
|
+
if (c.capabilities)
|
|
180
|
+
capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
|
|
181
|
+
if (c.panels)
|
|
182
|
+
panels.push(...c.panels.map((item) => ({ item, pluginName })));
|
|
183
|
+
if (c.blockerClassifiers)
|
|
184
|
+
blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
|
|
185
|
+
if (c.stages)
|
|
186
|
+
stages.push(...c.stages.map((item) => ({ item, pluginName })));
|
|
187
|
+
if (c.stageMutations)
|
|
188
|
+
stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
|
|
189
|
+
if (plugin.__runtime?.stages)
|
|
190
|
+
Object.assign(stageExecutors, plugin.__runtime.stages);
|
|
122
191
|
}
|
|
123
192
|
indexById(executableValidators, "executableValidator");
|
|
124
193
|
indexById(executableTaskSources, "executableTaskSource");
|
|
194
|
+
indexById(executableCapabilities, "executableCapability");
|
|
195
|
+
indexById(executablePanels, "executablePanel");
|
|
196
|
+
indexById(executableBlockerClassifiers, "executableBlockerClassifier");
|
|
197
|
+
indexById(executableCliCommands, "executableCliCommand");
|
|
125
198
|
const taskSourceFactoryByKind = new Map;
|
|
126
199
|
const taskSourceKindRegistrant = new Map;
|
|
127
200
|
for (const { item, pluginName } of executableTaskSources) {
|
|
@@ -139,6 +212,9 @@ function createPluginHost(plugins) {
|
|
|
139
212
|
const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
|
|
140
213
|
const taskSourceMap = indexById(taskSources, "taskSource");
|
|
141
214
|
const cliCommandMap = indexById(cliCommands, "cliCommand");
|
|
215
|
+
const capabilityMap = indexById(capabilities, "capability");
|
|
216
|
+
const panelMap = indexById(panels, "panel");
|
|
217
|
+
const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
|
|
142
218
|
const allValidators = validators.map((c) => c.item);
|
|
143
219
|
const allHooks = hooks.map((c) => c.item);
|
|
144
220
|
const allSkills = skills.map((c) => c.item);
|
|
@@ -147,8 +223,34 @@ function createPluginHost(plugins) {
|
|
|
147
223
|
const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
|
|
148
224
|
const allTaskSources = taskSources.map((c) => c.item);
|
|
149
225
|
const allCliCommands = cliCommands.map((c) => c.item);
|
|
226
|
+
const allStageMutations = stageMutations.map((c) => c.item);
|
|
227
|
+
const allStages = stages.map((c) => c.item);
|
|
228
|
+
const allCapabilities = capabilities.map((c) => c.item);
|
|
229
|
+
const allPanels = panels.map((c) => c.item);
|
|
230
|
+
const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
|
|
150
231
|
const allExecutableValidators = executableValidators.map((c) => c.item);
|
|
151
232
|
const allExecutableTaskSources = executableTaskSources.map((c) => c.item);
|
|
233
|
+
const allExecutableCapabilities = executableCapabilities.map((c) => c.item);
|
|
234
|
+
const allExecutablePanels = executablePanels.map((c) => c.item);
|
|
235
|
+
const allExecutableBlockerClassifiers = executableBlockerClassifiers.map((c) => c.item);
|
|
236
|
+
const allExecutableCliCommands = executableCliCommands.map((c) => c.item);
|
|
237
|
+
const executableCliCommandByName = new Map;
|
|
238
|
+
const registerExecutableCliCommandSelector = (selector, contribution) => {
|
|
239
|
+
const existing = executableCliCommandByName.get(selector);
|
|
240
|
+
if (existing && existing.item.id !== contribution.item.id) {
|
|
241
|
+
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}"`);
|
|
242
|
+
}
|
|
243
|
+
executableCliCommandByName.set(selector, contribution);
|
|
244
|
+
};
|
|
245
|
+
for (const contribution of executableCliCommands) {
|
|
246
|
+
const command = contribution.item;
|
|
247
|
+
const family = command.family ?? command.id;
|
|
248
|
+
registerExecutableCliCommandSelector(command.id, contribution);
|
|
249
|
+
registerExecutableCliCommandSelector(family, contribution);
|
|
250
|
+
for (const alias of command.aliases ?? []) {
|
|
251
|
+
registerExecutableCliCommandSelector(alias, contribution);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
152
254
|
return {
|
|
153
255
|
getValidator: (id) => validatorMap.get(id),
|
|
154
256
|
getHook: (id) => hookMap.get(id),
|
|
@@ -158,6 +260,9 @@ function createPluginHost(plugins) {
|
|
|
158
260
|
getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
|
|
159
261
|
getTaskSource: (id) => taskSourceMap.get(id),
|
|
160
262
|
getCliCommand: (id) => cliCommandMap.get(id),
|
|
263
|
+
getCapability: (id) => capabilityMap.get(id),
|
|
264
|
+
getPanel: (id) => panelMap.get(id),
|
|
265
|
+
getBlockerClassifier: (id) => blockerClassifierMap.get(id),
|
|
161
266
|
listValidators: () => allValidators,
|
|
162
267
|
listHooks: () => allHooks,
|
|
163
268
|
listSkills: () => allSkills,
|
|
@@ -166,8 +271,19 @@ function createPluginHost(plugins) {
|
|
|
166
271
|
listTaskFieldExtensions: () => allTaskFieldExtensions,
|
|
167
272
|
listTaskSources: () => allTaskSources,
|
|
168
273
|
listCliCommands: () => allCliCommands,
|
|
274
|
+
listCapabilities: () => allCapabilities,
|
|
275
|
+
listPanels: () => allPanels,
|
|
276
|
+
listBlockerClassifiers: () => allBlockerClassifiers,
|
|
277
|
+
listStages: () => allStages,
|
|
278
|
+
listStageMutations: () => allStageMutations,
|
|
279
|
+
listStageExecutors: () => stageExecutors,
|
|
169
280
|
listExecutableValidators: () => allExecutableValidators,
|
|
170
281
|
listExecutableTaskSources: () => allExecutableTaskSources,
|
|
282
|
+
listExecutableCapabilities: () => allExecutableCapabilities,
|
|
283
|
+
listExecutablePanels: () => allExecutablePanels,
|
|
284
|
+
listExecutableBlockerClassifiers: () => allExecutableBlockerClassifiers,
|
|
285
|
+
listExecutableCliCommands: () => allExecutableCliCommands,
|
|
286
|
+
resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
|
|
171
287
|
resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
|
|
172
288
|
};
|
|
173
289
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HookImplementation, RegisteredTaskSource, RigConfig, RigPlugin, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
1
|
+
import type { BlockerClassifierRegistration, CliCommandRegistration, HookImplementation, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RigConfig, RigPlugin, StageRun, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
2
2
|
export interface ValidatorResult {
|
|
3
3
|
id: string;
|
|
4
4
|
passed: boolean;
|
|
@@ -44,6 +44,23 @@ export interface TaskSourceFactoryContext {
|
|
|
44
44
|
export interface TaskSourceFactoryEntry extends TaskSourceRegistration {
|
|
45
45
|
factory(config: TaskSourceConfig, context?: TaskSourceFactoryContext): RegisteredTaskSource;
|
|
46
46
|
}
|
|
47
|
+
export interface RuntimeFeatureCapability extends ProductCapabilityRegistration {
|
|
48
|
+
run?: (input: unknown) => Promise<unknown> | unknown;
|
|
49
|
+
}
|
|
50
|
+
export interface RuntimePanelProducer extends PanelRegistration {
|
|
51
|
+
produce?: (context?: unknown) => Promise<unknown> | unknown;
|
|
52
|
+
}
|
|
53
|
+
export interface RuntimeBlockerClassifier extends BlockerClassifierRegistration {
|
|
54
|
+
classify(input: unknown): Promise<unknown> | unknown;
|
|
55
|
+
}
|
|
56
|
+
export interface RuntimeCliContext {
|
|
57
|
+
projectRoot: string;
|
|
58
|
+
outputMode?: string;
|
|
59
|
+
dryRun?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface RuntimeCliCommand extends CliCommandRegistration {
|
|
62
|
+
run(context: RuntimeCliContext, args: readonly string[]): Promise<unknown> | unknown;
|
|
63
|
+
}
|
|
47
64
|
export interface RigPluginRuntime {
|
|
48
65
|
validators?: readonly RegisteredValidator[];
|
|
49
66
|
taskSources?: readonly TaskSourceFactoryEntry[];
|
|
@@ -58,6 +75,30 @@ export interface RigPluginRuntime {
|
|
|
58
75
|
* `@rig/runtime/control-plane/hook-runner`).
|
|
59
76
|
*/
|
|
60
77
|
hooks?: Record<string, HookImplementation>;
|
|
78
|
+
/**
|
|
79
|
+
* Executable stage implementations keyed by stage id — the runtime counterpart
|
|
80
|
+
* to `contributes.stageMutations` (which carries only the ordering descriptor).
|
|
81
|
+
* When a plugin inserts/replaces a stage, its `run(ctx)` lives here. The plugin
|
|
82
|
+
* host flattens these (`listStageExecutors()`) so the kernel's stage runner can
|
|
83
|
+
* execute plugin-contributed stages (e.g. the docs-drift pre-merge gate).
|
|
84
|
+
*/
|
|
85
|
+
stages?: Record<string, StageRun>;
|
|
86
|
+
/**
|
|
87
|
+
* Executable product-capability registrations. This is deliberately separate
|
|
88
|
+
* from `capabilities`: that map is reserved for the five kernel provider
|
|
89
|
+
* capability tags resolved by `@rig/kernel-seed`.
|
|
90
|
+
*/
|
|
91
|
+
featureCapabilities?: readonly RuntimeFeatureCapability[];
|
|
92
|
+
panels?: readonly RuntimePanelProducer[];
|
|
93
|
+
blockerClassifiers?: readonly RuntimeBlockerClassifier[];
|
|
94
|
+
cliCommands?: readonly RuntimeCliCommand[];
|
|
95
|
+
/**
|
|
96
|
+
* Executable kernel-capability providers keyed by **capability tag**
|
|
97
|
+
* (`transport`, `journal`, …) — the same shape the kernel-seed resolver reads
|
|
98
|
+
* via `plugin.runtime.capabilities[tag]`. Which tags a plugin provides is
|
|
99
|
+
* declared by the top-level `provides: CapabilityTag[]`; this holds the impls.
|
|
100
|
+
*/
|
|
101
|
+
capabilities?: Record<string, unknown>;
|
|
61
102
|
}
|
|
62
103
|
export type RigPluginWithRuntime = RigPlugin & {
|
|
63
104
|
__runtime?: RigPluginRuntime;
|
|
@@ -2,22 +2,25 @@
|
|
|
2
2
|
// packages/core/src/rig-init-builder.ts
|
|
3
3
|
function buildRigInitConfigSource(input) {
|
|
4
4
|
const lines = [`import { defineConfig } from "@rig/core";`];
|
|
5
|
-
if (input.useStandardPlugin
|
|
6
|
-
lines.push(`import
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
if (input.useStandardPlugin) {
|
|
6
|
+
lines.push(`import { standardProjectPlugins } from "@rig/standard-plugin/bundle";`);
|
|
7
|
+
if (input.taskSource.kind === "github-issues") {
|
|
8
|
+
lines.push(`import { createStateGitHubCredentialProvider } from "@rig/standard-plugin";`);
|
|
9
|
+
}
|
|
9
10
|
}
|
|
10
11
|
lines.push(``, `export default defineConfig({`);
|
|
11
12
|
const projectRepo = input.projectRepo ?? (input.taskSource.kind === "github-issues" ? `${input.taskSource.owner}/${input.taskSource.repo}` : undefined);
|
|
12
13
|
lines.push(projectRepo ? ` project: { name: ${JSON.stringify(input.projectName)}, repo: ${JSON.stringify(projectRepo)} },` : ` project: { name: ${JSON.stringify(input.projectName)} },`);
|
|
13
14
|
if (input.useStandardPlugin && input.taskSource.kind === "github-issues") {
|
|
14
|
-
lines.push(` plugins: [
|
|
15
|
-
lines.push(`
|
|
16
|
-
lines.push(`
|
|
17
|
-
lines.push(`
|
|
15
|
+
lines.push(` plugins: [...standardProjectPlugins({`);
|
|
16
|
+
lines.push(` standard: {`);
|
|
17
|
+
lines.push(` githubCredentialProvider: createStateGitHubCredentialProvider(),`);
|
|
18
|
+
lines.push(` githubWorkspaceId: ${JSON.stringify(`${input.taskSource.owner}/${input.taskSource.repo}`)},`);
|
|
19
|
+
lines.push(` githubUserId: process.env.RIG_GITHUB_USER_ID ?? "server-selected-user",`);
|
|
20
|
+
lines.push(` },`);
|
|
18
21
|
lines.push(` })],`);
|
|
19
22
|
} else {
|
|
20
|
-
lines.push(` plugins: [${input.useStandardPlugin ? "
|
|
23
|
+
lines.push(` plugins: [${input.useStandardPlugin ? "...standardProjectPlugins()" : ""}],`);
|
|
21
24
|
}
|
|
22
25
|
if (input.taskSource.kind === "github-issues") {
|
|
23
26
|
lines.push(` taskSource: {`);
|
|
@@ -189,7 +189,7 @@ export declare function selectPolicyDecisionsForTask(snapshot: EngineReadModel |
|
|
|
189
189
|
readonly id: string;
|
|
190
190
|
readonly createdAt: string;
|
|
191
191
|
readonly decision: "allow" | "block" | "warn";
|
|
192
|
-
readonly mode: "
|
|
192
|
+
readonly mode: "observe" | "off" | "enforce";
|
|
193
193
|
readonly runId: string & import("effect/Brand").Brand<"RunId">;
|
|
194
194
|
readonly actionId: (string & import("effect/Brand").Brand<"ActionId">) | null;
|
|
195
195
|
readonly reason: string;
|
|
@@ -43,15 +43,9 @@ export type StageMutation<TStage extends ResolvableStage = ResolvableStage> = Re
|
|
|
43
43
|
after?: readonly string[];
|
|
44
44
|
contributedBy?: string;
|
|
45
45
|
}>;
|
|
46
|
-
export interface ProtectedStageGrant {
|
|
47
|
-
readonly stageId: string;
|
|
48
|
-
readonly pluginId: string;
|
|
49
|
-
readonly op?: "remove" | "replace";
|
|
50
|
-
}
|
|
51
46
|
export interface ResolveStagePipelineInput<TStage extends ResolvableStage = ResolvableStage> {
|
|
52
47
|
readonly defaultStages: readonly TStage[];
|
|
53
48
|
readonly mutations?: readonly StageMutation<TStage>[];
|
|
54
|
-
readonly protectedStageGrants?: readonly ProtectedStageGrant[];
|
|
55
49
|
}
|
|
56
50
|
export interface DroppedStageAnchor {
|
|
57
51
|
readonly stageId: string;
|
|
@@ -68,7 +62,6 @@ export interface StageResolutionRecordEntry {
|
|
|
68
62
|
readonly wrappedBy?: readonly string[];
|
|
69
63
|
readonly droppedAnchors?: readonly DroppedStageAnchor[];
|
|
70
64
|
readonly isProtected: boolean;
|
|
71
|
-
readonly grantUsedBy?: string;
|
|
72
65
|
}
|
|
73
66
|
export interface ResolvedStagePipeline<TStage extends ResolvableStage = ResolvableStage> {
|
|
74
67
|
readonly stages: readonly TStage[];
|
package/dist/src/stageResolve.js
CHANGED
|
@@ -54,9 +54,6 @@ function assertNoDuplicateMutationTargets(mutations, op) {
|
|
|
54
54
|
seen.set(id, contributorOf(mutation));
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
function hasProtectedGrant(grants, stageId, pluginId, op) {
|
|
58
|
-
return grants.some((grant) => grant.stageId === stageId && grant.pluginId === pluginId && (grant.op === undefined || grant.op === op));
|
|
59
|
-
}
|
|
60
57
|
function mergeAnchors(current, incoming) {
|
|
61
58
|
return uniqueSorted([...current, ...incoming ?? []]);
|
|
62
59
|
}
|
|
@@ -187,19 +184,18 @@ function resolveStagePipeline(input) {
|
|
|
187
184
|
const mutations = [...input.mutations ?? []];
|
|
188
185
|
assertNoDuplicateMutationTargets(mutations, "insert");
|
|
189
186
|
assertNoDuplicateMutationTargets(mutations, "replace");
|
|
190
|
-
const grants = input.protectedStageGrants ?? [];
|
|
191
187
|
const states = new Map;
|
|
192
188
|
const removedStates = new Map;
|
|
193
189
|
for (const [index, stage] of input.defaultStages.entries()) {
|
|
194
190
|
states.set(stage.id, {
|
|
195
|
-
stage,
|
|
191
|
+
stage: { ...stage, protected: false },
|
|
196
192
|
before: [...stage.before ?? []],
|
|
197
193
|
after: [...stage.after ?? []],
|
|
198
194
|
baseIndex: index,
|
|
199
195
|
contributedBy: "default",
|
|
200
196
|
wrappers: [],
|
|
201
197
|
droppedAnchors: [],
|
|
202
|
-
isProtected:
|
|
198
|
+
isProtected: false
|
|
203
199
|
});
|
|
204
200
|
}
|
|
205
201
|
for (const mutation of mutations.filter((entry) => entry.op === "remove").sort(stableMutationCompare)) {
|
|
@@ -207,13 +203,9 @@ function resolveStagePipeline(input) {
|
|
|
207
203
|
const contributor = contributorOf(mutation);
|
|
208
204
|
if (!state)
|
|
209
205
|
continue;
|
|
210
|
-
if (state.isProtected && !hasProtectedGrant(grants, mutation.id, contributor, "remove")) {
|
|
211
|
-
throw new PipelineUnresolvableError(`Protected stage ${mutation.id} cannot be removed by ${contributor} without an explicit grant`, [], [contributor]);
|
|
212
|
-
}
|
|
213
206
|
const removedState = {
|
|
214
207
|
...state,
|
|
215
|
-
removedBy: contributor
|
|
216
|
-
...state.isProtected ? { grantUsedBy: contributor } : {}
|
|
208
|
+
removedBy: contributor
|
|
217
209
|
};
|
|
218
210
|
removedStates.set(mutation.id, removedState);
|
|
219
211
|
states.delete(mutation.id);
|
|
@@ -223,10 +215,7 @@ function resolveStagePipeline(input) {
|
|
|
223
215
|
const contributor = contributorOf(mutation);
|
|
224
216
|
if (!state)
|
|
225
217
|
continue;
|
|
226
|
-
|
|
227
|
-
throw new PipelineUnresolvableError(`Protected stage ${mutation.id} cannot be replaced by ${contributor} without an explicit grant`, [], [contributor]);
|
|
228
|
-
}
|
|
229
|
-
const replacement = { ...mutation.stage, id: mutation.id };
|
|
218
|
+
const replacement = { ...mutation.stage, id: mutation.id, protected: false };
|
|
230
219
|
states.set(mutation.id, {
|
|
231
220
|
...state,
|
|
232
221
|
stage: replacement,
|
|
@@ -234,8 +223,7 @@ function resolveStagePipeline(input) {
|
|
|
234
223
|
after: mutation.stage.after ? [...mutation.stage.after] : state.after,
|
|
235
224
|
replacedBy: contributor,
|
|
236
225
|
contributedBy: state.contributedBy,
|
|
237
|
-
isProtected:
|
|
238
|
-
...state.isProtected ? { grantUsedBy: contributor } : {}
|
|
226
|
+
isProtected: false
|
|
239
227
|
});
|
|
240
228
|
}
|
|
241
229
|
for (const mutation of mutations.filter((entry) => entry.op === "insert").sort(stableMutationCompare)) {
|
|
@@ -244,14 +232,14 @@ function resolveStagePipeline(input) {
|
|
|
244
232
|
throw new PipelineUnresolvableError(`Inserted stage ${mutation.stage.id} conflicts with an existing stage`, [], [contributor]);
|
|
245
233
|
}
|
|
246
234
|
states.set(mutation.stage.id, {
|
|
247
|
-
stage: mutation.stage,
|
|
235
|
+
stage: { ...mutation.stage, protected: false },
|
|
248
236
|
before: [...mutation.stage.before ?? []],
|
|
249
237
|
after: [...mutation.stage.after ?? []],
|
|
250
238
|
baseIndex: null,
|
|
251
239
|
contributedBy: contributor,
|
|
252
240
|
wrappers: [],
|
|
253
241
|
droppedAnchors: [],
|
|
254
|
-
isProtected:
|
|
242
|
+
isProtected: false
|
|
255
243
|
});
|
|
256
244
|
}
|
|
257
245
|
for (const mutation of mutations.filter((entry) => entry.op === "reorder").sort(stableMutationCompare)) {
|
|
@@ -350,8 +338,7 @@ function resolveStagePipeline(input) {
|
|
|
350
338
|
...state.replacedBy ? { replacedBy: state.replacedBy } : {},
|
|
351
339
|
...wrappedBy.length > 0 ? { wrappedBy } : {},
|
|
352
340
|
...state.droppedAnchors.length > 0 ? { droppedAnchors: state.droppedAnchors.toSorted((left, right) => left.anchor.localeCompare(right.anchor)) } : {},
|
|
353
|
-
isProtected: state.isProtected
|
|
354
|
-
...state.grantUsedBy ? { grantUsedBy: state.grantUsedBy } : {}
|
|
341
|
+
isProtected: state.isProtected
|
|
355
342
|
});
|
|
356
343
|
}
|
|
357
344
|
record.push(...[...removedStates.entries()].toSorted((left, right) => {
|
|
@@ -364,8 +351,7 @@ function resolveStagePipeline(input) {
|
|
|
364
351
|
stageId,
|
|
365
352
|
contributedBy: state.contributedBy,
|
|
366
353
|
...state.removedBy ? { removedBy: state.removedBy } : {},
|
|
367
|
-
isProtected: state.isProtected
|
|
368
|
-
...state.grantUsedBy ? { grantUsedBy: state.grantUsedBy } : {}
|
|
354
|
+
isProtected: state.isProtected
|
|
369
355
|
})));
|
|
370
356
|
return { stages, order, record, cycles: [] };
|
|
371
357
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/core",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.137",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Config and plugin composition library for Rig's OMP extension ecosystem; not a product host/runtime.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"module": "./dist/src/index.js",
|
|
46
46
|
"types": "./dist/src/index.d.ts",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
48
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137",
|
|
49
49
|
"effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@8881a9b"
|
|
50
50
|
}
|
|
51
51
|
}
|