@synergenius/flow-weaver 0.10.10 → 0.10.12

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.
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Template operation handlers for the tunnel CLI.
3
+ * Ported from flow-weaver-platform/src/services/ast-helpers.ts template operations.
4
+ */
5
+ import * as fs from 'node:fs/promises';
6
+ import * as path from 'node:path';
7
+ import { parser } from '../../../parser.js';
8
+ import { workflowTemplates, nodeTemplates, getWorkflowTemplate, getNodeTemplate, } from '../../templates/index.js';
9
+ import { resolvePath } from '../path-resolver.js';
10
+ function mapTemplate(t) {
11
+ return {
12
+ id: t.id,
13
+ name: t.name,
14
+ description: t.description,
15
+ category: t.category,
16
+ ...(t.configSchema && { configSchema: t.configSchema }),
17
+ };
18
+ }
19
+ export const templateHandlers = {
20
+ listTemplates: async (params) => {
21
+ const type = params.type || 'workflow';
22
+ const templates = type === 'node' ? nodeTemplates : workflowTemplates;
23
+ return { templates: templates.map(mapTemplate) };
24
+ },
25
+ getTemplate: async (params) => {
26
+ const type = params.type || 'workflow';
27
+ const id = params.id;
28
+ if (!id)
29
+ throw new Error('id is required');
30
+ const template = type === 'node' ? getNodeTemplate(id) : getWorkflowTemplate(id);
31
+ return template ? mapTemplate(template) : null;
32
+ },
33
+ getTemplatePreviewAST: async (params) => {
34
+ const templateId = params.templateId;
35
+ if (!templateId)
36
+ return { ast: null };
37
+ const template = getWorkflowTemplate(templateId);
38
+ if (!template)
39
+ return { ast: null };
40
+ try {
41
+ const code = template.generate({ workflowName: 'preview' });
42
+ const parsed = parser.parseFromString(code);
43
+ const workflows = parsed.workflows || [];
44
+ return { ast: workflows[0] || null };
45
+ }
46
+ catch {
47
+ return { ast: null };
48
+ }
49
+ },
50
+ generateWorkflowCode: async (params) => {
51
+ const templateId = params.templateId;
52
+ const workflowName = params.workflowName;
53
+ if (!templateId || !workflowName)
54
+ throw new Error('templateId and workflowName are required');
55
+ const template = getWorkflowTemplate(templateId);
56
+ if (!template)
57
+ throw new Error(`Template "${templateId}" not found`);
58
+ const options = { workflowName };
59
+ if (params.async !== undefined)
60
+ options.async = params.async;
61
+ if (params.config)
62
+ options.config = params.config;
63
+ const code = template.generate(options);
64
+ return { code };
65
+ },
66
+ generateNodeCode: async (params) => {
67
+ const templateId = params.templateId;
68
+ const name = params.name;
69
+ if (!templateId || !name)
70
+ throw new Error('templateId and name are required');
71
+ const template = getNodeTemplate(templateId);
72
+ if (!template)
73
+ throw new Error(`Node template "${templateId}" not found`);
74
+ const code = template.generate(name, params.config);
75
+ return { code };
76
+ },
77
+ getNodeTemplatePreview: async (params) => {
78
+ const templateId = params.templateId;
79
+ const name = params.name || 'preview';
80
+ const template = getNodeTemplate(templateId);
81
+ if (!template)
82
+ return null;
83
+ try {
84
+ const code = template.generate(name);
85
+ const parsed = parser.parseFromString(code);
86
+ const workflows = parsed.workflows || [];
87
+ const nodeTypes = workflows[0]?.nodeTypes || [];
88
+ if (nodeTypes.length === 0)
89
+ return null;
90
+ const nt = nodeTypes[0];
91
+ return {
92
+ name: nt.name,
93
+ inputs: nt.inputs || nt.ports?.filter((p) => p.direction === 'input') || [],
94
+ outputs: nt.outputs || nt.ports?.filter((p) => p.direction === 'output') || [],
95
+ };
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ },
101
+ createWorkflowFromTemplate: async (params, ctx) => {
102
+ const templateId = params.templateId;
103
+ const workflowName = params.workflowName;
104
+ const fileName = params.fileName;
105
+ if (!templateId || !workflowName)
106
+ throw new Error('templateId and workflowName are required');
107
+ const template = getWorkflowTemplate(templateId);
108
+ if (!template)
109
+ throw new Error(`Template "${templateId}" not found`);
110
+ const genOpts = { workflowName };
111
+ if (params.async !== undefined)
112
+ genOpts.async = params.async;
113
+ if (params.config)
114
+ genOpts.config = params.config;
115
+ const code = template.generate(genOpts);
116
+ const targetFileName = fileName || `${workflowName}.ts`;
117
+ const resolved = resolvePath(ctx.workspaceRoot, targetFileName);
118
+ await fs.mkdir(path.dirname(resolved), { recursive: true });
119
+ await fs.writeFile(resolved, code, 'utf-8');
120
+ return { success: true, filePath: '/' + targetFileName };
121
+ },
122
+ };
123
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Path resolution with traversal protection for the tunnel CLI.
3
+ *
4
+ * Ported from flow-weaver-platform/src/services/workspace.ts:89-125.
5
+ * Virtual paths from Studio (e.g. `/workflow.ts`) are resolved to absolute
6
+ * filesystem paths within the workspace root.
7
+ */
8
+ /**
9
+ * Resolve a Studio virtual path to an absolute filesystem path.
10
+ * Blocks null bytes and path traversal.
11
+ */
12
+ export declare function resolvePath(workspaceRoot: string, studioPath: string): string;
13
+ /**
14
+ * Convert an absolute filesystem path to a Studio virtual path.
15
+ */
16
+ export declare function toVirtualPath(workspaceRoot: string, realPath: string): string;
17
+ //# sourceMappingURL=path-resolver.d.ts.map
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Path resolution with traversal protection for the tunnel CLI.
3
+ *
4
+ * Ported from flow-weaver-platform/src/services/workspace.ts:89-125.
5
+ * Virtual paths from Studio (e.g. `/workflow.ts`) are resolved to absolute
6
+ * filesystem paths within the workspace root.
7
+ */
8
+ import * as path from 'node:path';
9
+ /**
10
+ * Resolve a Studio virtual path to an absolute filesystem path.
11
+ * Blocks null bytes and path traversal.
12
+ */
13
+ export function resolvePath(workspaceRoot, studioPath) {
14
+ if (studioPath.includes('\0')) {
15
+ throw new Error('Path traversal blocked');
16
+ }
17
+ // If the path is already absolute and within the workspace, allow it
18
+ if (studioPath.startsWith(workspaceRoot + path.sep) || studioPath === workspaceRoot) {
19
+ const resolved = path.resolve(studioPath);
20
+ if (!resolved.startsWith(workspaceRoot + path.sep) && resolved !== workspaceRoot) {
21
+ throw new Error('Path traversal blocked');
22
+ }
23
+ return resolved;
24
+ }
25
+ // Strip /cloud prefix if present
26
+ let normalized = studioPath;
27
+ if (normalized.startsWith('/cloud')) {
28
+ normalized = normalized.slice('/cloud'.length);
29
+ }
30
+ // Strip leading slashes
31
+ normalized = normalized.replace(/^\/+/, '');
32
+ if (!normalized) {
33
+ return workspaceRoot;
34
+ }
35
+ const resolved = path.resolve(workspaceRoot, normalized);
36
+ // Block path traversal — resolved must be within workspace
37
+ if (!resolved.startsWith(workspaceRoot + path.sep) && resolved !== workspaceRoot) {
38
+ throw new Error('Path traversal blocked');
39
+ }
40
+ return resolved;
41
+ }
42
+ /**
43
+ * Convert an absolute filesystem path to a Studio virtual path.
44
+ */
45
+ export function toVirtualPath(workspaceRoot, realPath) {
46
+ const rel = path.relative(workspaceRoot, realPath);
47
+ if (rel.startsWith('..')) {
48
+ return '/' + path.basename(realPath);
49
+ }
50
+ return '/' + rel;
51
+ }
52
+ //# sourceMappingURL=path-resolver.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver",
3
- "version": "0.10.10",
3
+ "version": "0.10.12",
4
4
  "description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -139,12 +139,14 @@
139
139
  "socket.io-client": "^4.8.0",
140
140
  "source-map": "^0.7.6",
141
141
  "ts-morph": "^21.0.1",
142
+ "ws": "^8.19.0",
142
143
  "zod": "^3.22.4"
143
144
  },
144
145
  "devDependencies": {
145
146
  "@types/js-yaml": "^4.0.9",
146
147
  "@types/node": "^20.11.0",
147
148
  "@types/react": "^19.0.0",
149
+ "@types/ws": "^8.18.1",
148
150
  "@vitest/coverage-v8": "^4.0.18",
149
151
  "esbuild": "^0.27.2",
150
152
  "prettier": "^3.1.1",