btcp-browser-agent 0.1.14 → 0.1.17
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 -21
- package/README.md +338 -338
- package/package.json +69 -69
- package/packages/core/dist/actions.js +35 -35
- package/packages/core/dist/snapshot.js +50 -0
- package/packages/core/dist/types.d.ts +2 -1
- package/packages/extension/dist/background.js +1 -1
- package/packages/extension/dist/index.d.ts +37 -10
- package/packages/extension/dist/index.js +23 -58
- package/packages/extension/dist/session-manager.d.ts +10 -1
- package/packages/extension/dist/session-manager.js +49 -10
- package/packages/extension/dist/transport/base-transport.d.ts +65 -0
- package/packages/extension/dist/transport/base-transport.js +115 -0
- package/packages/extension/dist/transport/chrome-extension.d.ts +71 -0
- package/packages/extension/dist/transport/chrome-extension.js +131 -0
- package/packages/extension/dist/transport/direct.d.ts +69 -0
- package/packages/extension/dist/transport/direct.js +90 -0
- package/packages/extension/dist/transport/index.d.ts +10 -0
- package/packages/extension/dist/transport/index.js +12 -0
- package/packages/extension/dist/transport/types.d.ts +73 -0
- package/packages/extension/dist/transport/types.js +8 -0
|
@@ -15,6 +15,7 @@ export class SessionManager {
|
|
|
15
15
|
initializationPromise;
|
|
16
16
|
maxSession;
|
|
17
17
|
maxOpenTab;
|
|
18
|
+
ensureSessionPromise = null;
|
|
18
19
|
constructor(options = {}) {
|
|
19
20
|
this.maxSession = options.maxSession ?? 1;
|
|
20
21
|
this.maxOpenTab = options.maxOpenTab ?? 1;
|
|
@@ -222,17 +223,21 @@ export class SessionManager {
|
|
|
222
223
|
}
|
|
223
224
|
/**
|
|
224
225
|
* Create a new tab group
|
|
226
|
+
* @param options Group creation options
|
|
227
|
+
* @param internal If true, bypasses session limit check (used by ensureSession)
|
|
225
228
|
*/
|
|
226
|
-
async createGroup(options = {}) {
|
|
229
|
+
async createGroup(options = {}, internal = false) {
|
|
227
230
|
// Wait for initialization to complete first
|
|
228
231
|
await this.waitForInitialization();
|
|
229
|
-
console.log('[SessionManager] createGroup called with options:', options);
|
|
230
|
-
// Check if we can create a new session
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
console.log('[SessionManager] createGroup called with options:', options, 'internal:', internal);
|
|
233
|
+
// Check if we can create a new session (skip if internal call from ensureSession)
|
|
234
|
+
if (!internal) {
|
|
235
|
+
const canCreate = await this.canCreateSession();
|
|
236
|
+
if (!canCreate) {
|
|
237
|
+
const count = await this.getSessionCount();
|
|
238
|
+
throw new Error(`Maximum session limit reached (${count}/${this.maxSession}). ` +
|
|
239
|
+
`Close an existing session before creating a new one.`);
|
|
240
|
+
}
|
|
236
241
|
}
|
|
237
242
|
const { tabIds = [], title = this.generateSessionName(), color = 'blue', collapsed = false, } = options;
|
|
238
243
|
// If no tabIds provided, create a new blank tab for the session
|
|
@@ -512,18 +517,42 @@ export class SessionManager {
|
|
|
512
517
|
/**
|
|
513
518
|
* Ensure a session exists - restore from storage, use existing, or create new
|
|
514
519
|
* Returns the session group ID (creates if needed)
|
|
520
|
+
*
|
|
521
|
+
* This method is atomic - concurrent calls will wait for the same promise
|
|
515
522
|
*/
|
|
516
523
|
async ensureSession() {
|
|
524
|
+
// Return existing promise if already in progress
|
|
525
|
+
if (this.ensureSessionPromise) {
|
|
526
|
+
console.log('[SessionManager] ensureSession already in progress, waiting...');
|
|
527
|
+
return this.ensureSessionPromise;
|
|
528
|
+
}
|
|
529
|
+
// Create new promise and store it
|
|
530
|
+
this.ensureSessionPromise = this._doEnsureSession();
|
|
531
|
+
try {
|
|
532
|
+
return await this.ensureSessionPromise;
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
// Clear promise when done
|
|
536
|
+
this.ensureSessionPromise = null;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Internal implementation of ensureSession
|
|
541
|
+
*/
|
|
542
|
+
async _doEnsureSession() {
|
|
517
543
|
await this.waitForInitialization();
|
|
544
|
+
console.log('[SessionManager] Starting ensureSession...');
|
|
518
545
|
// Step 1: Already have active session
|
|
519
546
|
if (this.activeSessionGroupId !== null) {
|
|
520
547
|
// Verify it still exists
|
|
521
548
|
try {
|
|
522
549
|
await chrome.tabGroups.get(this.activeSessionGroupId);
|
|
550
|
+
console.log('[SessionManager] Active session still valid:', this.activeSessionGroupId);
|
|
523
551
|
return this.activeSessionGroupId;
|
|
524
552
|
}
|
|
525
553
|
catch {
|
|
526
554
|
// Group no longer exists, continue to restore/create
|
|
555
|
+
console.log('[SessionManager] Active session no longer exists');
|
|
527
556
|
this.activeSessionGroupId = null;
|
|
528
557
|
}
|
|
529
558
|
}
|
|
@@ -531,23 +560,33 @@ export class SessionManager {
|
|
|
531
560
|
const result = await chrome.storage.session.get(SESSION_STORAGE_KEY);
|
|
532
561
|
const stored = result[SESSION_STORAGE_KEY];
|
|
533
562
|
if (stored?.groupId) {
|
|
563
|
+
console.log('[SessionManager] Step 2: Attempting to reconnect to stored session:', stored.groupId);
|
|
534
564
|
const reconnected = await this.reconnectSession(stored.groupId);
|
|
535
565
|
if (reconnected && this.activeSessionGroupId !== null) {
|
|
566
|
+
console.log('[SessionManager] Reconnected to stored session:', this.activeSessionGroupId);
|
|
536
567
|
return this.activeSessionGroupId;
|
|
537
568
|
}
|
|
538
569
|
}
|
|
539
570
|
// Step 3: Find existing BTCP group
|
|
571
|
+
console.log('[SessionManager] Step 3: Looking for existing BTCP groups...');
|
|
540
572
|
const groups = await chrome.tabGroups.query({});
|
|
573
|
+
console.log('[SessionManager] Found tab groups:', groups.map(g => ({ id: g.id, title: g.title })));
|
|
541
574
|
const btcpGroup = groups.find(g => g.title?.startsWith('BTCP'));
|
|
542
575
|
if (btcpGroup) {
|
|
576
|
+
console.log('[SessionManager] Found existing BTCP group:', btcpGroup.id, btcpGroup.title);
|
|
543
577
|
const used = await this.useExistingGroupAsSession(btcpGroup.id);
|
|
544
578
|
if (used && this.activeSessionGroupId !== null) {
|
|
579
|
+
console.log('[SessionManager] Successfully reused existing session:', this.activeSessionGroupId);
|
|
545
580
|
return this.activeSessionGroupId;
|
|
546
581
|
}
|
|
547
582
|
}
|
|
548
|
-
|
|
583
|
+
else {
|
|
584
|
+
console.log('[SessionManager] No existing BTCP groups found');
|
|
585
|
+
}
|
|
586
|
+
// Step 4: Create new session (bypass limit check since we already tried to reuse)
|
|
549
587
|
console.log('[SessionManager] No existing session found, creating new one...');
|
|
550
|
-
const newGroup = await this.createGroup({ color: 'blue' });
|
|
588
|
+
const newGroup = await this.createGroup({ color: 'blue' }, true); // internal=true bypasses limit
|
|
589
|
+
console.log('[SessionManager] Created new session:', newGroup.id);
|
|
551
590
|
return newGroup.id;
|
|
552
591
|
}
|
|
553
592
|
/**
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base transport class with shared functionality
|
|
3
|
+
*
|
|
4
|
+
* Provides event handling, state management, and utility methods for transports.
|
|
5
|
+
*/
|
|
6
|
+
import type { Command, Response } from '../types.js';
|
|
7
|
+
import type { Transport, TransportEvents, TransportOptions, TransportState } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Abstract base class for transports
|
|
10
|
+
*
|
|
11
|
+
* Provides common functionality for event handling and state management.
|
|
12
|
+
* Subclasses must implement `send()`, `connect()`, and `disconnect()`.
|
|
13
|
+
*/
|
|
14
|
+
export declare abstract class BaseTransport implements Transport {
|
|
15
|
+
abstract readonly name: string;
|
|
16
|
+
protected state: TransportState;
|
|
17
|
+
protected debug: boolean;
|
|
18
|
+
private eventHandlers;
|
|
19
|
+
constructor(options?: TransportOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Send a command - must be implemented by subclasses
|
|
22
|
+
*/
|
|
23
|
+
abstract send(command: Command): Promise<Response>;
|
|
24
|
+
/**
|
|
25
|
+
* Connect the transport - must be implemented by subclasses
|
|
26
|
+
*/
|
|
27
|
+
abstract connect(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Disconnect the transport - must be implemented by subclasses
|
|
30
|
+
*/
|
|
31
|
+
abstract disconnect(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get the current connection state
|
|
34
|
+
*/
|
|
35
|
+
getState(): TransportState;
|
|
36
|
+
/**
|
|
37
|
+
* Check if the transport is connected
|
|
38
|
+
*/
|
|
39
|
+
isConnected(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Register an event handler
|
|
42
|
+
*/
|
|
43
|
+
on<K extends keyof TransportEvents>(event: K, handler: TransportEvents[K]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Unregister an event handler
|
|
46
|
+
*/
|
|
47
|
+
off<K extends keyof TransportEvents>(event: K, handler: TransportEvents[K]): void;
|
|
48
|
+
/**
|
|
49
|
+
* Emit an event to all registered handlers
|
|
50
|
+
*/
|
|
51
|
+
protected emit<K extends keyof TransportEvents>(event: K, ...args: Parameters<TransportEvents[K]>): void;
|
|
52
|
+
/**
|
|
53
|
+
* Update the transport state and emit stateChange event
|
|
54
|
+
*/
|
|
55
|
+
protected setState(newState: TransportState): void;
|
|
56
|
+
/**
|
|
57
|
+
* Log a message if debug is enabled
|
|
58
|
+
*/
|
|
59
|
+
protected log(level: 'debug' | 'info' | 'warn' | 'error', ...args: unknown[]): void;
|
|
60
|
+
/**
|
|
61
|
+
* Create an error response with the given message
|
|
62
|
+
*/
|
|
63
|
+
protected createErrorResponse(id: string, error: string): Response;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=base-transport.d.ts.map
|
|
@@ -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
|