mulmoclaude 0.1.0 → 0.1.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/bin/mulmoclaude.js +50 -5
- package/package.json +8 -4
package/bin/mulmoclaude.js
CHANGED
|
@@ -9,6 +9,7 @@ import { execSync, spawn } from "child_process";
|
|
|
9
9
|
import { existsSync } from "fs";
|
|
10
10
|
import { get as httpGet } from "http";
|
|
11
11
|
import { createRequire } from "module";
|
|
12
|
+
import net from "net";
|
|
12
13
|
import { join, dirname } from "path";
|
|
13
14
|
import { fileURLToPath } from "url";
|
|
14
15
|
|
|
@@ -44,6 +45,27 @@ function pickOpenCommand() {
|
|
|
44
45
|
return "xdg-open";
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
function isPortFree(portNum) {
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const server = net.createServer();
|
|
51
|
+
server.once("error", () => resolve(false));
|
|
52
|
+
server.once("listening", () => {
|
|
53
|
+
server.close(() => resolve(true));
|
|
54
|
+
});
|
|
55
|
+
server.listen(portNum, "127.0.0.1");
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Walk forward from `start` to find a free port. `MAX_PORT_PROBES` caps
|
|
60
|
+
// the scan so an accidentally-saturated system doesn't spin forever.
|
|
61
|
+
const MAX_PORT_PROBES = 20;
|
|
62
|
+
async function findFreePort(start) {
|
|
63
|
+
for (let candidate = start; candidate < start + MAX_PORT_PROBES; candidate++) {
|
|
64
|
+
if (await isPortFree(candidate)) return candidate;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
// Poll the server until it answers an HTTP request, then call `onReady`.
|
|
48
70
|
// Gives up after ~15s so the launcher doesn't hang forever on a crash loop.
|
|
49
71
|
function waitUntilReady(portNum, onReady) {
|
|
@@ -99,16 +121,16 @@ Options:
|
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
if (args.includes("--version")) {
|
|
102
|
-
console.log("mulmoclaude 0.1.
|
|
124
|
+
console.log("mulmoclaude 0.1.2");
|
|
103
125
|
process.exit(0);
|
|
104
126
|
}
|
|
105
127
|
|
|
106
|
-
const
|
|
128
|
+
const { requestedPort, portExplicit } = parsePortArg();
|
|
107
129
|
const noOpen = args.includes("--no-open");
|
|
108
130
|
|
|
109
|
-
function
|
|
131
|
+
function parsePortArg() {
|
|
110
132
|
const idx = args.indexOf("--port");
|
|
111
|
-
if (idx === -1) return DEFAULT_PORT;
|
|
133
|
+
if (idx === -1) return { requestedPort: DEFAULT_PORT, portExplicit: false };
|
|
112
134
|
const raw = args[idx + 1];
|
|
113
135
|
if (raw === undefined) {
|
|
114
136
|
error("--port requires a value (integer 1..65535)");
|
|
@@ -119,7 +141,7 @@ function resolvePort() {
|
|
|
119
141
|
error(`Invalid --port value: "${raw}" (expected integer 1..65535)`);
|
|
120
142
|
process.exit(1);
|
|
121
143
|
}
|
|
122
|
-
return parsed;
|
|
144
|
+
return { requestedPort: parsed, portExplicit: true };
|
|
123
145
|
}
|
|
124
146
|
|
|
125
147
|
// ── Pre-flight checks ───────────────────────────────────────
|
|
@@ -142,6 +164,29 @@ if (!existsSync(SERVER_ENTRY)) {
|
|
|
142
164
|
process.exit(1);
|
|
143
165
|
}
|
|
144
166
|
|
|
167
|
+
// ── Resolve a usable port ───────────────────────────────────
|
|
168
|
+
|
|
169
|
+
// Check the requested port before spawning the server — an explicit
|
|
170
|
+
// --port that's taken is a hard error (respect the user's choice),
|
|
171
|
+
// while the default can walk forward to the next free slot so casual
|
|
172
|
+
// double-launches don't crash.
|
|
173
|
+
const port = await chooseAvailablePort(requestedPort, portExplicit);
|
|
174
|
+
|
|
175
|
+
async function chooseAvailablePort(requested, explicit) {
|
|
176
|
+
if (await isPortFree(requested)) return requested;
|
|
177
|
+
if (explicit) {
|
|
178
|
+
error(`Port ${requested} is already in use. Stop the other process or pick a different --port.`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
const fallback = await findFreePort(requested + 1);
|
|
182
|
+
if (fallback === null) {
|
|
183
|
+
error(`Port ${requested} is in use and no free port found in ${requested}..${requested + MAX_PORT_PROBES - 1}.`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
log(`Port ${requested} busy → using ${fallback} instead. (Pass --port <N> to pin.)`);
|
|
187
|
+
return fallback;
|
|
188
|
+
}
|
|
189
|
+
|
|
145
190
|
// ── Start server ────────────────────────────────────────────
|
|
146
191
|
|
|
147
192
|
log(`Starting MulmoClaude on port ${port}...`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmoclaude",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MulmoClaude — GUI-chat with Claude Code + long-term memory. One command to start.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,11 +18,12 @@
|
|
|
18
18
|
"src/"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@mulmobridge/chat-service": "^0.1.
|
|
22
|
-
"@mulmobridge/client": "^0.1.
|
|
23
|
-
"@mulmobridge/protocol": "^0.1.
|
|
21
|
+
"@mulmobridge/chat-service": "^0.1.1",
|
|
22
|
+
"@mulmobridge/client": "^0.1.1",
|
|
23
|
+
"@mulmobridge/protocol": "^0.1.3",
|
|
24
24
|
"@receptron/task-scheduler": "^0.1.0",
|
|
25
25
|
"@google/genai": "^1.50.1",
|
|
26
|
+
"@mulmocast/types": "^2.6.7",
|
|
26
27
|
"@gui-chat-plugin/mindmap": "^0.4.0",
|
|
27
28
|
"@gui-chat-plugin/present3d": "^0.1.0",
|
|
28
29
|
"@gui-chat-plugin/browse": "^0.2.0",
|
|
@@ -39,9 +40,12 @@
|
|
|
39
40
|
"ignore": "^7.0.5",
|
|
40
41
|
"mammoth": "^1.12.0",
|
|
41
42
|
"marked": "^18.0.2",
|
|
43
|
+
"mulmocast": "^2.6.7",
|
|
44
|
+
"puppeteer": "^24.41.0",
|
|
42
45
|
"socket.io": "^4.8.3",
|
|
43
46
|
"socket.io-client": "^4.8.3",
|
|
44
47
|
"uuid": "^13.0.0",
|
|
48
|
+
"ws": "^8.20.0",
|
|
45
49
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
46
50
|
"zod": "^4.3.6",
|
|
47
51
|
"tsx": "^4.19.0"
|