@rigkit/sdk 0.2.6 → 0.2.8

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 CHANGED
@@ -8,9 +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 `step.log(message, options)` for structured step logs. Pass `step.log` to SDKs that accept a logger callback.
12
- Those events stream over the runtime run session and are rendered by interactive
13
- hosts such as the `rig` terminal run timeline.
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.
14
12
  Provider VM commands can also report incremental stdout/stderr through
15
13
  `ExecOptions.onOutput`; the runtime forwards those chunks over the same run
16
14
  session and falls back to buffered command output when a provider cannot stream.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigkit/sdk",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
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.6",
27
- "@rigkit/runtime-client": "0.2.6"
26
+ "@rigkit/engine": "0.2.8",
27
+ "@rigkit/runtime-client": "0.2.8"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/bun": "latest",
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ type ServeArgs = {
8
8
  projectDir?: string;
9
9
  configPath?: string;
10
10
  statePath?: string;
11
+ globalFragmentRoot?: string;
11
12
  sourceJson?: string;
12
13
  handlePath?: string;
13
14
  tokenPath?: string;
@@ -42,6 +43,7 @@ const runtime = await serveRuntime({
42
43
  projectDir: resolve(options.projectDir!),
43
44
  configPath: resolve(options.configPath!),
44
45
  statePath: options.statePath ? resolve(options.statePath) : undefined,
46
+ globalFragmentRoot: options.globalFragmentRoot ? resolve(options.globalFragmentRoot) : undefined,
45
47
  source: options.sourceJson ? JSON.parse(options.sourceJson) : undefined,
46
48
  handlePath: resolve(options.handlePath!),
47
49
  tokenPath: resolve(options.tokenPath!),
@@ -95,6 +97,9 @@ function parseServeArgs(args: string[]): ServeArgs {
95
97
  case "--state-path":
96
98
  parsed.statePath = readValue();
97
99
  break;
100
+ case "--global-fragment-root":
101
+ parsed.globalFragmentRoot = readValue();
102
+ break;
98
103
  case "--source-json":
99
104
  parsed.sourceJson = readValue();
100
105
  break;
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.6");
19
+ expect(RIGKIT_SDK_VERSION).toBe("0.2.8");
20
20
  expect(env).toBeTypeOf("function");
21
21
  expect(env.secret).toBeTypeOf("function");
22
22
  expect(defineConfig).toBeTypeOf("function");
package/src/index.ts CHANGED
@@ -49,6 +49,10 @@ export {
49
49
  RuntimeServerError,
50
50
  RunsResponseEffectSchema,
51
51
  SnapshotsResponseEffectSchema,
52
+ CacheEntryEffectSchema,
53
+ CacheResponseEffectSchema,
54
+ CacheClearRequestEffectSchema,
55
+ CacheClearResponseEffectSchema,
52
56
  WorkflowSummaryEffectSchema,
53
57
  WorkflowsResponseEffectSchema,
54
58
  WorkspaceEffectSchema,
@@ -29,6 +29,9 @@ import {
29
29
  shutdownRuntime,
30
30
  startRuntimeRun,
31
31
  submitHostResponse,
32
+ runtimeCache,
33
+ clearRuntimeCache,
34
+ invalidateRuntimeCache,
32
35
  type RuntimeAppState,
33
36
  } from "./control.ts";
34
37
  import type { RunStore } from "./runs.ts";
@@ -57,6 +60,11 @@ export const runtimeControlApiHandlersLayer = HttpApiBuilder.group(
57
60
  .handle("workflows", (request) => handleControlRequest(request, (state) => runtimeWorkflows(state.context)))
58
61
  .handle("workspaces", (request) => handleControlRequest(request, (state) => runtimeWorkspaces(state.context)))
59
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)))
60
68
  .handle("runs", (request) => handleControlRequest(request, (state) => runtimeRuns(state.store)))
61
69
  .handle("startRun", (request) =>
62
70
  handleControlRequest(request, (state) => startRuntimeRun(state, request.payload)))
@@ -13,6 +13,10 @@ export {
13
13
  RuntimeControlRunStartedEffectSchema as RunStartedEffectSchema,
14
14
  RuntimeControlRunsResponseEffectSchema as RunsResponseEffectSchema,
15
15
  RuntimeControlSnapshotsResponseEffectSchema as SnapshotsResponseEffectSchema,
16
+ RuntimeControlCacheEntryEffectSchema as CacheEntryEffectSchema,
17
+ RuntimeControlCacheResponseEffectSchema as CacheResponseEffectSchema,
18
+ RuntimeControlCacheClearRequestEffectSchema as CacheClearRequestEffectSchema,
19
+ RuntimeControlCacheClearResponseEffectSchema as CacheClearResponseEffectSchema,
16
20
  RuntimeControlWorkflowSummaryEffectSchema as WorkflowSummaryEffectSchema,
17
21
  RuntimeControlWorkflowsResponseEffectSchema as WorkflowsResponseEffectSchema,
18
22
  RuntimeControlWorkspaceEffectSchema as WorkspaceEffectSchema,
@@ -39,6 +39,7 @@ export function runtimeHealth(context: RuntimeContext) {
39
39
  projectDir: context.projectDir,
40
40
  configPath: context.configPath,
41
41
  statePath: context.statePath,
42
+ globalFragmentRoot: context.globalFragmentRoot,
42
43
  engineVersion: RIGKIT_ENGINE_VERSION,
43
44
  runtimeVersion: RIGKIT_RUNTIME_VERSION,
44
45
  expiresAt: context.getExpiresAt(),
@@ -79,6 +80,29 @@ export async function runtimeSnapshots(context: RuntimeContext) {
79
80
  return { snapshots: engine.listSnapshots() };
80
81
  }
81
82
 
83
+ export async function runtimeCache(context: RuntimeContext) {
84
+ const engine = await loadEngine(context);
85
+ return await engine.listCache();
86
+ }
87
+
88
+ export async function clearRuntimeCache(context: RuntimeContext, body: { scope?: "local" | "global" | "all" }) {
89
+ const engine = await loadEngine(context);
90
+ const result = await engine.clearCache({ scope: body.scope });
91
+ return { ok: true, deleted: result.deleted };
92
+ }
93
+
94
+ export async function invalidateRuntimeCache(
95
+ context: RuntimeContext,
96
+ body: { workflow?: string; nodePaths?: readonly string[] },
97
+ ) {
98
+ const engine = await loadEngine(context);
99
+ const result = await engine.invalidateCache({
100
+ workflow: body.workflow,
101
+ nodePaths: body.nodePaths,
102
+ });
103
+ return { ok: true, invalidated: result.invalidated };
104
+ }
105
+
82
106
  export function runtimeRuns(store: RunStore) {
83
107
  return {
84
108
  runs: [...store.runs.values()].map((run) => summarizeRun(run)),
@@ -0,0 +1,39 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ RuntimeEngineError,
4
+ runtimeFailureBody,
5
+ } from "./errors.ts";
6
+
7
+ describe("runtime failure serialization", () => {
8
+ test("preserves cause chain details for run logs", () => {
9
+ const upstream = new Error("INTERNAL_ERROR: Internal server error") as Error & {
10
+ body: Record<string, unknown>;
11
+ statusCode: number;
12
+ };
13
+ upstream.name = "InternalErrorError";
14
+ upstream.statusCode = 500;
15
+ upstream.body = {
16
+ code: "INTERNAL_ERROR",
17
+ message: "Internal server error",
18
+ requestId: "req_123",
19
+ authorization: "Bearer secret-token",
20
+ };
21
+
22
+ const body = runtimeFailureBody(new RuntimeEngineError({ cause: upstream }));
23
+
24
+ expect(body.code).toBe("ENGINE_FAILED");
25
+ expect(body.message).toBe("INTERNAL_ERROR: Internal server error");
26
+ expect(body.details?.name).toBe("RuntimeEngineError");
27
+ expect(body.details?.cause).toMatchObject({
28
+ name: "InternalErrorError",
29
+ message: "INTERNAL_ERROR: Internal server error",
30
+ statusCode: 500,
31
+ body: {
32
+ code: "INTERNAL_ERROR",
33
+ message: "Internal server error",
34
+ requestId: "req_123",
35
+ authorization: "[redacted]",
36
+ },
37
+ });
38
+ });
39
+ });
@@ -10,6 +10,12 @@ export type RuntimeFailureCode =
10
10
  | "OPERATION_VALIDATION_FAILED"
11
11
  | "RUN_CANCELLED";
12
12
 
13
+ export type RuntimeFailureBody = {
14
+ code: RuntimeFailureCode;
15
+ message: string;
16
+ details?: Record<string, unknown>;
17
+ };
18
+
13
19
  export class RuntimeOperationValidationError extends Error {
14
20
  readonly code = "OPERATION_VALIDATION_FAILED" as const;
15
21
  readonly operation: string;
@@ -95,14 +101,99 @@ export function normalizeRuntimeRunError(error: unknown): RuntimeRunError {
95
101
  return new RuntimeEngineError({ cause: error });
96
102
  }
97
103
 
98
- export function runtimeFailureBody(error: unknown): { code: RuntimeFailureCode; message: string } {
104
+ export function runtimeFailureBody(error: unknown): RuntimeFailureBody {
99
105
  const normalized = normalizeRuntimeRunError(error);
106
+ const details = failureDetails(normalized);
100
107
  return {
101
108
  code: normalized.code,
102
109
  message: normalized.message,
110
+ ...(details ? { details } : {}),
103
111
  };
104
112
  }
105
113
 
106
114
  function errorMessage(error: unknown): string {
107
115
  return error instanceof Error ? error.message : String(error);
108
116
  }
117
+
118
+ const MAX_DETAIL_DEPTH = 5;
119
+ const MAX_DETAIL_KEYS = 60;
120
+ const MAX_DETAIL_ITEMS = 40;
121
+ const MAX_DETAIL_STRING = 8_000;
122
+ const SENSITIVE_DETAIL_KEY = /authorization|api[-_]?key|access[-_]?token|refresh[-_]?token|password|secret|credential|cookie/i;
123
+
124
+ function failureDetails(error: RuntimeRunError): Record<string, unknown> | undefined {
125
+ const details = errorDetailValue(error, new WeakSet(), 0);
126
+ return isRecord(details) ? details : undefined;
127
+ }
128
+
129
+ function errorDetailValue(value: unknown, seen: WeakSet<object>, depth: number): unknown {
130
+ if (depth > MAX_DETAIL_DEPTH) return "[Max depth exceeded]";
131
+ if (value === null || value === undefined) return value;
132
+ if (typeof value === "string") return clipDetailString(value);
133
+ if (typeof value === "number" || typeof value === "boolean") return value;
134
+ if (typeof value === "bigint") return value.toString();
135
+ if (typeof value === "symbol" || typeof value === "function") return String(value);
136
+
137
+ if (seen.has(value)) return "[Circular]";
138
+ seen.add(value);
139
+
140
+ if (value instanceof Error) {
141
+ const detail: Record<string, unknown> = {
142
+ name: value.name,
143
+ message: value.message,
144
+ };
145
+ if (value.stack) detail.stack = clipDetailString(value.stack);
146
+
147
+ for (const key of [
148
+ "code",
149
+ "status",
150
+ "statusCode",
151
+ "requestId",
152
+ "method",
153
+ "path",
154
+ "hostCode",
155
+ "operation",
156
+ "reason",
157
+ "body",
158
+ ]) {
159
+ const field = (value as unknown as Record<string, unknown>)[key];
160
+ if (field !== undefined) detail[key] = redactOrDetail(key, field, seen, depth + 1);
161
+ }
162
+
163
+ const cause = (value as { cause?: unknown }).cause;
164
+ if (cause !== undefined) detail.cause = errorDetailValue(cause, seen, depth + 1);
165
+ return detail;
166
+ }
167
+
168
+ if (Array.isArray(value)) {
169
+ const items = value.slice(0, MAX_DETAIL_ITEMS).map((item) => errorDetailValue(item, seen, depth + 1));
170
+ if (value.length > MAX_DETAIL_ITEMS) items.push(`[${value.length - MAX_DETAIL_ITEMS} more items]`);
171
+ return items;
172
+ }
173
+
174
+ if (!isRecord(value)) return String(value);
175
+
176
+ const detail: Record<string, unknown> = {};
177
+ const entries = Object.entries(value).slice(0, MAX_DETAIL_KEYS);
178
+ for (const [key, field] of entries) {
179
+ detail[key] = redactOrDetail(key, field, seen, depth + 1);
180
+ }
181
+ const extra = Object.keys(value).length - entries.length;
182
+ if (extra > 0) detail.__truncated = `${extra} more keys`;
183
+ return detail;
184
+ }
185
+
186
+ function redactOrDetail(key: string, value: unknown, seen: WeakSet<object>, depth: number): unknown {
187
+ if (SENSITIVE_DETAIL_KEY.test(key)) return "[redacted]";
188
+ return errorDetailValue(value, seen, depth);
189
+ }
190
+
191
+ function clipDetailString(value: string): string {
192
+ return value.length > MAX_DETAIL_STRING
193
+ ? `${value.slice(0, MAX_DETAIL_STRING)}... [truncated]`
194
+ : value;
195
+ }
196
+
197
+ function isRecord(value: unknown): value is Record<string, unknown> {
198
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
199
+ }
@@ -46,6 +46,10 @@ export {
46
46
  RuntimeRunEffectSchema,
47
47
  RunsResponseEffectSchema,
48
48
  SnapshotsResponseEffectSchema,
49
+ CacheEntryEffectSchema,
50
+ CacheResponseEffectSchema,
51
+ CacheClearRequestEffectSchema,
52
+ CacheClearResponseEffectSchema,
49
53
  WorkflowSummaryEffectSchema,
50
54
  WorkflowsResponseEffectSchema,
51
55
  WorkspaceEffectSchema,
@@ -5,6 +5,7 @@ import {
5
5
  type JsonValue,
6
6
  type LocalHostCapabilityRequestOptions,
7
7
  } from "@rigkit/engine";
8
+ import { join } from "node:path";
8
9
  import { normalizeRuntimeRunError } from "./errors.ts";
9
10
  import {
10
11
  HostCommandResultSchema,
@@ -31,6 +32,7 @@ export type EngineLoadOptions = {
31
32
  projectDir: string;
32
33
  configPath: string;
33
34
  statePath?: string;
35
+ globalFragmentRoot?: string;
34
36
  source?: JsonValue;
35
37
  };
36
38
 
@@ -46,6 +48,11 @@ export async function loadEngine(input: EngineLoadOptions): Promise<DevMachineEn
46
48
  runtimeVersion: RIGKIT_RUNTIME_VERSION,
47
49
  source: input.source,
48
50
  }),
51
+ globalFragmentStateLocator: input.globalFragmentRoot
52
+ ? (fragment) => ({
53
+ statePath: join(input.globalFragmentRoot!, fragment.hash, "state.sqlite"),
54
+ })
55
+ : undefined,
49
56
  });
50
57
  await engine.load();
51
58
  return engine;
@@ -67,6 +74,11 @@ async function executeOperation(run: RunRecord, store: RunStore, options: Engine
67
74
  runtimeVersion: RIGKIT_RUNTIME_VERSION,
68
75
  source: options.source,
69
76
  }),
77
+ globalFragmentStateLocator: options.globalFragmentRoot
78
+ ? (fragment) => ({
79
+ statePath: join(options.globalFragmentRoot!, fragment.hash, "state.sqlite"),
80
+ })
81
+ : undefined,
70
82
  interaction: {
71
83
  present: async (request) => {
72
84
  await requestHost(store, run, "open.external", {
@@ -113,7 +113,7 @@ export type RunCompletedEvent = {
113
113
  export type RunFailedEvent = {
114
114
  type: "run.failed";
115
115
  runId: string;
116
- error: { code?: string; message: string };
116
+ error: { code?: string; message: string; details?: Record<string, unknown> };
117
117
  };
118
118
 
119
119
  export type RuntimeEvent =
@@ -1,6 +1,7 @@
1
1
  import type { RuntimeEvent, RuntimeOperation } from "./protocol.ts";
2
2
  import {
3
3
  RuntimeHostRequestError,
4
+ type RuntimeFailureBody,
4
5
  runtimeFailureBody,
5
6
  } from "./errors.ts";
6
7
 
@@ -33,7 +34,7 @@ export type RunRecord = {
33
34
  status: RunStatus;
34
35
  events: RuntimeEvent[];
35
36
  result?: unknown;
36
- error?: { code: string; message: string };
37
+ error?: RuntimeFailureBody;
37
38
  pendingHostRequestIds: Set<string>;
38
39
  pendingHostCapabilityResourceIds: Set<string>;
39
40
  subscribers: Set<ReadableStreamDefaultController<Uint8Array>>;
@@ -48,7 +49,7 @@ export type RuntimeRunSummary = {
48
49
  input: unknown;
49
50
  status: RunStatus;
50
51
  result?: unknown;
51
- error?: { code: string; message: string };
52
+ error?: RuntimeFailureBody;
52
53
  createdAt: string;
53
54
  updatedAt: string;
54
55
  };
@@ -41,6 +41,7 @@ export async function serveRuntime(options: ServeRuntimeOptions): Promise<Runtim
41
41
  const projectDir = resolve(options.projectDir);
42
42
  const configPath = resolve(options.configPath);
43
43
  const statePath = options.statePath ? resolve(options.statePath) : undefined;
44
+ const globalFragmentRoot = options.globalFragmentRoot ? resolve(options.globalFragmentRoot) : undefined;
44
45
  const host = options.host ?? "127.0.0.1";
45
46
  const token = options.token ?? readOrCreateToken(options.tokenPath);
46
47
  const idleMs = options.idleMs ?? DEFAULT_IDLE_MS;
@@ -65,6 +66,7 @@ export async function serveRuntime(options: ServeRuntimeOptions): Promise<Runtim
65
66
  projectDir,
66
67
  configPath,
67
68
  statePath,
69
+ globalFragmentRoot,
68
70
  pid: process.pid,
69
71
  url,
70
72
  tokenPath: options.tokenPath,
@@ -82,6 +84,7 @@ export async function serveRuntime(options: ServeRuntimeOptions): Promise<Runtim
82
84
  projectDir,
83
85
  configPath,
84
86
  statePath,
87
+ globalFragmentRoot,
85
88
  source: options.source,
86
89
  token,
87
90
  startedAt,
@@ -6,6 +6,7 @@ export type ServeRuntimeOptions = {
6
6
  projectDir: string;
7
7
  configPath: string;
8
8
  statePath?: string;
9
+ globalFragmentRoot?: string;
9
10
  source?: JsonValue;
10
11
  handlePath: string;
11
12
  tokenPath: string;
@@ -28,6 +29,7 @@ export type RuntimeContext = {
28
29
  readonly projectDir: string;
29
30
  readonly configPath: string;
30
31
  readonly statePath?: string;
32
+ readonly globalFragmentRoot?: string;
31
33
  readonly source?: JsonValue;
32
34
  readonly token: string;
33
35
  readonly startedAt: string;
@@ -1 +1 @@
1
- export const RIGKIT_RUNTIME_VERSION = "0.2.6";
1
+ export const RIGKIT_RUNTIME_VERSION = "0.2.8";
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const RIGKIT_SDK_VERSION = "0.2.6";
1
+ export const RIGKIT_SDK_VERSION = "0.2.8";