@workplane/workplans 0.3.1

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 ADDED
@@ -0,0 +1,53 @@
1
+ # @workplane/workplans
2
+
3
+ Workplan DSL and sequential runner for composing multi-step AI inference pipelines across local and frontier models.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @workplane/workplans
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { SequentialWorkplanRunner, LocalWorkplanContext } from "@workplane/workplans";
15
+
16
+ const plan = {
17
+ id: "my-plan",
18
+ name: "Summarize then critique",
19
+ steps: [
20
+ {
21
+ id: "summarize",
22
+ name: "Summarize",
23
+ adapter: "ollama",
24
+ provider: "ollama",
25
+ model: "llama3",
26
+ payload: { prompt: "Summarize: " + input },
27
+ output: { dest: "next" },
28
+ },
29
+ {
30
+ id: "critique",
31
+ name: "Critique",
32
+ adapter: "anthropic",
33
+ provider: "anthropic",
34
+ model: "claude-haiku-4-5-20251001",
35
+ payload: { prompt: "Critique this summary:\n{{prevOutput}}" },
36
+ },
37
+ ],
38
+ };
39
+
40
+ const result = await new SequentialWorkplanRunner().run(plan, new LocalWorkplanContext());
41
+ ```
42
+
43
+ ## Inline providers
44
+
45
+ Steps with `provider` set to `anthropic`, `openai`, `ollama`, `shell`, or `file` run inline without dispatching to a workplane fleet node. Steps without a provider are routed via `WorkplanRunContext.resolveNode()`.
46
+
47
+ ## Environment variables
48
+
49
+ | Variable | Purpose |
50
+ |----------|---------|
51
+ | `ANTHROPIC_API_KEY` | Required for `provider: "anthropic"` steps |
52
+ | `OPENAI_API_KEY` | Required for `provider: "openai"` steps |
53
+ | `OLLAMA_HOST` | Override Ollama URL (default `http://localhost:11434`) |
@@ -0,0 +1,16 @@
1
+ // src/providers/anthropic.ts
2
+ import Anthropic from "@anthropic-ai/sdk";
3
+ async function runAnthropicStep(opts) {
4
+ const client = new Anthropic({ apiKey: opts.apiKey ?? process.env.ANTHROPIC_API_KEY });
5
+ const message = await client.messages.create({
6
+ model: opts.model,
7
+ max_tokens: 4096,
8
+ messages: [{ role: "user", content: opts.prompt }]
9
+ });
10
+ const block = message.content[0];
11
+ return block.type === "text" ? block.text : "";
12
+ }
13
+ export {
14
+ runAnthropicStep
15
+ };
16
+ //# sourceMappingURL=anthropic-EGNPC77L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/anthropic.ts"],"sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\n\nexport interface AnthropicStepOptions {\n model: string;\n prompt: string;\n apiKey?: string;\n}\n\nexport async function runAnthropicStep(opts: AnthropicStepOptions): Promise<string> {\n const client = new Anthropic({ apiKey: opts.apiKey ?? process.env.ANTHROPIC_API_KEY });\n const message = await client.messages.create({\n model: opts.model,\n max_tokens: 4096,\n messages: [{ role: \"user\", content: opts.prompt }],\n });\n const block = message.content[0];\n return block.type === \"text\" ? block.text : \"\";\n}\n"],"mappings":";AAAA,OAAO,eAAe;AAQtB,eAAsB,iBAAiB,MAA6C;AAClF,QAAM,SAAS,IAAI,UAAU,EAAE,QAAQ,KAAK,UAAU,QAAQ,IAAI,kBAAkB,CAAC;AACrF,QAAM,UAAU,MAAM,OAAO,SAAS,OAAO;AAAA,IAC3C,OAAO,KAAK;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,KAAK,OAAO,CAAC;AAAA,EACnD,CAAC;AACD,QAAM,QAAQ,QAAQ,QAAQ,CAAC;AAC/B,SAAO,MAAM,SAAS,SAAS,MAAM,OAAO;AAC9C;","names":[]}
@@ -0,0 +1,10 @@
1
+ // src/providers/file.ts
2
+ import { readFile } from "fs/promises";
3
+ async function runFileStep(opts) {
4
+ return readFile(opts.path, opts.encoding ?? "utf8");
5
+ }
6
+
7
+ export {
8
+ runFileStep
9
+ };
10
+ //# sourceMappingURL=chunk-AEDO4EEG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/file.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\n\nexport interface FileStepOptions {\n path: string;\n encoding?: BufferEncoding;\n}\n\nexport async function runFileStep(opts: FileStepOptions): Promise<string> {\n return readFile(opts.path, opts.encoding ?? \"utf8\");\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AAOzB,eAAsB,YAAY,MAAwC;AACxE,SAAO,SAAS,KAAK,MAAM,KAAK,YAAY,MAAM;AACpD;","names":[]}
@@ -0,0 +1,16 @@
1
+ // src/providers/shell.ts
2
+ import { exec } from "child_process";
3
+ import { promisify } from "util";
4
+ var execAsync = promisify(exec);
5
+ async function runShellStep(opts) {
6
+ const { stdout } = await execAsync(opts.command, {
7
+ cwd: opts.cwd,
8
+ maxBuffer: 10 * 1024 * 1024
9
+ });
10
+ return stdout;
11
+ }
12
+
13
+ export {
14
+ runShellStep
15
+ };
16
+ //# sourceMappingURL=chunk-JC4YENPR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/shell.ts"],"sourcesContent":["import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execAsync = promisify(exec);\n\nexport interface ShellStepOptions {\n command: string;\n cwd?: string;\n}\n\nexport async function runShellStep(opts: ShellStepOptions): Promise<string> {\n const { stdout } = await execAsync(opts.command, {\n cwd: opts.cwd,\n maxBuffer: 10 * 1024 * 1024,\n });\n return stdout;\n}\n"],"mappings":";AAAA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,IAAM,YAAY,UAAU,IAAI;AAOhC,eAAsB,aAAa,MAAyC;AAC1E,QAAM,EAAE,OAAO,IAAI,MAAM,UAAU,KAAK,SAAS;AAAA,IAC/C,KAAK,KAAK;AAAA,IACV,WAAW,KAAK,OAAO;AAAA,EACzB,CAAC;AACD,SAAO;AACT;","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ runFileStep
3
+ } from "./chunk-AEDO4EEG.js";
4
+ export {
5
+ runFileStep
6
+ };
7
+ //# sourceMappingURL=file-DITVRYJ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,99 @@
1
+ type WorkplanStatus = "running" | "step_failed" | "completed" | "failed" | "cancelled";
2
+ type StepOutputDest = "stdout" | "artifact" | "next";
3
+ interface StepOutput {
4
+ dest: StepOutputDest;
5
+ format?: "text" | "json";
6
+ artifactName?: string;
7
+ }
8
+ interface WorkplanStep {
9
+ id: string;
10
+ name: string;
11
+ adapter: string;
12
+ payload: Record<string, unknown>;
13
+ requires?: string[];
14
+ provider?: string;
15
+ model?: string;
16
+ output?: StepOutput;
17
+ dependsOn?: string[];
18
+ continueOnError?: boolean;
19
+ }
20
+ interface Workplan {
21
+ id: string;
22
+ name: string;
23
+ description?: string;
24
+ steps: WorkplanStep[];
25
+ }
26
+ interface StepResult {
27
+ stepId: string;
28
+ output: string;
29
+ exitCode: number;
30
+ durationMs: number;
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+ interface WorkplanResult {
34
+ planId: string;
35
+ status: WorkplanStatus;
36
+ steps: StepResult[];
37
+ succeeded: boolean;
38
+ }
39
+ interface NodeHandle {
40
+ execute(step: WorkplanStep): Promise<StepResult>;
41
+ }
42
+ interface WorkplanRunContext {
43
+ resolveNode(requires: string[], provider?: string): Promise<NodeHandle>;
44
+ emitResult(stepId: string, stepName: string, output: string): void;
45
+ }
46
+ interface WorkplanRunner {
47
+ run(plan: Workplan, ctx: WorkplanRunContext): Promise<WorkplanResult>;
48
+ }
49
+ interface WorkplanSchedule {
50
+ id: string;
51
+ planId: string;
52
+ name: string;
53
+ cronExpression: string;
54
+ timezone: string;
55
+ inputs: Record<string, unknown>;
56
+ enabled: boolean;
57
+ lastRunAt?: string;
58
+ nextRunAt?: string;
59
+ createdBy?: string;
60
+ }
61
+
62
+ declare class SequentialWorkplanRunner implements WorkplanRunner {
63
+ run(plan: Workplan, ctx: WorkplanRunContext): Promise<WorkplanResult>;
64
+ }
65
+
66
+ interface LocalWorkplanContextOptions {
67
+ serverUrl?: string;
68
+ nodeToken?: string;
69
+ }
70
+ declare class LocalWorkplanContext implements WorkplanRunContext {
71
+ private readonly serverUrl;
72
+ private readonly nodeToken;
73
+ constructor(opts?: LocalWorkplanContextOptions);
74
+ resolveNode(_requires: string[], _provider?: string): Promise<NodeHandle>;
75
+ emitResult(_stepId: string, stepName: string, output: string): void;
76
+ }
77
+
78
+ declare class ScheduleBuilder {
79
+ static nextRunAt(cronExpression: string, timezone: string): string;
80
+ static withNextRunAt(schedule: WorkplanSchedule): WorkplanSchedule;
81
+ }
82
+
83
+ declare function applyTemplate(payload: Record<string, unknown>, prevOutput: string): Record<string, unknown>;
84
+
85
+ declare function printStepResult(stepName: string, output: string): void;
86
+
87
+ interface ShellStepOptions {
88
+ command: string;
89
+ cwd?: string;
90
+ }
91
+ declare function runShellStep(opts: ShellStepOptions): Promise<string>;
92
+
93
+ interface FileStepOptions {
94
+ path: string;
95
+ encoding?: BufferEncoding;
96
+ }
97
+ declare function runFileStep(opts: FileStepOptions): Promise<string>;
98
+
99
+ export { LocalWorkplanContext, type NodeHandle, ScheduleBuilder, SequentialWorkplanRunner, type StepOutput, type StepOutputDest, type StepResult, type Workplan, type WorkplanResult, type WorkplanRunContext, type WorkplanRunner, type WorkplanSchedule, type WorkplanStatus, type WorkplanStep, applyTemplate, printStepResult, runFileStep, runShellStep };
package/dist/index.js ADDED
@@ -0,0 +1,207 @@
1
+ import {
2
+ runShellStep
3
+ } from "./chunk-JC4YENPR.js";
4
+ import {
5
+ runFileStep
6
+ } from "./chunk-AEDO4EEG.js";
7
+
8
+ // src/template.ts
9
+ function applyTemplate(payload, prevOutput) {
10
+ return Object.fromEntries(
11
+ Object.entries(payload).map(([k, v]) => [
12
+ k,
13
+ typeof v === "string" ? v.replaceAll("{{prevOutput}}", prevOutput) : v
14
+ ])
15
+ );
16
+ }
17
+
18
+ // src/runner.ts
19
+ var INLINE_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "openai", "ollama", "shell", "file"]);
20
+ async function runInlineStep(step, prompt) {
21
+ const provider = step.provider;
22
+ if (provider === "anthropic") {
23
+ const { runAnthropicStep } = await import("./anthropic-EGNPC77L.js");
24
+ return runAnthropicStep({ model: step.model ?? "claude-haiku-4-5-20251001", prompt });
25
+ }
26
+ if (provider === "openai") {
27
+ const { runOpenAIStep } = await import("./openai-BBG5P5OG.js");
28
+ return runOpenAIStep({ model: step.model ?? "gpt-4o-mini", prompt });
29
+ }
30
+ if (provider === "ollama") {
31
+ const { runOllamaStep } = await import("./ollama-W3QN5A4L.js");
32
+ return runOllamaStep({ model: step.model ?? "llama3", prompt });
33
+ }
34
+ if (provider === "shell") {
35
+ const { runShellStep: runShellStep2 } = await import("./shell-IDXHRHCD.js");
36
+ return runShellStep2({
37
+ command: String(step.payload.command ?? ""),
38
+ cwd: typeof step.payload.cwd === "string" ? step.payload.cwd : void 0
39
+ });
40
+ }
41
+ if (provider === "file") {
42
+ const { runFileStep: runFileStep2 } = await import("./file-DITVRYJ2.js");
43
+ return runFileStep2({ path: String(step.payload.path ?? "") });
44
+ }
45
+ throw new Error(`unknown inline provider: ${provider}`);
46
+ }
47
+ var SequentialWorkplanRunner = class {
48
+ async run(plan, ctx) {
49
+ const results = [];
50
+ let prevOutput = "";
51
+ let planSucceeded = true;
52
+ for (const step of plan.steps) {
53
+ const payload = prevOutput ? applyTemplate(step.payload, prevOutput) : step.payload;
54
+ const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
55
+ const start = Date.now();
56
+ let output = "";
57
+ let exitCode = 0;
58
+ try {
59
+ if (step.provider && INLINE_PROVIDERS.has(step.provider)) {
60
+ output = await runInlineStep({ ...step, payload }, prompt);
61
+ } else {
62
+ const node = await ctx.resolveNode(step.requires ?? [], step.provider);
63
+ const result = await node.execute({ ...step, payload });
64
+ output = result.output;
65
+ exitCode = result.exitCode;
66
+ }
67
+ } catch (err) {
68
+ exitCode = 1;
69
+ output = err instanceof Error ? err.message : String(err);
70
+ planSucceeded = false;
71
+ const stepResult2 = {
72
+ stepId: step.id,
73
+ output,
74
+ exitCode,
75
+ durationMs: Date.now() - start
76
+ };
77
+ results.push(stepResult2);
78
+ ctx.emitResult(step.id, step.name, output);
79
+ if (!step.continueOnError) {
80
+ return {
81
+ planId: plan.id,
82
+ status: "step_failed",
83
+ steps: results,
84
+ succeeded: false
85
+ };
86
+ }
87
+ prevOutput = "";
88
+ continue;
89
+ }
90
+ const dest = step.output?.dest ?? "stdout";
91
+ const stepResult = {
92
+ stepId: step.id,
93
+ output,
94
+ exitCode,
95
+ durationMs: Date.now() - start
96
+ };
97
+ results.push(stepResult);
98
+ if (dest === "stdout" || dest === "artifact") {
99
+ ctx.emitResult(step.id, step.name, output);
100
+ prevOutput = "";
101
+ } else if (dest === "next") {
102
+ prevOutput = output;
103
+ }
104
+ }
105
+ return {
106
+ planId: plan.id,
107
+ status: planSucceeded ? "completed" : "failed",
108
+ steps: results,
109
+ succeeded: planSucceeded
110
+ };
111
+ }
112
+ };
113
+
114
+ // src/sinks/stdout.ts
115
+ function printStepResult(stepName, output) {
116
+ process.stdout.write(`=== step: ${stepName} ===
117
+ ${output}
118
+
119
+ `);
120
+ }
121
+
122
+ // src/context.ts
123
+ var WorkplaneNodeHandle = class {
124
+ constructor(serverUrl, nodeToken) {
125
+ this.serverUrl = serverUrl;
126
+ this.nodeToken = nodeToken;
127
+ }
128
+ serverUrl;
129
+ nodeToken;
130
+ async execute(step) {
131
+ const start = Date.now();
132
+ const taskRes = await fetch(`${this.serverUrl}/tasks`, {
133
+ method: "POST",
134
+ headers: {
135
+ "content-type": "application/json",
136
+ authorization: `Bearer ${this.nodeToken}`
137
+ },
138
+ body: JSON.stringify({
139
+ kind: "workplan.step",
140
+ adapter: step.adapter,
141
+ payload: step.payload,
142
+ requires: step.requires ?? []
143
+ })
144
+ });
145
+ if (!taskRes.ok) {
146
+ throw new Error(`failed to submit step ${step.id}: ${taskRes.status}`);
147
+ }
148
+ const task = await taskRes.json();
149
+ const output = await this.pollUntilDone(task.id);
150
+ return { stepId: step.id, output, exitCode: 0, durationMs: Date.now() - start };
151
+ }
152
+ async pollUntilDone(taskId) {
153
+ const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
154
+ for (; ; ) {
155
+ await new Promise((r) => setTimeout(r, 1e3));
156
+ const res = await fetch(`${this.serverUrl}/tasks/${taskId}`, {
157
+ headers: { authorization: `Bearer ${this.nodeToken}` }
158
+ });
159
+ const task = await res.json();
160
+ if (terminalStatuses.has(task.status)) {
161
+ if (task.status !== "succeeded") {
162
+ throw new Error(`step task ${taskId} ended with status: ${task.status}`);
163
+ }
164
+ return typeof task.payload?.output === "string" ? task.payload.output : "";
165
+ }
166
+ }
167
+ }
168
+ };
169
+ var LocalWorkplanContext = class {
170
+ serverUrl;
171
+ nodeToken;
172
+ constructor(opts = {}) {
173
+ this.serverUrl = opts.serverUrl ?? process.env.WORKPLANE_SERVER_URL ?? "http://localhost:8787";
174
+ this.nodeToken = opts.nodeToken ?? process.env.WORKPLANE_NODE_TOKEN ?? "";
175
+ }
176
+ async resolveNode(_requires, _provider) {
177
+ return new WorkplaneNodeHandle(this.serverUrl, this.nodeToken);
178
+ }
179
+ emitResult(_stepId, stepName, output) {
180
+ printStepResult(stepName, output);
181
+ }
182
+ };
183
+
184
+ // src/schedule-builder.ts
185
+ import { CronExpressionParser } from "cron-parser";
186
+ var ScheduleBuilder = class _ScheduleBuilder {
187
+ static nextRunAt(cronExpression, timezone) {
188
+ const expr = CronExpressionParser.parse(cronExpression, { tz: timezone });
189
+ return expr.next().toISOString() ?? (/* @__PURE__ */ new Date()).toISOString();
190
+ }
191
+ static withNextRunAt(schedule) {
192
+ return {
193
+ ...schedule,
194
+ nextRunAt: _ScheduleBuilder.nextRunAt(schedule.cronExpression, schedule.timezone)
195
+ };
196
+ }
197
+ };
198
+ export {
199
+ LocalWorkplanContext,
200
+ ScheduleBuilder,
201
+ SequentialWorkplanRunner,
202
+ applyTemplate,
203
+ printStepResult,
204
+ runFileStep,
205
+ runShellStep
206
+ };
207
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/template.ts","../src/runner.ts","../src/sinks/stdout.ts","../src/context.ts","../src/schedule-builder.ts"],"sourcesContent":["export function applyTemplate(payload: Record<string, unknown>, prevOutput: string): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(payload).map(([k, v]) => [\n k,\n typeof v === \"string\" ? v.replaceAll(\"{{prevOutput}}\", prevOutput) : v,\n ]),\n );\n}\n","import type { Workplan, WorkplanResult, WorkplanRunner, WorkplanRunContext, StepResult, WorkplanStep } from \"./types.js\";\nimport { applyTemplate } from \"./template.js\";\n\nconst INLINE_PROVIDERS = new Set([\"anthropic\", \"openai\", \"ollama\", \"shell\", \"file\"]);\n\nasync function runInlineStep(step: WorkplanStep, prompt: string): Promise<string> {\n const provider = step.provider!;\n if (provider === \"anthropic\") {\n const { runAnthropicStep } = await import(\"./providers/anthropic.js\");\n return runAnthropicStep({ model: step.model ?? \"claude-haiku-4-5-20251001\", prompt });\n }\n if (provider === \"openai\") {\n const { runOpenAIStep } = await import(\"./providers/openai.js\");\n return runOpenAIStep({ model: step.model ?? \"gpt-4o-mini\", prompt });\n }\n if (provider === \"ollama\") {\n const { runOllamaStep } = await import(\"./providers/ollama.js\");\n return runOllamaStep({ model: step.model ?? \"llama3\", prompt });\n }\n if (provider === \"shell\") {\n const { runShellStep } = await import(\"./providers/shell.js\");\n return runShellStep({\n command: String(step.payload.command ?? \"\"),\n cwd: typeof step.payload.cwd === \"string\" ? step.payload.cwd : undefined,\n });\n }\n if (provider === \"file\") {\n const { runFileStep } = await import(\"./providers/file.js\");\n return runFileStep({ path: String(step.payload.path ?? \"\") });\n }\n throw new Error(`unknown inline provider: ${provider}`);\n}\n\nexport class SequentialWorkplanRunner implements WorkplanRunner {\n async run(plan: Workplan, ctx: WorkplanRunContext): Promise<WorkplanResult> {\n const results: StepResult[] = [];\n let prevOutput = \"\";\n let planSucceeded = true;\n\n for (const step of plan.steps) {\n const payload = prevOutput ? applyTemplate(step.payload, prevOutput) : step.payload;\n const prompt = typeof payload.prompt === \"string\" ? payload.prompt : \"\";\n const start = Date.now();\n let output = \"\";\n let exitCode = 0;\n\n try {\n if (step.provider && INLINE_PROVIDERS.has(step.provider)) {\n output = await runInlineStep({ ...step, payload }, prompt);\n } else {\n const node = await ctx.resolveNode(step.requires ?? [], step.provider);\n const result = await node.execute({ ...step, payload });\n output = result.output;\n exitCode = result.exitCode;\n }\n } catch (err) {\n exitCode = 1;\n output = err instanceof Error ? err.message : String(err);\n planSucceeded = false;\n\n const stepResult: StepResult = {\n stepId: step.id,\n output,\n exitCode,\n durationMs: Date.now() - start,\n };\n results.push(stepResult);\n ctx.emitResult(step.id, step.name, output);\n\n if (!step.continueOnError) {\n return {\n planId: plan.id,\n status: \"step_failed\",\n steps: results,\n succeeded: false,\n };\n }\n prevOutput = \"\";\n continue;\n }\n\n const dest = step.output?.dest ?? \"stdout\";\n const stepResult: StepResult = {\n stepId: step.id,\n output,\n exitCode,\n durationMs: Date.now() - start,\n };\n results.push(stepResult);\n\n if (dest === \"stdout\" || dest === \"artifact\") {\n ctx.emitResult(step.id, step.name, output);\n prevOutput = \"\";\n } else if (dest === \"next\") {\n prevOutput = output;\n }\n }\n\n return {\n planId: plan.id,\n status: planSucceeded ? \"completed\" : \"failed\",\n steps: results,\n succeeded: planSucceeded,\n };\n }\n}\n","export function printStepResult(stepName: string, output: string): void {\n process.stdout.write(`=== step: ${stepName} ===\\n${output}\\n\\n`);\n}\n","import type { NodeHandle, WorkplanRunContext, WorkplanStep, StepResult } from \"./types.js\";\nimport { printStepResult } from \"./sinks/stdout.js\";\n\nexport interface LocalWorkplanContextOptions {\n serverUrl?: string;\n nodeToken?: string;\n}\n\nclass WorkplaneNodeHandle implements NodeHandle {\n constructor(\n private readonly serverUrl: string,\n private readonly nodeToken: string,\n ) {}\n\n async execute(step: WorkplanStep): Promise<StepResult> {\n const start = Date.now();\n const taskRes = await fetch(`${this.serverUrl}/tasks`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${this.nodeToken}`,\n },\n body: JSON.stringify({\n kind: \"workplan.step\",\n adapter: step.adapter,\n payload: step.payload,\n requires: step.requires ?? [],\n }),\n });\n\n if (!taskRes.ok) {\n throw new Error(`failed to submit step ${step.id}: ${taskRes.status}`);\n }\n\n const task = (await taskRes.json()) as { id: string };\n const output = await this.pollUntilDone(task.id);\n return { stepId: step.id, output, exitCode: 0, durationMs: Date.now() - start };\n }\n\n private async pollUntilDone(taskId: string): Promise<string> {\n const terminalStatuses = new Set([\"succeeded\", \"failed\", \"cancelled\"]);\n for (;;) {\n await new Promise((r) => setTimeout(r, 1000));\n const res = await fetch(`${this.serverUrl}/tasks/${taskId}`, {\n headers: { authorization: `Bearer ${this.nodeToken}` },\n });\n const task = (await res.json()) as { status: string; payload?: Record<string, unknown> };\n if (terminalStatuses.has(task.status)) {\n if (task.status !== \"succeeded\") {\n throw new Error(`step task ${taskId} ended with status: ${task.status}`);\n }\n return typeof task.payload?.output === \"string\" ? task.payload.output : \"\";\n }\n }\n }\n}\n\nexport class LocalWorkplanContext implements WorkplanRunContext {\n private readonly serverUrl: string;\n private readonly nodeToken: string;\n\n constructor(opts: LocalWorkplanContextOptions = {}) {\n this.serverUrl = opts.serverUrl ?? process.env.WORKPLANE_SERVER_URL ?? \"http://localhost:8787\";\n this.nodeToken = opts.nodeToken ?? process.env.WORKPLANE_NODE_TOKEN ?? \"\";\n }\n\n async resolveNode(_requires: string[], _provider?: string): Promise<NodeHandle> {\n return new WorkplaneNodeHandle(this.serverUrl, this.nodeToken);\n }\n\n emitResult(_stepId: string, stepName: string, output: string): void {\n printStepResult(stepName, output);\n }\n}\n","import { CronExpressionParser } from \"cron-parser\";\nimport type { WorkplanSchedule } from \"./types.js\";\n\nexport class ScheduleBuilder {\n static nextRunAt(cronExpression: string, timezone: string): string {\n const expr = CronExpressionParser.parse(cronExpression, { tz: timezone });\n return expr.next().toISOString() ?? new Date().toISOString();\n }\n\n static withNextRunAt(schedule: WorkplanSchedule): WorkplanSchedule {\n return {\n ...schedule,\n nextRunAt: ScheduleBuilder.nextRunAt(schedule.cronExpression, schedule.timezone),\n };\n }\n}\n"],"mappings":"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","names":["runShellStep","runFileStep","stepResult"]}
@@ -0,0 +1,12 @@
1
+ // src/providers/ollama.ts
2
+ import { Ollama } from "ollama";
3
+ async function runOllamaStep(opts) {
4
+ const host = opts.host ?? process.env.OLLAMA_HOST ?? "http://localhost:11434";
5
+ const client = new Ollama({ host });
6
+ const response = await client.generate({ model: opts.model, prompt: opts.prompt });
7
+ return response.response;
8
+ }
9
+ export {
10
+ runOllamaStep
11
+ };
12
+ //# sourceMappingURL=ollama-W3QN5A4L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/ollama.ts"],"sourcesContent":["import { Ollama } from \"ollama\";\n\nexport interface OllamaStepOptions {\n model: string;\n prompt: string;\n host?: string;\n}\n\nexport async function runOllamaStep(opts: OllamaStepOptions): Promise<string> {\n const host = opts.host ?? process.env.OLLAMA_HOST ?? \"http://localhost:11434\";\n const client = new Ollama({ host });\n const response = await client.generate({ model: opts.model, prompt: opts.prompt });\n return response.response;\n}\n"],"mappings":";AAAA,SAAS,cAAc;AAQvB,eAAsB,cAAc,MAA0C;AAC5E,QAAM,OAAO,KAAK,QAAQ,QAAQ,IAAI,eAAe;AACrD,QAAM,SAAS,IAAI,OAAO,EAAE,KAAK,CAAC;AAClC,QAAM,WAAW,MAAM,OAAO,SAAS,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO,CAAC;AACjF,SAAO,SAAS;AAClB;","names":[]}
@@ -0,0 +1,14 @@
1
+ // src/providers/openai.ts
2
+ import OpenAI from "openai";
3
+ async function runOpenAIStep(opts) {
4
+ const client = new OpenAI({ apiKey: opts.apiKey ?? process.env.OPENAI_API_KEY });
5
+ const completion = await client.chat.completions.create({
6
+ model: opts.model,
7
+ messages: [{ role: "user", content: opts.prompt }]
8
+ });
9
+ return completion.choices[0]?.message.content ?? "";
10
+ }
11
+ export {
12
+ runOpenAIStep
13
+ };
14
+ //# sourceMappingURL=openai-BBG5P5OG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/openai.ts"],"sourcesContent":["import OpenAI from \"openai\";\n\nexport interface OpenAIStepOptions {\n model: string;\n prompt: string;\n apiKey?: string;\n}\n\nexport async function runOpenAIStep(opts: OpenAIStepOptions): Promise<string> {\n const client = new OpenAI({ apiKey: opts.apiKey ?? process.env.OPENAI_API_KEY });\n const completion = await client.chat.completions.create({\n model: opts.model,\n messages: [{ role: \"user\", content: opts.prompt }],\n });\n return completion.choices[0]?.message.content ?? \"\";\n}\n"],"mappings":";AAAA,OAAO,YAAY;AAQnB,eAAsB,cAAc,MAA0C;AAC5E,QAAM,SAAS,IAAI,OAAO,EAAE,QAAQ,KAAK,UAAU,QAAQ,IAAI,eAAe,CAAC;AAC/E,QAAM,aAAa,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,IACtD,OAAO,KAAK;AAAA,IACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,KAAK,OAAO,CAAC;AAAA,EACnD,CAAC;AACD,SAAO,WAAW,QAAQ,CAAC,GAAG,QAAQ,WAAW;AACnD;","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ runShellStep
3
+ } from "./chunk-JC4YENPR.js";
4
+ export {
5
+ runShellStep
6
+ };
7
+ //# sourceMappingURL=shell-IDXHRHCD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@workplane/workplans",
3
+ "version": "0.3.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/MindStaq/workplane.git"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "sideEffects": false,
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "registry": "https://registry.npmjs.org/"
27
+ },
28
+ "dependencies": {
29
+ "cron-parser": "^5.1.0"
30
+ },
31
+ "peerDependencies": {
32
+ "@anthropic-ai/sdk": "^0.54.0",
33
+ "openai": "^4.103.0",
34
+ "ollama": "^0.5.16"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup"
38
+ }
39
+ }