gemix-dash 0.0.1 → 0.0.2
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 +26 -2
- package/bin/gemix-dash.js +120 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
Local web dashboard for [gemix](https://www.npmjs.com/package/gemix) accounts (`~/.gemix`).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install -g gemix-dash
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI usage
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
gemix-dash # start the server and open the dashboard in your browser
|
|
15
|
+
gemix-dash stop # stop the running server
|
|
16
|
+
gemix-dash status # check whether the server is running
|
|
17
|
+
gemix-dash -p 8080 # start on a custom port
|
|
18
|
+
gemix-dash --help # show all options
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> The dashboard binds to `127.0.0.1` only. A PID file is written to the OS temp directory so `gemix-dash stop` can find and terminate the process.
|
|
22
|
+
|
|
23
|
+
## Development
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm start # http://127.0.0.1:4577 (PORT env to change)
|
|
27
|
+
npm test # smoke test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Features
|
|
7
31
|
|
|
8
32
|
- Cards per account with real per-model limits (same source as `agy` `/usage`: Cloud Code `fetchAvailableModels`), cached 60s.
|
|
9
33
|
- Add (Google link + paste code), Use (same as `gemix use`), Rename, Remove, all with confirmations.
|
package/bin/gemix-dash.js
CHANGED
|
@@ -1,14 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { readFileSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
const PID_FILE = path.join(tmpdir(), 'gemix-dash.pid');
|
|
11
|
+
|
|
12
|
+
function readPid() {
|
|
13
|
+
try { return JSON.parse(readFileSync(PID_FILE, 'utf8')); } catch { return null; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function writePid(pid, port) {
|
|
17
|
+
writeFileSync(PID_FILE, JSON.stringify({ pid, port }), 'utf8');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function removePid() {
|
|
21
|
+
try { rmSync(PID_FILE); } catch { /* already gone */ }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Open a URL in the default browser (cross-platform). */
|
|
25
|
+
function openBrowser(url) {
|
|
26
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
27
|
+
: process.platform === 'win32' ? 'start'
|
|
28
|
+
: 'xdg-open';
|
|
29
|
+
spawn(cmd, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Check whether a port is already listening. */
|
|
33
|
+
function portInUse(port) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
const s = createServer();
|
|
36
|
+
s.once('error', () => resolve(true));
|
|
37
|
+
s.once('listening', () => { s.close(); resolve(false); });
|
|
38
|
+
s.listen(port, '127.0.0.1');
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── arg parsing ───────────────────────────────────────────────────────────────
|
|
3
43
|
|
|
4
44
|
const args = process.argv.slice(2);
|
|
45
|
+
const cmd = args[0]; // 'stop' | 'status' | undefined
|
|
46
|
+
|
|
47
|
+
const portIndex = args.findIndex((a) => a === '-p' || a === '--port');
|
|
48
|
+
const cliPort = portIndex !== -1 ? Number(args[portIndex + 1]) : undefined;
|
|
5
49
|
|
|
6
50
|
if (args.includes('-h') || args.includes('--help')) {
|
|
7
51
|
console.log(`
|
|
8
|
-
Usage: gemix-dash [
|
|
52
|
+
Usage: gemix-dash [stop|status] [-p <port>]
|
|
9
53
|
|
|
10
54
|
Local web dashboard for gemix accounts (~/.gemix)
|
|
11
55
|
|
|
56
|
+
Commands:
|
|
57
|
+
(none) Start the server and open the dashboard in your browser
|
|
58
|
+
stop Stop a running gemix-dash server
|
|
59
|
+
status Show whether gemix-dash is running
|
|
60
|
+
|
|
12
61
|
Options:
|
|
13
62
|
-p, --port <number> Port to listen on (default: 4577 or PORT env)
|
|
14
63
|
-h, --help Show this help message
|
|
@@ -16,9 +65,75 @@ Options:
|
|
|
16
65
|
process.exit(0);
|
|
17
66
|
}
|
|
18
67
|
|
|
19
|
-
|
|
20
|
-
|
|
68
|
+
// ── stop ──────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
if (cmd === 'stop') {
|
|
71
|
+
const info = readPid();
|
|
72
|
+
if (!info) {
|
|
73
|
+
console.log('gemix-dash is not running.');
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
process.kill(info.pid, 'SIGTERM');
|
|
78
|
+
removePid();
|
|
79
|
+
console.log(`gemix-dash (pid ${info.pid}) stopped.`);
|
|
80
|
+
} catch {
|
|
81
|
+
console.log(`Process ${info.pid} not found – cleaning up stale PID file.`);
|
|
82
|
+
removePid();
|
|
83
|
+
}
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── status ────────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
if (cmd === 'status') {
|
|
90
|
+
const info = readPid();
|
|
91
|
+
if (!info) {
|
|
92
|
+
console.log('gemix-dash is not running.');
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
process.kill(info.pid, 0); // throws if process is gone
|
|
97
|
+
console.log(`gemix-dash is running pid=${info.pid} http://127.0.0.1:${info.port}`);
|
|
98
|
+
} catch {
|
|
99
|
+
console.log('gemix-dash is not running (stale PID file removed).');
|
|
100
|
+
removePid();
|
|
101
|
+
}
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── start ─────────────────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
// Dynamic import so we only load the server when we actually start.
|
|
108
|
+
const { server, PORT: DEFAULT_PORT } = await import('../server.mjs');
|
|
109
|
+
const port = cliPort || Number(process.env.PORT || DEFAULT_PORT);
|
|
110
|
+
|
|
111
|
+
// Check if already running on this port
|
|
112
|
+
if (await portInUse(port)) {
|
|
113
|
+
// If there's a PID file for this port, just open the browser
|
|
114
|
+
const info = readPid();
|
|
115
|
+
if (info && info.port === port) {
|
|
116
|
+
console.log(`gemix-dash is already running at http://127.0.0.1:${port}`);
|
|
117
|
+
openBrowser(`http://127.0.0.1:${port}`);
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
console.error(`Error: port ${port} is already in use by another process.`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
21
123
|
|
|
22
124
|
server.listen(port, '127.0.0.1', () => {
|
|
23
|
-
|
|
125
|
+
const url = `http://127.0.0.1:${port}`;
|
|
126
|
+
console.log(`gemix-dash running at ${url}`);
|
|
127
|
+
console.log(`Run 'gemix-dash stop' to shut it down.`);
|
|
128
|
+
writePid(process.pid, port);
|
|
129
|
+
openBrowser(url);
|
|
24
130
|
});
|
|
131
|
+
|
|
132
|
+
// Clean up PID file on exit
|
|
133
|
+
function cleanup() {
|
|
134
|
+
const info = readPid();
|
|
135
|
+
if (info && info.pid === process.pid) removePid();
|
|
136
|
+
}
|
|
137
|
+
process.on('exit', cleanup);
|
|
138
|
+
process.on('SIGINT', () => { cleanup(); process.exit(0); });
|
|
139
|
+
process.on('SIGTERM', () => { cleanup(); process.exit(0); });
|