hyper-agent-browser 0.1.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 +196 -0
- package/package.json +63 -0
- package/src/browser/context.ts +66 -0
- package/src/browser/manager.ts +414 -0
- package/src/browser/sync-chrome-data.ts +53 -0
- package/src/cli.ts +628 -0
- package/src/cli.ts.backup +529 -0
- package/src/commands/actions.ts +232 -0
- package/src/commands/advanced.ts +252 -0
- package/src/commands/config.ts +44 -0
- package/src/commands/getters.ts +110 -0
- package/src/commands/info.ts +195 -0
- package/src/commands/navigation.ts +50 -0
- package/src/commands/session.ts +83 -0
- package/src/daemon/browser-pool.ts +65 -0
- package/src/daemon/client.ts +128 -0
- package/src/daemon/main.ts +200 -0
- package/src/daemon/server.ts +562 -0
- package/src/session/manager.ts +110 -0
- package/src/session/store.ts +172 -0
- package/src/snapshot/accessibility.ts +182 -0
- package/src/snapshot/dom-extractor.ts +220 -0
- package/src/snapshot/formatter.ts +115 -0
- package/src/snapshot/reference-store.ts +97 -0
- package/src/utils/config.ts +183 -0
- package/src/utils/errors.ts +121 -0
- package/src/utils/logger.ts +12 -0
- package/src/utils/selector.ts +23 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { DaemonServer } from "./server";
|
|
7
|
+
|
|
8
|
+
const DAEMON_PORT = 9527;
|
|
9
|
+
const DAEMON_HOST = "127.0.0.1";
|
|
10
|
+
const PID_FILE = join(homedir(), ".hab", "daemon.pid");
|
|
11
|
+
const CONFIG_FILE = join(homedir(), ".hab", "daemon.json");
|
|
12
|
+
|
|
13
|
+
async function saveDaemonInfo(pid: number, port: number, host: string): Promise<void> {
|
|
14
|
+
const config = { pid, port, host, startedAt: Date.now() };
|
|
15
|
+
await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
16
|
+
await writeFile(PID_FILE, String(pid));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function loadDaemonInfo(): Promise<{ pid: number; port: number; host: string } | null> {
|
|
20
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const content = await readFile(CONFIG_FILE, "utf-8");
|
|
26
|
+
return JSON.parse(content);
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function removeDaemonInfo(): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
if (existsSync(PID_FILE)) await unlink(PID_FILE);
|
|
35
|
+
if (existsSync(CONFIG_FILE)) await unlink(CONFIG_FILE);
|
|
36
|
+
} catch {
|
|
37
|
+
// Ignore errors
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function isDaemonRunning(pid: number): Promise<boolean> {
|
|
42
|
+
try {
|
|
43
|
+
process.kill(pid, 0);
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function checkHealth(host: string, port: number): Promise<boolean> {
|
|
51
|
+
try {
|
|
52
|
+
const response = await fetch(`http://${host}:${port}/health`, {
|
|
53
|
+
signal: AbortSignal.timeout(1000),
|
|
54
|
+
});
|
|
55
|
+
return response.ok;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main() {
|
|
62
|
+
const args = process.argv.slice(2);
|
|
63
|
+
const command = args[0];
|
|
64
|
+
|
|
65
|
+
if (command === "start") {
|
|
66
|
+
// Check if daemon is already running
|
|
67
|
+
const info = await loadDaemonInfo();
|
|
68
|
+
if (info) {
|
|
69
|
+
const running = await isDaemonRunning(info.pid);
|
|
70
|
+
const healthy = await checkHealth(info.host, info.port);
|
|
71
|
+
|
|
72
|
+
if (running && healthy) {
|
|
73
|
+
console.log(`Daemon already running on ${info.host}:${info.port} (PID: ${info.pid})`);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Daemon process exists but not healthy, clean up
|
|
78
|
+
await removeDaemonInfo();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Start daemon
|
|
82
|
+
const server = new DaemonServer({ port: DAEMON_PORT, host: DAEMON_HOST });
|
|
83
|
+
await server.start();
|
|
84
|
+
|
|
85
|
+
// Save daemon info
|
|
86
|
+
await saveDaemonInfo(process.pid, DAEMON_PORT, DAEMON_HOST);
|
|
87
|
+
|
|
88
|
+
console.log(`Daemon started on ${DAEMON_HOST}:${DAEMON_PORT} (PID: ${process.pid})`);
|
|
89
|
+
|
|
90
|
+
// Handle graceful shutdown
|
|
91
|
+
const shutdown = async () => {
|
|
92
|
+
console.log("\nShutting down daemon...");
|
|
93
|
+
await server.stop();
|
|
94
|
+
await removeDaemonInfo();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
process.on("SIGINT", shutdown);
|
|
99
|
+
process.on("SIGTERM", shutdown);
|
|
100
|
+
|
|
101
|
+
// Keep process alive
|
|
102
|
+
await new Promise(() => {});
|
|
103
|
+
} else if (command === "stop") {
|
|
104
|
+
const info = await loadDaemonInfo();
|
|
105
|
+
if (!info) {
|
|
106
|
+
console.log("Daemon is not running");
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const running = await isDaemonRunning(info.pid);
|
|
111
|
+
if (!running) {
|
|
112
|
+
console.log("Daemon is not running (stale PID file)");
|
|
113
|
+
await removeDaemonInfo();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Send SIGTERM to daemon
|
|
118
|
+
try {
|
|
119
|
+
process.kill(info.pid, "SIGTERM");
|
|
120
|
+
console.log(`Stopped daemon (PID: ${info.pid})`);
|
|
121
|
+
|
|
122
|
+
// Wait a bit for graceful shutdown
|
|
123
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
124
|
+
|
|
125
|
+
// Force cleanup
|
|
126
|
+
await removeDaemonInfo();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error("Failed to stop daemon:", error);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
} else if (command === "status") {
|
|
132
|
+
const info = await loadDaemonInfo();
|
|
133
|
+
if (!info) {
|
|
134
|
+
console.log("Daemon is not running");
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const running = await isDaemonRunning(info.pid);
|
|
139
|
+
const healthy = await checkHealth(info.host, info.port);
|
|
140
|
+
|
|
141
|
+
if (running && healthy) {
|
|
142
|
+
console.log(`Daemon is running on ${info.host}:${info.port} (PID: ${info.pid})`);
|
|
143
|
+
|
|
144
|
+
// Get active sessions
|
|
145
|
+
try {
|
|
146
|
+
const response = await fetch(`http://${info.host}:${info.port}/health`);
|
|
147
|
+
const data = await response.json();
|
|
148
|
+
console.log(
|
|
149
|
+
`Active sessions: ${data.sessions.length > 0 ? data.sessions.join(", ") : "none"}`,
|
|
150
|
+
);
|
|
151
|
+
} catch {
|
|
152
|
+
console.log("Could not fetch session info");
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
console.log("Daemon is not running (stale PID file)");
|
|
156
|
+
await removeDaemonInfo();
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
} else if (command === "restart") {
|
|
160
|
+
// Stop daemon
|
|
161
|
+
const info = await loadDaemonInfo();
|
|
162
|
+
if (info) {
|
|
163
|
+
try {
|
|
164
|
+
process.kill(info.pid, "SIGTERM");
|
|
165
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
166
|
+
} catch {
|
|
167
|
+
// Ignore
|
|
168
|
+
}
|
|
169
|
+
await removeDaemonInfo();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Start daemon
|
|
173
|
+
const server = new DaemonServer({ port: DAEMON_PORT, host: DAEMON_HOST });
|
|
174
|
+
await server.start();
|
|
175
|
+
await saveDaemonInfo(process.pid, DAEMON_PORT, DAEMON_HOST);
|
|
176
|
+
|
|
177
|
+
console.log(`Daemon restarted on ${DAEMON_HOST}:${DAEMON_PORT} (PID: ${process.pid})`);
|
|
178
|
+
|
|
179
|
+
// Handle graceful shutdown
|
|
180
|
+
const shutdown = async () => {
|
|
181
|
+
console.log("\nShutting down daemon...");
|
|
182
|
+
await server.stop();
|
|
183
|
+
await removeDaemonInfo();
|
|
184
|
+
process.exit(0);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
process.on("SIGINT", shutdown);
|
|
188
|
+
process.on("SIGTERM", shutdown);
|
|
189
|
+
|
|
190
|
+
await new Promise(() => {});
|
|
191
|
+
} else {
|
|
192
|
+
console.log("Usage: bun src/daemon/main.ts [start|stop|status|restart]");
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
main().catch((error) => {
|
|
198
|
+
console.error("Daemon error:", error);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
});
|