@kernel.chat/kbot 3.69.1 → 3.71.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/a2a.d.ts +50 -5
- package/dist/a2a.js +305 -44
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +43 -4
- package/dist/doctor.js +53 -7
- package/dist/integrations/ableton-bridge.d.ts +158 -0
- package/dist/integrations/ableton-bridge.js +486 -0
- package/dist/integrations/ableton-m4l.d.ts +94 -0
- package/dist/integrations/ableton-m4l.js +252 -1
- package/dist/integrations/install-remote-script.d.ts +23 -0
- package/dist/integrations/install-remote-script.js +121 -0
- package/dist/machine.d.ts +1 -0
- package/dist/machine.js +17 -1
- package/dist/serve.js +3 -2
- package/dist/tools/a2a.d.ts +2 -0
- package/dist/tools/a2a.js +233 -0
- package/dist/tools/ableton-bridge-tools.d.ts +14 -0
- package/dist/tools/ableton-bridge-tools.js +327 -0
- package/dist/tools/ai-analysis.d.ts +2 -0
- package/dist/tools/ai-analysis.js +677 -0
- package/dist/tools/financial-analysis.d.ts +2 -0
- package/dist/tools/financial-analysis.js +945 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/music-gen.d.ts +2 -0
- package/dist/tools/music-gen.js +1006 -0
- package/dist/tools/threat-intel.d.ts +2 -0
- package/dist/tools/threat-intel.js +1619 -0
- package/package.json +2 -2
|
@@ -301,7 +301,204 @@ export class AbletonM4L {
|
|
|
301
301
|
return this.send({ action: 'lom_call', path, method, args });
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Client for the KBotBridge Remote Script (TCP 9998).
|
|
306
|
+
*
|
|
307
|
+
* This is separate from the M4L bridge (9999) because the Browser API
|
|
308
|
+
* (browser.load_item) is ONLY available from Python Remote Scripts,
|
|
309
|
+
* not from Max for Live.
|
|
310
|
+
*
|
|
311
|
+
* Use this to programmatically load any native device (Saturator,
|
|
312
|
+
* EQ Eight, Compressor, etc.) onto any track.
|
|
313
|
+
*/
|
|
314
|
+
export class AbletonBrowserBridge {
|
|
315
|
+
static instance = null;
|
|
316
|
+
socket = null;
|
|
317
|
+
connected = false;
|
|
318
|
+
pending = new Map();
|
|
319
|
+
nextId = 1;
|
|
320
|
+
buffer = '';
|
|
321
|
+
static PORT = 9998;
|
|
322
|
+
static HOST = '127.0.0.1';
|
|
323
|
+
static TIMEOUT = 15_000; // Browser operations can be slow
|
|
324
|
+
constructor() { }
|
|
325
|
+
static getInstance() {
|
|
326
|
+
if (!AbletonBrowserBridge.instance) {
|
|
327
|
+
AbletonBrowserBridge.instance = new AbletonBrowserBridge();
|
|
328
|
+
}
|
|
329
|
+
return AbletonBrowserBridge.instance;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Connect to the KBotBridge Remote Script on port 9998.
|
|
333
|
+
* Returns true if connected and the bridge responds to ping.
|
|
334
|
+
*/
|
|
335
|
+
async connect() {
|
|
336
|
+
if (this.connected && this.socket) {
|
|
337
|
+
try {
|
|
338
|
+
await this.send({ action: 'ping' });
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
this.disconnect();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return new Promise((resolve) => {
|
|
346
|
+
this.socket = new net.Socket();
|
|
347
|
+
this.buffer = '';
|
|
348
|
+
this.socket.on('data', (data) => {
|
|
349
|
+
this.buffer += data.toString();
|
|
350
|
+
const lines = this.buffer.split('\n');
|
|
351
|
+
this.buffer = lines.pop() || '';
|
|
352
|
+
for (const line of lines) {
|
|
353
|
+
const trimmed = line.trim();
|
|
354
|
+
if (!trimmed)
|
|
355
|
+
continue;
|
|
356
|
+
try {
|
|
357
|
+
const response = JSON.parse(trimmed);
|
|
358
|
+
this.handleResponse(response);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Malformed JSON — skip
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
this.socket.on('error', () => {
|
|
366
|
+
if (!this.connected)
|
|
367
|
+
resolve(false);
|
|
368
|
+
this.handleDisconnect();
|
|
369
|
+
});
|
|
370
|
+
this.socket.on('close', () => {
|
|
371
|
+
this.handleDisconnect();
|
|
372
|
+
});
|
|
373
|
+
this.socket.connect(AbletonBrowserBridge.PORT, AbletonBrowserBridge.HOST, async () => {
|
|
374
|
+
this.connected = true;
|
|
375
|
+
try {
|
|
376
|
+
const pong = await this.send({ action: 'ping' });
|
|
377
|
+
resolve(pong.ok);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
resolve(false);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
setTimeout(() => {
|
|
384
|
+
if (!this.connected) {
|
|
385
|
+
this.socket?.destroy();
|
|
386
|
+
resolve(false);
|
|
387
|
+
}
|
|
388
|
+
}, 5000);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
disconnect() {
|
|
392
|
+
this.connected = false;
|
|
393
|
+
if (this.socket) {
|
|
394
|
+
this.socket.destroy();
|
|
395
|
+
this.socket = null;
|
|
396
|
+
}
|
|
397
|
+
for (const [, req] of this.pending) {
|
|
398
|
+
clearTimeout(req.timer);
|
|
399
|
+
req.reject(new Error('Disconnected'));
|
|
400
|
+
}
|
|
401
|
+
this.pending.clear();
|
|
402
|
+
this.buffer = '';
|
|
403
|
+
}
|
|
404
|
+
async send(cmd) {
|
|
405
|
+
if (!this.connected || !this.socket) {
|
|
406
|
+
throw new Error('Not connected to KBotBridge Remote Script.\n' +
|
|
407
|
+
'Make sure KBotBridge is selected as a Control Surface in Ableton Preferences.');
|
|
408
|
+
}
|
|
409
|
+
const id = this.nextId++;
|
|
410
|
+
const fullCmd = { id, ...cmd };
|
|
411
|
+
return new Promise((resolve, reject) => {
|
|
412
|
+
const timer = setTimeout(() => {
|
|
413
|
+
this.pending.delete(id);
|
|
414
|
+
reject(new Error(`Timeout: ${cmd.action}`));
|
|
415
|
+
}, AbletonBrowserBridge.TIMEOUT);
|
|
416
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
417
|
+
this.socket.write(JSON.stringify(fullCmd) + '\n');
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
get isConnected() {
|
|
421
|
+
return this.connected;
|
|
422
|
+
}
|
|
423
|
+
handleResponse(response) {
|
|
424
|
+
if (response.id && this.pending.has(response.id)) {
|
|
425
|
+
const req = this.pending.get(response.id);
|
|
426
|
+
this.pending.delete(response.id);
|
|
427
|
+
clearTimeout(req.timer);
|
|
428
|
+
req.resolve(response);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
handleDisconnect() {
|
|
432
|
+
if (!this.connected)
|
|
433
|
+
return;
|
|
434
|
+
this.connected = false;
|
|
435
|
+
this.socket = null;
|
|
436
|
+
for (const [, req] of this.pending) {
|
|
437
|
+
clearTimeout(req.timer);
|
|
438
|
+
req.reject(new Error('Connection lost'));
|
|
439
|
+
}
|
|
440
|
+
this.pending.clear();
|
|
441
|
+
}
|
|
442
|
+
// ── Browser convenience methods ─────────────────────────────────
|
|
443
|
+
async ping() {
|
|
444
|
+
try {
|
|
445
|
+
const r = await this.send({ action: 'ping' });
|
|
446
|
+
return r.ok;
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Search Ableton's browser for items matching a query.
|
|
454
|
+
* @param query - Search string (case-insensitive)
|
|
455
|
+
* @param category - instruments/audio_effects/midi_effects/drums/samples/all
|
|
456
|
+
*/
|
|
457
|
+
async browserSearch(query, category = 'all') {
|
|
458
|
+
const r = await this.send({ action: 'browser_search', query, category });
|
|
459
|
+
if (!r.ok)
|
|
460
|
+
throw new Error(r.error || 'Browser search failed');
|
|
461
|
+
return r.results || [];
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Load a browser item by URI onto a track.
|
|
465
|
+
* Use the URI from a browserSearch() result.
|
|
466
|
+
*/
|
|
467
|
+
async browserLoad(track, uri) {
|
|
468
|
+
return this.send({ action: 'browser_load', track, uri });
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Search + load in one step. Finds first loadable match and loads it.
|
|
472
|
+
* @param track - 0-indexed track number
|
|
473
|
+
* @param name - Device name to search for (e.g., "Saturator", "EQ Eight")
|
|
474
|
+
* @param category - instruments/audio_effects/midi_effects/drums/samples/all
|
|
475
|
+
*/
|
|
476
|
+
async browserLoadByName(track, name, category = 'all') {
|
|
477
|
+
return this.send({ action: 'browser_load_by_name', track, name, category });
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* List top-level browser categories with child counts.
|
|
481
|
+
*/
|
|
482
|
+
async browserCategories() {
|
|
483
|
+
const r = await this.send({ action: 'browser_categories' });
|
|
484
|
+
if (!r.ok)
|
|
485
|
+
throw new Error(r.error || 'Failed to list categories');
|
|
486
|
+
return r.categories || [];
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* List all tracks with names and device counts.
|
|
490
|
+
*/
|
|
491
|
+
async listTracks() {
|
|
492
|
+
return this.send({ action: 'list_tracks' });
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* List devices on a track with full parameter details.
|
|
496
|
+
*/
|
|
497
|
+
async listDevices(track) {
|
|
498
|
+
return this.send({ action: 'list_devices', track });
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// ── Convenience exports ─────────────────────────────────────────────
|
|
305
502
|
/**
|
|
306
503
|
* Get a connected M4L bridge instance.
|
|
307
504
|
* Throws if the bridge is not available.
|
|
@@ -320,6 +517,41 @@ export async function ensureM4L() {
|
|
|
320
517
|
}
|
|
321
518
|
return m4l;
|
|
322
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Get a connected Browser bridge instance (KBotBridge Remote Script on port 9998).
|
|
522
|
+
* Throws if not available.
|
|
523
|
+
*/
|
|
524
|
+
export async function ensureBrowserBridge() {
|
|
525
|
+
const bridge = AbletonBrowserBridge.getInstance();
|
|
526
|
+
if (bridge.isConnected)
|
|
527
|
+
return bridge;
|
|
528
|
+
const ok = await bridge.connect();
|
|
529
|
+
if (!ok) {
|
|
530
|
+
throw new Error('Cannot connect to KBotBridge Remote Script.\n\n' +
|
|
531
|
+
'Make sure:\n' +
|
|
532
|
+
'1. Ableton Live is running\n' +
|
|
533
|
+
'2. KBotBridge is selected as a Control Surface in Preferences > Link, Tempo & MIDI\n' +
|
|
534
|
+
'3. Ableton status bar shows "KBotBridge: Listening on port 9998"\n\n' +
|
|
535
|
+
'To install: kbot ableton install-bridge\n');
|
|
536
|
+
}
|
|
537
|
+
return bridge;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Connect to both M4L bridge (9999) and Browser bridge (9998).
|
|
541
|
+
* Returns whichever connections succeed. At least one must connect.
|
|
542
|
+
*/
|
|
543
|
+
export async function connectBrowser() {
|
|
544
|
+
const m4l = AbletonM4L.getInstance();
|
|
545
|
+
const browser = AbletonBrowserBridge.getInstance();
|
|
546
|
+
const [m4lOk, browserOk] = await Promise.all([
|
|
547
|
+
m4l.connect().catch(() => false),
|
|
548
|
+
browser.connect().catch(() => false),
|
|
549
|
+
]);
|
|
550
|
+
return {
|
|
551
|
+
m4l: m4lOk ? m4l : null,
|
|
552
|
+
browser: browserOk ? browser : null,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
323
555
|
/**
|
|
324
556
|
* Format a friendly error message for M4L connection failures.
|
|
325
557
|
*/
|
|
@@ -335,4 +567,23 @@ export function formatM4LError() {
|
|
|
335
567
|
'The M4L bridge gives kbot full control over Ableton — instruments, effects, clips, mixing, everything.',
|
|
336
568
|
].join('\n');
|
|
337
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* Format a friendly error message for Browser bridge connection failures.
|
|
572
|
+
*/
|
|
573
|
+
export function formatBrowserBridgeError() {
|
|
574
|
+
return [
|
|
575
|
+
'**KBotBridge Remote Script not connected**',
|
|
576
|
+
'',
|
|
577
|
+
'The Browser API (for loading native devices) requires the KBotBridge Remote Script:',
|
|
578
|
+
'1. Install: `kbot ableton install-bridge`',
|
|
579
|
+
'2. Open Ableton Live Preferences (Cmd+,)',
|
|
580
|
+
'3. Go to Link, Tempo & MIDI',
|
|
581
|
+
'4. Set a Control Surface to "KBotBridge"',
|
|
582
|
+
'5. Close Preferences',
|
|
583
|
+
'',
|
|
584
|
+
'This runs alongside the M4L bridge — they use different ports:',
|
|
585
|
+
'- KBotBridge: TCP 9998 (Browser API, device loading)',
|
|
586
|
+
'- M4L Bridge: TCP 9999 (LOM access, clips, mixing)',
|
|
587
|
+
].join('\n');
|
|
588
|
+
}
|
|
338
589
|
//# sourceMappingURL=ableton-m4l.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install-remote-script.ts — Install KBotBridge Remote Script into Ableton Live
|
|
3
|
+
*
|
|
4
|
+
* Copies the KBotBridge Python Remote Script to Ableton's User Library,
|
|
5
|
+
* enabling the Browser API bridge on TCP port 9998.
|
|
6
|
+
*
|
|
7
|
+
* The Remote Script exposes Ableton's browser.load_item() API, which is
|
|
8
|
+
* ONLY available from Python Remote Scripts (not from Max for Live).
|
|
9
|
+
* This lets kbot programmatically load any native device (Saturator,
|
|
10
|
+
* EQ Eight, Compressor, etc.) onto any track.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npx tsx packages/kbot/src/integrations/install-remote-script.ts
|
|
14
|
+
* kbot ableton install-bridge
|
|
15
|
+
*/
|
|
16
|
+
export declare function installKBotBridge(): Promise<string>;
|
|
17
|
+
export declare function isKBotBridgeInstalled(): boolean;
|
|
18
|
+
export declare function uninstallKBotBridge(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Get the path to the KBotBridge log file inside Ableton's Remote Scripts.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getKBotBridgeLogPath(): string | null;
|
|
23
|
+
//# sourceMappingURL=install-remote-script.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install-remote-script.ts — Install KBotBridge Remote Script into Ableton Live
|
|
3
|
+
*
|
|
4
|
+
* Copies the KBotBridge Python Remote Script to Ableton's User Library,
|
|
5
|
+
* enabling the Browser API bridge on TCP port 9998.
|
|
6
|
+
*
|
|
7
|
+
* The Remote Script exposes Ableton's browser.load_item() API, which is
|
|
8
|
+
* ONLY available from Python Remote Scripts (not from Max for Live).
|
|
9
|
+
* This lets kbot programmatically load any native device (Saturator,
|
|
10
|
+
* EQ Eight, Compressor, etc.) onto any track.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npx tsx packages/kbot/src/integrations/install-remote-script.ts
|
|
14
|
+
* kbot ableton install-bridge
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'node:fs';
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import * as os from 'node:os';
|
|
19
|
+
const SCRIPT_NAME = 'KBotBridge';
|
|
20
|
+
const SOURCE_FILES = [
|
|
21
|
+
'__init__.py',
|
|
22
|
+
'kbot_control_surface.py',
|
|
23
|
+
'tcp_server.py',
|
|
24
|
+
];
|
|
25
|
+
function getRemoteScriptsDir() {
|
|
26
|
+
const home = os.homedir();
|
|
27
|
+
return path.join(home, 'Music', 'Ableton', 'User Library', 'Remote Scripts');
|
|
28
|
+
}
|
|
29
|
+
function getDestDir() {
|
|
30
|
+
return path.join(getRemoteScriptsDir(), SCRIPT_NAME);
|
|
31
|
+
}
|
|
32
|
+
function getSourceDir() {
|
|
33
|
+
// Source files live alongside this installer in the integrations directory
|
|
34
|
+
return path.join(__dirname, SCRIPT_NAME);
|
|
35
|
+
}
|
|
36
|
+
export async function installKBotBridge() {
|
|
37
|
+
const lines = [];
|
|
38
|
+
const log = (msg) => { lines.push(msg); console.log(msg); };
|
|
39
|
+
log('Installing KBotBridge Remote Script...');
|
|
40
|
+
log('');
|
|
41
|
+
const sourceDir = getSourceDir();
|
|
42
|
+
const destDir = getDestDir();
|
|
43
|
+
const remoteScriptsDir = getRemoteScriptsDir();
|
|
44
|
+
// Verify source files exist
|
|
45
|
+
for (const file of SOURCE_FILES) {
|
|
46
|
+
const srcPath = path.join(sourceDir, file);
|
|
47
|
+
if (!fs.existsSync(srcPath)) {
|
|
48
|
+
log(` ERROR: Source file missing: ${srcPath}`);
|
|
49
|
+
log(' Run from the kbot package directory.');
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Create Remote Scripts directory if it doesn't exist
|
|
54
|
+
if (!fs.existsSync(remoteScriptsDir)) {
|
|
55
|
+
fs.mkdirSync(remoteScriptsDir, { recursive: true });
|
|
56
|
+
log(` Created: ${remoteScriptsDir}`);
|
|
57
|
+
}
|
|
58
|
+
// Remove old installation if present
|
|
59
|
+
if (fs.existsSync(destDir)) {
|
|
60
|
+
fs.rmSync(destDir, { recursive: true });
|
|
61
|
+
log(' Removed previous KBotBridge installation');
|
|
62
|
+
}
|
|
63
|
+
// Copy files
|
|
64
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
65
|
+
for (const file of SOURCE_FILES) {
|
|
66
|
+
const src = path.join(sourceDir, file);
|
|
67
|
+
const dst = path.join(destDir, file);
|
|
68
|
+
fs.copyFileSync(src, dst);
|
|
69
|
+
log(` Copied: ${file}`);
|
|
70
|
+
}
|
|
71
|
+
// Create logs directory (the script will log here)
|
|
72
|
+
const logsDir = path.join(destDir, 'logs');
|
|
73
|
+
if (!fs.existsSync(logsDir)) {
|
|
74
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
log('');
|
|
77
|
+
log(`KBotBridge installed to: ${destDir}`);
|
|
78
|
+
log('');
|
|
79
|
+
log('To activate:');
|
|
80
|
+
log(' 1. Open Ableton Live (or restart if already running)');
|
|
81
|
+
log(' 2. Preferences (Cmd+,) > Link, Tempo & MIDI');
|
|
82
|
+
log(' 3. Set a Control Surface slot to "KBotBridge"');
|
|
83
|
+
log(' 4. Input/Output can be left as "None"');
|
|
84
|
+
log(' 5. Close Preferences');
|
|
85
|
+
log('');
|
|
86
|
+
log('Verify:');
|
|
87
|
+
log(' - Ableton status bar shows "KBotBridge: Listening on port 9998"');
|
|
88
|
+
log(' - Run: echo \'{"id":1,"action":"ping"}\\n\' | nc localhost 9998');
|
|
89
|
+
log('');
|
|
90
|
+
log('KBotBridge runs alongside AbletonOSC — they use different ports:');
|
|
91
|
+
log(' - KBotBridge: TCP 9998 (Browser API, device loading)');
|
|
92
|
+
log(' - M4L Bridge: TCP 9999 (LOM access, clips, mixing)');
|
|
93
|
+
log(' - AbletonOSC: UDP 11000/11001 (OSC, legacy)');
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
export function isKBotBridgeInstalled() {
|
|
97
|
+
const destDir = getDestDir();
|
|
98
|
+
return fs.existsSync(path.join(destDir, '__init__.py'))
|
|
99
|
+
&& fs.existsSync(path.join(destDir, 'kbot_control_surface.py'))
|
|
100
|
+
&& fs.existsSync(path.join(destDir, 'tcp_server.py'));
|
|
101
|
+
}
|
|
102
|
+
export function uninstallKBotBridge() {
|
|
103
|
+
const destDir = getDestDir();
|
|
104
|
+
if (fs.existsSync(destDir)) {
|
|
105
|
+
fs.rmSync(destDir, { recursive: true });
|
|
106
|
+
return `KBotBridge removed from ${destDir}`;
|
|
107
|
+
}
|
|
108
|
+
return 'KBotBridge was not installed.';
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the path to the KBotBridge log file inside Ableton's Remote Scripts.
|
|
112
|
+
*/
|
|
113
|
+
export function getKBotBridgeLogPath() {
|
|
114
|
+
const logPath = path.join(getDestDir(), 'logs', 'kbot_bridge.log');
|
|
115
|
+
return fs.existsSync(logPath) ? logPath : null;
|
|
116
|
+
}
|
|
117
|
+
// ── CLI entrypoint ──────────────────────────────────────────────────
|
|
118
|
+
if (require.main === module || process.argv[1]?.includes('install-remote-script')) {
|
|
119
|
+
installKBotBridge().catch(console.error);
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=install-remote-script.js.map
|
package/dist/machine.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface MachineProfile {
|
|
|
73
73
|
canRunLocalModels: boolean;
|
|
74
74
|
gpuAcceleration: 'metal' | 'cuda' | 'vulkan' | 'cpu-only';
|
|
75
75
|
recommendedModelSize: string;
|
|
76
|
+
mlxAvailable: boolean;
|
|
76
77
|
probedAt: string;
|
|
77
78
|
}
|
|
78
79
|
export declare function probeMachine(): Promise<MachineProfile>;
|
package/dist/machine.js
CHANGED
|
@@ -314,6 +314,18 @@ function detectGpuAcceleration(gpus) {
|
|
|
314
314
|
return 'vulkan';
|
|
315
315
|
return 'cpu-only';
|
|
316
316
|
}
|
|
317
|
+
// ── MLX framework detection (Apple Silicon) ──
|
|
318
|
+
function detectMLX() {
|
|
319
|
+
if (platform() !== 'darwin' || arch() !== 'arm64')
|
|
320
|
+
return false;
|
|
321
|
+
// Quick check: try importing mlx in Python
|
|
322
|
+
const result = exec('python3 -c "import mlx; print(mlx.__version__)" 2>/dev/null', 3000);
|
|
323
|
+
if (result && !result.includes('ModuleNotFoundError'))
|
|
324
|
+
return true;
|
|
325
|
+
// Fallback: check common pip install path
|
|
326
|
+
const globCheck = exec('ls /usr/local/lib/python3.*/site-packages/mlx/__init__.py 2>/dev/null || ls ~/Library/Python/3.*/lib/python/site-packages/mlx/__init__.py 2>/dev/null', 2000);
|
|
327
|
+
return !!globCheck;
|
|
328
|
+
}
|
|
317
329
|
// ── Model size recommendation ──
|
|
318
330
|
function recommendModelSize(totalMemoryGB, gpuAccel) {
|
|
319
331
|
// Conservative: leave room for OS + apps
|
|
@@ -374,6 +386,7 @@ export async function probeMachine() {
|
|
|
374
386
|
osInfo = probeLinuxOs();
|
|
375
387
|
}
|
|
376
388
|
const gpuAccel = detectGpuAcceleration(gpus);
|
|
389
|
+
const mlxAvailable = detectMLX();
|
|
377
390
|
const devTools = probeDevTools();
|
|
378
391
|
// Uptime
|
|
379
392
|
const uptimeSeconds = plat === 'darwin'
|
|
@@ -420,6 +433,7 @@ export async function probeMachine() {
|
|
|
420
433
|
canRunLocalModels: gpuAccel !== 'cpu-only' || totalGB >= 8,
|
|
421
434
|
gpuAcceleration: gpuAccel,
|
|
422
435
|
recommendedModelSize: recommendModelSize(totalGB, gpuAccel),
|
|
436
|
+
mlxAvailable,
|
|
423
437
|
probedAt: new Date().toISOString(),
|
|
424
438
|
};
|
|
425
439
|
cached = profile;
|
|
@@ -505,6 +519,8 @@ export function formatMachineProfile(p) {
|
|
|
505
519
|
lines.push('');
|
|
506
520
|
lines.push(' AI Capabilities');
|
|
507
521
|
lines.push(` Acceleration ${p.gpuAcceleration}`);
|
|
522
|
+
if (p.mlxAvailable)
|
|
523
|
+
lines.push(` MLX framework available (Apple Silicon accelerated)`);
|
|
508
524
|
lines.push(` Local models ${p.canRunLocalModels ? 'yes' : 'no'}`);
|
|
509
525
|
lines.push(` Recommended up to ${p.recommendedModelSize} parameters`);
|
|
510
526
|
lines.push('');
|
|
@@ -529,7 +545,7 @@ export function formatMachineForPrompt(p) {
|
|
|
529
545
|
if (p.battery.present) {
|
|
530
546
|
parts.push(`Battery: ${p.battery.percent}% ${p.battery.charging ? 'charging' : 'discharging'}`);
|
|
531
547
|
}
|
|
532
|
-
parts.push(`GPU accel: ${p.gpuAcceleration} — local models up to ${p.recommendedModelSize}`);
|
|
548
|
+
parts.push(`GPU accel: ${p.gpuAcceleration}${p.mlxAvailable ? ' + MLX' : ''} — local models up to ${p.recommendedModelSize}`);
|
|
533
549
|
const toolNames = p.devTools.map(t => `${t.name} ${t.version}`).join(', ');
|
|
534
550
|
if (toolNames)
|
|
535
551
|
parts.push(`Tools: ${toolNames}`);
|
package/dist/serve.js
CHANGED
|
@@ -235,8 +235,9 @@ export async function startServe(options) {
|
|
|
235
235
|
printInfo(` GET /metrics — Execution metrics`);
|
|
236
236
|
printInfo(` GET /apps — List MCP App-capable tools`);
|
|
237
237
|
printInfo(` GET /.well-known/agent.json — A2A Agent Card`);
|
|
238
|
-
printInfo(` POST /a2a
|
|
239
|
-
printInfo(`
|
|
238
|
+
printInfo(` POST /a2a — A2A JSON-RPC endpoint`);
|
|
239
|
+
printInfo(` POST /a2a/tasks — A2A submit task (REST)`);
|
|
240
|
+
printInfo(` GET /a2a/tasks/:id — A2A task status (REST)`);
|
|
240
241
|
if (options.token) {
|
|
241
242
|
printInfo(` Auth: Bearer token required`);
|
|
242
243
|
}
|