@rigkit/sdk 0.2.2 → 0.2.4
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/README.md +1 -1
- package/package.json +3 -3
- package/src/host.ts +10 -1
- package/src/index.test.ts +1 -1
- package/src/index.ts +0 -2
- package/src/runtime/api.ts +0 -2
- package/src/runtime/app.test.ts +54 -82
- package/src/runtime/control.ts +39 -8
- package/src/runtime/index.ts +0 -2
- package/src/runtime/operations.ts +21 -61
- package/src/runtime/protocol.ts +3 -21
- package/src/runtime/runs.ts +17 -3
- package/src/runtime/sessions.ts +1 -56
- package/src/runtime/version.ts +1 -1
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Configs should import authoring helpers from this package:
|
|
|
8
8
|
import { env, workflow } from "@rigkit/sdk";
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Task handlers receive `
|
|
11
|
+
Task handlers receive `step.log(message, options)` for structured step logs. Pass `step.log` to SDKs that accept a logger callback.
|
|
12
12
|
Those events stream over the runtime run session and are rendered by interactive
|
|
13
13
|
hosts such as the `rig` terminal run timeline.
|
|
14
14
|
Provider VM commands can also report incremental stdout/stderr through
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigkit/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"@effect/platform": "0.96.1",
|
|
24
24
|
"@effect/platform-bun": "0.89.0",
|
|
25
25
|
"effect": "^3.21.2",
|
|
26
|
-
"@rigkit/engine": "0.2.
|
|
27
|
-
"@rigkit/runtime-client": "0.2.
|
|
26
|
+
"@rigkit/engine": "0.2.4",
|
|
27
|
+
"@rigkit/runtime-client": "0.2.4"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/bun": "latest",
|
package/src/host.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
export type HostCapabilityLogOptions = {
|
|
2
|
+
stream?: "stdout" | "stderr" | "info";
|
|
3
|
+
label?: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type HostCapabilityContext = {
|
|
7
|
+
log(data: string, options?: HostCapabilityLogOptions): void;
|
|
8
|
+
};
|
|
9
|
+
|
|
1
10
|
export type HostCapabilityDefinition<Input = unknown, Result = unknown> = {
|
|
2
11
|
readonly schemaHash?: string;
|
|
3
12
|
readonly input?: unknown;
|
|
4
13
|
readonly output?: unknown;
|
|
5
|
-
readonly handle: (params: Input) => Result | Promise<Result>;
|
|
14
|
+
readonly handle: (params: Input, context?: HostCapabilityContext) => Result | Promise<Result>;
|
|
6
15
|
};
|
|
7
16
|
|
|
8
17
|
export type HostCapabilityHandler<Input = unknown, Result = unknown> =
|
package/src/index.test.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { defineHostCapabilities, defineHostCapability } from "./host.ts";
|
|
|
16
16
|
|
|
17
17
|
describe("@rigkit/sdk package boundary", () => {
|
|
18
18
|
test("exports authoring API and project runtime entrypoints", () => {
|
|
19
|
-
expect(RIGKIT_SDK_VERSION).toBe("0.2.
|
|
19
|
+
expect(RIGKIT_SDK_VERSION).toBe("0.2.4");
|
|
20
20
|
expect(env).toBeTypeOf("function");
|
|
21
21
|
expect(env.secret).toBeTypeOf("function");
|
|
22
22
|
expect(defineConfig).toBeTypeOf("function");
|
package/src/index.ts
CHANGED
|
@@ -19,12 +19,10 @@ export {
|
|
|
19
19
|
DEFAULT_IDLE_MS,
|
|
20
20
|
RIGKIT_RUNTIME_VERSION,
|
|
21
21
|
createRuntimeStateService,
|
|
22
|
-
HostCapabilityRequirementEffectSchema,
|
|
23
22
|
HostCommandRequestEffectSchema,
|
|
24
23
|
HostCommandRequestSchema,
|
|
25
24
|
HostCommandResultEffectSchema,
|
|
26
25
|
HostCommandResultSchema,
|
|
27
|
-
HostMethodRequirementEffectSchema,
|
|
28
26
|
HostResponseEffectSchema,
|
|
29
27
|
HostResponseSchema,
|
|
30
28
|
OkResponseEffectSchema,
|
package/src/runtime/api.ts
CHANGED
|
@@ -3,8 +3,6 @@ import { runtimeControlApi } from "@rigkit/runtime-client";
|
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
5
|
RuntimeControlHealthEffectSchema as RuntimeHealthEffectSchema,
|
|
6
|
-
RuntimeControlHostCapabilityRequirementEffectSchema as HostCapabilityRequirementEffectSchema,
|
|
7
|
-
RuntimeControlHostMethodRequirementEffectSchema as HostMethodRequirementEffectSchema,
|
|
8
6
|
RuntimeControlMetadataEffectSchema as RuntimeMetadataEffectSchema,
|
|
9
7
|
RuntimeControlOkResponseEffectSchema as OkResponseEffectSchema,
|
|
10
8
|
RuntimeControlOperationCliEffectSchema as RuntimeOperationCliEffectSchema,
|
package/src/runtime/app.test.ts
CHANGED
|
@@ -237,11 +237,10 @@ describe("runtime HTTP app", () => {
|
|
|
237
237
|
scheme: "bearer",
|
|
238
238
|
});
|
|
239
239
|
expect(body.components.schemas.RuntimeOperation.required).toContain("inputSchema");
|
|
240
|
-
expect(body.components.schemas.Workspace.required).toContain("
|
|
240
|
+
expect(body.components.schemas.Workspace.required).toContain("ctx");
|
|
241
241
|
expect(body.components.schemas.OperationsManifest.required).toEqual([
|
|
242
|
-
"hostMethods",
|
|
243
|
-
"hostCapabilities",
|
|
244
242
|
"operations",
|
|
243
|
+
"workspaceOperations",
|
|
245
244
|
]);
|
|
246
245
|
});
|
|
247
246
|
|
|
@@ -277,7 +276,6 @@ describe("runtime HTTP app", () => {
|
|
|
277
276
|
kind: "command",
|
|
278
277
|
title: "SSH",
|
|
279
278
|
description: "Get an SSH command",
|
|
280
|
-
requiredHostMethods: [{ id: "host.command.run", modes: ["interactive"] }],
|
|
281
279
|
inputFields: [
|
|
282
280
|
{ kind: "string", name: "workflow", required: false },
|
|
283
281
|
{ kind: "string", name: "workspaceOrVmId", position: 0, required: true },
|
|
@@ -299,9 +297,6 @@ describe("runtime HTTP app", () => {
|
|
|
299
297
|
source: "config",
|
|
300
298
|
title: "Open",
|
|
301
299
|
description: "Open a workspace",
|
|
302
|
-
createsWorkspace: false,
|
|
303
|
-
requiredHostMethods: [{ id: "host.command.run", modes: ["interactive"] }],
|
|
304
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
305
300
|
inputFields: [
|
|
306
301
|
{
|
|
307
302
|
kind: "workspace",
|
|
@@ -319,33 +314,47 @@ describe("runtime HTTP app", () => {
|
|
|
319
314
|
},
|
|
320
315
|
],
|
|
321
316
|
},
|
|
317
|
+
{
|
|
318
|
+
workflow: "",
|
|
319
|
+
id: "create",
|
|
320
|
+
source: "core",
|
|
321
|
+
kind: "command",
|
|
322
|
+
createsWorkspace: true,
|
|
323
|
+
inputFields: [{ kind: "string", name: "name", required: true }],
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
listRuntimeWorkspaceOperations: () => [
|
|
322
327
|
{
|
|
323
328
|
workflow: "test",
|
|
324
|
-
id: "
|
|
329
|
+
id: "remove",
|
|
330
|
+
source: "core",
|
|
331
|
+
kind: "workspace-action",
|
|
332
|
+
title: "Remove",
|
|
333
|
+
description: "Remove a workspace",
|
|
334
|
+
inputFields: [],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
workflow: "test",
|
|
338
|
+
id: "open-cmux",
|
|
325
339
|
source: "config",
|
|
326
|
-
|
|
340
|
+
kind: "workspace-action",
|
|
341
|
+
title: "Open cmux",
|
|
342
|
+
description: "Open a workspace in cmux",
|
|
327
343
|
inputFields: [],
|
|
328
344
|
},
|
|
329
345
|
],
|
|
330
346
|
} as any);
|
|
331
347
|
|
|
332
348
|
const operation = manifest.operations.find((item) => item.id === "open");
|
|
333
|
-
const
|
|
349
|
+
const createOperation = manifest.operations.find((item) => item.id === "create");
|
|
334
350
|
const sshOperation = manifest.operations.find((item) => item.id === "ssh");
|
|
351
|
+
const cmuxOperation = manifest.workspaceOperations.find((item) => item.id === "open-cmux");
|
|
335
352
|
const inputSchema = operation?.inputSchema as any;
|
|
336
353
|
const sshInputSchema = sshOperation?.inputSchema as any;
|
|
337
354
|
|
|
338
355
|
expect(operation?.source).toBe("config");
|
|
339
356
|
expect(operation?.kind).toBe("workspace-action");
|
|
340
|
-
expect(
|
|
341
|
-
{ id: "host.command.run", modes: ["interactive"] },
|
|
342
|
-
]);
|
|
343
|
-
expect(operation?.requiredHostCapabilities).toEqual([
|
|
344
|
-
{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" },
|
|
345
|
-
]);
|
|
346
|
-
expect(manifest.hostCapabilities.optional).toEqual([
|
|
347
|
-
{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" },
|
|
348
|
-
]);
|
|
357
|
+
expect(cmuxOperation?.id).toBe("open-cmux");
|
|
349
358
|
expect(operation?.cli?.positionals).toEqual([{ name: "workspace", index: 0 }]);
|
|
350
359
|
expect(operation?.cli?.options).toEqual([
|
|
351
360
|
{ name: "rebuild", flag: "--rebuild", required: false, type: "boolean" },
|
|
@@ -361,9 +370,9 @@ describe("runtime HTTP app", () => {
|
|
|
361
370
|
default: false,
|
|
362
371
|
description: "Rebuild before opening",
|
|
363
372
|
});
|
|
364
|
-
expect(
|
|
365
|
-
expect(
|
|
366
|
-
expect(manifest.
|
|
373
|
+
expect(createOperation?.source).toBe("core");
|
|
374
|
+
expect(createOperation?.createsWorkspace).toBe(true);
|
|
375
|
+
expect(manifest.workspaceOperations.map((item) => item.id)).toEqual(["remove", "open-cmux"]);
|
|
367
376
|
expect(sshOperation?.cli?.options?.find((item) => item.name === "print")).toEqual({
|
|
368
377
|
name: "print",
|
|
369
378
|
flag: "--print",
|
|
@@ -407,7 +416,7 @@ describe("runtime HTTP app", () => {
|
|
|
407
416
|
`
|
|
408
417
|
import { defineConfig, sequence } from "${import.meta.dir}/../../../engine/src/index.ts";
|
|
409
418
|
|
|
410
|
-
const root = sequence("session-test").step("noop", async () => ({ ok: true }));
|
|
419
|
+
const root = sequence("session-test").step("noop", async () => ({ ctx: { ok: true } }));
|
|
411
420
|
|
|
412
421
|
export default defineConfig({
|
|
413
422
|
providers: {},
|
|
@@ -447,8 +456,6 @@ describe("runtime HTTP app", () => {
|
|
|
447
456
|
|
|
448
457
|
expect(ack?.operation).toEqual({
|
|
449
458
|
id: "plan",
|
|
450
|
-
requiredHostCapabilities: [],
|
|
451
|
-
requiredHostMethods: [],
|
|
452
459
|
});
|
|
453
460
|
expect(messages.some((message) => message.type === "heartbeat.ack")).toBe(true);
|
|
454
461
|
expect(messages.some((message) => message.type === "run.completed")).toBe(true);
|
|
@@ -457,21 +464,24 @@ describe("runtime HTTP app", () => {
|
|
|
457
464
|
}
|
|
458
465
|
});
|
|
459
466
|
|
|
460
|
-
test("exposes persisted workspace payload as workspace
|
|
461
|
-
const projectDir = mkdtempSync(join(tmpdir(), "rigkit-runtime-workspace-
|
|
467
|
+
test("exposes persisted workspace payload as workspace context", async () => {
|
|
468
|
+
const projectDir = mkdtempSync(join(tmpdir(), "rigkit-runtime-workspace-ctx-"));
|
|
462
469
|
const configPath = join(projectDir, "rig.config.ts");
|
|
463
470
|
writeFileSync(
|
|
464
471
|
configPath,
|
|
465
472
|
`
|
|
466
473
|
import { defineConfig, sequence } from "${import.meta.dir}/../../../engine/src/index.ts";
|
|
467
474
|
|
|
468
|
-
const root = sequence("workspace-
|
|
469
|
-
.step("prepare", async () => ({ repoPath: "/workspace/repo" }))
|
|
470
|
-
.
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
const root = sequence("workspace-ctx")
|
|
476
|
+
.step("prepare", async () => ({ ctx: { repoPath: "/workspace/repo" } }))
|
|
477
|
+
.workspace({
|
|
478
|
+
create: async ({ workflow, workspace }) => ({
|
|
479
|
+
name: workspace.name,
|
|
480
|
+
vmId: "vm-" + workspace.name,
|
|
481
|
+
repoPath: workflow.ctx.repoPath,
|
|
482
|
+
}),
|
|
483
|
+
remove: async () => {},
|
|
484
|
+
});
|
|
475
485
|
|
|
476
486
|
export default defineConfig({
|
|
477
487
|
providers: {},
|
|
@@ -481,7 +491,7 @@ describe("runtime HTTP app", () => {
|
|
|
481
491
|
);
|
|
482
492
|
|
|
483
493
|
const server = await serveRuntime({
|
|
484
|
-
projectId: "project-workspace-
|
|
494
|
+
projectId: "project-workspace-ctx-test",
|
|
485
495
|
projectDir,
|
|
486
496
|
configPath,
|
|
487
497
|
statePath: join(projectDir, "state.sqlite"),
|
|
@@ -505,14 +515,13 @@ describe("runtime HTTP app", () => {
|
|
|
505
515
|
|
|
506
516
|
const { workspaces } = await fetch(new URL("/workspaces", server.url), {
|
|
507
517
|
headers: { authorization: `Bearer ${server.token}` },
|
|
508
|
-
}).then((response) => response.json() as Promise<{ workspaces: Array<{
|
|
518
|
+
}).then((response) => response.json() as Promise<{ workspaces: Array<{ ctx: Record<string, unknown> }> }>);
|
|
509
519
|
|
|
510
|
-
expect(workspaces[0]?.
|
|
520
|
+
expect(workspaces[0]?.ctx).toEqual({
|
|
511
521
|
name: "demo",
|
|
512
|
-
|
|
522
|
+
vmId: "vm-demo",
|
|
513
523
|
repoPath: "/workspace/repo",
|
|
514
524
|
});
|
|
515
|
-
expect(workspaces[0]?.metadata).toEqual(workspaces[0]?.data);
|
|
516
525
|
} finally {
|
|
517
526
|
server.stop();
|
|
518
527
|
}
|
|
@@ -527,8 +536,11 @@ describe("runtime HTTP app", () => {
|
|
|
527
536
|
import { defineConfig, sequence } from "${import.meta.dir}/../../../engine/src/index.ts";
|
|
528
537
|
|
|
529
538
|
const root = sequence("validation")
|
|
530
|
-
.step("prepare", async () => ({ ok: true }))
|
|
531
|
-
.
|
|
539
|
+
.step("prepare", async () => ({ ctx: { ok: true } }))
|
|
540
|
+
.workspace({
|
|
541
|
+
create: async ({ workspace }) => ({ name: workspace.name, vmId: "vm-" + workspace.name }),
|
|
542
|
+
remove: async () => {},
|
|
543
|
+
});
|
|
532
544
|
|
|
533
545
|
export default defineConfig({
|
|
534
546
|
providers: {},
|
|
@@ -574,46 +586,9 @@ describe("runtime HTTP app", () => {
|
|
|
574
586
|
}
|
|
575
587
|
});
|
|
576
588
|
|
|
577
|
-
test("fails run sessions when host hello lacks required methods or capabilities", async () => {
|
|
578
|
-
const { server, projectDir } = await serveRuntimeFixture("rigkit-runtime-required-host-", `
|
|
579
|
-
const root = sequence("required-host").operation("needs-host", {
|
|
580
|
-
requiredHostMethods: [{ id: "host.command.run", modes: ["capture"] }],
|
|
581
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
582
|
-
run: async () => await new Promise(() => {}),
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
export default defineConfig({
|
|
586
|
-
providers: {},
|
|
587
|
-
workflows: { root },
|
|
588
|
-
});
|
|
589
|
-
`);
|
|
590
|
-
|
|
591
|
-
try {
|
|
592
|
-
const started = await startRun(server, "needs-host");
|
|
593
|
-
const messages = await collectSessionMessages(
|
|
594
|
-
new URL(started.sessionUrl, server.url),
|
|
595
|
-
server.token,
|
|
596
|
-
{
|
|
597
|
-
done: (items) => items.some((item) => item.type === "run.failed"),
|
|
598
|
-
},
|
|
599
|
-
);
|
|
600
|
-
const failed = messages.find((message) => message.type === "run.failed");
|
|
601
|
-
const message = String(failed?.error?.message ?? "");
|
|
602
|
-
|
|
603
|
-
expect(failed?.error?.code).toBe("HOST_REQUEST_FAILED");
|
|
604
|
-
expect(message).toContain("host method host.command.run:capture");
|
|
605
|
-
expect(message).toContain("host capability cmux.open@sha256:cmux-open-schema");
|
|
606
|
-
expect(messages.some((item) => item.type === "hello.ack")).toBe(false);
|
|
607
|
-
} finally {
|
|
608
|
-
server.stop();
|
|
609
|
-
rmSync(projectDir, { recursive: true, force: true });
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
|
|
613
589
|
test("bridges typed host capability requests over run sessions", async () => {
|
|
614
590
|
const { server, projectDir } = await serveRuntimeFixture("rigkit-runtime-capability-", `
|
|
615
591
|
const root = sequence("capability-test").operation("open", {
|
|
616
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
617
592
|
run: async ({ local }) => await local.requestCapability("cmux.open", { name: "demo" }),
|
|
618
593
|
});
|
|
619
594
|
|
|
@@ -659,7 +634,6 @@ describe("runtime HTTP app", () => {
|
|
|
659
634
|
test("resolves host capability resource lifetimes from session close reports", async () => {
|
|
660
635
|
const { server, projectDir } = await serveRuntimeFixture("rigkit-runtime-capability-close-", `
|
|
661
636
|
const root = sequence("capability-close-test").operation("open", {
|
|
662
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
663
637
|
run: async ({ local }) => {
|
|
664
638
|
if (!local.requestCapabilitySession) throw new Error("requestCapabilitySession unavailable");
|
|
665
639
|
const session = await local.requestCapabilitySession("cmux.open", { name: "demo" });
|
|
@@ -708,7 +682,6 @@ describe("runtime HTTP app", () => {
|
|
|
708
682
|
test("keeps host-owned capability runs attached until the host cancels", async () => {
|
|
709
683
|
const { server, projectDir } = await serveRuntimeFixture("rigkit-runtime-capability-attached-", `
|
|
710
684
|
const root = sequence("capability-attached-test").operation("open", {
|
|
711
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
712
685
|
run: async ({ local }) => {
|
|
713
686
|
await local.requestCapability("cmux.open", { name: "demo" });
|
|
714
687
|
await new Promise(() => {});
|
|
@@ -755,7 +728,6 @@ describe("runtime HTTP app", () => {
|
|
|
755
728
|
test("turns host response errors into typed host request failures", async () => {
|
|
756
729
|
const { server, projectDir } = await serveRuntimeFixture("rigkit-runtime-capability-error-", `
|
|
757
730
|
const root = sequence("capability-error-test").operation("cmux-open", {
|
|
758
|
-
requiredHostCapabilities: [{ id: "cmux.open", schemaHash: "sha256:cmux-open-schema" }],
|
|
759
731
|
run: async ({ local }) => await local.requestCapability("cmux.open", { name: "demo" }),
|
|
760
732
|
});
|
|
761
733
|
|
|
@@ -834,7 +806,7 @@ function writeNoopConfig(projectDir: string): void {
|
|
|
834
806
|
`
|
|
835
807
|
import { defineConfig, sequence } from "${import.meta.dir}/../../../engine/src/index.ts";
|
|
836
808
|
|
|
837
|
-
const root = sequence("noop").step("ready", async () => ({ ready: true }));
|
|
809
|
+
const root = sequence("noop").step("ready", async () => ({ ctx: { ready: true } }));
|
|
838
810
|
|
|
839
811
|
export default defineConfig({
|
|
840
812
|
providers: {},
|
package/src/runtime/control.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type HostResponse,
|
|
10
10
|
type RunOperationRequest,
|
|
11
11
|
type RuntimeOperation,
|
|
12
|
+
type RuntimeOperationsManifest,
|
|
12
13
|
} from "./protocol.ts";
|
|
13
14
|
import {
|
|
14
15
|
createRun,
|
|
@@ -87,10 +88,10 @@ export function runtimeRuns(store: RunStore) {
|
|
|
87
88
|
export async function startRuntimeRun(state: RuntimeAppState, body: RunOperationRequest) {
|
|
88
89
|
const engine = await loadEngine(state.context);
|
|
89
90
|
const manifest = operationManifestFor(engine);
|
|
90
|
-
const
|
|
91
|
-
if (!
|
|
91
|
+
const resolved = resolveRuntimeOperation(manifest, body.operation);
|
|
92
|
+
if (!resolved) throw new RuntimeControlHttpError(400, `Unknown operation ${body.operation}`);
|
|
92
93
|
|
|
93
|
-
const run = createRun(
|
|
94
|
+
const run = createRun(resolved.runOperation, body.input ?? {}, resolved.operation);
|
|
94
95
|
state.store.runs.set(run.id, run);
|
|
95
96
|
runOperation(run, state.store, state.context);
|
|
96
97
|
|
|
@@ -144,16 +145,46 @@ export function runtimeControlErrorStatus(error: unknown): number {
|
|
|
144
145
|
return 500;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
function
|
|
148
|
-
|
|
148
|
+
function resolveRuntimeOperation(
|
|
149
|
+
manifest: RuntimeOperationsManifest,
|
|
150
|
+
requestedOperation: string,
|
|
151
|
+
): { operation: RuntimeOperation; runOperation: string } | undefined {
|
|
152
|
+
const workspaceOperation = parseWorkspaceOperationId(requestedOperation);
|
|
153
|
+
if (workspaceOperation) {
|
|
154
|
+
const operation = manifest.workspaceOperations.find((item) => item.id === workspaceOperation.operation);
|
|
155
|
+
return operation ? { operation, runOperation: requestedOperation } : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const operation = manifest.operations.find((operation) =>
|
|
149
159
|
operation.id === requestedOperation || operation.aliases?.includes(requestedOperation)
|
|
150
160
|
);
|
|
161
|
+
return operation ? { operation, runOperation: operation.id } : undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function parseWorkspaceOperationId(value: string): { workspace: string; operation: string } | undefined {
|
|
165
|
+
const slash = value.indexOf("/");
|
|
166
|
+
if (slash <= 0 || slash === value.length - 1) return undefined;
|
|
167
|
+
return {
|
|
168
|
+
workspace: value.slice(0, slash),
|
|
169
|
+
operation: value.slice(slash + 1),
|
|
170
|
+
};
|
|
151
171
|
}
|
|
152
172
|
|
|
153
|
-
function runtimeWorkspace(workspace: WorkspaceRecord):
|
|
173
|
+
function runtimeWorkspace(workspace: WorkspaceRecord): {
|
|
174
|
+
id: string;
|
|
175
|
+
name: string;
|
|
176
|
+
workflow: string;
|
|
177
|
+
ctx: WorkspaceRecord["ctx"];
|
|
178
|
+
createdAt: string;
|
|
179
|
+
updatedAt: string;
|
|
180
|
+
} {
|
|
154
181
|
return {
|
|
155
|
-
|
|
156
|
-
|
|
182
|
+
id: workspace.id,
|
|
183
|
+
name: workspace.name,
|
|
184
|
+
workflow: workspace.workflow,
|
|
185
|
+
ctx: workspace.ctx,
|
|
186
|
+
createdAt: workspace.createdAt,
|
|
187
|
+
updatedAt: workspace.updatedAt,
|
|
157
188
|
};
|
|
158
189
|
}
|
|
159
190
|
|
package/src/runtime/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type DevMachineEngine,
|
|
4
4
|
type EngineOperationSummary,
|
|
5
5
|
type JsonValue,
|
|
6
|
+
type LocalHostCapabilityRequestOptions,
|
|
6
7
|
} from "@rigkit/engine";
|
|
7
8
|
import { normalizeRuntimeRunError } from "./errors.ts";
|
|
8
9
|
import {
|
|
@@ -83,10 +84,18 @@ async function executeOperation(run: RunRecord, store: RunStore, options: Engine
|
|
|
83
84
|
const result = await requestHost(store, run, "host.command.run", command);
|
|
84
85
|
return HostCommandResultSchema.parse(result);
|
|
85
86
|
},
|
|
86
|
-
requestCapability: async <Result = unknown>(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
requestCapability: async <Result = unknown>(
|
|
88
|
+
capability: string,
|
|
89
|
+
params: unknown,
|
|
90
|
+
requestOptions?: LocalHostCapabilityRequestOptions,
|
|
91
|
+
) =>
|
|
92
|
+
await requestHostCapability(store, run, capability, params, requestOptions) as Result,
|
|
93
|
+
requestCapabilitySession: async <Result = unknown>(
|
|
94
|
+
capability: string,
|
|
95
|
+
params: unknown,
|
|
96
|
+
requestOptions?: LocalHostCapabilityRequestOptions,
|
|
97
|
+
) =>
|
|
98
|
+
await requestHostCapabilitySession<Result>(store, run, capability, params, requestOptions),
|
|
90
99
|
},
|
|
91
100
|
});
|
|
92
101
|
engine.onEvent((event) => emitRunEvent(run, event));
|
|
@@ -100,6 +109,12 @@ export function operationsFor(engine: DevMachineEngine): RuntimeOperation[] {
|
|
|
100
109
|
return engine.listRuntimeOperations().map((operation) => runtimeOperationForEngineOperation(engine, operation));
|
|
101
110
|
}
|
|
102
111
|
|
|
112
|
+
export function workspaceOperationsFor(engine: DevMachineEngine): RuntimeOperation[] {
|
|
113
|
+
return engine.listRuntimeWorkspaceOperations().map((operation) =>
|
|
114
|
+
runtimeOperationForEngineOperation(engine, operation)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
function runtimeOperationForEngineOperation(engine: DevMachineEngine, operation: EngineOperationSummary): RuntimeOperation {
|
|
104
119
|
const required = operation.inputFields
|
|
105
120
|
.filter((field) => field.required ?? true)
|
|
@@ -116,14 +131,6 @@ function runtimeOperationForEngineOperation(engine: DevMachineEngine, operation:
|
|
|
116
131
|
title: operation.title ?? titleize(operation.id),
|
|
117
132
|
description: operation.description ?? "",
|
|
118
133
|
createsWorkspace: operation.createsWorkspace,
|
|
119
|
-
requiredHostMethods: operation.requiredHostMethods?.map((method) => ({
|
|
120
|
-
id: method.id,
|
|
121
|
-
...(method.modes?.length ? { modes: [...method.modes] } : {}),
|
|
122
|
-
})),
|
|
123
|
-
requiredHostCapabilities: operation.requiredHostCapabilities?.map((capability) => ({
|
|
124
|
-
id: capability.id,
|
|
125
|
-
...(capability.schemaHash ? { schemaHash: capability.schemaHash } : {}),
|
|
126
|
-
})),
|
|
127
134
|
cli: operation.cli ? cloneOperationCli(operation.cli) : cliForFields(operation.inputFields),
|
|
128
135
|
inputSchema: objectSchema(properties, required),
|
|
129
136
|
};
|
|
@@ -188,60 +195,13 @@ function cliForFields(fields: EngineOperationSummary["inputFields"]): NonNullabl
|
|
|
188
195
|
|
|
189
196
|
export function operationManifestFor(engine: DevMachineEngine): RuntimeOperationsManifest {
|
|
190
197
|
const operations = operationsFor(engine);
|
|
198
|
+
const workspaceOperations = workspaceOperationsFor(engine);
|
|
191
199
|
return {
|
|
192
|
-
hostMethods: {
|
|
193
|
-
known: [
|
|
194
|
-
{ id: "message.show" },
|
|
195
|
-
{ id: "prompt.text" },
|
|
196
|
-
{ id: "prompt.confirm" },
|
|
197
|
-
{ id: "prompt.select" },
|
|
198
|
-
{ id: "open.external" },
|
|
199
|
-
{ id: "host.command.run", modes: ["capture", "interactive"] },
|
|
200
|
-
],
|
|
201
|
-
requiredByOperations: Object.fromEntries(
|
|
202
|
-
operations
|
|
203
|
-
.filter((operation) => operation.requiredHostMethods?.length)
|
|
204
|
-
.map((operation) => [
|
|
205
|
-
operation.id,
|
|
206
|
-
operation.requiredHostMethods!.flatMap((method) =>
|
|
207
|
-
method.modes?.length
|
|
208
|
-
? method.modes.map((mode) => `${method.id}:${mode}`)
|
|
209
|
-
: [method.id]
|
|
210
|
-
),
|
|
211
|
-
]),
|
|
212
|
-
),
|
|
213
|
-
},
|
|
214
|
-
hostCapabilities: {
|
|
215
|
-
optional: dedupeHostCapabilities(
|
|
216
|
-
operations.flatMap((operation) => operation.requiredHostCapabilities ?? []),
|
|
217
|
-
),
|
|
218
|
-
requiredByOperations: Object.fromEntries(
|
|
219
|
-
operations
|
|
220
|
-
.filter((operation) => operation.requiredHostCapabilities?.length)
|
|
221
|
-
.map((operation) => [operation.id, operation.requiredHostCapabilities!.map((capability) => capability.id)]),
|
|
222
|
-
),
|
|
223
|
-
},
|
|
224
200
|
operations,
|
|
201
|
+
workspaceOperations,
|
|
225
202
|
};
|
|
226
203
|
}
|
|
227
204
|
|
|
228
|
-
function dedupeHostCapabilities(
|
|
229
|
-
capabilities: Array<{ id: string; schemaHash?: string }>,
|
|
230
|
-
): Array<{ id: string; schemaHash?: string }> {
|
|
231
|
-
const seen = new Set<string>();
|
|
232
|
-
const deduped: Array<{ id: string; schemaHash?: string }> = [];
|
|
233
|
-
for (const capability of capabilities) {
|
|
234
|
-
const key = capability.schemaHash ? `${capability.id}\0${capability.schemaHash}` : capability.id;
|
|
235
|
-
if (seen.has(key)) continue;
|
|
236
|
-
seen.add(key);
|
|
237
|
-
deduped.push({
|
|
238
|
-
id: capability.id,
|
|
239
|
-
...(capability.schemaHash ? { schemaHash: capability.schemaHash } : {}),
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
return deduped;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
205
|
function workflowJsonSchema(workflows: string[]): JsonSchema {
|
|
246
206
|
return workflows.length > 0
|
|
247
207
|
? { type: "string", enum: workflows }
|
package/src/runtime/protocol.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Schema } from "effect";
|
|
|
2
2
|
import type { WorkflowEvent } from "@rigkit/engine";
|
|
3
3
|
|
|
4
4
|
export const RUNTIME_API_VERSION = 1;
|
|
5
|
-
export const RUNTIME_PROTOCOL_HASH = "sha256:
|
|
5
|
+
export const RUNTIME_PROTOCOL_HASH = "sha256:ac8d4a503b56c15b333ea51f57ab1f6fca776bea93f498120b10ab601cc0960a";
|
|
6
6
|
export const DEFAULT_IDLE_MS = 30 * 60 * 1000;
|
|
7
7
|
|
|
8
8
|
export class RuntimeProtocolSchemaError extends Error {
|
|
@@ -99,6 +99,7 @@ export type HostCapabilityRequestEvent = {
|
|
|
99
99
|
type: "host.capability.request";
|
|
100
100
|
requestId: string;
|
|
101
101
|
id: string;
|
|
102
|
+
nodePath?: string;
|
|
102
103
|
capability: string;
|
|
103
104
|
params: unknown;
|
|
104
105
|
};
|
|
@@ -147,16 +148,6 @@ export type RuntimeOperationCli = {
|
|
|
147
148
|
options?: RuntimeOperationCliOption[];
|
|
148
149
|
};
|
|
149
150
|
|
|
150
|
-
export type RuntimeHostMethodRequirement = {
|
|
151
|
-
id: string;
|
|
152
|
-
modes?: string[];
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
export type RuntimeHostCapabilityRequirement = {
|
|
156
|
-
id: string;
|
|
157
|
-
schemaHash?: string;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
151
|
export type RuntimeOperation = {
|
|
161
152
|
id: string;
|
|
162
153
|
aliases?: string[];
|
|
@@ -165,22 +156,13 @@ export type RuntimeOperation = {
|
|
|
165
156
|
title: string;
|
|
166
157
|
description: string;
|
|
167
158
|
createsWorkspace?: boolean;
|
|
168
|
-
requiredHostMethods?: RuntimeHostMethodRequirement[];
|
|
169
|
-
requiredHostCapabilities?: RuntimeHostCapabilityRequirement[];
|
|
170
159
|
cli?: RuntimeOperationCli;
|
|
171
160
|
inputSchema: JsonSchema;
|
|
172
161
|
};
|
|
173
162
|
|
|
174
163
|
export type RuntimeOperationsManifest = {
|
|
175
|
-
hostMethods: {
|
|
176
|
-
known: RuntimeHostMethodRequirement[];
|
|
177
|
-
requiredByOperations: Record<string, string[]>;
|
|
178
|
-
};
|
|
179
|
-
hostCapabilities: {
|
|
180
|
-
optional: RuntimeHostCapabilityRequirement[];
|
|
181
|
-
requiredByOperations: Record<string, string[]>;
|
|
182
|
-
};
|
|
183
164
|
operations: RuntimeOperation[];
|
|
165
|
+
workspaceOperations: RuntimeOperation[];
|
|
184
166
|
};
|
|
185
167
|
|
|
186
168
|
export function objectSchema(properties: Record<string, unknown>, required: string[] = []): JsonSchema {
|
package/src/runtime/runs.ts
CHANGED
|
@@ -21,6 +21,10 @@ export type HostCapabilitySessionResult<Result = unknown> = {
|
|
|
21
21
|
closed: Promise<void>;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
export type HostCapabilityRequestOptions = {
|
|
25
|
+
nodePath?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
24
28
|
export type RunRecord = {
|
|
25
29
|
id: string;
|
|
26
30
|
operation: string;
|
|
@@ -197,8 +201,9 @@ export function requestHostCapability(
|
|
|
197
201
|
run: RunRecord,
|
|
198
202
|
capability: string,
|
|
199
203
|
params: unknown,
|
|
204
|
+
options: HostCapabilityRequestOptions = {},
|
|
200
205
|
): Promise<unknown> {
|
|
201
|
-
const { requestId } = emitHostCapabilityRequest(run, capability, params);
|
|
206
|
+
const { requestId } = emitHostCapabilityRequest(run, capability, params, options);
|
|
202
207
|
return waitForHostResponse(store, requestId);
|
|
203
208
|
}
|
|
204
209
|
|
|
@@ -207,8 +212,9 @@ export async function requestHostCapabilitySession<Result = unknown>(
|
|
|
207
212
|
run: RunRecord,
|
|
208
213
|
capability: string,
|
|
209
214
|
params: unknown,
|
|
215
|
+
options: HostCapabilityRequestOptions = {},
|
|
210
216
|
): Promise<HostCapabilitySessionResult<Result>> {
|
|
211
|
-
const { requestId } = emitHostCapabilityRequest(run, capability, params);
|
|
217
|
+
const { requestId } = emitHostCapabilityRequest(run, capability, params, options);
|
|
212
218
|
const closed = new Promise<void>((resolveClosed, rejectClosed) => {
|
|
213
219
|
store.hostCapabilityResources.set(requestId, { resolveClosed, rejectClosed });
|
|
214
220
|
run.pendingHostCapabilityResourceIds.add(requestId);
|
|
@@ -234,6 +240,7 @@ function emitHostCapabilityRequest(
|
|
|
234
240
|
run: RunRecord,
|
|
235
241
|
capability: string,
|
|
236
242
|
params: unknown,
|
|
243
|
+
options: HostCapabilityRequestOptions = {},
|
|
237
244
|
): { requestId: string } {
|
|
238
245
|
if (run.status !== "running") {
|
|
239
246
|
throw new RuntimeHostRequestError({
|
|
@@ -243,7 +250,14 @@ function emitHostCapabilityRequest(
|
|
|
243
250
|
}
|
|
244
251
|
const requestId = `cap_req_${crypto.randomUUID()}`;
|
|
245
252
|
run.pendingHostRequestIds.add(requestId);
|
|
246
|
-
emitRunEvent(run, {
|
|
253
|
+
emitRunEvent(run, {
|
|
254
|
+
type: "host.capability.request",
|
|
255
|
+
requestId,
|
|
256
|
+
id: requestId,
|
|
257
|
+
...(options.nodePath ? { nodePath: options.nodePath } : {}),
|
|
258
|
+
capability,
|
|
259
|
+
params,
|
|
260
|
+
});
|
|
247
261
|
return { requestId };
|
|
248
262
|
}
|
|
249
263
|
|
package/src/runtime/sessions.ts
CHANGED
|
@@ -81,19 +81,6 @@ function openRunSession(
|
|
|
81
81
|
const message = parseSessionMessage(value);
|
|
82
82
|
if (!message) return;
|
|
83
83
|
if (message.type === "hello") {
|
|
84
|
-
const unsupported = unsupportedSessionRequirements(message, run.operationDefinition);
|
|
85
|
-
if (unsupported.length > 0) {
|
|
86
|
-
failRun(
|
|
87
|
-
run,
|
|
88
|
-
new RuntimeHostRequestError({
|
|
89
|
-
message: `Host does not support required ${unsupported.join(", ")}`,
|
|
90
|
-
}),
|
|
91
|
-
state.store,
|
|
92
|
-
);
|
|
93
|
-
acknowledged = true;
|
|
94
|
-
sendBacklog();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
84
|
acknowledged = true;
|
|
98
85
|
sendHelloAck(socket, run);
|
|
99
86
|
sendBacklog();
|
|
@@ -160,53 +147,10 @@ function sendHelloAck(ws: RuntimeSessionTransport, run: { operation: string; ope
|
|
|
160
147
|
},
|
|
161
148
|
operation: {
|
|
162
149
|
id: run.operation,
|
|
163
|
-
requiredHostCapabilities: run.operationDefinition?.requiredHostCapabilities ?? [],
|
|
164
|
-
requiredHostMethods: run.operationDefinition?.requiredHostMethods ?? [],
|
|
165
150
|
},
|
|
166
151
|
}));
|
|
167
152
|
}
|
|
168
153
|
|
|
169
|
-
function unsupportedSessionRequirements(message: SessionMessage, operation: RuntimeOperation | undefined): string[] {
|
|
170
|
-
const unsupported: string[] = [];
|
|
171
|
-
for (const requirement of operation?.requiredHostMethods ?? []) {
|
|
172
|
-
const hostMethod = sessionItems(message.hostMethods).find((item) => item.id === requirement.id);
|
|
173
|
-
if (!hostMethod || !supportsModes(hostMethod.modes, requirement.modes)) {
|
|
174
|
-
unsupported.push(`host method ${formatRequirement(requirement.id, requirement.modes)}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
for (const requirement of operation?.requiredHostCapabilities ?? []) {
|
|
178
|
-
const capability = sessionItems(message.hostCapabilities).find((item) => item.id === requirement.id);
|
|
179
|
-
if (!capability || (requirement.schemaHash && capability.schemaHash !== requirement.schemaHash)) {
|
|
180
|
-
unsupported.push(requirement.schemaHash
|
|
181
|
-
? `host capability ${requirement.id}@${requirement.schemaHash}`
|
|
182
|
-
: `host capability ${requirement.id}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return unsupported;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function sessionItems(value: unknown): Array<{ id: string; modes?: string[]; schemaHash?: string }> {
|
|
189
|
-
if (!Array.isArray(value)) return [];
|
|
190
|
-
return value.flatMap((item) => {
|
|
191
|
-
if (!isRecord(item) || typeof item.id !== "string") return [];
|
|
192
|
-
return [{
|
|
193
|
-
id: item.id,
|
|
194
|
-
modes: Array.isArray(item.modes) ? item.modes.filter((mode): mode is string => typeof mode === "string") : undefined,
|
|
195
|
-
schemaHash: typeof item.schemaHash === "string" ? item.schemaHash : undefined,
|
|
196
|
-
}];
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function supportsModes(hostModes: string[] | undefined, requiredModes: string[] | undefined): boolean {
|
|
201
|
-
if (!requiredModes?.length) return true;
|
|
202
|
-
const supported = new Set(hostModes ?? []);
|
|
203
|
-
return requiredModes.every((mode) => supported.has(mode));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function formatRequirement(id: string, modes: string[] | undefined): string {
|
|
207
|
-
return modes?.length ? `${id}:${modes.join("|")}` : id;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
154
|
function sessionEvent(event: unknown): Record<string, unknown> {
|
|
211
155
|
if (isRecord(event) && event.type === "host.request") {
|
|
212
156
|
return {
|
|
@@ -220,6 +164,7 @@ function sessionEvent(event: unknown): Record<string, unknown> {
|
|
|
220
164
|
return {
|
|
221
165
|
type: "host.capability.request",
|
|
222
166
|
id: typeof event.id === "string" ? event.id : event.requestId,
|
|
167
|
+
...(typeof event.nodePath === "string" ? { nodePath: event.nodePath } : {}),
|
|
223
168
|
capability: event.capability,
|
|
224
169
|
params: event.params,
|
|
225
170
|
};
|
package/src/runtime/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const RIGKIT_RUNTIME_VERSION = "0.2.
|
|
1
|
+
export const RIGKIT_RUNTIME_VERSION = "0.2.4";
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const RIGKIT_SDK_VERSION = "0.2.
|
|
1
|
+
export const RIGKIT_SDK_VERSION = "0.2.4";
|