@fnet/cli 0.133.0 → 1.0.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 (182) hide show
  1. package/dist/fbin/index.js +14 -1
  2. package/dist/fnet/index.0jcm9pn5.js +1 -0
  3. package/dist/fnet/index.2084z2ed.js +2 -0
  4. package/dist/fnet/index.33f1ggpr.js +1 -0
  5. package/dist/fnet/index.3exge2js.js +1 -0
  6. package/dist/fnet/index.3kfx538h.js +1 -0
  7. package/dist/fnet/index.490y87nc.js +1 -0
  8. package/dist/fnet/index.4g9yezkq.js +1 -0
  9. package/dist/fnet/index.4jkat7r4.js +1 -0
  10. package/dist/fnet/index.7crx8ky1.js +1 -0
  11. package/dist/fnet/index.7vw06nrn.js +1 -0
  12. package/dist/fnet/index.9567fa9x.js +1 -0
  13. package/dist/fnet/index.gh75wt1m.js +1 -0
  14. package/dist/fnet/index.hzsfswvp.js +1 -0
  15. package/dist/fnet/index.jgpc3grc.js +1 -0
  16. package/dist/fnet/index.js +19 -1
  17. package/dist/fnet/index.p0zb7e1b.js +1 -0
  18. package/dist/fnet/index.r19p3bpa.js +2 -0
  19. package/dist/fnet/index.r82rtnmz.js +1 -0
  20. package/dist/fnet/index.s662t98v.js +1 -0
  21. package/dist/fnet/index.w74dpnpn.js +1 -0
  22. package/dist/fnet/index.xeaw5xa9.js +1 -0
  23. package/dist/fnet/index.zm4kesg6.js +1 -0
  24. package/dist/fnode/index.05n3mvs9.js +2 -0
  25. package/dist/fnode/index.2hc9tbyx.js +1 -0
  26. package/dist/fnode/index.2pnjg6dc.js +1 -0
  27. package/dist/fnode/index.9qgtmxq3.js +2 -0
  28. package/dist/fnode/index.b1q7y05p.js +1 -0
  29. package/dist/fnode/index.bhapgrs7.js +1 -0
  30. package/dist/fnode/index.cvxrf34y.js +2 -0
  31. package/dist/fnode/index.dp9wyahp.js +1 -0
  32. package/dist/fnode/index.eqxmcpdc.js +1 -0
  33. package/dist/fnode/index.f2tb0x3t.js +1 -0
  34. package/dist/fnode/index.fbzv6wwf.js +1 -0
  35. package/dist/fnode/index.gazd9raq.js +1 -0
  36. package/dist/fnode/index.hv4s25f0.js +3 -0
  37. package/dist/fnode/index.j5z7dtsx.js +1 -0
  38. package/dist/fnode/index.js +9 -1
  39. package/dist/fnode/index.kb4e4bxf.js +1 -0
  40. package/dist/fnode/index.qn0schqp.js +1 -0
  41. package/dist/fnode/index.rht29phd.js +1 -0
  42. package/dist/fnode/index.rzsfmek6.js +3 -0
  43. package/dist/fnode/index.s0nk6cv8.js +4 -0
  44. package/dist/fnode/index.s66v6wt4.js +1 -0
  45. package/dist/fnode/index.sv7v0y60.js +1 -0
  46. package/dist/fnode/index.tgkhgnrp.js +1 -0
  47. package/dist/fnode/index.vq706f75.js +1 -0
  48. package/dist/fnode/index.y8pvdcny.js +1 -0
  49. package/dist/fnode/index.z4vz93ww.js +1 -0
  50. package/dist/frun/index.js +1 -1
  51. package/dist/fservice/index.js +18 -1
  52. package/dist/fservice/index.q01yvaz0.js +2 -0
  53. package/package.json +21 -18
  54. package/readme.md +62 -15
  55. package/template/fnet/core/object.js +10 -10
  56. package/template/fnet/node/package.json.njk +6 -3
  57. package/template/fnet/node/src/cli/index.js.njk +5 -315
  58. package/template/fnet/node/src/cli/index.js.v1.njk +318 -0
  59. package/template/fnet/node/src/cli/v2/core/args-parser.njk +10 -0
  60. package/template/fnet/node/src/cli/v2/core/imports.njk +31 -0
  61. package/template/fnet/node/src/cli/v2/core/run-wrapper.njk +25 -0
  62. package/template/fnet/node/src/cli/v2/index.js.njk +184 -0
  63. package/template/fnet/node/src/cli/v2/modes/default/extend.njk +11 -0
  64. package/template/fnet/node/src/cli/v2/modes/default/standard.njk +20 -0
  65. package/template/fnet/node/src/cli/v2/modes/http/builtin.njk +66 -0
  66. package/template/fnet/node/src/cli/v2/modes/http/imports.njk +9 -0
  67. package/template/fnet/node/src/cli/v2/modes/http/index.njk +12 -0
  68. package/template/fnet/node/src/cli/v2/modes/mcp/imports.njk +33 -0
  69. package/template/fnet/node/src/cli/v2/modes/mcp/index.njk +30 -0
  70. package/template/fnet/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
  71. package/template/fnet/node/src/cli/v2/modes/mcp/tool-handlers.njk +46 -0
  72. package/template/fnet/node/src/cli/v2/modes/mcp/transport-http.njk +222 -0
  73. package/template/fnet/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
  74. package/template/fnet/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
  75. package/template/fnet/node/src/cli/v2/modes/pipeline/index.njk +113 -0
  76. package/template/fnet/node/src/cli/v2/modes/webhook/imports.njk +9 -0
  77. package/template/fnet/node/src/cli/v2/modes/webhook/index.njk +127 -0
  78. package/template/fnet/node/src/cli/v2/modes/websocket/imports.njk +15 -0
  79. package/template/fnet/node/src/cli/v2/modes/websocket/index.njk +104 -0
  80. package/template/fnet/node/src/default/blocks/assign.js.njk +10 -48
  81. package/template/fnet/node/src/default/blocks/call.js.njk +110 -156
  82. package/template/fnet/node/src/default/blocks/for.js.njk +58 -74
  83. package/template/fnet/node/src/default/blocks/form.js.njk +21 -59
  84. package/template/fnet/node/src/default/blocks/http.js.njk +135 -0
  85. package/template/fnet/node/src/default/blocks/modules.js.njk +10 -52
  86. package/template/fnet/node/src/default/blocks/new.js.njk +40 -85
  87. package/template/fnet/node/src/default/blocks/next.js.njk +10 -32
  88. package/template/fnet/node/src/default/blocks/output.js.njk +10 -48
  89. package/template/fnet/node/src/default/blocks/parallel.js.njk +64 -0
  90. package/template/fnet/node/src/default/blocks/pipeline.js.njk +109 -0
  91. package/template/fnet/node/src/default/blocks/raise.js.njk +9 -25
  92. package/template/fnet/node/src/default/blocks/retry.js.njk +70 -0
  93. package/template/fnet/node/src/default/blocks/return.js.njk +13 -25
  94. package/template/fnet/node/src/default/blocks/schedule.js.njk +66 -0
  95. package/template/fnet/node/src/default/blocks/signal.js.njk +10 -32
  96. package/template/fnet/node/src/default/blocks/steps.js.njk +32 -48
  97. package/template/fnet/node/src/default/blocks/switch.js.njk +37 -67
  98. package/template/fnet/node/src/default/blocks/tryexcept.js.njk +58 -52
  99. package/template/fnet/node/src/default/blocks/wait.js.njk +10 -30
  100. package/template/fnet/node/src/default/input.args.js.njk +5 -2
  101. package/template/fnet/node/src/default/macros/block-body-header.js.njk +1 -1
  102. package/template/fnet/node/src/default/macros/block-header.js.njk +0 -3
  103. package/template/fnet/node/src/default/macros/block-next.js.njk +1 -1
  104. package/template/fnet/node/src/default/macros/block-run-footer.js.njk +1 -1
  105. package/template/fnet/node/src/default/macros/block-run-header.js.njk +14 -2
  106. package/template/fnet/node/src/default/macros/page.js.njk +1 -1
  107. package/template/fnet/node/src/default/types/block.js.njk +133 -0
  108. package/template/fnet/node/src/default/workflow.js.njk +8 -6
  109. package/template/fnode/node/package.json.njk +5 -4
  110. package/template/fnode/node/src/cli/index.js.njk +3 -397
  111. package/template/fnode/node/src/cli/index.js.v1.njk +464 -0
  112. package/template/fnode/node/src/cli/v2/core/args-parser.njk +10 -0
  113. package/template/fnode/node/src/cli/v2/core/imports.njk +29 -0
  114. package/template/fnode/node/src/cli/v2/core/run-wrapper.njk +25 -0
  115. package/template/fnode/node/src/cli/v2/index.js.njk +184 -0
  116. package/template/fnode/node/src/cli/v2/modes/default/extend.njk +11 -0
  117. package/template/fnode/node/src/cli/v2/modes/default/standard.njk +19 -0
  118. package/template/fnode/node/src/cli/v2/modes/http/builtin.njk +61 -0
  119. package/template/fnode/node/src/cli/v2/modes/http/imports.njk +9 -0
  120. package/template/fnode/node/src/cli/v2/modes/http/index.njk +12 -0
  121. package/template/fnode/node/src/cli/v2/modes/mcp/imports.njk +33 -0
  122. package/template/fnode/node/src/cli/v2/modes/mcp/index.njk +30 -0
  123. package/template/fnode/node/src/cli/v2/modes/mcp/server-setup.njk +15 -0
  124. package/template/fnode/node/src/cli/v2/modes/mcp/tool-handlers.njk +41 -0
  125. package/template/fnode/node/src/cli/v2/modes/mcp/transport-http.njk +212 -0
  126. package/template/fnode/node/src/cli/v2/modes/mcp/transport-stdio.njk +9 -0
  127. package/template/fnode/node/src/cli/v2/modes/pipeline/imports.njk +9 -0
  128. package/template/fnode/node/src/cli/v2/modes/pipeline/index.njk +103 -0
  129. package/template/fnode/node/src/cli/v2/modes/webhook/imports.njk +9 -0
  130. package/template/fnode/node/src/cli/v2/modes/webhook/index.njk +122 -0
  131. package/template/fnode/node/src/cli/v2/modes/websocket/imports.njk +15 -0
  132. package/template/fnode/node/src/cli/v2/modes/websocket/index.njk +99 -0
  133. package/template/fnode/node/src/default/input.args.js.njk +6 -2
  134. package/dist/fnet/index.-SGbq2cI.js +0 -1
  135. package/dist/fnet/index.B5XE4ChJ.js +0 -1
  136. package/dist/fnet/index.Bft2w7m3.js +0 -1
  137. package/dist/fnet/index.BuYxdKtK.js +0 -1
  138. package/dist/fnet/index.C0YpfQ5j.js +0 -1
  139. package/dist/fnet/index.C2S9JYhS.js +0 -1
  140. package/dist/fnet/index.C7saWH6d.js +0 -1
  141. package/dist/fnet/index.CDct_kkF.js +0 -1
  142. package/dist/fnet/index.CMC8mlye.js +0 -1
  143. package/dist/fnet/index.CmMM-Ek9.js +0 -1
  144. package/dist/fnet/index.CuMyez3E.js +0 -1
  145. package/dist/fnet/index.CzAV0S36.js +0 -1
  146. package/dist/fnet/index.D2N9YZmA.js +0 -1
  147. package/dist/fnet/index.D3p7pncT.js +0 -1
  148. package/dist/fnet/index.DG8TqL-1.js +0 -1
  149. package/dist/fnet/index.DI3yyTtl.js +0 -1
  150. package/dist/fnet/index.DOYkqsYT.js +0 -1
  151. package/dist/fnet/index.DWpw12No.js +0 -1
  152. package/dist/fnet/index.DrwlOzAe.js +0 -1
  153. package/dist/fnet/index.Q-CYRcna.js +0 -1
  154. package/dist/fnet/index.W6RYgypK.js +0 -1
  155. package/dist/fnet/index.xd8c7XMr.js +0 -1
  156. package/dist/fnode/index.-SGbq2cI.js +0 -1
  157. package/dist/fnode/index.B5XE4ChJ.js +0 -1
  158. package/dist/fnode/index.B8gal9up.js +0 -1
  159. package/dist/fnode/index.BU1440aO.js +0 -1
  160. package/dist/fnode/index.BcGYSPne.js +0 -1
  161. package/dist/fnode/index.Bft2w7m3.js +0 -1
  162. package/dist/fnode/index.BuYxdKtK.js +0 -1
  163. package/dist/fnode/index.C2S9JYhS.js +0 -1
  164. package/dist/fnode/index.C7saWH6d.js +0 -1
  165. package/dist/fnode/index.CDct_kkF.js +0 -1
  166. package/dist/fnode/index.CMC8mlye.js +0 -1
  167. package/dist/fnode/index.CmMM-Ek9.js +0 -1
  168. package/dist/fnode/index.CuMyez3E.js +0 -1
  169. package/dist/fnode/index.CzAV0S36.js +0 -1
  170. package/dist/fnode/index.D2N9YZmA.js +0 -1
  171. package/dist/fnode/index.D3p7pncT.js +0 -1
  172. package/dist/fnode/index.DG8TqL-1.js +0 -1
  173. package/dist/fnode/index.DI3yyTtl.js +0 -1
  174. package/dist/fnode/index.DWpw12No.js +0 -1
  175. package/dist/fnode/index.DrwlOzAe.js +0 -1
  176. package/dist/fnode/index.N_a5FdgA.js +0 -1
  177. package/dist/fnode/index.Q-CYRcna.js +0 -1
  178. package/dist/fnode/index.UNoFg95r.js +0 -1
  179. package/dist/fnode/index.W6RYgypK.js +0 -1
  180. package/dist/fnode/index.xd8c7XMr.js +0 -1
  181. package/template/fnet/core/print.js +0 -1
  182. package/template/fnet/node/src/default/macros/workflow-header.js.njk +0 -7
@@ -0,0 +1,12 @@
1
+ {# ============================================================================
2
+ HTTP MODE - MAIN
3
+ ============================================================================
4
+ HTTP REST API mode using Node.js built-in http module
5
+ Zero dependencies, simple and fast
6
+ ============================================================================ #}
7
+
8
+ if (cliMode === 'http') {
9
+ // HTTP mode code
10
+ {% include "./builtin.njk" %}
11
+ }
12
+
@@ -0,0 +1,33 @@
1
+ {# ============================================================================
2
+ MCP MODE - IMPORTS
3
+ ============================================================================
4
+ MCP SDK dependencies for Model Context Protocol support
5
+ Supports both ESM and CJS formats
6
+ ============================================================================ #}
7
+
8
+ {% if atom.doc.features.project.format==='esm' %}
9
+ {# ESM Format #}
10
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
+ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
14
+ import express from "express";
15
+ {% if atom.doc.features.cli.mcp.ws===true %}
16
+ import expressWs from "express-ws";
17
+ {% endif %}
18
+ {# crypto imported in shared section #}
19
+
20
+ {% elif atom.doc.features.project.format==='cjs' %}
21
+ {# CJS Format #}
22
+ const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
23
+ const { ListToolsRequestSchema, CallToolRequestSchema } = require("@modelcontextprotocol/sdk/types.js");
24
+ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
25
+ const { StreamableHTTPServerTransport } = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
26
+ const express = require("express");
27
+ {% if atom.doc.features.cli.mcp.ws===true %}
28
+ const expressWs = require("express-ws");
29
+ {% endif %}
30
+ {# crypto imported in shared section #}
31
+
32
+ {% endif %}
33
+
@@ -0,0 +1,30 @@
1
+ {# ============================================================================
2
+ MCP MODE - MAIN
3
+ ============================================================================
4
+ Model Context Protocol mode orchestrator
5
+ Supports multiple transports: STDIO, HTTP (future: WebSocket, SSE, gRPC)
6
+ ============================================================================ #}
7
+
8
+ if (cliMode === 'mcp') {
9
+ // MCP mode code
10
+ {% include "./server-setup.njk" %}
11
+ {% include "./tool-handlers.njk" %}
12
+
13
+ // Get transport type from arguments
14
+ const transportType = args['mcp-transport'] || args.mcp_transport || 'stdio';
15
+ let transport;
16
+
17
+ if (transportType === 'stdio') {
18
+ {% include "./transport-stdio.njk" %}
19
+ } else if (transportType === 'http') {
20
+ {% include "./transport-http.njk" %}
21
+ } else {
22
+ console.error(`Unknown MCP transport type: ${transportType}`);
23
+ console.error(`Supported types: stdio, http`);
24
+ process.exit(1);
25
+ }
26
+
27
+ await server.connect(transport);
28
+ return;
29
+ }
30
+
@@ -0,0 +1,15 @@
1
+ {# ============================================================================
2
+ MCP MODE - SERVER SETUP
3
+ ============================================================================
4
+ Initialize MCP server with capabilities
5
+ ============================================================================ #}
6
+
7
+ const server = new Server({
8
+ name: "{{atom.doc.features.cli.mcp.name or atom.doc.name}}",
9
+ version: "{{atom.doc.version or '0.0.1'}}"
10
+ }, {
11
+ capabilities: {
12
+ tools: {}
13
+ }
14
+ });
15
+
@@ -0,0 +1,46 @@
1
+ {# ============================================================================
2
+ MCP MODE - TOOL HANDLERS
3
+ ============================================================================
4
+ Register MCP tool handlers for listing and executing tools
5
+ ============================================================================ #}
6
+
7
+ // Define available tools
8
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
9
+ return {
10
+ tools: [{
11
+ name: "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}",
12
+ description: `{{atom.doc.features.cli.mcp.tool.description or atom.doc.description}}`,
13
+ inputSchema: inputSchema
14
+ }]
15
+ };
16
+ });
17
+
18
+ // Handle tool execution
19
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
20
+ if (request.params.name === "{{atom.doc.features.cli.mcp.tool.name or atom.doc.name}}") {
21
+ try {
22
+ {% if atom.doc.features.cli.extend===true %}
23
+ const result = await runExtended(request.params.arguments, { Engine });
24
+ {% else %}
25
+ const engine = new Engine();
26
+ const result = await engine.run(request.params.arguments);
27
+ {% endif %}
28
+ return {
29
+ content: [{
30
+ type: "text",
31
+ text: JSON.stringify(result)
32
+ }]
33
+ };
34
+ } catch (error) {
35
+ return {
36
+ content: [{
37
+ type: "text",
38
+ text: `Error: ${error.message}`
39
+ }],
40
+ isError: true
41
+ };
42
+ }
43
+ }
44
+ throw new Error("Tool not found");
45
+ });
46
+
@@ -0,0 +1,222 @@
1
+ {# ============================================================================
2
+ MCP MODE - HTTP TRANSPORT
3
+ ============================================================================
4
+ Streamable HTTP transport for MCP protocol with session management
5
+ Official transport as of MCP 2025-03-26
6
+ ============================================================================ #}
7
+
8
+ // Use Streamable HTTP transport (official transport as of MCP 2025-03-26)
9
+ const app = express();
10
+ {% if atom.doc.features.cli.mcp.ws===true %}
11
+ // Enable WebSocket support
12
+ expressWs(app);
13
+ {% endif %}
14
+ app.use(express.json());
15
+
16
+ const port = args['cli-port'] || args.cli_port || 3000;
17
+ const host = args['cli-host'] || args.cli_host || '0.0.0.0';
18
+ const basePath = '{{atom.doc.features.cli.mcp.path or 'mcp'}}';
19
+ const mcpEndpoint = `/${basePath}`; // MCP protocol endpoint
20
+ const verbose = args['cli-verbose'] || args.cli_verbose || false;
21
+
22
+ // Optional endpoints (enabled via config)
23
+ const httpEnabled = {{atom.doc.features.cli.mcp.http or false}};
24
+ const wsEnabled = {{atom.doc.features.cli.mcp.ws or false}};
25
+ const httpEndpoint = `/${basePath}/input`; // HTTP input endpoint
26
+ const wsEndpoint = `/${basePath}/ws`; // WebSocket endpoint
27
+
28
+ // Verbose logging helper
29
+ const log = (...args) => {
30
+ if (verbose) {
31
+ console.log('[MCP]', new Date().toISOString(), ...args);
32
+ }
33
+ };
34
+
35
+ // Map to store transports by session ID
36
+ const transports = {};
37
+
38
+ // Handle POST requests for client-to-server communication
39
+ app.post(mcpEndpoint, async (req, res) => {
40
+ // Check for existing session ID
41
+ const sessionId = req.get('Mcp-Session-Id');
42
+
43
+ log('POST request received');
44
+ log('Session ID:', sessionId || 'none');
45
+ log('Request body:', JSON.stringify(req.body, null, 2));
46
+
47
+ let transport;
48
+
49
+ if (sessionId && transports[sessionId]) {
50
+ // Reuse existing transport
51
+ log('Reusing existing transport for session:', sessionId);
52
+ transport = transports[sessionId];
53
+ } else if (!sessionId && req.body && (req.body.method === 'initialize' ||
54
+ (Array.isArray(req.body) && req.body.some(item => item.method === 'initialize')))) {
55
+ // New initialization request
56
+ log('Creating new transport for initialization');
57
+ transport = new StreamableHTTPServerTransport({
58
+ sessionIdGenerator: () => {
59
+ // Generate cryptographically secure session ID
60
+ return crypto.randomBytes(16).toString('hex');
61
+ },
62
+ onsessioninitialized: (sessionId) => {
63
+ // Store the transport by session ID
64
+ log('Session initialized:', sessionId);
65
+ transports[sessionId] = transport;
66
+ }
67
+ });
68
+
69
+ // Clean up transport when closed
70
+ transport.onclose = () => {
71
+ if (transport.sessionId) {
72
+ log('Transport closed, cleaning up session:', transport.sessionId);
73
+ delete transports[transport.sessionId];
74
+ }
75
+ };
76
+
77
+ // Connect to the MCP server
78
+ await server.connect(transport);
79
+ log('Server connected to transport');
80
+ } else {
81
+ // Invalid request
82
+ log('Invalid request - no valid session ID');
83
+ return res.status(400).json({
84
+ jsonrpc: '2.0',
85
+ error: {
86
+ code: -32000,
87
+ message: 'Bad Request: No valid session ID provided'
88
+ },
89
+ id: null
90
+ });
91
+ }
92
+
93
+ // Handle the request
94
+ log('Handling request with transport');
95
+ await transport.handleRequest(req, res, req.body);
96
+ });
97
+
98
+ // Reusable handler for GET and DELETE requests
99
+ const handleSessionRequest = async (req, res) => {
100
+ const sessionId = req.get('Mcp-Session-Id');
101
+ log(`${req.method} request received for session:`, sessionId || 'none');
102
+
103
+ if (!sessionId || !transports[sessionId]) {
104
+ log('Invalid or missing session ID');
105
+ return res.status(400).send('Invalid or missing session ID');
106
+ }
107
+
108
+ const transport = transports[sessionId];
109
+ log('Handling request with existing transport');
110
+ await transport.handleRequest(req, res);
111
+ };
112
+
113
+ // Handle GET requests for server-to-client notifications via SSE
114
+ app.get(mcpEndpoint, handleSessionRequest);
115
+
116
+ // Handle DELETE requests for session termination
117
+ app.delete(mcpEndpoint, handleSessionRequest);
118
+
119
+ {% if atom.doc.features.cli.mcp.http===true %}
120
+ // HTTP input endpoint (enabled via config: cli.mcp.http: true)
121
+ app.post(httpEndpoint, async (req, res) => {
122
+ log('HTTP input request received');
123
+ log('Request body:', JSON.stringify(req.body, null, 2));
124
+
125
+ try {
126
+ // Call the Engine with the request body
127
+ {% if atom.doc.features.cli.extend===true %}
128
+ const result = await runExtended(req.body, { Engine });
129
+ {% else %}
130
+ const engine = new Engine();
131
+ const result = await engine.run(req.body);
132
+ {% endif %}
133
+
134
+ log('HTTP input processed successfully');
135
+ log('Result:', JSON.stringify(result, null, 2));
136
+
137
+ // Return the result as JSON
138
+ res.json(result);
139
+ } catch (error) {
140
+ log('HTTP input processing error:', error.message);
141
+
142
+ // Return error response
143
+ res.status(500).json({
144
+ error: error.message,
145
+ stack: verbose ? error.stack : undefined
146
+ });
147
+ }
148
+ });
149
+ {% endif %}
150
+
151
+ {% if atom.doc.features.cli.mcp.ws===true %}
152
+ // WebSocket endpoint (enabled via config: cli.mcp.ws: true)
153
+ // Map to store WebSocket clients
154
+ const wsClients = new Set();
155
+
156
+ app.ws(wsEndpoint, (ws, req) => {
157
+ log('WebSocket connection established');
158
+ wsClients.add(ws);
159
+
160
+ // Handle incoming messages
161
+ ws.on('message', async (message) => {
162
+ log('WebSocket message received:', message);
163
+
164
+ try {
165
+ // Parse JSON message
166
+ const data = JSON.parse(message);
167
+
168
+ // Call the Engine
169
+ {% if atom.doc.features.cli.extend===true %}
170
+ const result = await runExtended(data, { Engine });
171
+ {% else %}
172
+ const engine = new Engine();
173
+ const result = await engine.run(data);
174
+ {% endif %}
175
+
176
+ log('WebSocket message processed successfully');
177
+ log('Result:', JSON.stringify(result, null, 2));
178
+
179
+ // Send result back to client
180
+ ws.send(JSON.stringify(result));
181
+ } catch (error) {
182
+ log('WebSocket message processing error:', error.message);
183
+
184
+ // Send error response
185
+ ws.send(JSON.stringify({
186
+ error: error.message,
187
+ stack: verbose ? error.stack : undefined
188
+ }));
189
+ }
190
+ });
191
+
192
+ // Handle connection close
193
+ ws.on('close', () => {
194
+ log('WebSocket connection closed');
195
+ wsClients.delete(ws);
196
+ });
197
+
198
+ // Handle errors
199
+ ws.on('error', (error) => {
200
+ log('WebSocket error:', error.message);
201
+ wsClients.delete(ws);
202
+ });
203
+ });
204
+ {% endif %}
205
+
206
+ app.listen(port, host, () => {
207
+ console.log(`MCP server started with Streamable HTTP transport`);
208
+ console.log(`MCP endpoint: http://${host}:${port}${mcpEndpoint}`);
209
+ {% if atom.doc.features.cli.mcp.http===true %}
210
+ console.log(`HTTP endpoint: http://${host}:${port}${httpEndpoint}`);
211
+ {% endif %}
212
+ {% if atom.doc.features.cli.mcp.ws===true %}
213
+ console.log(`WebSocket endpoint: ws://${host}:${port}${wsEndpoint}`);
214
+ {% endif %}
215
+ console.log(`Listening on ${host}:${port}`);
216
+ if (verbose) {
217
+ console.log(`Verbose logging enabled`);
218
+ }
219
+ });
220
+
221
+ return;
222
+
@@ -0,0 +1,9 @@
1
+ {# ============================================================================
2
+ MCP MODE - STDIO TRANSPORT
3
+ ============================================================================
4
+ Standard input/output transport for MCP protocol
5
+ ============================================================================ #}
6
+
7
+ // Use stdio transport
8
+ transport = new StdioServerTransport();
9
+
@@ -0,0 +1,9 @@
1
+ {# ============================================================================
2
+ PIPELINE MODE - IMPORTS
3
+ ============================================================================
4
+ No external dependencies needed - uses native Node.js streams
5
+ stdin/stdout are available globally
6
+ ============================================================================ #}
7
+
8
+ {# No imports needed for pipeline mode - stdin/stdout are native #}
9
+
@@ -0,0 +1,113 @@
1
+ {# ============================================================================
2
+ PIPELINE MODE - MAIN
3
+ ============================================================================
4
+ Unix pipeline integration via stdin/stdout
5
+ Supports line-by-line processing and full input mode
6
+ ============================================================================ #}
7
+
8
+ if (cliMode === 'pipeline') {
9
+ // Pipeline mode code
10
+ const pipelineMode = args['pipeline-mode'] || args.pipeline_mode || 'lines';
11
+ const pipelineFormat = args['pipeline-format'] || args.pipeline_format || 'json';
12
+
13
+ if (pipelineMode === 'lines') {
14
+ // Line-by-line processing (default)
15
+ {% if atom.doc.features.project.format==='esm' %}
16
+ const readline = await import('readline');
17
+ {% elif atom.doc.features.project.format==='cjs' %}
18
+ const readline = require('readline');
19
+ {% endif %}
20
+ const rl = readline.createInterface({
21
+ input: process.stdin,
22
+ output: process.stdout,
23
+ terminal: false
24
+ });
25
+
26
+ rl.on('line', async (line) => {
27
+ try {
28
+ // Parse input based on format
29
+ let input;
30
+ if (pipelineFormat === 'json') {
31
+ input = JSON.parse(line);
32
+ } else {
33
+ input = { data: line };
34
+ }
35
+
36
+ // Validate input
37
+ // TODO: Add schema validation if needed
38
+
39
+ // Process with Engine
40
+ {% if atom.doc.features.cli.extend===true %}
41
+ const result = await runExtended(input, { Engine });
42
+ {% else %}
43
+ const engine = new Engine();
44
+ const result = await engine.run(input);
45
+ {% endif %}
46
+
47
+ // Output result
48
+ if (pipelineFormat === 'json') {
49
+ console.log(JSON.stringify(result));
50
+ } else {
51
+ console.log(result);
52
+ }
53
+ } catch (error) {
54
+ console.error(`Error processing line: ${error.message}`);
55
+ }
56
+ });
57
+
58
+ rl.on('close', () => {
59
+ process.exit(0);
60
+ });
61
+
62
+ } else if (pipelineMode === 'full') {
63
+ // Full input processing
64
+ let inputData = '';
65
+
66
+ process.stdin.on('data', (chunk) => {
67
+ inputData += chunk;
68
+ });
69
+
70
+ process.stdin.on('end', async () => {
71
+ try {
72
+ // Parse input based on format
73
+ let input;
74
+ if (pipelineFormat === 'json') {
75
+ input = JSON.parse(inputData);
76
+ } else {
77
+ input = { data: inputData };
78
+ }
79
+
80
+ // Validate input
81
+ // TODO: Add schema validation if needed
82
+
83
+ // Process with Engine
84
+ {% if atom.doc.features.cli.extend===true %}
85
+ const result = await runExtended(input, { Engine });
86
+ {% else %}
87
+ const engine = new Engine();
88
+ const result = await engine.run(input);
89
+ {% endif %}
90
+
91
+ // Output result
92
+ if (pipelineFormat === 'json') {
93
+ console.log(JSON.stringify(result, null, 2));
94
+ } else {
95
+ console.log(result);
96
+ }
97
+
98
+ process.exit(0);
99
+ } catch (error) {
100
+ console.error(`Error processing input: ${error.message}`);
101
+ process.exit(1);
102
+ }
103
+ });
104
+
105
+ } else {
106
+ console.error(`Unknown pipeline mode: ${pipelineMode}`);
107
+ console.error(`Supported modes: lines, full`);
108
+ process.exit(1);
109
+ }
110
+
111
+ return;
112
+ }
113
+
@@ -0,0 +1,9 @@
1
+ {# ============================================================================
2
+ WEBHOOK MODE - IMPORTS
3
+ ============================================================================
4
+ No mode-specific imports needed
5
+ http and crypto modules are imported in shared imports section
6
+ ============================================================================ #}
7
+
8
+ {# http and crypto modules imported in shared section #}
9
+
@@ -0,0 +1,127 @@
1
+ {# ============================================================================
2
+ WEBHOOK MODE - IMPLEMENTATION
3
+ ============================================================================
4
+ Event-driven HTTP webhook server with HMAC signature verification
5
+ ============================================================================ #}
6
+
7
+ if (cliMode === 'webhook') {
8
+ const port = args['cli-port'] || args.cli_port || 3000;
9
+ const webhookPath = args['webhook-path'] || args.webhook_path || '/webhook';
10
+ const webhookSecret = args['webhook-secret'] || args.webhook_secret || process.env.WEBHOOK_SECRET;
11
+ const signatureHeader = args['webhook-signature-header'] || args.webhook_signature_header || 'x-hub-signature-256';
12
+
13
+ const server = http.createServer(async (req, res) => {
14
+ // CORS headers
15
+ res.setHeader('Access-Control-Allow-Origin', '*');
16
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
17
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, ' + signatureHeader);
18
+
19
+ // Handle OPTIONS preflight
20
+ if (req.method === 'OPTIONS') {
21
+ res.writeHead(200);
22
+ res.end();
23
+ return;
24
+ }
25
+
26
+ // Only accept POST requests
27
+ if (req.method !== 'POST') {
28
+ res.writeHead(405, { 'Content-Type': 'application/json' });
29
+ res.end(JSON.stringify({ error: 'Method not allowed. Use POST.' }));
30
+ return;
31
+ }
32
+
33
+ // Only accept webhook path
34
+ if (req.url !== webhookPath) {
35
+ res.writeHead(404, { 'Content-Type': 'application/json' });
36
+ res.end(JSON.stringify({ error: 'Not found. Use ' + webhookPath }));
37
+ return;
38
+ }
39
+
40
+ // Read request body
41
+ let body = '';
42
+ req.on('data', chunk => {
43
+ body += chunk.toString();
44
+ });
45
+
46
+ req.on('end', async () => {
47
+ try {
48
+ // Verify signature if secret is provided
49
+ if (webhookSecret) {
50
+ const signature = req.headers[signatureHeader.toLowerCase()];
51
+
52
+ if (!signature) {
53
+ res.writeHead(401, { 'Content-Type': 'application/json' });
54
+ res.end(JSON.stringify({ error: 'Missing signature header: ' + signatureHeader }));
55
+ return;
56
+ }
57
+
58
+ // Calculate expected signature (HMAC-SHA256)
59
+ const hmac = crypto.createHmac('sha256', webhookSecret);
60
+ hmac.update(body);
61
+ const expectedSignature = 'sha256=' + hmac.digest('hex');
62
+
63
+ // Compare signatures (timing-safe)
64
+ const signatureBuffer = Buffer.from(signature);
65
+ const expectedBuffer = Buffer.from(expectedSignature);
66
+
67
+ if (signatureBuffer.length !== expectedBuffer.length ||
68
+ !crypto.timingSafeEqual(signatureBuffer, expectedBuffer)) {
69
+ res.writeHead(401, { 'Content-Type': 'application/json' });
70
+ res.end(JSON.stringify({ error: 'Invalid signature' }));
71
+ return;
72
+ }
73
+ }
74
+
75
+ // Parse JSON payload
76
+ let payload;
77
+ try {
78
+ payload = JSON.parse(body);
79
+ } catch (e) {
80
+ res.writeHead(400, { 'Content-Type': 'application/json' });
81
+ res.end(JSON.stringify({ error: 'Invalid JSON payload' }));
82
+ return;
83
+ }
84
+
85
+ // Process webhook with Engine
86
+ {% if atom.doc.features.cli.extend===true %}
87
+ const result = await runExtended(payload, { Engine });
88
+ {% else %}
89
+ const engine = new Engine();
90
+ const result = await engine.run(payload);
91
+ {% endif %}
92
+
93
+ // Send response
94
+ res.writeHead(200, { 'Content-Type': 'application/json' });
95
+ res.end(JSON.stringify(result));
96
+
97
+ } catch (error) {
98
+ res.writeHead(500, { 'Content-Type': 'application/json' });
99
+ res.end(JSON.stringify({
100
+ error: error.message,
101
+ type: 'processing_error'
102
+ }));
103
+ }
104
+ });
105
+
106
+ req.on('error', (error) => {
107
+ res.writeHead(500, { 'Content-Type': 'application/json' });
108
+ res.end(JSON.stringify({
109
+ error: error.message,
110
+ type: 'request_error'
111
+ }));
112
+ });
113
+ });
114
+
115
+ server.listen(port, () => {
116
+ console.log(`Webhook server started on port ${port}`);
117
+ console.log(`Webhook endpoint: ${webhookPath}`);
118
+ if (webhookSecret) {
119
+ console.log(`Signature verification: enabled (header: ${signatureHeader})`);
120
+ } else {
121
+ console.log(`Signature verification: disabled (no secret provided)`);
122
+ }
123
+ });
124
+
125
+ return;
126
+ }
127
+
@@ -0,0 +1,15 @@
1
+ {# ============================================================================
2
+ WEBSOCKET MODE - IMPORTS
3
+ ============================================================================
4
+ WebSocket server dependencies using 'ws' library
5
+ Supports both ESM and CJS formats
6
+ ============================================================================ #}
7
+
8
+ {% if atom.doc.features.project.format==='esm' %}
9
+ // Using ws library for WebSocket mode
10
+ import { WebSocketServer } from 'ws';
11
+ {% elif atom.doc.features.project.format==='cjs' %}
12
+ // Using ws library for WebSocket mode
13
+ const { WebSocketServer } = require('ws');
14
+ {% endif %}
15
+