@fnet/cli 0.115.0 → 0.116.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.
@@ -1,223 +1,332 @@
1
1
  {% if atom.doc.features.cli.enabled===true %}
2
- import argv from '../default/input.args.js';
3
- import { default as Engine } from '../default/{{atom.doc.features.cli_default_entry_file or atom.doc.features.main_default_entry_file}}';
4
2
 
5
- {% if atom.doc.features.cli.mcp.enabled===true %}
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ {# Define macros for reusable code blocks #}
4
+ {% macro importMcpDependencies() %}
5
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
+ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
- {% endif %}
8
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9
+ import express from "express";
10
+ {% endmacro %}
9
11
 
10
- {% if atom.doc.features.cli.http.enabled===true %}
11
- // Using Node.js built-in http module instead of express
12
- {% endif %}
13
-
14
- {% if atom.doc.features.cli.extend===true %}
15
- {# TYPE 1 #}
16
- import { default as runExtended } from '../../../cli';
12
+ {% macro mcpModeCodeExtended(runFn) %}
13
+ if (cliMode === 'mcp') {
14
+ // MCP mode code
15
+ const server = new Server({
16
+ name: "{{atom.doc.features.cli.mcp.name or atom.doc.name}}",
17
+ version: "{{atom.doc.version or '0.0.1'}}"
18
+ }, {
19
+ capabilities: {
20
+ tools: {}
21
+ }
22
+ });
17
23
 
18
- const run = async () => {
19
- const args = await argv();
20
- const cliMode = args['cli-mode'] || args.cli_mode || 'default';
24
+ // Define available tools
25
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
26
+ return {
27
+ tools: [{
28
+ name: "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}",
29
+ description: "{{atom.doc.features.cli.mcp.tool.description or atom.doc.description}}",
30
+ {% if atom.doc.input %}
31
+ inputSchema: {{atom.doc.input | dump | safe}}
32
+ {% else %}
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {},
36
+ additionalProperties: true
37
+ }
38
+ {% endif %}
39
+ }]
40
+ };
41
+ });
21
42
 
22
- if (cliMode === 'default') {
23
- // Default mode code
24
- return await runExtended(args, { Engine });
43
+ // Handle tool execution
44
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
45
+ if (request.params.name === "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}") {
46
+ try {
47
+ const result = await {{ runFn }}(request.params.arguments, { Engine });
48
+ return {
49
+ content: [{
50
+ type: "text",
51
+ text: JSON.stringify(result)
52
+ }]
53
+ };
54
+ } catch (error) {
55
+ return {
56
+ content: [{
57
+ type: "text",
58
+ text: `Error: ${error.message}`
59
+ }],
60
+ isError: true
61
+ };
62
+ }
25
63
  }
64
+ throw new Error("Tool not found");
65
+ });
26
66
 
27
- {% if atom.doc.features.cli.mcp.enabled===true %}
28
- if (cliMode === 'mcp') {
29
- // MCP mode code
30
- const server = new McpServer({
31
- name: "{{atom.doc.name}}",
32
- version: "{{atom.doc.version}}"
33
- });
34
-
35
- server.tool(
36
- "{{atom.doc.name}}",
37
- async (toolArgs) => {
38
- try {
39
- const result = await runExtended(toolArgs, { Engine });
40
- return {
41
- content: [{
42
- type: "text",
43
- text: JSON.stringify(result)
44
- }]
45
- };
46
- } catch (error) {
47
- return {
48
- content: [{
49
- type: "text",
50
- text: `Error: ${error.message}`
51
- }],
52
- isError: true
53
- };
54
- }
55
- }
56
- );
67
+ // Get transport type from arguments
68
+ const transportType = args['mcp-transport'] || args.mcp_transport || 'stdio';
69
+ let transport;
70
+
71
+ if (transportType === 'stdio') {
72
+ // Use stdio transport
73
+ transport = new StdioServerTransport();
74
+ } else if (transportType === 'sse') {
75
+ // Use SSE transport
76
+ const app = express();
77
+ app.use(express.json());
78
+
79
+ const port = args['cli-port'] || args.cli_port || 3000;
80
+ const server = app.listen(port, () => {
81
+ console.log(`MCP server started with SSE transport on port ${port}`);
82
+ });
83
+
84
+ transport = new StreamableHTTPServerTransport({
85
+ sessionIdGenerator: () => Math.random().toString(36).substring(2, 15),
86
+ });
87
+
88
+ app.post('/sse', async (req, res) => {
89
+ await transport.handleRequest(req, res, req.body);
90
+ });
57
91
 
58
- const transport = new StdioServerTransport();
59
- await server.connect(transport);
60
- console.log("MCP server started with stdio transport");
61
- return;
92
+ app.get('/sse', async (req, res) => {
93
+ await transport.handleRequest(req, res);
94
+ });
95
+
96
+ app.delete('/sse', async (req, res) => {
97
+ await transport.handleRequest(req, res);
98
+ });
99
+ } else {
100
+ console.error(`Unknown MCP transport type: ${transportType}`);
101
+ console.error(`Supported types: stdio, sse`);
102
+ process.exit(1);
103
+ }
104
+
105
+ await server.connect(transport);
106
+ return;
107
+ }
108
+ {% endmacro %}
109
+
110
+ {% macro mcpModeCodeEngine(engineVar) %}
111
+ if (cliMode === 'mcp') {
112
+ // MCP mode code
113
+ const server = new Server({
114
+ name: "{{atom.doc.features.cli.mcp.name or atom.doc.name}}",
115
+ version: "{{atom.doc.version or '0.0.1'}}"
116
+ }, {
117
+ capabilities: {
118
+ tools: {}
62
119
  }
63
- {% endif %}
64
-
65
- {% if atom.doc.features.cli.http.enabled===true %}
66
- if (cliMode === 'http') {
67
- // HTTP mode code using built-in http module
68
- const http = require('http');
69
-
70
- const server = http.createServer((req, res) => {
71
- if (req.method === 'POST' && req.url === '/{{atom.doc.name}}') {
72
- let body = '';
73
- req.on('data', chunk => {
74
- body += chunk.toString();
75
- });
76
- req.on('end', async () => {
77
- try {
78
- const data = JSON.parse(body);
79
- const result = await runExtended(data, { Engine });
80
- res.writeHead(200, { 'Content-Type': 'application/json' });
81
- res.end(JSON.stringify(result));
82
- } catch (error) {
83
- res.writeHead(500, { 'Content-Type': 'application/json' });
84
- res.end(JSON.stringify({ error: error.message }));
85
- }
86
- });
87
- } else {
88
- res.writeHead(404);
89
- res.end();
120
+ });
121
+
122
+ // Define available tools
123
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
124
+ return {
125
+ tools: [{
126
+ name: "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}",
127
+ description: "{{atom.doc.features.cli.mcp.tool.description or atom.doc.description}}",
128
+ {% if atom.doc.input %}
129
+ inputSchema: {{atom.doc.input | dump | safe}}
130
+ {% else %}
131
+ inputSchema: {
132
+ type: "object",
133
+ properties: {},
134
+ additionalProperties: true
90
135
  }
91
- });
136
+ {% endif %}
137
+ }]
138
+ };
139
+ });
92
140
 
93
- const port = args['cli-port'] || args.cli_port || 3000;
94
- server.listen(port, () => {
95
- console.log(`HTTP server started on port ${port}`);
96
- });
97
- return;
141
+ // Handle tool execution
142
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
143
+ if (request.params.name === "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}") {
144
+ try {
145
+ const result = await {{ engineVar }}.run(request.params.arguments);
146
+ return {
147
+ content: [{
148
+ type: "text",
149
+ text: JSON.stringify(result)
150
+ }]
151
+ };
152
+ } catch (error) {
153
+ return {
154
+ content: [{
155
+ type: "text",
156
+ text: `Error: ${error.message}`
157
+ }],
158
+ isError: true
159
+ };
160
+ }
98
161
  }
99
- {% endif %}
162
+ throw new Error("Tool not found");
163
+ });
100
164
 
101
- console.error(`Unknown CLI mode: ${cliMode}`);
102
- process.exit(1);
103
- };
104
-
105
- run()
106
- .then(() => {
107
- {# process.exit(0); #}
108
- })
109
- .catch((error) => {
110
- console.error(error.message);
111
- process.exit(1);
165
+ // Note: Direct access to workflow nodes is not implemented in this version
166
+ // In a future version, we could expose workflow nodes as separate MCP tools
167
+
168
+ // Get transport type from arguments
169
+ const transportType = args['mcp-transport'] || args.mcp_transport || 'stdio';
170
+ let transport;
171
+
172
+ if (transportType === 'stdio') {
173
+ // Use stdio transport
174
+ transport = new StdioServerTransport();
175
+ } else if (transportType === 'sse') {
176
+ // Use SSE transport
177
+ const app = express();
178
+ app.use(express.json());
179
+
180
+ const port = args['cli-port'] || args.cli_port || 3000;
181
+ const server = app.listen(port, () => {
182
+ console.log(`MCP server started with SSE transport on port ${port}`);
112
183
  });
113
- {% else %}
114
- {# TYPE 2 #}
115
- const run = async () => {
116
- const args = await argv();
117
- const cliMode = args['cli-mode'] || args.cli_mode || 'default';
118
- const engine = new Engine();
119
-
120
- if (cliMode === 'default') {
121
- // Default mode code
122
- const result = await engine.run(args);
123
-
124
- if (typeof result !== 'undefined') {
125
- const stdout_format = args['stdout-format'] || args.stdout_format || null;
126
-
127
- if (stdout_format === 'json') console.log(JSON.stringify(result, null, 2));
128
- else console.log(result);
129
- }
130
- return;
131
- }
132
184
 
133
- {% if atom.doc.features.cli.mcp.enabled===true %}
134
- if (cliMode === 'mcp') {
135
- // MCP mode code
136
- const server = new McpServer({
137
- name: "{{atom.doc.name}}",
138
- version: "{{atom.doc.version}}"
139
- });
140
-
141
- server.tool(
142
- "{{atom.doc.name}}",
143
- async (toolArgs) => {
144
- try {
145
- const result = await engine.run(toolArgs);
146
- return {
147
- content: [{
148
- type: "text",
149
- text: JSON.stringify(result)
150
- }]
151
- };
152
- } catch (error) {
153
- return {
154
- content: [{
155
- type: "text",
156
- text: `Error: ${error.message}`
157
- }],
158
- isError: true
159
- };
160
- }
161
- }
162
- );
185
+ transport = new StreamableHTTPServerTransport({
186
+ sessionIdGenerator: () => Math.random().toString(36).substring(2, 15),
187
+ });
163
188
 
164
- // Note: Direct access to workflow nodes is not implemented in this version
165
- // In a future version, we could expose workflow nodes as separate MCP tools
189
+ app.post('/sse', async (req, res) => {
190
+ await transport.handleRequest(req, res, req.body);
191
+ });
166
192
 
167
- const transport = new StdioServerTransport();
168
- await server.connect(transport);
169
- console.log("MCP server started with stdio transport");
170
- return;
171
- }
172
- {% endif %}
173
-
174
- {% if atom.doc.features.cli.http.enabled===true %}
175
- if (cliMode === 'http') {
176
- // HTTP mode code using built-in http module
177
- const http = require('http');
178
-
179
- const server = http.createServer((req, res) => {
180
- if (req.method === 'POST' && req.url === '/{{atom.doc.name}}') {
181
- let body = '';
182
- req.on('data', chunk => {
183
- body += chunk.toString();
184
- });
185
- req.on('end', async () => {
186
- try {
187
- const data = JSON.parse(body);
188
- const result = await engine.run(data);
189
- res.writeHead(200, { 'Content-Type': 'application/json' });
190
- res.end(JSON.stringify(result));
191
- } catch (error) {
192
- res.writeHead(500, { 'Content-Type': 'application/json' });
193
- res.end(JSON.stringify({ error: error.message }));
194
- }
195
- });
196
- } else {
197
- res.writeHead(404);
198
- res.end();
199
- }
200
- });
193
+ app.get('/sse', async (req, res) => {
194
+ await transport.handleRequest(req, res);
195
+ });
201
196
 
202
- // Note: Direct access to workflow nodes is not implemented in this version
203
- // In a future version, we could expose workflow nodes as separate HTTP endpoints
197
+ app.delete('/sse', async (req, res) => {
198
+ await transport.handleRequest(req, res);
199
+ });
200
+ } else {
201
+ console.error(`Unknown MCP transport type: ${transportType}`);
202
+ console.error(`Supported types: stdio, sse`);
203
+ process.exit(1);
204
+ }
204
205
 
205
- const port = args['cli-port'] || args.cli_port || 3000;
206
- server.listen(port, () => {
207
- console.log(`HTTP server started on port ${port}`);
208
- });
209
- return;
206
+ await server.connect(transport);
207
+ return;
208
+ }
209
+ {% endmacro %}
210
+
211
+ {% macro httpModeCodeExpress(runFn, engineParam) %}
212
+ if (cliMode === 'http') {
213
+ // HTTP mode code using Express
214
+ const app = express();
215
+ app.use(express.json());
216
+
217
+ app.post('/{{atom.doc.features.cli.http.path or atom.doc.name}}', async (req, res) => {
218
+ try {
219
+ const result = await {{ runFn }}(req.body{{ engineParam }});
220
+ res.json(result);
221
+ } catch (error) {
222
+ res.status(500).json({ error: error.message });
210
223
  }
211
- {% endif %}
224
+ });
212
225
 
213
- console.error(`Unknown CLI mode: ${cliMode}`);
214
- process.exit(1);
215
- };
226
+ const port = args['cli-port'] || args.cli_port || 3000;
227
+ app.listen(port, () => {
228
+ console.log(`HTTP server started on port ${port}`);
229
+ });
230
+ return;
231
+ }
232
+ {% endmacro %}
216
233
 
217
- run()
218
- .catch((error) => {
219
- console.error(error.message);
220
- process.exit(1);
221
- });
222
- {% endif %}
234
+ {% macro defaultModeExtended() %}
235
+ if (cliMode === 'default') {
236
+ // Default mode code
237
+ return await runExtended(args, { Engine });
238
+ }
239
+ {% endmacro %}
240
+
241
+ {% macro defaultModeEngine(engineVar) %}
242
+ if (cliMode === 'default') {
243
+ // Default mode code
244
+ const result = await {{ engineVar }}.run(args);
245
+
246
+ if (typeof result !== 'undefined') {
247
+ const stdout_format = args['stdout-format'] || args.stdout_format || null;
248
+
249
+ if (stdout_format === 'json') console.log(JSON.stringify(result, null, 2));
250
+ else console.log(result);
251
+ }
252
+ return;
253
+ }
254
+ {% endmacro %}
255
+
256
+ {% macro runWithCatch() %}
257
+ run()
258
+ .catch((error) => {
259
+ console.error(error.message);
260
+ process.exit(1);
261
+ });
262
+ {% endmacro %}
263
+
264
+ {% macro runWithThenCatch() %}
265
+ run()
266
+ .then(() => {
267
+ {# process.exit(0); #}
268
+ })
269
+ .catch((error) => {
270
+ console.error(error.message);
271
+ process.exit(1);
272
+ });
273
+ {% endmacro %}
274
+
275
+ {# Main template starts here #}
276
+ import argv from '../default/input.args.js';
277
+ import { default as Engine } from '../default/{{atom.doc.features.cli_default_entry_file or atom.doc.features.main_default_entry_file}}';
278
+
279
+ {% if atom.doc.features.cli.mcp.enabled===true %}
280
+ {{ importMcpDependencies() }}
281
+ {% elif atom.doc.features.cli.http.enabled===true %}
282
+ // Using express for HTTP mode
283
+ import express from 'express';
284
+ {% endif %}
285
+
286
+ {% if atom.doc.features.cli.extend===true %}
287
+ {# TYPE 1 #}
288
+ import { default as runExtended } from '../../../cli';
289
+
290
+ const run = async () => {
291
+ const args = await argv();
292
+ const cliMode = args['cli-mode'] || args.cli_mode || 'default';
293
+
294
+ {{ defaultModeExtended() }}
295
+
296
+ {% if atom.doc.features.cli.mcp.enabled===true %}
297
+ {{ mcpModeCodeExtended('runExtended') }}
298
+ {% endif %}
299
+
300
+ {% if atom.doc.features.cli.http.enabled===true %}
301
+ {{ httpModeCodeExpress('runExtended', ', { Engine }') }}
302
+ {% endif %}
303
+
304
+ console.error(`Unknown CLI mode: ${cliMode}`);
305
+ process.exit(1);
306
+ };
307
+
308
+ {{ runWithThenCatch() }}
309
+ {% else %}
310
+ {# TYPE 2 #}
311
+ const run = async () => {
312
+ const args = await argv();
313
+ const cliMode = args['cli-mode'] || args.cli_mode || 'default';
314
+ const engine = new Engine();
315
+
316
+ {{ defaultModeEngine('engine') }}
317
+
318
+ {% if atom.doc.features.cli.mcp.enabled===true %}
319
+ {{ mcpModeCodeEngine('engine') }}
320
+ {% endif %}
321
+
322
+ {% if atom.doc.features.cli.http.enabled===true %}
323
+ {{ httpModeCodeExpress('engine.run', '') }}
324
+ {% endif %}
325
+
326
+ console.error(`Unknown CLI mode: ${cliMode}`);
327
+ process.exit(1);
328
+ };
329
+
330
+ {{ runWithCatch() }}
331
+ {% endif %}
223
332
  {% endif %}
@@ -54,13 +54,22 @@
54
54
  "license": "{{atom.doc.license or 'MIT'}}",
55
55
  "scripts": {
56
56
  "build": "rollup --config",
57
+ "build:dev": "rollup --config --sourcemap --environment DEVELOPMENT",
57
58
  "watch": "rollup --config --watch --sourcemap --environment DEVELOPMENT",
58
59
  "serve": "bunx serve ./"
59
60
  {% if atom.doc.features.cli.enabled %}
60
61
  {% if atom.doc.features.project.format ==='cjs' %}
61
- ,"cli": "bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/index.cjs"
62
+ ,"cli": "bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/index.cjs",
63
+ {% if atom.doc.features.cli.mcp.enabled===true %}
64
+ ,"mcp": "bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/index.cjs --cli-mode=mcp"
65
+ ,"mcp-inspect": "bunx @modelcontextprotocol/inspector bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/index.cjs --cli-mode=mcp"
66
+ {% endif %}
62
67
  {% else %}
63
68
  ,"cli": "bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/"
69
+ {% if atom.doc.features.cli.mcp.enabled===true %}
70
+ ,"mcp": "bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/ --cli-mode=mcp"
71
+ ,"mcp-inspect": "bunx @modelcontextprotocol/inspector bun {{atom.doc.features.cli.node_options}} {{atom.doc.features.cli.dir}}/ --cli-mode=mcp"
72
+ {% endif %}
64
73
  {% endif %}
65
74
  ,"compile": "fbin compile {{atom.doc.features.cli.dir}}/index.js -o .bin/{{atom.doc['npm::bin'] or atom.doc['name'] or atom['id']}}"
66
75
  ,"install-bin": "fbin install ./.bin/{{atom.doc['npm::bin'] or atom.doc['name'] or atom['id']}} --yes"