@synergenius/flow-weaver 0.20.7 → 0.21.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.
- package/dist/api/command-runner.d.ts +13 -0
- package/dist/api/command-runner.js +217 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/cli/flow-weaver.mjs +24064 -38374
- package/dist/cli/index.js +0 -60
- package/dist/doc-metadata/extractors/cli-commands.js +37 -56
- package/dist/doc-metadata/extractors/mcp-tools.d.ts +1 -1
- package/dist/doc-metadata/extractors/mcp-tools.js +1 -213
- package/dist/doc-metadata/types.d.ts +2 -0
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/mcp/index.d.ts +1 -5
- package/dist/mcp/index.js +0 -4
- package/dist/mcp/server.js +3 -55
- package/dist/mcp/types.d.ts +0 -50
- package/dist/mcp/types.js +1 -7
- package/dist/mcp/workflow-executor.js +23 -1
- package/dist/parser.js +5 -1
- package/package.json +1 -2
- package/dist/cli/commands/listen.d.ts +0 -16
- package/dist/cli/commands/listen.js +0 -39
- package/dist/cli/commands/tunnel.d.ts +0 -19
- package/dist/cli/commands/tunnel.js +0 -119
- package/dist/cli/commands/ui.d.ts +0 -16
- package/dist/cli/commands/ui.js +0 -130
- package/dist/cli/tunnel/dispatch.d.ts +0 -18
- package/dist/cli/tunnel/dispatch.js +0 -36
- package/dist/cli/tunnel/file-lock.d.ts +0 -9
- package/dist/cli/tunnel/file-lock.js +0 -36
- package/dist/cli/tunnel/handlers/ast-ops.d.ts +0 -10
- package/dist/cli/tunnel/handlers/ast-ops.js +0 -252
- package/dist/cli/tunnel/handlers/execution.d.ts +0 -7
- package/dist/cli/tunnel/handlers/execution.js +0 -89
- package/dist/cli/tunnel/handlers/file-ops.d.ts +0 -7
- package/dist/cli/tunnel/handlers/file-ops.js +0 -204
- package/dist/cli/tunnel/handlers/mutations.d.ts +0 -7
- package/dist/cli/tunnel/handlers/mutations.js +0 -285
- package/dist/cli/tunnel/handlers/stubs.d.ts +0 -7
- package/dist/cli/tunnel/handlers/stubs.js +0 -143
- package/dist/cli/tunnel/handlers/templates.d.ts +0 -7
- package/dist/cli/tunnel/handlers/templates.js +0 -123
- package/dist/cli/tunnel/path-resolver.d.ts +0 -17
- package/dist/cli/tunnel/path-resolver.js +0 -54
- package/dist/defaults.d.ts +0 -3
- package/dist/defaults.js +0 -3
- package/dist/mcp/editor-connection.d.ts +0 -52
- package/dist/mcp/editor-connection.js +0 -142
- package/dist/mcp/event-buffer.d.ts +0 -62
- package/dist/mcp/event-buffer.js +0 -150
- package/dist/mcp/resources.d.ts +0 -14
- package/dist/mcp/resources.js +0 -55
- package/dist/mcp/tools-editor.d.ts +0 -5
- package/dist/mcp/tools-editor.js +0 -283
package/dist/mcp/server.js
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
import '../extensions/index.js';
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
-
import { DEFAULT_SERVER_URL } from '../defaults.js';
|
|
6
|
-
import { EventBuffer } from './event-buffer.js';
|
|
7
|
-
import { EditorConnection } from './editor-connection.js';
|
|
8
|
-
import { registerEditorTools } from './tools-editor.js';
|
|
9
5
|
import { registerQueryTools } from './tools-query.js';
|
|
10
6
|
import { registerTemplateTools } from './tools-template.js';
|
|
11
7
|
import { registerPatternTools } from './tools-pattern.js';
|
|
@@ -16,61 +12,15 @@ import { registerDocsTools } from './tools-docs.js';
|
|
|
16
12
|
import { registerModelTools } from './tools-model.js';
|
|
17
13
|
import { registerDebugTools } from './tools-debug.js';
|
|
18
14
|
import { registerContextTools } from './tools-context.js';
|
|
19
|
-
import { registerResources } from './resources.js';
|
|
20
15
|
import { registerPrompts } from './prompts.js';
|
|
21
16
|
import { registerPackMcpTools } from './pack-tools.js';
|
|
22
|
-
function parseEventFilterFromEnv() {
|
|
23
|
-
const filter = {};
|
|
24
|
-
const include = process.env.FW_EVENT_INCLUDE;
|
|
25
|
-
if (include) {
|
|
26
|
-
filter.include = include
|
|
27
|
-
.split(',')
|
|
28
|
-
.map((s) => s.trim())
|
|
29
|
-
.filter(Boolean);
|
|
30
|
-
}
|
|
31
|
-
const exclude = process.env.FW_EVENT_EXCLUDE;
|
|
32
|
-
if (exclude) {
|
|
33
|
-
filter.exclude = exclude
|
|
34
|
-
.split(',')
|
|
35
|
-
.map((s) => s.trim())
|
|
36
|
-
.filter(Boolean);
|
|
37
|
-
}
|
|
38
|
-
const dedupeMs = process.env.FW_EVENT_DEDUPE_MS;
|
|
39
|
-
if (dedupeMs) {
|
|
40
|
-
const parsed = parseInt(dedupeMs, 10);
|
|
41
|
-
if (!isNaN(parsed))
|
|
42
|
-
filter.dedupeWindowMs = parsed;
|
|
43
|
-
}
|
|
44
|
-
const maxBuf = process.env.FW_EVENT_MAX;
|
|
45
|
-
if (maxBuf) {
|
|
46
|
-
const parsed = parseInt(maxBuf, 10);
|
|
47
|
-
if (!isNaN(parsed))
|
|
48
|
-
filter.maxBufferSize = parsed;
|
|
49
|
-
}
|
|
50
|
-
return filter;
|
|
51
|
-
}
|
|
52
17
|
export async function startMcpServer(options) {
|
|
53
|
-
const serverUrl = options.server || DEFAULT_SERVER_URL;
|
|
54
|
-
// Parse event filter config from environment variables
|
|
55
|
-
const filterFromEnv = parseEventFilterFromEnv();
|
|
56
|
-
// Use injected deps for testing, or create real ones
|
|
57
|
-
const buffer = options._testDeps?.buffer ?? new EventBuffer(undefined, undefined, filterFromEnv);
|
|
58
|
-
const connection = options._testDeps?.connection ?? new EditorConnection(serverUrl, buffer);
|
|
59
|
-
// Connect to Studio (non-blocking)
|
|
60
|
-
if (!options._testDeps) {
|
|
61
|
-
const log = options.stdio
|
|
62
|
-
? (msg) => process.stderr.write(msg + '\n')
|
|
63
|
-
: // eslint-disable-next-line no-console
|
|
64
|
-
(msg) => console.log(msg);
|
|
65
|
-
connection.connect(log);
|
|
66
|
-
}
|
|
67
18
|
// Create MCP server
|
|
68
19
|
const mcp = new McpServer({
|
|
69
20
|
name: 'flow-weaver',
|
|
70
21
|
version: '1.0.0',
|
|
71
22
|
});
|
|
72
|
-
// Register all tools
|
|
73
|
-
registerEditorTools(mcp, connection, buffer);
|
|
23
|
+
// Register all tools
|
|
74
24
|
registerQueryTools(mcp);
|
|
75
25
|
registerTemplateTools(mcp);
|
|
76
26
|
registerPatternTools(mcp);
|
|
@@ -81,17 +31,15 @@ export async function startMcpServer(options) {
|
|
|
81
31
|
registerModelTools(mcp);
|
|
82
32
|
registerDebugTools(mcp);
|
|
83
33
|
registerContextTools(mcp);
|
|
84
|
-
registerResources(mcp, connection, buffer);
|
|
85
34
|
registerPrompts(mcp);
|
|
86
35
|
await registerPackMcpTools(mcp);
|
|
87
36
|
// Connect transport (only in stdio MCP mode)
|
|
88
|
-
if (
|
|
37
|
+
if (options.stdio) {
|
|
89
38
|
const transport = new StdioServerTransport();
|
|
90
39
|
await mcp.connect(transport);
|
|
91
40
|
}
|
|
92
41
|
}
|
|
93
42
|
export async function mcpServerCommand(options) {
|
|
94
|
-
const serverUrl = options.server || DEFAULT_SERVER_URL;
|
|
95
43
|
// In stdio mode, stdout is reserved for JSON-RPC — log to stderr.
|
|
96
44
|
// In interactive mode, log to stdout (write + flush to survive SIGTERM).
|
|
97
45
|
const log = options.stdio
|
|
@@ -99,7 +47,7 @@ export async function mcpServerCommand(options) {
|
|
|
99
47
|
: (msg) => process.stdout.write(msg + '\n');
|
|
100
48
|
if (!options.stdio) {
|
|
101
49
|
log('Tip: run "flow-weaver mcp-setup" to register with your AI tools.');
|
|
102
|
-
log(
|
|
50
|
+
log('Starting MCP server...');
|
|
103
51
|
}
|
|
104
52
|
await startMcpServer(options);
|
|
105
53
|
if (!options.stdio) {
|
package/dist/mcp/types.d.ts
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import type { io as socketIO } from 'socket.io-client';
|
|
2
|
-
import type { EventBuffer } from './event-buffer.js';
|
|
3
|
-
import type { EditorConnection } from './editor-connection.js';
|
|
4
1
|
/** Options for initializing the MCP server. */
|
|
5
2
|
export interface McpServerOptions {
|
|
6
|
-
/** WebSocket server URL for Studio connection. */
|
|
7
|
-
server?: string;
|
|
8
3
|
/** Whether to use stdio transport instead of SSE. */
|
|
9
4
|
stdio?: boolean;
|
|
10
|
-
/** Internal: injected deps for testing */
|
|
11
|
-
_testDeps?: {
|
|
12
|
-
buffer?: EventBuffer;
|
|
13
|
-
connection?: EditorConnection;
|
|
14
|
-
};
|
|
15
5
|
}
|
|
16
6
|
/** Dependencies injected into tool registration functions for CLI interaction and logging. */
|
|
17
7
|
export interface RegistrationDeps {
|
|
@@ -27,44 +17,4 @@ export interface RegistrationDeps {
|
|
|
27
17
|
/** Resolve the path to the Flow Weaver CLI executable. */
|
|
28
18
|
resolveCliPath: () => string;
|
|
29
19
|
}
|
|
30
|
-
/** Acknowledgement response returned by Studio after processing a command. */
|
|
31
|
-
export interface AckResponse {
|
|
32
|
-
/** Unique identifier correlating the response to its originating request. */
|
|
33
|
-
requestId: string;
|
|
34
|
-
/** Whether the command executed successfully. */
|
|
35
|
-
success: boolean;
|
|
36
|
-
/** The command result payload, present on success. */
|
|
37
|
-
result?: unknown;
|
|
38
|
-
/** Error message, present on failure. */
|
|
39
|
-
error?: string;
|
|
40
|
-
}
|
|
41
|
-
/** A timestamped event stored in the event buffer. */
|
|
42
|
-
export interface BufferedEvent {
|
|
43
|
-
/** The event name (e.g. "fw:node-added", "mcp:status"). */
|
|
44
|
-
event: string;
|
|
45
|
-
/** The event payload. */
|
|
46
|
-
data: unknown;
|
|
47
|
-
/** ISO 8601 timestamp of when the event was buffered. */
|
|
48
|
-
timestamp: string;
|
|
49
|
-
}
|
|
50
|
-
/** Options for configuring the Studio WebSocket connection. */
|
|
51
|
-
export interface EditorConnectionOptions {
|
|
52
|
-
/** Custom socket.io factory, primarily used for injecting mocks in tests. */
|
|
53
|
-
ioFactory?: typeof socketIO;
|
|
54
|
-
/** Timeout in milliseconds for awaiting command acknowledgements. Defaults to 10000. */
|
|
55
|
-
ackTimeout?: number;
|
|
56
|
-
}
|
|
57
|
-
/** Configuration for filtering and deduplicating events in the event buffer. */
|
|
58
|
-
export interface EventFilterConfig {
|
|
59
|
-
/** Event name patterns to include. Empty array means all events are included. Supports trailing `*` for prefix matching. */
|
|
60
|
-
include: string[];
|
|
61
|
-
/** Event name patterns to exclude (applied after include). Supports trailing `*` for prefix matching. */
|
|
62
|
-
exclude: string[];
|
|
63
|
-
/** Collapse duplicate events of the same type within this window in milliseconds. 0 disables deduplication. */
|
|
64
|
-
dedupeWindowMs: number;
|
|
65
|
-
/** Maximum number of events to retain before evicting the oldest. */
|
|
66
|
-
maxBufferSize: number;
|
|
67
|
-
}
|
|
68
|
-
/** Default event filter configuration. Excludes `fw:ack` events, dedupes within 200ms, and retains up to 500 events. */
|
|
69
|
-
export declare const DEFAULT_EVENT_FILTER: EventFilterConfig;
|
|
70
20
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/mcp/types.js
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export const DEFAULT_EVENT_FILTER = {
|
|
3
|
-
include: [],
|
|
4
|
-
exclude: ['fw:ack'],
|
|
5
|
-
dedupeWindowMs: 200,
|
|
6
|
-
maxBufferSize: 500,
|
|
7
|
-
};
|
|
1
|
+
export {};
|
|
8
2
|
//# sourceMappingURL=types.js.map
|
|
@@ -71,7 +71,29 @@ export async function executeWorkflowFromFile(filePath, params, options) {
|
|
|
71
71
|
esModuleInterop: true,
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
|
-
|
|
74
|
+
// When source lives under src/, rewrite relative imports to point to
|
|
75
|
+
// dist/ equivalents so Node.js ESM resolver finds the compiled JS files.
|
|
76
|
+
// This happens with marketplace packs that ship TS source for parsing
|
|
77
|
+
// but only have compiled JS in dist/.
|
|
78
|
+
let transpiledOutput = jsOutput.outputText;
|
|
79
|
+
const srcDir = path.dirname(tmpTsFile);
|
|
80
|
+
if (srcDir.includes(`${path.sep}src${path.sep}`)) {
|
|
81
|
+
const distDir = srcDir.replace(`${path.sep}src${path.sep}`, `${path.sep}dist${path.sep}`);
|
|
82
|
+
transpiledOutput = transpiledOutput.replace(/from\s+['"](\.[^'"]+)['"]/g, (_match, specifier) => {
|
|
83
|
+
const resolvedSrc = path.resolve(srcDir, specifier);
|
|
84
|
+
// Only rewrite if the source .js file doesn't exist but the dist equivalent does
|
|
85
|
+
if (!fs.existsSync(resolvedSrc)) {
|
|
86
|
+
const distEquivalent = resolvedSrc.replace(`${path.sep}src${path.sep}`, `${path.sep}dist${path.sep}`);
|
|
87
|
+
if (fs.existsSync(distEquivalent)) {
|
|
88
|
+
const relative = path.relative(srcDir, distEquivalent);
|
|
89
|
+
const normalized = relative.startsWith('.') ? relative : `./${relative}`;
|
|
90
|
+
return `from '${normalized}'`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return _match;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
fs.writeFileSync(tmpFile, transpiledOutput, 'utf8');
|
|
75
97
|
// Create debugger to capture trace events
|
|
76
98
|
const trace = [];
|
|
77
99
|
const debugger_ = includeTrace
|
package/dist/parser.js
CHANGED
|
@@ -1873,7 +1873,11 @@ export class AnnotationParser {
|
|
|
1873
1873
|
const firstParamName = firstParam.getName();
|
|
1874
1874
|
const firstParamType = firstParam.getType();
|
|
1875
1875
|
const firstParamTypeText = firstParamType.getText();
|
|
1876
|
-
|
|
1876
|
+
// Accept "execute: any" (from compiled .js files where types are stripped)
|
|
1877
|
+
// when JSDoc @param confirms it as a step port.
|
|
1878
|
+
const isExecutePort = firstParamName === 'execute' &&
|
|
1879
|
+
(firstParamTypeText === 'boolean' || (firstParamTypeText === 'any' && config?.startPorts?.['execute']));
|
|
1880
|
+
if (isExecutePort) {
|
|
1877
1881
|
// Correct new format: first param is execute
|
|
1878
1882
|
// Check if JSDoc has explicit metadata override for execute port (from @param annotation)
|
|
1879
1883
|
if (config?.startPorts && config.startPorts['execute']) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -159,7 +159,6 @@
|
|
|
159
159
|
"immer": "^10.2.0",
|
|
160
160
|
"js-yaml": "^4.1.0",
|
|
161
161
|
"picocolors": "^1.1.1",
|
|
162
|
-
"socket.io-client": "^4.8.0",
|
|
163
162
|
"source-map": "^0.7.6",
|
|
164
163
|
"ts-morph": "^21.0.1",
|
|
165
164
|
"ws": "^8.19.0",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { io as socketIO } from 'socket.io-client';
|
|
2
|
-
export interface AckResponse {
|
|
3
|
-
requestId: string;
|
|
4
|
-
success: boolean;
|
|
5
|
-
result?: unknown;
|
|
6
|
-
error?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ListenOptions {
|
|
9
|
-
server?: string;
|
|
10
|
-
/** Override the socket.io-client `io` factory (for testing) */
|
|
11
|
-
ioFactory?: typeof socketIO;
|
|
12
|
-
/** Override stdout (for testing) */
|
|
13
|
-
output?: NodeJS.WritableStream;
|
|
14
|
-
}
|
|
15
|
-
export declare function listenCommand(options: ListenOptions): Promise<void>;
|
|
16
|
-
//# sourceMappingURL=listen.d.ts.map
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { io as socketIO } from 'socket.io-client';
|
|
2
|
-
import { logger } from '../utils/logger.js';
|
|
3
|
-
import { DEFAULT_SERVER_URL } from '../../defaults.js';
|
|
4
|
-
export async function listenCommand(options) {
|
|
5
|
-
const serverUrl = options.server || DEFAULT_SERVER_URL;
|
|
6
|
-
const ioFactory = options.ioFactory ?? socketIO;
|
|
7
|
-
const output = options.output ?? process.stdout;
|
|
8
|
-
const socket = ioFactory(`${serverUrl}/integrations`, {
|
|
9
|
-
query: { clientType: 'cli' },
|
|
10
|
-
transports: ['websocket', 'polling'],
|
|
11
|
-
reconnection: true,
|
|
12
|
-
reconnectionDelay: 1000,
|
|
13
|
-
reconnectionAttempts: Infinity,
|
|
14
|
-
});
|
|
15
|
-
socket.on('connect', () => {
|
|
16
|
-
logger.info(`Connected to ${serverUrl}/integrations`);
|
|
17
|
-
socket.emit('integration:getContext', (ctx) => {
|
|
18
|
-
if (ctx) {
|
|
19
|
-
output.write(JSON.stringify({ event: 'integration:context', data: ctx }) + '\n');
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
socket.on('disconnect', (reason) => {
|
|
24
|
-
logger.warn(`Disconnected: ${reason}`);
|
|
25
|
-
});
|
|
26
|
-
socket.onAny((event, data) => {
|
|
27
|
-
if (event.startsWith('fw:') || event.startsWith('integration:')) {
|
|
28
|
-
output.write(JSON.stringify({ event, data }) + '\n');
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
process.on('SIGINT', () => {
|
|
32
|
-
logger.info('Disconnecting...');
|
|
33
|
-
socket.disconnect();
|
|
34
|
-
process.exit(0);
|
|
35
|
-
});
|
|
36
|
-
// Keep alive until SIGINT
|
|
37
|
-
await new Promise(() => { });
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=listen.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tunnel command — Self-contained tunnel for cloud Studio.
|
|
3
|
-
*
|
|
4
|
-
* Handles all RPC methods locally using Node.js fs and the
|
|
5
|
-
* @synergenius/flow-weaver AST API. No local server required.
|
|
6
|
-
*
|
|
7
|
-
* 1. Opens a WebSocket to the cloud server's /api/tunnel endpoint.
|
|
8
|
-
* 2. Dispatches RPC calls directly to local handler functions.
|
|
9
|
-
*/
|
|
10
|
-
import WebSocket from 'ws';
|
|
11
|
-
export interface TunnelOptions {
|
|
12
|
-
key: string;
|
|
13
|
-
cloud?: string;
|
|
14
|
-
dir?: string;
|
|
15
|
-
/** Override WebSocket factory (for testing) */
|
|
16
|
-
createWs?: (url: string) => WebSocket;
|
|
17
|
-
}
|
|
18
|
-
export declare function tunnelCommand(options: TunnelOptions): Promise<void>;
|
|
19
|
-
//# sourceMappingURL=tunnel.d.ts.map
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tunnel command — Self-contained tunnel for cloud Studio.
|
|
3
|
-
*
|
|
4
|
-
* Handles all RPC methods locally using Node.js fs and the
|
|
5
|
-
* @synergenius/flow-weaver AST API. No local server required.
|
|
6
|
-
*
|
|
7
|
-
* 1. Opens a WebSocket to the cloud server's /api/tunnel endpoint.
|
|
8
|
-
* 2. Dispatches RPC calls directly to local handler functions.
|
|
9
|
-
*/
|
|
10
|
-
import * as path from 'node:path';
|
|
11
|
-
import WebSocket from 'ws';
|
|
12
|
-
import { logger } from '../utils/logger.js';
|
|
13
|
-
import { dispatch } from '../tunnel/dispatch.js';
|
|
14
|
-
export async function tunnelCommand(options) {
|
|
15
|
-
const cloudUrl = options.cloud || 'https://flowweaver.dev';
|
|
16
|
-
const workspaceRoot = path.resolve(options.dir || process.cwd());
|
|
17
|
-
const createWs = options.createWs ?? ((url) => new WebSocket(url));
|
|
18
|
-
logger.section('Flow Weaver Tunnel');
|
|
19
|
-
logger.info(`Cloud: ${cloudUrl}`);
|
|
20
|
-
logger.info(`Workspace: ${workspaceRoot}`);
|
|
21
|
-
logger.newline();
|
|
22
|
-
// -----------------------------------------------------------------------
|
|
23
|
-
// 1. Connect to cloud server via WebSocket
|
|
24
|
-
// -----------------------------------------------------------------------
|
|
25
|
-
logger.info('Connecting to cloud server...');
|
|
26
|
-
const wsProtocol = cloudUrl.startsWith('https') ? 'wss' : 'ws';
|
|
27
|
-
const wsHost = cloudUrl.replace(/^https?:\/\//, '');
|
|
28
|
-
const wsUrl = `${wsProtocol}://${wsHost}/api/tunnel?token=${encodeURIComponent(options.key)}`;
|
|
29
|
-
const cloudWs = createWs(wsUrl);
|
|
30
|
-
await new Promise((resolve, reject) => {
|
|
31
|
-
const timeout = setTimeout(() => {
|
|
32
|
-
cloudWs.close();
|
|
33
|
-
reject(new Error('Cloud server connection timeout (10s)'));
|
|
34
|
-
}, 10_000);
|
|
35
|
-
cloudWs.on('open', () => {
|
|
36
|
-
clearTimeout(timeout);
|
|
37
|
-
});
|
|
38
|
-
cloudWs.on('message', (raw) => {
|
|
39
|
-
try {
|
|
40
|
-
const msg = JSON.parse(raw.toString());
|
|
41
|
-
if (msg.type === 'tunnel:hello') {
|
|
42
|
-
logger.success('Connected to cloud server');
|
|
43
|
-
resolve();
|
|
44
|
-
}
|
|
45
|
-
else if (msg.type === 'error') {
|
|
46
|
-
clearTimeout(timeout);
|
|
47
|
-
cloudWs.close();
|
|
48
|
-
reject(new Error(`Cloud server rejected connection: ${msg.message}`));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// Ignore parse errors during handshake
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
cloudWs.on('error', (err) => {
|
|
56
|
-
clearTimeout(timeout);
|
|
57
|
-
reject(new Error(`Cannot connect to cloud server: ${err.message}`));
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
logger.newline();
|
|
61
|
-
logger.success('Tunnel active — local development mode enabled');
|
|
62
|
-
logger.info('Press Ctrl+C to disconnect');
|
|
63
|
-
logger.newline();
|
|
64
|
-
let requestCount = 0;
|
|
65
|
-
const ctx = { workspaceRoot };
|
|
66
|
-
// -----------------------------------------------------------------------
|
|
67
|
-
// 2. Handle RPC: cloud → local dispatch → cloud
|
|
68
|
-
// -----------------------------------------------------------------------
|
|
69
|
-
cloudWs.on('message', async (raw) => {
|
|
70
|
-
let msg;
|
|
71
|
-
try {
|
|
72
|
-
msg = JSON.parse(raw.toString());
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (msg.type === 'ping') {
|
|
78
|
-
cloudWs.send(JSON.stringify({ type: 'pong' }));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (msg.type === 'tunnel:request') {
|
|
82
|
-
const req = msg;
|
|
83
|
-
requestCount++;
|
|
84
|
-
logger.debug(`[${requestCount}] → ${req.method}`);
|
|
85
|
-
const response = await dispatch(req.method, req.params || {}, ctx);
|
|
86
|
-
cloudWs.send(JSON.stringify({
|
|
87
|
-
type: 'tunnel:response',
|
|
88
|
-
requestId: req.requestId,
|
|
89
|
-
id: req.id,
|
|
90
|
-
success: response.success,
|
|
91
|
-
result: response.result,
|
|
92
|
-
error: response.error,
|
|
93
|
-
}));
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
// -----------------------------------------------------------------------
|
|
97
|
-
// 3. Handle disconnections
|
|
98
|
-
// -----------------------------------------------------------------------
|
|
99
|
-
cloudWs.on('close', (code, reason) => {
|
|
100
|
-
logger.warn(`Cloud server disconnected: ${code} ${reason.toString()}`);
|
|
101
|
-
logger.info('Shutting down tunnel...');
|
|
102
|
-
process.exit(code === 4001 ? 1 : 0);
|
|
103
|
-
});
|
|
104
|
-
cloudWs.on('error', (err) => {
|
|
105
|
-
logger.error(`Cloud WebSocket error: ${err.message}`);
|
|
106
|
-
});
|
|
107
|
-
// -----------------------------------------------------------------------
|
|
108
|
-
// 4. Graceful shutdown
|
|
109
|
-
// -----------------------------------------------------------------------
|
|
110
|
-
process.on('SIGINT', () => {
|
|
111
|
-
logger.newline();
|
|
112
|
-
logger.info(`Shutting down tunnel (${requestCount} requests handled)...`);
|
|
113
|
-
cloudWs.close();
|
|
114
|
-
process.exit(0);
|
|
115
|
-
});
|
|
116
|
-
// Keep alive until SIGINT or cloud disconnect
|
|
117
|
-
await new Promise(() => { });
|
|
118
|
-
}
|
|
119
|
-
//# sourceMappingURL=tunnel.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export declare function uiFocusNode(nodeId: string, options: {
|
|
2
|
-
server?: string;
|
|
3
|
-
}): Promise<void>;
|
|
4
|
-
export declare function uiAddNode(nodeTypeName: string, options: {
|
|
5
|
-
server?: string;
|
|
6
|
-
}): Promise<void>;
|
|
7
|
-
export declare function uiOpenWorkflow(filePath: string, options: {
|
|
8
|
-
server?: string;
|
|
9
|
-
}): Promise<void>;
|
|
10
|
-
export declare function uiGetState(options: {
|
|
11
|
-
server?: string;
|
|
12
|
-
}): Promise<void>;
|
|
13
|
-
export declare function uiBatch(json: string, options: {
|
|
14
|
-
server?: string;
|
|
15
|
-
}): Promise<void>;
|
|
16
|
-
//# sourceMappingURL=ui.d.ts.map
|
package/dist/cli/commands/ui.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { io as socketIO } from 'socket.io-client';
|
|
2
|
-
import { logger } from '../utils/logger.js';
|
|
3
|
-
import { DEFAULT_SERVER_URL } from '../../defaults.js';
|
|
4
|
-
const DEFAULT_SERVER = DEFAULT_SERVER_URL;
|
|
5
|
-
const ACK_TIMEOUT = 10_000;
|
|
6
|
-
const VERBOSE_OUTPUT = !!(process.env.DEBUG || process.argv.includes('--verbose'));
|
|
7
|
-
async function sendCommand(serverUrl, action, params) {
|
|
8
|
-
const socket = socketIO(`${serverUrl}/integrations`, {
|
|
9
|
-
query: { clientType: 'cli' },
|
|
10
|
-
transports: ['websocket', 'polling'],
|
|
11
|
-
});
|
|
12
|
-
try {
|
|
13
|
-
await new Promise((resolve, reject) => {
|
|
14
|
-
socket.on('connect', () => resolve());
|
|
15
|
-
socket.on('connect_error', (err) => reject(err));
|
|
16
|
-
setTimeout(() => reject(new Error('Connection timeout')), ACK_TIMEOUT);
|
|
17
|
-
});
|
|
18
|
-
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
19
|
-
const result = await new Promise((resolve) => {
|
|
20
|
-
const timeout = setTimeout(() => {
|
|
21
|
-
resolve({ requestId, success: false, error: 'Timeout' });
|
|
22
|
-
}, ACK_TIMEOUT);
|
|
23
|
-
socket.on('fw:ack', (data) => {
|
|
24
|
-
if (data.requestId === requestId) {
|
|
25
|
-
clearTimeout(timeout);
|
|
26
|
-
resolve(data);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
socket.emit('integration:command', { requestId, action, params });
|
|
30
|
-
});
|
|
31
|
-
if (result.success) {
|
|
32
|
-
if (VERBOSE_OUTPUT) {
|
|
33
|
-
logger.success(JSON.stringify(result, null, 2));
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
logger.success(`Command "${action}" executed successfully`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
logger.error(VERBOSE_OUTPUT
|
|
41
|
-
? JSON.stringify(result, null, 2)
|
|
42
|
-
: `Command "${action}" failed: ${result.error || 'unknown error'}`);
|
|
43
|
-
process.exitCode = 1;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
logger.error(String(err instanceof Error ? err.message : err));
|
|
48
|
-
process.exitCode = 1;
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
socket.disconnect();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
async function sendBatch(serverUrl, commands) {
|
|
55
|
-
const socket = socketIO(`${serverUrl}/integrations`, {
|
|
56
|
-
query: { clientType: 'cli' },
|
|
57
|
-
transports: ['websocket', 'polling'],
|
|
58
|
-
});
|
|
59
|
-
try {
|
|
60
|
-
await new Promise((resolve, reject) => {
|
|
61
|
-
socket.on('connect', () => resolve());
|
|
62
|
-
socket.on('connect_error', (err) => reject(err));
|
|
63
|
-
setTimeout(() => reject(new Error('Connection timeout')), ACK_TIMEOUT);
|
|
64
|
-
});
|
|
65
|
-
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
66
|
-
const result = await new Promise((resolve) => {
|
|
67
|
-
const timeout = setTimeout(() => {
|
|
68
|
-
resolve({ requestId, success: false, error: 'Timeout' });
|
|
69
|
-
}, ACK_TIMEOUT);
|
|
70
|
-
socket.on('fw:ack', (data) => {
|
|
71
|
-
if (data.requestId === requestId) {
|
|
72
|
-
clearTimeout(timeout);
|
|
73
|
-
resolve(data);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
socket.emit('integration:batch', { requestId, commands });
|
|
77
|
-
});
|
|
78
|
-
if (result.success) {
|
|
79
|
-
if (VERBOSE_OUTPUT) {
|
|
80
|
-
logger.success(JSON.stringify(result, null, 2));
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
logger.success('Batch executed successfully');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
logger.error(VERBOSE_OUTPUT
|
|
88
|
-
? JSON.stringify(result, null, 2)
|
|
89
|
-
: `Batch failed: ${result.error || 'unknown error'}`);
|
|
90
|
-
process.exitCode = 1;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
logger.error(String(err instanceof Error ? err.message : err));
|
|
95
|
-
process.exitCode = 1;
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
socket.disconnect();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
export async function uiFocusNode(nodeId, options) {
|
|
102
|
-
const server = options.server || DEFAULT_SERVER;
|
|
103
|
-
await sendCommand(server, 'focus-node', { nodeId });
|
|
104
|
-
}
|
|
105
|
-
export async function uiAddNode(nodeTypeName, options) {
|
|
106
|
-
const server = options.server || DEFAULT_SERVER;
|
|
107
|
-
await sendCommand(server, 'add-node', { nodeTypeName });
|
|
108
|
-
}
|
|
109
|
-
export async function uiOpenWorkflow(filePath, options) {
|
|
110
|
-
const server = options.server || DEFAULT_SERVER;
|
|
111
|
-
await sendCommand(server, 'open-workflow', { filePath });
|
|
112
|
-
}
|
|
113
|
-
export async function uiGetState(options) {
|
|
114
|
-
const server = options.server || DEFAULT_SERVER;
|
|
115
|
-
await sendCommand(server, 'get-state', {});
|
|
116
|
-
}
|
|
117
|
-
export async function uiBatch(json, options) {
|
|
118
|
-
let commands;
|
|
119
|
-
try {
|
|
120
|
-
commands = JSON.parse(json);
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
logger.error('Invalid JSON. Provide an array of {action, params} objects.');
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const server = options.server || DEFAULT_SERVER;
|
|
128
|
-
await sendBatch(server, commands);
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=ui.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Method dispatcher for the self-contained tunnel.
|
|
3
|
-
*
|
|
4
|
-
* Maps RPC method names to handler functions and wraps them
|
|
5
|
-
* in a standard try/catch envelope.
|
|
6
|
-
*/
|
|
7
|
-
export interface TunnelContext {
|
|
8
|
-
workspaceRoot: string;
|
|
9
|
-
}
|
|
10
|
-
export type HandlerFn = (params: Record<string, unknown>, ctx: TunnelContext) => Promise<unknown>;
|
|
11
|
-
export declare function dispatch(method: string, params: Record<string, unknown>, ctx: TunnelContext): Promise<{
|
|
12
|
-
success: boolean;
|
|
13
|
-
result?: unknown;
|
|
14
|
-
error?: {
|
|
15
|
-
message: string;
|
|
16
|
-
};
|
|
17
|
-
}>;
|
|
18
|
-
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Method dispatcher for the self-contained tunnel.
|
|
3
|
-
*
|
|
4
|
-
* Maps RPC method names to handler functions and wraps them
|
|
5
|
-
* in a standard try/catch envelope.
|
|
6
|
-
*/
|
|
7
|
-
import { fileOpsHandlers } from './handlers/file-ops.js';
|
|
8
|
-
import { astOpsHandlers } from './handlers/ast-ops.js';
|
|
9
|
-
import { mutationHandlers } from './handlers/mutations.js';
|
|
10
|
-
import { templateHandlers } from './handlers/templates.js';
|
|
11
|
-
import { executionHandlers } from './handlers/execution.js';
|
|
12
|
-
import { stubHandlers } from './handlers/stubs.js';
|
|
13
|
-
const handlers = {
|
|
14
|
-
...stubHandlers,
|
|
15
|
-
...fileOpsHandlers,
|
|
16
|
-
...astOpsHandlers,
|
|
17
|
-
...mutationHandlers,
|
|
18
|
-
...templateHandlers,
|
|
19
|
-
...executionHandlers,
|
|
20
|
-
};
|
|
21
|
-
export async function dispatch(method, params, ctx) {
|
|
22
|
-
const handler = handlers[method];
|
|
23
|
-
if (!handler) {
|
|
24
|
-
// Unknown methods return undefined — matches platform behaviour
|
|
25
|
-
return { success: true, result: undefined };
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
const result = await handler(params, ctx);
|
|
29
|
-
return { success: true, result };
|
|
30
|
-
}
|
|
31
|
-
catch (err) {
|
|
32
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
-
return { success: false, error: { message } };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=dispatch.js.map
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File mutation lock — serializes concurrent mutations to the same file.
|
|
3
|
-
*
|
|
4
|
-
* Ported from flow-weaver-platform/src/services/ast-helpers.ts:54-85.
|
|
5
|
-
* Prevents file corruption when rapid Studio mutations (e.g. dragging nodes)
|
|
6
|
-
* trigger concurrent write operations.
|
|
7
|
-
*/
|
|
8
|
-
export declare function withFileLock<T>(filePath: string, operation: () => Promise<T>): Promise<T>;
|
|
9
|
-
//# sourceMappingURL=file-lock.d.ts.map
|