@mobileaidev/ai-app-bridge 0.2.3 → 0.2.5

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/README.md CHANGED
@@ -27,6 +27,27 @@ session start. Set `AI_APP_BRIDGE_MCP_SURFACE=full` before launching
27
27
  `ai-app-bridge-mcp` only when a client needs the legacy one-tool-per-command
28
28
  surface.
29
29
 
30
+ For multi-step app automation, call `run` with `command: "batch"`. Batch steps
31
+ run serially in one MCP call, so a failed step can stop and mark the remaining
32
+ steps as skipped without mixing results from different commands:
33
+
34
+ ```json
35
+ {
36
+ "command": "batch",
37
+ "arguments": {
38
+ "defaults": {
39
+ "packageName": "io.github.mobileaidev.aiappbridge.sample"
40
+ },
41
+ "steps": [
42
+ { "id": "launch", "command": "launch-app" },
43
+ { "id": "wait-home", "command": "wait-text", "arguments": { "targetText": "Home" } },
44
+ { "id": "capture-logs", "command": "logs", "arguments": { "limit": 20 } }
45
+ ],
46
+ "stopOnError": true
47
+ }
48
+ }
49
+ ```
50
+
30
51
  WebView network and console capture use Android WebView DevTools/CDP when the
31
52
  target app is debuggable and WebView debugging is enabled.
32
53
 
@@ -976,6 +976,13 @@ function normalizeBridgeError(error) {
976
976
  suggestion: 'Check the device state and retry; if the bridge port is known, pass --port to skip package port discovery.',
977
977
  };
978
978
  }
979
+ if (error?.aiAppBridgePackageMismatch || lower.includes('bridge package mismatch')) {
980
+ return {
981
+ code: 'bridge_package_mismatch',
982
+ message,
983
+ suggestion: 'The resolved bridge port belongs to another package. Relaunch the target app and retry with the explicit packageName.',
984
+ };
985
+ }
979
986
  if (error?.aiAppBridgePortDiscovery || lower.includes('bridge port discovery failed') || lower.includes('run-as') || lower.includes('package not found')) {
980
987
  return {
981
988
  code: 'bridge_port_discovery_failed',
@@ -1001,21 +1008,38 @@ function firstErrorLine(error) {
1001
1008
  return String(error?.message || error || 'unknown_error').split(/\r?\n/).find(Boolean) || 'unknown_error';
1002
1009
  }
1003
1010
 
1004
- async function bridgeGet(ctx, requestPath) {
1005
- await ensureForward(ctx);
1006
- const body = await httpGet(bridgeUrl(ctx, requestPath));
1007
- return JSON.parse(body);
1008
- }
1009
-
1010
- async function bridgePost(ctx, requestPath, payload) {
1011
- await ensureForward(ctx);
1012
- const body = await httpPost(bridgeUrl(ctx, requestPath), payload);
1013
- return JSON.parse(body);
1014
- }
1015
-
1016
- function bridgeUrl(ctx, requestPath) {
1017
- return `http://127.0.0.1:${ctx.hostPort || ctx.port}${requestPath}`;
1018
- }
1011
+ async function bridgeGet(ctx, requestPath) {
1012
+ await ensureForward(ctx);
1013
+ const body = await httpGet(bridgeUrl(ctx, requestPath));
1014
+ const payload = JSON.parse(body);
1015
+ verifyBridgeTargetPackage(ctx, payload, requestPath);
1016
+ return payload;
1017
+ }
1018
+
1019
+ async function bridgePost(ctx, requestPath, payload) {
1020
+ await ensureForward(ctx);
1021
+ const body = await httpPost(bridgeUrl(ctx, requestPath), payload);
1022
+ const responsePayload = JSON.parse(body);
1023
+ verifyBridgeTargetPackage(ctx, responsePayload, requestPath);
1024
+ return responsePayload;
1025
+ }
1026
+
1027
+ function bridgeUrl(ctx, requestPath) {
1028
+ return `http://127.0.0.1:${ctx.hostPort || ctx.port}${requestPath}`;
1029
+ }
1030
+
1031
+ function verifyBridgeTargetPackage(ctx, payload, requestPath) {
1032
+ if (!ctx.explicitPackageName || !payload || typeof payload !== 'object') return;
1033
+ const responsePackageName = payload.app && typeof payload.app === 'object'
1034
+ ? payload.app.packageName
1035
+ : undefined;
1036
+ if (!responsePackageName || responsePackageName === ctx.packageName) return;
1037
+ const error = new Error(`bridge package mismatch for ${requestPath}: expected ${ctx.packageName}, got ${responsePackageName}`);
1038
+ error.aiAppBridgePackageMismatch = true;
1039
+ error.expectedPackageName = ctx.packageName;
1040
+ error.actualPackageName = responsePackageName;
1041
+ throw error;
1042
+ }
1019
1043
 
1020
1044
  function httpGet(url) {
1021
1045
  return new Promise((resolve, reject) => {
@@ -4311,16 +4335,17 @@ module.exports = {
4311
4335
  parseForegroundWindow,
4312
4336
  chooseWebViewDevToolsSocket,
4313
4337
  chooseWebViewPage,
4314
- shapeNetworkCapture,
4315
- compactNetworkRecord,
4316
- pruneGeneratedArtifacts,
4317
- shouldSkipInstallerTapForInstalledPackage,
4338
+ shapeNetworkCapture,
4339
+ compactNetworkRecord,
4340
+ pruneGeneratedArtifacts,
4341
+ shouldSkipInstallerTapForInstalledPackage,
4318
4342
  shouldDismissKeyboardForPoint,
4319
4343
  shouldUseDefaultPortFallback,
4320
4344
  screenshotOutputPath,
4321
- statusSearchText,
4322
- uiautomatorLockPath,
4323
- waitTextConditionsMet,
4324
- withFileLock,
4325
- };
4345
+ statusSearchText,
4346
+ uiautomatorLockPath,
4347
+ verifyBridgeTargetPackage,
4348
+ waitTextConditionsMet,
4349
+ withFileLock,
4350
+ };
4326
4351
 
package/bin/mcp-server.js CHANGED
@@ -17,13 +17,15 @@ const serverInstructions = [
17
17
  ].join(' ');
18
18
 
19
19
  let buffer = Buffer.alloc(0);
20
-
21
- process.stdin.on('data', (chunk) => {
22
- buffer = Buffer.concat([buffer, chunk]);
23
- drainMessages();
24
- });
25
-
26
- process.stdin.on('error', () => {});
20
+
21
+ function startServer() {
22
+ process.stdin.on('data', (chunk) => {
23
+ buffer = Buffer.concat([buffer, chunk]);
24
+ drainMessages();
25
+ });
26
+
27
+ process.stdin.on('error', () => {});
28
+ }
27
29
 
28
30
  function drainMessages() {
29
31
  while (true) {
@@ -444,6 +446,7 @@ const commandDefinitions = [
444
446
  { command: 'webview-console', domain: 'webview', summary: 'Capture WebView console/log events through CDP.', targetApp: true, options: ['serial', 'packageName', 'webviewPort', 'socketName', 'targetId', 'pageUrlFilter', 'durationMs', 'script', 'maxEvents'] },
445
447
  { command: 'forward', domain: 'advanced', summary: 'Create the ADB port forward for the bridge.', targetApp: true, options: ['serial', 'packageName', 'port'] },
446
448
  { command: 'remove-forward', domain: 'advanced', summary: 'Remove the ADB port forward for the bridge.', options: ['serial', 'port'] },
449
+ { command: 'batch', domain: 'advanced', summary: 'Run multiple AI App Bridge commands serially in one MCP call.', options: ['defaults', 'steps', 'stopOnError', 'includeRaw', 'maxRawChars'] },
447
450
  { command: 'smoke', domain: 'diagnostics', summary: 'Run the native sample smoke test.', options: ['serial', 'packageName', 'outFile', 'artifactDir', 'skipFlutterLaunch'] },
448
451
  ];
449
452
 
@@ -550,6 +553,9 @@ async function runGeneric(args = {}) {
550
553
  commandArgs[key] = args[key];
551
554
  }
552
555
  }
556
+ if (command === 'batch') {
557
+ return runBatch(commandArgs);
558
+ }
553
559
  return runBridgeChecked(command, commandArgs);
554
560
  }
555
561
 
@@ -564,10 +570,234 @@ function runBridgeChecked(command, args = {}) {
564
570
  }
565
571
  return runBridge(command, args);
566
572
  }
567
-
568
- async function runBridge(command, args) {
569
- const cliArgs = [cliScript, command];
570
- addCommonArgs(cliArgs, args);
573
+
574
+ async function runBatch(args = {}, runner = runBridgeChecked) {
575
+ const startedAtMs = Date.now();
576
+ const mode = args.mode ? String(args.mode) : 'serial';
577
+ if (mode !== 'serial') {
578
+ return toolJson({ ok: false, error: 'batch_mode_not_supported', mode }, true);
579
+ }
580
+ const steps = Array.isArray(args.steps) ? args.steps : [];
581
+ if (steps.length === 0) {
582
+ return toolJson({ ok: false, error: 'batch_steps_required' }, true);
583
+ }
584
+ const maxSteps = args.maxSteps === undefined ? 30 : Number(args.maxSteps);
585
+ if (!Number.isInteger(maxSteps) || maxSteps < 1) {
586
+ return toolJson({ ok: false, error: 'invalid_max_steps', maxSteps: args.maxSteps }, true);
587
+ }
588
+ if (steps.length > maxSteps) {
589
+ return toolJson({ ok: false, error: 'batch_too_many_steps', stepCount: steps.length, maxSteps }, true);
590
+ }
591
+
592
+ const defaults = args.defaults && typeof args.defaults === 'object' ? { ...args.defaults } : {};
593
+ for (const key of ['adb', 'serial', 'port', 'packageName', 'artifactDir']) {
594
+ if (args[key] !== undefined && defaults[key] === undefined) {
595
+ defaults[key] = args[key];
596
+ }
597
+ }
598
+
599
+ const normalizedSteps = [];
600
+ const seenIds = new Set();
601
+ for (let index = 0; index < steps.length; index += 1) {
602
+ const rawStep = steps[index] && typeof steps[index] === 'object' ? steps[index] : {};
603
+ const stepId = String(rawStep.id || `step_${index + 1}`);
604
+ if (seenIds.has(stepId)) {
605
+ return toolJson({ ok: false, error: 'duplicate_batch_step_id', stepId }, true);
606
+ }
607
+ seenIds.add(stepId);
608
+ const command = normalizeCommandName(rawStep.command);
609
+ if (!commandByName.has(command)) {
610
+ return toolJson({ ok: false, error: 'unknown_batch_step_command', stepId, command: rawStep.command || '' }, true);
611
+ }
612
+ if (command === 'batch') {
613
+ return toolJson({ ok: false, error: 'nested_batch_not_supported', stepId }, true);
614
+ }
615
+ normalizedSteps.push({ ...rawStep, id: stepId, command });
616
+ }
617
+
618
+ const stopOnError = args.stopOnError !== false;
619
+ const includeRaw = Boolean(args.includeRaw);
620
+ const maxRawChars = args.maxRawChars === undefined ? 4000 : Number(args.maxRawChars);
621
+ if (!Number.isInteger(maxRawChars) || maxRawChars < 0) {
622
+ return toolJson({ ok: false, error: 'invalid_max_raw_chars', maxRawChars: args.maxRawChars }, true);
623
+ }
624
+ const results = [];
625
+ let stopped = false;
626
+
627
+ for (const step of normalizedSteps) {
628
+ if (stopped) {
629
+ results.push({
630
+ id: step.id,
631
+ command: step.command,
632
+ status: 'skipped',
633
+ ok: false,
634
+ skipped: true,
635
+ reason: 'stopOnError',
636
+ });
637
+ continue;
638
+ }
639
+
640
+ const stepStartedAtMs = Date.now();
641
+ const stepArgs = {
642
+ ...defaults,
643
+ ...(step.arguments && typeof step.arguments === 'object' ? step.arguments : {}),
644
+ };
645
+ for (const key of ['adb', 'serial', 'port', 'packageName']) {
646
+ if (step[key] !== undefined) {
647
+ stepArgs[key] = step[key];
648
+ }
649
+ }
650
+ try {
651
+ const toolResult = await runner(step.command, stepArgs);
652
+ const parsed = parseToolResult(toolResult);
653
+ const passed = !parsed.isError && parsed.payload?.ok !== false;
654
+ const stepResult = {
655
+ id: step.id,
656
+ command: step.command,
657
+ status: passed ? 'passed' : 'failed',
658
+ ok: passed,
659
+ packageName: stepArgs.packageName,
660
+ port: stepArgs.port,
661
+ durationMs: Date.now() - stepStartedAtMs,
662
+ summary: summarizeToolPayload(parsed),
663
+ };
664
+ if (!passed) {
665
+ stepResult.error = parsed.payload?.error || firstTextLine(parsed.text) || 'command_failed';
666
+ }
667
+ if (includeRaw) {
668
+ stepResult.result = parsed.payload || undefined;
669
+ stepResult.rawText = parsed.payload ? undefined : truncateText(parsed.text, maxRawChars);
670
+ }
671
+ results.push(stepResult);
672
+ if (!passed && stopOnError) {
673
+ stopped = true;
674
+ }
675
+ } catch (error) {
676
+ const stepResult = {
677
+ id: step.id,
678
+ command: step.command,
679
+ status: 'failed',
680
+ ok: false,
681
+ packageName: stepArgs.packageName,
682
+ port: stepArgs.port,
683
+ durationMs: Date.now() - stepStartedAtMs,
684
+ error: error.message || String(error),
685
+ };
686
+ results.push(stepResult);
687
+ if (stopOnError) {
688
+ stopped = true;
689
+ }
690
+ }
691
+ }
692
+
693
+ const failed = results.filter((item) => item.status === 'failed').length;
694
+ const skipped = results.filter((item) => item.status === 'skipped').length;
695
+ const passed = results.filter((item) => item.status === 'passed').length;
696
+ return toolJson({
697
+ ok: failed === 0,
698
+ batchId: args.batchId || generatedBatchId(),
699
+ mode,
700
+ stopOnError,
701
+ stepCount: normalizedSteps.length,
702
+ passed,
703
+ failed,
704
+ skipped,
705
+ durationMs: Date.now() - startedAtMs,
706
+ steps: results,
707
+ }, failed > 0);
708
+ }
709
+
710
+ async function runBridge(command, args) {
711
+ return runProcess(buildBridgeCliArgs(command, args));
712
+ }
713
+
714
+ function parseToolResult(toolResult) {
715
+ const text = String(toolResult?.content?.[0]?.text || '');
716
+ try {
717
+ return {
718
+ isError: Boolean(toolResult?.isError),
719
+ text,
720
+ payload: JSON.parse(text),
721
+ };
722
+ } catch (_) {
723
+ return {
724
+ isError: Boolean(toolResult?.isError),
725
+ text,
726
+ payload: null,
727
+ };
728
+ }
729
+ }
730
+
731
+ function summarizeToolPayload(parsed) {
732
+ const payload = parsed.payload;
733
+ if (!payload || typeof payload !== 'object') {
734
+ return { text: truncateText(parsed.text, 500) };
735
+ }
736
+ const summary = {
737
+ ok: payload.ok,
738
+ error: payload.error || null,
739
+ };
740
+ if (payload.packageName) summary.packageName = payload.packageName;
741
+ if (payload.app?.packageName) summary.app = payload.app.packageName;
742
+ if (payload.activity) summary.activity = payload.activity;
743
+ if (payload.component) summary.component = payload.component;
744
+ if (payload.transport) summary.transport = payload.transport;
745
+ if (payload.source) summary.source = payload.source;
746
+ if (payload.path) summary.path = payload.path;
747
+ if (payload.debugBridge) {
748
+ summary.bridge = {
749
+ version: payload.debugBridge.version,
750
+ port: payload.debugBridge.port,
751
+ };
752
+ }
753
+ if (payload.count !== undefined) summary.count = payload.count;
754
+ if (payload.nodeCount !== undefined) summary.nodeCount = payload.nodeCount;
755
+ if (Array.isArray(payload.items)) summary.items = payload.items.length;
756
+ if (payload.values && typeof payload.values === 'object') {
757
+ summary.values = Object.keys(payload.values).length;
758
+ }
759
+ if (payload.counts) summary.counts = payload.counts;
760
+ if (Array.isArray(payload.requests)) summary.requests = payload.requests.length;
761
+ if (Array.isArray(payload.console)) summary.console = payload.console.length;
762
+ if (payload.flutter?.layout?.operable) {
763
+ summary.flutterOperable = {
764
+ ok: payload.flutter.layout.operable.ok,
765
+ count: payload.flutter.layout.operable.count,
766
+ };
767
+ }
768
+ if (payload.result && typeof payload.result === 'object') {
769
+ summary.result = {
770
+ ok: payload.result.ok,
771
+ error: payload.result.error || null,
772
+ value: truncateText(payload.result.value, 200),
773
+ bodyText: truncateText(payload.result.bodyText, 200),
774
+ };
775
+ }
776
+ return summary;
777
+ }
778
+
779
+ function truncateText(value, maxChars) {
780
+ if (value === undefined || value === null) return value;
781
+ const text = String(value);
782
+ if (text.length <= maxChars) return text;
783
+ return `${text.slice(0, maxChars)}...`;
784
+ }
785
+
786
+ function firstTextLine(value) {
787
+ const lines = String(value || '').split(/\r?\n/).filter((line) => line.trim());
788
+ return lines.find((line) => {
789
+ const text = line.trim().toLowerCase();
790
+ return text !== 'stderr:' && text !== 'stdout:';
791
+ }) || lines[0] || '';
792
+ }
793
+
794
+ function generatedBatchId() {
795
+ return `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
796
+ }
797
+
798
+ function buildBridgeCliArgs(command, args = {}) {
799
+ const cliArgs = [cliScript, command];
800
+ addCommonArgs(cliArgs, args);
571
801
  addArg(cliArgs, 'initial-route', args.initialRoute);
572
802
  addArg(cliArgs, 'activity', args.activity);
573
803
  addArg(cliArgs, 'component', args.component);
@@ -601,33 +831,45 @@ async function runBridge(command, args) {
601
831
  addArg(cliArgs, 'page-url-filter', args.pageUrlFilter);
602
832
  addArg(cliArgs, 'url-filter', args.urlFilter);
603
833
  addArg(cliArgs, 'method', args.method);
604
- addArg(cliArgs, 'status-code', args.statusCode);
605
- addArg(cliArgs, 'compact', args.compact);
606
- addArg(cliArgs, 'full', args.full);
607
- addArg(cliArgs, 'no-bodies', args.noBodies);
608
- addArg(cliArgs, 'duration-ms', args.durationMs);
609
- addArg(cliArgs, 'include-response-body', args.includeResponseBody);
610
- addArg(cliArgs, 'body-max-bytes', args.bodyMaxBytes);
611
- addArg(cliArgs, 'max-events', args.maxEvents);
612
- addArg(cliArgs, 'keep-forward', args.keepForward);
613
- addArg(cliArgs, 'key-code', args.keyCode);
614
- addArg(cliArgs, 'permission', args.permission);
615
- addArg(cliArgs, 'op', args.op);
616
- addArg(cliArgs, 'mode', args.mode);
617
- addArg(cliArgs, 'script', args.script);
618
- addArg(cliArgs, 'selector', args.selector);
834
+ addArg(cliArgs, 'status-code', args.statusCode);
835
+ addArg(cliArgs, 'compact', args.compact);
836
+ addArg(cliArgs, 'full', args.full);
837
+ addArg(cliArgs, 'text-filter', args.textFilter);
838
+ addArg(cliArgs, 'resource-id-filter', args.resourceIdFilter);
839
+ addArg(cliArgs, 'class-filter', args.classFilter);
840
+ addArg(cliArgs, 'visible-only', args.visibleOnly);
841
+ addArg(cliArgs, 'max-nodes', args.maxNodes);
842
+ addArg(cliArgs, 'max-depth', args.maxDepth);
843
+ addArg(cliArgs, 'no-bodies', args.noBodies);
844
+ addArg(cliArgs, 'duration-ms', args.durationMs);
845
+ addArg(cliArgs, 'include-response-body', args.includeResponseBody);
846
+ addArg(cliArgs, 'body-max-bytes', args.bodyMaxBytes);
847
+ addArg(cliArgs, 'max-events', args.maxEvents);
848
+ addArg(cliArgs, 'keep-forward', args.keepForward);
849
+ addArg(cliArgs, 'key-code', args.keyCode);
850
+ addArg(cliArgs, 'payload', args.payload);
851
+ addArg(cliArgs, 'delta', args.delta);
852
+ addArg(cliArgs, 'max-swipes', args.maxSwipes);
853
+ addArg(cliArgs, 'permission', args.permission);
854
+ addArg(cliArgs, 'op', args.op);
855
+ addArg(cliArgs, 'mode', args.mode);
856
+ addArg(cliArgs, 'script', args.script);
857
+ addArg(cliArgs, 'selector', args.selector);
619
858
  addArg(cliArgs, 'target-text', args.targetText);
620
859
  addArg(cliArgs, 'value', args.value);
621
860
  addArg(cliArgs, 'exact', args.exact);
622
861
  addArg(cliArgs, 'button-text', args.buttonText);
623
862
  addArg(cliArgs, 'resource-id', args.resourceId);
624
- addArg(cliArgs, 'attempts', args.attempts);
625
- addArg(cliArgs, 'interval-ms', args.intervalMs);
626
- addArg(cliArgs, 'delta-x', args.deltaX);
627
- addArg(cliArgs, 'delta-y', args.deltaY);
628
- addArg(cliArgs, 'since-id', args.sinceId);
629
- addArg(cliArgs, 'since-ms', args.sinceMs);
630
- addArg(cliArgs, 'limit', args.limit);
863
+ addArg(cliArgs, 'attempts', args.attempts);
864
+ addArg(cliArgs, 'interval-ms', args.intervalMs);
865
+ addArg(cliArgs, 'delta-x', args.deltaX);
866
+ addArg(cliArgs, 'delta-y', args.deltaY);
867
+ addArg(cliArgs, 'require-text', args.requireText);
868
+ addArg(cliArgs, 'absent-text', args.absentText);
869
+ addArg(cliArgs, 'require-activity', args.requireActivity);
870
+ addArg(cliArgs, 'since-id', args.sinceId);
871
+ addArg(cliArgs, 'since-ms', args.sinceMs);
872
+ addArg(cliArgs, 'limit', args.limit);
631
873
  addArg(cliArgs, 'pid', args.pid);
632
874
  addArg(cliArgs, 'app-pid', args.appPid);
633
875
  addArg(cliArgs, 'tag', args.tag);
@@ -635,19 +877,21 @@ async function runBridge(command, args) {
635
877
  addArg(cliArgs, 'grep', args.grep);
636
878
  addArg(cliArgs, 'lines', args.lines);
637
879
  addArg(cliArgs, 'since', args.since);
638
- addArg(cliArgs, 'follow', args.follow);
639
- addArg(cliArgs, 'duration-sec', args.durationSec);
640
- addArg(cliArgs, 'clear', args.clear);
641
- return runProcess(cliArgs);
642
- }
880
+ addArg(cliArgs, 'follow', args.follow);
881
+ addArg(cliArgs, 'duration-sec', args.durationSec);
882
+ addArg(cliArgs, 'clear', args.clear);
883
+ addArg(cliArgs, 'skip-flutter-launch', args.skipFlutterLaunch);
884
+ return cliArgs;
885
+ }
643
886
 
644
887
  async function runSmoke(args) {
645
888
  const cliArgs = [cliScript, 'smoke'];
646
- addCommonArgs(cliArgs, args);
647
- addArg(cliArgs, 'out-file', args.outFile);
648
- addArg(cliArgs, 'artifact-dir', args.artifactDir || defaultArtifactDirFor('smoke', args));
649
- return runProcess(cliArgs);
650
- }
889
+ addCommonArgs(cliArgs, args);
890
+ addArg(cliArgs, 'out-file', args.outFile);
891
+ addArg(cliArgs, 'artifact-dir', args.artifactDir || defaultArtifactDirFor('smoke', args));
892
+ addArg(cliArgs, 'skip-flutter-launch', args.skipFlutterLaunch);
893
+ return runProcess(cliArgs);
894
+ }
651
895
 
652
896
  function defaultArtifactDirFor(command, args) {
653
897
  if (args.outFile) return '';
@@ -776,7 +1020,16 @@ function send(message) {
776
1020
  process.stdout.write(body);
777
1021
  }
778
1022
 
779
- function writeLog(text) {
780
- process.stderr.write(`${text}\n`);
781
- }
782
-
1023
+ function writeLog(text) {
1024
+ process.stderr.write(`${text}\n`);
1025
+ }
1026
+
1027
+ if (require.main === module) {
1028
+ startServer();
1029
+ }
1030
+
1031
+ module.exports = {
1032
+ buildBridgeCliArgs,
1033
+ runBatch,
1034
+ startServer,
1035
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mobileaidev/ai-app-bridge",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Desktop CLI and MCP server for AI App Bridge.",
5
5
  "repository": {
6
6
  "type": "git",