@rigkit/engine 0.2.1 → 0.2.3
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/package.json +1 -1
- package/src/authoring.ts +82 -50
- package/src/authoring.typecheck.ts +67 -0
- package/src/db/schema/core.ts +3 -7
- package/src/engine.test.ts +127 -103
- package/src/engine.ts +309 -475
- package/src/provider/types.ts +2 -30
- package/src/state.ts +6 -14
- package/src/types.ts +223 -109
- package/src/version.ts +1 -1
package/package.json
CHANGED
package/src/authoring.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RESERVED_WORKFLOW_OPERATION_IDS } from "./types.ts";
|
|
1
2
|
import type {
|
|
2
3
|
EnvResolver,
|
|
3
4
|
RigkitConfigDefinition,
|
|
@@ -5,14 +6,14 @@ import type {
|
|
|
5
6
|
OutputSchema,
|
|
6
7
|
OutputSchemaValue,
|
|
7
8
|
WorkflowDefinition,
|
|
8
|
-
WorkflowCreateDefinition,
|
|
9
|
-
WorkflowCreateHandler,
|
|
10
9
|
WorkflowInputFieldDefinition,
|
|
11
10
|
WorkflowInputShape,
|
|
12
11
|
WorkflowOperationDefinition,
|
|
13
12
|
WorkflowOperationInputBuilder,
|
|
14
13
|
WorkflowOperationInputHelpers,
|
|
15
14
|
WorkflowOperationOptions,
|
|
15
|
+
WorkflowWorkspaceOperationDefinition,
|
|
16
|
+
WorkflowWorkspaceOperationOptions,
|
|
16
17
|
WorkflowNodeDefinition,
|
|
17
18
|
WorkflowProviderDefinition,
|
|
18
19
|
WorkflowProviderMap,
|
|
@@ -25,7 +26,7 @@ import type {
|
|
|
25
26
|
} from "./types.ts";
|
|
26
27
|
|
|
27
28
|
const reservedTaskContextKeys = new Set(["ctx", "runtime", "providers"]);
|
|
28
|
-
const reservedHostOperationIds = new Set(
|
|
29
|
+
const reservedHostOperationIds = new Set<string>(RESERVED_WORKFLOW_OPERATION_IDS);
|
|
29
30
|
|
|
30
31
|
const readEnv = (name: string, fallback?: string): string => {
|
|
31
32
|
const value = process.env[name];
|
|
@@ -90,12 +91,11 @@ export function defineProvider<
|
|
|
90
91
|
const ProviderId extends string,
|
|
91
92
|
const Config extends object,
|
|
92
93
|
Runtime = unknown,
|
|
93
|
-
WorkspaceContext extends object = object,
|
|
94
94
|
>(
|
|
95
95
|
providerId: ProviderId,
|
|
96
|
-
config: WorkflowProviderDefinition<ProviderId, Config, Runtime
|
|
96
|
+
config: WorkflowProviderDefinition<ProviderId, Config, Runtime>["config"],
|
|
97
97
|
plugin?: unknown,
|
|
98
|
-
): WorkflowProviderDefinition<ProviderId, Config, Runtime
|
|
98
|
+
): WorkflowProviderDefinition<ProviderId, Config, Runtime> {
|
|
99
99
|
return {
|
|
100
100
|
kind: "rigkit.provider",
|
|
101
101
|
providerId,
|
|
@@ -120,14 +120,28 @@ export function isProviderDefinition(value: unknown): value is WorkflowProviderD
|
|
|
120
120
|
return Boolean(value && typeof value === "object" && getKind(value) === "rigkit.provider");
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
function createSequence<
|
|
123
|
+
function createSequence<
|
|
124
|
+
Providers extends WorkflowProviderMap,
|
|
125
|
+
InputContext extends JsonObject,
|
|
126
|
+
OutputContext extends JsonObject,
|
|
127
|
+
WorkspaceData extends object = JsonObject,
|
|
128
|
+
OperationIds extends string = never,
|
|
129
|
+
WorkspaceOperationIds extends string = never,
|
|
130
|
+
>(
|
|
124
131
|
app: WorkflowDefinition<string, Providers>,
|
|
125
132
|
name: string,
|
|
126
133
|
children: readonly WorkflowNodeDefinition<Providers, any, any>[],
|
|
127
|
-
workspace?: WorkflowWorkspaceDefinition<Providers, OutputContext>,
|
|
128
|
-
create?: WorkflowCreateDefinition<Providers, OutputContext>,
|
|
134
|
+
workspace?: WorkflowWorkspaceDefinition<Providers, OutputContext, any>,
|
|
129
135
|
operations: readonly WorkflowOperationDefinition<Providers, any>[] = [],
|
|
130
|
-
|
|
136
|
+
workspaceOperations: readonly WorkflowWorkspaceOperationDefinition<Providers, OutputContext, WorkspaceData, any>[] = [],
|
|
137
|
+
): WorkflowSequenceBuilder<
|
|
138
|
+
Providers,
|
|
139
|
+
InputContext,
|
|
140
|
+
OutputContext,
|
|
141
|
+
WorkspaceData,
|
|
142
|
+
OperationIds,
|
|
143
|
+
WorkspaceOperationIds
|
|
144
|
+
> {
|
|
131
145
|
const node = {
|
|
132
146
|
kind: "rigkit.workflow-node" as const,
|
|
133
147
|
nodeKind: "sequence" as const,
|
|
@@ -135,15 +149,15 @@ function createSequence<Providers extends WorkflowProviderMap, InputContext exte
|
|
|
135
149
|
workflow: app,
|
|
136
150
|
children,
|
|
137
151
|
workspaceDefinition: workspace,
|
|
138
|
-
createDefinition: create,
|
|
139
152
|
operations,
|
|
153
|
+
workspaceOperations,
|
|
140
154
|
task: (
|
|
141
155
|
taskName: string,
|
|
142
156
|
optionsOrHandler: WorkflowTaskOptions | WorkflowTaskHandler<Providers, OutputContext, any>,
|
|
143
157
|
maybeHandler?: WorkflowTaskHandler<Providers, OutputContext, any>,
|
|
144
158
|
) => {
|
|
145
159
|
const task = createTask(app, taskName, optionsOrHandler as any, maybeHandler as any);
|
|
146
|
-
return createSequence(app, name, [...children, task], workspace,
|
|
160
|
+
return createSequence(app, name, [...children, task], workspace, operations, workspaceOperations);
|
|
147
161
|
},
|
|
148
162
|
step: (
|
|
149
163
|
taskName: string,
|
|
@@ -151,11 +165,11 @@ function createSequence<Providers extends WorkflowProviderMap, InputContext exte
|
|
|
151
165
|
maybeHandler?: WorkflowTaskHandler<Providers, OutputContext, any>,
|
|
152
166
|
) => {
|
|
153
167
|
const task = createTask(app, taskName, optionsOrHandler as any, maybeHandler as any);
|
|
154
|
-
return createSequence(app, name, [...children, task], workspace,
|
|
168
|
+
return createSequence(app, name, [...children, task], workspace, operations, workspaceOperations);
|
|
155
169
|
},
|
|
156
170
|
add: (child: WorkflowNodeDefinition<Providers, any, any>) => {
|
|
157
171
|
assertSameWorkflow(app, child);
|
|
158
|
-
return createSequence(app, name, [...children, child], workspace,
|
|
172
|
+
return createSequence(app, name, [...children, child], workspace, operations, workspaceOperations);
|
|
159
173
|
},
|
|
160
174
|
parallel: (branches: Record<string, WorkflowNodeDefinition<Providers, any, any>>) => {
|
|
161
175
|
for (const [branchName, branch] of Object.entries(branches)) {
|
|
@@ -173,17 +187,39 @@ function createSequence<Providers extends WorkflowProviderMap, InputContext exte
|
|
|
173
187
|
workflow: app,
|
|
174
188
|
branches,
|
|
175
189
|
};
|
|
176
|
-
return createSequence(app, name, [...children, parallelNode], workspace,
|
|
190
|
+
return createSequence(app, name, [...children, parallelNode], workspace, operations, workspaceOperations);
|
|
191
|
+
},
|
|
192
|
+
workspace: (definition: WorkflowWorkspaceDefinition<Providers, OutputContext, any>) =>
|
|
193
|
+
createSequence(app, name, children, definition, operations, workspaceOperations),
|
|
194
|
+
operation: (id: string, options: WorkflowOperationOptions<Providers, any>) => {
|
|
195
|
+
const operation = createOperation(id, options);
|
|
196
|
+
assertUniqueOperationId(operations, operation.id, "Operation");
|
|
197
|
+
return createSequence(app, name, children, workspace, [...operations, operation], workspaceOperations);
|
|
198
|
+
},
|
|
199
|
+
workspaceOperation: (id: string, options: WorkflowWorkspaceOperationOptions<Providers, OutputContext, any, any>) => {
|
|
200
|
+
const operation = createWorkspaceOperation(id, options);
|
|
201
|
+
assertUniqueOperationId(workspaceOperations, operation.id, "Workspace operation");
|
|
202
|
+
return createSequence(app, name, children, workspace, operations, [...workspaceOperations, operation]);
|
|
177
203
|
},
|
|
178
|
-
workspace: (definition: WorkflowWorkspaceDefinition<Providers, OutputContext>) =>
|
|
179
|
-
createSequence(app, name, children, definition, create, operations),
|
|
180
|
-
create: (handler: WorkflowCreateHandler<Providers, OutputContext, any>) =>
|
|
181
|
-
createSequence(app, name, children, workspace, { handler }, operations),
|
|
182
|
-
operation: (id: string, options: WorkflowOperationOptions<Providers, any>) =>
|
|
183
|
-
createSequence(app, name, children, workspace, create, [...operations, createOperation(id, options)]),
|
|
184
204
|
};
|
|
185
205
|
|
|
186
|
-
return node as unknown as WorkflowSequenceBuilder<
|
|
206
|
+
return node as unknown as WorkflowSequenceBuilder<
|
|
207
|
+
Providers,
|
|
208
|
+
InputContext,
|
|
209
|
+
OutputContext,
|
|
210
|
+
WorkspaceData,
|
|
211
|
+
OperationIds,
|
|
212
|
+
WorkspaceOperationIds
|
|
213
|
+
>;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function assertUniqueOperationId(
|
|
217
|
+
operations: readonly { id: string }[],
|
|
218
|
+
id: string,
|
|
219
|
+
label: string,
|
|
220
|
+
): void {
|
|
221
|
+
if (!operations.some((operation) => operation.id === id)) return;
|
|
222
|
+
throw new Error(`${label} id ${id} is already defined`);
|
|
187
223
|
}
|
|
188
224
|
|
|
189
225
|
function createOperation<Providers extends WorkflowProviderMap, Input extends object>(
|
|
@@ -199,9 +235,6 @@ function createOperation<Providers extends WorkflowProviderMap, Input extends ob
|
|
|
199
235
|
id: normalized,
|
|
200
236
|
title: options.title,
|
|
201
237
|
description: options.description,
|
|
202
|
-
createsWorkspace: options.createsWorkspace,
|
|
203
|
-
requiredHostMethods: normalizeHostMethodRequirements(options.requiredHostMethods),
|
|
204
|
-
requiredHostCapabilities: normalizeHostCapabilityRequirements(options.requiredHostCapabilities),
|
|
205
238
|
input: typeof options.input === "function"
|
|
206
239
|
? options.input(createOperationInputHelpers())
|
|
207
240
|
: options.input,
|
|
@@ -209,31 +242,30 @@ function createOperation<Providers extends WorkflowProviderMap, Input extends ob
|
|
|
209
242
|
};
|
|
210
243
|
}
|
|
211
244
|
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
245
|
+
function createWorkspaceOperation<
|
|
246
|
+
Providers extends WorkflowProviderMap,
|
|
247
|
+
Context extends JsonObject,
|
|
248
|
+
Data extends JsonObject,
|
|
249
|
+
Input extends object,
|
|
250
|
+
>(
|
|
251
|
+
id: string,
|
|
252
|
+
options: WorkflowWorkspaceOperationOptions<Providers, Context, Data, Input>,
|
|
253
|
+
): WorkflowWorkspaceOperationDefinition<Providers, Context, Data, Input> {
|
|
254
|
+
const normalized = id.trim();
|
|
255
|
+
if (!normalized) throw new Error(`Workspace operation ids must be non-empty`);
|
|
256
|
+
if (normalized.includes("/")) throw new Error(`Workspace operation ids cannot contain "/"`);
|
|
257
|
+
if (reservedHostOperationIds.has(normalized)) {
|
|
258
|
+
throw new Error(`Workspace operation id ${normalized} is reserved by the Rigkit host`);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
id: normalized,
|
|
262
|
+
title: options.title,
|
|
263
|
+
description: options.description,
|
|
264
|
+
input: typeof options.input === "function"
|
|
265
|
+
? options.input(createOperationInputHelpers())
|
|
266
|
+
: options.input,
|
|
267
|
+
run: options.run,
|
|
268
|
+
};
|
|
237
269
|
}
|
|
238
270
|
|
|
239
271
|
function createOperationInputHelpers(): WorkflowOperationInputHelpers {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { sequence } from "./authoring.ts";
|
|
2
|
+
|
|
3
|
+
sequence("normal-operation-ids")
|
|
4
|
+
.operation("open" as const, {
|
|
5
|
+
run: async () => null,
|
|
6
|
+
})
|
|
7
|
+
// @ts-expect-error duplicate operation ids are rejected for literal ids
|
|
8
|
+
.operation("open" as const, {
|
|
9
|
+
run: async () => null,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
sequence("workspace-operation-ids")
|
|
13
|
+
.step("prepare", async () => ({ snapshotId: "snap-1" }))
|
|
14
|
+
.workspace({
|
|
15
|
+
create: async ({ workflow, workspace }) => {
|
|
16
|
+
const snapshotId: string = workflow.ctx.snapshotId;
|
|
17
|
+
const name: string = workspace.name;
|
|
18
|
+
void snapshotId;
|
|
19
|
+
void name;
|
|
20
|
+
return { vmId: "vm-1", test: "ok" };
|
|
21
|
+
},
|
|
22
|
+
remove: async ({ workspace }) => {
|
|
23
|
+
const vmId: string = workspace.ctx.vmId;
|
|
24
|
+
const test: string = workspace.ctx.test;
|
|
25
|
+
void vmId;
|
|
26
|
+
void test;
|
|
27
|
+
// @ts-expect-error workspace context is read-only
|
|
28
|
+
workspace.ctx.vmId = "next";
|
|
29
|
+
// @ts-expect-error provider resources are not part of the workspace authoring API
|
|
30
|
+
workspace.resources;
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
.workspaceOperation("open-cmux" as const, {
|
|
34
|
+
run: async ({ workspace }) => {
|
|
35
|
+
const vmId: string = workspace.ctx.vmId;
|
|
36
|
+
void vmId;
|
|
37
|
+
// @ts-expect-error missing data properties are rejected
|
|
38
|
+
workspace.ctx.missing;
|
|
39
|
+
return null;
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
// @ts-expect-error duplicate workspace operation ids are rejected for literal ids
|
|
43
|
+
.workspaceOperation("open-cmux" as const, {
|
|
44
|
+
run: async () => null,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
sequence("reserved-operation-id")
|
|
48
|
+
// @ts-expect-error reserved operation ids are rejected for literal ids
|
|
49
|
+
.operation("create" as const, {
|
|
50
|
+
run: async () => null,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
sequence("reserved-workspace-operation-id")
|
|
54
|
+
.workspace({
|
|
55
|
+
create: async () => ({}),
|
|
56
|
+
remove: async () => {},
|
|
57
|
+
})
|
|
58
|
+
// @ts-expect-error reserved workspace operation ids are rejected for literal ids
|
|
59
|
+
.workspaceOperation("remove" as const, {
|
|
60
|
+
run: async () => null,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
sequence("slash-operation-id")
|
|
64
|
+
// @ts-expect-error operation ids cannot contain slashes
|
|
65
|
+
.operation("workspace/open" as const, {
|
|
66
|
+
run: async () => null,
|
|
67
|
+
});
|
package/src/db/schema/core.ts
CHANGED
|
@@ -6,19 +6,15 @@ export const workspaces = sqliteTable(
|
|
|
6
6
|
{
|
|
7
7
|
id: text("id").primaryKey(),
|
|
8
8
|
name: text("name").notNull(),
|
|
9
|
-
providerId: text("provider_id").notNull(),
|
|
10
9
|
workflow: text("workflow").notNull(),
|
|
11
|
-
|
|
12
|
-
snapshotId: text("snapshot_id"),
|
|
13
|
-
sourceRef: text("source_ref_json", { mode: "json" }).$type<JsonValue>().notNull(),
|
|
14
|
-
context: text("context_json", { mode: "json" }).$type<Record<string, JsonValue>>().notNull(),
|
|
10
|
+
workflowCtx: text("workflow_ctx_json", { mode: "json" }).$type<Record<string, JsonValue>>().notNull(),
|
|
15
11
|
createdAt: text("created_at").notNull(),
|
|
16
12
|
updatedAt: text("updated_at").notNull(),
|
|
17
|
-
|
|
13
|
+
ctx: text("ctx_json", { mode: "json" }).$type<Record<string, JsonValue>>().notNull(),
|
|
18
14
|
},
|
|
19
15
|
(table) => [
|
|
20
16
|
uniqueIndex("workspaces_name_idx").on(table.name),
|
|
21
|
-
index("
|
|
17
|
+
index("workspaces_workflow_idx").on(table.workflow),
|
|
22
18
|
],
|
|
23
19
|
);
|
|
24
20
|
|