@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/doctor.js CHANGED
@@ -9,7 +9,7 @@ import { join } from 'node:path';
9
9
  import { existsSync, readdirSync, statSync } from 'node:fs';
10
10
  import { execSync } from 'node:child_process';
11
11
  import chalk from 'chalk';
12
- import { loadConfig, isByokEnabled, getByokProvider, isLocalProvider, PROVIDERS, KBOT_DIR, ENV_KEYS, } from './auth.js';
12
+ import { loadConfig, isByokEnabled, getByokProvider, isLocalProvider, PROVIDERS, KBOT_DIR, ENV_KEYS, getOllamaVersion, isOllamaMLXBackend, } from './auth.js';
13
13
  import { getExtendedStats } from './learning.js';
14
14
  import { getMachineProfile, probeMachine } from './machine.js';
15
15
  import { createRequire } from 'node:module';
@@ -168,15 +168,25 @@ async function checkLocalProviderReachable(providerId, providerConfig) {
168
168
  if (!res.ok) {
169
169
  return { name: providerConfig.name, status: 'fail', message: `returned ${res.status}` };
170
170
  }
171
- // For Ollama, report model count
171
+ // For Ollama, report model count, version, and MLX backend
172
172
  if (providerId === 'ollama') {
173
173
  try {
174
174
  const data = await res.json();
175
175
  const models = data.models ?? [];
176
- const modelInfo = models.length > 0
177
- ? `running (${models.length} model${models.length !== 1 ? 's' : ''})`
178
- : 'running (no models pulled)';
179
- return { name: providerConfig.name, status: 'pass', message: modelInfo };
176
+ const parts = [];
177
+ // Model count
178
+ parts.push(models.length > 0
179
+ ? `${models.length} model${models.length !== 1 ? 's' : ''}`
180
+ : 'no models pulled');
181
+ // Version
182
+ const version = await getOllamaVersion();
183
+ if (version)
184
+ parts.push(`v${version}`);
185
+ // MLX backend detection
186
+ const mlxActive = await isOllamaMLXBackend();
187
+ if (mlxActive)
188
+ parts.push('MLX backend');
189
+ return { name: providerConfig.name, status: 'pass', message: `running (${parts.join(', ')})` };
180
190
  }
181
191
  catch {
182
192
  return { name: providerConfig.name, status: 'pass', message: 'running' };
@@ -362,6 +372,38 @@ function checkShell() {
362
372
  const completionStatus = completionsInstalled ? ', completions installed' : '';
363
373
  return { name: 'Shell', status: 'pass', message: `${shellName}${completionStatus}` };
364
374
  }
375
+ // ── Ollama MLX check (0.19+ on Apple Silicon) ──
376
+ async function checkOllamaMLX() {
377
+ // Only relevant on Apple Silicon
378
+ if (process.platform !== 'darwin' || process.arch !== 'arm64')
379
+ return null;
380
+ const version = await getOllamaVersion();
381
+ if (!version)
382
+ return null; // Ollama not running — already reported by provider check
383
+ const parts = version.split('.').map(Number);
384
+ const [major = 0, minor = 0] = parts;
385
+ if (major > 0 || minor >= 19) {
386
+ const mlxActive = await isOllamaMLXBackend();
387
+ if (mlxActive) {
388
+ return {
389
+ name: 'Ollama MLX',
390
+ status: 'pass',
391
+ message: `Ollama ${version} — MLX backend available for 2x faster inference on Apple Silicon`,
392
+ };
393
+ }
394
+ return {
395
+ name: 'Ollama MLX',
396
+ status: 'pass',
397
+ message: `Ollama ${version} — MLX supported (57% faster prefill, 93% faster decode on Apple Silicon)`,
398
+ };
399
+ }
400
+ // Ollama is running but older than 0.19
401
+ return {
402
+ name: 'Ollama MLX',
403
+ status: 'warn',
404
+ message: `Ollama ${version} — upgrade to 0.19+ for MLX backend (2x faster on Apple Silicon)`,
405
+ };
406
+ }
365
407
  // ── Hardware checks (uses machine.ts) ──
366
408
  async function checkHardware() {
367
409
  const results = [];
@@ -435,16 +477,20 @@ export async function runDoctor() {
435
477
  checks.push(checkKbotVersion());
436
478
  checks.push(checkApiKey());
437
479
  // Async checks — run in parallel for speed
438
- const [providerResults, hardwareResults] = await Promise.all([
480
+ const [providerResults, hardwareResults, ollamaMLXResult] = await Promise.all([
439
481
  checkAllProviders().catch(() => [
440
482
  { name: 'Providers', status: 'warn', message: 'check failed unexpectedly' },
441
483
  ]),
442
484
  checkHardware().catch(() => [
443
485
  { name: 'Hardware', status: 'warn', message: 'check failed unexpectedly' },
444
486
  ]),
487
+ checkOllamaMLX().catch(() => null),
445
488
  ]);
446
489
  // Provider checks (one line per provider)
447
490
  checks.push(...providerResults);
491
+ // Ollama MLX check (Apple Silicon only)
492
+ if (ollamaMLXResult)
493
+ checks.push(ollamaMLXResult);
448
494
  // Hardware checks
449
495
  checks.push(...hardwareResults);
450
496
  // More synchronous checks
@@ -0,0 +1,158 @@
1
+ /**
2
+ * ableton-bridge.ts — kbot ↔ AbletonBridge TCP Client
3
+ *
4
+ * Connects to AbletonBridge (https://github.com/hidingwill/AbletonBridge),
5
+ * a 353-tool Remote Script that exposes Ableton's full Browser API
6
+ * via a TCP server on localhost:9001.
7
+ *
8
+ * Protocol:
9
+ * Send: {"id": 1, "method": "search_browser", "params": {...}}\n
10
+ * Receive: {"id": 1, "result": {...}}\n
11
+ *
12
+ * Fallback chain (used by tools):
13
+ * 1. AbletonBridge (port 9001) — full browser API
14
+ * 2. KBotBridge (port 9998) — kbot's own Remote Script
15
+ * 3. Error with install instructions
16
+ *
17
+ * Follows the same singleton + newline-delimited JSON pattern as AbletonM4L.
18
+ */
19
+ export interface BrowserItem {
20
+ name: string;
21
+ uri: string;
22
+ isLoadable: boolean;
23
+ isDevice: boolean;
24
+ isFolder: boolean;
25
+ }
26
+ export interface Preset {
27
+ name: string;
28
+ uri: string;
29
+ }
30
+ export interface Device {
31
+ name: string;
32
+ className: string;
33
+ index: number;
34
+ }
35
+ export interface BridgeCommand {
36
+ id: number;
37
+ method: string;
38
+ params?: Record<string, unknown>;
39
+ }
40
+ export interface BridgeResponse {
41
+ id: number;
42
+ result?: unknown;
43
+ error?: string;
44
+ }
45
+ export declare class AbletonBridgeClient {
46
+ private static instance;
47
+ private socket;
48
+ private connected;
49
+ private pending;
50
+ private nextId;
51
+ private buffer;
52
+ static PORT: number;
53
+ static HOST: string;
54
+ static TIMEOUT: number;
55
+ static CONNECT_TIMEOUT: number;
56
+ private constructor();
57
+ /**
58
+ * Get the singleton instance.
59
+ */
60
+ static getInstance(): AbletonBridgeClient;
61
+ /**
62
+ * Connect to AbletonBridge TCP server.
63
+ * Returns true if connected and responds to a ping/handshake.
64
+ */
65
+ connect(): Promise<boolean>;
66
+ /**
67
+ * Check if connected.
68
+ */
69
+ isConnected(): boolean;
70
+ /**
71
+ * Disconnect from the bridge.
72
+ */
73
+ disconnect(): void;
74
+ /**
75
+ * Send a method call and wait for a response.
76
+ */
77
+ send(method: string, params?: Record<string, unknown>): Promise<BridgeResponse>;
78
+ private handleResponse;
79
+ private handleDisconnect;
80
+ /**
81
+ * Search Ableton's browser for items matching a query.
82
+ * Optionally filter by category: "instruments", "audio_effects", "midi_effects",
83
+ * "drums", "sounds", "packs", "plugins", "samples", "presets".
84
+ */
85
+ searchBrowser(query: string, category?: string): Promise<BrowserItem[]>;
86
+ /**
87
+ * Load a device onto a track by its browser URI.
88
+ */
89
+ loadDevice(trackIndex: number, uri: string): Promise<boolean>;
90
+ /**
91
+ * Search for a device by name and load the first loadable match onto a track.
92
+ * Optionally filter by category to narrow results.
93
+ */
94
+ loadDeviceByName(trackIndex: number, name: string, category?: string): Promise<boolean>;
95
+ /**
96
+ * List presets available for a device by its URI.
97
+ */
98
+ listPresets(deviceUri: string): Promise<Preset[]>;
99
+ /**
100
+ * Load a preset onto a device on a specific track.
101
+ */
102
+ loadPreset(trackIndex: number, deviceIndex: number, presetUri: string): Promise<boolean>;
103
+ /**
104
+ * Get the effect/device chain on a track.
105
+ */
106
+ getEffectChain(trackIndex: number): Promise<Device[]>;
107
+ }
108
+ /**
109
+ * Lightweight TCP probe for the kbot Remote Script on port 9998.
110
+ * Uses the same newline-delimited JSON protocol as AbletonM4L.
111
+ */
112
+ export declare class KBotRemoteClient {
113
+ private static instance;
114
+ private socket;
115
+ private connected;
116
+ private pending;
117
+ private nextId;
118
+ private buffer;
119
+ static PORT: number;
120
+ static HOST: string;
121
+ static TIMEOUT: number;
122
+ static CONNECT_TIMEOUT: number;
123
+ private constructor();
124
+ static getInstance(): KBotRemoteClient;
125
+ connect(): Promise<boolean>;
126
+ isConnected(): boolean;
127
+ disconnect(): void;
128
+ send(cmd: Record<string, unknown>): Promise<Record<string, unknown>>;
129
+ /** Load a device by name via the kbot Remote Script's search. */
130
+ loadDevice(trackIndex: number, name: string): Promise<boolean>;
131
+ /** Search the browser via the kbot Remote Script. */
132
+ searchBrowser(query: string): Promise<BrowserItem[]>;
133
+ private handleResponse;
134
+ private handleDisconnect;
135
+ }
136
+ /**
137
+ * Try to connect to AbletonBridge (port 9001).
138
+ * Returns the connected client or null if unavailable.
139
+ */
140
+ export declare function tryAbletonBridge(): Promise<AbletonBridgeClient | null>;
141
+ /**
142
+ * Try to connect to KBotBridge Remote Script (port 9998).
143
+ * Returns the connected client or null if unavailable.
144
+ */
145
+ export declare function tryKBotRemote(): Promise<KBotRemoteClient | null>;
146
+ /**
147
+ * Get any available bridge, trying AbletonBridge first, then KBotBridge.
148
+ * Returns { bridge, type } or null if neither is available.
149
+ */
150
+ export declare function getAvailableBridge(): Promise<{
151
+ bridge: AbletonBridgeClient | KBotRemoteClient;
152
+ type: 'ableton-bridge' | 'kbot-remote';
153
+ } | null>;
154
+ /**
155
+ * Format a helpful error message when no bridge is available.
156
+ */
157
+ export declare function formatBridgeError(): string;
158
+ //# sourceMappingURL=ableton-bridge.d.ts.map