@avasapp/agent-bridge 0.1.0 → 0.3.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,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 = () => ({});
@@ -0,0 +1,279 @@
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/realtime/index.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
+
30
+ // src/realtime/patch.ts
31
+ var list = (names) => Array.isArray(names) ? names : [names];
32
+ var sameKey = (a, b) => JSON.stringify(a) === JSON.stringify(b);
33
+ function split(args) {
34
+ const last = args[args.length - 1];
35
+ return typeof last === "function" ? { key: args.slice(0, -1), listener: last } : { key: args };
36
+ }
37
+ function tapListeners(tap, target, options) {
38
+ const methods = target;
39
+ const tracked = [];
40
+ const spread = options.message === "args";
41
+ for (const name of list(options.add)) {
42
+ const add = methods[name];
43
+ if (typeof add !== "function") continue;
44
+ methods[name] = function(...args) {
45
+ const { key, listener: original } = split(args);
46
+ const channel = original && options.channel(key);
47
+ if (!original || channel === void 0) return add.apply(this, args);
48
+ const accepts = options.accepts;
49
+ const { listener, unsubscribe } = tap.wrap(
50
+ channel,
51
+ (message, ...rest) => spread ? original.apply(target, message) : original.call(target, message, ...rest),
52
+ accepts ? { accepts: (m) => accepts(key, m) } : {}
53
+ );
54
+ const wrapped = spread ? (...received) => listener(received) : (message, ...rest) => listener(message, ...rest);
55
+ tracked.push({ key, original, wrapped, unsubscribe });
56
+ return add.apply(this, [...key, wrapped]);
57
+ };
58
+ }
59
+ for (const name of list(options.remove)) {
60
+ const remove = methods[name];
61
+ if (typeof remove !== "function") continue;
62
+ methods[name] = function(...args) {
63
+ const { key, listener } = split(args);
64
+ const hits = tracked.filter(
65
+ (t) => (!listener || t.original === listener || t.original.fn === listener) && (!key.length || sameKey(t.key, key))
66
+ );
67
+ for (const hit of hits) {
68
+ tracked.splice(tracked.indexOf(hit), 1);
69
+ hit.unsubscribe();
70
+ }
71
+ if (!listener || !hits.length) return remove.apply(this, args);
72
+ let result;
73
+ for (const hit of hits) result = remove.apply(this, [...hit.key, hit.wrapped]);
74
+ return result;
75
+ };
76
+ }
77
+ }
78
+ function fakeableConnection(target, options) {
79
+ const methods = target;
80
+ const events = methods?.[options.events];
81
+ if (!methods || typeof events !== "function") return void 0;
82
+ const read = options.read ?? String;
83
+ const write = options.write ?? ((state) => state);
84
+ const values = methods;
85
+ let fake = null;
86
+ let real = values[options.property];
87
+ const readReal = () => read(options.fakeProperty === false ? values[options.property] : real);
88
+ if (options.fakeProperty !== false)
89
+ Object.defineProperty(target, options.property, {
90
+ configurable: true,
91
+ enumerable: true,
92
+ get: () => fake === null ? real : write(fake),
93
+ set: (value) => {
94
+ real = value;
95
+ }
96
+ });
97
+ methods[options.events] = function(...args) {
98
+ return fake === null ? events.apply(this, args) : void 0;
99
+ };
100
+ const emit = (...args) => void events.apply(target, args);
101
+ return {
102
+ state: readReal,
103
+ states: options.states,
104
+ live: options.live,
105
+ fake: (state, previous) => {
106
+ fake = state;
107
+ const current = state ?? readReal();
108
+ if (current !== previous)
109
+ options.announce(emit, { current, previous, faked: state !== null });
110
+ }
111
+ };
112
+ }
113
+ function realtimeAdapter(install) {
114
+ const taps = /* @__PURE__ */ new WeakMap();
115
+ return (client, options = {}) => {
116
+ let tools = taps.get(client);
117
+ if (!tools) taps.set(client, tools = install(client, options).tools);
118
+ return tools;
119
+ };
120
+ }
121
+
122
+ // src/realtime/tap.ts
123
+ var LOG_SIZE = 50;
124
+ var ALL = "*";
125
+ var defaultDescribe = (message) => {
126
+ const id = message?.id;
127
+ return typeof id === "string" ? { data: message, id } : { data: message };
128
+ };
129
+ var sameMessage = (a, b) => a === b || Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);
130
+ function createRealtimeTap(options = {}) {
131
+ const ns = options.namespace ?? "realtime";
132
+ const describe = options.describe ?? defaultDescribe;
133
+ const toMessage = options.toMessage ?? ((values) => values[0]);
134
+ const connection = options.connection;
135
+ const live = connection?.live ?? "connected";
136
+ const channels = /* @__PURE__ */ new Map();
137
+ const mutes = /* @__PURE__ */ new Map();
138
+ const log = [];
139
+ const last = /* @__PURE__ */ new Map();
140
+ let injectedCount = 0;
141
+ let faked = null;
142
+ const muteFor = (channel) => mutes.get(channel) ?? mutes.get(ALL);
143
+ const liveChannels = () => [...channels.keys()].join(", ") || "none";
144
+ function record(channel, message, injected, dropped) {
145
+ log.push({
146
+ at: (/* @__PURE__ */ new Date()).toISOString(),
147
+ channel,
148
+ ...describe(message),
149
+ injected,
150
+ ...dropped && { dropped }
151
+ });
152
+ if (log.length > LOG_SIZE) log.splice(0, log.length - LOG_SIZE);
153
+ }
154
+ function receive(channel, entry, message, rest) {
155
+ const previous = last.get(channel);
156
+ const dropped = muteFor(channel) ? "muted" : faked !== null && faked !== live ? "connection" : void 0;
157
+ if (previous && !previous.seen.has(entry) && sameMessage(previous.message, message)) {
158
+ previous.seen.add(entry);
159
+ } else {
160
+ last.set(channel, { message, seen: /* @__PURE__ */ new Set([entry]) });
161
+ record(channel, message, false, dropped);
162
+ }
163
+ if (!dropped) entry.deliver(message, ...rest);
164
+ }
165
+ const wrap = (channel, listener, wrapOptions = {}) => {
166
+ const entry = {
167
+ deliver: listener,
168
+ accepts: wrapOptions.accepts
169
+ };
170
+ let entries = channels.get(channel);
171
+ if (!entries) channels.set(channel, entries = /* @__PURE__ */ new Set());
172
+ entries.add(entry);
173
+ return {
174
+ listener: (message, ...rest) => receive(channel, entry, message, rest),
175
+ unsubscribe: () => {
176
+ const current = channels.get(channel);
177
+ if (!current?.delete(entry) || current.size) return;
178
+ channels.delete(channel);
179
+ last.delete(channel);
180
+ }
181
+ };
182
+ };
183
+ function setMute(channel, mute) {
184
+ if (typeof channel !== "string" || !channel)
185
+ throw new Error(`Pass a channel name, or "${ALL}" for every channel`);
186
+ if (mute) mutes.set(channel, mute);
187
+ else if (channel === ALL) mutes.clear();
188
+ else mutes.delete(channel);
189
+ return { muted: [...mutes.keys()] };
190
+ }
191
+ function setConnection(conn, state) {
192
+ const shown = faked ?? conn.state();
193
+ if (state !== null && typeof state !== "string")
194
+ throw new Error('Pass a state such as "disconnected", or null for the real one');
195
+ if (state !== null && conn.states && !conn.states.includes(state))
196
+ throw new Error(
197
+ `Unknown state "${state}". Known: ${conn.states.join(", ")}`
198
+ );
199
+ if (state === faked) return;
200
+ faked = state;
201
+ conn.fake(state, shown);
202
+ }
203
+ const tools = {
204
+ [`${ns}.channels`]: {
205
+ description: "Channels the app listens on: listener count, muted, and what the library says about each.",
206
+ run: () => [...channels].map(([name, entries]) => ({
207
+ name,
208
+ listeners: entries.size,
209
+ muted: Boolean(muteFor(name)),
210
+ ...options.channelInfo?.(name)
211
+ }))
212
+ },
213
+ [`${ns}.log`]: {
214
+ description: `Recent messages, real and injected, newest first (last ${LOG_SIZE}). Filter: { channel?, event?, limit? }; clear: true empties the log after reading.`,
215
+ run: (filter = {}) => {
216
+ const entries = log.filter(
217
+ (e) => (!filter.channel || e.channel === filter.channel) && (!filter.event || e.event === filter.event)
218
+ ).reverse().slice(0, filter.limit ?? 20);
219
+ if (filter.clear) log.length = 0;
220
+ return entries;
221
+ }
222
+ },
223
+ [`${ns}.emit`]: {
224
+ description: "Deliver a fake message to every listener on a channel: [channel, message]. Runs the app's own handlers. Returns { id, delivered }.",
225
+ run: (channel, ...values) => {
226
+ const entries = channels.get(channel);
227
+ if (!entries?.size)
228
+ throw new Error(
229
+ `Nothing listens on "${channel}". Live channels: ${liveChannels()}`
230
+ );
231
+ injectedCount += 1;
232
+ const message = toMessage(values, {
233
+ channel,
234
+ id: `agent-bridge-${injectedCount}`
235
+ });
236
+ const id = describe(message).id;
237
+ if (muteFor(channel)?.dropInjected) {
238
+ record(channel, message, true, "muted");
239
+ return { id, delivered: 0 };
240
+ }
241
+ record(channel, message, true);
242
+ let delivered = 0;
243
+ for (const entry of [...entries]) {
244
+ if (entry.accepts && !entry.accepts(message)) continue;
245
+ entry.deliver(message);
246
+ delivered += 1;
247
+ }
248
+ return { id, delivered };
249
+ }
250
+ },
251
+ [`${ns}.mute`]: {
252
+ description: `Drop real messages on a channel ("${ALL}" for all) until unmuted. Injected messages still get through unless { dropInjected: true }.`,
253
+ run: (channel, muteOptions = {}) => setMute(channel, { dropInjected: Boolean(muteOptions.dropInjected) })
254
+ },
255
+ [`${ns}.unmute`]: {
256
+ description: `Stop dropping messages on a channel ("${ALL}" for all).`,
257
+ run: (channel) => setMute(channel, null)
258
+ },
259
+ [`${ns}.restore`]: {
260
+ description: "Undo the agent: unmute every channel and go back to the real connection state.",
261
+ run: () => {
262
+ const unmuted = [...mutes.keys()];
263
+ mutes.clear();
264
+ const connectionFaked = faked !== null;
265
+ if (connection && connectionFaked) setConnection(connection, null);
266
+ return { unmuted, connectionFaked };
267
+ }
268
+ }
269
+ };
270
+ if (connection)
271
+ tools[`${ns}.connection`] = {
272
+ 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.`,
273
+ run: (...args) => {
274
+ if (args.length) setConnection(connection, args[0] ?? null);
275
+ return { state: faked ?? connection.state(), real: connection.state(), faked: faked !== null };
276
+ }
277
+ };
278
+ return { wrap, tools };
279
+ }
@@ -0,0 +1,138 @@
1
+ import { T as Tools } from '../types-6kKQsLC1.cjs';
2
+
3
+ /** How a message shows up in `realtime.log`. */
4
+ type MessageSummary = {
5
+ /** The event or message name, when the library has one. */
6
+ event?: string;
7
+ data?: unknown;
8
+ id?: string;
9
+ };
10
+ /** Lets the agent fake the client's connection state. */
11
+ type RealtimeConnection = {
12
+ /** The client's real state, e.g. "connected". */
13
+ state: () => string;
14
+ /**
15
+ * Show `state` to the app instead of the real one, as if the client had
16
+ * moved there from `previous`. `null` goes back to the real state.
17
+ */
18
+ fake: (state: string | null, previous: string) => void;
19
+ /** States the agent may pick, for error messages. */
20
+ states?: readonly string[];
21
+ /** The state in which real messages flow. Defaults to "connected". */
22
+ live?: string;
23
+ };
24
+ type RealtimeTapOptions<M> = {
25
+ /** Tool name prefix. Defaults to "realtime"; set it when an app has two taps. */
26
+ namespace?: string;
27
+ /** What to log for a message. Defaults to the message as `data`, with its `id` if it has one. */
28
+ describe?: (message: M) => MessageSummary;
29
+ /**
30
+ * Builds the message `realtime.emit` delivers, from the JSON values the
31
+ * agent passed after the channel. Defaults to the first value.
32
+ */
33
+ toMessage?: (values: unknown[], context: {
34
+ channel: string;
35
+ id: string;
36
+ }) => M;
37
+ /** Extra fields for `realtime.channels`, e.g. the library's channel state. */
38
+ channelInfo?: (channel: string) => Record<string, unknown>;
39
+ /** Adds `realtime.connection`, to fake connection states. */
40
+ connection?: RealtimeConnection;
41
+ };
42
+ type WrapOptions<M> = {
43
+ /**
44
+ * Whether this listener would get the message from the library, e.g. an
45
+ * event-name filter. Injected messages skip listeners that say no.
46
+ */
47
+ accepts?: (message: M) => boolean;
48
+ };
49
+ type RealtimeTap<M> = {
50
+ /**
51
+ * Wraps the app's listener for `channel`. Hand `listener` to the library
52
+ * instead of the original, and call `unsubscribe` when the app unsubscribes.
53
+ * Arguments after the message reach the app's listener untouched.
54
+ */
55
+ wrap: (channel: string, listener: (message: M, ...rest: never[]) => void, options?: WrapOptions<M>) => {
56
+ listener: (message: M, ...rest: unknown[]) => void;
57
+ unsubscribe: () => void;
58
+ };
59
+ /** `realtime.*`, or `<namespace>.*`. */
60
+ tools: Tools;
61
+ };
62
+ type RealtimeLogEntry = MessageSummary & {
63
+ at: string;
64
+ channel: string;
65
+ injected: boolean;
66
+ /** Why the app didn't get it. */
67
+ dropped?: 'muted' | 'connection';
68
+ };
69
+ /**
70
+ * Lets the agent see, fake and drop realtime messages, whatever library
71
+ * delivers them. Wrap each listener with `wrap`, and add `tools` to the bridge.
72
+ */
73
+ declare function createRealtimeTap<M = unknown>(options?: RealtimeTapOptions<M>): RealtimeTap<M>;
74
+
75
+ type TapListenersOptions<M> = {
76
+ /** Methods that add a listener, e.g. "on" or ["on", "addEventListener"]. */
77
+ add: string | string[];
78
+ /** Methods that remove one, e.g. "off". */
79
+ remove: string | string[];
80
+ /**
81
+ * The channel a call is for, from the arguments before the listener (its
82
+ * "key", e.g. the event name). Return undefined to leave the call alone.
83
+ */
84
+ channel: (key: unknown[]) => string | undefined;
85
+ /** Whether a listener added with `key` would get `message`, e.g. an event-name filter. */
86
+ accepts?: (key: unknown[], message: M) => boolean;
87
+ /**
88
+ * "first" (default): the message is the listener's first argument, and the
89
+ * rest reach the listener untouched. "args": every argument, as an array.
90
+ */
91
+ message?: 'first' | 'args';
92
+ };
93
+ /**
94
+ * Routes a client's add/remove-listener methods through `tap`, e.g.
95
+ * `socket.on` / `socket.off` or `channel.subscribe` / `channel.unsubscribe`.
96
+ * Removing works in every usual form: all, by key, by listener, or both.
97
+ */
98
+ declare function tapListeners<M>(tap: RealtimeTap<M>, target: object, options: TapListenersOptions<M>): void;
99
+ type FakeableConnectionOptions = {
100
+ /** The property holding the state, e.g. "state" or "connected". */
101
+ property: string;
102
+ /** The method the client tells listeners about changes with. Silent while a state is faked. */
103
+ events: string;
104
+ /** States the agent may pick. */
105
+ states: readonly string[];
106
+ /** The state in which real messages flow. Defaults to "connected". */
107
+ live?: string;
108
+ /** Property value to state name. Defaults to the value itself. */
109
+ read?: (value: unknown) => string;
110
+ /** State name to property value. Defaults to the name itself. */
111
+ write?: (state: string) => unknown;
112
+ /**
113
+ * false leaves the property showing the real state. Use it when the client
114
+ * reads the property itself to decide whether to deliver or send (socket.io
115
+ * does), or faking it would hold real traffic back. Defaults to true.
116
+ */
117
+ fakeProperty?: boolean;
118
+ /** Tells the app about a change, through the client's own (unsilenced) events method. */
119
+ announce: (emit: (...args: unknown[]) => void, change: {
120
+ current: string;
121
+ previous: string;
122
+ faked: boolean;
123
+ }) => void;
124
+ };
125
+ /**
126
+ * Lets `realtime.connection` fake a client's connection state: the property
127
+ * reads the fake (unless `fakeProperty: false`), real changes still land
128
+ * underneath but aren't announced, and going back announces the real state. Undefined when `target` lacks
129
+ * the events method, so the tool is left out rather than broken.
130
+ */
131
+ declare function fakeableConnection(target: object | undefined, options: FakeableConnectionOptions): RealtimeConnection | undefined;
132
+ /**
133
+ * Turns an install function into an adapter's `xxxTools(client, options)`.
134
+ * The client is patched on the first call; later calls return the same tools.
135
+ */
136
+ declare function realtimeAdapter<C extends object, O extends object>(install: (client: C, options: O) => Pick<RealtimeTap<unknown>, 'tools'>): (client: C, options?: O) => Tools;
137
+
138
+ export { type FakeableConnectionOptions, type MessageSummary, type RealtimeConnection, type RealtimeLogEntry, type RealtimeTap, type RealtimeTapOptions, type TapListenersOptions, type WrapOptions, createRealtimeTap, fakeableConnection, realtimeAdapter, tapListeners };