expo-agent-bridge 0.1.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/LICENSE +21 -0
- package/README.md +234 -0
- package/bin/cli.js +9 -0
- package/dist/DevAgentBridge.d.ts +31 -0
- package/dist/DevAgentBridge.d.ts.map +1 -0
- package/dist/DevAgentBridge.js +355 -0
- package/dist/DevAgentBridge.js.map +1 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +262 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +276 -0
- package/dist/server.js.map +1 -0
- package/package.json +68 -0
- package/skills/expo-agent-bridge/SKILL.md +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fs02
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# expo-agent-bridge ๐
|
|
2
|
+
|
|
3
|
+
**Autonomous AI Agent Dev Bridge for Expo & React Native apps.**
|
|
4
|
+
|
|
5
|
+
Give AI coding agents (Antigravity, Cursor, Claude Code, Windsurf, Devin) complete visual feedback and device control over your live running mobile app โ **without USB cables, without custom tunnels, and fully compatible with WSL & Wi-Fi.**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## โจ Features
|
|
10
|
+
|
|
11
|
+
- ๐ธ **Live Visual Feedback (`get_screenshot`)**: Real-time full-screen captures directly from physical devices or simulators.
|
|
12
|
+
- ๐ชต **Real-Time Error Streaming (`get_logs`)**: Captures `console.error`, `console.warn`, and unhandled JS exceptions with full stack traces.
|
|
13
|
+
- ๐ **Dual-Layer Reload (`reload`)**: Reloads the app via Metro broadcast and in-app `DevSettings.reload` (works even if JS thread is stuck).
|
|
14
|
+
- ๐งญ **Navigation Control (`navigate` & `get_route`)**: Navigate Expo Router screens and verify active route segments.
|
|
15
|
+
- ๐ฏ **Targeted Element Interaction (`tap`, `scroll`, `type_text`)**: Interacts with UI components via `testID` or hooks without fragile pixel-coordinate guessing.
|
|
16
|
+
- ๐ **Element Discovery (`get_elements`)**: Lists all currently mounted interactive buttons and inputs on screen.
|
|
17
|
+
- ๐งน **Storage & State Reset (`reset_storage`, `get_state`)**: Clears AsyncStorage on the fly to test clean first-install experience.
|
|
18
|
+
- ๐ชถ **Zero Production Footprint**: Evaluates to `null` and empty functions when `!__DEV__`.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ๐ Quickstart
|
|
23
|
+
|
|
24
|
+
### 1. Install in your Expo project
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Using npm
|
|
28
|
+
npm install expo-agent-bridge react-native-view-shot
|
|
29
|
+
|
|
30
|
+
# Using yarn
|
|
31
|
+
yarn add expo-agent-bridge react-native-view-shot
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For a local sibling checkout of this repository, install the bridge explicitly
|
|
35
|
+
from that checkout:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
yarn add expo-agent-bridge@file:../expo-agent-bridge react-native-view-shot
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This local-file setup is for development only. Do not leave a `file:../โฆ`
|
|
42
|
+
bridge dependency in an EAS or other remote production build: the sibling
|
|
43
|
+
checkout is not part of the build upload. Use a published package there, or
|
|
44
|
+
remove the bridge from the release dependency graph and resolve it only in
|
|
45
|
+
local development.
|
|
46
|
+
|
|
47
|
+
### 2. Initialize Agent Config & Skills
|
|
48
|
+
|
|
49
|
+
Run in your project root:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx expo-agent-bridge init
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The default Antigravity profile generates:
|
|
56
|
+
- `.agents/mcp_config.json` (MCP server configuration)
|
|
57
|
+
- `.agents/skills/expo-agent-bridge/SKILL.md` (MCP-first instructions with CLI fallback)
|
|
58
|
+
|
|
59
|
+
Choose another supported agent profile when needed:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx expo-agent-bridge init --agent claude-code
|
|
63
|
+
npx expo-agent-bridge init --agent cursor
|
|
64
|
+
npx expo-agent-bridge init --agent windsurf
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If multiple Expo/Metro servers are running, assign each project its own port
|
|
68
|
+
and pass it during initialization:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx expo-agent-bridge init --metro-port 8082
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Start that app on the same port, for example `npx expo start --port 8082`.
|
|
75
|
+
|
|
76
|
+
For direct CLI commands, pass the same port with `--metro-port <port>` (or `--port <port>`). The option may appear anywhere after the command:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx expo-agent-bridge screenshot /tmp/screen.png --metro-port 8082
|
|
80
|
+
npx expo-agent-bridge navigate /settings --metro-port=8082
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The generated MCP configuration pins the bridge to that projectโs port.
|
|
84
|
+
|
|
85
|
+
Each profile writes its MCP configuration and skill to that agent's project directory.
|
|
86
|
+
For an unsupported or custom agent, specify the skill location directly:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx expo-agent-bridge init \
|
|
90
|
+
--skills-dir .my-agent/skills
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Existing bridge skills are left untouched; pass `--force` to replace one.
|
|
94
|
+
|
|
95
|
+
MCP is enabled by default, but the generated skill falls back to the direct CLI
|
|
96
|
+
when an MCP call is unavailable or cannot reach the running app. To configure a
|
|
97
|
+
CLI-only project, use `npx expo-agent-bridge init --no-mcp`.
|
|
98
|
+
|
|
99
|
+
### 3. Mount in your Root Layout
|
|
100
|
+
|
|
101
|
+
In your root layout (e.g. `app/_layout.tsx` or `App.tsx`), load the component
|
|
102
|
+
only in development:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import React from 'react';
|
|
106
|
+
|
|
107
|
+
// Keeps the bridge component out of the production module graph.
|
|
108
|
+
const DevAgentBridge = __DEV__
|
|
109
|
+
? (require('expo-agent-bridge').AgentBridge as React.ComponentType)
|
|
110
|
+
: null;
|
|
111
|
+
|
|
112
|
+
export default function RootLayout() {
|
|
113
|
+
return (
|
|
114
|
+
<>
|
|
115
|
+
{__DEV__ && DevAgentBridge ? <DevAgentBridge /> : null}
|
|
116
|
+
{/* Rest of your app */}
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The package still has to be resolvable when Metro builds a development bundle.
|
|
123
|
+
For remote release builds, either use a published package or configure Metro
|
|
124
|
+
to map `expo-agent-bridge` to a local no-op module, as the app's release setup
|
|
125
|
+
requires.
|
|
126
|
+
|
|
127
|
+
### 4. Run Expo
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx expo start
|
|
131
|
+
# Or on WSL:
|
|
132
|
+
npx expo start --tunnel
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Scan the QR code with your iPhone/Android device. Your AI agent can now immediately inspect and control the app!
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## ๐ ๏ธ Bridge Commands
|
|
140
|
+
|
|
141
|
+
| Command | Description |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `screenshot [file]` | Captures current mobile screen as PNG |
|
|
144
|
+
| `logs` | Streams recent errors, warnings, and exceptions |
|
|
145
|
+
| `reload` | Reloads the app bundle on device |
|
|
146
|
+
| `route` | Returns the active route |
|
|
147
|
+
| `elements` | Lists mounted interactive elements |
|
|
148
|
+
| `state` | Inspects exposed custom state |
|
|
149
|
+
| `reset-storage` | Clears AsyncStorage |
|
|
150
|
+
| `dev-menu` | Opens the developer menu |
|
|
151
|
+
| `navigate <route>` | Navigates to an Expo Router route |
|
|
152
|
+
| `tap <testID>` | Taps an element by test ID |
|
|
153
|
+
| `scroll <up\|down> [amount]` | Scrolls the active view |
|
|
154
|
+
| `type-text <testID> <text>` | Types text into a TextInput |
|
|
155
|
+
|
|
156
|
+
## ๐ฅ๏ธ Direct CLI Commands
|
|
157
|
+
|
|
158
|
+
The bridge can also be used directly from a shell without creating a temporary
|
|
159
|
+
JavaScript client:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx expo-agent-bridge screenshot /tmp/screen.png
|
|
163
|
+
npx expo-agent-bridge logs
|
|
164
|
+
npx expo-agent-bridge route
|
|
165
|
+
npx expo-agent-bridge navigate /settings
|
|
166
|
+
npx expo-agent-bridge tap settings-button
|
|
167
|
+
npx expo-agent-bridge scroll down 300
|
|
168
|
+
npx expo-agent-bridge type-text search-input "Mecca"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
These commands are the supported fallback for the MCP tools. They use the same
|
|
172
|
+
live app connection and do not require temporary JavaScript files.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## ๐งฉ Element Registration
|
|
177
|
+
|
|
178
|
+
Components with `testID` can be registered declaratively:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useAgentElement } from 'expo-agent-bridge';
|
|
182
|
+
|
|
183
|
+
function CustomButton({ onPress, title }) {
|
|
184
|
+
useAgentElement('my-btn', { onPress, title, type: 'button' });
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<TouchableOpacity testID="my-btn" onPress={onPress}>
|
|
188
|
+
<Text>{title}</Text>
|
|
189
|
+
</TouchableOpacity>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Or imperatively:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { registerElement, unregisterElement } from 'expo-agent-bridge';
|
|
198
|
+
|
|
199
|
+
registerElement('submit-btn', { onPress: handleSubmit, title: 'Submit' });
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## ๐๏ธ Architecture
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
AI Coding Agent (Antigravity / Cursor / Claude)
|
|
208
|
+
โ
|
|
209
|
+
โ MCP stdio protocol
|
|
210
|
+
โผ
|
|
211
|
+
npx --no-install expo-agent-bridge mcp
|
|
212
|
+
โ
|
|
213
|
+
โ WebSocket (ws://localhost:8081/expo-dev-plugins/broadcast)
|
|
214
|
+
โผ
|
|
215
|
+
Metro Dev Server (Standard Expo Bundler)
|
|
216
|
+
โ
|
|
217
|
+
โ Existing DevTools Plugin channel (ws:// or wss:// via tunnel)
|
|
218
|
+
โผ
|
|
219
|
+
<AgentBridge /> (in running mobile app)
|
|
220
|
+
โ
|
|
221
|
+
โโ react-native-view-shot (native screenshot capture)
|
|
222
|
+
โโ ErrorUtils & console hook (live error streaming)
|
|
223
|
+
โโ Element Registry (tap, scroll, type dispatch)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- **No second tunnel**: Piggybacks on Metro's existing WebSocket broadcast channel.
|
|
227
|
+
- **No ATS overrides**: Tunnel mode uses `wss://` (secure TLS) automatically.
|
|
228
|
+
- **Works in WSL**: Connects over the single existing Expo tunnel.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## ๐ License
|
|
233
|
+
|
|
234
|
+
MIT ยฉ Fs02
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare const PLUGIN_NAME = "expo-agent-bridge";
|
|
3
|
+
export type ElementHandler = {
|
|
4
|
+
ref?: React.RefObject<any>;
|
|
5
|
+
onPress?: () => void;
|
|
6
|
+
onChangeText?: (text: string) => void;
|
|
7
|
+
type?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
};
|
|
10
|
+
export type LogEntry = {
|
|
11
|
+
level: 'error' | 'warn' | 'log';
|
|
12
|
+
message: string;
|
|
13
|
+
stack?: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const logBuffer: LogEntry[];
|
|
17
|
+
export declare const elementRegistry: Map<string, ElementHandler>;
|
|
18
|
+
export declare function registerElement(testID: string, handler: ElementHandler | React.RefObject<any>): void;
|
|
19
|
+
export declare function unregisterElement(testID: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Hook to declaratively register any interactive component with the agent bridge.
|
|
22
|
+
* Example:
|
|
23
|
+
* useAgentElement('my-button', { onPress, title: 'Submit' });
|
|
24
|
+
*/
|
|
25
|
+
export declare function useAgentElement(testID: string, handler: ElementHandler): void;
|
|
26
|
+
export interface AgentBridgeProps {
|
|
27
|
+
pluginName?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function AgentBridge({ pluginName }: AgentBridgeProps): React.JSX.Element | null;
|
|
30
|
+
export default AgentBridge;
|
|
31
|
+
//# sourceMappingURL=DevAgentBridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevAgentBridge.d.ts","sourceRoot":"","sources":["../src/DevAgentBridge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAGjD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,eAAO,MAAM,SAAS,EAAE,QAAQ,EAAO,CAAC;AACxC,eAAO,MAAM,eAAe,6BAAoC,CAAC;AAEjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,QAkB7F;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,QAG/C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,QAMtE;AAyOD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAAE,UAAwB,EAAE,EAAE,gBAAgB,4BAOzE;AAmDD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.elementRegistry = exports.logBuffer = exports.PLUGIN_NAME = void 0;
|
|
37
|
+
exports.registerElement = registerElement;
|
|
38
|
+
exports.unregisterElement = unregisterElement;
|
|
39
|
+
exports.useAgentElement = useAgentElement;
|
|
40
|
+
exports.AgentBridge = AgentBridge;
|
|
41
|
+
const react_1 = __importStar(require("react"));
|
|
42
|
+
const react_native_1 = require("react-native");
|
|
43
|
+
exports.PLUGIN_NAME = 'expo-agent-bridge';
|
|
44
|
+
const MAX_LOGS = 100;
|
|
45
|
+
exports.logBuffer = [];
|
|
46
|
+
exports.elementRegistry = new Map();
|
|
47
|
+
function registerElement(testID, handler) {
|
|
48
|
+
if (typeof __DEV__ !== 'undefined' && !__DEV__)
|
|
49
|
+
return;
|
|
50
|
+
if (handler && 'current' in handler) {
|
|
51
|
+
exports.elementRegistry.set(testID, { ref: handler });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const originalOnPress = handler.onPress;
|
|
55
|
+
const wrappedHandler = {
|
|
56
|
+
...handler,
|
|
57
|
+
onPress: originalOnPress
|
|
58
|
+
? () => {
|
|
59
|
+
const label = handler.title ? `"${handler.title}" (${testID})` : `"${testID}"`;
|
|
60
|
+
recordLog('log', `[Interaction] Pressed ${label}`);
|
|
61
|
+
return originalOnPress();
|
|
62
|
+
}
|
|
63
|
+
: undefined,
|
|
64
|
+
};
|
|
65
|
+
exports.elementRegistry.set(testID, wrappedHandler);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function unregisterElement(testID) {
|
|
69
|
+
if (typeof __DEV__ !== 'undefined' && !__DEV__)
|
|
70
|
+
return;
|
|
71
|
+
exports.elementRegistry.delete(testID);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Hook to declaratively register any interactive component with the agent bridge.
|
|
75
|
+
* Example:
|
|
76
|
+
* useAgentElement('my-button', { onPress, title: 'Submit' });
|
|
77
|
+
*/
|
|
78
|
+
function useAgentElement(testID, handler) {
|
|
79
|
+
(0, react_1.useEffect)(() => {
|
|
80
|
+
if (typeof __DEV__ !== 'undefined' && !__DEV__)
|
|
81
|
+
return;
|
|
82
|
+
registerElement(testID, handler);
|
|
83
|
+
return () => unregisterElement(testID);
|
|
84
|
+
}, [testID, handler.onPress, handler.onChangeText, handler.title]);
|
|
85
|
+
}
|
|
86
|
+
// โโโ Route Tracking โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
87
|
+
let currentRouteState = {
|
|
88
|
+
pathname: '/',
|
|
89
|
+
segments: [],
|
|
90
|
+
};
|
|
91
|
+
// โโโ Log & Error Monitoring โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
92
|
+
let isLogging = false;
|
|
93
|
+
let activeClient = null;
|
|
94
|
+
function recordLog(level, message, stack) {
|
|
95
|
+
if (isLogging)
|
|
96
|
+
return;
|
|
97
|
+
isLogging = true;
|
|
98
|
+
try {
|
|
99
|
+
const entry = {
|
|
100
|
+
level,
|
|
101
|
+
message,
|
|
102
|
+
stack,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
};
|
|
105
|
+
exports.logBuffer.push(entry);
|
|
106
|
+
if (exports.logBuffer.length > MAX_LOGS)
|
|
107
|
+
exports.logBuffer.shift();
|
|
108
|
+
if (activeClient && activeClient.isConnected?.()) {
|
|
109
|
+
activeClient.sendMessage('log', entry);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Ignore logging errors
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
isLogging = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function formatArgs(args) {
|
|
120
|
+
return args
|
|
121
|
+
.map((arg) => {
|
|
122
|
+
if (typeof arg === 'string')
|
|
123
|
+
return arg;
|
|
124
|
+
if (arg instanceof Error)
|
|
125
|
+
return `${arg.name}: ${arg.message}${arg.stack ? `\n${arg.stack}` : ''}`;
|
|
126
|
+
try {
|
|
127
|
+
return JSON.stringify(arg);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return String(arg);
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
.join(' ');
|
|
134
|
+
}
|
|
135
|
+
// Hook console and ErrorUtils once in DEV mode
|
|
136
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__ && !global.__expoAgentBridgeLogsHooked) {
|
|
137
|
+
global.__expoAgentBridgeLogsHooked = true;
|
|
138
|
+
const originalError = console.error;
|
|
139
|
+
const originalWarn = console.warn;
|
|
140
|
+
const originalLog = console.log;
|
|
141
|
+
console.error = (...args) => {
|
|
142
|
+
const msg = formatArgs(args);
|
|
143
|
+
if (!msg.startsWith('[AgentBridge]') && !msg.startsWith('[DevAgentBridge]')) {
|
|
144
|
+
const errorObj = args.find((a) => a instanceof Error);
|
|
145
|
+
recordLog('error', msg, errorObj?.stack);
|
|
146
|
+
}
|
|
147
|
+
originalError(...args);
|
|
148
|
+
};
|
|
149
|
+
console.warn = (...args) => {
|
|
150
|
+
const msg = formatArgs(args);
|
|
151
|
+
if (!msg.startsWith('[AgentBridge]') && !msg.startsWith('[DevAgentBridge]')) {
|
|
152
|
+
recordLog('warn', msg);
|
|
153
|
+
}
|
|
154
|
+
originalWarn(...args);
|
|
155
|
+
};
|
|
156
|
+
console.log = (...args) => {
|
|
157
|
+
const msg = formatArgs(args);
|
|
158
|
+
recordLog('log', msg);
|
|
159
|
+
originalLog(...args);
|
|
160
|
+
};
|
|
161
|
+
const ErrorUtils = global.ErrorUtils;
|
|
162
|
+
if (ErrorUtils && typeof ErrorUtils.getGlobalHandler === 'function') {
|
|
163
|
+
const defaultHandler = ErrorUtils.getGlobalHandler();
|
|
164
|
+
ErrorUtils.setGlobalHandler((error, isFatal) => {
|
|
165
|
+
const message = error?.message ? `${error.name || 'Error'}: ${error.message}` : String(error);
|
|
166
|
+
recordLog('error', `[Unhandled ${isFatal ? 'Fatal ' : ''}Exception] ${message}`, error?.stack);
|
|
167
|
+
defaultHandler?.(error, isFatal);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// โโโ Command dispatch โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
172
|
+
async function handleCommand(cmd) {
|
|
173
|
+
const { id, action } = cmd;
|
|
174
|
+
switch (action) {
|
|
175
|
+
case 'screenshot': {
|
|
176
|
+
console.log('[expo-agent-bridge] โ screenshot');
|
|
177
|
+
recordLog('log', '[Bridge] Screenshot captured');
|
|
178
|
+
const { captureScreen } = require('react-native-view-shot');
|
|
179
|
+
const base64 = await captureScreen({ format: 'png', result: 'base64' });
|
|
180
|
+
return { id, data: base64 };
|
|
181
|
+
}
|
|
182
|
+
case 'navigate': {
|
|
183
|
+
console.log(`[expo-agent-bridge] โ navigate ${cmd.route}`);
|
|
184
|
+
recordLog('log', `[Bridge] Navigate to ${cmd.route}`);
|
|
185
|
+
try {
|
|
186
|
+
const { router } = require('expo-router');
|
|
187
|
+
router.push(cmd.route);
|
|
188
|
+
return { id, success: true };
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
recordLog('error', `[Bridge] Navigate error: ${e?.message}`);
|
|
192
|
+
return { id, error: 'Expo Router not available: ' + e?.message };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
case 'get_route': {
|
|
196
|
+
return { id, route: currentRouteState };
|
|
197
|
+
}
|
|
198
|
+
case 'get_elements': {
|
|
199
|
+
const elements = Array.from(exports.elementRegistry.entries()).map(([testID, handler]) => ({
|
|
200
|
+
testID,
|
|
201
|
+
type: handler.type || (handler.onPress ? 'button' : handler.onChangeText ? 'input' : 'element'),
|
|
202
|
+
title: handler.title,
|
|
203
|
+
}));
|
|
204
|
+
return { id, elements };
|
|
205
|
+
}
|
|
206
|
+
case 'get_state': {
|
|
207
|
+
try {
|
|
208
|
+
// Look for common stores or custom global state
|
|
209
|
+
const stateObj = {};
|
|
210
|
+
if (global.__AGENT_CUSTOM_STATE__) {
|
|
211
|
+
stateObj.custom = global.__AGENT_CUSTOM_STATE__();
|
|
212
|
+
}
|
|
213
|
+
return { id, state: stateObj };
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
return { id, error: err.message };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
case 'reset_storage': {
|
|
220
|
+
try {
|
|
221
|
+
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
222
|
+
await AsyncStorage.clear();
|
|
223
|
+
recordLog('log', '[Bridge] Storage reset cleared');
|
|
224
|
+
return { id, success: true };
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
return { id, error: err.message };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
case 'open_dev_menu': {
|
|
231
|
+
try {
|
|
232
|
+
if (react_native_1.NativeModules.DevMenu?.show) {
|
|
233
|
+
react_native_1.NativeModules.DevMenu.show();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch { }
|
|
237
|
+
return { id, success: true };
|
|
238
|
+
}
|
|
239
|
+
case 'tap': {
|
|
240
|
+
console.log(`[expo-agent-bridge] โ tap "${cmd.target}"`);
|
|
241
|
+
const handler = exports.elementRegistry.get(cmd.target);
|
|
242
|
+
if (handler?.onPress) {
|
|
243
|
+
recordLog('log', `[Bridge] Tap "${cmd.target}" (${handler.title || 'button'})`);
|
|
244
|
+
handler.onPress();
|
|
245
|
+
return { id, success: true };
|
|
246
|
+
}
|
|
247
|
+
const node = handler?.ref?.current;
|
|
248
|
+
if (typeof node?.props?.onPress === 'function') {
|
|
249
|
+
recordLog('log', `[Bridge] Tap "${cmd.target}" via ref`);
|
|
250
|
+
node.props.onPress();
|
|
251
|
+
return { id, success: true };
|
|
252
|
+
}
|
|
253
|
+
recordLog('warn', `[Bridge] Tap "${cmd.target}" failed: not found in element registry`);
|
|
254
|
+
return { id, success: false };
|
|
255
|
+
}
|
|
256
|
+
case 'scroll': {
|
|
257
|
+
console.log(`[expo-agent-bridge] โ scroll ${cmd.direction}`);
|
|
258
|
+
recordLog('log', `[Bridge] Scroll ${cmd.direction}`);
|
|
259
|
+
for (const [, handler] of exports.elementRegistry) {
|
|
260
|
+
const node = handler?.ref?.current;
|
|
261
|
+
if (node && 'scrollToEnd' in node) {
|
|
262
|
+
const sv = node;
|
|
263
|
+
if (cmd.direction === 'down')
|
|
264
|
+
sv.scrollToEnd({ animated: true });
|
|
265
|
+
else
|
|
266
|
+
sv.scrollTo({ y: 0, animated: true });
|
|
267
|
+
return { id, success: true };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { id, success: false };
|
|
271
|
+
}
|
|
272
|
+
case 'type': {
|
|
273
|
+
console.log(`[expo-agent-bridge] โ type "${cmd.target}": "${cmd.text}"`);
|
|
274
|
+
recordLog('log', `[Bridge] Type into "${cmd.target}": "${cmd.text}"`);
|
|
275
|
+
const handler = exports.elementRegistry.get(cmd.target);
|
|
276
|
+
if (handler?.onChangeText) {
|
|
277
|
+
handler.onChangeText(cmd.text);
|
|
278
|
+
return { id, success: true };
|
|
279
|
+
}
|
|
280
|
+
const node = handler?.ref?.current;
|
|
281
|
+
if (typeof node?.props?.onChangeText === 'function') {
|
|
282
|
+
node.props.onChangeText(cmd.text);
|
|
283
|
+
return { id, success: true };
|
|
284
|
+
}
|
|
285
|
+
return { id, success: false };
|
|
286
|
+
}
|
|
287
|
+
case 'get_logs': {
|
|
288
|
+
return { id, logs: exports.logBuffer };
|
|
289
|
+
}
|
|
290
|
+
case 'reload': {
|
|
291
|
+
console.log('[expo-agent-bridge] โ reload');
|
|
292
|
+
recordLog('log', '[Bridge] App reload requested');
|
|
293
|
+
if (typeof react_native_1.DevSettings?.reload === 'function') {
|
|
294
|
+
react_native_1.DevSettings.reload('Agent requested reload');
|
|
295
|
+
}
|
|
296
|
+
return { id, success: true };
|
|
297
|
+
}
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unknown action: ${action}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function AgentBridge({ pluginName = exports.PLUGIN_NAME }) {
|
|
303
|
+
// If production, render nothing and initialize nothing
|
|
304
|
+
if (typeof __DEV__ !== 'undefined' && !__DEV__) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
return <DevAgentBridgeImpl pluginName={pluginName}/>;
|
|
308
|
+
}
|
|
309
|
+
function DevAgentBridgeImpl({ pluginName }) {
|
|
310
|
+
let client = null;
|
|
311
|
+
try {
|
|
312
|
+
const { useDevToolsPluginClient } = require('@expo/devtools');
|
|
313
|
+
client = useDevToolsPluginClient(pluginName);
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
console.warn('[AgentBridge] @expo/devtools not available in this environment');
|
|
317
|
+
}
|
|
318
|
+
const subRef = (0, react_1.useRef)(null);
|
|
319
|
+
// Attempt to hook Expo Router if present
|
|
320
|
+
try {
|
|
321
|
+
const { usePathname, useSegments } = require('expo-router');
|
|
322
|
+
const pathname = usePathname();
|
|
323
|
+
const segments = useSegments();
|
|
324
|
+
(0, react_1.useEffect)(() => {
|
|
325
|
+
currentRouteState = { pathname, segments };
|
|
326
|
+
}, [pathname, segments]);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Non-Expo-Router app fallback
|
|
330
|
+
}
|
|
331
|
+
(0, react_1.useEffect)(() => {
|
|
332
|
+
if (!client)
|
|
333
|
+
return;
|
|
334
|
+
activeClient = client;
|
|
335
|
+
console.log(`[AgentBridge] Connected via DevTools Plugin (${pluginName}) โ`);
|
|
336
|
+
recordLog('log', `AgentBridge initialized and connected (${pluginName})`);
|
|
337
|
+
subRef.current = client.addMessageListener('command', async (cmd) => {
|
|
338
|
+
let result;
|
|
339
|
+
try {
|
|
340
|
+
result = await handleCommand(cmd);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
result = { id: cmd.id, error: err?.message ?? String(err) };
|
|
344
|
+
}
|
|
345
|
+
client.sendMessage('result', result);
|
|
346
|
+
});
|
|
347
|
+
return () => {
|
|
348
|
+
activeClient = null;
|
|
349
|
+
subRef.current?.remove();
|
|
350
|
+
};
|
|
351
|
+
}, [client, pluginName]);
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
exports.default = AgentBridge;
|
|
355
|
+
//# sourceMappingURL=DevAgentBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevAgentBridge.js","sourceRoot":"","sources":["../src/DevAgentBridge.tsx"],"names":[],"mappings":"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"}
|