cyclecad 0.2.2 → 0.2.3

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 (69) hide show
  1. package/API-BUILD-MANIFEST.txt +339 -0
  2. package/API-SERVER.md +535 -0
  3. package/Architecture-Deck.pptx +0 -0
  4. package/CLAUDE.md +172 -11
  5. package/CLI-BUILD-SUMMARY.md +504 -0
  6. package/CLI-INDEX.md +356 -0
  7. package/CLI-README.md +466 -0
  8. package/COLLABORATION-INTEGRATION-GUIDE.md +325 -0
  9. package/CONNECTED_FABS_GUIDE.md +612 -0
  10. package/CONNECTED_FABS_README.md +310 -0
  11. package/DELIVERABLES.md +343 -0
  12. package/DFM-ANALYZER-INTEGRATION.md +368 -0
  13. package/DFM-QUICK-START.js +253 -0
  14. package/Dockerfile +69 -0
  15. package/IMPLEMENTATION.md +327 -0
  16. package/LICENSE +31 -0
  17. package/MARKETPLACE_QUICK_REFERENCE.txt +294 -0
  18. package/MCP-INDEX.md +264 -0
  19. package/QUICKSTART-API.md +388 -0
  20. package/QUICKSTART-CLI.md +211 -0
  21. package/QUICKSTART-MCP.md +196 -0
  22. package/README-MCP.md +208 -0
  23. package/TEST-TOKEN-ENGINE.md +319 -0
  24. package/TOKEN-ENGINE-SUMMARY.md +266 -0
  25. package/TOKENS-README.md +263 -0
  26. package/TOOLS-REFERENCE.md +254 -0
  27. package/app/index.html +168 -3
  28. package/app/js/TOKEN-INTEGRATION.md +391 -0
  29. package/app/js/agent-api.js +3 -3
  30. package/app/js/ai-copilot.js +1435 -0
  31. package/app/js/cam-pipeline.js +840 -0
  32. package/app/js/collaboration-ui.js +995 -0
  33. package/app/js/collaboration.js +1116 -0
  34. package/app/js/connected-fabs-example.js +404 -0
  35. package/app/js/connected-fabs.js +1449 -0
  36. package/app/js/dfm-analyzer.js +1760 -0
  37. package/app/js/marketplace.js +1994 -0
  38. package/app/js/material-library.js +2115 -0
  39. package/app/js/token-dashboard.js +563 -0
  40. package/app/js/token-engine.js +743 -0
  41. package/app/test-agent.html +1801 -0
  42. package/bin/cyclecad-cli.js +662 -0
  43. package/bin/cyclecad-mcp +2 -0
  44. package/bin/server.js +242 -0
  45. package/cycleCAD-Architecture.pptx +0 -0
  46. package/cycleCAD-Investor-Deck.pptx +0 -0
  47. package/demo-mcp.sh +60 -0
  48. package/docs/API-SERVER-SUMMARY.md +375 -0
  49. package/docs/API-SERVER.md +667 -0
  50. package/docs/CAM-EXAMPLES.md +344 -0
  51. package/docs/CAM-INTEGRATION.md +612 -0
  52. package/docs/CAM-QUICK-REFERENCE.md +199 -0
  53. package/docs/CLI-INTEGRATION.md +510 -0
  54. package/docs/CLI.md +872 -0
  55. package/docs/MARKETPLACE-API-SCHEMA.json +564 -0
  56. package/docs/MARKETPLACE-INTEGRATION.md +467 -0
  57. package/docs/MARKETPLACE-SETUP.html +439 -0
  58. package/docs/MCP-SERVER.md +403 -0
  59. package/examples/api-client-example.js +488 -0
  60. package/examples/api-client-example.py +359 -0
  61. package/examples/batch-manufacturing.txt +28 -0
  62. package/examples/batch-simple.txt +26 -0
  63. package/model-marketplace.html +1273 -0
  64. package/package.json +14 -3
  65. package/server/api-server.js +1120 -0
  66. package/server/mcp-server.js +1161 -0
  67. package/test-api-server.js +432 -0
  68. package/test-mcp.js +198 -0
  69. package/~$cycleCAD-Investor-Deck.pptx +0 -0
@@ -0,0 +1,488 @@
1
+ /**
2
+ * cycleCAD API Client Example (JavaScript/Node.js)
3
+ *
4
+ * Simple client for the cycleCAD REST API with both HTTP and WebSocket support.
5
+ *
6
+ * Usage (Node.js):
7
+ * node api-client-example.js
8
+ * node api-client-example.js --host localhost --port 3000
9
+ *
10
+ * Usage (Browser):
11
+ * Include <script src="api-client-example.js"></script>
12
+ * const client = new CycleCADClient('http://localhost:3000');
13
+ * client.execute('sketch.start', {plane: 'XY'}).then(r => console.log(r));
14
+ */
15
+
16
+ // ============================================================================
17
+ // CycleCAD HTTP Client
18
+ // ============================================================================
19
+
20
+ class CycleCADClient {
21
+ constructor(baseUrl = 'http://localhost:3000', apiKey = null) {
22
+ this.baseUrl = baseUrl;
23
+ this.apiKey = apiKey;
24
+ this.isNode = typeof window === 'undefined';
25
+ }
26
+
27
+ /**
28
+ * Make HTTP request (works in Node.js and browser)
29
+ */
30
+ async _request(method, endpoint, data = null) {
31
+ const url = `${this.baseUrl}${endpoint}`;
32
+ const headers = { 'Content-Type': 'application/json' };
33
+
34
+ if (this.apiKey) {
35
+ headers['X-API-Key'] = this.apiKey;
36
+ }
37
+
38
+ const options = { method, headers };
39
+ if (data) options.body = JSON.stringify(data);
40
+
41
+ try {
42
+ let response;
43
+
44
+ // Use native fetch if available (browser or Node 18+)
45
+ if (typeof fetch !== 'undefined') {
46
+ response = await fetch(url, options);
47
+ } else {
48
+ // Fallback for older Node.js versions
49
+ const http = require('http');
50
+ response = await this._nodeRequest(url, options);
51
+ }
52
+
53
+ if (!response.ok) {
54
+ const error = await response.json();
55
+ throw new Error(`API Error (${response.status}): ${error.error || response.statusText}`);
56
+ }
57
+
58
+ return await response.json();
59
+ } catch (e) {
60
+ throw new Error(`Request failed: ${e.message}`);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Node.js HTTP request fallback
66
+ */
67
+ _nodeRequest(url, options) {
68
+ return new Promise((resolve, reject) => {
69
+ const urlObj = new URL(url);
70
+ const http = require(urlObj.protocol === 'https:' ? 'https' : 'http');
71
+ const isHttps = urlObj.protocol === 'https:';
72
+
73
+ const opts = {
74
+ hostname: urlObj.hostname,
75
+ port: urlObj.port || (isHttps ? 443 : 80),
76
+ path: urlObj.pathname + urlObj.search,
77
+ method: options.method,
78
+ headers: options.headers
79
+ };
80
+
81
+ const req = http.request(opts, (res) => {
82
+ let data = '';
83
+ res.on('data', chunk => data += chunk);
84
+ res.on('end', () => {
85
+ resolve({
86
+ ok: res.statusCode < 400,
87
+ status: res.statusCode,
88
+ json: () => Promise.resolve(JSON.parse(data))
89
+ });
90
+ });
91
+ });
92
+
93
+ req.on('error', reject);
94
+ if (options.body) req.write(options.body);
95
+ req.end();
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Execute single command
101
+ */
102
+ async execute(method, params = {}) {
103
+ return this._request('POST', '/api/execute', { method, params });
104
+ }
105
+
106
+ /**
107
+ * Execute batch of commands
108
+ */
109
+ async batch(commands) {
110
+ return this._request('POST', '/api/batch', { commands });
111
+ }
112
+
113
+ /**
114
+ * Get API schema
115
+ */
116
+ async getSchema() {
117
+ return this._request('GET', '/api/schema');
118
+ }
119
+
120
+ /**
121
+ * Get server health
122
+ */
123
+ async getHealth() {
124
+ return this._request('GET', '/api/health');
125
+ }
126
+
127
+ /**
128
+ * Get command history
129
+ */
130
+ async getHistory(count = 20) {
131
+ return this._request('GET', `/api/history?count=${count}`);
132
+ }
133
+
134
+ /**
135
+ * Get all models
136
+ */
137
+ async getModels() {
138
+ return this._request('GET', '/api/models');
139
+ }
140
+
141
+ /**
142
+ * Get specific model
143
+ */
144
+ async getModel(modelId) {
145
+ return this._request('GET', `/api/models/${modelId}`);
146
+ }
147
+
148
+ /**
149
+ * Delete model
150
+ */
151
+ async deleteModel(modelId) {
152
+ return this._request('DELETE', `/api/models/${modelId}`);
153
+ }
154
+
155
+ /**
156
+ * Connect WebSocket
157
+ */
158
+ connectWebSocket() {
159
+ return new Promise((resolve, reject) => {
160
+ const wsUrl = this.baseUrl.replace(/^http/, 'ws') + '/api/ws';
161
+
162
+ try {
163
+ const ws = new (this.isNode ? require('ws') : WebSocket)(wsUrl);
164
+
165
+ ws.onopen = () => {
166
+ console.log('✓ WebSocket connected');
167
+ resolve(new CycleCADWebSocket(ws));
168
+ };
169
+
170
+ ws.onerror = (e) => {
171
+ reject(new Error(`WebSocket error: ${e.message}`));
172
+ };
173
+ } catch (e) {
174
+ reject(e);
175
+ }
176
+ });
177
+ }
178
+ }
179
+
180
+ // ============================================================================
181
+ // CycleCAD WebSocket Client
182
+ // ============================================================================
183
+
184
+ class CycleCADWebSocket {
185
+ constructor(ws) {
186
+ this.ws = ws;
187
+ this.listeners = {};
188
+ this.pendingRequests = new Map();
189
+ this.requestId = 0;
190
+
191
+ ws.onmessage = (event) => {
192
+ const data = typeof event.data === 'string'
193
+ ? JSON.parse(event.data)
194
+ : JSON.parse(event.data.toString());
195
+
196
+ // Emit events
197
+ if (data.type) {
198
+ this._emit(data.type, data);
199
+ }
200
+
201
+ // Resolve pending requests
202
+ if (data.requestId && this.pendingRequests.has(data.requestId)) {
203
+ const { resolve } = this.pendingRequests.get(data.requestId);
204
+ this.pendingRequests.delete(data.requestId);
205
+ resolve(data);
206
+ }
207
+ };
208
+
209
+ ws.onerror = (e) => this._emit('error', e);
210
+ ws.onclose = () => this._emit('close');
211
+ }
212
+
213
+ /**
214
+ * Execute command via WebSocket
215
+ */
216
+ async execute(method, params = {}) {
217
+ const requestId = ++this.requestId;
218
+ const message = { requestId, method, params };
219
+
220
+ return new Promise((resolve) => {
221
+ this.pendingRequests.set(requestId, { resolve });
222
+ this.ws.send(JSON.stringify(message));
223
+
224
+ // Timeout after 30s
225
+ setTimeout(() => {
226
+ if (this.pendingRequests.has(requestId)) {
227
+ this.pendingRequests.delete(requestId);
228
+ resolve({ ok: false, error: 'Request timeout' });
229
+ }
230
+ }, 30000);
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Listen for events
236
+ */
237
+ on(event, callback) {
238
+ if (!this.listeners[event]) this.listeners[event] = [];
239
+ this.listeners[event].push(callback);
240
+ return () => {
241
+ this.listeners[event] = this.listeners[event].filter(cb => cb !== callback);
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Emit event
247
+ */
248
+ _emit(event, data) {
249
+ if (this.listeners[event]) {
250
+ this.listeners[event].forEach(cb => {
251
+ try { cb(data); } catch (e) { console.error(`Event listener error: ${e.message}`); }
252
+ });
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Close connection
258
+ */
259
+ close() {
260
+ this.ws.close();
261
+ }
262
+ }
263
+
264
+ // ============================================================================
265
+ // EXAMPLES
266
+ // ============================================================================
267
+
268
+ async function example1_simplePartHTTP(client) {
269
+ console.log('\n' + '='.repeat(60));
270
+ console.log('EXAMPLE 1: Simple Part (HTTP)');
271
+ console.log('='.repeat(60) + '\n');
272
+
273
+ // Start sketch
274
+ console.log('1. Starting sketch...');
275
+ let r = await client.execute('sketch.start', { plane: 'XY' });
276
+ console.log(` ✓ ${r.result.message}`);
277
+
278
+ // Draw circle
279
+ console.log('2. Drawing circle (r=25mm)...');
280
+ r = await client.execute('sketch.circle', { cx: 0, cy: 0, radius: 25 });
281
+ console.log(` ✓ Circle: ${r.result.entityId}`);
282
+
283
+ // End sketch
284
+ console.log('3. Ending sketch...');
285
+ r = await client.execute('sketch.end', {});
286
+
287
+ // Extrude
288
+ console.log('4. Extruding 50mm...');
289
+ r = await client.execute('ops.extrude', {
290
+ height: 50,
291
+ symmetric: false,
292
+ material: 'steel'
293
+ });
294
+ console.log(` ✓ Extrusion: ${r.result.featureId}`);
295
+ console.log(` Volume: ${r.result.volume} mm³`);
296
+ }
297
+
298
+ async function example2_batchOperations(client) {
299
+ console.log('\n' + '='.repeat(60));
300
+ console.log('EXAMPLE 2: Batch Operations');
301
+ console.log('='.repeat(60) + '\n');
302
+
303
+ const commands = [
304
+ { method: 'sketch.start', params: { plane: 'XY' } },
305
+ { method: 'sketch.rect', params: { x: 0, y: 0, width: 60, height: 40 } },
306
+ { method: 'sketch.end', params: {} },
307
+ { method: 'ops.extrude', params: { height: 30, material: 'aluminum' } }
308
+ ];
309
+
310
+ console.log('Executing 4 commands in batch...');
311
+ const r = await client.batch(commands);
312
+
313
+ if (r.ok) {
314
+ console.log(`✓ All ${r.executed} commands succeeded (${r.elapsed}ms)`);
315
+ r.results.forEach((res, i) => {
316
+ console.log(` [${i + 1}] ${commands[i].method}: ${res.elapsed}ms`);
317
+ });
318
+ } else {
319
+ console.log(`✗ Failed with ${r.errors.length} errors`);
320
+ }
321
+ }
322
+
323
+ async function example3_queryValidation(client) {
324
+ console.log('\n' + '='.repeat(60));
325
+ console.log('EXAMPLE 3: Query & Validation');
326
+ console.log('='.repeat(60) + '\n');
327
+
328
+ console.log('1. Available materials:');
329
+ let r = await client.execute('query.materials', {});
330
+ r.result.materials.forEach(m => console.log(` • ${m}`));
331
+
332
+ console.log('\n2. Validate mass:');
333
+ r = await client.execute('validate.mass', {
334
+ target: 'extrude_1234',
335
+ material: 'steel'
336
+ });
337
+ console.log(` Mass: ${r.result.mass} kg`);
338
+
339
+ console.log('\n3. Estimate cost:');
340
+ r = await client.execute('validate.cost', {
341
+ target: 'extrude_1234',
342
+ process: 'FDM',
343
+ material: 'PLA'
344
+ });
345
+ console.log(` Cost: $${r.result.estimatedCost} USD`);
346
+ }
347
+
348
+ async function example4_serverInfo(client) {
349
+ console.log('\n' + '='.repeat(60));
350
+ console.log('EXAMPLE 4: Server Information');
351
+ console.log('='.repeat(60) + '\n');
352
+
353
+ console.log('1. Health check:');
354
+ const health = await client.getHealth();
355
+ console.log(` Status: ${health.status}`);
356
+ console.log(` Version: ${health.version}`);
357
+ console.log(` Uptime: ${health.uptime}s`);
358
+ console.log(` Commands available: ${health.commands}`);
359
+ console.log(` Commands executed: ${health.commandsExecuted}`);
360
+
361
+ console.log('\n2. Command history (last 5):');
362
+ const history = await client.getHistory(5);
363
+ console.log(` Total: ${history.total}`);
364
+ history.recent.forEach(entry => {
365
+ const status = entry.ok ? '✓' : '✗';
366
+ console.log(` [${status}] ${entry.method} (${entry.elapsed}ms)`);
367
+ });
368
+ }
369
+
370
+ async function example5_websocket(client) {
371
+ console.log('\n' + '='.repeat(60));
372
+ console.log('EXAMPLE 5: WebSocket Real-Time Connection');
373
+ console.log('='.repeat(60) + '\n');
374
+
375
+ try {
376
+ console.log('Connecting to WebSocket...');
377
+ const ws = await client.connectWebSocket();
378
+
379
+ // Listen for events
380
+ ws.on('ping', (data) => {
381
+ console.log(` ← ping (${new Date(data.timestamp).toLocaleTimeString()})`);
382
+ });
383
+
384
+ ws.on('error', (e) => {
385
+ console.log(` ✗ Error: ${e.message}`);
386
+ });
387
+
388
+ ws.on('close', () => {
389
+ console.log(' ✗ Connection closed');
390
+ });
391
+
392
+ // Send commands
393
+ console.log('\n1. Sending commands via WebSocket:');
394
+
395
+ console.log(' → sketch.start');
396
+ let r = await ws.execute('sketch.start', { plane: 'XY' });
397
+ console.log(` ← ${r.result.message}`);
398
+
399
+ console.log(' → sketch.circle');
400
+ r = await ws.execute('sketch.circle', { cx: 0, cy: 0, radius: 30 });
401
+ console.log(` ← Circle: ${r.result.entityId}`);
402
+
403
+ console.log(' → sketch.end');
404
+ r = await ws.execute('sketch.end', {});
405
+ console.log(` ← ${r.result.message}`);
406
+
407
+ // Keep connection open for 3 seconds to see pings
408
+ console.log('\n2. Listening for pings (3s)...');
409
+ await new Promise(resolve => setTimeout(resolve, 3000));
410
+
411
+ console.log('\n3. Closing connection...');
412
+ ws.close();
413
+ } catch (e) {
414
+ console.log(` ✗ WebSocket error: ${e.message}`);
415
+ }
416
+ }
417
+
418
+ // ============================================================================
419
+ // MAIN
420
+ // ============================================================================
421
+
422
+ async function main() {
423
+ const args = require('minimist')(process.argv.slice(2));
424
+ const host = args.host || 'localhost';
425
+ const port = args.port || 3000;
426
+ const example = args.example || 'all';
427
+
428
+ console.log('\n' + '█'.repeat(60));
429
+ console.log('█ cycleCAD API Client — Example Usage');
430
+ console.log('█'.repeat(60));
431
+ console.log(`\nConnecting to ${host}:${port}...`);
432
+
433
+ try {
434
+ const client = new CycleCADClient(`http://${host}:${port}`, args['api-key']);
435
+
436
+ // Test connection
437
+ const health = await client.getHealth();
438
+ console.log(`✓ Connected to cycleCAD v${health.version}\n`);
439
+
440
+ // Run examples
441
+ const examples = {
442
+ all: [
443
+ example4_serverInfo,
444
+ example1_simplePartHTTP,
445
+ example2_batchOperations,
446
+ example3_queryValidation,
447
+ example5_websocket
448
+ ],
449
+ simple: [example1_simplePartHTTP],
450
+ batch: [example2_batchOperations],
451
+ query: [example3_queryValidation],
452
+ server: [example4_serverInfo],
453
+ websocket: [example5_websocket]
454
+ };
455
+
456
+ const exampleFns = examples[example] || examples.all;
457
+
458
+ for (const fn of exampleFns) {
459
+ try {
460
+ await fn(client);
461
+ } catch (e) {
462
+ console.log(`\n✗ Example failed: ${e.message}`);
463
+ }
464
+ }
465
+
466
+ console.log('\n' + '█'.repeat(60));
467
+ console.log('█ Examples completed!');
468
+ console.log('█'.repeat(60) + '\n');
469
+ } catch (e) {
470
+ console.log(`\n✗ Connection failed: ${e.message}`);
471
+ console.log('\nMake sure the API server is running:');
472
+ console.log(` npm run server`);
473
+ process.exit(1);
474
+ }
475
+ }
476
+
477
+ // Export for use as module
478
+ if (typeof module !== 'undefined') {
479
+ module.exports = { CycleCADClient, CycleCADWebSocket };
480
+
481
+ // Run examples if called directly
482
+ if (require.main === module) {
483
+ main().catch(e => {
484
+ console.error(e);
485
+ process.exit(1);
486
+ });
487
+ }
488
+ }