@boardwalk-labs/cli 0.1.0

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 (73) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +110 -0
  3. package/bin/boardwalk.js +4 -0
  4. package/dist/artifact.d.ts +53 -0
  5. package/dist/artifact.js +267 -0
  6. package/dist/artifact.js.map +1 -0
  7. package/dist/auth/discovery.d.ts +8 -0
  8. package/dist/auth/discovery.js +43 -0
  9. package/dist/auth/discovery.js.map +1 -0
  10. package/dist/auth/login.d.ts +13 -0
  11. package/dist/auth/login.js +74 -0
  12. package/dist/auth/login.js.map +1 -0
  13. package/dist/auth/pkce.d.ts +63 -0
  14. package/dist/auth/pkce.js +197 -0
  15. package/dist/auth/pkce.js.map +1 -0
  16. package/dist/auth/resolve.d.ts +12 -0
  17. package/dist/auth/resolve.js +51 -0
  18. package/dist/auth/resolve.js.map +1 -0
  19. package/dist/bundle.d.ts +35 -0
  20. package/dist/bundle.js +176 -0
  21. package/dist/bundle.js.map +1 -0
  22. package/dist/client.d.ts +65 -0
  23. package/dist/client.js +193 -0
  24. package/dist/client.js.map +1 -0
  25. package/dist/commands/cancel.d.ts +14 -0
  26. package/dist/commands/cancel.js +53 -0
  27. package/dist/commands/cancel.js.map +1 -0
  28. package/dist/commands/check.d.ts +7 -0
  29. package/dist/commands/check.js +34 -0
  30. package/dist/commands/check.js.map +1 -0
  31. package/dist/commands/deploy.d.ts +15 -0
  32. package/dist/commands/deploy.js +56 -0
  33. package/dist/commands/deploy.js.map +1 -0
  34. package/dist/commands/dev.d.ts +14 -0
  35. package/dist/commands/dev.js +128 -0
  36. package/dist/commands/dev.js.map +1 -0
  37. package/dist/commands/init.d.ts +12 -0
  38. package/dist/commands/init.js +171 -0
  39. package/dist/commands/init.js.map +1 -0
  40. package/dist/commands/run.d.ts +32 -0
  41. package/dist/commands/run.js +105 -0
  42. package/dist/commands/run.js.map +1 -0
  43. package/dist/commands/session.d.ts +13 -0
  44. package/dist/commands/session.js +58 -0
  45. package/dist/commands/session.js.map +1 -0
  46. package/dist/config.d.ts +14 -0
  47. package/dist/config.js +64 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/credentials.d.ts +23 -0
  50. package/dist/credentials.js +69 -0
  51. package/dist/credentials.js.map +1 -0
  52. package/dist/deployment.d.ts +44 -0
  53. package/dist/deployment.js +100 -0
  54. package/dist/deployment.js.map +1 -0
  55. package/dist/dev/host.d.ts +16 -0
  56. package/dist/dev/host.js +76 -0
  57. package/dist/dev/host.js.map +1 -0
  58. package/dist/errors.d.ts +8 -0
  59. package/dist/errors.js +18 -0
  60. package/dist/errors.js.map +1 -0
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.js +147 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/manifest.d.ts +12 -0
  65. package/dist/manifest.js +64 -0
  66. package/dist/manifest.js.map +1 -0
  67. package/dist/project.d.ts +15 -0
  68. package/dist/project.js +79 -0
  69. package/dist/project.js.map +1 -0
  70. package/dist/render/renderer.d.ts +18 -0
  71. package/dist/render/renderer.js +108 -0
  72. package/dist/render/renderer.js.map +1 -0
  73. package/package.json +56 -0
@@ -0,0 +1,100 @@
1
+ // Shared deploy logic for `boardwalk deploy` and `boardwalk run`.
2
+ //
3
+ // Deploy is artifact-based: build the program into a content-addressed tarball, upload it straight
4
+ // to object storage via a presigned PUT (the CLI holds no storage credentials), then finalize the
5
+ // version with a reference the server re-reads + verifies. Identity is the project LINK
6
+ // (.boardwalk/project.json), not the program name. Both commands:
7
+ // 1. build the program artifact (name from its `meta`),
8
+ // 2. resolve the org (--org, else the link's orgSlug),
9
+ // 3. upload the artifact, then update the linked workflow BY ID (rename-safe) — or, when unlinked,
10
+ // adopt an existing same-name workflow / create a new one — then (re)write the link.
11
+ import { CliError } from "./errors.js";
12
+ import { extractWorkflowName } from "./manifest.js";
13
+ import { buildArtifact } from "./artifact.js";
14
+ import { projectDirFor, readLink, writeLink } from "./project.js";
15
+ /**
16
+ * Resolve a target path to its deployable artifact: build the program (bundle + assets → tarball,
17
+ * content-addressed) and extract `meta.name` from the bundled entry for the deploy identity.
18
+ */
19
+ export async function loadProgram(file) {
20
+ const artifact = await buildArtifact(file);
21
+ const name = extractWorkflowName(artifact.entrySource, artifact.entry);
22
+ return { name, entry: artifact.entry, artifact };
23
+ }
24
+ /** Decide create vs update by matching the program name against the org's existing workflows. */
25
+ export function planDeploy(existing, name) {
26
+ const match = existing.find((w) => w.name === name);
27
+ return match !== undefined
28
+ ? { action: "update", name, workflowId: match.id }
29
+ : { action: "create", name };
30
+ }
31
+ /** The artifact reference recorded on the version (everything but the bytes, which go to storage). */
32
+ function refOf(artifact) {
33
+ return {
34
+ digest: artifact.digest,
35
+ size: artifact.size,
36
+ entry: artifact.entry,
37
+ sdkVersion: artifact.sdkVersion,
38
+ lockfileDigest: artifact.lockfileDigest,
39
+ };
40
+ }
41
+ /**
42
+ * Deploy the program, honoring the project link: upload the artifact, then update the linked workflow
43
+ * by id (rename-safe); when unlinked, adopt an existing same-name workflow or create a new one. Always
44
+ * (re)writes the link so the next deploy/run is pinned.
45
+ */
46
+ export async function deployWithLink(client, ctx) {
47
+ const projectDir = projectDirFor(ctx.target);
48
+ const link = readLink(projectDir);
49
+ const orgSlug = ctx.orgSlug ?? link?.orgSlug;
50
+ if (orgSlug === undefined || orgSlug.length === 0) {
51
+ throw new CliError("No org to deploy into.", "Pass --org <slug> (it'll be linked in .boardwalk/project.json for next time).");
52
+ }
53
+ let workflowId = link?.workflowId ?? null;
54
+ let outcome = "updated";
55
+ // Unlinked: adopt an existing workflow with the same name, if any (so a second machine re-links
56
+ // instead of creating a duplicate). Otherwise we'll create below.
57
+ if (workflowId === null) {
58
+ const match = (await client.listWorkflows(orgSlug)).find((w) => w.name === ctx.prog.name);
59
+ if (match !== undefined) {
60
+ workflowId = match.id;
61
+ outcome = "adopted";
62
+ }
63
+ }
64
+ // Upload the artifact bytes once (presigned PUT); both create + update reference it by digest.
65
+ const { artifact } = ctx.prog;
66
+ const { uploadUrl, contentType } = await client.getArtifactUploadUrl(orgSlug, {
67
+ digest: artifact.digest,
68
+ size: artifact.size,
69
+ });
70
+ await client.uploadArtifact(uploadUrl, contentType, artifact.tarball);
71
+ const ref = refOf(artifact);
72
+ let versionNumber;
73
+ if (workflowId !== null) {
74
+ try {
75
+ const result = await client.updateWorkflow(workflowId, ref);
76
+ versionNumber = result.version.number;
77
+ }
78
+ catch (err) {
79
+ if (err instanceof CliError && err.status === 404) {
80
+ // The linked workflow was deleted out from under us — recreate + relink.
81
+ const result = await client.createWorkflow(orgSlug, ref);
82
+ workflowId = result.workflow.id;
83
+ versionNumber = result.version.number;
84
+ outcome = "created";
85
+ }
86
+ else {
87
+ throw err;
88
+ }
89
+ }
90
+ }
91
+ else {
92
+ const result = await client.createWorkflow(orgSlug, ref);
93
+ workflowId = result.workflow.id;
94
+ versionNumber = result.version.number;
95
+ outcome = "created";
96
+ }
97
+ const { gitignoreUpdated } = writeLink(projectDir, { orgSlug, workflowId });
98
+ return { workflowId, orgSlug, versionNumber, outcome, gitignoreUpdated };
99
+ }
100
+ //# sourceMappingURL=deployment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../src/deployment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { WorkflowManifest } from "@boardwalk-labs/workflow";
2
+ import type { WorkflowHost } from "@boardwalk-labs/workflow/runtime";
3
+ import type { RunEvent } from "@boardwalk-labs/workflow";
4
+ /** A run event minus its envelope — the emitter (dev.ts) stamps runId/turnId/seq/t. */
5
+ export type RunEventBody = RunEvent extends infer E ? E extends RunEvent ? Omit<E, "runId" | "turnId" | "seq" | "t"> : never : never;
6
+ export interface DevHostContext {
7
+ manifest: WorkflowManifest;
8
+ /** Parsed env file contents — the local secret store. */
9
+ envVars: ReadonlyMap<string, string>;
10
+ /** The env file path, for actionable error messages (never its contents). */
11
+ envLabel: string;
12
+ /** Directory artifacts are written under (created lazily). */
13
+ artifactsDir: string;
14
+ emit: (body: RunEventBody) => void;
15
+ }
16
+ export declare function createDevHost(ctx: DevHostContext): WorkflowHost;
@@ -0,0 +1,76 @@
1
+ // The `boardwalk dev` WorkflowHost — the minimal embedded host backing a one-off local run.
2
+ //
3
+ // Scope (v0.1, until `@boardwalk-labs/engine` ships and `dev` delegates to its embedded mode):
4
+ // - secrets resolve from the project's env file (fail-closed against `meta.secrets`)
5
+ // - sleep really sleeps (hold-and-pay semantics: locals survive, the process waits)
6
+ // - Phase() markers and output() flow into the run-event stream
7
+ // - artifacts write under `.bw-runs/<runId>/artifacts/`
8
+ // - agent() and workflows.call() fail with a clear pointer at what supports them
9
+ //
10
+ // Secret VALUES never reach the event stream or any error message.
11
+ import { mkdirSync, writeFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { pathToFileURL } from "node:url";
14
+ import { CliError } from "../errors.js";
15
+ export function createDevHost(ctx) {
16
+ let phaseCount = 0;
17
+ let artifactCount = 0;
18
+ return {
19
+ setPhase(name, opts) {
20
+ phaseCount += 1;
21
+ ctx.emit({ kind: "phase", name, id: opts?.id ?? `phase-${String(phaseCount)}` });
22
+ },
23
+ agent() {
24
+ throw new CliError("agent() isn't available in `boardwalk dev` yet.", "The embedded local engine ships with @boardwalk-labs/engine — until then, deploy and use " +
25
+ "`boardwalk run` to execute agent loops.");
26
+ },
27
+ callWorkflow() {
28
+ throw new CliError("workflows.call() isn't available in `boardwalk dev` — durable child runs need an engine.", "Deploy and use `boardwalk run`, or run the callee directly with `boardwalk dev <its file>`.");
29
+ },
30
+ async sleep(arg) {
31
+ const ms = sleepMs(arg);
32
+ if (ms <= 0)
33
+ return;
34
+ await new Promise((resolve) => {
35
+ setTimeout(resolve, ms);
36
+ });
37
+ },
38
+ getSecret(name) {
39
+ const declared = (ctx.manifest.secrets ?? []).some((s) => s.name === name);
40
+ if (!declared) {
41
+ throw new CliError(`Secret "${name}" is not declared in meta.secrets.`, `Add { name: "${name}" } to meta.secrets — secret access is fail-closed everywhere.`);
42
+ }
43
+ const value = ctx.envVars.get(name) ?? process.env[name];
44
+ if (value === undefined || value.length === 0) {
45
+ throw new CliError(`Secret "${name}" has no value for this local run.`, `Set ${name}=… in ${ctx.envLabel} (boardwalk dev resolves secrets from your env file).`);
46
+ }
47
+ return Promise.resolve(value);
48
+ },
49
+ writeArtifact(name, _contentType, body, _metadata) {
50
+ if (name.includes("/") || name.includes("\\") || name.includes("..")) {
51
+ throw new CliError(`Artifact name "${name}" must be a plain file name.`);
52
+ }
53
+ mkdirSync(ctx.artifactsDir, { recursive: true });
54
+ const path = join(ctx.artifactsDir, name);
55
+ writeFileSync(path, body);
56
+ artifactCount += 1;
57
+ return Promise.resolve({
58
+ id: `art-${String(artifactCount)}`,
59
+ name,
60
+ url: pathToFileURL(path).href,
61
+ });
62
+ },
63
+ };
64
+ }
65
+ function sleepMs(arg) {
66
+ if (typeof arg === "number")
67
+ return arg;
68
+ if ("durationMs" in arg)
69
+ return arg.durationMs;
70
+ const until = arg.until instanceof Date ? arg.until.getTime() : Date.parse(arg.until);
71
+ if (Number.isNaN(until)) {
72
+ throw new CliError(`sleep({ until }) got an unparseable date: ${String(arg.until)}`);
73
+ }
74
+ return until - Date.now();
75
+ }
76
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/dev/host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare class CliError extends Error {
2
+ readonly hint: string | undefined;
3
+ /** HTTP status, when this error came from an API response (lets callers branch on e.g. 404). */
4
+ readonly status: number | undefined;
5
+ /** Process exit code override (default 1) — e.g. 130 for a cancelled `dev` run. */
6
+ readonly exitCode: number | undefined;
7
+ constructor(message: string, hint?: string, status?: number, exitCode?: number);
8
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,18 @@
1
+ // CliError — a user-facing error. The top-level handler prints `message` (and `hint`, if any) to
2
+ // stderr and exits non-zero, WITHOUT a stack trace. Throw this for expected failures (not
3
+ // authenticated, file missing, API rejected the request); let unexpected errors surface their stack.
4
+ export class CliError extends Error {
5
+ hint;
6
+ /** HTTP status, when this error came from an API response (lets callers branch on e.g. 404). */
7
+ status;
8
+ /** Process exit code override (default 1) — e.g. 130 for a cancelled `dev` run. */
9
+ exitCode;
10
+ constructor(message, hint, status, exitCode) {
11
+ super(message);
12
+ this.name = "CliError";
13
+ this.hint = hint;
14
+ this.status = status;
15
+ this.exitCode = exitCode;
16
+ }
17
+ }
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,0FAA0F;AAC1F,qGAAqG;AAErG,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAqB;IAClC,gGAAgG;IACvF,MAAM,CAAqB;IACpC,mFAAmF;IAC1E,QAAQ,CAAqB;IAEtC,YAAY,OAAe,EAAE,IAAa,EAAE,MAAe,EAAE,QAAiB;QAC5E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,147 @@
1
+ // boardwalk CLI entrypoint — argument parsing + command dispatch (commander).
2
+ //
3
+ // Commands:
4
+ // boardwalk init [dir] Scaffold a new workflow project from a template.
5
+ // boardwalk dev <file> Run the workflow now, locally — no account needed.
6
+ // boardwalk check <file> Validate a workflow locally (no auth/network).
7
+ // boardwalk login Authenticate via browser (OAuth PKCE).
8
+ // boardwalk logout Remove local credentials.
9
+ // boardwalk whoami Show the current session.
10
+ // boardwalk deploy <file> --org <s> Create/update a workflow from a program file.
11
+ // boardwalk run <file> --org <s> Deploy + trigger a real run, wait for the result.
12
+ // boardwalk cancel <runId> Cancel a queued or in-flight run.
13
+ //
14
+ // Auth precedence for deploy/run/cancel: --token > BOARDWALK_API_KEY env > stored `login` session.
15
+ //
16
+ // Every command body is lazy-imported inside its action — `boardwalk --help` must stay fast, so
17
+ // nothing heavy (esbuild, the SDK extractor, the API client) loads until its command actually runs.
18
+ import { Command } from "commander";
19
+ import { CliError } from "./errors.js";
20
+ import { loadConfig } from "./config.js";
21
+ const VERSION = "0.1.0";
22
+ function buildProgram() {
23
+ const program = new Command();
24
+ program
25
+ .name("boardwalk")
26
+ .description("Boardwalk CLI — author and ship agent workflows.")
27
+ .version(VERSION);
28
+ program
29
+ .command("init")
30
+ .argument("[dir]", "directory to scaffold into (created if missing)", ".")
31
+ .option("--template <name>", "template: the built-in `hello`, or any name from the examples registry", "hello")
32
+ .description("Scaffold a new workflow project from a template.")
33
+ .action(async (dir, options) => {
34
+ const { runInit } = await import("./commands/init.js");
35
+ await runInit({ dir, template: options.template ?? "hello" });
36
+ });
37
+ program
38
+ .command("dev")
39
+ .argument("<file>", "workflow program file, or a package directory")
40
+ .option("--input <json>", "trigger payload exposed to the program as `input`")
41
+ // Named --env (not --env-file): Node ≥26 claims --env-file even after the script path,
42
+ // so that spelling would be processed (or rejected) by node itself before we ever parse it.
43
+ .option("--env <path>", "env file resolving secrets for the run (default: .env)")
44
+ .option("--verbose", "stream every event channel (agent turns, tool calls, logs)", false)
45
+ .option("--stream <channels>", "comma-separated channels: lifecycle,phase,output,log,agent")
46
+ .description("Run the workflow now, locally — no account needed.")
47
+ .action(async (file, options) => {
48
+ const { runDev } = await import("./commands/dev.js");
49
+ await runDev({
50
+ file,
51
+ input: options.input,
52
+ envFile: options.env,
53
+ verbose: options.verbose ?? false,
54
+ stream: options.stream,
55
+ });
56
+ });
57
+ program
58
+ .command("check")
59
+ .argument("<file>", "workflow program file, or a package directory")
60
+ .description("Validate a workflow locally (no auth, no network).")
61
+ .action(async (file) => {
62
+ const { runCheck } = await import("./commands/check.js");
63
+ await runCheck({ file });
64
+ });
65
+ program
66
+ .command("login")
67
+ .description("Authenticate via browser (OAuth PKCE), or store an API key with --token.")
68
+ .option("--token <key>", "store this API key (bwk_…) instead of the browser flow")
69
+ .action(async (options) => {
70
+ const { runLogin } = await import("./commands/session.js");
71
+ await runLogin({ config: loadConfig() }, { token: options.token });
72
+ });
73
+ program
74
+ .command("logout")
75
+ .description("Remove local credentials.")
76
+ .action(async () => {
77
+ const { runLogout } = await import("./commands/session.js");
78
+ runLogout({ config: loadConfig() });
79
+ });
80
+ program
81
+ .command("whoami")
82
+ .description("Show the current session.")
83
+ .action(async () => {
84
+ const { runWhoami } = await import("./commands/session.js");
85
+ runWhoami({ config: loadConfig() });
86
+ });
87
+ program
88
+ .command("deploy")
89
+ .argument("<file>", "workflow program file, or a package directory")
90
+ .option("--org <slug>", "the org to deploy into (optional once the project is linked)")
91
+ .option("--dry-run", "print the plan (create vs update) without writing", false)
92
+ .option("--token <token>", "use this Bearer token instead of stored/env credentials")
93
+ .description("Create or update a workflow from a program file.")
94
+ .action(async (file, options) => {
95
+ const { runDeploy } = await import("./commands/deploy.js");
96
+ await runDeploy({
97
+ file,
98
+ org: options.org,
99
+ check: options.dryRun ?? false,
100
+ token: options.token,
101
+ }, { config: loadConfig() });
102
+ });
103
+ program
104
+ .command("run")
105
+ .argument("<file>", "workflow program file, or a package directory")
106
+ .option("--org <slug>", "the org to run in (optional once the project is linked)")
107
+ .option("--input <json>", "trigger payload exposed to the program as `input`")
108
+ .option("--no-wait", "trigger and exit without waiting for the run to finish")
109
+ .option("--token <token>", "use this Bearer token instead of stored/env credentials")
110
+ .description("Deploy the program, trigger a real run, and wait for the result.")
111
+ .action(async (file, options) => {
112
+ const { runRun } = await import("./commands/run.js");
113
+ await runRun({
114
+ file,
115
+ org: options.org,
116
+ input: options.input,
117
+ noWait: options.wait === false,
118
+ token: options.token,
119
+ }, { config: loadConfig() });
120
+ });
121
+ program
122
+ .command("cancel")
123
+ .argument("<runId>", "the run to cancel")
124
+ .option("--token <token>", "use this Bearer token instead of stored/env credentials")
125
+ .description("Cancel a queued or in-flight run.")
126
+ .action(async (runId, options) => {
127
+ const { runCancel } = await import("./commands/cancel.js");
128
+ await runCancel({ runId, token: options.token }, { config: loadConfig() });
129
+ });
130
+ return program;
131
+ }
132
+ async function main() {
133
+ await buildProgram().parseAsync(process.argv);
134
+ }
135
+ main().catch((err) => {
136
+ if (err instanceof CliError) {
137
+ console.error(`error: ${err.message}`);
138
+ if (err.hint !== undefined)
139
+ console.error(` ${err.hint}`);
140
+ process.exitCode = err.exitCode ?? 1;
141
+ }
142
+ else {
143
+ console.error(err);
144
+ process.exitCode = 1;
145
+ }
146
+ });
147
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type WorkflowManifest } from "@boardwalk-labs/workflow";
2
+ /**
3
+ * Statically read `export const meta = { name: "...", ... }` and return the name string.
4
+ * Throws `CliError` when there is no pure-literal `meta` or `name` is missing/empty/not a string.
5
+ */
6
+ export declare function extractWorkflowName(source: string, fileName?: string): string;
7
+ /**
8
+ * Statically extract `meta` and validate it against the manifest schema (the same one every
9
+ * engine consumes), returning the fully-defaulted manifest. Throws `CliError` with the precise
10
+ * extraction or per-field validation failure.
11
+ */
12
+ export declare function extractValidatedManifest(source: string, fileName?: string): WorkflowManifest;
@@ -0,0 +1,64 @@
1
+ // Manifest extraction for CLI commands — thin wrappers over `@boardwalk-labs/workflow/extract`,
2
+ // translating extraction/validation failures into actionable `CliError`s.
3
+ //
4
+ // Two tiers, on purpose:
5
+ // - `extractWorkflowName` — name only. `deploy`/`run` need the deploy identity (create-vs-update
6
+ // is matched by NAME, which is environment-independent, unlike a path or stored id); the server
7
+ // re-derives and validates the FULL manifest from the uploaded source, so the CLI doesn't
8
+ // pre-judge field-level validity on the way to a deploy.
9
+ // - `extractValidatedManifest` — the full schema. `check` and `dev` run entirely locally, so they
10
+ // ARE the validator: same extractor, same schema, same errors as every engine.
11
+ //
12
+ // Pure logic; the program is never executed.
13
+ import { extractManifest, extractMetaLiteral, MetaExtractionError, } from "@boardwalk-labs/workflow/extract";
14
+ import { MetaValidationError } from "@boardwalk-labs/workflow";
15
+ import { CliError } from "./errors.js";
16
+ /**
17
+ * Statically read `export const meta = { name: "...", ... }` and return the name string.
18
+ * Throws `CliError` when there is no pure-literal `meta` or `name` is missing/empty/not a string.
19
+ */
20
+ export function extractWorkflowName(source, fileName = "index.ts") {
21
+ const meta = extractLiteralOrThrow(source, fileName);
22
+ const name = meta.name;
23
+ if (name !== undefined && typeof name !== "string") {
24
+ throw new CliError("`meta.name` must be a plain string literal.");
25
+ }
26
+ const trimmed = typeof name === "string" ? name.trim() : "";
27
+ if (trimmed.length === 0) {
28
+ throw new CliError("`meta.name` is missing or empty.", "Give the workflow a stable name — it's the deploy identity used to match create vs update.");
29
+ }
30
+ return trimmed;
31
+ }
32
+ /**
33
+ * Statically extract `meta` and validate it against the manifest schema (the same one every
34
+ * engine consumes), returning the fully-defaulted manifest. Throws `CliError` with the precise
35
+ * extraction or per-field validation failure.
36
+ */
37
+ export function extractValidatedManifest(source, fileName = "index.ts") {
38
+ try {
39
+ return extractManifest(source, { fileName });
40
+ }
41
+ catch (err) {
42
+ if (err instanceof MetaExtractionError) {
43
+ throw new CliError(err.message, META_HINT);
44
+ }
45
+ if (err instanceof MetaValidationError) {
46
+ throw new CliError(err.message);
47
+ }
48
+ throw err;
49
+ }
50
+ }
51
+ const META_HINT = "A workflow program must export a pure-literal `export const meta = { … }` " +
52
+ "(no spreads, calls, or interpolation — engines read it without executing your code).";
53
+ function extractLiteralOrThrow(source, fileName) {
54
+ try {
55
+ return extractMetaLiteral(source, { fileName });
56
+ }
57
+ catch (err) {
58
+ if (err instanceof MetaExtractionError) {
59
+ throw new CliError(err.message, META_HINT);
60
+ }
61
+ throw err;
62
+ }
63
+ }
64
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,0EAA0E;AAC1E,EAAE;AACF,yBAAyB;AACzB,mGAAmG;AACnG,oGAAoG;AACpG,8FAA8F;AAC9F,6DAA6D;AAC7D,oGAAoG;AACpG,mFAAmF;AACnF,EAAE;AACF,6CAA6C;AAE7C,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAyB,MAAM,0BAA0B,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,QAAQ,GAAG,UAAU;IACvE,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnD,MAAM,IAAI,QAAQ,CAAC,6CAA6C,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,QAAQ,CAChB,kCAAkC,EAClC,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,QAAQ,GAAG,UAAU;IAC5E,IAAI,CAAC;QACH,OAAO,eAAe,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,SAAS,GACb,4EAA4E;IAC5E,sFAAsF,CAAC;AAEzF,SAAS,qBAAqB,CAAC,MAAc,EAAE,QAAgB;IAC7D,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface ProjectLink {
2
+ orgSlug: string;
3
+ workflowId: string;
4
+ }
5
+ /** The project directory for a target path: the directory itself, or a file's parent directory. */
6
+ export declare function projectDirFor(target: string): string;
7
+ /** Read the link for a project dir, or null when absent/malformed. */
8
+ export declare function readLink(projectDir: string): ProjectLink | null;
9
+ /**
10
+ * Persist the link and ensure `.boardwalk/` is gitignored. Returns true if it added a `.gitignore`
11
+ * entry (so the caller can mention it). Best-effort on the gitignore side — never fatal.
12
+ */
13
+ export declare function writeLink(projectDir: string, link: ProjectLink): {
14
+ gitignoreUpdated: boolean;
15
+ };
@@ -0,0 +1,79 @@
1
+ // Project link — the Vercel-style tie between a local workflow directory and its deployed workflow.
2
+ //
3
+ // `<projectDir>/.boardwalk/project.json` (gitignored) stores `{ orgSlug, workflowId }`. Once linked,
4
+ // `deploy`/`run` update the workflow BY ID — so renaming `meta.name` (or the entry file) just
5
+ // updates the same workflow instead of forking a new one, and `--org` is no longer needed. The id is
6
+ // environment-specific (a dev workflow id ≠ prod), so the link is gitignored, not committed.
7
+ //
8
+ // One workflow per project directory (the "a workflow is a package with an index entry" model). The
9
+ // project dir is the directory target itself, or the directory containing a single-file entry.
10
+ import { mkdirSync, readFileSync, writeFileSync, statSync } from "node:fs";
11
+ import { dirname, join, resolve } from "node:path";
12
+ const LINK_DIR = ".boardwalk";
13
+ const LINK_FILE = "project.json";
14
+ /** The project directory for a target path: the directory itself, or a file's parent directory. */
15
+ export function projectDirFor(target) {
16
+ const abs = resolve(target);
17
+ try {
18
+ if (statSync(abs).isDirectory())
19
+ return abs;
20
+ }
21
+ catch {
22
+ // Missing path — treat the target as a file and use its parent.
23
+ }
24
+ return dirname(abs);
25
+ }
26
+ /** Read the link for a project dir, or null when absent/malformed. */
27
+ export function readLink(projectDir) {
28
+ let raw;
29
+ try {
30
+ raw = readFileSync(join(projectDir, LINK_DIR, LINK_FILE), "utf8");
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ try {
36
+ const parsed = JSON.parse(raw);
37
+ if (typeof parsed === "object" && parsed !== null) {
38
+ const p = parsed;
39
+ if (typeof p.orgSlug === "string" && typeof p.workflowId === "string") {
40
+ return { orgSlug: p.orgSlug, workflowId: p.workflowId };
41
+ }
42
+ }
43
+ }
44
+ catch {
45
+ // Malformed → treat as unlinked.
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * Persist the link and ensure `.boardwalk/` is gitignored. Returns true if it added a `.gitignore`
51
+ * entry (so the caller can mention it). Best-effort on the gitignore side — never fatal.
52
+ */
53
+ export function writeLink(projectDir, link) {
54
+ const dir = join(projectDir, LINK_DIR);
55
+ mkdirSync(dir, { recursive: true });
56
+ writeFileSync(join(dir, LINK_FILE), `${JSON.stringify(link, null, 2)}\n`);
57
+ return { gitignoreUpdated: ensureGitignored(projectDir) };
58
+ }
59
+ function ensureGitignored(projectDir) {
60
+ const gitignore = join(projectDir, ".gitignore");
61
+ let content = "";
62
+ try {
63
+ content = readFileSync(gitignore, "utf8");
64
+ }
65
+ catch {
66
+ // No .gitignore yet — we'll create one.
67
+ }
68
+ if (/^\.boardwalk\/?\s*$/m.test(content))
69
+ return false;
70
+ const prefix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
71
+ try {
72
+ writeFileSync(gitignore, `${content}${prefix}.boardwalk/\n`);
73
+ return true;
74
+ }
75
+ catch {
76
+ return false; // best-effort: a read-only dir shouldn't break deploy
77
+ }
78
+ }
79
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type Channel, type RunEvent } from "@boardwalk-labs/workflow";
2
+ export interface ChannelFlags {
3
+ /** `--verbose`: subscribe to every channel. */
4
+ verbose: boolean;
5
+ /** `--stream <channels>`: explicit comma-separated channel list. */
6
+ stream?: string | undefined;
7
+ }
8
+ /** Resolve `--verbose` / `--stream` to a channel subscription (default: lifecycle+phase+output). */
9
+ export declare function parseChannels(flags: ChannelFlags): readonly Channel[];
10
+ export interface EventRenderer {
11
+ /** Render one event to the writer iff it matches the subscription. */
12
+ render(event: RunEvent): void;
13
+ }
14
+ /**
15
+ * Build the renderer. `write` receives raw text (newlines included where intended) — pass
16
+ * `process.stdout.write` in the CLI, an accumulator in tests.
17
+ */
18
+ export declare function createRenderer(channels: readonly Channel[], write: (text: string) => void): EventRenderer;