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 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
- npm start # http://127.0.0.1:4577 (PORT env to change)
6
- npm test # smoke test
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 { server, PORT } from '../server.mjs';
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 [options]
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
- const portIndex = args.findIndex((arg) => arg === '-p' || arg === '--port');
20
- const port = (portIndex !== -1 && Number(args[portIndex + 1])) || PORT;
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
- console.log(`gemix-dash on http://127.0.0.1:${port}`);
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); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemix-dash",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Local web dashboard for gemix accounts (~/.gemix)",
5
5
  "type": "module",
6
6
  "main": "server.mjs",