@boardwalk-labs/runner 0.1.2 → 0.1.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.
@@ -1,5 +1,15 @@
1
1
  import type { WorkflowHost } from "@boardwalk-labs/workflow/runtime";
2
2
  import { type SuspendSignal } from "./suspension.js";
3
+ /**
4
+ * Link THIS runtime's `@boardwalk-labs/workflow` into the exec dir's `node_modules` so the
5
+ * program's bare import resolves anywhere (a self-hosted daemon workspace has no ancestor
6
+ * `node_modules`; hosted images do, making the link a harmless no-op there). A symlink — not a
7
+ * copy — is load-bearing: Node resolves it to the REAL path, so the program gets the same
8
+ * module instance the host adapter was installed on (the singleton contract). `junction` covers
9
+ * Windows without elevation; a failed link is only logged, since a resolvable ancestor may
10
+ * still exist.
11
+ */
12
+ export declare function ensureSdkLink(execDir: string): Promise<void>;
3
13
  export interface RunProgramArgs {
4
14
  /** Run id — used for the temp dir path + correlation. */
5
15
  runId: string;
@@ -20,8 +20,9 @@
20
20
  // Durability: the body runs once, in-process. `sleep`/`workflows.call` hold in-process via the host
21
21
  // (no checkpoint, no exit). A crash mid-run restarts the run from the top (handled by the
22
22
  // worker/scheduler-sweep, not here). Output capture is deferred (v0 returns null).
23
- import { mkdir, writeFile, rm } from "node:fs/promises";
24
- import { join } from "node:path";
23
+ import { mkdir, writeFile, rm, symlink } from "node:fs/promises";
24
+ import { dirname, join } from "node:path";
25
+ import { createRequire } from "node:module";
25
26
  import { pathToFileURL } from "node:url";
26
27
  import { randomUUID } from "node:crypto";
27
28
  import { installHost, installInput, installConfig, takeDeclaredOutput, resetRuntime, } from "@boardwalk-labs/workflow/runtime";
@@ -30,6 +31,33 @@ import { SuspendError } from "./suspension.js";
30
31
  const log = createLogger("ProgramRunner");
31
32
  /** Subdirectory (under the work root) that holds transient extracted program trees. */
32
33
  const RUN_DIR = ".bw-runs";
34
+ /**
35
+ * Link THIS runtime's `@boardwalk-labs/workflow` into the exec dir's `node_modules` so the
36
+ * program's bare import resolves anywhere (a self-hosted daemon workspace has no ancestor
37
+ * `node_modules`; hosted images do, making the link a harmless no-op there). A symlink — not a
38
+ * copy — is load-bearing: Node resolves it to the REAL path, so the program gets the same
39
+ * module instance the host adapter was installed on (the singleton contract). `junction` covers
40
+ * Windows without elevation; a failed link is only logged, since a resolvable ancestor may
41
+ * still exist.
42
+ */
43
+ export async function ensureSdkLink(execDir) {
44
+ try {
45
+ // Resolve via the MAIN entry (the SDK's export map exposes no "./package.json") and cut the
46
+ // path back to the package root.
47
+ const entry = createRequire(import.meta.url).resolve("@boardwalk-labs/workflow");
48
+ const marker = join("node_modules", "@boardwalk-labs", "workflow");
49
+ const idx = entry.lastIndexOf(marker);
50
+ const sdkDir = idx === -1 ? dirname(dirname(entry)) : entry.slice(0, idx + marker.length);
51
+ const scopeDir = join(execDir, "node_modules", "@boardwalk-labs");
52
+ await mkdir(scopeDir, { recursive: true });
53
+ await symlink(sdkDir, join(scopeDir, "workflow"), process.platform === "win32" ? "junction" : "dir");
54
+ }
55
+ catch (err) {
56
+ if (err.code !== "EEXIST") {
57
+ log.warn("sdk_link_failed", { error: err instanceof Error ? err.message : String(err) });
58
+ }
59
+ }
60
+ }
33
61
  /** Scratch filename for the in-flight artifact tarball inside a run's dir. */
34
62
  const ARTIFACT_FILE = "__program.tgz";
35
63
  /**
@@ -54,6 +82,9 @@ export async function runWorkflowProgram(args, deps) {
54
82
  await rm(tgzPath, { force: true });
55
83
  // The bundled tree is now on disk — let the worker point the agent() leaf at `<dir>/skills/*.md`.
56
84
  deps.onExtracted?.(dir);
85
+ // Make the bare `@boardwalk-labs/workflow` import resolve from ANY work root — hosted images
86
+ // provide it via an ancestor node_modules, but a self-hosted daemon's workspace has none.
87
+ await ensureSdkLink(dir);
57
88
  const entryPath = join(dir, ...args.entry.split("/"));
58
89
  // Run the program body. A SUSPEND is surfaced two ways and both land here as a `suspended`
59
90
  // result: (a) out of band via `suspendSignal` — racing the body, immune to a program's own
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boardwalk-labs/runner",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Boardwalk self-hosted runner: execute cloud-scheduled runs on your own machines. Currently: the canonical runner contract types.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {