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.
@@ -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
+ });