@kernel.chat/kbot 3.71.0 → 3.73.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/buddy.d.ts CHANGED
@@ -74,6 +74,27 @@ export declare function addBuddyXP(amount: number): {
74
74
  levelInfo: BuddyLevelInfo;
75
75
  leveledUp: boolean;
76
76
  };
77
+ /**
78
+ * Sync buddy stats to the cloud leaderboard.
79
+ * Anonymous — uses a SHA-256 hash of hostname+homedir, not user identity.
80
+ * Requires a kernel.chat token (cloud sync enabled).
81
+ */
82
+ export declare function syncBuddyToCloud(): Promise<boolean>;
83
+ /**
84
+ * Fetch the buddy leaderboard from the cloud.
85
+ * Returns ranked entries sorted by XP descending.
86
+ */
87
+ export declare function fetchBuddyLeaderboard(opts?: {
88
+ limit?: number;
89
+ species?: string;
90
+ }): Promise<Array<{
91
+ species: string;
92
+ level: number;
93
+ xp: number;
94
+ achievement_count: number;
95
+ sessions: number;
96
+ rank: number;
97
+ }>>;
77
98
  /**
78
99
  * Get the buddy's current level info without modifying state.
79
100
  * Includes level, XP, XP to next level, and species-specific title.
package/dist/buddy.js CHANGED
@@ -7,7 +7,7 @@
7
7
  // Achievements: milestones that unlock as the user uses kbot. Persisted in buddy.json.
8
8
  //
9
9
  // Persists buddy name + achievements to ~/.kbot/buddy.json
10
- import { homedir } from 'node:os';
10
+ import { homedir, hostname } from 'node:os';
11
11
  import { join } from 'node:path';
12
12
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
13
13
  import { createHash } from 'node:crypto';
@@ -15,6 +15,7 @@ import { createInterface } from 'node:readline';
15
15
  import { getDreamStatus, getDreamPrompt } from './dream.js';
16
16
  import { getExtendedStats, getProfileSummary } from './learning.js';
17
17
  import { getToolMetrics } from './tools/index.js';
18
+ import { getCloudToken } from './cloud-sync.js';
18
19
  // ── Paths ──
19
20
  const KBOT_DIR = join(homedir(), '.kbot');
20
21
  const BUDDY_FILE = join(KBOT_DIR, 'buddy.json');
@@ -930,6 +931,8 @@ export function addBuddyXP(amount) {
930
931
  config.evolution = evo;
931
932
  saveBuddyConfig(config);
932
933
  cachedEvolution = evo;
934
+ // Debounced cloud sync — leaderboard update
935
+ scheduleBuddySync();
933
936
  const species = resolveSpecies();
934
937
  const nextLevel = (newLevel < 3 ? (newLevel + 1) : null);
935
938
  const xpToNext = nextLevel !== null ? LEVEL_THRESHOLDS[nextLevel] - evo.xp : null;
@@ -943,6 +946,99 @@ export function addBuddyXP(amount) {
943
946
  leveledUp,
944
947
  };
945
948
  }
949
+ // ── Cloud Sync — Buddy Leaderboard ──
950
+ const ENGINE_URL = 'https://eoxxpyixdieprsxlpwcs.supabase.co/functions/v1/kbot-engine';
951
+ const BUDDY_SYNC_DEBOUNCE_MS = 5 * 60 * 1000; // max once per 5 minutes
952
+ let buddySyncTimer = null;
953
+ let lastBuddySync = 0;
954
+ /** Generate an anonymous device hash from hostname + homedir */
955
+ function getDeviceHash() {
956
+ return createHash('sha256')
957
+ .update(`${hostname()}:${homedir()}`)
958
+ .digest('hex');
959
+ }
960
+ /**
961
+ * Sync buddy stats to the cloud leaderboard.
962
+ * Anonymous — uses a SHA-256 hash of hostname+homedir, not user identity.
963
+ * Requires a kernel.chat token (cloud sync enabled).
964
+ */
965
+ export async function syncBuddyToCloud() {
966
+ const token = getCloudToken();
967
+ if (!token)
968
+ return false;
969
+ try {
970
+ const buddy = getBuddy();
971
+ const lvl = getBuddyLevel();
972
+ const achievements = getAchievements();
973
+ const stats = getExtendedStats();
974
+ const unlockedCount = achievements.filter(a => a.unlockedAt !== null).length;
975
+ const res = await fetch(`${ENGINE_URL}/sync`, {
976
+ method: 'POST',
977
+ headers: {
978
+ 'Content-Type': 'application/json',
979
+ 'Authorization': `Bearer ${token}`,
980
+ },
981
+ body: JSON.stringify({
982
+ action: 'buddy_sync',
983
+ device_hash: getDeviceHash(),
984
+ species: buddy.species,
985
+ level: lvl.level,
986
+ xp: lvl.xp,
987
+ achievement_count: unlockedCount,
988
+ sessions: stats.sessions,
989
+ }),
990
+ signal: AbortSignal.timeout(10_000),
991
+ });
992
+ return res.ok;
993
+ }
994
+ catch {
995
+ return false;
996
+ }
997
+ }
998
+ /** Debounced buddy sync — called from addBuddyXP, max once per 5 minutes */
999
+ function scheduleBuddySync() {
1000
+ const now = Date.now();
1001
+ if (now - lastBuddySync < BUDDY_SYNC_DEBOUNCE_MS)
1002
+ return;
1003
+ if (buddySyncTimer)
1004
+ return;
1005
+ buddySyncTimer = setTimeout(() => {
1006
+ buddySyncTimer = null;
1007
+ lastBuddySync = Date.now();
1008
+ syncBuddyToCloud().catch(() => { }); // fire and forget
1009
+ }, 1000); // short delay to batch rapid XP gains
1010
+ }
1011
+ /**
1012
+ * Fetch the buddy leaderboard from the cloud.
1013
+ * Returns ranked entries sorted by XP descending.
1014
+ */
1015
+ export async function fetchBuddyLeaderboard(opts) {
1016
+ const token = getCloudToken();
1017
+ if (!token)
1018
+ return [];
1019
+ try {
1020
+ const res = await fetch(`${ENGINE_URL}/sync`, {
1021
+ method: 'POST',
1022
+ headers: {
1023
+ 'Content-Type': 'application/json',
1024
+ 'Authorization': `Bearer ${token}`,
1025
+ },
1026
+ body: JSON.stringify({
1027
+ action: 'buddy_leaderboard',
1028
+ limit: opts?.limit ?? 50,
1029
+ ...(opts?.species ? { species: opts.species } : {}),
1030
+ }),
1031
+ signal: AbortSignal.timeout(10_000),
1032
+ });
1033
+ if (!res.ok)
1034
+ return [];
1035
+ const data = await res.json();
1036
+ return data.leaderboard ?? [];
1037
+ }
1038
+ catch {
1039
+ return [];
1040
+ }
1041
+ }
946
1042
  /**
947
1043
  * Get the buddy's current level info without modifying state.
948
1044
  * Includes level, XP, XP to next level, and species-specific title.
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, pr
27
27
  import { checkForUpdate, selfUpdate } from './updater.js';
28
28
  import { runTutorial } from './tutorial.js';
29
29
  import { syncOnStartup, schedulePush, flushCloudSync, isCloudSyncEnabled, setCloudToken, getCloudToken } from './cloud-sync.js';
30
- import { getBuddy, getBuddyGreeting, formatBuddyStatus, getBuddyDreamNarration, renameBuddy, buddyChat, getAchievements, getBuddyLevel } from './buddy.js';
30
+ import { getBuddy, getBuddyGreeting, formatBuddyStatus, getBuddyDreamNarration, renameBuddy, buddyChat, getAchievements, getBuddyLevel, fetchBuddyLeaderboard } from './buddy.js';
31
31
  import chalk from 'chalk';
32
32
  import { createRequire } from 'node:module';
33
33
  const __require = createRequire(import.meta.url);
@@ -1962,16 +1962,22 @@ async function main() {
1962
1962
  });
1963
1963
  program
1964
1964
  .command('serve')
1965
- .description('Start HTTP server — expose all 223 tools for kernel.chat or any client')
1965
+ .description('Start HTTP/HTTPS server — expose all tools for kernel.chat, Claude Cowork, or any client')
1966
1966
  .option('-p, --port <port>', 'Port to listen on', '7437')
1967
1967
  .option('--token <token>', 'Require auth token for all requests')
1968
1968
  .option('--computer-use', 'Enable computer use tools')
1969
+ .option('--https', 'Enable HTTPS with auto-generated self-signed cert (~/.kbot/certs/)')
1970
+ .option('--cert <path>', 'Path to TLS certificate file (implies HTTPS)')
1971
+ .option('--key <path>', 'Path to TLS private key file (implies HTTPS)')
1969
1972
  .action(async (opts) => {
1970
1973
  const { startServe } = await import('./serve.js');
1971
1974
  await startServe({
1972
1975
  port: parseInt(opts.port, 10),
1973
1976
  token: opts.token,
1974
1977
  computerUse: opts.computerUse,
1978
+ https: opts.https,
1979
+ cert: opts.cert,
1980
+ key: opts.key,
1975
1981
  });
1976
1982
  });
1977
1983
  program
@@ -3573,6 +3579,62 @@ async function main() {
3573
3579
  console.log();
3574
3580
  process.exit(0);
3575
3581
  });
3582
+ buddyCmd
3583
+ .command('leaderboard')
3584
+ .description('Show the global buddy leaderboard — anonymous rankings across all kbot installs')
3585
+ .option('-l, --limit <n>', 'Number of entries to show', '20')
3586
+ .option('-s, --species <species>', 'Filter by species (fox, owl, cat, robot, ghost, mushroom, octopus, dragon)')
3587
+ .action(async (opts) => {
3588
+ const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 20, 1), 200);
3589
+ const species = opts.species?.toLowerCase();
3590
+ const validSpecies = ['fox', 'owl', 'cat', 'robot', 'ghost', 'mushroom', 'octopus', 'dragon'];
3591
+ if (species && !validSpecies.includes(species)) {
3592
+ printError(`Unknown species "${species}". Valid: ${validSpecies.join(', ')}`);
3593
+ process.exit(1);
3594
+ }
3595
+ printInfo('Fetching leaderboard...');
3596
+ const entries = await fetchBuddyLeaderboard({ limit, species });
3597
+ if (entries.length === 0) {
3598
+ console.log();
3599
+ printWarn('No entries on the leaderboard yet.');
3600
+ printInfo('Use kbot to earn XP and enable cloud sync to appear on the leaderboard.');
3601
+ console.log();
3602
+ process.exit(0);
3603
+ }
3604
+ const SPECIES_ICONS = {
3605
+ fox: '[fox]', owl: '[owl]', cat: '[cat]', robot: '[bot]',
3606
+ ghost: '[gho]', mushroom: '[msh]', octopus: '[oct]', dragon: '[drg]',
3607
+ };
3608
+ const LEVEL_TITLES_SHORT = {
3609
+ 0: 'Novice', 1: 'Adept', 2: 'Master', 3: 'Legend',
3610
+ };
3611
+ const header = species
3612
+ ? `Buddy Leaderboard — ${species}`
3613
+ : 'Global Buddy Leaderboard';
3614
+ console.log();
3615
+ console.log(` ${chalk.bold(header)}`);
3616
+ console.log(` ${chalk.dim('─'.repeat(56))}`);
3617
+ console.log(` ${chalk.dim('#'.padStart(3))} ${chalk.dim('Species'.padEnd(7))} ${chalk.dim('Level'.padEnd(12))} ${chalk.dim('XP'.padStart(6))} ${chalk.dim('Achv'.padStart(4))} ${chalk.dim('Sessions'.padStart(8))}`);
3618
+ console.log(` ${chalk.dim('─'.repeat(56))}`);
3619
+ for (const entry of entries) {
3620
+ const icon = SPECIES_ICONS[entry.species] || entry.species.slice(0, 5);
3621
+ const title = LEVEL_TITLES_SHORT[entry.level] ?? `L${entry.level}`;
3622
+ const levelStr = `${entry.level} ${title}`;
3623
+ const rankStr = String(entry.rank).padStart(3);
3624
+ const xpStr = String(entry.xp).padStart(6);
3625
+ const achvStr = String(entry.achievement_count).padStart(4);
3626
+ const sessStr = String(entry.sessions).padStart(8);
3627
+ // Highlight top 3
3628
+ const rankColor = entry.rank === 1 ? chalk.hex('#FFD700') :
3629
+ entry.rank === 2 ? chalk.hex('#C0C0C0') :
3630
+ entry.rank === 3 ? chalk.hex('#CD7F32') : chalk.white;
3631
+ console.log(` ${rankColor(rankStr)} ${chalk.hex('#A78BFA')(icon.padEnd(7))} ${levelStr.padEnd(12)} ${chalk.hex('#4ADE80')(xpStr)} ${achvStr} ${chalk.dim(sessStr)}`);
3632
+ }
3633
+ console.log();
3634
+ console.log(` ${chalk.dim(`${entries.length} entries shown`)}`);
3635
+ console.log();
3636
+ process.exit(0);
3637
+ });
3576
3638
  buddyCmd.action(() => {
3577
3639
  buddyCmd.commands.find(c => c.name() === 'status')?.parse(['', '', 'status']);
3578
3640
  });
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * Fallback chain (used by tools):
13
13
  * 1. AbletonBridge (port 9001) — full browser API
14
- * 2. KBotBridge (port 9998) — kbot's own Remote Script
14
+ * 2. KBotBridge (port 9997) — kbot's own Remote Script
15
15
  * 3. Error with install instructions
16
16
  *
17
17
  * Follows the same singleton + newline-delimited JSON pattern as AbletonM4L.
@@ -106,7 +106,7 @@ export declare class AbletonBridgeClient {
106
106
  getEffectChain(trackIndex: number): Promise<Device[]>;
107
107
  }
108
108
  /**
109
- * Lightweight TCP probe for the kbot Remote Script on port 9998.
109
+ * Lightweight TCP probe for the kbot Remote Script on port 9997.
110
110
  * Uses the same newline-delimited JSON protocol as AbletonM4L.
111
111
  */
112
112
  export declare class KBotRemoteClient {
@@ -139,7 +139,7 @@ export declare class KBotRemoteClient {
139
139
  */
140
140
  export declare function tryAbletonBridge(): Promise<AbletonBridgeClient | null>;
141
141
  /**
142
- * Try to connect to KBotBridge Remote Script (port 9998).
142
+ * Try to connect to KBotBridge Remote Script (port 9997).
143
143
  * Returns the connected client or null if unavailable.
144
144
  */
145
145
  export declare function tryKBotRemote(): Promise<KBotRemoteClient | null>;
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * Fallback chain (used by tools):
13
13
  * 1. AbletonBridge (port 9001) — full browser API
14
- * 2. KBotBridge (port 9998) — kbot's own Remote Script
14
+ * 2. KBotBridge (port 9997) — kbot's own Remote Script
15
15
  * 3. Error with install instructions
16
16
  *
17
17
  * Follows the same singleton + newline-delimited JSON pattern as AbletonM4L.
@@ -268,9 +268,9 @@ export class AbletonBridgeClient {
268
268
  }));
269
269
  }
270
270
  }
271
- // ── KBotBridge fallback (port 9998) ────────────────────────────────────
271
+ // ── KBotBridge fallback (port 9997) ────────────────────────────────────
272
272
  /**
273
- * Lightweight TCP probe for the kbot Remote Script on port 9998.
273
+ * Lightweight TCP probe for the kbot Remote Script on port 9997.
274
274
  * Uses the same newline-delimited JSON protocol as AbletonM4L.
275
275
  */
276
276
  export class KBotRemoteClient {
@@ -280,7 +280,7 @@ export class KBotRemoteClient {
280
280
  pending = new Map();
281
281
  nextId = 1;
282
282
  buffer = '';
283
- static PORT = 9998;
283
+ static PORT = 9997;
284
284
  static HOST = '127.0.0.1';
285
285
  static TIMEOUT = 10_000;
286
286
  static CONNECT_TIMEOUT = 3_000;
@@ -431,7 +431,7 @@ export async function tryAbletonBridge() {
431
431
  return ok ? client : null;
432
432
  }
433
433
  /**
434
- * Try to connect to KBotBridge Remote Script (port 9998).
434
+ * Try to connect to KBotBridge Remote Script (port 9997).
435
435
  * Returns the connected client or null if unavailable.
436
436
  */
437
437
  export async function tryKBotRemote() {
@@ -478,7 +478,7 @@ export function formatBridgeError() {
478
478
  ' kbot\'s own Remote Script. Install:',
479
479
  ' 1. Run `kbot ableton install` or copy KBotBridge to Remote Scripts',
480
480
  ' 2. Enable in Ableton: Preferences → Link/Tempo/MIDI → Control Surface → KBotBridge',
481
- ' 3. Verify: TCP server starts on localhost:9998',
481
+ ' 3. Verify: TCP server starts on localhost:9997',
482
482
  '',
483
483
  'Both require Ableton Live to be running.',
484
484
  ].join('\n');
@@ -124,7 +124,7 @@ export interface BrowserCategory {
124
124
  child_count: number;
125
125
  }
126
126
  /**
127
- * Client for the KBotBridge Remote Script (TCP 9998).
127
+ * Client for the KBotBridge Remote Script (TCP 9997).
128
128
  *
129
129
  * This is separate from the M4L bridge (9999) because the Browser API
130
130
  * (browser.load_item) is ONLY available from Python Remote Scripts,
@@ -146,7 +146,7 @@ export declare class AbletonBrowserBridge {
146
146
  private constructor();
147
147
  static getInstance(): AbletonBrowserBridge;
148
148
  /**
149
- * Connect to the KBotBridge Remote Script on port 9998.
149
+ * Connect to the KBotBridge Remote Script on port 9997.
150
150
  * Returns true if connected and the bridge responds to ping.
151
151
  */
152
152
  connect(): Promise<boolean>;
@@ -195,12 +195,12 @@ export declare class AbletonBrowserBridge {
195
195
  */
196
196
  export declare function ensureM4L(): Promise<AbletonM4L>;
197
197
  /**
198
- * Get a connected Browser bridge instance (KBotBridge Remote Script on port 9998).
198
+ * Get a connected Browser bridge instance (KBotBridge Remote Script on port 9997).
199
199
  * Throws if not available.
200
200
  */
201
201
  export declare function ensureBrowserBridge(): Promise<AbletonBrowserBridge>;
202
202
  /**
203
- * Connect to both M4L bridge (9999) and Browser bridge (9998).
203
+ * Connect to both M4L bridge (9999) and Browser bridge (9997).
204
204
  * Returns whichever connections succeed. At least one must connect.
205
205
  */
206
206
  export declare function connectBrowser(): Promise<{
@@ -302,7 +302,7 @@ export class AbletonM4L {
302
302
  }
303
303
  }
304
304
  /**
305
- * Client for the KBotBridge Remote Script (TCP 9998).
305
+ * Client for the KBotBridge Remote Script (TCP 9997).
306
306
  *
307
307
  * This is separate from the M4L bridge (9999) because the Browser API
308
308
  * (browser.load_item) is ONLY available from Python Remote Scripts,
@@ -318,7 +318,7 @@ export class AbletonBrowserBridge {
318
318
  pending = new Map();
319
319
  nextId = 1;
320
320
  buffer = '';
321
- static PORT = 9998;
321
+ static PORT = 9997;
322
322
  static HOST = '127.0.0.1';
323
323
  static TIMEOUT = 15_000; // Browser operations can be slow
324
324
  constructor() { }
@@ -329,7 +329,7 @@ export class AbletonBrowserBridge {
329
329
  return AbletonBrowserBridge.instance;
330
330
  }
331
331
  /**
332
- * Connect to the KBotBridge Remote Script on port 9998.
332
+ * Connect to the KBotBridge Remote Script on port 9997.
333
333
  * Returns true if connected and the bridge responds to ping.
334
334
  */
335
335
  async connect() {
@@ -518,7 +518,7 @@ export async function ensureM4L() {
518
518
  return m4l;
519
519
  }
520
520
  /**
521
- * Get a connected Browser bridge instance (KBotBridge Remote Script on port 9998).
521
+ * Get a connected Browser bridge instance (KBotBridge Remote Script on port 9997).
522
522
  * Throws if not available.
523
523
  */
524
524
  export async function ensureBrowserBridge() {
@@ -531,13 +531,13 @@ export async function ensureBrowserBridge() {
531
531
  'Make sure:\n' +
532
532
  '1. Ableton Live is running\n' +
533
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' +
534
+ '3. Ableton status bar shows "KBotBridge: Listening on port 9997"\n\n' +
535
535
  'To install: kbot ableton install-bridge\n');
536
536
  }
537
537
  return bridge;
538
538
  }
539
539
  /**
540
- * Connect to both M4L bridge (9999) and Browser bridge (9998).
540
+ * Connect to both M4L bridge (9999) and Browser bridge (9997).
541
541
  * Returns whichever connections succeed. At least one must connect.
542
542
  */
543
543
  export async function connectBrowser() {
@@ -582,7 +582,7 @@ export function formatBrowserBridgeError() {
582
582
  '5. Close Preferences',
583
583
  '',
584
584
  'This runs alongside the M4L bridge — they use different ports:',
585
- '- KBotBridge: TCP 9998 (Browser API, device loading)',
585
+ '- KBotBridge: TCP 9997 (Browser API, device loading)',
586
586
  '- M4L Bridge: TCP 9999 (LOM access, clips, mixing)',
587
587
  ].join('\n');
588
588
  }
@@ -2,7 +2,7 @@
2
2
  * install-remote-script.ts — Install KBotBridge Remote Script into Ableton Live
3
3
  *
4
4
  * Copies the KBotBridge Python Remote Script to Ableton's User Library,
5
- * enabling the Browser API bridge on TCP port 9998.
5
+ * enabling the Browser API bridge on TCP port 9997.
6
6
  *
7
7
  * The Remote Script exposes Ableton's browser.load_item() API, which is
8
8
  * ONLY available from Python Remote Scripts (not from Max for Live).
@@ -2,7 +2,7 @@
2
2
  * install-remote-script.ts — Install KBotBridge Remote Script into Ableton Live
3
3
  *
4
4
  * Copies the KBotBridge Python Remote Script to Ableton's User Library,
5
- * enabling the Browser API bridge on TCP port 9998.
5
+ * enabling the Browser API bridge on TCP port 9997.
6
6
  *
7
7
  * The Remote Script exposes Ableton's browser.load_item() API, which is
8
8
  * ONLY available from Python Remote Scripts (not from Max for Live).
@@ -84,11 +84,11 @@ export async function installKBotBridge() {
84
84
  log(' 5. Close Preferences');
85
85
  log('');
86
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');
87
+ log(' - Ableton status bar shows "KBotBridge: Listening on port 9997"');
88
+ log(' - Run: echo \'{"id":1,"action":"ping"}\\n\' | nc localhost 9997');
89
89
  log('');
90
90
  log('KBotBridge runs alongside AbletonOSC — they use different ports:');
91
- log(' - KBotBridge: TCP 9998 (Browser API, device loading)');
91
+ log(' - KBotBridge: TCP 9997 (Browser API, device loading)');
92
92
  log(' - M4L Bridge: TCP 9999 (LOM access, clips, mixing)');
93
93
  log(' - AbletonOSC: UDP 11000/11001 (OSC, legacy)');
94
94
  return lines.join('\n');
@@ -0,0 +1,111 @@
1
+ /**
2
+ * mobile-mcp-client.ts — kbot <-> mobile-mcp integration
3
+ *
4
+ * Singleton client that manages the mobile-mcp server process lifecycle.
5
+ * Communicates via MCP protocol over stdio transport.
6
+ * Auto-installs @mobilenext/mobile-mcp via npm if not present.
7
+ *
8
+ * mobile-mcp provides native accessibility-tree-based automation for
9
+ * iOS and Android devices connected via USB or WiFi.
10
+ *
11
+ * @see https://github.com/mobile-next/mobile-mcp
12
+ */
13
+ export interface MobileDevice {
14
+ id: string;
15
+ name: string;
16
+ platform: 'ios' | 'android';
17
+ type: 'real' | 'simulator' | 'emulator';
18
+ version: string;
19
+ state: 'online' | 'offline';
20
+ }
21
+ export interface MobileElement {
22
+ type: string;
23
+ text?: string;
24
+ label?: string;
25
+ name?: string;
26
+ value?: string;
27
+ identifier?: string;
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ }
33
+ export interface MobileScreenSize {
34
+ width: number;
35
+ height: number;
36
+ }
37
+ export declare class MobileMCPClient {
38
+ private static instance;
39
+ private process;
40
+ private messageId;
41
+ private pending;
42
+ private buffer;
43
+ private initialized;
44
+ private activeDeviceId;
45
+ static getInstance(): MobileMCPClient;
46
+ /** Whether the MCP server process is running and initialized */
47
+ get isConnected(): boolean;
48
+ /** The device ID currently being controlled */
49
+ get currentDeviceId(): string | null;
50
+ /** Start the mobile-mcp server process and perform MCP handshake */
51
+ start(): Promise<void>;
52
+ /** Stop the mobile-mcp server process */
53
+ stop(): void;
54
+ private parseMessages;
55
+ private sendRequest;
56
+ private sendNotification;
57
+ /** Call a tool on the mobile-mcp server */
58
+ callTool(toolName: string, args: Record<string, unknown>): Promise<unknown>;
59
+ /** Extract text content from an MCP tool result */
60
+ extractText(result: unknown): string;
61
+ /** Extract image content (base64) from an MCP tool result */
62
+ extractImage(result: unknown): {
63
+ data: string;
64
+ mimeType: string;
65
+ } | null;
66
+ /** List all available devices */
67
+ listDevices(): Promise<MobileDevice[]>;
68
+ /** Set the active device for subsequent operations */
69
+ setActiveDevice(deviceId: string): void;
70
+ /** Get the active device ID, throwing if none set */
71
+ private requireDevice;
72
+ /** List apps on the active device */
73
+ listApps(deviceId?: string): Promise<string>;
74
+ /** Launch an app by bundle ID */
75
+ launchApp(packageName: string, deviceId?: string): Promise<string>;
76
+ /** Take a screenshot, returns base64 image data */
77
+ takeScreenshot(deviceId?: string): Promise<{
78
+ data: string;
79
+ mimeType: string;
80
+ } | string>;
81
+ /** Save screenshot to a file */
82
+ saveScreenshot(saveTo: string, deviceId?: string): Promise<string>;
83
+ /** List UI elements on screen via accessibility tree */
84
+ listElements(deviceId?: string): Promise<string>;
85
+ /** Tap at coordinates */
86
+ tap(x: number, y: number, deviceId?: string): Promise<string>;
87
+ /** Swipe on screen */
88
+ swipe(direction: 'up' | 'down' | 'left' | 'right', opts?: {
89
+ x?: number;
90
+ y?: number;
91
+ distance?: number;
92
+ deviceId?: string;
93
+ }): Promise<string>;
94
+ /** Type text */
95
+ typeText(text: string, submit?: boolean, deviceId?: string): Promise<string>;
96
+ /** Press a device button */
97
+ pressButton(button: 'HOME' | 'BACK' | 'VOLUME_UP' | 'VOLUME_DOWN' | 'ENTER', deviceId?: string): Promise<string>;
98
+ /** Get screen size */
99
+ getScreenSize(deviceId?: string): Promise<string>;
100
+ /** Open a URL in the device browser */
101
+ openUrl(url: string, deviceId?: string): Promise<string>;
102
+ /** Get device orientation */
103
+ getOrientation(deviceId?: string): Promise<string>;
104
+ /** Terminate an app */
105
+ terminateApp(packageName: string, deviceId?: string): Promise<string>;
106
+ /** Double tap at coordinates */
107
+ doubleTap(x: number, y: number, deviceId?: string): Promise<string>;
108
+ /** Long press at coordinates */
109
+ longPress(x: number, y: number, duration?: number, deviceId?: string): Promise<string>;
110
+ }
111
+ //# sourceMappingURL=mobile-mcp-client.d.ts.map