@reasonlayer/sdk 0.0.1-rc.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.
Files changed (101) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +35 -0
  3. package/bin/rl.mjs +75 -0
  4. package/dist/builder.d.ts +38 -0
  5. package/dist/builder.js +41 -0
  6. package/dist/bundler/index.d.ts +32 -0
  7. package/dist/bundler/index.js +143 -0
  8. package/dist/bundler/node.d.ts +10 -0
  9. package/dist/bundler/node.js +55 -0
  10. package/dist/cli/args.d.ts +9 -0
  11. package/dist/cli/args.js +71 -0
  12. package/dist/cli/config.d.ts +38 -0
  13. package/dist/cli/config.js +94 -0
  14. package/dist/cli/convex.d.ts +9 -0
  15. package/dist/cli/convex.js +25 -0
  16. package/dist/cli/format.d.ts +11 -0
  17. package/dist/cli/format.js +140 -0
  18. package/dist/cli/index.d.ts +1 -0
  19. package/dist/cli/index.js +721 -0
  20. package/dist/cli/keychain.d.ts +21 -0
  21. package/dist/cli/keychain.js +100 -0
  22. package/dist/cli/operations.d.ts +106 -0
  23. package/dist/cli/operations.js +623 -0
  24. package/dist/cli/sdk-package.d.ts +18 -0
  25. package/dist/cli/sdk-package.js +45 -0
  26. package/dist/cli/storage-upload.d.ts +9 -0
  27. package/dist/cli/storage-upload.js +21 -0
  28. package/dist/cli/watcher-compile.d.ts +1 -0
  29. package/dist/cli/watcher-compile.js +20 -0
  30. package/dist/cli/watcher-process.d.ts +1 -0
  31. package/dist/cli/watcher-process.js +312 -0
  32. package/dist/cli/workspace-files.d.ts +12 -0
  33. package/dist/cli/workspace-files.js +91 -0
  34. package/dist/cli/workspace-local.d.ts +27 -0
  35. package/dist/cli/workspace-local.js +310 -0
  36. package/dist/cli/workspace-session.d.ts +13 -0
  37. package/dist/cli/workspace-session.js +12 -0
  38. package/dist/compile.d.ts +27 -0
  39. package/dist/compile.js +834 -0
  40. package/dist/index.d.ts +10 -0
  41. package/dist/index.js +5 -0
  42. package/dist/schedule-validation.d.ts +1 -0
  43. package/dist/schedule-validation.js +16 -0
  44. package/dist/selector-schema.d.ts +26 -0
  45. package/dist/selector-schema.js +780 -0
  46. package/dist/selectors.d.ts +25 -0
  47. package/dist/selectors.js +99 -0
  48. package/dist/testkit.d.ts +32 -0
  49. package/dist/testkit.js +161 -0
  50. package/dist/types.d.ts +184 -0
  51. package/dist/types.js +110 -0
  52. package/node_modules/@reasonlayer/integrations/dist/catalog.d.ts +4937 -0
  53. package/node_modules/@reasonlayer/integrations/dist/catalog.js +20 -0
  54. package/node_modules/@reasonlayer/integrations/dist/generated/slack.d.ts +3014 -0
  55. package/node_modules/@reasonlayer/integrations/dist/generated/slack.js +3101 -0
  56. package/node_modules/@reasonlayer/integrations/dist/index.d.ts +2 -0
  57. package/node_modules/@reasonlayer/integrations/dist/index.js +1 -0
  58. package/node_modules/@reasonlayer/integrations/dist/internal/definitions.d.ts +44 -0
  59. package/node_modules/@reasonlayer/integrations/dist/internal/definitions.js +52 -0
  60. package/node_modules/@reasonlayer/integrations/dist/internal/transport.d.ts +7 -0
  61. package/node_modules/@reasonlayer/integrations/dist/internal/transport.js +48 -0
  62. package/node_modules/@reasonlayer/integrations/dist/testkit.d.ts +1 -0
  63. package/node_modules/@reasonlayer/integrations/dist/testkit.js +1 -0
  64. package/node_modules/@reasonlayer/integrations/package.json +26 -0
  65. package/node_modules/@reasonlayer/protocol/dist/environments.d.ts +7 -0
  66. package/node_modules/@reasonlayer/protocol/dist/environments.js +7 -0
  67. package/node_modules/@reasonlayer/protocol/dist/events.d.ts +313 -0
  68. package/node_modules/@reasonlayer/protocol/dist/events.js +20 -0
  69. package/node_modules/@reasonlayer/protocol/dist/index.d.ts +18 -0
  70. package/node_modules/@reasonlayer/protocol/dist/index.js +9 -0
  71. package/node_modules/@reasonlayer/protocol/dist/ir.d.ts +252 -0
  72. package/node_modules/@reasonlayer/protocol/dist/ir.js +116 -0
  73. package/node_modules/@reasonlayer/protocol/dist/permissions.d.ts +5 -0
  74. package/node_modules/@reasonlayer/protocol/dist/permissions.js +30 -0
  75. package/node_modules/@reasonlayer/protocol/dist/readmodels.d.ts +617 -0
  76. package/node_modules/@reasonlayer/protocol/dist/readmodels.js +36 -0
  77. package/node_modules/@reasonlayer/protocol/dist/runtime.d.ts +67 -0
  78. package/node_modules/@reasonlayer/protocol/dist/runtime.js +66 -0
  79. package/node_modules/@reasonlayer/protocol/dist/scheduling.d.ts +69 -0
  80. package/node_modules/@reasonlayer/protocol/dist/scheduling.js +126 -0
  81. package/node_modules/@reasonlayer/protocol/dist/selector.d.ts +23 -0
  82. package/node_modules/@reasonlayer/protocol/dist/selector.js +77 -0
  83. package/node_modules/@reasonlayer/protocol/dist/tasks.d.ts +69 -0
  84. package/node_modules/@reasonlayer/protocol/dist/tasks.js +6 -0
  85. package/node_modules/@reasonlayer/protocol/dist/validate.d.ts +6 -0
  86. package/node_modules/@reasonlayer/protocol/dist/validate.js +18 -0
  87. package/node_modules/@reasonlayer/protocol/package.json +21 -0
  88. package/node_modules/@reasonlayer/runner/dist/environment-materializer.d.ts +13 -0
  89. package/node_modules/@reasonlayer/runner/dist/environment-materializer.js +92 -0
  90. package/node_modules/@reasonlayer/runner/dist/flue-transcript.d.ts +17 -0
  91. package/node_modules/@reasonlayer/runner/dist/flue-transcript.js +182 -0
  92. package/node_modules/@reasonlayer/runner/dist/index.d.ts +184 -0
  93. package/node_modules/@reasonlayer/runner/dist/index.js +884 -0
  94. package/node_modules/@reasonlayer/runner/dist/mcp-supervisor.d.ts +15 -0
  95. package/node_modules/@reasonlayer/runner/dist/mcp-supervisor.js +142 -0
  96. package/node_modules/@reasonlayer/runner/dist/sandbox-env.d.ts +36 -0
  97. package/node_modules/@reasonlayer/runner/dist/sandbox-env.js +171 -0
  98. package/node_modules/@reasonlayer/runner/dist/task-child.d.ts +1 -0
  99. package/node_modules/@reasonlayer/runner/dist/task-child.js +72 -0
  100. package/node_modules/@reasonlayer/runner/package.json +21 -0
  101. package/package.json +65 -0
@@ -0,0 +1,310 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { spawn } from "node:child_process";
3
+ import { closeSync, openSync } from "node:fs";
4
+ import { access, lstat, mkdir, readFile, readdir, rm, symlink, writeFile, } from "node:fs/promises";
5
+ import { hostname } from "node:os";
6
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
7
+ import { FN } from "@reasonlayer/protocol";
8
+ import { EXACT_VERSION, pairedFlueRuntimeVersion, SDK_ROOT, sdkProvenance, sdkVersion, } from "./sdk-package.js";
9
+ import { LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION, parseLocalWorkspaceSession, } from "./workspace-session.js";
10
+ const SESSION_PATH = join(".rl", "session.json");
11
+ async function pathExists(path) {
12
+ try {
13
+ await lstat(path);
14
+ return true;
15
+ }
16
+ catch (error) {
17
+ if (error.code === "ENOENT")
18
+ return false;
19
+ throw error;
20
+ }
21
+ }
22
+ async function assertEmptyTarget(target) {
23
+ if (!(await pathExists(target)))
24
+ return;
25
+ const entries = await readdir(target);
26
+ if (entries.length > 0)
27
+ throw new Error(`checkout directory is not empty: ${target}`);
28
+ }
29
+ async function scaffold(target, workflowId) {
30
+ const [version, flueVersion] = await Promise.all([
31
+ sdkVersion(),
32
+ pairedFlueRuntimeVersion(),
33
+ ]);
34
+ await mkdir(join(target, "src"), { recursive: true });
35
+ const files = [
36
+ ["package.json", `${JSON.stringify({
37
+ name: workflowId,
38
+ private: true,
39
+ type: "module",
40
+ dependencies: {
41
+ "@reasonlayer/sdk": version,
42
+ "@flue/runtime": flueVersion,
43
+ },
44
+ }, null, 2)}\n`],
45
+ ["tsconfig.json", `${JSON.stringify({
46
+ compilerOptions: {
47
+ target: "ES2022",
48
+ module: "NodeNext",
49
+ moduleResolution: "NodeNext",
50
+ strict: true,
51
+ noEmit: true,
52
+ },
53
+ include: ["src/**/*.ts"],
54
+ }, null, 2)}\n`],
55
+ [".gitignore", "node_modules/\n.rl/\ndist/\n"],
56
+ [join("src", "workflow.ts"), `import { defineWorkflow, v } from "@reasonlayer/sdk";
57
+
58
+ const message = v.object({ message: v.string() });
59
+
60
+ export default defineWorkflow({
61
+ id: ${JSON.stringify(workflowId)},
62
+ input: message,
63
+ output: message,
64
+ })
65
+ .code("echo", {
66
+ output: message,
67
+ host: { kind: "cloud" },
68
+ run: (input: { message: string }) => input,
69
+ })
70
+ .output((steps) => steps.echo);
71
+ `],
72
+ ];
73
+ await Promise.all(files.map(async ([path, contents]) => await writeFile(join(target, path), contents)));
74
+ return files.length;
75
+ }
76
+ const INSTALL_TIMEOUT_MS = 180_000;
77
+ async function checkoutToolchainPins(target) {
78
+ const manifestPath = join(target, "package.json");
79
+ const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
80
+ const pins = { sdkVersion: "", flueVersion: "" };
81
+ for (const [name, key] of [
82
+ ["@reasonlayer/sdk", "sdkVersion"],
83
+ ["@flue/runtime", "flueVersion"],
84
+ ]) {
85
+ const pin = manifest.dependencies?.[name];
86
+ if (typeof pin !== "string" || !EXACT_VERSION.test(pin)) {
87
+ throw new Error(`${manifestPath} must pin ${name} to an exact version (found ${typeof pin === "string" ? `"${pin}"` : "no dependency"}); a range is not a pin, so edit it back to a single version like "0.1.0"`);
88
+ }
89
+ pins[key] = pin;
90
+ }
91
+ return pins;
92
+ }
93
+ async function installCheckoutToolchain(target) {
94
+ await new Promise((resolveInstall, rejectInstall) => {
95
+ const child = spawn("npm", ["install", "--no-audit", "--no-fund", "--package-lock=true"], { cwd: target, env: process.env });
96
+ let stdout = "";
97
+ let stderr = "";
98
+ const timer = setTimeout(() => {
99
+ child.kill("SIGKILL");
100
+ rejectInstall(new Error(`failed to install the checkout toolchain: npm install did not finish within ${INSTALL_TIMEOUT_MS / 1000}s`));
101
+ }, INSTALL_TIMEOUT_MS);
102
+ child.stdout.setEncoding("utf8");
103
+ child.stderr.setEncoding("utf8");
104
+ child.stdout.on("data", (chunk) => (stdout += chunk));
105
+ child.stderr.on("data", (chunk) => (stderr += chunk));
106
+ child.once("error", (error) => {
107
+ clearTimeout(timer);
108
+ rejectInstall(new Error(`failed to install the checkout toolchain: ${error.message}`));
109
+ });
110
+ child.once("close", (code) => {
111
+ clearTimeout(timer);
112
+ if (code === 0) {
113
+ resolveInstall();
114
+ return;
115
+ }
116
+ const diagnostic = stderr.trim() || stdout.trim();
117
+ rejectInstall(new Error(`failed to install the checkout toolchain: npm install exited ${code ?? "without a status"}${diagnostic ? `: ${diagnostic}` : ""}`));
118
+ });
119
+ });
120
+ }
121
+ async function linkLocalCheckoutToolchain(target) {
122
+ const links = [
123
+ [join(target, "node_modules", "@reasonlayer", "sdk"), SDK_ROOT],
124
+ [join(target, "node_modules", "@flue", "runtime"), join(SDK_ROOT, "node_modules", "@flue", "runtime")],
125
+ ];
126
+ for (const [link, source] of links) {
127
+ await mkdir(dirname(link), { recursive: true });
128
+ await rm(link, { recursive: true, force: true });
129
+ await symlink(source, link, "dir");
130
+ }
131
+ }
132
+ async function materializeCheckoutToolchain(target) {
133
+ const pins = await checkoutToolchainPins(target);
134
+ const provenance = await sdkProvenance();
135
+ if (provenance.installSource === "installed") {
136
+ await installCheckoutToolchain(target);
137
+ return;
138
+ }
139
+ const [version, flueVersion] = await Promise.all([
140
+ sdkVersion(),
141
+ pairedFlueRuntimeVersion(),
142
+ ]);
143
+ if (pins.sdkVersion !== version || pins.flueVersion !== flueVersion) {
144
+ throw new Error(`local SDK cannot materialize checkout toolchain ${pins.sdkVersion} / ${pins.flueVersion}; expected ${version} / ${flueVersion}`);
145
+ }
146
+ await linkLocalCheckoutToolchain(target);
147
+ }
148
+ async function materializeCloudFiles(runtime, started, target) {
149
+ const files = await runtime.client.query(FN.workspacesCheckout, {
150
+ token: started.authoringToken,
151
+ sessionId: started.sessionId,
152
+ });
153
+ for (const file of files) {
154
+ const path = resolve(target, file.path);
155
+ const withinTarget = relative(resolve(target), path);
156
+ if (!withinTarget || withinTarget.startsWith("..") || isAbsolute(withinTarget)) {
157
+ throw new Error(`invalid workspace path from server: ${file.path}`);
158
+ }
159
+ const response = await fetch(file.url);
160
+ if (!response.ok)
161
+ throw new Error(`failed to download ${file.path} (HTTP ${response.status})`);
162
+ const bytes = new Uint8Array(await response.arrayBuffer());
163
+ const sha256 = createHash("sha256").update(bytes).digest("hex");
164
+ if (sha256 !== file.sha256 || bytes.byteLength !== file.size) {
165
+ throw new Error(`downloaded workspace blob failed verification for ${file.path}`);
166
+ }
167
+ await mkdir(dirname(path), { recursive: true });
168
+ await writeFile(path, bytes);
169
+ }
170
+ return files.length;
171
+ }
172
+ async function writeSession(state) {
173
+ const path = join(state.checkoutPath, SESSION_PATH);
174
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
175
+ await writeFile(path, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
176
+ return path;
177
+ }
178
+ export async function startDaemon(sessionPath) {
179
+ const checkoutPath = dirname(dirname(sessionPath));
180
+ const script = join(checkoutPath, "node_modules", "@reasonlayer", "sdk", "dist", "cli", "watcher-process.js");
181
+ try {
182
+ await access(script);
183
+ }
184
+ catch (error) {
185
+ if (error.code === "ENOENT") {
186
+ throw new Error(`checkout SDK is not installed: missing ${script}`);
187
+ }
188
+ throw error;
189
+ }
190
+ const logPath = join(dirname(sessionPath), "watcher.log");
191
+ const logFd = openSync(logPath, "a", 0o600);
192
+ try {
193
+ const child = spawn(process.execPath, [script, sessionPath], {
194
+ detached: true,
195
+ stdio: ["ignore", logFd, logFd],
196
+ env: process.env,
197
+ });
198
+ if (!child.pid)
199
+ throw new Error("watcher daemon did not start");
200
+ child.unref();
201
+ return child.pid;
202
+ }
203
+ finally {
204
+ closeSync(logFd);
205
+ }
206
+ }
207
+ export async function createCheckout(runtime, options) {
208
+ const checkoutPath = resolve(options.targetDir ?? join(process.cwd(), options.workflowId));
209
+ await assertEmptyTarget(checkoutPath);
210
+ await mkdir(checkoutPath, { recursive: true });
211
+ const checkoutId = randomUUID();
212
+ const started = await runtime.client.mutation(FN.workspacesStartSession, {
213
+ token: runtime.config.token,
214
+ workflowId: options.workflowId,
215
+ machineLabel: `${hostname()} (${process.platform})`,
216
+ checkoutId,
217
+ mode: options.mode,
218
+ });
219
+ try {
220
+ const files = options.mode === "init"
221
+ ? await scaffold(checkoutPath, options.workflowId)
222
+ : await materializeCloudFiles(runtime, started, checkoutPath);
223
+ await materializeCheckoutToolchain(checkoutPath);
224
+ const state = {
225
+ schemaVersion: LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION,
226
+ workflowId: options.workflowId,
227
+ sessionId: started.sessionId,
228
+ checkoutId,
229
+ checkoutPath,
230
+ convexUrl: runtime.config.convexUrl,
231
+ authoringToken: started.authoringToken,
232
+ credentialExpiresAt: started.expiresAt,
233
+ };
234
+ const sessionPath = await writeSession(state);
235
+ const daemonPid = await startDaemon(sessionPath);
236
+ await writeSession({ ...state, daemonPid });
237
+ return { checkoutPath, sessionId: started.sessionId, daemonPid, files };
238
+ }
239
+ catch (error) {
240
+ await runtime.client.mutation(FN.workspacesEndSession, {
241
+ token: started.authoringToken,
242
+ sessionId: started.sessionId,
243
+ }).catch(() => undefined);
244
+ await rm(checkoutPath, { recursive: true, force: true });
245
+ throw error;
246
+ }
247
+ }
248
+ export async function findCheckoutRoot(cwd = process.cwd()) {
249
+ let current = resolve(cwd);
250
+ for (;;) {
251
+ if (await pathExists(join(current, SESSION_PATH)))
252
+ return current;
253
+ const parent = dirname(current);
254
+ if (parent === current)
255
+ throw new Error("not inside a Reasonlayer checkout");
256
+ current = parent;
257
+ }
258
+ }
259
+ export async function readLocalSession(cwd = process.cwd()) {
260
+ const root = await findCheckoutRoot(cwd);
261
+ const sessionPath = join(root, SESSION_PATH);
262
+ const source = relative(cwd, sessionPath) || sessionPath;
263
+ const state = parseLocalWorkspaceSession(await readFile(sessionPath, "utf8"), source);
264
+ return { ...state, checkoutPath: root };
265
+ }
266
+ export async function endLocalSession(runtime) {
267
+ const state = await readLocalSession();
268
+ if (!state.daemonPid) {
269
+ throw new Error("cannot finish safely: checkout watcher pid is missing; checkout was preserved");
270
+ }
271
+ try {
272
+ process.kill(state.daemonPid, "SIGTERM");
273
+ }
274
+ catch (error) {
275
+ if (error.code === "ESRCH") {
276
+ throw new Error("cannot finish safely: checkout watcher is not running; checkout was preserved");
277
+ }
278
+ throw error;
279
+ }
280
+ const deadline = Date.now() + 60_000;
281
+ while (Date.now() < deadline) {
282
+ try {
283
+ process.kill(state.daemonPid, 0);
284
+ }
285
+ catch (error) {
286
+ if (error.code === "ESRCH")
287
+ break;
288
+ throw error;
289
+ }
290
+ await new Promise((resolveWait) => setTimeout(resolveWait, 25));
291
+ }
292
+ try {
293
+ process.kill(state.daemonPid, 0);
294
+ throw new Error("cannot finish safely: watcher did not drain checkout changes; checkout was preserved");
295
+ }
296
+ catch (error) {
297
+ if (error.code !== "ESRCH")
298
+ throw error;
299
+ }
300
+ await runtime.client.mutation(FN.workspacesEndSession, {
301
+ token: runtime.config.token,
302
+ sessionId: state.sessionId,
303
+ });
304
+ await rm(state.checkoutPath, { recursive: true, force: true });
305
+ return {
306
+ workflowId: state.workflowId,
307
+ sessionId: state.sessionId,
308
+ checkoutPath: state.checkoutPath,
309
+ };
310
+ }
@@ -0,0 +1,13 @@
1
+ export declare const LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION = 1;
2
+ export interface LocalWorkspaceSession {
3
+ schemaVersion: typeof LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION;
4
+ workflowId: string;
5
+ sessionId: string;
6
+ checkoutId: string;
7
+ checkoutPath: string;
8
+ convexUrl: string;
9
+ authoringToken: string;
10
+ credentialExpiresAt: number;
11
+ daemonPid?: number;
12
+ }
13
+ export declare function parseLocalWorkspaceSession(text: string, source: string): LocalWorkspaceSession;
@@ -0,0 +1,12 @@
1
+ export const LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION = 1;
2
+ export function parseLocalWorkspaceSession(text, source) {
3
+ const value = JSON.parse(text);
4
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
5
+ throw new Error(`invalid Reasonlayer checkout session in ${source}`);
6
+ }
7
+ const schemaVersion = value.schemaVersion;
8
+ if (schemaVersion !== LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION) {
9
+ throw new Error(`unsupported Reasonlayer checkout session schemaVersion ${String(schemaVersion)} in ${source}; expected ${LOCAL_WORKSPACE_SESSION_SCHEMA_VERSION}`);
10
+ }
11
+ return value;
12
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * compileWorkflow (spec §4.4): lower a WorkflowDefinition to WorkflowIR.
3
+ * Every selector is validated against the step graph; wrong wiring is a
4
+ * compile error, not a runtime surprise. Returns { ok, ir, codeRefs,
5
+ * agentRefs } or { ok: false, errors } — it never throws for authoring
6
+ * mistakes.
7
+ */
8
+ import type { CompileResult, WorkflowDefinition } from "./types.js";
9
+ export interface CompileWorkflowOptions {
10
+ /**
11
+ * Base directory used to resolve environment file sources. Build paths pass
12
+ * the workflow entry directory so customer file content is captured before
13
+ * deploy; runner cold-start compiles can omit this because they only need
14
+ * executable references.
15
+ */
16
+ environmentFileBaseDir?: string;
17
+ inlineEnvironmentFiles?: boolean;
18
+ /**
19
+ * Emitted runtime bundles already consumed the build-time IR and only need
20
+ * executable refs at cold start. They can omit environment lowering to avoid
21
+ * repeating build-time hashing and file reads at runner startup.
22
+ */
23
+ omitEnvironments?: boolean;
24
+ }
25
+ /** IR step ids must be safe as JS export names in the emitted bundles. */
26
+ export declare function exportNameFor(stepId: string): string;
27
+ export declare function compileWorkflow(definition: WorkflowDefinition, options?: CompileWorkflowOptions): CompileResult;