neko-harness 0.1.0 → 0.2.0
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/client/assets/index-CxnbrvVk.css +2 -0
- package/dist/client/assets/index-D6NpJLXy.js +88 -0
- package/dist/client/index.html +2 -2
- package/dist/gateway.js +166 -120
- package/dist/neko.js +223 -66
- package/package.json +1 -1
- package/dist/client/assets/index-B1I59uXv.js +0 -88
- package/dist/client/assets/index-U3kl-2BJ.css +0 -2
package/dist/client/index.html
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='26' fill='%23ffb7c5'/%3E%3Ctext x='50' y='70' font-size='58' text-anchor='middle' fill='%23ffffff' font-family='sans-serif' font-weight='bold'%3En%3C/text%3E%3C/svg%3E"
|
|
10
10
|
/>
|
|
11
11
|
<title>neko harness</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-D6NpJLXy.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CxnbrvVk.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
package/dist/gateway.js
CHANGED
|
@@ -1,10 +1,113 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
|
+
// packages/gateway/src/cli-args.ts
|
|
5
|
+
var GATEWAY_USAGE = `\u7528\u6CD5: main.ts [\u9009\u9879]
|
|
6
|
+
|
|
7
|
+
\u9009\u9879:
|
|
8
|
+
--host <ip> WebSocket \u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u4EC5\u672C\u673A\uFF09
|
|
9
|
+
--port <n> WebSocket \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\uFF0CWindows \u9ED8\u8BA4\u968F\u673A\uFF1B0/null \u7981\u7528 WS\uFF09
|
|
10
|
+
--socket-path <path> Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF1BWindows \u9ED8\u8BA4\u7981\u7528\uFF09
|
|
11
|
+
--no-socket \u7981\u7528 Unix socket \u4F20\u8F93\uFF08\u4EC5 WebSocket\uFF09
|
|
12
|
+
--server-id <id> \u903B\u8F91\u670D\u52A1\u5668\u8EAB\u4EFD\uFF08\u9ED8\u8BA4\u8BFB\u53D6/\u751F\u6210 <\u6570\u636E\u76EE\u5F55>/gateway-server-id\uFF09
|
|
13
|
+
--cwd <dir> \u4F1A\u8BDD\u5DE5\u5177\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
14
|
+
--model <ref> \u6A21\u578B\uFF0C\u683C\u5F0F provider/modelId\uFF08\u9ED8\u8BA4 deepseek/deepseek-flash\uFF09
|
|
15
|
+
--storage <path> SQLite \u5B58\u50A8\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/neko.sqlite\uFF09
|
|
16
|
+
--credentials <path> \u7528\u6237\u51ED\u636E\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/credentials.json\uFF09
|
|
17
|
+
--config <dir> neko.config.json \u67E5\u627E\u8D77\u59CB\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
18
|
+
-h, --help \u663E\u793A\u6B64\u5E2E\u52A9
|
|
19
|
+
|
|
20
|
+
\u53EF\u901A\u8FC7\u524D\u7AEF\u8BBE\u7F6E\u6216 DEEPSEEK_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
|
|
21
|
+
function parseGatewayArgs(argv) {
|
|
22
|
+
let port;
|
|
23
|
+
const options = {};
|
|
24
|
+
for (let i = 0;i < argv.length; i++) {
|
|
25
|
+
const arg = argv[i];
|
|
26
|
+
const next = argv[i + 1];
|
|
27
|
+
switch (arg) {
|
|
28
|
+
case "--host":
|
|
29
|
+
if (next === undefined)
|
|
30
|
+
throw new Error("--host \u9700\u8981\u53C2\u6570");
|
|
31
|
+
options.host = next;
|
|
32
|
+
i++;
|
|
33
|
+
break;
|
|
34
|
+
case "--port": {
|
|
35
|
+
if (next === undefined)
|
|
36
|
+
throw new Error("--port \u9700\u8981\u53C2\u6570");
|
|
37
|
+
if (next === "0" || next === "null") {
|
|
38
|
+
port = null;
|
|
39
|
+
} else {
|
|
40
|
+
const parsed = Number(next);
|
|
41
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
42
|
+
throw new Error(`--port \u9700\u8981 0-65535 \u7684\u6574\u6570: ${next}`);
|
|
43
|
+
}
|
|
44
|
+
port = parsed;
|
|
45
|
+
}
|
|
46
|
+
i++;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "--socket-path":
|
|
50
|
+
if (next === undefined)
|
|
51
|
+
throw new Error("--socket-path \u9700\u8981\u53C2\u6570");
|
|
52
|
+
options.socketPath = next;
|
|
53
|
+
i++;
|
|
54
|
+
break;
|
|
55
|
+
case "--no-socket":
|
|
56
|
+
options.socketPath = null;
|
|
57
|
+
break;
|
|
58
|
+
case "--server-id":
|
|
59
|
+
if (next === undefined)
|
|
60
|
+
throw new Error("--server-id \u9700\u8981\u53C2\u6570");
|
|
61
|
+
options.serverId = next;
|
|
62
|
+
i++;
|
|
63
|
+
break;
|
|
64
|
+
case "--cwd":
|
|
65
|
+
if (next === undefined)
|
|
66
|
+
throw new Error("--cwd \u9700\u8981\u53C2\u6570");
|
|
67
|
+
options.cwd = next;
|
|
68
|
+
i++;
|
|
69
|
+
break;
|
|
70
|
+
case "--model": {
|
|
71
|
+
if (next === undefined)
|
|
72
|
+
throw new Error("--model \u9700\u8981\u53C2\u6570");
|
|
73
|
+
const slash = next.indexOf("/");
|
|
74
|
+
if (slash <= 0 || slash === next.length - 1)
|
|
75
|
+
throw new Error(`--model \u683C\u5F0F\u4E3A provider/modelId: ${next}`);
|
|
76
|
+
options.model = { provider: next.slice(0, slash), modelId: next.slice(slash + 1) };
|
|
77
|
+
i++;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "--credentials":
|
|
81
|
+
if (next === undefined)
|
|
82
|
+
throw new Error("--credentials \u9700\u8981\u53C2\u6570");
|
|
83
|
+
options.credentialsPath = next;
|
|
84
|
+
i++;
|
|
85
|
+
break;
|
|
86
|
+
case "--storage":
|
|
87
|
+
if (next === undefined)
|
|
88
|
+
throw new Error("--storage \u9700\u8981\u53C2\u6570");
|
|
89
|
+
options.storagePath = next;
|
|
90
|
+
i++;
|
|
91
|
+
break;
|
|
92
|
+
case "--config":
|
|
93
|
+
if (next === undefined)
|
|
94
|
+
throw new Error("--config \u9700\u8981\u53C2\u6570");
|
|
95
|
+
options.configDir = next;
|
|
96
|
+
i++;
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`\u672A\u77E5\u9009\u9879: ${arg}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (port !== undefined)
|
|
103
|
+
options.port = port;
|
|
104
|
+
return options;
|
|
105
|
+
}
|
|
106
|
+
|
|
4
107
|
// packages/gateway/src/gateway.ts
|
|
5
108
|
import { randomUUID } from "crypto";
|
|
6
|
-
import { mkdir as mkdir2, readFile as
|
|
7
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
109
|
+
import { mkdir as mkdir2, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
|
|
110
|
+
import { basename as basename4, dirname as dirname6, join as join9 } from "path";
|
|
8
111
|
import { BACKGROUND_CONTEXT as BACKGROUND_CONTEXT2 } from "@earendil-works/chord/context";
|
|
9
112
|
import { isServerId } from "@earendil-works/pi-protocol";
|
|
10
113
|
import { Server, SessionNotFoundError } from "@earendil-works/pi-server";
|
|
@@ -217,8 +320,8 @@ function validate(config, source) {
|
|
|
217
320
|
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
218
321
|
throw new Error(`neko config: ${source}: "gateway.port" must be an integer between 0 and 65535.`);
|
|
219
322
|
}
|
|
220
|
-
if (socketPath !== undefined && typeof socketPath !== "string") {
|
|
221
|
-
throw new Error(`neko config: ${source}: "gateway.socketPath" must be a string.`);
|
|
323
|
+
if (socketPath !== undefined && socketPath !== null && typeof socketPath !== "string") {
|
|
324
|
+
throw new Error(`neko config: ${source}: "gateway.socketPath" must be a string or null.`);
|
|
222
325
|
}
|
|
223
326
|
}
|
|
224
327
|
const theme = config.theme;
|
|
@@ -1897,7 +2000,10 @@ async function createNekoRuntime(options = {}) {
|
|
|
1897
2000
|
conversationCreated: async (tx, record) => {
|
|
1898
2001
|
(await tx.doc(SessionMetadataDoc, record.id)).createdAt = Date.now();
|
|
1899
2002
|
},
|
|
1900
|
-
env: ({ cwd }) => new NodeExecutionEnv({
|
|
2003
|
+
env: ({ cwd }) => new NodeExecutionEnv({
|
|
2004
|
+
cwd: executionWorkspace(cwd),
|
|
2005
|
+
...process.platform === "win32" ? { shellEnv: { PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" } } : {}
|
|
2006
|
+
})
|
|
1901
2007
|
}, context);
|
|
1902
2008
|
const scanConversations = () => harness.commit(async (tx) => {
|
|
1903
2009
|
const records = [];
|
|
@@ -2323,6 +2429,20 @@ async function createNekoRuntime(options = {}) {
|
|
|
2323
2429
|
}
|
|
2324
2430
|
};
|
|
2325
2431
|
}
|
|
2432
|
+
// packages/gateway/src/port-file.ts
|
|
2433
|
+
import { readFile as readFile4, rm, writeFile } from "fs/promises";
|
|
2434
|
+
import { join as join6 } from "path";
|
|
2435
|
+
function gatewayPortFilePath(nekoDir, serverId) {
|
|
2436
|
+
return join6(nekoDir, `${serverId}.port`);
|
|
2437
|
+
}
|
|
2438
|
+
async function writeGatewayPortFile(nekoDir, serverId, wsUrl) {
|
|
2439
|
+
await writeFile(gatewayPortFilePath(nekoDir, serverId), `${wsUrl}
|
|
2440
|
+
`, { mode: 384 });
|
|
2441
|
+
}
|
|
2442
|
+
async function removeGatewayPortFile(nekoDir, serverId) {
|
|
2443
|
+
await rm(gatewayPortFilePath(nekoDir, serverId), { force: true });
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2326
2446
|
// packages/gateway/src/server-services.ts
|
|
2327
2447
|
import {
|
|
2328
2448
|
createRemoteServiceEndpoint,
|
|
@@ -2334,13 +2454,13 @@ import {
|
|
|
2334
2454
|
import { spawn } from "child_process";
|
|
2335
2455
|
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
2336
2456
|
import { realpath, stat } from "fs/promises";
|
|
2337
|
-
import { basename as basename2, delimiter, isAbsolute as isAbsolute2, join as
|
|
2457
|
+
import { basename as basename2, delimiter, isAbsolute as isAbsolute2, join as join7, relative, resolve as resolve4, sep } from "path";
|
|
2338
2458
|
function findFd() {
|
|
2339
2459
|
for (const name of process.platform === "win32" ? ["fd.exe", "fdfind.exe"] : ["fd", "fdfind"]) {
|
|
2340
2460
|
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
2341
2461
|
if (!directory)
|
|
2342
2462
|
continue;
|
|
2343
|
-
const path =
|
|
2463
|
+
const path = join7(directory, name);
|
|
2344
2464
|
try {
|
|
2345
2465
|
accessSync2(path, constants2.X_OK);
|
|
2346
2466
|
return path;
|
|
@@ -2459,11 +2579,15 @@ var NekoWorkspaceFiles = defineService("neko.workspace-files");
|
|
|
2459
2579
|
// packages/gateway/src/workspaces.ts
|
|
2460
2580
|
import { constants as constants3 } from "fs";
|
|
2461
2581
|
import { access, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
2462
|
-
import { dirname as dirname5, isAbsolute as isAbsolute3, join as
|
|
2582
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join8 } from "path";
|
|
2463
2583
|
async function browseWorkspaces(options) {
|
|
2464
|
-
|
|
2584
|
+
const requested = options.path.replace(/^\/+([A-Za-z]:[\\/])/, "$1");
|
|
2585
|
+
if (process.platform === "win32" && (requested === "/" || requested === "\\")) {
|
|
2586
|
+
return { path: "/", parent: null, entries: await listDrives(), next: null };
|
|
2587
|
+
}
|
|
2588
|
+
if (!isAbsolute3(requested))
|
|
2465
2589
|
throw new Error("\u8BF7\u9009\u62E9\u5B8C\u6574\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84");
|
|
2466
|
-
const path = accessibleWorkspace(
|
|
2590
|
+
const path = accessibleWorkspace(requested);
|
|
2467
2591
|
const query = options.query?.toLowerCase() ?? "";
|
|
2468
2592
|
const key = JSON.stringify([path, query, options.showHidden === true]);
|
|
2469
2593
|
let offset = 0;
|
|
@@ -2482,7 +2606,7 @@ async function browseWorkspaces(options) {
|
|
|
2482
2606
|
let next = offset;
|
|
2483
2607
|
for (;next < entries.length && directories.length < 100; next++) {
|
|
2484
2608
|
const entry = entries[next];
|
|
2485
|
-
const target =
|
|
2609
|
+
const target = join8(path, entry.name);
|
|
2486
2610
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
2487
2611
|
continue;
|
|
2488
2612
|
try {
|
|
@@ -2501,6 +2625,17 @@ async function browseWorkspaces(options) {
|
|
|
2501
2625
|
next: next < entries.length ? JSON.stringify({ key, offset: next }) : null
|
|
2502
2626
|
};
|
|
2503
2627
|
}
|
|
2628
|
+
async function listDrives() {
|
|
2629
|
+
const drives = [];
|
|
2630
|
+
for (const letter of "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
2631
|
+
const root = `${letter}:\\`;
|
|
2632
|
+
try {
|
|
2633
|
+
await access(root);
|
|
2634
|
+
drives.push({ name: `${letter}:`, path: root });
|
|
2635
|
+
} catch {}
|
|
2636
|
+
}
|
|
2637
|
+
return drives;
|
|
2638
|
+
}
|
|
2504
2639
|
|
|
2505
2640
|
// packages/gateway/src/server-services.ts
|
|
2506
2641
|
async function createNekoServerServices(options) {
|
|
@@ -3123,10 +3258,10 @@ async function startGateway(options = {}) {
|
|
|
3123
3258
|
const model = options.model ?? fileConfig.defaultModel ?? DEFAULT_MODEL;
|
|
3124
3259
|
const storagePath = options.storagePath ?? fileConfig.storagePath ?? DEFAULT_STORAGE_PATH;
|
|
3125
3260
|
const host = options.host ?? fileConfig.gateway?.host ?? DEFAULT_HOST;
|
|
3126
|
-
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? DEFAULT_PORT;
|
|
3261
|
+
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? (process.platform === "win32" ? 0 : DEFAULT_PORT);
|
|
3127
3262
|
const nekoDir = dirname6(storagePath);
|
|
3128
3263
|
const serverId = options.serverId ?? await loadOrCreateServerId(nekoDir);
|
|
3129
|
-
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath
|
|
3264
|
+
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null : join9(nekoDir, `${serverId}.sock`);
|
|
3130
3265
|
const runtime = await createNekoRuntime({
|
|
3131
3266
|
context,
|
|
3132
3267
|
storage: options.storage ?? "sqlite",
|
|
@@ -3147,6 +3282,7 @@ async function startGateway(options = {}) {
|
|
|
3147
3282
|
port,
|
|
3148
3283
|
socketPath,
|
|
3149
3284
|
serverId,
|
|
3285
|
+
nekoDir,
|
|
3150
3286
|
model,
|
|
3151
3287
|
onError: options.onError
|
|
3152
3288
|
});
|
|
@@ -3156,7 +3292,7 @@ async function startGateway(options = {}) {
|
|
|
3156
3292
|
}
|
|
3157
3293
|
}
|
|
3158
3294
|
async function bindGateway(runtime, options) {
|
|
3159
|
-
const { context, host, port, socketPath, serverId, model } = options;
|
|
3295
|
+
const { context, host, port, socketPath, serverId, nekoDir, model } = options;
|
|
3160
3296
|
const modelLabel = `${model.provider}/${model.modelId}`;
|
|
3161
3297
|
const serverServices = await createNekoServerServices({ runtime, context });
|
|
3162
3298
|
let wsListener;
|
|
@@ -3189,6 +3325,10 @@ async function bindGateway(runtime, options) {
|
|
|
3189
3325
|
await serverServices.dispose().catch(() => {});
|
|
3190
3326
|
throw toStartupError(error, { socketPath, port, host, serverId });
|
|
3191
3327
|
}
|
|
3328
|
+
const portFile = process.platform === "win32" && wsListener !== undefined ? { nekoDir, serverId } : undefined;
|
|
3329
|
+
if (portFile !== undefined && wsListener !== undefined) {
|
|
3330
|
+
await writeGatewayPortFile(portFile.nekoDir, portFile.serverId, `ws://${host}:${wsListener.port}`);
|
|
3331
|
+
}
|
|
3192
3332
|
let closed = false;
|
|
3193
3333
|
return {
|
|
3194
3334
|
serverId,
|
|
@@ -3200,6 +3340,8 @@ async function bindGateway(runtime, options) {
|
|
|
3200
3340
|
if (closed)
|
|
3201
3341
|
return;
|
|
3202
3342
|
closed = true;
|
|
3343
|
+
if (portFile !== undefined)
|
|
3344
|
+
await removeGatewayPortFile(portFile.nekoDir, portFile.serverId).catch(() => {});
|
|
3203
3345
|
await server.close();
|
|
3204
3346
|
await serverServices.dispose();
|
|
3205
3347
|
await runtime.close();
|
|
@@ -3209,7 +3351,8 @@ async function bindGateway(runtime, options) {
|
|
|
3209
3351
|
function toStartupError(error, info) {
|
|
3210
3352
|
const message = error instanceof Error ? error.message : String(error);
|
|
3211
3353
|
const existingServerId = info.socketPath === null ? undefined : serverIdFromSocketPath(info.socketPath);
|
|
3212
|
-
const
|
|
3354
|
+
const existingAddress = info.socketPath !== null ? `Unix socket ${info.socketPath}${existingServerId !== undefined ? `\uFF08serverId: ${existingServerId}\uFF09` : ""}` : process.platform === "win32" ? `\u7AEF\u53E3\u6587\u4EF6 <\u6570\u636E\u76EE\u5F55>/${info.serverId}.port \u4E2D\u7684 ws \u5730\u5740` : "Unix socket\uFF08\u672A\u542F\u7528\uFF09";
|
|
3355
|
+
const connectHint = `\u5982\u9700\u8FDE\u63A5\u5DF2\u6709\u5B9E\u4F8B\uFF1A${existingAddress}\uFF0C\u6216\u505C\u6389\u5360\u7528\u65B9\u540E\u91CD\u8BD5\u3002`;
|
|
3213
3356
|
if (message.includes("already running")) {
|
|
3214
3357
|
return new Error(`neko gateway \u542F\u52A8\u5931\u8D25\uFF1A${message}
|
|
3215
3358
|
\u8BE5\u5730\u5740\u5DF2\u88AB\u4E00\u4E2A\u6D3B\u8DC3\u7F51\u5173\u5360\u7528\uFF0C\u4E0D\u80FD\u542F\u52A8\u7B2C\u4E8C\u4E2A\u5B9E\u4F8B\u3002
|
|
@@ -3229,123 +3372,26 @@ function serverIdFromSocketPath(socketPath) {
|
|
|
3229
3372
|
return isServerId(candidate) ? candidate : undefined;
|
|
3230
3373
|
}
|
|
3231
3374
|
async function loadOrCreateServerId(nekoDir) {
|
|
3232
|
-
const file =
|
|
3375
|
+
const file = join9(nekoDir, SERVER_ID_FILENAME);
|
|
3233
3376
|
try {
|
|
3234
|
-
const existing = (await
|
|
3377
|
+
const existing = (await readFile5(file, "utf8")).trim();
|
|
3235
3378
|
if (existing !== "")
|
|
3236
3379
|
return existing;
|
|
3237
3380
|
} catch {}
|
|
3238
3381
|
const created = randomUUID();
|
|
3239
3382
|
await mkdir2(nekoDir, { recursive: true, mode: 448 });
|
|
3240
|
-
await
|
|
3383
|
+
await writeFile2(file, `${created}
|
|
3241
3384
|
`, { mode: 384 });
|
|
3242
3385
|
return created;
|
|
3243
3386
|
}
|
|
3244
3387
|
|
|
3245
3388
|
// packages/gateway/src/main.ts
|
|
3246
|
-
var
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
--port <n> WebSocket \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\uFF1B0 = \u968F\u673A\u5206\u914D\uFF09
|
|
3251
|
-
--socket-path <path> Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF09
|
|
3252
|
-
--server-id <id> \u903B\u8F91\u670D\u52A1\u5668\u8EAB\u4EFD\uFF08\u9ED8\u8BA4\u8BFB\u53D6/\u751F\u6210 <\u6570\u636E\u76EE\u5F55>/gateway-server-id\uFF09
|
|
3253
|
-
--cwd <dir> \u4F1A\u8BDD\u5DE5\u5177\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
3254
|
-
--model <ref> \u6A21\u578B\uFF0C\u683C\u5F0F provider/modelId\uFF08\u9ED8\u8BA4 deepseek/deepseek-flash\uFF09
|
|
3255
|
-
--storage <path> SQLite \u5B58\u50A8\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/neko.sqlite\uFF09
|
|
3256
|
-
--credentials <path> \u7528\u6237\u51ED\u636E\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/credentials.json\uFF09
|
|
3257
|
-
--config <dir> neko.config.json \u67E5\u627E\u8D77\u59CB\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
3258
|
-
-h, --help \u663E\u793A\u6B64\u5E2E\u52A9
|
|
3259
|
-
|
|
3260
|
-
\u53EF\u901A\u8FC7\u524D\u7AEF\u8BBE\u7F6E\u6216 DEEPSEEK_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
|
|
3261
|
-
function parseArgs(argv) {
|
|
3262
|
-
let port;
|
|
3263
|
-
const options = {};
|
|
3264
|
-
for (let i = 0;i < argv.length; i++) {
|
|
3265
|
-
const arg = argv[i];
|
|
3266
|
-
const next = argv[i + 1];
|
|
3267
|
-
switch (arg) {
|
|
3268
|
-
case "--host":
|
|
3269
|
-
if (next === undefined)
|
|
3270
|
-
throw new Error("--host \u9700\u8981\u53C2\u6570");
|
|
3271
|
-
options.host = next;
|
|
3272
|
-
i++;
|
|
3273
|
-
break;
|
|
3274
|
-
case "--port": {
|
|
3275
|
-
if (next === undefined)
|
|
3276
|
-
throw new Error("--port \u9700\u8981\u53C2\u6570");
|
|
3277
|
-
if (next === "0" || next === "null") {
|
|
3278
|
-
port = null;
|
|
3279
|
-
} else {
|
|
3280
|
-
const parsed = Number(next);
|
|
3281
|
-
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
3282
|
-
throw new Error(`--port \u9700\u8981 0-65535 \u7684\u6574\u6570: ${next}`);
|
|
3283
|
-
}
|
|
3284
|
-
port = parsed;
|
|
3285
|
-
}
|
|
3286
|
-
i++;
|
|
3287
|
-
break;
|
|
3288
|
-
}
|
|
3289
|
-
case "--socket-path":
|
|
3290
|
-
if (next === undefined)
|
|
3291
|
-
throw new Error("--socket-path \u9700\u8981\u53C2\u6570");
|
|
3292
|
-
options.socketPath = next;
|
|
3293
|
-
i++;
|
|
3294
|
-
break;
|
|
3295
|
-
case "--server-id":
|
|
3296
|
-
if (next === undefined)
|
|
3297
|
-
throw new Error("--server-id \u9700\u8981\u53C2\u6570");
|
|
3298
|
-
options.serverId = next;
|
|
3299
|
-
i++;
|
|
3300
|
-
break;
|
|
3301
|
-
case "--cwd":
|
|
3302
|
-
if (next === undefined)
|
|
3303
|
-
throw new Error("--cwd \u9700\u8981\u53C2\u6570");
|
|
3304
|
-
options.cwd = next;
|
|
3305
|
-
i++;
|
|
3306
|
-
break;
|
|
3307
|
-
case "--model": {
|
|
3308
|
-
if (next === undefined)
|
|
3309
|
-
throw new Error("--model \u9700\u8981\u53C2\u6570");
|
|
3310
|
-
const slash = next.indexOf("/");
|
|
3311
|
-
if (slash <= 0 || slash === next.length - 1)
|
|
3312
|
-
throw new Error(`--model \u683C\u5F0F\u4E3A provider/modelId: ${next}`);
|
|
3313
|
-
options.model = { provider: next.slice(0, slash), modelId: next.slice(slash + 1) };
|
|
3314
|
-
i++;
|
|
3315
|
-
break;
|
|
3316
|
-
}
|
|
3317
|
-
case "--credentials":
|
|
3318
|
-
if (next === undefined)
|
|
3319
|
-
throw new Error("--credentials \u9700\u8981\u53C2\u6570");
|
|
3320
|
-
options.credentialsPath = next;
|
|
3321
|
-
i++;
|
|
3322
|
-
break;
|
|
3323
|
-
case "--storage":
|
|
3324
|
-
if (next === undefined)
|
|
3325
|
-
throw new Error("--storage \u9700\u8981\u53C2\u6570");
|
|
3326
|
-
options.storagePath = next;
|
|
3327
|
-
i++;
|
|
3328
|
-
break;
|
|
3329
|
-
case "--config":
|
|
3330
|
-
if (next === undefined)
|
|
3331
|
-
throw new Error("--config \u9700\u8981\u53C2\u6570");
|
|
3332
|
-
options.configDir = next;
|
|
3333
|
-
i++;
|
|
3334
|
-
break;
|
|
3335
|
-
case "-h":
|
|
3336
|
-
case "--help":
|
|
3337
|
-
console.log(USAGE);
|
|
3338
|
-
process.exit(0);
|
|
3339
|
-
break;
|
|
3340
|
-
default:
|
|
3341
|
-
throw new Error(`\u672A\u77E5\u9009\u9879: ${arg}`);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
if (port !== undefined)
|
|
3345
|
-
options.port = port;
|
|
3346
|
-
return options;
|
|
3389
|
+
var argv = process.argv.slice(2);
|
|
3390
|
+
if (argv.includes("-h") || argv.includes("--help")) {
|
|
3391
|
+
console.log(GATEWAY_USAGE);
|
|
3392
|
+
process.exit(0);
|
|
3347
3393
|
}
|
|
3348
|
-
var options =
|
|
3394
|
+
var options = parseGatewayArgs(argv);
|
|
3349
3395
|
var gateway = await startGateway({
|
|
3350
3396
|
...options,
|
|
3351
3397
|
onError: (error) => console.error("gateway error:", error.message)
|