btcp-browser-agent 0.1.12 → 0.1.16

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,115 @@
1
+ /**
2
+ * Base transport class with shared functionality
3
+ *
4
+ * Provides event handling, state management, and utility methods for transports.
5
+ */
6
+ /**
7
+ * Abstract base class for transports
8
+ *
9
+ * Provides common functionality for event handling and state management.
10
+ * Subclasses must implement `send()`, `connect()`, and `disconnect()`.
11
+ */
12
+ export class BaseTransport {
13
+ state = 'disconnected';
14
+ debug;
15
+ eventHandlers = new Map();
16
+ constructor(options = {}) {
17
+ this.debug = options.debug ?? false;
18
+ }
19
+ /**
20
+ * Get the current connection state
21
+ */
22
+ getState() {
23
+ return this.state;
24
+ }
25
+ /**
26
+ * Check if the transport is connected
27
+ */
28
+ isConnected() {
29
+ return this.state === 'connected';
30
+ }
31
+ /**
32
+ * Register an event handler
33
+ */
34
+ on(event, handler) {
35
+ if (!this.eventHandlers.has(event)) {
36
+ this.eventHandlers.set(event, new Set());
37
+ }
38
+ this.eventHandlers.get(event).add(handler);
39
+ }
40
+ /**
41
+ * Unregister an event handler
42
+ */
43
+ off(event, handler) {
44
+ const handlers = this.eventHandlers.get(event);
45
+ if (handlers) {
46
+ handlers.delete(handler);
47
+ }
48
+ }
49
+ /**
50
+ * Emit an event to all registered handlers
51
+ */
52
+ emit(event, ...args) {
53
+ const handlers = this.eventHandlers.get(event);
54
+ if (handlers) {
55
+ for (const handler of handlers) {
56
+ try {
57
+ handler(...args);
58
+ }
59
+ catch (error) {
60
+ this.log('error', `Error in event handler for "${event}":`, error);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ /**
66
+ * Update the transport state and emit stateChange event
67
+ */
68
+ setState(newState) {
69
+ if (this.state !== newState) {
70
+ const oldState = this.state;
71
+ this.state = newState;
72
+ this.log('debug', `State changed: ${oldState} -> ${newState}`);
73
+ this.emit('stateChange', newState);
74
+ // Also emit specific events
75
+ if (newState === 'connected') {
76
+ this.emit('connected');
77
+ }
78
+ else if (newState === 'disconnected') {
79
+ this.emit('disconnected');
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Log a message if debug is enabled
85
+ */
86
+ log(level, ...args) {
87
+ if (!this.debug && level === 'debug') {
88
+ return;
89
+ }
90
+ const prefix = `[${this.name}]`;
91
+ switch (level) {
92
+ case 'debug':
93
+ case 'info':
94
+ console.log(prefix, ...args);
95
+ break;
96
+ case 'warn':
97
+ console.warn(prefix, ...args);
98
+ break;
99
+ case 'error':
100
+ console.error(prefix, ...args);
101
+ break;
102
+ }
103
+ }
104
+ /**
105
+ * Create an error response with the given message
106
+ */
107
+ createErrorResponse(id, error) {
108
+ return {
109
+ id,
110
+ success: false,
111
+ error,
112
+ };
113
+ }
114
+ }
115
+ //# sourceMappingURL=base-transport.js.map
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Chrome Extension Transport
3
+ *
4
+ * Transport implementation that uses chrome.runtime.sendMessage to communicate
5
+ * with the background script.
6
+ */
7
+ import type { Command, Response } from '../types.js';
8
+ import { BaseTransport } from './base-transport.js';
9
+ import type { TransportOptions } from './types.js';
10
+ /**
11
+ * Options for the Chrome extension transport
12
+ */
13
+ export interface ChromeExtensionTransportOptions extends TransportOptions {
14
+ /**
15
+ * Whether to auto-connect on first send
16
+ * @default true
17
+ */
18
+ autoConnect?: boolean;
19
+ }
20
+ /**
21
+ * Chrome Extension Transport
22
+ *
23
+ * Uses chrome.runtime.sendMessage to send commands to the background script.
24
+ * This is the default transport for popup and content script contexts.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const transport = new ChromeExtensionTransport({ debug: true });
29
+ * await transport.connect();
30
+ * const response = await transport.send({ action: 'navigate', url: 'https://example.com' });
31
+ * ```
32
+ */
33
+ export declare class ChromeExtensionTransport extends BaseTransport {
34
+ readonly name = "chrome-extension";
35
+ private autoConnect;
36
+ private initPromise;
37
+ constructor(options?: ChromeExtensionTransportOptions);
38
+ /**
39
+ * Connect to the background script
40
+ *
41
+ * Sends a popupInitialize command to reconnect to any existing session.
42
+ */
43
+ connect(): Promise<void>;
44
+ /**
45
+ * Disconnect the transport
46
+ */
47
+ disconnect(): void;
48
+ /**
49
+ * Send a command to the background script
50
+ */
51
+ send(command: Command): Promise<Response>;
52
+ /**
53
+ * Ensure the transport is connected (lazy initialization)
54
+ */
55
+ private ensureConnected;
56
+ /**
57
+ * Send a command without auto-connect (used for popupInitialize itself)
58
+ */
59
+ private sendRaw;
60
+ }
61
+ /**
62
+ * Create a Chrome extension transport
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const transport = createChromeExtensionTransport({ debug: true });
67
+ * const client = createClient({ transport });
68
+ * ```
69
+ */
70
+ export declare function createChromeExtensionTransport(options?: ChromeExtensionTransportOptions): ChromeExtensionTransport;
71
+ //# sourceMappingURL=chrome-extension.d.ts.map
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Chrome Extension Transport
3
+ *
4
+ * Transport implementation that uses chrome.runtime.sendMessage to communicate
5
+ * with the background script.
6
+ */
7
+ import { BaseTransport } from './base-transport.js';
8
+ let commandIdCounter = 0;
9
+ /**
10
+ * Generate a unique command ID for BTCP commands
11
+ */
12
+ function generateCommandId() {
13
+ return `cmd_${Date.now()}_${commandIdCounter++}`;
14
+ }
15
+ /**
16
+ * Chrome Extension Transport
17
+ *
18
+ * Uses chrome.runtime.sendMessage to send commands to the background script.
19
+ * This is the default transport for popup and content script contexts.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const transport = new ChromeExtensionTransport({ debug: true });
24
+ * await transport.connect();
25
+ * const response = await transport.send({ action: 'navigate', url: 'https://example.com' });
26
+ * ```
27
+ */
28
+ export class ChromeExtensionTransport extends BaseTransport {
29
+ name = 'chrome-extension';
30
+ autoConnect;
31
+ initPromise = null;
32
+ constructor(options = {}) {
33
+ super(options);
34
+ this.autoConnect = options.autoConnect ?? true;
35
+ }
36
+ /**
37
+ * Connect to the background script
38
+ *
39
+ * Sends a popupInitialize command to reconnect to any existing session.
40
+ */
41
+ async connect() {
42
+ if (this.state === 'connected' || this.state === 'connecting') {
43
+ return;
44
+ }
45
+ this.setState('connecting');
46
+ try {
47
+ // Send popupInitialize to reconnect to existing session
48
+ await this.sendRaw({
49
+ id: generateCommandId(),
50
+ action: 'popupInitialize',
51
+ });
52
+ this.setState('connected');
53
+ }
54
+ catch (error) {
55
+ // Silent fail - session will be created on first tool use if needed
56
+ // Still mark as connected since messaging works
57
+ this.log('debug', 'popupInitialize failed (session will be created on demand):', error);
58
+ this.setState('connected');
59
+ }
60
+ }
61
+ /**
62
+ * Disconnect the transport
63
+ */
64
+ disconnect() {
65
+ if (this.state === 'disconnected') {
66
+ return;
67
+ }
68
+ this.initPromise = null;
69
+ this.setState('disconnected');
70
+ }
71
+ /**
72
+ * Send a command to the background script
73
+ */
74
+ async send(command) {
75
+ // Auto-connect on first send if enabled
76
+ if (this.autoConnect && this.state === 'disconnected') {
77
+ await this.ensureConnected();
78
+ }
79
+ const id = command.id || generateCommandId();
80
+ return this.sendRaw({ ...command, id });
81
+ }
82
+ /**
83
+ * Ensure the transport is connected (lazy initialization)
84
+ */
85
+ async ensureConnected() {
86
+ if (this.state === 'connected') {
87
+ return;
88
+ }
89
+ if (this.initPromise === null) {
90
+ this.initPromise = this.connect();
91
+ }
92
+ return this.initPromise;
93
+ }
94
+ /**
95
+ * Send a command without auto-connect (used for popupInitialize itself)
96
+ */
97
+ sendRaw(command) {
98
+ const id = command.id || generateCommandId();
99
+ return new Promise((resolve) => {
100
+ chrome.runtime.sendMessage({ type: 'btcp:command', command: { ...command, id } }, (response) => {
101
+ if (chrome.runtime.lastError) {
102
+ const error = chrome.runtime.lastError.message || 'Unknown error';
103
+ this.log('debug', 'sendMessage error:', error);
104
+ resolve(this.createErrorResponse(id, error));
105
+ }
106
+ else {
107
+ const resp = response;
108
+ if (resp?.type === 'btcp:response') {
109
+ resolve(resp.response);
110
+ }
111
+ else {
112
+ resolve(this.createErrorResponse(id, 'Unexpected response type'));
113
+ }
114
+ }
115
+ });
116
+ });
117
+ }
118
+ }
119
+ /**
120
+ * Create a Chrome extension transport
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * const transport = createChromeExtensionTransport({ debug: true });
125
+ * const client = createClient({ transport });
126
+ * ```
127
+ */
128
+ export function createChromeExtensionTransport(options = {}) {
129
+ return new ChromeExtensionTransport(options);
130
+ }
131
+ //# sourceMappingURL=chrome-extension.js.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Direct Transport
3
+ *
4
+ * Transport implementation that calls BackgroundAgent.execute() directly,
5
+ * bypassing message passing. Use this in background script context for
6
+ * better performance.
7
+ */
8
+ import type { BackgroundAgent } from '../background.js';
9
+ import type { Command, Response } from '../types.js';
10
+ import { BaseTransport } from './base-transport.js';
11
+ import type { TransportOptions } from './types.js';
12
+ /**
13
+ * Options for the direct transport
14
+ */
15
+ export interface DirectTransportOptions extends TransportOptions {
16
+ /**
17
+ * The BackgroundAgent instance to use for executing commands.
18
+ * This is required since DirectTransport calls execute() directly.
19
+ */
20
+ agent: BackgroundAgent;
21
+ }
22
+ /**
23
+ * Direct Transport
24
+ *
25
+ * Executes commands directly via BackgroundAgent.execute() without
26
+ * any message passing. This is more efficient for background script
27
+ * contexts where the agent is available in-process.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { getBackgroundAgent } from '@btcp/browser-agent/extension';
32
+ * import { createDirectTransport } from '@btcp/browser-agent/extension/transport';
33
+ *
34
+ * const agent = getBackgroundAgent();
35
+ * const transport = createDirectTransport({ agent });
36
+ * const client = createClient({ transport });
37
+ * ```
38
+ */
39
+ export declare class DirectTransport extends BaseTransport {
40
+ readonly name = "direct";
41
+ private agent;
42
+ constructor(options: DirectTransportOptions);
43
+ /**
44
+ * Connect the transport (no-op for direct transport)
45
+ */
46
+ connect(): Promise<void>;
47
+ /**
48
+ * Disconnect the transport
49
+ */
50
+ disconnect(): void;
51
+ /**
52
+ * Send a command directly to the BackgroundAgent
53
+ */
54
+ send(command: Command): Promise<Response>;
55
+ }
56
+ /**
57
+ * Create a direct transport
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * import { getBackgroundAgent } from '@btcp/browser-agent/extension';
62
+ * import { createDirectTransport } from '@btcp/browser-agent/extension/transport';
63
+ *
64
+ * const transport = createDirectTransport({ agent: getBackgroundAgent() });
65
+ * const client = createClient({ transport });
66
+ * ```
67
+ */
68
+ export declare function createDirectTransport(options: DirectTransportOptions): DirectTransport;
69
+ //# sourceMappingURL=direct.d.ts.map
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Direct Transport
3
+ *
4
+ * Transport implementation that calls BackgroundAgent.execute() directly,
5
+ * bypassing message passing. Use this in background script context for
6
+ * better performance.
7
+ */
8
+ import { BaseTransport } from './base-transport.js';
9
+ let commandIdCounter = 0;
10
+ /**
11
+ * Generate a unique command ID for BTCP commands
12
+ */
13
+ function generateCommandId() {
14
+ return `direct_${Date.now()}_${commandIdCounter++}`;
15
+ }
16
+ /**
17
+ * Direct Transport
18
+ *
19
+ * Executes commands directly via BackgroundAgent.execute() without
20
+ * any message passing. This is more efficient for background script
21
+ * contexts where the agent is available in-process.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { getBackgroundAgent } from '@btcp/browser-agent/extension';
26
+ * import { createDirectTransport } from '@btcp/browser-agent/extension/transport';
27
+ *
28
+ * const agent = getBackgroundAgent();
29
+ * const transport = createDirectTransport({ agent });
30
+ * const client = createClient({ transport });
31
+ * ```
32
+ */
33
+ export class DirectTransport extends BaseTransport {
34
+ name = 'direct';
35
+ agent;
36
+ constructor(options) {
37
+ super(options);
38
+ this.agent = options.agent;
39
+ // Direct transport is always "connected" since it's in-process
40
+ this.setState('connected');
41
+ }
42
+ /**
43
+ * Connect the transport (no-op for direct transport)
44
+ */
45
+ async connect() {
46
+ // Direct transport is always connected
47
+ this.setState('connected');
48
+ }
49
+ /**
50
+ * Disconnect the transport
51
+ */
52
+ disconnect() {
53
+ this.setState('disconnected');
54
+ }
55
+ /**
56
+ * Send a command directly to the BackgroundAgent
57
+ */
58
+ async send(command) {
59
+ if (this.state !== 'connected') {
60
+ const id = command.id || generateCommandId();
61
+ return this.createErrorResponse(id, 'Transport is not connected');
62
+ }
63
+ try {
64
+ const id = command.id || generateCommandId();
65
+ return await this.agent.execute({ ...command, id });
66
+ }
67
+ catch (error) {
68
+ const id = command.id || generateCommandId();
69
+ const message = error instanceof Error ? error.message : 'Unknown error';
70
+ this.log('error', 'Direct execute error:', error);
71
+ return this.createErrorResponse(id, message);
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * Create a direct transport
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * import { getBackgroundAgent } from '@btcp/browser-agent/extension';
81
+ * import { createDirectTransport } from '@btcp/browser-agent/extension/transport';
82
+ *
83
+ * const transport = createDirectTransport({ agent: getBackgroundAgent() });
84
+ * const client = createClient({ transport });
85
+ * ```
86
+ */
87
+ export function createDirectTransport(options) {
88
+ return new DirectTransport(options);
89
+ }
90
+ //# sourceMappingURL=direct.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Transport module re-exports
3
+ *
4
+ * This module exports all transport-related types and implementations.
5
+ */
6
+ export type { Transport, TransportState, TransportEvents, TransportOptions, } from './types.js';
7
+ export { BaseTransport } from './base-transport.js';
8
+ export { ChromeExtensionTransport, createChromeExtensionTransport, type ChromeExtensionTransportOptions, } from './chrome-extension.js';
9
+ export { DirectTransport, createDirectTransport, type DirectTransportOptions, } from './direct.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Transport module re-exports
3
+ *
4
+ * This module exports all transport-related types and implementations.
5
+ */
6
+ // Base class (for custom transport implementations)
7
+ export { BaseTransport } from './base-transport.js';
8
+ // Chrome extension transport
9
+ export { ChromeExtensionTransport, createChromeExtensionTransport, } from './chrome-extension.js';
10
+ // Direct transport (in-process, for background scripts)
11
+ export { DirectTransport, createDirectTransport, } from './direct.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Transport abstraction for the client API
3
+ *
4
+ * This module defines the transport interface that allows the client to use
5
+ * different communication mechanisms (Chrome extension messaging, HTTP, WebSocket, etc.)
6
+ */
7
+ import type { Command, Response } from '../types.js';
8
+ /**
9
+ * Transport connection state
10
+ */
11
+ export type TransportState = 'disconnected' | 'connecting' | 'connected' | 'error';
12
+ /**
13
+ * Events emitted by transports
14
+ */
15
+ export interface TransportEvents {
16
+ /** Transport connected successfully */
17
+ connected: () => void;
18
+ /** Transport disconnected */
19
+ disconnected: (reason?: string) => void;
20
+ /** Transport encountered an error */
21
+ error: (error: Error) => void;
22
+ /** Transport state changed */
23
+ stateChange: (state: TransportState) => void;
24
+ }
25
+ /**
26
+ * Base transport options shared by all transports
27
+ */
28
+ export interface TransportOptions {
29
+ /** Enable debug logging */
30
+ debug?: boolean;
31
+ }
32
+ /**
33
+ * Transport interface for sending commands to the backend
34
+ *
35
+ * Transports handle the communication layer between the client and the
36
+ * command execution backend (background script, HTTP server, etc.)
37
+ */
38
+ export interface Transport {
39
+ /**
40
+ * Human-readable name for this transport (e.g., 'chrome-extension', 'http')
41
+ */
42
+ readonly name: string;
43
+ /**
44
+ * Send a command and wait for the response
45
+ */
46
+ send(command: Command): Promise<Response>;
47
+ /**
48
+ * Connect the transport (if applicable)
49
+ * Some transports may be stateless and not require connection
50
+ */
51
+ connect(): Promise<void>;
52
+ /**
53
+ * Disconnect the transport and clean up resources
54
+ */
55
+ disconnect(): void;
56
+ /**
57
+ * Get the current connection state
58
+ */
59
+ getState(): TransportState;
60
+ /**
61
+ * Check if the transport is currently connected
62
+ */
63
+ isConnected(): boolean;
64
+ /**
65
+ * Register an event handler
66
+ */
67
+ on<K extends keyof TransportEvents>(event: K, handler: TransportEvents[K]): void;
68
+ /**
69
+ * Unregister an event handler
70
+ */
71
+ off<K extends keyof TransportEvents>(event: K, handler: TransportEvents[K]): void;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Transport abstraction for the client API
3
+ *
4
+ * This module defines the transport interface that allows the client to use
5
+ * different communication mechanisms (Chrome extension messaging, HTTP, WebSocket, etc.)
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map