mslxdff 0.1.3 → 0.1.5
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 +8 -3
- package/bin/mslxdff.js +81 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -46,16 +46,21 @@ Priority: `-port` arg > persisted port > `PORT` env > default `8989`.
|
|
|
46
46
|
|
|
47
47
|
### Daemon (background, stays resident)
|
|
48
48
|
|
|
49
|
+
Just run `mslxdff` (no args): if no daemon is running yet, it **starts a background
|
|
50
|
+
daemon and exits** — the command never holds your terminal, so `npx mslxdff` works
|
|
51
|
+
the same way. If a daemon is already up, the bare command shows status + help.
|
|
52
|
+
|
|
49
53
|
```
|
|
50
|
-
mslxdff
|
|
51
|
-
mslxdff -
|
|
54
|
+
mslxdff # starts the background daemon (if none running); exits immediately
|
|
55
|
+
mslxdff -d # same, explicit
|
|
56
|
+
mslxdff -stop # stop it
|
|
52
57
|
```
|
|
53
58
|
|
|
54
59
|
Logs go to `~/.config/mslxdff/daemon.log`, the daemon pid to `daemon.pid` (both overridable via `MSLXDFF_DAEMON_DIR`). The daemon keeps running after your shell exits.
|
|
55
60
|
|
|
56
61
|
### Status
|
|
57
62
|
|
|
58
|
-
Running `mslxdff` with no args shows a status panel when a daemon is already up
|
|
63
|
+
Running `mslxdff` with no args shows a status panel when a daemon is already up: joined groups with their members, failover targets, the free model list, the last 5 calls (model/status/latency), and the most recent error (`mslxdff -status` for status only, `mslxdff -help` for the full command reference). Call and error history are stored as JSON-lines at `calls.log` / `errors.log` in the state dir.
|
|
59
64
|
|
|
60
65
|
```
|
|
61
66
|
$ mslxdff -status
|
package/bin/mslxdff.js
CHANGED
|
@@ -55,6 +55,60 @@ if (args.includes("-status") || args.includes("--status") || args.includes("-s")
|
|
|
55
55
|
process.exit(0);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// -model list | -models : show the free models this proxy serves (cache-first)
|
|
59
|
+
// -model refresh : force a fresh fetch from the upstream and update the cache
|
|
60
|
+
if (args.includes("-model") || args.includes("-models")) {
|
|
61
|
+
const idx = args.findIndex((x) => x === "-model" || x === "-models");
|
|
62
|
+
const sub = args[idx + 1];
|
|
63
|
+
if (sub === "refresh") {
|
|
64
|
+
const models = createModelsService({
|
|
65
|
+
baseUrl: process.env.UPSTREAM_BASE_URL || "https://opencode.ai",
|
|
66
|
+
headers: createUpstreamClient({}).headers,
|
|
67
|
+
refreshMs: 0,
|
|
68
|
+
cacheFile: join(logDir(), "models.json"),
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
const list = await models.get();
|
|
72
|
+
const ids = (list.data || []).map((m) => m.id).filter(Boolean);
|
|
73
|
+
console.log(`refreshed: ${ids.length} free model(s)`);
|
|
74
|
+
for (const id of ids) console.log(` ${id}`);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`could not refresh models: ${String(err?.message || err)}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
if (sub !== undefined && sub !== "list") {
|
|
82
|
+
console.error("usage: mslxdff -model list | mslxdff -model refresh");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const cacheFile = join(logDir(), "models.json");
|
|
86
|
+
try {
|
|
87
|
+
const cached = readModelsCache(cacheFile);
|
|
88
|
+
if (cached) {
|
|
89
|
+
const ids = (cached.data || []).map((m) => m.id).filter(Boolean);
|
|
90
|
+
const at = cached.cachedAt ? ` (cached ${new Date(cached.cachedAt).toISOString().slice(0, 16).replace("T", " ")})` : "";
|
|
91
|
+
console.log(`${ids.length} free model(s)${at}:`);
|
|
92
|
+
for (const id of ids) console.log(` ${id}`);
|
|
93
|
+
} else {
|
|
94
|
+
const models = createModelsService({
|
|
95
|
+
baseUrl: process.env.UPSTREAM_BASE_URL || "https://opencode.ai",
|
|
96
|
+
headers: createUpstreamClient({}).headers,
|
|
97
|
+
refreshMs: 0,
|
|
98
|
+
cacheFile,
|
|
99
|
+
});
|
|
100
|
+
const list = await models.get();
|
|
101
|
+
const ids = (list.data || []).map((m) => m.id).filter(Boolean);
|
|
102
|
+
console.log(`${ids.length} free model(s):`);
|
|
103
|
+
for (const id of ids) console.log(` ${id}`);
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error(`could not fetch models: ${String(err?.message || err)}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
58
112
|
// -creategroup <name> | -group create <name> | -group sync | -group leave <name> | -group list |
|
|
59
113
|
// -addtogroup <leader-host> <name> | -resetban [ip]
|
|
60
114
|
const createGroupArg = argValue("-creategroup", "--creategroup") || groupIs("create", args);
|
|
@@ -178,6 +232,14 @@ function markJoined(entry) {
|
|
|
178
232
|
|
|
179
233
|
const errMsg = (err) => String(err?.message || err);
|
|
180
234
|
|
|
235
|
+
function readModelsCache(cacheFile) {
|
|
236
|
+
try {
|
|
237
|
+
return JSON.parse(readFileSync(cacheFile, "utf8"));
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
181
243
|
// Sync every joined group into the local peer list. Leaders read their local
|
|
182
244
|
// groups state; members re-register with the leader (idempotent) to get the
|
|
183
245
|
// freshest member list. Returns per-group results.
|
|
@@ -250,10 +312,22 @@ if (args.includes("-d") || args.includes("--daemon")) {
|
|
|
250
312
|
// we ARE the daemon; stdout/stderr already point at the log file via startDaemon stdio
|
|
251
313
|
}
|
|
252
314
|
|
|
253
|
-
// Bare run:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
315
|
+
// Bare run: show status + help when the daemon is already up; otherwise spawn it
|
|
316
|
+
// as a background daemon and exit — never holds the terminal (npx-friendly).
|
|
317
|
+
if (!process.env.MSLXDFF_DAEMON) {
|
|
318
|
+
if (readPid()) {
|
|
319
|
+
await printStatus();
|
|
320
|
+
printHelp();
|
|
321
|
+
process.exit(0);
|
|
322
|
+
}
|
|
323
|
+
const port = effectivePort();
|
|
324
|
+
const spawnedPid = startDaemon([]);
|
|
325
|
+
await waitForHealth(port, 4000);
|
|
326
|
+
console.log(`mslxdff v${VERSION} started as a background daemon (pid ${spawnedPid})`);
|
|
327
|
+
console.log(`endpoint: http://localhost:${port}/v1`);
|
|
328
|
+
console.log(`log: ${logFile()}`);
|
|
329
|
+
console.log(`pid: ${pidFile()}`);
|
|
330
|
+
console.log(`status: run \`mslxdff\` again (or \`mslxdff -status\`)`);
|
|
257
331
|
process.exit(0);
|
|
258
332
|
}
|
|
259
333
|
|
|
@@ -383,9 +457,11 @@ function printHelp() {
|
|
|
383
457
|
console.log(`mslxdff v${VERSION} — OpenCode Free OpenAI-compatible proxy
|
|
384
458
|
|
|
385
459
|
Usage:
|
|
386
|
-
mslxdff start
|
|
460
|
+
mslxdff start as a background daemon and exit (status + help if one is already running)
|
|
387
461
|
mslxdff -d start as a background daemon
|
|
388
462
|
mslxdff -status show current status (daemon, models, recent calls, last error)
|
|
463
|
+
mslxdff -model list list the free models this proxy serves (cached)
|
|
464
|
+
mslxdff -model refresh force-refresh the model cache from the upstream
|
|
389
465
|
mslxdff -stop stop the running daemon
|
|
390
466
|
mslxdff -port N persist the listen port (restarts the daemon on it if running)
|
|
391
467
|
mslxdff -update update mslxdff to the latest published version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mslxdff",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "测试项目,请勿使用。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/mslxdff.js",
|
|
11
|
-
"test": "node --test test
|
|
11
|
+
"test": "node --test test/*.test.js"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=20"
|