@bratsos/workflow-engine 0.0.11 → 0.2.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 (49) hide show
  1. package/README.md +270 -513
  2. package/dist/chunk-D7RVRRM2.js +3 -0
  3. package/dist/chunk-D7RVRRM2.js.map +1 -0
  4. package/dist/chunk-HL3OJG7W.js +1033 -0
  5. package/dist/chunk-HL3OJG7W.js.map +1 -0
  6. package/dist/chunk-MUWP5SF2.js +33 -0
  7. package/dist/chunk-MUWP5SF2.js.map +1 -0
  8. package/dist/chunk-NYKMT46J.js +1143 -0
  9. package/dist/chunk-NYKMT46J.js.map +1 -0
  10. package/dist/chunk-P4KMGCT3.js +2292 -0
  11. package/dist/chunk-P4KMGCT3.js.map +1 -0
  12. package/dist/chunk-SPXBCZLB.js +17 -0
  13. package/dist/chunk-SPXBCZLB.js.map +1 -0
  14. package/dist/cli/sync-models.d.ts +1 -0
  15. package/dist/cli/sync-models.js +210 -0
  16. package/dist/cli/sync-models.js.map +1 -0
  17. package/dist/client-D4PoxADF.d.ts +798 -0
  18. package/dist/client.d.ts +5 -0
  19. package/dist/client.js +4 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/index-DAzCfO1R.d.ts +217 -0
  22. package/dist/index.d.ts +569 -0
  23. package/dist/index.js +399 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/interface-MMqhfQQK.d.ts +411 -0
  26. package/dist/kernel/index.d.ts +26 -0
  27. package/dist/kernel/index.js +3 -0
  28. package/dist/kernel/index.js.map +1 -0
  29. package/dist/kernel/testing/index.d.ts +44 -0
  30. package/dist/kernel/testing/index.js +85 -0
  31. package/dist/kernel/testing/index.js.map +1 -0
  32. package/dist/persistence/index.d.ts +2 -0
  33. package/dist/persistence/index.js +6 -0
  34. package/dist/persistence/index.js.map +1 -0
  35. package/dist/persistence/prisma/index.d.ts +37 -0
  36. package/dist/persistence/prisma/index.js +5 -0
  37. package/dist/persistence/prisma/index.js.map +1 -0
  38. package/dist/plugins-BCnDUwIc.d.ts +415 -0
  39. package/dist/ports-tU3rzPXJ.d.ts +245 -0
  40. package/dist/stage-BPw7m9Wx.d.ts +144 -0
  41. package/dist/testing/index.d.ts +264 -0
  42. package/dist/testing/index.js +920 -0
  43. package/dist/testing/index.js.map +1 -0
  44. package/package.json +11 -1
  45. package/skills/workflow-engine/SKILL.md +234 -348
  46. package/skills/workflow-engine/references/03-runtime-setup.md +111 -426
  47. package/skills/workflow-engine/references/05-persistence-setup.md +32 -0
  48. package/skills/workflow-engine/references/07-testing-patterns.md +141 -474
  49. package/skills/workflow-engine/references/08-common-patterns.md +118 -431
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { join, resolve, dirname } from 'path';
4
+ import { pathToFileURL } from 'url';
5
+
6
+ async function main() {
7
+ const apiKey = process.env.OPENROUTER_API_KEY;
8
+ if (!apiKey) {
9
+ console.error("\u274C OPENROUTER_API_KEY environment variable is required");
10
+ process.exit(1);
11
+ }
12
+ const cwd = process.cwd();
13
+ console.log(`\u{1F4C1} Working directory: ${cwd}`);
14
+ let config = {};
15
+ const configPath = join(cwd, "workflow-engine.models.ts");
16
+ if (existsSync(configPath)) {
17
+ try {
18
+ const configModule = await import(pathToFileURL(configPath).href);
19
+ config = configModule.default || configModule;
20
+ console.log("\u{1F4CB} Loaded config from workflow-engine.models.ts");
21
+ } catch (err) {
22
+ console.warn(`\u26A0\uFE0F Could not load config: ${err}`);
23
+ }
24
+ }
25
+ const outputPath = resolve(
26
+ cwd,
27
+ config.outputPath || "src/generated/models.ts"
28
+ );
29
+ const includePatterns = config.include || [];
30
+ const excludePatterns = config.exclude || [];
31
+ const customModels = config.customModels || {};
32
+ console.log("\u{1F504} Fetching models from OpenRouter API...");
33
+ const response = await fetch("https://openrouter.ai/api/v1/models", {
34
+ headers: {
35
+ Authorization: `Bearer ${apiKey}`,
36
+ "Content-Type": "application/json"
37
+ }
38
+ });
39
+ if (!response.ok) {
40
+ console.error(
41
+ `\u274C OpenRouter API error: ${response.status} ${response.statusText}`
42
+ );
43
+ process.exit(1);
44
+ }
45
+ const data = await response.json();
46
+ console.log(`\u2705 Fetched ${data.data.length} models from OpenRouter`);
47
+ console.log("\u{1F504} Fetching embedding models from OpenRouter API...");
48
+ const embeddingResponse = await fetch(
49
+ "https://openrouter.ai/api/v1/embeddings/models",
50
+ {
51
+ headers: {
52
+ Authorization: `Bearer ${apiKey}`,
53
+ "Content-Type": "application/json"
54
+ }
55
+ }
56
+ );
57
+ let embeddingModelIds = /* @__PURE__ */ new Set();
58
+ if (embeddingResponse.ok) {
59
+ const embeddingData = await embeddingResponse.json();
60
+ console.log(
61
+ `\u2705 Fetched ${embeddingData.data.length} embedding models from OpenRouter`
62
+ );
63
+ embeddingModelIds = new Set(embeddingData.data.map((m) => m.id));
64
+ for (const model of embeddingData.data) {
65
+ if (!data.data.find((m) => m.id === model.id)) {
66
+ data.data.push(model);
67
+ }
68
+ }
69
+ } else {
70
+ console.warn(
71
+ `\u26A0\uFE0F Could not fetch embedding models: ${embeddingResponse.status}`
72
+ );
73
+ }
74
+ const filteredModels = data.data.filter((model) => {
75
+ if (includePatterns.length > 0) {
76
+ let matchesInclude = false;
77
+ for (const pattern of includePatterns) {
78
+ if (typeof pattern === "string") {
79
+ if (model.id.includes(pattern)) {
80
+ matchesInclude = true;
81
+ break;
82
+ }
83
+ } else if (pattern instanceof RegExp) {
84
+ if (pattern.test(model.id)) {
85
+ matchesInclude = true;
86
+ break;
87
+ }
88
+ }
89
+ }
90
+ if (!matchesInclude) return false;
91
+ }
92
+ for (const pattern of excludePatterns) {
93
+ if (typeof pattern === "string") {
94
+ if (model.id === pattern) return false;
95
+ } else if (pattern instanceof RegExp) {
96
+ if (pattern.test(model.id)) return false;
97
+ }
98
+ }
99
+ return true;
100
+ });
101
+ console.log(
102
+ `\u{1F4CA} After filtering: ${filteredModels.length} models (excluded ${data.data.length - filteredModels.length})`
103
+ );
104
+ const models = {};
105
+ for (const model of filteredModels) {
106
+ const promptPrice = parseFloat(model.pricing?.prompt || "0");
107
+ const completionPrice = parseFloat(model.pricing?.completion || "0");
108
+ const supportsBatch = model.id.startsWith("anthropic/") || model.id.startsWith("openai/") && !model.id.includes("gpt-oss") || model.id.startsWith("google/") && model.id.includes("gemini");
109
+ const isEmbedding = embeddingModelIds.has(model.id);
110
+ const supportsTools = model.supported_parameters?.includes("tools") || false;
111
+ const supportsStructuredOutputs = model.supported_parameters?.includes("structured_outputs") || false;
112
+ models[model.id] = {
113
+ id: model.id,
114
+ name: model.name,
115
+ inputCostPerMillion: Math.round(promptPrice * 1e6 * 1e4) / 1e4,
116
+ outputCostPerMillion: Math.round(completionPrice * 1e6 * 1e4) / 1e4,
117
+ provider: "openrouter",
118
+ description: model.description,
119
+ contextLength: model.top_provider?.context_length || model.context_length,
120
+ maxCompletionTokens: model.top_provider?.max_completion_tokens,
121
+ ...isEmbedding && { isEmbeddingModel: true },
122
+ ...supportsTools && { supportsTools: true },
123
+ ...supportsStructuredOutputs && { supportsStructuredOutputs: true },
124
+ ...supportsBatch && !isEmbedding && {
125
+ supportsAsyncBatch: true,
126
+ batchDiscountPercent: 50
127
+ }
128
+ };
129
+ }
130
+ for (const [key, modelConfig] of Object.entries(customModels)) {
131
+ models[key] = modelConfig;
132
+ }
133
+ const allModelIds = Object.keys(models);
134
+ console.log(`\u{1F4E6} Total models to generate: ${allModelIds.length}`);
135
+ const generatedContent = generateTypeScript(models);
136
+ const dir = dirname(outputPath);
137
+ if (!existsSync(dir)) {
138
+ mkdirSync(dir, { recursive: true });
139
+ }
140
+ writeFileSync(outputPath, generatedContent, "utf-8");
141
+ console.log(`\u2705 Generated ${outputPath}`);
142
+ console.log(
143
+ "\n\u{1F4A1} Import this file in your app entry to enable autocomplete:"
144
+ );
145
+ console.log(` import "${outputPath.replace(cwd, ".")}";`);
146
+ }
147
+ function generateTypeScript(models) {
148
+ const modelIds = Object.keys(models);
149
+ const modelEntries = Object.entries(models).map(([key, config]) => {
150
+ const lines = [
151
+ ` "${key}": {`,
152
+ ` id: "${config.id}",`,
153
+ ` name: "${config.name.replace(/"/g, '\\"')}",`,
154
+ ` description: "${config.description?.replace(/"/g, '\\"').replace(/\n/g, "\\n")}",`,
155
+ ` inputCostPerMillion: ${config.inputCostPerMillion},`,
156
+ ` outputCostPerMillion: ${config.outputCostPerMillion},`,
157
+ ` provider: "${config.provider}",`,
158
+ ` isEmbeddingModel: ${config.isEmbeddingModel || false},`,
159
+ ` supportsTools: ${config.supportsTools || false},`,
160
+ ` supportsStructuredOutputs: ${config.supportsStructuredOutputs || false},`,
161
+ ` supportsAsyncBatch: ${config.supportsAsyncBatch || false},`,
162
+ ` batchDiscountPercent: ${config.batchDiscountPercent || 0},`,
163
+ ` contextLength: ${config.contextLength || 0},`,
164
+ ` maxCompletionTokens: ${config.maxCompletionTokens},`,
165
+ ` },`
166
+ ];
167
+ return lines.join("\n");
168
+ }).join("\n");
169
+ const registryEntries = modelIds.map((id) => ` "${id}": true;`).join("\n");
170
+ const enumEntries = modelIds.map((id) => ` "${id}",`).join("\n");
171
+ return `// AUTO-GENERATED by workflow-engine-sync
172
+ // Run \`npx workflow-engine-sync\` to regenerate
173
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
174
+
175
+ import { z } from "zod";
176
+ import { registerModels, type ModelConfig } from "@bratsos/workflow-engine/client";
177
+
178
+ // Register all models (OpenRouter + custom)
179
+ const MODELS: Record<string, ModelConfig> = {
180
+ ${modelEntries}
181
+ };
182
+
183
+ registerModels(MODELS);
184
+
185
+ /**
186
+ * Zod enum of all generated model IDs
187
+ * Use GeneratedModelKey.enum["model-id"] for type-safe model selection
188
+ */
189
+ export const GeneratedModelKey = z.enum([
190
+ ${enumEntries}
191
+ ]);
192
+
193
+ export type GeneratedModelKey = z.infer<typeof GeneratedModelKey>;
194
+
195
+ // TypeScript module augmentation for autocomplete
196
+ declare module "@bratsos/workflow-engine/client" {
197
+ interface ModelRegistry {
198
+ ${registryEntries}
199
+ }
200
+ }
201
+
202
+ export { MODELS };
203
+ `;
204
+ }
205
+ main().catch((err) => {
206
+ console.error("\u274C Error:", err);
207
+ process.exit(1);
208
+ });
209
+ //# sourceMappingURL=sync-models.js.map
210
+ //# sourceMappingURL=sync-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/sync-models.ts"],"names":[],"mappings":"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","file":"sync-models.js","sourcesContent":["#!/usr/bin/env node\n/**\n * workflow-engine-sync - CLI to sync models from OpenRouter API\n *\n * Usage:\n * npx workflow-engine-sync\n *\n * Reads optional workflow-engine.models.ts config from cwd for:\n * - include: patterns to include models\n * - exclude: patterns to filter out models\n * - customModels: additional models to include\n */\n\n/// <reference types=\"node\" />\n\nimport { existsSync, mkdirSync, writeFileSync } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { type ModelConfig, type ModelSyncConfig } from \"../ai/model-helper\";\n\ninterface OpenRouterModel {\n id: string;\n name: string;\n description?: string;\n context_length?: number;\n pricing?: {\n prompt?: string;\n completion?: string;\n };\n supported_parameters: string[];\n top_provider?: {\n context_length?: number;\n max_completion_tokens?: number;\n };\n}\n\ninterface OpenRouterResponse {\n data: OpenRouterModel[];\n}\n\n// Main\nasync function main() {\n const apiKey = process.env.OPENROUTER_API_KEY;\n if (!apiKey) {\n console.error(\"āŒ OPENROUTER_API_KEY environment variable is required\");\n process.exit(1);\n }\n\n const cwd = process.cwd();\n console.log(`šŸ“ Working directory: ${cwd}`);\n\n // Load config if exists\n let config: ModelSyncConfig = {};\n const configPath = join(cwd, \"workflow-engine.models.ts\");\n if (existsSync(configPath)) {\n try {\n const configModule = await import(pathToFileURL(configPath).href);\n config = configModule.default || configModule;\n console.log(\"šŸ“‹ Loaded config from workflow-engine.models.ts\");\n } catch (err) {\n console.warn(`āš ļø Could not load config: ${err}`);\n }\n }\n\n const outputPath = resolve(\n cwd,\n config.outputPath || \"src/generated/models.ts\",\n );\n const includePatterns = config.include || [];\n const excludePatterns = config.exclude || [];\n const customModels = config.customModels || {};\n\n // Fetch models from OpenRouter\n console.log(\"šŸ”„ Fetching models from OpenRouter API...\");\n const response = await fetch(\"https://openrouter.ai/api/v1/models\", {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) {\n console.error(\n `āŒ OpenRouter API error: ${response.status} ${response.statusText}`,\n );\n process.exit(1);\n }\n\n const data = (await response.json()) as OpenRouterResponse;\n console.log(`āœ… Fetched ${data.data.length} models from OpenRouter`);\n\n // Fetch embedding models from OpenRouter\n console.log(\"šŸ”„ Fetching embedding models from OpenRouter API...\");\n const embeddingResponse = await fetch(\n \"https://openrouter.ai/api/v1/embeddings/models\",\n {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n\n let embeddingModelIds = new Set<string>();\n if (embeddingResponse.ok) {\n const embeddingData =\n (await embeddingResponse.json()) as OpenRouterResponse;\n console.log(\n `āœ… Fetched ${embeddingData.data.length} embedding models from OpenRouter`,\n );\n // Track embedding model IDs for later marking\n embeddingModelIds = new Set(embeddingData.data.map((m) => m.id));\n // Add embedding models that might not be in the main list\n for (const model of embeddingData.data) {\n if (!data.data.find((m) => m.id === model.id)) {\n data.data.push(model);\n }\n }\n } else {\n console.warn(\n `āš ļø Could not fetch embedding models: ${embeddingResponse.status}`,\n );\n }\n\n // Filter models - include patterns first, then exclude\n const filteredModels = data.data.filter((model) => {\n // If include patterns are specified, model must match at least one\n if (includePatterns.length > 0) {\n let matchesInclude = false;\n for (const pattern of includePatterns) {\n if (typeof pattern === \"string\") {\n if (model.id.includes(pattern)) {\n matchesInclude = true;\n break;\n }\n } else if (pattern instanceof RegExp) {\n if (pattern.test(model.id)) {\n matchesInclude = true;\n break;\n }\n }\n }\n if (!matchesInclude) return false;\n }\n\n // Check exclude patterns\n for (const pattern of excludePatterns) {\n if (typeof pattern === \"string\") {\n if (model.id === pattern) return false;\n } else if (pattern instanceof RegExp) {\n if (pattern.test(model.id)) return false;\n }\n }\n return true;\n });\n\n console.log(\n `šŸ“Š After filtering: ${filteredModels.length} models (excluded ${data.data.length - filteredModels.length})`,\n );\n\n // Transform to ModelConfig\n const models: Record<string, ModelConfig> = {};\n\n for (const model of filteredModels) {\n // Convert per-token pricing to per-million\n const promptPrice = parseFloat(model.pricing?.prompt || \"0\");\n const completionPrice = parseFloat(model.pricing?.completion || \"0\");\n\n // Check if model supports async batch (Anthropic, OpenAI, or Google Gemini)\n const supportsBatch =\n model.id.startsWith(\"anthropic/\") ||\n (model.id.startsWith(\"openai/\") && !model.id.includes(\"gpt-oss\")) ||\n (model.id.startsWith(\"google/\") && model.id.includes(\"gemini\"));\n\n // Check if this is an embedding model\n const isEmbedding = embeddingModelIds.has(model.id);\n\n // Check for tool and structured output support from supported_parameters\n const supportsTools =\n model.supported_parameters?.includes(\"tools\") || false;\n const supportsStructuredOutputs =\n model.supported_parameters?.includes(\"structured_outputs\") || false;\n\n models[model.id] = {\n id: model.id,\n name: model.name,\n inputCostPerMillion: Math.round(promptPrice * 1_000_000 * 10000) / 10000,\n outputCostPerMillion:\n Math.round(completionPrice * 1_000_000 * 10000) / 10000,\n provider: \"openrouter\",\n description: model.description,\n contextLength: model.top_provider?.context_length || model.context_length,\n maxCompletionTokens: model.top_provider?.max_completion_tokens,\n ...(isEmbedding && { isEmbeddingModel: true }),\n ...(supportsTools && { supportsTools: true }),\n ...(supportsStructuredOutputs && { supportsStructuredOutputs: true }),\n ...(supportsBatch &&\n !isEmbedding && {\n supportsAsyncBatch: true,\n batchDiscountPercent: 50,\n }),\n };\n }\n\n // Merge custom models\n for (const [key, modelConfig] of Object.entries(customModels)) {\n models[key] = modelConfig;\n }\n\n const allModelIds = Object.keys(models);\n console.log(`šŸ“¦ Total models to generate: ${allModelIds.length}`);\n\n // Generate TypeScript file\n const generatedContent = generateTypeScript(models);\n\n // Ensure directory exists\n const dir = dirname(outputPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(outputPath, generatedContent, \"utf-8\");\n console.log(`āœ… Generated ${outputPath}`);\n console.log(\n \"\\nšŸ’” Import this file in your app entry to enable autocomplete:\",\n );\n console.log(` import \"${outputPath.replace(cwd, \".\")}\";`);\n}\n\nfunction generateTypeScript(models: Record<string, ModelConfig>): string {\n const modelIds = Object.keys(models);\n\n // Generate model entries\n const modelEntries = Object.entries(models)\n .map(([key, config]) => {\n const lines = [\n ` \"${key}\": {`,\n ` id: \"${config.id}\",`,\n ` name: \"${config.name.replace(/\"/g, '\\\\\"')}\",`,\n ` description: \"${config.description?.replace(/\"/g, '\\\\\"').replace(/\\n/g, \"\\\\n\")}\",`,\n ` inputCostPerMillion: ${config.inputCostPerMillion},`,\n ` outputCostPerMillion: ${config.outputCostPerMillion},`,\n ` provider: \"${config.provider}\",`,\n ` isEmbeddingModel: ${config.isEmbeddingModel || false},`,\n ` supportsTools: ${config.supportsTools || false},`,\n ` supportsStructuredOutputs: ${config.supportsStructuredOutputs || false},`,\n ` supportsAsyncBatch: ${config.supportsAsyncBatch || false},`,\n ` batchDiscountPercent: ${config.batchDiscountPercent || 0},`,\n ` contextLength: ${config.contextLength || 0},`,\n ` maxCompletionTokens: ${config.maxCompletionTokens},`,\n ` },`,\n ];\n\n return lines.join(\"\\n\");\n })\n .join(\"\\n\");\n\n // Generate registry interface entries\n const registryEntries = modelIds.map((id) => ` \"${id}\": true;`).join(\"\\n\");\n\n // Generate enum entries for GeneratedModelKey\n const enumEntries = modelIds.map((id) => ` \"${id}\",`).join(\"\\n\");\n\n return `// AUTO-GENERATED by workflow-engine-sync\n// Run \\`npx workflow-engine-sync\\` to regenerate\n// Generated at: ${new Date().toISOString()}\n\nimport { z } from \"zod\";\nimport { registerModels, type ModelConfig } from \"@bratsos/workflow-engine/client\";\n\n// Register all models (OpenRouter + custom)\nconst MODELS: Record<string, ModelConfig> = {\n${modelEntries}\n};\n\nregisterModels(MODELS);\n\n/**\n * Zod enum of all generated model IDs\n * Use GeneratedModelKey.enum[\"model-id\"] for type-safe model selection\n */\nexport const GeneratedModelKey = z.enum([\n${enumEntries}\n]);\n\nexport type GeneratedModelKey = z.infer<typeof GeneratedModelKey>;\n\n// TypeScript module augmentation for autocomplete\ndeclare module \"@bratsos/workflow-engine/client\" {\n interface ModelRegistry {\n${registryEntries}\n }\n}\n\nexport { MODELS };\n`;\n}\n\nmain().catch((err) => {\n console.error(\"āŒ Error:\", err);\n process.exit(1);\n});\n"]}