bitwrench 2.0.22 → 2.0.24

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 (88) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +4 -3
  3. package/bin/bwmcp.js +3 -0
  4. package/dist/bitwrench-bccl.cjs.js +1 -1
  5. package/dist/bitwrench-bccl.cjs.min.js +1 -1
  6. package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
  7. package/dist/bitwrench-bccl.esm.js +1 -1
  8. package/dist/bitwrench-bccl.esm.min.js +1 -1
  9. package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
  10. package/dist/bitwrench-bccl.umd.js +1 -1
  11. package/dist/bitwrench-bccl.umd.min.js +1 -1
  12. package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
  13. package/dist/bitwrench-code-edit.cjs.js +1 -1
  14. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  15. package/dist/bitwrench-code-edit.es5.js +1 -1
  16. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  17. package/dist/bitwrench-code-edit.esm.js +1 -1
  18. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  19. package/dist/bitwrench-code-edit.umd.js +1 -1
  20. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  21. package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
  22. package/dist/bitwrench-debug.js +1 -1
  23. package/dist/bitwrench-debug.min.js +1 -1
  24. package/dist/bitwrench-lean.cjs.js +3 -3
  25. package/dist/bitwrench-lean.cjs.min.js +2 -2
  26. package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
  27. package/dist/bitwrench-lean.es5.js +3 -3
  28. package/dist/bitwrench-lean.es5.min.js +2 -2
  29. package/dist/bitwrench-lean.es5.min.js.gz +0 -0
  30. package/dist/bitwrench-lean.esm.js +3 -3
  31. package/dist/bitwrench-lean.esm.min.js +2 -2
  32. package/dist/bitwrench-lean.esm.min.js.gz +0 -0
  33. package/dist/bitwrench-lean.umd.js +3 -3
  34. package/dist/bitwrench-lean.umd.min.js +2 -2
  35. package/dist/bitwrench-lean.umd.min.js.gz +0 -0
  36. package/dist/bitwrench-util-css.cjs.js +1 -1
  37. package/dist/bitwrench-util-css.cjs.min.js +1 -1
  38. package/dist/bitwrench-util-css.es5.js +1 -1
  39. package/dist/bitwrench-util-css.es5.min.js +1 -1
  40. package/dist/bitwrench-util-css.esm.js +1 -1
  41. package/dist/bitwrench-util-css.esm.min.js +1 -1
  42. package/dist/bitwrench-util-css.umd.js +1 -1
  43. package/dist/bitwrench-util-css.umd.min.js +1 -1
  44. package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
  45. package/dist/bitwrench.cjs.js +3 -3
  46. package/dist/bitwrench.cjs.min.js +2 -2
  47. package/dist/bitwrench.cjs.min.js.gz +0 -0
  48. package/dist/bitwrench.css +1 -1
  49. package/dist/bitwrench.es5.js +3 -3
  50. package/dist/bitwrench.es5.min.js +2 -2
  51. package/dist/bitwrench.es5.min.js.gz +0 -0
  52. package/dist/bitwrench.esm.js +3 -3
  53. package/dist/bitwrench.esm.min.js +2 -2
  54. package/dist/bitwrench.esm.min.js.gz +0 -0
  55. package/dist/bitwrench.umd.js +3 -3
  56. package/dist/bitwrench.umd.min.js +2 -2
  57. package/dist/bitwrench.umd.min.js.gz +0 -0
  58. package/dist/builds.json +65 -65
  59. package/dist/bwserve.cjs.js +2 -2
  60. package/dist/bwserve.esm.js +2 -2
  61. package/dist/sri.json +45 -45
  62. package/docs/README.md +76 -0
  63. package/docs/app-patterns.md +264 -0
  64. package/docs/bitwrench-mcp.md +426 -0
  65. package/docs/bitwrench_api.md +2232 -0
  66. package/docs/bw-attach.md +399 -0
  67. package/docs/bwserve.md +841 -0
  68. package/docs/cli.md +307 -0
  69. package/docs/component-cheatsheet.md +144 -0
  70. package/docs/component-library.md +1099 -0
  71. package/docs/framework-translation-table.md +33 -0
  72. package/docs/llm-bitwrench-guide.md +672 -0
  73. package/docs/routing.md +562 -0
  74. package/docs/state-management.md +767 -0
  75. package/docs/taco-format.md +373 -0
  76. package/docs/theming.md +309 -0
  77. package/docs/thinking-in-bitwrench.md +1457 -0
  78. package/docs/tutorial-bwserve.md +297 -0
  79. package/docs/tutorial-embedded.md +314 -0
  80. package/docs/tutorial-website.md +255 -0
  81. package/package.json +11 -3
  82. package/readme.html +5 -4
  83. package/src/mcp/knowledge.js +231 -0
  84. package/src/mcp/live.js +226 -0
  85. package/src/mcp/server.js +216 -0
  86. package/src/mcp/tools.js +369 -0
  87. package/src/mcp/transport.js +55 -0
  88. package/src/version.js +3 -3
@@ -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
+ }