@rigkit/sdk 0.0.0-canary-20260518T014918-c5bc0c2
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 +17 -0
- package/package.json +41 -0
- package/src/cli.ts +126 -0
- package/src/host.ts +55 -0
- package/src/index.test.ts +53 -0
- package/src/index.ts +86 -0
- package/src/runtime/api-handlers.ts +174 -0
- package/src/runtime/api.ts +71 -0
- package/src/runtime/app.test.ts +934 -0
- package/src/runtime/app.ts +63 -0
- package/src/runtime/cli.ts +120 -0
- package/src/runtime/control.ts +219 -0
- package/src/runtime/errors.test.ts +39 -0
- package/src/runtime/errors.ts +199 -0
- package/src/runtime/index.ts +83 -0
- package/src/runtime/openapi.ts +5 -0
- package/src/runtime/operations.ts +239 -0
- package/src/runtime/protocol.ts +175 -0
- package/src/runtime/runs.ts +307 -0
- package/src/runtime/server.ts +190 -0
- package/src/runtime/sessions.ts +202 -0
- package/src/runtime/state.ts +12 -0
- package/src/runtime/token.ts +16 -0
- package/src/runtime/types.ts +39 -0
- package/src/runtime/version.ts +1 -0
- package/src/version.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @rigkit/sdk
|
|
2
|
+
|
|
3
|
+
Project-local rigkit authoring API and runtime daemon package.
|
|
4
|
+
|
|
5
|
+
Configs should import authoring helpers from this package:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { env, workflow } from "@rigkit/sdk";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Inside a task handler, `console.log`, `console.info`, `console.debug`, `console.warn`, and `console.error` are intercepted and streamed to the runtime as structured `log.output` events with a level. The CLI renders each level distinctly (debug dim, log default, warn yellow, error red). For SDKs that accept a logger callback (e.g. `freestyle.client.vms.create({ logger: console.log })`), pass `console.log` directly. Set `RIGKIT_NO_CONSOLE_INTERCEPT=1` to disable the intercept and write to the host's terminal instead.
|
|
12
|
+
Provider VM commands can also report incremental stdout/stderr through
|
|
13
|
+
`ExecOptions.onOutput`; the runtime forwards those chunks over the same run
|
|
14
|
+
session and falls back to buffered command output when a provider cannot stream.
|
|
15
|
+
|
|
16
|
+
Local hosts start the project daemon through the `rigkit-project-runtime` binary
|
|
17
|
+
installed by this package.
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rigkit/sdk",
|
|
3
|
+
"version": "0.0.0-canary-20260518T014918-c5bc0c2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/freestyle-sh/rigkit.git",
|
|
8
|
+
"directory": "packages/sdk"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"rigkit-project-runtime": "./src/cli.ts"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./src/index.ts",
|
|
15
|
+
"./host": "./src/host.ts",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"src",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@effect/platform": "0.96.1",
|
|
24
|
+
"@effect/platform-bun": "0.89.0",
|
|
25
|
+
"effect": "^3.21.2",
|
|
26
|
+
"@rigkit/engine": "0.0.0-canary-20260518T014918-c5bc0c2",
|
|
27
|
+
"@rigkit/runtime-client": "0.0.0-canary-20260518T014918-c5bc0c2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"typescript": "latest"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc --noEmit",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"test": "bun test"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { serveRuntime } from "./runtime/server.ts";
|
|
4
|
+
|
|
5
|
+
type ServeArgs = {
|
|
6
|
+
projectId?: string;
|
|
7
|
+
runtimeFingerprint?: string;
|
|
8
|
+
projectDir?: string;
|
|
9
|
+
configPath?: string;
|
|
10
|
+
statePath?: string;
|
|
11
|
+
globalFragmentRoot?: string;
|
|
12
|
+
sourceJson?: string;
|
|
13
|
+
handlePath?: string;
|
|
14
|
+
tokenPath?: string;
|
|
15
|
+
host?: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
idleMs?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const [command, ...args] = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
if (command !== "serve") {
|
|
23
|
+
console.error(`Usage: rigkit-project-runtime serve --project-id <id> --project-dir <dir> --config <file> --handle <file> --token <file>`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const options = parseServeArgs(args);
|
|
28
|
+
const missing = [
|
|
29
|
+
["--project-id", options.projectId],
|
|
30
|
+
["--project-dir", options.projectDir],
|
|
31
|
+
["--config", options.configPath],
|
|
32
|
+
["--handle", options.handlePath],
|
|
33
|
+
["--token", options.tokenPath],
|
|
34
|
+
].filter(([, value]) => !value);
|
|
35
|
+
if (missing.length > 0) {
|
|
36
|
+
console.error(`Missing required runtime args: ${missing.map(([name]) => name).join(", ")}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const runtime = await serveRuntime({
|
|
41
|
+
projectId: options.projectId!,
|
|
42
|
+
runtimeFingerprint: options.runtimeFingerprint,
|
|
43
|
+
projectDir: resolve(options.projectDir!),
|
|
44
|
+
configPath: resolve(options.configPath!),
|
|
45
|
+
statePath: options.statePath ? resolve(options.statePath) : undefined,
|
|
46
|
+
globalFragmentRoot: options.globalFragmentRoot ? resolve(options.globalFragmentRoot) : undefined,
|
|
47
|
+
source: options.sourceJson ? JSON.parse(options.sourceJson) : undefined,
|
|
48
|
+
handlePath: resolve(options.handlePath!),
|
|
49
|
+
tokenPath: resolve(options.tokenPath!),
|
|
50
|
+
host: options.host,
|
|
51
|
+
port: options.port,
|
|
52
|
+
idleMs: options.idleMs,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
console.log(JSON.stringify({
|
|
56
|
+
type: "ready",
|
|
57
|
+
url: runtime.url,
|
|
58
|
+
token: runtime.token,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
62
|
+
process.on(signal, () => {
|
|
63
|
+
runtime.stop();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await runtime.closed;
|
|
68
|
+
await Bun.sleep(25);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
|
|
71
|
+
function parseServeArgs(args: string[]): ServeArgs {
|
|
72
|
+
const parsed: ServeArgs = {};
|
|
73
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
74
|
+
const arg = args[index]!;
|
|
75
|
+
const next = args[index + 1];
|
|
76
|
+
const readValue = () => {
|
|
77
|
+
if (!next) throw new Error(`${arg} requires a value`);
|
|
78
|
+
index += 1;
|
|
79
|
+
return next;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
switch (arg) {
|
|
83
|
+
case "--project-id":
|
|
84
|
+
parsed.projectId = readValue();
|
|
85
|
+
break;
|
|
86
|
+
case "--runtime-fingerprint":
|
|
87
|
+
parsed.runtimeFingerprint = readValue();
|
|
88
|
+
break;
|
|
89
|
+
case "--project-dir":
|
|
90
|
+
case "--project":
|
|
91
|
+
parsed.projectDir = readValue();
|
|
92
|
+
break;
|
|
93
|
+
case "--config":
|
|
94
|
+
parsed.configPath = readValue();
|
|
95
|
+
break;
|
|
96
|
+
case "--state":
|
|
97
|
+
case "--state-path":
|
|
98
|
+
parsed.statePath = readValue();
|
|
99
|
+
break;
|
|
100
|
+
case "--global-fragment-root":
|
|
101
|
+
parsed.globalFragmentRoot = readValue();
|
|
102
|
+
break;
|
|
103
|
+
case "--source-json":
|
|
104
|
+
parsed.sourceJson = readValue();
|
|
105
|
+
break;
|
|
106
|
+
case "--handle":
|
|
107
|
+
parsed.handlePath = readValue();
|
|
108
|
+
break;
|
|
109
|
+
case "--token":
|
|
110
|
+
parsed.tokenPath = readValue();
|
|
111
|
+
break;
|
|
112
|
+
case "--host":
|
|
113
|
+
parsed.host = readValue();
|
|
114
|
+
break;
|
|
115
|
+
case "--port":
|
|
116
|
+
parsed.port = Number(readValue());
|
|
117
|
+
break;
|
|
118
|
+
case "--idle-ms":
|
|
119
|
+
parsed.idleMs = Number(readValue());
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
throw new Error(`Unknown runtime arg ${arg}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return parsed;
|
|
126
|
+
}
|
package/src/host.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
|
|
10
|
+
export type HostCapabilityDefinition<Input = unknown, Result = unknown> = {
|
|
11
|
+
readonly schemaHash?: string;
|
|
12
|
+
readonly input?: unknown;
|
|
13
|
+
readonly output?: unknown;
|
|
14
|
+
readonly handle: (params: Input, context?: HostCapabilityContext) => Result | Promise<Result>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type HostCapabilityHandler<Input = unknown, Result = unknown> =
|
|
18
|
+
& HostCapabilityDefinition<Input, Result>
|
|
19
|
+
& {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function defineHostCapability<const Id extends string, Input = unknown, Result = unknown>(
|
|
24
|
+
id: Id,
|
|
25
|
+
definition: HostCapabilityDefinition<Input, Result>,
|
|
26
|
+
): HostCapabilityHandler<Input, Result> & { readonly id: Id } {
|
|
27
|
+
return {
|
|
28
|
+
id,
|
|
29
|
+
...definition,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function defineHostCapabilities<const Definitions extends Record<string, HostCapabilityDefinition<any, any>>>(
|
|
34
|
+
definitions: Definitions,
|
|
35
|
+
): Array<{
|
|
36
|
+
readonly [Id in keyof Definitions & string]: HostCapabilityHandler<
|
|
37
|
+
HostCapabilityInput<Definitions[Id]>,
|
|
38
|
+
HostCapabilityResult<Definitions[Id]>
|
|
39
|
+
> & { readonly id: Id };
|
|
40
|
+
}[keyof Definitions & string]> {
|
|
41
|
+
return Object.entries(definitions).map(([id, definition]) =>
|
|
42
|
+
defineHostCapability(id, definition as HostCapabilityDefinition)
|
|
43
|
+
) as Array<{
|
|
44
|
+
readonly [Id in keyof Definitions & string]: HostCapabilityHandler<
|
|
45
|
+
HostCapabilityInput<Definitions[Id]>,
|
|
46
|
+
HostCapabilityResult<Definitions[Id]>
|
|
47
|
+
> & { readonly id: Id };
|
|
48
|
+
}[keyof Definitions & string]>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type HostCapabilityInput<Definition> =
|
|
52
|
+
Definition extends HostCapabilityDefinition<infer Input, any> ? Input : unknown;
|
|
53
|
+
|
|
54
|
+
type HostCapabilityResult<Definition> =
|
|
55
|
+
Definition extends HostCapabilityDefinition<any, infer Result> ? Result : unknown;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
RIGKIT_SDK_VERSION,
|
|
4
|
+
createRuntimeControlApiHandler,
|
|
5
|
+
defineConfig,
|
|
6
|
+
env,
|
|
7
|
+
runtimeControlApi,
|
|
8
|
+
runtimeControlOpenApiDocument,
|
|
9
|
+
RuntimeHostRequestError,
|
|
10
|
+
serveRuntime,
|
|
11
|
+
serveRuntimeEffect,
|
|
12
|
+
sequence,
|
|
13
|
+
workflow,
|
|
14
|
+
} from "./index.ts";
|
|
15
|
+
import { defineHostCapabilities, defineHostCapability } from "./host.ts";
|
|
16
|
+
|
|
17
|
+
describe("@rigkit/sdk package boundary", () => {
|
|
18
|
+
test("exports authoring API and project runtime entrypoints", () => {
|
|
19
|
+
expect(RIGKIT_SDK_VERSION).toBe("0.0.0-canary-20260518T014918-c5bc0c2");
|
|
20
|
+
expect(env).toBeTypeOf("function");
|
|
21
|
+
expect(env.secret).toBeTypeOf("function");
|
|
22
|
+
expect(defineConfig).toBeTypeOf("function");
|
|
23
|
+
expect(sequence).toBeTypeOf("function");
|
|
24
|
+
expect(workflow).toBeTypeOf("function");
|
|
25
|
+
expect(serveRuntime).toBeTypeOf("function");
|
|
26
|
+
expect(serveRuntimeEffect).toBeTypeOf("function");
|
|
27
|
+
expect(createRuntimeControlApiHandler).toBeTypeOf("function");
|
|
28
|
+
expect(runtimeControlApi).toBeDefined();
|
|
29
|
+
const document = runtimeControlOpenApiDocument() as any;
|
|
30
|
+
expect(document.paths["/runs"].post.operationId).toBe("startRun");
|
|
31
|
+
expect(new RuntimeHostRequestError({ message: "host failed" }).code).toBe("HOST_REQUEST_FAILED");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("exports typed host capability registration helpers", async () => {
|
|
35
|
+
const single = defineHostCapability("demo.open", {
|
|
36
|
+
schemaHash: "sha256:demo",
|
|
37
|
+
handle: (params: { name: string }) => ({ opened: params.name }),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(single.id).toBe("demo.open");
|
|
41
|
+
expect(single.schemaHash).toBe("sha256:demo");
|
|
42
|
+
expect(single.handle({ name: "workspace" })).toEqual({ opened: "workspace" });
|
|
43
|
+
|
|
44
|
+
const [registered] = defineHostCapabilities({
|
|
45
|
+
"demo.close": {
|
|
46
|
+
handle: async (params: { sessionId: string }) => ({ closed: params.sessionId }),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(registered?.id).toBe("demo.close");
|
|
51
|
+
await expect(registered?.handle({ sessionId: "session-1" })).resolves.toEqual({ closed: "session-1" });
|
|
52
|
+
});
|
|
53
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export { RIGKIT_SDK_VERSION } from "./version.ts";
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
defineConfig,
|
|
5
|
+
defineProvider,
|
|
6
|
+
env,
|
|
7
|
+
isRigkitConfig,
|
|
8
|
+
isProviderDefinition,
|
|
9
|
+
isWorkflow,
|
|
10
|
+
isWorkflowNode,
|
|
11
|
+
sequence,
|
|
12
|
+
workflow,
|
|
13
|
+
} from "@rigkit/engine";
|
|
14
|
+
export type * from "@rigkit/engine";
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
createRuntimeControlApiHandler,
|
|
18
|
+
createRuntimeApp,
|
|
19
|
+
DEFAULT_IDLE_MS,
|
|
20
|
+
RIGKIT_RUNTIME_VERSION,
|
|
21
|
+
createRuntimeStateService,
|
|
22
|
+
HostCommandRequestEffectSchema,
|
|
23
|
+
HostCommandRequestSchema,
|
|
24
|
+
HostCommandResultEffectSchema,
|
|
25
|
+
HostCommandResultSchema,
|
|
26
|
+
HostResponseEffectSchema,
|
|
27
|
+
HostResponseSchema,
|
|
28
|
+
OkResponseEffectSchema,
|
|
29
|
+
OperationsManifestEffectSchema,
|
|
30
|
+
ProjectInfoEffectSchema,
|
|
31
|
+
RUNTIME_API_VERSION,
|
|
32
|
+
RUNTIME_PROTOCOL_HASH,
|
|
33
|
+
RunStartedEffectSchema,
|
|
34
|
+
RunOperationRequestEffectSchema,
|
|
35
|
+
RunOperationRequestSchema,
|
|
36
|
+
RuntimeApiState,
|
|
37
|
+
RuntimeControlHttpError,
|
|
38
|
+
RuntimeEngineError,
|
|
39
|
+
RuntimeHealthEffectSchema,
|
|
40
|
+
RuntimeHostRequestError,
|
|
41
|
+
RuntimeMetadataEffectSchema,
|
|
42
|
+
RuntimeOperationNotFoundError,
|
|
43
|
+
RuntimeOperationCliEffectSchema,
|
|
44
|
+
RuntimeOperationEffectSchema,
|
|
45
|
+
RuntimeOperationValidationError,
|
|
46
|
+
RuntimeProtocolSchemaError,
|
|
47
|
+
RuntimeRunEffectSchema,
|
|
48
|
+
RuntimeRunCancelledError,
|
|
49
|
+
RuntimeServerError,
|
|
50
|
+
RunsResponseEffectSchema,
|
|
51
|
+
SnapshotsResponseEffectSchema,
|
|
52
|
+
CacheEntryEffectSchema,
|
|
53
|
+
CacheResponseEffectSchema,
|
|
54
|
+
CacheClearRequestEffectSchema,
|
|
55
|
+
CacheClearResponseEffectSchema,
|
|
56
|
+
WorkflowSummaryEffectSchema,
|
|
57
|
+
WorkflowsResponseEffectSchema,
|
|
58
|
+
WorkspaceEffectSchema,
|
|
59
|
+
WorkspacesResponseEffectSchema,
|
|
60
|
+
runtimeControlApi,
|
|
61
|
+
runtimeControlApiErrorMiddlewareLayer,
|
|
62
|
+
runtimeApiStateLayer,
|
|
63
|
+
runtimeControlApiHandlersLayer,
|
|
64
|
+
runtimeControlOpenApiDocument,
|
|
65
|
+
runtimeControlErrorStatus,
|
|
66
|
+
isRuntimeRunError,
|
|
67
|
+
normalizeRuntimeRunError,
|
|
68
|
+
runtimeFailureBody,
|
|
69
|
+
serveRuntime,
|
|
70
|
+
serveRuntimeEffect,
|
|
71
|
+
type RuntimeFailureCode,
|
|
72
|
+
type HostCommandRequest,
|
|
73
|
+
type HostCommandResult,
|
|
74
|
+
type HostRequestEvent,
|
|
75
|
+
type HostResponse,
|
|
76
|
+
type RuntimeAppState,
|
|
77
|
+
type RuntimeEvent,
|
|
78
|
+
type HostCapabilitySessionResult,
|
|
79
|
+
type RuntimeOperation,
|
|
80
|
+
type RuntimeOperationsManifest,
|
|
81
|
+
type RuntimeRunError,
|
|
82
|
+
type RuntimeServer,
|
|
83
|
+
type RuntimeStateService,
|
|
84
|
+
type RuntimeStateServiceOptions,
|
|
85
|
+
type ServeRuntimeOptions,
|
|
86
|
+
} from "./runtime/index.ts";
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpApiBuilder,
|
|
3
|
+
HttpApp,
|
|
4
|
+
HttpServer,
|
|
5
|
+
HttpServerResponse,
|
|
6
|
+
type HttpServerRequest,
|
|
7
|
+
} from "@effect/platform";
|
|
8
|
+
import { Cause, Context, Effect, Layer } from "effect";
|
|
9
|
+
import { RIGKIT_ENGINE_VERSION } from "@rigkit/engine";
|
|
10
|
+
import { RIGKIT_RUNTIME_VERSION } from "./version.ts";
|
|
11
|
+
import { runtimeControlApi } from "./api.ts";
|
|
12
|
+
import {
|
|
13
|
+
RUNTIME_API_VERSION,
|
|
14
|
+
RUNTIME_PROTOCOL_HASH,
|
|
15
|
+
} from "./protocol.ts";
|
|
16
|
+
import { openApiDocument } from "./openapi.ts";
|
|
17
|
+
import {
|
|
18
|
+
runtimeControlErrorStatus,
|
|
19
|
+
runtimeHealth,
|
|
20
|
+
runtimeMetadata,
|
|
21
|
+
runtimeOperations,
|
|
22
|
+
runtimeProject,
|
|
23
|
+
runtimeRun,
|
|
24
|
+
runtimeRunEvents,
|
|
25
|
+
runtimeRuns,
|
|
26
|
+
runtimeSnapshots,
|
|
27
|
+
runtimeWorkflows,
|
|
28
|
+
runtimeWorkspaces,
|
|
29
|
+
shutdownRuntime,
|
|
30
|
+
startRuntimeRun,
|
|
31
|
+
submitHostResponse,
|
|
32
|
+
runtimeCache,
|
|
33
|
+
clearRuntimeCache,
|
|
34
|
+
invalidateRuntimeCache,
|
|
35
|
+
type RuntimeAppState,
|
|
36
|
+
} from "./control.ts";
|
|
37
|
+
import type { RunStore } from "./runs.ts";
|
|
38
|
+
import type { RuntimeContext } from "./types.ts";
|
|
39
|
+
|
|
40
|
+
export class RuntimeApiState extends Context.Tag("rigkit/RuntimeApiState")<
|
|
41
|
+
RuntimeApiState,
|
|
42
|
+
RuntimeAppState
|
|
43
|
+
>() {}
|
|
44
|
+
|
|
45
|
+
export function runtimeApiStateLayer(context: RuntimeContext, store: RunStore) {
|
|
46
|
+
return Layer.succeed(RuntimeApiState, { context, store });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const runtimeControlApiHandlersLayer = HttpApiBuilder.group(
|
|
50
|
+
runtimeControlApi,
|
|
51
|
+
"control",
|
|
52
|
+
(handlers) =>
|
|
53
|
+
handlers
|
|
54
|
+
.handle("health", (request) =>
|
|
55
|
+
handleControlRequest(request, (state) => runtimeHealth(state.context), { public: true }))
|
|
56
|
+
.handle("openApi", (request) => handleControlRequest(request, () => openApiDocument()))
|
|
57
|
+
.handle("runtime", (request) => handleControlRequest(request, () => runtimeMetadata()))
|
|
58
|
+
.handle("project", (request) => handleControlRequest(request, (state) => runtimeProject(state.context)))
|
|
59
|
+
.handle("operations", (request) => handleControlRequest(request, (state) => runtimeOperations(state.context)))
|
|
60
|
+
.handle("workflows", (request) => handleControlRequest(request, (state) => runtimeWorkflows(state.context)))
|
|
61
|
+
.handle("workspaces", (request) => handleControlRequest(request, (state) => runtimeWorkspaces(state.context)))
|
|
62
|
+
.handle("snapshots", (request) => handleControlRequest(request, (state) => runtimeSnapshots(state.context)))
|
|
63
|
+
.handle("cache", (request) => handleControlRequest(request, (state) => runtimeCache(state.context)))
|
|
64
|
+
.handle("clearCache", (request) =>
|
|
65
|
+
handleControlRequest(request, (state) => clearRuntimeCache(state.context, request.payload)))
|
|
66
|
+
.handle("invalidateCache", (request) =>
|
|
67
|
+
handleControlRequest(request, (state) => invalidateRuntimeCache(state.context, request.payload)))
|
|
68
|
+
.handle("runs", (request) => handleControlRequest(request, (state) => runtimeRuns(state.store)))
|
|
69
|
+
.handle("startRun", (request) =>
|
|
70
|
+
handleControlRequest(request, (state) => startRuntimeRun(state, request.payload)))
|
|
71
|
+
.handle("run", (request) =>
|
|
72
|
+
handleControlRequest(request, (state) => runtimeRun(state.store, request.path.runId)))
|
|
73
|
+
.handle("runEvents", (request) =>
|
|
74
|
+
handleControlRequest(
|
|
75
|
+
request,
|
|
76
|
+
(state) => HttpServerResponse.fromWeb(runtimeRunEvents(state.store, request.path.runId)),
|
|
77
|
+
))
|
|
78
|
+
.handle("hostResponse", (request) =>
|
|
79
|
+
handleControlRequest(
|
|
80
|
+
request,
|
|
81
|
+
(state) => submitHostResponse(state, request.path.requestId, request.payload),
|
|
82
|
+
))
|
|
83
|
+
.handle("shutdown", (request) =>
|
|
84
|
+
handleControlRequest(request, (state) => shutdownRuntime(state.context))),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
export const runtimeControlApiErrorMiddlewareLayer = HttpApiBuilder.middleware(
|
|
88
|
+
normalizeRuntimeControlErrors,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export function createRuntimeControlApiHandler(context: RuntimeContext, store: RunStore) {
|
|
92
|
+
const runtimeApiLayer = HttpApiBuilder.api(runtimeControlApi).pipe(
|
|
93
|
+
Layer.provide(runtimeControlApiHandlersLayer),
|
|
94
|
+
Layer.provide(runtimeApiStateLayer(context, store)),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return HttpApiBuilder.toWebHandler(
|
|
98
|
+
Layer.mergeAll(
|
|
99
|
+
runtimeApiLayer,
|
|
100
|
+
runtimeControlApiErrorMiddlewareLayer,
|
|
101
|
+
HttpServer.layerContext,
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type ControlRequest = {
|
|
107
|
+
readonly request: HttpServerRequest.HttpServerRequest;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
type ControlOptions = {
|
|
111
|
+
readonly public?: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function handleControlRequest<A>(
|
|
115
|
+
request: ControlRequest,
|
|
116
|
+
run: (state: RuntimeAppState) => A | PromiseLike<A>,
|
|
117
|
+
options: ControlOptions = {},
|
|
118
|
+
) {
|
|
119
|
+
return Effect.gen(function* () {
|
|
120
|
+
const state = yield* RuntimeApiState;
|
|
121
|
+
yield* appendRuntimeHeaders;
|
|
122
|
+
if (!options.public && request.request.headers.authorization !== `Bearer ${state.context.token}`) {
|
|
123
|
+
return jsonError(401, "Unauthorized");
|
|
124
|
+
}
|
|
125
|
+
state.context.touch();
|
|
126
|
+
return yield* Effect.tryPromise({
|
|
127
|
+
try: async () => run(state),
|
|
128
|
+
catch: (error) => error,
|
|
129
|
+
}).pipe(
|
|
130
|
+
Effect.catchAll((error) => Effect.succeed(errorResponse(error))),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const appendRuntimeHeaders = HttpApp.appendPreResponseHandler((_request, response) =>
|
|
136
|
+
Effect.succeed(HttpServerResponse.setHeaders(response, runtimeHeaders())));
|
|
137
|
+
|
|
138
|
+
function runtimeHeaders() {
|
|
139
|
+
return {
|
|
140
|
+
"x-rigkit-api-version": String(RUNTIME_API_VERSION),
|
|
141
|
+
"x-rigkit-protocol-hash": RUNTIME_PROTOCOL_HASH,
|
|
142
|
+
"x-rigkit-engine-version": RIGKIT_ENGINE_VERSION,
|
|
143
|
+
"x-rigkit-runtime-version": RIGKIT_RUNTIME_VERSION,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function errorResponse(error: unknown) {
|
|
148
|
+
return jsonError(runtimeControlErrorStatus(error), error instanceof Error ? error.message : String(error));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function jsonError(status: number, message: string) {
|
|
152
|
+
return HttpServerResponse.unsafeJson({ error: { message } }, { status });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function normalizeRuntimeControlErrors(app: HttpApp.Default): HttpApp.Default {
|
|
156
|
+
return Effect.catchAllCause(app as HttpApp.Default<unknown>, (cause) => {
|
|
157
|
+
const error = Cause.squash(cause);
|
|
158
|
+
if (isHttpApiDecodeError(error)) {
|
|
159
|
+
return Effect.succeed(jsonError(400, error.message));
|
|
160
|
+
}
|
|
161
|
+
return Effect.failCause(cause);
|
|
162
|
+
}) as HttpApp.Default;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isHttpApiDecodeError(error: unknown): error is { _tag: "HttpApiDecodeError"; message: string } {
|
|
166
|
+
return Boolean(
|
|
167
|
+
error &&
|
|
168
|
+
typeof error === "object" &&
|
|
169
|
+
"_tag" in error &&
|
|
170
|
+
error._tag === "HttpApiDecodeError" &&
|
|
171
|
+
"message" in error &&
|
|
172
|
+
typeof error.message === "string",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { OpenApi } from "@effect/platform";
|
|
2
|
+
import { runtimeControlApi } from "@rigkit/runtime-client";
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
RuntimeControlHealthEffectSchema as RuntimeHealthEffectSchema,
|
|
6
|
+
RuntimeControlMetadataEffectSchema as RuntimeMetadataEffectSchema,
|
|
7
|
+
RuntimeControlOkResponseEffectSchema as OkResponseEffectSchema,
|
|
8
|
+
RuntimeControlOperationCliEffectSchema as RuntimeOperationCliEffectSchema,
|
|
9
|
+
RuntimeControlOperationEffectSchema as RuntimeOperationEffectSchema,
|
|
10
|
+
RuntimeControlOperationsManifestEffectSchema as OperationsManifestEffectSchema,
|
|
11
|
+
RuntimeControlProjectInfoEffectSchema as ProjectInfoEffectSchema,
|
|
12
|
+
RuntimeControlRunEffectSchema as RuntimeRunEffectSchema,
|
|
13
|
+
RuntimeControlRunStartedEffectSchema as RunStartedEffectSchema,
|
|
14
|
+
RuntimeControlRunsResponseEffectSchema as RunsResponseEffectSchema,
|
|
15
|
+
RuntimeControlSnapshotsResponseEffectSchema as SnapshotsResponseEffectSchema,
|
|
16
|
+
RuntimeControlCacheEntryEffectSchema as CacheEntryEffectSchema,
|
|
17
|
+
RuntimeControlCacheResponseEffectSchema as CacheResponseEffectSchema,
|
|
18
|
+
RuntimeControlCacheClearRequestEffectSchema as CacheClearRequestEffectSchema,
|
|
19
|
+
RuntimeControlCacheClearResponseEffectSchema as CacheClearResponseEffectSchema,
|
|
20
|
+
RuntimeControlWorkflowSummaryEffectSchema as WorkflowSummaryEffectSchema,
|
|
21
|
+
RuntimeControlWorkflowsResponseEffectSchema as WorkflowsResponseEffectSchema,
|
|
22
|
+
RuntimeControlWorkspaceEffectSchema as WorkspaceEffectSchema,
|
|
23
|
+
RuntimeControlWorkspacesResponseEffectSchema as WorkspacesResponseEffectSchema,
|
|
24
|
+
runtimeControlApi,
|
|
25
|
+
} from "@rigkit/runtime-client";
|
|
26
|
+
|
|
27
|
+
export function runtimeControlOpenApiDocument(): OpenApi.OpenAPISpec {
|
|
28
|
+
const spec = OpenApi.fromApi(runtimeControlApi, { additionalPropertiesStrategy: "allow" });
|
|
29
|
+
spec.security = [{ bearerAuth: [] }];
|
|
30
|
+
spec.components.securitySchemes = {
|
|
31
|
+
...spec.components.securitySchemes,
|
|
32
|
+
bearerAuth: {
|
|
33
|
+
type: "http",
|
|
34
|
+
scheme: "bearer",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
for (const [path, methods] of Object.entries(spec.paths)) {
|
|
39
|
+
for (const operation of Object.values(methods)) {
|
|
40
|
+
operation.security = path === "/health" ? [] : [{ bearerAuth: [] }];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const hostResponseOperation = spec.paths["/host-responses/{requestId}"]?.post as any;
|
|
45
|
+
const hostResponseSchema = hostResponseOperation?.requestBody?.content?.["application/json"]?.schema;
|
|
46
|
+
if (hostResponseSchema) {
|
|
47
|
+
spec.components.schemas.HostResponse = hostResponseSchema;
|
|
48
|
+
hostResponseOperation.requestBody.content["application/json"].schema = {
|
|
49
|
+
$ref: "#/components/schemas/HostResponse",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
spec.paths["/runs/{runId}/session"] = {
|
|
54
|
+
get: {
|
|
55
|
+
tags: ["control"],
|
|
56
|
+
operationId: "control.runSession",
|
|
57
|
+
parameters: [{
|
|
58
|
+
name: "runId",
|
|
59
|
+
in: "path",
|
|
60
|
+
required: true,
|
|
61
|
+
schema: { type: "string" },
|
|
62
|
+
}],
|
|
63
|
+
security: [{ bearerAuth: [] }],
|
|
64
|
+
responses: {
|
|
65
|
+
"101": { description: "Run WebSocket session" },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return spec;
|
|
71
|
+
}
|