@avasapp/agent-bridge 0.1.0 → 0.2.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.
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/socket-io.ts
21
+ var socket_io_exports = {};
22
+ __export(socket_io_exports, {
23
+ socketIoTools: () => socketIoTools
24
+ });
25
+ module.exports = __toCommonJS(socket_io_exports);
26
+
27
+ // src/realtime/patch.ts
28
+ var list = (names) => Array.isArray(names) ? names : [names];
29
+ var sameKey = (a, b) => JSON.stringify(a) === JSON.stringify(b);
30
+ function split(args) {
31
+ const last = args[args.length - 1];
32
+ return typeof last === "function" ? { key: args.slice(0, -1), listener: last } : { key: args };
33
+ }
34
+ function tapListeners(tap, target, options) {
35
+ const methods = target;
36
+ const tracked = [];
37
+ const spread = options.message === "args";
38
+ for (const name of list(options.add)) {
39
+ const add = methods[name];
40
+ if (typeof add !== "function") continue;
41
+ methods[name] = function(...args) {
42
+ const { key, listener: original } = split(args);
43
+ const channel = original && options.channel(key);
44
+ if (!original || channel === void 0) return add.apply(this, args);
45
+ const accepts = options.accepts;
46
+ const { listener, unsubscribe } = tap.wrap(
47
+ channel,
48
+ (message, ...rest) => spread ? original.apply(target, message) : original.call(target, message, ...rest),
49
+ accepts ? { accepts: (m) => accepts(key, m) } : {}
50
+ );
51
+ const wrapped = spread ? (...received) => listener(received) : (message, ...rest) => listener(message, ...rest);
52
+ tracked.push({ key, original, wrapped, unsubscribe });
53
+ return add.apply(this, [...key, wrapped]);
54
+ };
55
+ }
56
+ for (const name of list(options.remove)) {
57
+ const remove = methods[name];
58
+ if (typeof remove !== "function") continue;
59
+ methods[name] = function(...args) {
60
+ const { key, listener } = split(args);
61
+ const hits = tracked.filter(
62
+ (t) => (!listener || t.original === listener || t.original.fn === listener) && (!key.length || sameKey(t.key, key))
63
+ );
64
+ for (const hit of hits) {
65
+ tracked.splice(tracked.indexOf(hit), 1);
66
+ hit.unsubscribe();
67
+ }
68
+ if (!listener || !hits.length) return remove.apply(this, args);
69
+ let result;
70
+ for (const hit of hits) result = remove.apply(this, [...hit.key, hit.wrapped]);
71
+ return result;
72
+ };
73
+ }
74
+ }
75
+ function fakeableConnection(target, options) {
76
+ const methods = target;
77
+ const events = methods?.[options.events];
78
+ if (!methods || typeof events !== "function") return void 0;
79
+ const read = options.read ?? String;
80
+ const write = options.write ?? ((state) => state);
81
+ const values = methods;
82
+ let fake = null;
83
+ let real = values[options.property];
84
+ const readReal = () => read(options.fakeProperty === false ? values[options.property] : real);
85
+ if (options.fakeProperty !== false)
86
+ Object.defineProperty(target, options.property, {
87
+ configurable: true,
88
+ enumerable: true,
89
+ get: () => fake === null ? real : write(fake),
90
+ set: (value) => {
91
+ real = value;
92
+ }
93
+ });
94
+ methods[options.events] = function(...args) {
95
+ return fake === null ? events.apply(this, args) : void 0;
96
+ };
97
+ const emit = (...args) => void events.apply(target, args);
98
+ return {
99
+ state: readReal,
100
+ states: options.states,
101
+ live: options.live,
102
+ fake: (state, previous) => {
103
+ fake = state;
104
+ const current = state ?? readReal();
105
+ if (current !== previous)
106
+ options.announce(emit, { current, previous, faked: state !== null });
107
+ }
108
+ };
109
+ }
110
+ function realtimeAdapter(install) {
111
+ const taps = /* @__PURE__ */ new WeakMap();
112
+ return (client, options = {}) => {
113
+ let tools = taps.get(client);
114
+ if (!tools) taps.set(client, tools = install(client, options).tools);
115
+ return tools;
116
+ };
117
+ }
118
+
119
+ // src/realtime/tap.ts
120
+ var LOG_SIZE = 50;
121
+ var ALL = "*";
122
+ var defaultDescribe = (message) => {
123
+ const id = message?.id;
124
+ return typeof id === "string" ? { data: message, id } : { data: message };
125
+ };
126
+ var sameMessage = (a, b) => a === b || Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);
127
+ function createRealtimeTap(options = {}) {
128
+ const ns = options.namespace ?? "realtime";
129
+ const describe = options.describe ?? defaultDescribe;
130
+ const toMessage = options.toMessage ?? ((values) => values[0]);
131
+ const connection = options.connection;
132
+ const live = connection?.live ?? "connected";
133
+ const channels = /* @__PURE__ */ new Map();
134
+ const mutes = /* @__PURE__ */ new Map();
135
+ const log = [];
136
+ const last = /* @__PURE__ */ new Map();
137
+ let injectedCount = 0;
138
+ let faked = null;
139
+ const muteFor = (channel) => mutes.get(channel) ?? mutes.get(ALL);
140
+ const liveChannels = () => [...channels.keys()].join(", ") || "none";
141
+ function record(channel, message, injected, dropped) {
142
+ log.push({
143
+ at: (/* @__PURE__ */ new Date()).toISOString(),
144
+ channel,
145
+ ...describe(message),
146
+ injected,
147
+ ...dropped && { dropped }
148
+ });
149
+ if (log.length > LOG_SIZE) log.splice(0, log.length - LOG_SIZE);
150
+ }
151
+ function receive(channel, entry, message, rest) {
152
+ const previous = last.get(channel);
153
+ const dropped = muteFor(channel) ? "muted" : faked !== null && faked !== live ? "connection" : void 0;
154
+ if (previous && !previous.seen.has(entry) && sameMessage(previous.message, message)) {
155
+ previous.seen.add(entry);
156
+ } else {
157
+ last.set(channel, { message, seen: /* @__PURE__ */ new Set([entry]) });
158
+ record(channel, message, false, dropped);
159
+ }
160
+ if (!dropped) entry.deliver(message, ...rest);
161
+ }
162
+ const wrap = (channel, listener, wrapOptions = {}) => {
163
+ const entry = {
164
+ deliver: listener,
165
+ accepts: wrapOptions.accepts
166
+ };
167
+ let entries = channels.get(channel);
168
+ if (!entries) channels.set(channel, entries = /* @__PURE__ */ new Set());
169
+ entries.add(entry);
170
+ return {
171
+ listener: (message, ...rest) => receive(channel, entry, message, rest),
172
+ unsubscribe: () => {
173
+ const current = channels.get(channel);
174
+ if (!current?.delete(entry) || current.size) return;
175
+ channels.delete(channel);
176
+ last.delete(channel);
177
+ }
178
+ };
179
+ };
180
+ function setMute(channel, mute) {
181
+ if (typeof channel !== "string" || !channel)
182
+ throw new Error(`Pass a channel name, or "${ALL}" for every channel`);
183
+ if (mute) mutes.set(channel, mute);
184
+ else if (channel === ALL) mutes.clear();
185
+ else mutes.delete(channel);
186
+ return { muted: [...mutes.keys()] };
187
+ }
188
+ function setConnection(conn, state) {
189
+ const shown = faked ?? conn.state();
190
+ if (state !== null && typeof state !== "string")
191
+ throw new Error('Pass a state such as "disconnected", or null for the real one');
192
+ if (state !== null && conn.states && !conn.states.includes(state))
193
+ throw new Error(
194
+ `Unknown state "${state}". Known: ${conn.states.join(", ")}`
195
+ );
196
+ if (state === faked) return;
197
+ faked = state;
198
+ conn.fake(state, shown);
199
+ }
200
+ const tools = {
201
+ [`${ns}.channels`]: {
202
+ description: "Channels the app listens on: listener count, muted, and what the library says about each.",
203
+ run: () => [...channels].map(([name, entries]) => ({
204
+ name,
205
+ listeners: entries.size,
206
+ muted: Boolean(muteFor(name)),
207
+ ...options.channelInfo?.(name)
208
+ }))
209
+ },
210
+ [`${ns}.log`]: {
211
+ description: `Recent messages, real and injected, newest first (last ${LOG_SIZE}). Filter: { channel?, event?, limit? }; clear: true empties the log after reading.`,
212
+ run: (filter = {}) => {
213
+ const entries = log.filter(
214
+ (e) => (!filter.channel || e.channel === filter.channel) && (!filter.event || e.event === filter.event)
215
+ ).reverse().slice(0, filter.limit ?? 20);
216
+ if (filter.clear) log.length = 0;
217
+ return entries;
218
+ }
219
+ },
220
+ [`${ns}.emit`]: {
221
+ description: "Deliver a fake message to every listener on a channel: [channel, message]. Runs the app's own handlers. Returns { id, delivered }.",
222
+ run: (channel, ...values) => {
223
+ const entries = channels.get(channel);
224
+ if (!entries?.size)
225
+ throw new Error(
226
+ `Nothing listens on "${channel}". Live channels: ${liveChannels()}`
227
+ );
228
+ injectedCount += 1;
229
+ const message = toMessage(values, {
230
+ channel,
231
+ id: `agent-bridge-${injectedCount}`
232
+ });
233
+ const id = describe(message).id;
234
+ if (muteFor(channel)?.dropInjected) {
235
+ record(channel, message, true, "muted");
236
+ return { id, delivered: 0 };
237
+ }
238
+ record(channel, message, true);
239
+ let delivered = 0;
240
+ for (const entry of [...entries]) {
241
+ if (entry.accepts && !entry.accepts(message)) continue;
242
+ entry.deliver(message);
243
+ delivered += 1;
244
+ }
245
+ return { id, delivered };
246
+ }
247
+ },
248
+ [`${ns}.mute`]: {
249
+ description: `Drop real messages on a channel ("${ALL}" for all) until unmuted. Injected messages still get through unless { dropInjected: true }.`,
250
+ run: (channel, muteOptions = {}) => setMute(channel, { dropInjected: Boolean(muteOptions.dropInjected) })
251
+ },
252
+ [`${ns}.unmute`]: {
253
+ description: `Stop dropping messages on a channel ("${ALL}" for all).`,
254
+ run: (channel) => setMute(channel, null)
255
+ },
256
+ [`${ns}.restore`]: {
257
+ description: "Undo the agent: unmute every channel and go back to the real connection state.",
258
+ run: () => {
259
+ const unmuted = [...mutes.keys()];
260
+ mutes.clear();
261
+ const connectionFaked = faked !== null;
262
+ if (connection && connectionFaked) setConnection(connection, null);
263
+ return { unmuted, connectionFaked };
264
+ }
265
+ }
266
+ };
267
+ if (connection)
268
+ tools[`${ns}.connection`] = {
269
+ description: `The connection state, or fake one: [state] shows the app that state${connection.states ? ` (${connection.states.join(", ")})` : ""} and drops real messages unless it is "${live}"; [null] goes back to the real one.`,
270
+ run: (...args) => {
271
+ if (args.length) setConnection(connection, args[0] ?? null);
272
+ return { state: faked ?? connection.state(), real: connection.state(), faked: faked !== null };
273
+ }
274
+ };
275
+ return { wrap, tools };
276
+ }
277
+
278
+ // src/adapters/socket-io.ts
279
+ var RESERVED = /* @__PURE__ */ new Set([
280
+ "connect",
281
+ "connect_error",
282
+ "disconnect",
283
+ "disconnecting",
284
+ "newListener",
285
+ "removeListener"
286
+ ]);
287
+ var socketIoTools = realtimeAdapter(
288
+ (socket, options) => {
289
+ const tap = createRealtimeTap({
290
+ namespace: options.namespace,
291
+ // The last argument is an ack callback when the server asked for one.
292
+ describe: (args) => {
293
+ const values = args.filter((a) => typeof a !== "function");
294
+ return { data: values.length === 1 ? values[0] : values };
295
+ },
296
+ toMessage: (values) => values,
297
+ // socket.io-client announces lifecycle events with emitReserved.
298
+ connection: fakeableConnection(socket, {
299
+ property: "connected",
300
+ events: "emitReserved",
301
+ states: ["connected", "disconnected"],
302
+ read: (connected) => connected ? "connected" : "disconnected",
303
+ // socket.io reads `connected` to decide whether to deliver incoming
304
+ // events and send outgoing ones; faking it would hold them back.
305
+ fakeProperty: false,
306
+ announce: (emit, { current }) => current === "connected" ? emit("connect") : emit("disconnect", "transport close")
307
+ })
308
+ });
309
+ tapListeners(tap, socket, {
310
+ add: ["on", "addEventListener"],
311
+ remove: ["off", "removeListener", "removeAllListeners", "removeEventListener"],
312
+ channel: ([event]) => typeof event === "string" && !RESERVED.has(event) ? event : void 0,
313
+ message: "args"
314
+ });
315
+ return tap;
316
+ }
317
+ );
@@ -0,0 +1,21 @@
1
+ import { T as Tools } from '../types-C6DUUHnB.cjs';
2
+
3
+ type Listener = (...args: any[]) => void;
4
+ /** A socket.io client socket (`io(url)`), typed structurally. */
5
+ type SocketLike = {
6
+ on(event: string, listener: Listener): unknown;
7
+ off(event?: string, listener?: Listener): unknown;
8
+ connected?: boolean;
9
+ };
10
+ type SocketIoToolsOptions = {
11
+ /** Tool name prefix. Defaults to "realtime". */
12
+ namespace?: string;
13
+ };
14
+ /**
15
+ * See, fake and drop socket.io events: `realtime.*`, where a channel is an
16
+ * event name. Patches the socket, so call it where you create the socket,
17
+ * before the app adds listeners. Calling it again with the same socket is free.
18
+ */
19
+ declare const socketIoTools: (client: SocketLike, options?: SocketIoToolsOptions | undefined) => Tools;
20
+
21
+ export { type SocketIoToolsOptions, type SocketLike, socketIoTools };
@@ -174,6 +174,8 @@ async function connectExpo(metro, device, discoveryMs = 600) {
174
174
 
175
175
  // src/client/cdp.ts
176
176
  import WebSocket2 from "ws";
177
+ var CannotEvaluate = class extends Error {
178
+ };
177
179
  var targetLabel = (t) => `${t.title}${t.deviceName ? ` [${t.deviceName}]` : ""}`;
178
180
  async function connectCdp(metro, device) {
179
181
  const target = pickOne(await listCdpTargets(metro), device, targetLabel);
@@ -220,6 +222,7 @@ async function connectCdp(metro, device) {
220
222
  expression,
221
223
  returnByValue: true
222
224
  });
225
+ if (message.error?.code === -32601) throw new CannotEvaluate();
223
226
  const details = message.result?.exceptionDetails;
224
227
  if (details) {
225
228
  const text = details.exception?.description ?? details.text ?? "evaluation failed";
@@ -234,10 +237,10 @@ async function connectCdp(metro, device) {
234
237
  info = JSON.parse(
235
238
  String(await evaluate(`${CDP_GLOBAL}.info()`))
236
239
  );
237
- } catch {
240
+ } catch (error) {
238
241
  ws.close();
239
242
  throw new Error(
240
- `agent-bridge isn't running in ${targetLabel(target)}. Is useAgentBridge mounted in a dev build?`
243
+ error instanceof CannotEvaluate ? `The debugger for ${targetLabel(target)} can't run code (no Runtime.evaluate, as in Expo Go on Android). Use --transport expo, or a dev build.` : `agent-bridge isn't running in ${targetLabel(target)}. Is useAgentBridge mounted in a dev build?`
241
244
  );
242
245
  }
243
246
  return {
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  sessionRequest,
20
20
  stateDir,
21
21
  writePrivate
22
- } from "./chunk-UEWFQWCY.js";
22
+ } from "./chunk-LBYZ5JHF.js";
23
23
 
24
24
  // src/cli.ts
25
25
  import { readFile } from "fs/promises";
@@ -213,6 +213,8 @@ async function connectExpo(metro, device, discoveryMs = 600) {
213
213
 
214
214
  // src/client/cdp.ts
215
215
  var import_ws2 = __toESM(require("ws"), 1);
216
+ var CannotEvaluate = class extends Error {
217
+ };
216
218
  var targetLabel = (t) => `${t.title}${t.deviceName ? ` [${t.deviceName}]` : ""}`;
217
219
  async function connectCdp(metro, device) {
218
220
  const target = pickOne(await listCdpTargets(metro), device, targetLabel);
@@ -259,6 +261,7 @@ async function connectCdp(metro, device) {
259
261
  expression,
260
262
  returnByValue: true
261
263
  });
264
+ if (message.error?.code === -32601) throw new CannotEvaluate();
262
265
  const details = message.result?.exceptionDetails;
263
266
  if (details) {
264
267
  const text = details.exception?.description ?? details.text ?? "evaluation failed";
@@ -273,10 +276,10 @@ async function connectCdp(metro, device) {
273
276
  info = JSON.parse(
274
277
  String(await evaluate(`${CDP_GLOBAL}.info()`))
275
278
  );
276
- } catch {
279
+ } catch (error) {
277
280
  ws.close();
278
281
  throw new Error(
279
- `agent-bridge isn't running in ${targetLabel(target)}. Is useAgentBridge mounted in a dev build?`
282
+ error instanceof CannotEvaluate ? `The debugger for ${targetLabel(target)} can't run code (no Runtime.evaluate, as in Expo Go on Android). Use --transport expo, or a dev build.` : `agent-bridge isn't running in ${targetLabel(target)}. Is useAgentBridge mounted in a dev build?`
280
283
  );
281
284
  }
282
285
  return {
@@ -4,7 +4,7 @@ import {
4
4
  connectSession,
5
5
  listDevices,
6
6
  listSessions
7
- } from "../chunk-UEWFQWCY.js";
7
+ } from "../chunk-LBYZ5JHF.js";
8
8
  export {
9
9
  AgentBridgeCallError,
10
10
  connect,
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/noop/ably.ts
21
+ var ably_exports = {};
22
+ __export(ably_exports, {
23
+ ablyTools: () => ablyTools
24
+ });
25
+ module.exports = __toCommonJS(ably_exports);
26
+ var ablyTools = () => ({});
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/noop/realtime.ts
21
+ var realtime_exports = {};
22
+ __export(realtime_exports, {
23
+ createRealtimeTap: () => createRealtimeTap,
24
+ fakeableConnection: () => fakeableConnection,
25
+ realtimeAdapter: () => realtimeAdapter,
26
+ tapListeners: () => tapListeners
27
+ });
28
+ module.exports = __toCommonJS(realtime_exports);
29
+ var createRealtimeTap = () => ({
30
+ wrap: (_channel, listener) => ({
31
+ listener,
32
+ unsubscribe: () => {
33
+ }
34
+ }),
35
+ tools: {}
36
+ });
37
+ var tapListeners = () => {
38
+ };
39
+ var fakeableConnection = () => void 0;
40
+ var realtimeAdapter = () => () => ({});
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/noop/socket-io.ts
21
+ var socket_io_exports = {};
22
+ __export(socket_io_exports, {
23
+ socketIoTools: () => socketIoTools
24
+ });
25
+ module.exports = __toCommonJS(socket_io_exports);
26
+ var socketIoTools = () => ({});