bitwrench 2.0.21 → 2.0.23
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/LICENSE.txt +1 -1
- package/README.md +4 -5
- package/bin/bwmcp.js +3 -0
- package/dist/bitwrench-bccl.cjs.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
- package/dist/bitwrench-bccl.esm.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
- package/dist/bitwrench-bccl.umd.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.cjs.min.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +1 -1
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
- package/dist/bitwrench-debug.js +1 -1
- package/dist/bitwrench-debug.min.js +1 -1
- package/dist/bitwrench-lean.cjs.js +3 -3
- package/dist/bitwrench-lean.cjs.min.js +2 -2
- package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
- package/dist/bitwrench-lean.es5.js +3 -3
- package/dist/bitwrench-lean.es5.min.js +2 -2
- package/dist/bitwrench-lean.es5.min.js.gz +0 -0
- package/dist/bitwrench-lean.esm.js +3 -3
- package/dist/bitwrench-lean.esm.min.js +2 -2
- package/dist/bitwrench-lean.esm.min.js.gz +0 -0
- package/dist/bitwrench-lean.umd.js +3 -3
- package/dist/bitwrench-lean.umd.min.js +2 -2
- package/dist/bitwrench-lean.umd.min.js.gz +0 -0
- package/dist/bitwrench-util-css.cjs.js +1 -1
- package/dist/bitwrench-util-css.cjs.min.js +1 -1
- package/dist/bitwrench-util-css.es5.js +1 -1
- package/dist/bitwrench-util-css.es5.min.js +1 -1
- package/dist/bitwrench-util-css.esm.js +1 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -1
- package/dist/bitwrench-util-css.umd.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
- package/dist/bitwrench.cjs.js +3 -3
- package/dist/bitwrench.cjs.min.js +2 -2
- package/dist/bitwrench.cjs.min.js.gz +0 -0
- package/dist/bitwrench.css +1 -1
- package/dist/bitwrench.es5.js +3 -3
- package/dist/bitwrench.es5.min.js +2 -2
- package/dist/bitwrench.es5.min.js.gz +0 -0
- package/dist/bitwrench.esm.js +3 -3
- package/dist/bitwrench.esm.min.js +2 -2
- package/dist/bitwrench.esm.min.js.gz +0 -0
- package/dist/bitwrench.umd.js +3 -3
- package/dist/bitwrench.umd.min.js +2 -2
- package/dist/bitwrench.umd.min.js.gz +0 -0
- package/dist/builds.json +61 -61
- package/dist/bwserve.cjs.js +2 -2
- package/dist/bwserve.esm.js +2 -2
- package/dist/sri.json +45 -45
- package/docs/README.md +76 -0
- package/docs/app-patterns.md +264 -0
- package/docs/bitwrench-mcp.md +426 -0
- package/docs/bitwrench_api.md +2232 -0
- package/docs/bw-attach.md +399 -0
- package/docs/bwserve.md +841 -0
- package/docs/cli.md +307 -0
- package/docs/component-cheatsheet.md +144 -0
- package/docs/component-library.md +1099 -0
- package/docs/framework-translation-table.md +33 -0
- package/docs/llm-bitwrench-guide.md +672 -0
- package/docs/routing.md +562 -0
- package/docs/state-management.md +767 -0
- package/docs/taco-format.md +373 -0
- package/docs/theming.md +309 -0
- package/docs/thinking-in-bitwrench.md +1457 -0
- package/docs/tutorial-bwserve.md +297 -0
- package/docs/tutorial-embedded.md +314 -0
- package/docs/tutorial-website.md +255 -0
- package/package.json +11 -3
- package/readme.html +6 -5
- package/src/mcp/knowledge.js +231 -0
- package/src/mcp/live.js +226 -0
- package/src/mcp/server.js +216 -0
- package/src/mcp/tools.js +369 -0
- package/src/mcp/transport.js +55 -0
- package/src/version.js +3 -3
package/src/mcp/live.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP live rendering -- bwserve integration for render_live, screenshot, query_dom.
|
|
3
|
+
*
|
|
4
|
+
* Starts a bwserve instance that serves a shell page. The MCP agent pushes
|
|
5
|
+
* TACO to the browser via SSE, captures screenshots, and queries DOM state.
|
|
6
|
+
*
|
|
7
|
+
* @module mcp/live
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import bwserve from '../bwserve/index.js';
|
|
11
|
+
|
|
12
|
+
var _app = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Start the bwserve live rendering server.
|
|
16
|
+
*
|
|
17
|
+
* @param {Object} opts
|
|
18
|
+
* @param {number} [opts.port=7910]
|
|
19
|
+
* @param {string} [opts.theme]
|
|
20
|
+
* @param {boolean} [opts.open=false]
|
|
21
|
+
* @returns {Promise<Object>} The bwserve app instance
|
|
22
|
+
*/
|
|
23
|
+
export function startLive(opts) {
|
|
24
|
+
opts = opts || {};
|
|
25
|
+
var port = opts.port || 7910;
|
|
26
|
+
|
|
27
|
+
_app = bwserve.create({
|
|
28
|
+
port: port,
|
|
29
|
+
title: 'bwmcp',
|
|
30
|
+
allowScreenshot: true,
|
|
31
|
+
theme: opts.theme || null
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Register the default page handler
|
|
35
|
+
_app.page('/', function(client) {
|
|
36
|
+
// Render an initial placeholder
|
|
37
|
+
client.render('#app', {
|
|
38
|
+
t: 'div',
|
|
39
|
+
a: { class: 'bw_container', style: 'padding: 2rem; text-align: center; color: #888;' },
|
|
40
|
+
c: { t: 'p', c: 'bwmcp ready -- waiting for agent to render UI...' }
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return new Promise(function(resolve, reject) {
|
|
45
|
+
try {
|
|
46
|
+
_app.listen(function() {
|
|
47
|
+
process.stderr.write('[bwmcp] bwserve listening on http://localhost:' + port + '\n');
|
|
48
|
+
|
|
49
|
+
// Open browser if requested
|
|
50
|
+
if (opts.open) {
|
|
51
|
+
import('child_process').then(function(cp) {
|
|
52
|
+
var url = 'http://localhost:' + port;
|
|
53
|
+
var cmd = process.platform === 'darwin' ? 'open'
|
|
54
|
+
: process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
55
|
+
cp.exec(cmd + ' ' + url);
|
|
56
|
+
}).catch(function() { /* ignore open failures */ });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
resolve(_app);
|
|
60
|
+
});
|
|
61
|
+
} catch (e) {
|
|
62
|
+
reject(e);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the current bwserve app instance (for testing).
|
|
69
|
+
*/
|
|
70
|
+
export function getApp() {
|
|
71
|
+
return _app;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Stop the live server.
|
|
76
|
+
*/
|
|
77
|
+
export function stopLive() {
|
|
78
|
+
if (_app) {
|
|
79
|
+
_app.close();
|
|
80
|
+
_app = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the first connected client, or null.
|
|
86
|
+
*/
|
|
87
|
+
function getClient() {
|
|
88
|
+
if (!_app) return null;
|
|
89
|
+
var clients = _app._clients;
|
|
90
|
+
if (!clients) return null;
|
|
91
|
+
// _clients is a Map in bwserve
|
|
92
|
+
for (var [, record] of clients) {
|
|
93
|
+
if (record && record.client && !record.client._closed) {
|
|
94
|
+
return record.client;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// -- Live tool definitions --
|
|
101
|
+
|
|
102
|
+
export var liveToolDefs = [
|
|
103
|
+
{
|
|
104
|
+
name: 'render_live',
|
|
105
|
+
title: 'Render TACO Live in Browser',
|
|
106
|
+
description: 'Push a TACO to the connected browser window via bwserve SSE. The UI appears immediately. Use action "replace" (default), "append", "patch", or "remove".',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
target: { type: 'string', description: 'CSS selector of target element (default: "#app")' },
|
|
111
|
+
taco: { type: 'object', description: 'TACO object to render' },
|
|
112
|
+
action: { type: 'string', enum: ['replace', 'append', 'patch', 'remove'], description: 'Render action (default: replace)' },
|
|
113
|
+
content: { type: 'string', description: 'For patch action: new text content' },
|
|
114
|
+
attr: { type: 'object', description: 'For patch action: attributes to update' }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'screenshot',
|
|
120
|
+
title: 'Capture Browser Screenshot',
|
|
121
|
+
description: 'Capture a screenshot of the live browser window. Returns a base64 PNG image. Useful for iterating on UI -- render, screenshot, evaluate, adjust.',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
selector: { type: 'string', description: 'CSS selector of element to capture (default: "body")' }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'query_dom',
|
|
131
|
+
title: 'Query DOM State',
|
|
132
|
+
description: 'Execute JavaScript in the browser and return the result. Use this to inspect DOM state, read text content, count elements, etc.',
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
code: { type: 'string', description: 'JavaScript code to evaluate in the browser. The return value is sent back.' }
|
|
137
|
+
},
|
|
138
|
+
required: ['code']
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
// -- Live tool handlers --
|
|
144
|
+
|
|
145
|
+
export var liveHandlers = {
|
|
146
|
+
render_live: function(args) {
|
|
147
|
+
if (!_app) {
|
|
148
|
+
return { content: [{ type: 'text', text: 'Error: bwserve not started. Live rendering not available.' }], isError: true };
|
|
149
|
+
}
|
|
150
|
+
var client = getClient();
|
|
151
|
+
var target = args.target || '#app';
|
|
152
|
+
var action = args.action || 'replace';
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
if (action === 'replace' && args.taco) {
|
|
156
|
+
_app.broadcast({ type: 'replace', target: target, node: args.taco });
|
|
157
|
+
} else if (action === 'append' && args.taco) {
|
|
158
|
+
_app.broadcast({ type: 'append', target: target, node: args.taco });
|
|
159
|
+
} else if (action === 'patch') {
|
|
160
|
+
_app.broadcast({ type: 'patch', target: target, content: args.content || '', attr: args.attr || null });
|
|
161
|
+
} else if (action === 'remove') {
|
|
162
|
+
_app.broadcast({ type: 'remove', target: target });
|
|
163
|
+
} else {
|
|
164
|
+
return { content: [{ type: 'text', text: 'Error: invalid action or missing taco' }], isError: true };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: 'text', text: JSON.stringify({ status: 'rendered', action: action, target: target, clientCount: _app.clientCount || 0 }) }],
|
|
169
|
+
structuredContent: { status: 'rendered', action: action, target: target, clientCount: _app.clientCount || 0 }
|
|
170
|
+
};
|
|
171
|
+
} catch (e) {
|
|
172
|
+
return { content: [{ type: 'text', text: 'Error: ' + e.message }], isError: true };
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
screenshot: function(args) {
|
|
177
|
+
var client = getClient();
|
|
178
|
+
if (!client) {
|
|
179
|
+
return Promise.resolve({
|
|
180
|
+
content: [{ type: 'text', text: 'Error: no browser client connected. Open http://localhost:' + (_app ? _app._port : 7910) + ' in a browser.' }],
|
|
181
|
+
isError: true
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
var selector = (args && args.selector) || 'body';
|
|
186
|
+
return client.screenshot(selector, { timeout: 10000 })
|
|
187
|
+
.then(function(result) {
|
|
188
|
+
var base64 = result.data.toString('base64');
|
|
189
|
+
return {
|
|
190
|
+
content: [{
|
|
191
|
+
type: 'image',
|
|
192
|
+
data: base64,
|
|
193
|
+
mimeType: 'image/png'
|
|
194
|
+
}]
|
|
195
|
+
};
|
|
196
|
+
})
|
|
197
|
+
.catch(function(e) {
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: 'text', text: 'Screenshot error: ' + e.message }],
|
|
200
|
+
isError: true
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
query_dom: function(args) {
|
|
206
|
+
var client = getClient();
|
|
207
|
+
if (!client) {
|
|
208
|
+
return Promise.resolve({
|
|
209
|
+
content: [{ type: 'text', text: 'Error: no browser client connected.' }],
|
|
210
|
+
isError: true
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return client.query(args.code, { timeout: 5000 })
|
|
215
|
+
.then(function(result) {
|
|
216
|
+
var text = typeof result === 'string' ? result : JSON.stringify(result);
|
|
217
|
+
return { content: [{ type: 'text', text: text }] };
|
|
218
|
+
})
|
|
219
|
+
.catch(function(e) {
|
|
220
|
+
return {
|
|
221
|
+
content: [{ type: 'text', text: 'Query error: ' + e.message }],
|
|
222
|
+
isError: true
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bwmcp -- MCP server for bitwrench.
|
|
3
|
+
*
|
|
4
|
+
* JSON-RPC 2.0 dispatch: initialize, notifications/initialized,
|
|
5
|
+
* tools/list, tools/call. Wires knowledge tools, component tools,
|
|
6
|
+
* utility tools, and live rendering tools together.
|
|
7
|
+
*
|
|
8
|
+
* @module mcp/server
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { VERSION } from '../version.js';
|
|
12
|
+
import { createStdioTransport } from './transport.js';
|
|
13
|
+
import { knowledgeToolDefs, knowledgeHandlers } from './knowledge.js';
|
|
14
|
+
import { componentToolDefs, utilityToolDefs, toolHandlers } from './tools.js';
|
|
15
|
+
import { liveToolDefs, liveHandlers, startLive, stopLive } from './live.js';
|
|
16
|
+
|
|
17
|
+
var PROTOCOL_VERSION = '2024-11-05';
|
|
18
|
+
|
|
19
|
+
// Ordered tool list: knowledge first (start_here at 0), then components, then utilities, then live
|
|
20
|
+
var allToolDefs = [].concat(knowledgeToolDefs, componentToolDefs, utilityToolDefs, liveToolDefs);
|
|
21
|
+
|
|
22
|
+
// Merge all handlers
|
|
23
|
+
var allHandlers = Object.assign({}, knowledgeHandlers, toolHandlers, liveHandlers);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handle a single JSON-RPC 2.0 message. Returns a response object,
|
|
27
|
+
* or null for notifications (no id).
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} msg - JSON-RPC message
|
|
30
|
+
* @returns {Object|Promise<Object>|null} Response or null for notifications
|
|
31
|
+
*/
|
|
32
|
+
export function handleMessage(msg) {
|
|
33
|
+
var id = msg.id;
|
|
34
|
+
var method = msg.method;
|
|
35
|
+
var params = msg.params || {};
|
|
36
|
+
|
|
37
|
+
// Notification (no id) -- no response expected
|
|
38
|
+
if (id === undefined || id === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
switch (method) {
|
|
43
|
+
case 'initialize':
|
|
44
|
+
return {
|
|
45
|
+
jsonrpc: '2.0',
|
|
46
|
+
id: id,
|
|
47
|
+
result: {
|
|
48
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
49
|
+
capabilities: {
|
|
50
|
+
tools: { listChanged: false }
|
|
51
|
+
},
|
|
52
|
+
serverInfo: {
|
|
53
|
+
name: 'bwmcp',
|
|
54
|
+
version: VERSION,
|
|
55
|
+
description: 'Bitwrench MCP server with live browser UI. Call bitwrench_start_here first.'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
case 'tools/list':
|
|
61
|
+
return {
|
|
62
|
+
jsonrpc: '2.0',
|
|
63
|
+
id: id,
|
|
64
|
+
result: {
|
|
65
|
+
tools: allToolDefs
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
case 'tools/call': {
|
|
70
|
+
var toolName = params.name;
|
|
71
|
+
var toolArgs = params.arguments || {};
|
|
72
|
+
var handler = allHandlers[toolName];
|
|
73
|
+
if (!handler) {
|
|
74
|
+
return {
|
|
75
|
+
jsonrpc: '2.0',
|
|
76
|
+
id: id,
|
|
77
|
+
error: { code: -32602, message: 'Unknown tool: ' + toolName }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
var result = handler(toolArgs);
|
|
82
|
+
// Handle async tool results (screenshot, query_dom)
|
|
83
|
+
if (result && typeof result.then === 'function') {
|
|
84
|
+
return result.then(function(r) {
|
|
85
|
+
return { jsonrpc: '2.0', id: id, result: r };
|
|
86
|
+
}).catch(function(e) {
|
|
87
|
+
return {
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
id: id,
|
|
90
|
+
result: { content: [{ type: 'text', text: 'Error: ' + e.message }], isError: true }
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return { jsonrpc: '2.0', id: id, result: result };
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return {
|
|
97
|
+
jsonrpc: '2.0',
|
|
98
|
+
id: id,
|
|
99
|
+
result: { content: [{ type: 'text', text: 'Error: ' + e.message }], isError: true }
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
default:
|
|
105
|
+
return {
|
|
106
|
+
jsonrpc: '2.0',
|
|
107
|
+
id: id,
|
|
108
|
+
error: { code: -32601, message: 'Method not found: ' + method }
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create an MCP server with the given options.
|
|
115
|
+
*
|
|
116
|
+
* @param {Object} [opts]
|
|
117
|
+
* @param {number} [opts.port=7910] - bwserve port
|
|
118
|
+
* @param {string} [opts.theme] - Default theme
|
|
119
|
+
* @param {boolean} [opts.open=false] - Open browser on start
|
|
120
|
+
* @param {boolean} [opts.noBrowser=false] - Skip starting bwserve
|
|
121
|
+
* @returns {{ listen: Function, close: Function }}
|
|
122
|
+
*/
|
|
123
|
+
export function createMcpServer(opts) {
|
|
124
|
+
opts = opts || {};
|
|
125
|
+
var transport = null;
|
|
126
|
+
|
|
127
|
+
function listen() {
|
|
128
|
+
transport = createStdioTransport(function(msg) {
|
|
129
|
+
var response = handleMessage(msg);
|
|
130
|
+
if (response === null) return; // notification, no response
|
|
131
|
+
if (response && typeof response.then === 'function') {
|
|
132
|
+
response.then(function(r) { transport.send(r); });
|
|
133
|
+
} else {
|
|
134
|
+
transport.send(response);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Start bwserve unless disabled
|
|
139
|
+
if (!opts.noBrowser) {
|
|
140
|
+
startLive({
|
|
141
|
+
port: opts.port || 7910,
|
|
142
|
+
theme: opts.theme,
|
|
143
|
+
open: opts.open || false
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
process.stderr.write('[bwmcp] MCP server ready (stdio)\n');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function close() {
|
|
151
|
+
if (transport) transport.close();
|
|
152
|
+
stopLive();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { listen: listen, close: close };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* CLI entry point. Parse args and start the server.
|
|
160
|
+
*
|
|
161
|
+
* @param {string[]} argv - Command-line arguments (after node and script)
|
|
162
|
+
*/
|
|
163
|
+
export function run(argv) {
|
|
164
|
+
argv = argv || [];
|
|
165
|
+
var opts = {
|
|
166
|
+
port: 7910,
|
|
167
|
+
theme: null,
|
|
168
|
+
open: false,
|
|
169
|
+
noBrowser: false
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
for (var i = 0; i < argv.length; i++) {
|
|
173
|
+
switch (argv[i]) {
|
|
174
|
+
case '--port':
|
|
175
|
+
opts.port = parseInt(argv[++i], 10) || 7910;
|
|
176
|
+
break;
|
|
177
|
+
case '--theme':
|
|
178
|
+
opts.theme = argv[++i];
|
|
179
|
+
break;
|
|
180
|
+
case '--open':
|
|
181
|
+
opts.open = true;
|
|
182
|
+
break;
|
|
183
|
+
case '--no-browser':
|
|
184
|
+
opts.noBrowser = true;
|
|
185
|
+
break;
|
|
186
|
+
case '--help':
|
|
187
|
+
case '-h':
|
|
188
|
+
process.stderr.write([
|
|
189
|
+
'Usage: bwmcp [options]',
|
|
190
|
+
'',
|
|
191
|
+
'Options:',
|
|
192
|
+
' --port <n> bwserve port (default: 7910)',
|
|
193
|
+
' --theme <name> Default theme preset',
|
|
194
|
+
' --open Open browser on start',
|
|
195
|
+
' --no-browser Skip starting bwserve (testing mode)',
|
|
196
|
+
' --help, -h Show this help',
|
|
197
|
+
''
|
|
198
|
+
].join('\n'));
|
|
199
|
+
process.exit(0);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
var server = createMcpServer(opts);
|
|
205
|
+
server.listen();
|
|
206
|
+
|
|
207
|
+
// Clean shutdown
|
|
208
|
+
process.on('SIGINT', function() {
|
|
209
|
+
server.close();
|
|
210
|
+
process.exit(0);
|
|
211
|
+
});
|
|
212
|
+
process.on('SIGTERM', function() {
|
|
213
|
+
server.close();
|
|
214
|
+
process.exit(0);
|
|
215
|
+
});
|
|
216
|
+
}
|