@markus-global/cli 0.5.10 → 0.6.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/dist/tray.d.ts +8 -0
- package/dist/tray.d.ts.map +1 -0
- package/dist/tray.js +154 -0
- package/dist/tray.js.map +1 -0
- package/dist/tray.mjs +130 -0
- package/dist/web-ui/assets/{index-475KjsXj.js → index-CtHciovM.js} +1 -1
- package/dist/web-ui/index.html +1 -1
- package/package.json +4 -3
package/dist/tray.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tray.d.ts","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/dist/tray.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform system tray controller for Markus.
|
|
3
|
+
* Provides start/stop controls and quick access to the Web UI.
|
|
4
|
+
*
|
|
5
|
+
* Launched by the desktop shortcut / .app instead of `markus start` directly.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn, exec } from 'node:child_process';
|
|
8
|
+
import { readFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
9
|
+
import { resolve, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { platform, homedir } from 'node:os';
|
|
12
|
+
import SysTrayModule from 'systray2';
|
|
13
|
+
// systray2 is CJS; default export is the class
|
|
14
|
+
const SysTray = SysTrayModule.default || SysTrayModule;
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const WEB_UI_PORT = 8056;
|
|
17
|
+
const WEB_UI_URL = `http://localhost:${WEB_UI_PORT}`;
|
|
18
|
+
// Resolve paths relative to the binary distribution layout:
|
|
19
|
+
// bin/tray.mjs (this file)
|
|
20
|
+
// bin/node (bundled node)
|
|
21
|
+
// bin/markus.mjs
|
|
22
|
+
const BIN_DIR = __dirname;
|
|
23
|
+
const APP_DIR = resolve(BIN_DIR, '..');
|
|
24
|
+
const NODE_BIN = resolve(BIN_DIR, platform() === 'win32' ? 'node.exe' : 'node');
|
|
25
|
+
const MARKUS_MJS = resolve(BIN_DIR, 'markus.mjs');
|
|
26
|
+
const LOG_DIR = resolve(homedir(), '.markus', 'logs');
|
|
27
|
+
function loadIconBase64() {
|
|
28
|
+
const candidates = [
|
|
29
|
+
resolve(APP_DIR, 'logo.png'),
|
|
30
|
+
resolve(APP_DIR, 'markus.ico'),
|
|
31
|
+
];
|
|
32
|
+
for (const p of candidates) {
|
|
33
|
+
if (existsSync(p)) {
|
|
34
|
+
return readFileSync(p).toString('base64');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Fallback: empty icon (systray2 will show a default)
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
function openBrowser(url) {
|
|
41
|
+
const sys = platform();
|
|
42
|
+
const cmd = sys === 'darwin' ? `open "${url}"`
|
|
43
|
+
: sys === 'win32' ? `start "" "${url}"`
|
|
44
|
+
: `xdg-open "${url}"`;
|
|
45
|
+
exec(cmd, () => { });
|
|
46
|
+
}
|
|
47
|
+
// ── Server process management ────────────────────────────────────────────────
|
|
48
|
+
let serverProcess = null;
|
|
49
|
+
let serverRunning = false;
|
|
50
|
+
function startServer() {
|
|
51
|
+
if (serverRunning)
|
|
52
|
+
return;
|
|
53
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
54
|
+
serverProcess = spawn(NODE_BIN, [MARKUS_MJS, 'start'], {
|
|
55
|
+
stdio: 'ignore',
|
|
56
|
+
detached: false,
|
|
57
|
+
env: { ...process.env, NO_BROWSER: '1' },
|
|
58
|
+
});
|
|
59
|
+
serverRunning = true;
|
|
60
|
+
updateTrayMenu();
|
|
61
|
+
serverProcess.on('exit', (code) => {
|
|
62
|
+
serverRunning = false;
|
|
63
|
+
serverProcess = null;
|
|
64
|
+
updateTrayMenu();
|
|
65
|
+
if (code && code !== 0) {
|
|
66
|
+
console.error(`Markus server exited with code ${code}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
serverProcess.on('error', (err) => {
|
|
70
|
+
serverRunning = false;
|
|
71
|
+
serverProcess = null;
|
|
72
|
+
updateTrayMenu();
|
|
73
|
+
console.error('Failed to start Markus server:', err.message);
|
|
74
|
+
});
|
|
75
|
+
// Auto-open browser after a short delay
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
if (serverRunning)
|
|
78
|
+
openBrowser(WEB_UI_URL);
|
|
79
|
+
}, 3000);
|
|
80
|
+
}
|
|
81
|
+
function stopServer() {
|
|
82
|
+
if (!serverProcess)
|
|
83
|
+
return;
|
|
84
|
+
serverProcess.kill('SIGTERM');
|
|
85
|
+
// Force kill after 5 seconds if still alive
|
|
86
|
+
const forceTimer = setTimeout(() => {
|
|
87
|
+
if (serverProcess && !serverProcess.killed) {
|
|
88
|
+
serverProcess.kill('SIGKILL');
|
|
89
|
+
}
|
|
90
|
+
}, 5000);
|
|
91
|
+
serverProcess.on('exit', () => clearTimeout(forceTimer));
|
|
92
|
+
}
|
|
93
|
+
// ── Tray setup ───────────────────────────────────────────────────────────────
|
|
94
|
+
const SEPARATOR = { title: '<SEPARATOR>', tooltip: '', enabled: true };
|
|
95
|
+
const ITEM_OPEN_UI = { title: 'Open Web UI', tooltip: 'Open Markus in browser', enabled: true };
|
|
96
|
+
const ITEM_START = { title: 'Start Server', tooltip: 'Start Markus server', enabled: true };
|
|
97
|
+
const ITEM_STOP = { title: 'Stop Server', tooltip: 'Stop Markus server', enabled: false };
|
|
98
|
+
const ITEM_QUIT = { title: 'Quit Markus', tooltip: 'Stop server and quit', enabled: true };
|
|
99
|
+
let tray = null;
|
|
100
|
+
function buildMenu() {
|
|
101
|
+
return {
|
|
102
|
+
icon: loadIconBase64(),
|
|
103
|
+
title: '',
|
|
104
|
+
tooltip: serverRunning ? 'Markus (running)' : 'Markus (stopped)',
|
|
105
|
+
items: [
|
|
106
|
+
{ ...ITEM_OPEN_UI, enabled: serverRunning },
|
|
107
|
+
SEPARATOR,
|
|
108
|
+
{ ...ITEM_START, enabled: !serverRunning },
|
|
109
|
+
{ ...ITEM_STOP, enabled: serverRunning },
|
|
110
|
+
SEPARATOR,
|
|
111
|
+
ITEM_QUIT,
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function updateTrayMenu() {
|
|
116
|
+
if (!tray)
|
|
117
|
+
return;
|
|
118
|
+
tray.sendAction({ type: 'update-menu', menu: buildMenu() });
|
|
119
|
+
}
|
|
120
|
+
async function main() {
|
|
121
|
+
tray = new SysTray({ menu: buildMenu(), copyDir: false });
|
|
122
|
+
await tray.ready();
|
|
123
|
+
tray.onClick(async (action) => {
|
|
124
|
+
const title = action.item.title;
|
|
125
|
+
if (title === ITEM_OPEN_UI.title) {
|
|
126
|
+
openBrowser(WEB_UI_URL);
|
|
127
|
+
}
|
|
128
|
+
else if (title === ITEM_START.title) {
|
|
129
|
+
startServer();
|
|
130
|
+
}
|
|
131
|
+
else if (title === ITEM_STOP.title) {
|
|
132
|
+
stopServer();
|
|
133
|
+
}
|
|
134
|
+
else if (title === ITEM_QUIT.title) {
|
|
135
|
+
stopServer();
|
|
136
|
+
// Wait briefly for server to stop, then exit
|
|
137
|
+
setTimeout(async () => {
|
|
138
|
+
if (tray)
|
|
139
|
+
await tray.kill(false);
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}, 1000);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
tray.onError((err) => {
|
|
145
|
+
console.error('Tray error:', err.message);
|
|
146
|
+
});
|
|
147
|
+
// Auto-start the server on launch
|
|
148
|
+
startServer();
|
|
149
|
+
}
|
|
150
|
+
main().catch((err) => {
|
|
151
|
+
console.error('Tray failed to start:', err);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=tray.js.map
|
package/dist/tray.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"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"}
|
package/dist/tray.mjs
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/tray.ts
|
|
5
|
+
import { spawn, exec } from "node:child_process";
|
|
6
|
+
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
7
|
+
import { resolve, dirname } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { platform, homedir } from "node:os";
|
|
10
|
+
import SysTrayModule from "systray2";
|
|
11
|
+
var SysTray = SysTrayModule.default || SysTrayModule;
|
|
12
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
var WEB_UI_PORT = 8056;
|
|
14
|
+
var WEB_UI_URL = `http://localhost:${WEB_UI_PORT}`;
|
|
15
|
+
var BIN_DIR = __dirname;
|
|
16
|
+
var APP_DIR = resolve(BIN_DIR, "..");
|
|
17
|
+
var NODE_BIN = resolve(BIN_DIR, platform() === "win32" ? "node.exe" : "node");
|
|
18
|
+
var MARKUS_MJS = resolve(BIN_DIR, "markus.mjs");
|
|
19
|
+
var LOG_DIR = resolve(homedir(), ".markus", "logs");
|
|
20
|
+
function loadIconBase64() {
|
|
21
|
+
const candidates = [
|
|
22
|
+
resolve(APP_DIR, "logo.png"),
|
|
23
|
+
resolve(APP_DIR, "markus.ico")
|
|
24
|
+
];
|
|
25
|
+
for (const p of candidates) {
|
|
26
|
+
if (existsSync(p)) {
|
|
27
|
+
return readFileSync(p).toString("base64");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
function openBrowser(url) {
|
|
33
|
+
const sys = platform();
|
|
34
|
+
const cmd = sys === "darwin" ? `open "${url}"` : sys === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
35
|
+
exec(cmd, () => {
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
var serverProcess = null;
|
|
39
|
+
var serverRunning = false;
|
|
40
|
+
function startServer() {
|
|
41
|
+
if (serverRunning) return;
|
|
42
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
43
|
+
serverProcess = spawn(NODE_BIN, [MARKUS_MJS, "start"], {
|
|
44
|
+
stdio: "ignore",
|
|
45
|
+
detached: false,
|
|
46
|
+
env: { ...process.env, NO_BROWSER: "1" }
|
|
47
|
+
});
|
|
48
|
+
serverRunning = true;
|
|
49
|
+
updateTrayMenu();
|
|
50
|
+
serverProcess.on("exit", (code) => {
|
|
51
|
+
serverRunning = false;
|
|
52
|
+
serverProcess = null;
|
|
53
|
+
updateTrayMenu();
|
|
54
|
+
if (code && code !== 0) {
|
|
55
|
+
console.error(`Markus server exited with code ${code}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
serverProcess.on("error", (err) => {
|
|
59
|
+
serverRunning = false;
|
|
60
|
+
serverProcess = null;
|
|
61
|
+
updateTrayMenu();
|
|
62
|
+
console.error("Failed to start Markus server:", err.message);
|
|
63
|
+
});
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
if (serverRunning) openBrowser(WEB_UI_URL);
|
|
66
|
+
}, 3e3);
|
|
67
|
+
}
|
|
68
|
+
function stopServer() {
|
|
69
|
+
if (!serverProcess) return;
|
|
70
|
+
serverProcess.kill("SIGTERM");
|
|
71
|
+
const forceTimer = setTimeout(() => {
|
|
72
|
+
if (serverProcess && !serverProcess.killed) {
|
|
73
|
+
serverProcess.kill("SIGKILL");
|
|
74
|
+
}
|
|
75
|
+
}, 5e3);
|
|
76
|
+
serverProcess.on("exit", () => clearTimeout(forceTimer));
|
|
77
|
+
}
|
|
78
|
+
var SEPARATOR = { title: "<SEPARATOR>", tooltip: "", enabled: true };
|
|
79
|
+
var ITEM_OPEN_UI = { title: "Open Web UI", tooltip: "Open Markus in browser", enabled: true };
|
|
80
|
+
var ITEM_START = { title: "Start Server", tooltip: "Start Markus server", enabled: true };
|
|
81
|
+
var ITEM_STOP = { title: "Stop Server", tooltip: "Stop Markus server", enabled: false };
|
|
82
|
+
var ITEM_QUIT = { title: "Quit Markus", tooltip: "Stop server and quit", enabled: true };
|
|
83
|
+
var tray = null;
|
|
84
|
+
function buildMenu() {
|
|
85
|
+
return {
|
|
86
|
+
icon: loadIconBase64(),
|
|
87
|
+
title: "",
|
|
88
|
+
tooltip: serverRunning ? "Markus (running)" : "Markus (stopped)",
|
|
89
|
+
items: [
|
|
90
|
+
{ ...ITEM_OPEN_UI, enabled: serverRunning },
|
|
91
|
+
SEPARATOR,
|
|
92
|
+
{ ...ITEM_START, enabled: !serverRunning },
|
|
93
|
+
{ ...ITEM_STOP, enabled: serverRunning },
|
|
94
|
+
SEPARATOR,
|
|
95
|
+
ITEM_QUIT
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function updateTrayMenu() {
|
|
100
|
+
if (!tray) return;
|
|
101
|
+
tray.sendAction({ type: "update-menu", menu: buildMenu() });
|
|
102
|
+
}
|
|
103
|
+
async function main() {
|
|
104
|
+
tray = new SysTray({ menu: buildMenu(), copyDir: false });
|
|
105
|
+
await tray.ready();
|
|
106
|
+
tray.onClick(async (action) => {
|
|
107
|
+
const title = action.item.title;
|
|
108
|
+
if (title === ITEM_OPEN_UI.title) {
|
|
109
|
+
openBrowser(WEB_UI_URL);
|
|
110
|
+
} else if (title === ITEM_START.title) {
|
|
111
|
+
startServer();
|
|
112
|
+
} else if (title === ITEM_STOP.title) {
|
|
113
|
+
stopServer();
|
|
114
|
+
} else if (title === ITEM_QUIT.title) {
|
|
115
|
+
stopServer();
|
|
116
|
+
setTimeout(async () => {
|
|
117
|
+
if (tray) await tray.kill(false);
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}, 1e3);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
tray.onError((err) => {
|
|
123
|
+
console.error("Tray error:", err.message);
|
|
124
|
+
});
|
|
125
|
+
startServer();
|
|
126
|
+
}
|
|
127
|
+
main().catch((err) => {
|
|
128
|
+
console.error("Tray failed to start:", err);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|