codebot-ai 1.0.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 +247 -0
- package/bin/codebot +5 -0
- package/dist/agent.d.ts +31 -0
- package/dist/agent.js +256 -0
- package/dist/banner.d.ts +19 -0
- package/dist/banner.js +148 -0
- package/dist/browser/cdp.d.ts +29 -0
- package/dist/browser/cdp.js +292 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +518 -0
- package/dist/context/manager.d.ts +27 -0
- package/dist/context/manager.js +139 -0
- package/dist/context/repo-map.d.ts +5 -0
- package/dist/context/repo-map.js +100 -0
- package/dist/history.d.ts +27 -0
- package/dist/history.js +146 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +42 -0
- package/dist/memory.d.ts +39 -0
- package/dist/memory.js +168 -0
- package/dist/parser.d.ts +8 -0
- package/dist/parser.js +79 -0
- package/dist/providers/anthropic.d.ts +9 -0
- package/dist/providers/anthropic.js +288 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +13 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +173 -0
- package/dist/providers/registry.d.ts +15 -0
- package/dist/providers/registry.js +115 -0
- package/dist/setup.d.ts +17 -0
- package/dist/setup.js +243 -0
- package/dist/tools/browser.d.ts +43 -0
- package/dist/tools/browser.js +329 -0
- package/dist/tools/edit.d.ts +26 -0
- package/dist/tools/edit.js +73 -0
- package/dist/tools/execute.d.ts +26 -0
- package/dist/tools/execute.js +52 -0
- package/dist/tools/glob.d.ts +24 -0
- package/dist/tools/glob.js +102 -0
- package/dist/tools/grep.d.ts +29 -0
- package/dist/tools/grep.js +125 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +49 -0
- package/dist/tools/memory.d.ts +36 -0
- package/dist/tools/memory.js +114 -0
- package/dist/tools/read.d.ts +26 -0
- package/dist/tools/read.js +75 -0
- package/dist/tools/think.d.ts +18 -0
- package/dist/tools/think.js +20 -0
- package/dist/tools/web-fetch.d.ts +36 -0
- package/dist/tools/web-fetch.js +83 -0
- package/dist/tools/write.d.ts +22 -0
- package/dist/tools/write.js +65 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +3 -0
- package/package.json +57 -0
package/dist/banner.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CodeBot AI mascot and CLI banner.
|
|
4
|
+
*
|
|
5
|
+
* Mascot name: Codi
|
|
6
|
+
* Three designs: Pixel Bot, Monitor Bot, Visor Helmet
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.banner = exports.BANNER_3 = exports.MASCOT_3 = exports.BANNER_2 = exports.MASCOT_2 = exports.BANNER_1 = exports.MASCOT_1 = void 0;
|
|
10
|
+
exports.randomGreeting = randomGreeting;
|
|
11
|
+
exports.compactBanner = compactBanner;
|
|
12
|
+
const C = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
green: '\x1b[32m',
|
|
18
|
+
yellow: '\x1b[33m',
|
|
19
|
+
blue: '\x1b[34m',
|
|
20
|
+
magenta: '\x1b[35m',
|
|
21
|
+
cyan: '\x1b[36m',
|
|
22
|
+
white: '\x1b[37m',
|
|
23
|
+
brightCyan: '\x1b[96m',
|
|
24
|
+
brightGreen: '\x1b[92m',
|
|
25
|
+
brightYellow: '\x1b[93m',
|
|
26
|
+
brightMagenta: '\x1b[95m',
|
|
27
|
+
brightWhite: '\x1b[97m',
|
|
28
|
+
};
|
|
29
|
+
// ─────────────────────────────────────────────────────
|
|
30
|
+
// DESIGN 1: "Pixel Bot" — Half-block pixel art robot head
|
|
31
|
+
// Retro-modern pixel aesthetic. The signature look.
|
|
32
|
+
// ─────────────────────────────────────────────────────
|
|
33
|
+
exports.MASCOT_1 = `
|
|
34
|
+
██
|
|
35
|
+
▄▄████████████▄▄
|
|
36
|
+
█ █
|
|
37
|
+
█ ▄██▄ ▄██▄ █
|
|
38
|
+
█ ▀██▀ ▀██▀ █
|
|
39
|
+
█ █
|
|
40
|
+
█ ▀██████▀ █
|
|
41
|
+
▀▀████████████▀▀
|
|
42
|
+
`;
|
|
43
|
+
const BANNER_1 = (version, model, provider, session, autonomous) => {
|
|
44
|
+
const lines = [
|
|
45
|
+
'',
|
|
46
|
+
`${C.brightGreen} ██${C.reset}`,
|
|
47
|
+
`${C.cyan} ▄▄████████████▄▄${C.reset}`,
|
|
48
|
+
`${C.cyan} █${C.reset} ${C.cyan}█${C.reset} ${C.bold}${C.brightCyan}CodeBot AI${C.reset} ${C.dim}v${version}${C.reset}`,
|
|
49
|
+
`${C.cyan} █ ${C.brightGreen}▄██▄${C.reset} ${C.brightGreen}▄██▄${C.reset} ${C.cyan}█${C.reset} ${C.dim}Think local. Code global.${C.reset}`,
|
|
50
|
+
`${C.cyan} █ ${C.brightGreen}▀██▀${C.reset} ${C.brightGreen}▀██▀${C.reset} ${C.cyan}█${C.reset}`,
|
|
51
|
+
`${C.cyan} █${C.reset} ${C.cyan}█${C.reset} ${C.dim}Model: ${C.white}${model}${C.reset}`,
|
|
52
|
+
`${C.cyan} █ ${C.brightCyan}▀██████▀${C.reset} ${C.cyan}█${C.reset} ${C.dim}Provider: ${C.white}${provider}${C.reset}`,
|
|
53
|
+
`${C.cyan} ▀▀████████████▀▀${C.reset} ${C.dim}Session: ${C.white}${session}${C.reset}`,
|
|
54
|
+
autonomous ? ` ${C.brightYellow}${C.bold}⚡ AUTONOMOUS${C.reset}` : '',
|
|
55
|
+
'',
|
|
56
|
+
];
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
};
|
|
59
|
+
exports.BANNER_1 = BANNER_1;
|
|
60
|
+
// ─────────────────────────────────────────────────────
|
|
61
|
+
// DESIGN 2: "Monitor Bot" — Screen face with side panels
|
|
62
|
+
// Clean technical look. Like a friendly terminal display.
|
|
63
|
+
// ─────────────────────────────────────────────────────
|
|
64
|
+
exports.MASCOT_2 = `
|
|
65
|
+
╔╗ ╔════════════════╗ ╔╗
|
|
66
|
+
║║ ║ ║ ║║
|
|
67
|
+
║║ ║ ● ● ║ ║║
|
|
68
|
+
║║ ║ ║ ║║
|
|
69
|
+
║║ ║ └──────┘ ║ ║║
|
|
70
|
+
║║ ║ ║ ║║
|
|
71
|
+
╚╝ ╚════════════════╝ ╚╝
|
|
72
|
+
`;
|
|
73
|
+
const BANNER_2 = (version, model, provider, session, autonomous) => {
|
|
74
|
+
const lines = [
|
|
75
|
+
'',
|
|
76
|
+
`${C.dim} ╔╗${C.reset} ${C.cyan}╔════════════════╗${C.reset} ${C.dim}╔╗${C.reset}`,
|
|
77
|
+
`${C.dim} ║║${C.reset} ${C.cyan}║${C.reset} ${C.cyan}║${C.reset} ${C.dim}║║${C.reset}`,
|
|
78
|
+
`${C.dim} ║║${C.reset} ${C.cyan}║${C.reset} ${C.brightGreen}●${C.reset} ${C.brightGreen}●${C.reset} ${C.cyan}║${C.reset} ${C.dim}║║${C.reset} ${C.bold}${C.brightCyan}CodeBot AI${C.reset} ${C.dim}v${version}${C.reset}`,
|
|
79
|
+
`${C.dim} ║║${C.reset} ${C.cyan}║${C.reset} ${C.cyan}║${C.reset} ${C.dim}║║${C.reset} ${C.dim}Think local. Code global.${C.reset}`,
|
|
80
|
+
`${C.dim} ║║${C.reset} ${C.cyan}║${C.reset} ${C.brightCyan}└──────┘${C.reset} ${C.cyan}║${C.reset} ${C.dim}║║${C.reset}`,
|
|
81
|
+
`${C.dim} ║║${C.reset} ${C.cyan}║${C.reset} ${C.cyan}║${C.reset} ${C.dim}║║${C.reset} ${C.dim}Model: ${C.white}${model}${C.reset}`,
|
|
82
|
+
`${C.dim} ╚╝${C.reset} ${C.cyan}╚════════════════╝${C.reset} ${C.dim}╚╝${C.reset} ${C.dim}Provider: ${C.white}${provider}${C.reset}`,
|
|
83
|
+
` ${C.dim}Session: ${C.white}${session}${C.reset}`,
|
|
84
|
+
autonomous ? ` ${C.brightYellow}${C.bold}⚡ AUTONOMOUS${C.reset}` : '',
|
|
85
|
+
'',
|
|
86
|
+
];
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
};
|
|
89
|
+
exports.BANNER_2 = BANNER_2;
|
|
90
|
+
// ─────────────────────────────────────────────────────
|
|
91
|
+
// DESIGN 3: "Visor Helmet" — Daft Punk style with glowing visor
|
|
92
|
+
// Sleek, aggressive, cyberpunk. Diamond silhouette.
|
|
93
|
+
// ─────────────────────────────────────────────────────
|
|
94
|
+
exports.MASCOT_3 = `
|
|
95
|
+
▄████████▄
|
|
96
|
+
█▀ ▀█
|
|
97
|
+
█ ░░██████░░ █
|
|
98
|
+
█ ░░░░░░░░░░ █
|
|
99
|
+
█▄ ▄█
|
|
100
|
+
▀████████▀
|
|
101
|
+
`;
|
|
102
|
+
const BANNER_3 = (version, model, provider, session, autonomous) => {
|
|
103
|
+
const lines = [
|
|
104
|
+
'',
|
|
105
|
+
`${C.cyan} ▄████████▄${C.reset}`,
|
|
106
|
+
`${C.cyan} █▀${C.reset} ${C.cyan}▀█${C.reset} ${C.bold}${C.brightCyan}CodeBot AI${C.reset} ${C.dim}v${version}${C.reset}`,
|
|
107
|
+
`${C.cyan} █ ${C.brightCyan}░░${C.brightGreen}██████${C.brightCyan}░░${C.cyan} █${C.reset} ${C.dim}Think local. Code global.${C.reset}`,
|
|
108
|
+
`${C.cyan} █ ${C.dim}░░░░░░░░░░${C.cyan} █${C.reset}`,
|
|
109
|
+
`${C.cyan} █▄${C.reset} ${C.cyan}▄█${C.reset} ${C.dim}Model: ${C.white}${model}${C.reset}`,
|
|
110
|
+
`${C.cyan} ▀████████▀${C.reset} ${C.dim}Provider: ${C.white}${provider}${C.reset}`,
|
|
111
|
+
` ${C.dim}Session: ${C.white}${session}${C.reset}`,
|
|
112
|
+
autonomous ? ` ${C.brightYellow}${C.bold}⚡ AUTONOMOUS${C.reset}` : '',
|
|
113
|
+
'',
|
|
114
|
+
];
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
};
|
|
117
|
+
exports.BANNER_3 = BANNER_3;
|
|
118
|
+
// ─────────────────────────────────────────────────────
|
|
119
|
+
// Default banner (Design 1 — Pixel Bot)
|
|
120
|
+
// ─────────────────────────────────────────────────────
|
|
121
|
+
exports.banner = exports.BANNER_1;
|
|
122
|
+
/**
|
|
123
|
+
* Random startup greeting from Codi.
|
|
124
|
+
*/
|
|
125
|
+
const GREETINGS = [
|
|
126
|
+
"Systems online. Let's ship.",
|
|
127
|
+
"All circuits green. Ready to code.",
|
|
128
|
+
"What are we building today?",
|
|
129
|
+
"I read your codebase. We need to talk.",
|
|
130
|
+
"Local power, global ambitions.",
|
|
131
|
+
"No cloud. No limits. Let's go.",
|
|
132
|
+
"Standing by. Say the word.",
|
|
133
|
+
"Initialized. Awaiting instructions.",
|
|
134
|
+
"Your code, your machine, your move.",
|
|
135
|
+
"Another day, another deploy.",
|
|
136
|
+
"Signal locked. Ready to transmit.",
|
|
137
|
+
"Booted up. Zero dependencies loaded.",
|
|
138
|
+
];
|
|
139
|
+
function randomGreeting() {
|
|
140
|
+
return GREETINGS[Math.floor(Math.random() * GREETINGS.length)];
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Compact single-line startup for non-TTY / piped usage.
|
|
144
|
+
*/
|
|
145
|
+
function compactBanner(version, model) {
|
|
146
|
+
return `${C.bold}${C.brightCyan}CodeBot AI${C.reset} ${C.dim}v${version}${C.reset} ${C.dim}[${model}]${C.reset}`;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare class CDPClient {
|
|
2
|
+
private socket;
|
|
3
|
+
private messageId;
|
|
4
|
+
private pending;
|
|
5
|
+
private buffer;
|
|
6
|
+
private connected;
|
|
7
|
+
/** Connect to Chrome's debugging WebSocket */
|
|
8
|
+
connect(wsUrl: string): Promise<void>;
|
|
9
|
+
/** Send a CDP command and wait for response */
|
|
10
|
+
send(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
11
|
+
/** Close the connection */
|
|
12
|
+
close(): void;
|
|
13
|
+
isConnected(): boolean;
|
|
14
|
+
/** Send a WebSocket text frame (masked, as required by client) */
|
|
15
|
+
private sendFrame;
|
|
16
|
+
/** Handle incoming data — parse WebSocket frames */
|
|
17
|
+
private onData;
|
|
18
|
+
private sendPong;
|
|
19
|
+
}
|
|
20
|
+
/** Get the WebSocket debugger URL from Chrome's HTTP endpoint */
|
|
21
|
+
export declare function getDebuggerUrl(port?: number): Promise<string>;
|
|
22
|
+
/** Get list of open tabs */
|
|
23
|
+
export declare function getTargets(port?: number): Promise<Array<{
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
url: string;
|
|
27
|
+
webSocketDebuggerUrl: string;
|
|
28
|
+
}>>;
|
|
29
|
+
//# sourceMappingURL=cdp.d.ts.map
|
|
@@ -0,0 +1,292 @@
|
|
|
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.CDPClient = void 0;
|
|
37
|
+
exports.getDebuggerUrl = getDebuggerUrl;
|
|
38
|
+
exports.getTargets = getTargets;
|
|
39
|
+
/**
|
|
40
|
+
* Minimal Chrome DevTools Protocol client.
|
|
41
|
+
* Zero dependencies — uses Node's built-in net/http/crypto modules.
|
|
42
|
+
*/
|
|
43
|
+
const http = __importStar(require("http"));
|
|
44
|
+
const crypto = __importStar(require("crypto"));
|
|
45
|
+
class CDPClient {
|
|
46
|
+
socket = null;
|
|
47
|
+
messageId = 0;
|
|
48
|
+
pending = new Map();
|
|
49
|
+
buffer = Buffer.alloc(0);
|
|
50
|
+
connected = false;
|
|
51
|
+
/** Connect to Chrome's debugging WebSocket */
|
|
52
|
+
async connect(wsUrl) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const url = new URL(wsUrl);
|
|
55
|
+
const key = crypto.randomBytes(16).toString('base64');
|
|
56
|
+
const req = http.request({
|
|
57
|
+
hostname: url.hostname,
|
|
58
|
+
port: url.port,
|
|
59
|
+
path: url.pathname,
|
|
60
|
+
method: 'GET',
|
|
61
|
+
headers: {
|
|
62
|
+
'Connection': 'Upgrade',
|
|
63
|
+
'Upgrade': 'websocket',
|
|
64
|
+
'Sec-WebSocket-Key': key,
|
|
65
|
+
'Sec-WebSocket-Version': '13',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
req.on('upgrade', (_res, socket) => {
|
|
69
|
+
this.socket = socket;
|
|
70
|
+
this.connected = true;
|
|
71
|
+
socket.on('data', (data) => this.onData(data));
|
|
72
|
+
socket.on('close', () => {
|
|
73
|
+
this.connected = false;
|
|
74
|
+
this.socket = null;
|
|
75
|
+
});
|
|
76
|
+
socket.on('error', (err) => {
|
|
77
|
+
this.connected = false;
|
|
78
|
+
for (const [, p] of this.pending) {
|
|
79
|
+
p.reject(err);
|
|
80
|
+
}
|
|
81
|
+
this.pending.clear();
|
|
82
|
+
});
|
|
83
|
+
resolve();
|
|
84
|
+
});
|
|
85
|
+
req.on('error', reject);
|
|
86
|
+
req.end();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** Send a CDP command and wait for response */
|
|
90
|
+
async send(method, params = {}) {
|
|
91
|
+
if (!this.socket || !this.connected) {
|
|
92
|
+
throw new Error('Not connected to Chrome');
|
|
93
|
+
}
|
|
94
|
+
const id = ++this.messageId;
|
|
95
|
+
const msg = JSON.stringify({ id, method, params });
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const timeout = setTimeout(() => {
|
|
98
|
+
this.pending.delete(id);
|
|
99
|
+
reject(new Error(`CDP timeout: ${method}`));
|
|
100
|
+
}, 30000);
|
|
101
|
+
this.pending.set(id, {
|
|
102
|
+
resolve: (resp) => {
|
|
103
|
+
clearTimeout(timeout);
|
|
104
|
+
if (resp.error) {
|
|
105
|
+
reject(new Error(`CDP error: ${resp.error.message}`));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
resolve(resp.result || {});
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
reject: (err) => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
reject(err);
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
this.sendFrame(msg);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Close the connection */
|
|
120
|
+
close() {
|
|
121
|
+
if (this.socket) {
|
|
122
|
+
try {
|
|
123
|
+
// Send WebSocket close frame
|
|
124
|
+
const closeFrame = Buffer.alloc(6);
|
|
125
|
+
closeFrame[0] = 0x88; // FIN + Close
|
|
126
|
+
closeFrame[1] = 0x80; // Masked, 0 length
|
|
127
|
+
this.socket.write(closeFrame);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// ignore
|
|
131
|
+
}
|
|
132
|
+
this.socket.destroy();
|
|
133
|
+
this.socket = null;
|
|
134
|
+
this.connected = false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
isConnected() {
|
|
138
|
+
return this.connected;
|
|
139
|
+
}
|
|
140
|
+
/** Send a WebSocket text frame (masked, as required by client) */
|
|
141
|
+
sendFrame(data) {
|
|
142
|
+
if (!this.socket)
|
|
143
|
+
return;
|
|
144
|
+
const payload = Buffer.from(data, 'utf-8');
|
|
145
|
+
const mask = crypto.randomBytes(4);
|
|
146
|
+
let header;
|
|
147
|
+
if (payload.length < 126) {
|
|
148
|
+
header = Buffer.alloc(6);
|
|
149
|
+
header[0] = 0x81; // FIN + Text
|
|
150
|
+
header[1] = 0x80 | payload.length; // Masked + length
|
|
151
|
+
mask.copy(header, 2);
|
|
152
|
+
}
|
|
153
|
+
else if (payload.length < 65536) {
|
|
154
|
+
header = Buffer.alloc(8);
|
|
155
|
+
header[0] = 0x81;
|
|
156
|
+
header[1] = 0x80 | 126;
|
|
157
|
+
header.writeUInt16BE(payload.length, 2);
|
|
158
|
+
mask.copy(header, 4);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
header = Buffer.alloc(14);
|
|
162
|
+
header[0] = 0x81;
|
|
163
|
+
header[1] = 0x80 | 127;
|
|
164
|
+
// Write 64-bit length (we only use lower 32 bits)
|
|
165
|
+
header.writeUInt32BE(0, 2);
|
|
166
|
+
header.writeUInt32BE(payload.length, 6);
|
|
167
|
+
mask.copy(header, 10);
|
|
168
|
+
}
|
|
169
|
+
// Mask the payload
|
|
170
|
+
const masked = Buffer.alloc(payload.length);
|
|
171
|
+
for (let i = 0; i < payload.length; i++) {
|
|
172
|
+
masked[i] = payload[i] ^ mask[i % 4];
|
|
173
|
+
}
|
|
174
|
+
this.socket.write(Buffer.concat([header, masked]));
|
|
175
|
+
}
|
|
176
|
+
/** Handle incoming data — parse WebSocket frames */
|
|
177
|
+
onData(data) {
|
|
178
|
+
this.buffer = Buffer.concat([this.buffer, data]);
|
|
179
|
+
while (this.buffer.length >= 2) {
|
|
180
|
+
const firstByte = this.buffer[0];
|
|
181
|
+
const secondByte = this.buffer[1];
|
|
182
|
+
const isFin = (firstByte & 0x80) !== 0;
|
|
183
|
+
const opcode = firstByte & 0x0f;
|
|
184
|
+
const isMasked = (secondByte & 0x80) !== 0;
|
|
185
|
+
let payloadLength = secondByte & 0x7f;
|
|
186
|
+
let offset = 2;
|
|
187
|
+
if (payloadLength === 126) {
|
|
188
|
+
if (this.buffer.length < 4)
|
|
189
|
+
return; // Need more data
|
|
190
|
+
payloadLength = this.buffer.readUInt16BE(2);
|
|
191
|
+
offset = 4;
|
|
192
|
+
}
|
|
193
|
+
else if (payloadLength === 127) {
|
|
194
|
+
if (this.buffer.length < 10)
|
|
195
|
+
return;
|
|
196
|
+
payloadLength = this.buffer.readUInt32BE(6); // Only lower 32 bits
|
|
197
|
+
offset = 10;
|
|
198
|
+
}
|
|
199
|
+
if (isMasked)
|
|
200
|
+
offset += 4; // Skip mask (server shouldn't mask)
|
|
201
|
+
if (this.buffer.length < offset + payloadLength)
|
|
202
|
+
return; // Need more data
|
|
203
|
+
const payload = this.buffer.subarray(offset, offset + payloadLength);
|
|
204
|
+
this.buffer = this.buffer.subarray(offset + payloadLength);
|
|
205
|
+
if (opcode === 0x01 && isFin) {
|
|
206
|
+
// Text frame
|
|
207
|
+
const text = payload.toString('utf-8');
|
|
208
|
+
try {
|
|
209
|
+
const msg = JSON.parse(text);
|
|
210
|
+
if (msg.id !== undefined && this.pending.has(msg.id)) {
|
|
211
|
+
const handler = this.pending.get(msg.id);
|
|
212
|
+
this.pending.delete(msg.id);
|
|
213
|
+
handler.resolve(msg);
|
|
214
|
+
}
|
|
215
|
+
// Events (no id) are ignored for now
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Malformed JSON, skip
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (opcode === 0x08) {
|
|
222
|
+
// Close frame
|
|
223
|
+
this.close();
|
|
224
|
+
}
|
|
225
|
+
else if (opcode === 0x09) {
|
|
226
|
+
// Ping — respond with pong
|
|
227
|
+
this.sendPong(payload);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
sendPong(data) {
|
|
232
|
+
if (!this.socket)
|
|
233
|
+
return;
|
|
234
|
+
const mask = crypto.randomBytes(4);
|
|
235
|
+
const header = Buffer.alloc(6);
|
|
236
|
+
header[0] = 0x8a; // FIN + Pong
|
|
237
|
+
header[1] = 0x80 | data.length;
|
|
238
|
+
mask.copy(header, 2);
|
|
239
|
+
const masked = Buffer.alloc(data.length);
|
|
240
|
+
for (let i = 0; i < data.length; i++) {
|
|
241
|
+
masked[i] = data[i] ^ mask[i % 4];
|
|
242
|
+
}
|
|
243
|
+
this.socket.write(Buffer.concat([header, masked]));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
exports.CDPClient = CDPClient;
|
|
247
|
+
/** Get the WebSocket debugger URL from Chrome's HTTP endpoint */
|
|
248
|
+
async function getDebuggerUrl(port = 9222) {
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
const req = http.get(`http://127.0.0.1:${port}/json/version`, (res) => {
|
|
251
|
+
let data = '';
|
|
252
|
+
res.on('data', (chunk) => (data += chunk));
|
|
253
|
+
res.on('end', () => {
|
|
254
|
+
try {
|
|
255
|
+
const info = JSON.parse(data);
|
|
256
|
+
resolve(info.webSocketDebuggerUrl);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
reject(new Error('Failed to parse Chrome debugger info'));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
req.on('error', () => reject(new Error(`Chrome not running on port ${port}. Launch with: chrome --remote-debugging-port=${port}`)));
|
|
264
|
+
req.setTimeout(3000, () => {
|
|
265
|
+
req.destroy();
|
|
266
|
+
reject(new Error('Chrome debugger timeout'));
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** Get list of open tabs */
|
|
271
|
+
async function getTargets(port = 9222) {
|
|
272
|
+
return new Promise((resolve, reject) => {
|
|
273
|
+
const req = http.get(`http://127.0.0.1:${port}/json/list`, (res) => {
|
|
274
|
+
let data = '';
|
|
275
|
+
res.on('data', (chunk) => (data += chunk));
|
|
276
|
+
res.on('end', () => {
|
|
277
|
+
try {
|
|
278
|
+
resolve(JSON.parse(data));
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
reject(new Error('Failed to parse targets'));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
req.on('error', () => reject(new Error('Chrome not accessible')));
|
|
286
|
+
req.setTimeout(3000, () => {
|
|
287
|
+
req.destroy();
|
|
288
|
+
reject(new Error('timeout'));
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=cdp.js.map
|
package/dist/cli.d.ts
ADDED