token-larper 1.0.0 → 1.0.1
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/README.md +3 -3
- package/bin/cli.js +23 -0
- package/package.json +1 -1
- package/src/server.ts +55 -17
package/README.md
CHANGED
|
@@ -27,9 +27,9 @@ It consolidates all sessions into a real-time, interactive local dashboard runni
|
|
|
27
27
|
|
|
28
28
|
The screens below use sample data. No local sessions or project names are included.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
<video src="https://github.com/user-attachments/assets/104c8036-694e-474b-81e1-d33576f2cc30" controls playsinline poster="https://raw.githubusercontent.com/YarooqH/tokenlarper/main/docs/media/overview.jpg" width="820">
|
|
31
|
+
<a href="docs/media/walkthrough.mp4">Watch the 20-second walkthrough</a>
|
|
32
|
+
</video>
|
|
33
33
|
|
|
34
34
|
| Usage breakdown | Projects |
|
|
35
35
|
| --- | --- |
|
package/bin/cli.js
CHANGED
|
@@ -8,6 +8,29 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
const rootDir = join(__dirname, "..");
|
|
9
9
|
const serverScript = join(rootDir, "src", "server.ts");
|
|
10
10
|
|
|
11
|
+
// Check if Token Larper is already running on the requested port
|
|
12
|
+
const port = process.env.PORT || 4269;
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/tray-status`, {
|
|
15
|
+
signal: AbortSignal.timeout(600),
|
|
16
|
+
});
|
|
17
|
+
if (res.ok) {
|
|
18
|
+
console.log(`🔥 Token Larper is already running at http://localhost:${port}`);
|
|
19
|
+
console.log(`🚀 Opening dashboard in your default browser...`);
|
|
20
|
+
const cmd =
|
|
21
|
+
process.platform === "win32"
|
|
22
|
+
? `start http://localhost:${port}`
|
|
23
|
+
: process.platform === "darwin"
|
|
24
|
+
? `open http://localhost:${port}`
|
|
25
|
+
: `xdg-open http://localhost:${port}`;
|
|
26
|
+
const { exec } = await import("node:child_process");
|
|
27
|
+
exec(cmd);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
// Server is not running yet, proceed with startup
|
|
32
|
+
}
|
|
33
|
+
|
|
11
34
|
// If executed directly inside Bun runtime
|
|
12
35
|
if (typeof Bun !== "undefined") {
|
|
13
36
|
await import(serverScript);
|
package/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -48,23 +48,49 @@ function isAllowedMutation(req: Request, port: number): boolean {
|
|
|
48
48
|
return (req.headers.get("content-type") || "").toLowerCase().startsWith("application/json");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// Check if Token Larper is already running on the target port
|
|
52
|
+
try {
|
|
53
|
+
const existing = await fetch(`http://127.0.0.1:${PORT}/api/tray-status`, {
|
|
54
|
+
signal: AbortSignal.timeout(600),
|
|
55
|
+
});
|
|
56
|
+
if (existing.ok) {
|
|
57
|
+
console.log(`🔥 Token Larper is already running at http://localhost:${PORT}`);
|
|
58
|
+
console.log(`🚀 Opening dashboard in your default browser...`);
|
|
59
|
+
const cmd =
|
|
60
|
+
process.platform === "win32"
|
|
61
|
+
? `start http://localhost:${PORT}`
|
|
62
|
+
: process.platform === "darwin"
|
|
63
|
+
? `open http://localhost:${PORT}`
|
|
64
|
+
: `xdg-open http://localhost:${PORT}`;
|
|
65
|
+
const { exec } = await import("node:child_process");
|
|
66
|
+
exec(cmd);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// Not running, proceed with startup
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
// Warm cached usage before the first dashboard request.
|
|
52
74
|
void getDashboardData();
|
|
53
75
|
|
|
54
|
-
|
|
55
|
-
port
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
function startServer(preferredPort: number) {
|
|
77
|
+
let port = preferredPort;
|
|
78
|
+
while (port < preferredPort + 10) {
|
|
79
|
+
try {
|
|
80
|
+
return Bun.serve({
|
|
81
|
+
port,
|
|
82
|
+
hostname: "127.0.0.1",
|
|
83
|
+
idleTimeout: 120,
|
|
84
|
+
async fetch(req) {
|
|
85
|
+
const url = new URL(req.url);
|
|
86
|
+
const activePort = server?.port ?? port;
|
|
87
|
+
|
|
88
|
+
if (!isAllowedHost(req.headers.get("host"), activePort)) {
|
|
89
|
+
return new Response("Forbidden", { status: 403 });
|
|
90
|
+
}
|
|
91
|
+
if (req.method !== "GET" && req.method !== "HEAD" && !isAllowedMutation(req, activePort)) {
|
|
92
|
+
return new Response("Forbidden", { status: 403 });
|
|
93
|
+
}
|
|
68
94
|
|
|
69
95
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
70
96
|
return new Response(Bun.file(join(CLIENT_DIR, "index.html")), {
|
|
@@ -155,10 +181,22 @@ const server = Bun.serve({
|
|
|
155
181
|
return Response.json({ ok: true, message: "Shutting down Token Larper..." });
|
|
156
182
|
}
|
|
157
183
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
});
|
|
184
|
+
return new Response("Not Found", { status: 404 });
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
} catch (err: any) {
|
|
188
|
+
if (err?.code === "EADDRINUSE" || String(err).includes("EADDRINUSE")) {
|
|
189
|
+
console.warn(`Port ${port} in use, trying port ${port + 1}...`);
|
|
190
|
+
port++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
throw new Error(`Could not find an open port starting from ${preferredPort}`);
|
|
197
|
+
}
|
|
161
198
|
|
|
199
|
+
const server = startServer(PORT);
|
|
162
200
|
startSystemTray(server.port ?? PORT);
|
|
163
201
|
|
|
164
202
|
process.on("SIGINT", () => {
|