neko-harness 0.1.1 → 0.2.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/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 +210 -130
- package/dist/neko.js +269 -77
- 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/neko.js
CHANGED
|
@@ -221,8 +221,8 @@ function validate(config, source) {
|
|
|
221
221
|
if (port !== undefined && (!Number.isInteger(port) || port < 0 || port > 65535)) {
|
|
222
222
|
throw new Error(`neko config: ${source}: "gateway.port" must be an integer between 0 and 65535.`);
|
|
223
223
|
}
|
|
224
|
-
if (socketPath !== undefined && typeof socketPath !== "string") {
|
|
225
|
-
throw new Error(`neko config: ${source}: "gateway.socketPath" must be a string.`);
|
|
224
|
+
if (socketPath !== undefined && socketPath !== null && typeof socketPath !== "string") {
|
|
225
|
+
throw new Error(`neko config: ${source}: "gateway.socketPath" must be a string or null.`);
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
const theme = config.theme;
|
|
@@ -1897,7 +1897,10 @@ async function createNekoRuntime(options = {}) {
|
|
|
1897
1897
|
conversationCreated: async (tx, record) => {
|
|
1898
1898
|
(await tx.doc(SessionMetadataDoc, record.id)).createdAt = Date.now();
|
|
1899
1899
|
},
|
|
1900
|
-
env: ({ cwd }) => new NodeExecutionEnv({
|
|
1900
|
+
env: ({ cwd }) => new NodeExecutionEnv({
|
|
1901
|
+
cwd: executionWorkspace(cwd),
|
|
1902
|
+
...process.platform === "win32" ? { shellEnv: { PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" } } : {}
|
|
1903
|
+
})
|
|
1901
1904
|
}, context);
|
|
1902
1905
|
const scanConversations = () => harness.commit(async (tx) => {
|
|
1903
1906
|
const records = [];
|
|
@@ -3462,7 +3465,7 @@ async function workspaceTarget(root, path) {
|
|
|
3462
3465
|
}
|
|
3463
3466
|
async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
|
|
3464
3467
|
if (!fd)
|
|
3465
|
-
throw new
|
|
3468
|
+
throw new MissingFileSearchDependencyError;
|
|
3466
3469
|
query = relativeQuery(query);
|
|
3467
3470
|
const slash = query.lastIndexOf("/");
|
|
3468
3471
|
const prefix = slash < 0 ? "" : query.slice(0, slash + 1);
|
|
@@ -3486,6 +3489,8 @@ async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
|
|
|
3486
3489
|
"--max-results",
|
|
3487
3490
|
"101",
|
|
3488
3491
|
"--print0",
|
|
3492
|
+
"--path-separator",
|
|
3493
|
+
"/",
|
|
3489
3494
|
"--color",
|
|
3490
3495
|
"never"
|
|
3491
3496
|
];
|
|
@@ -3543,7 +3548,15 @@ async function searchWorkspaceFiles(root, query, signal, fd = findFd()) {
|
|
|
3543
3548
|
entries.sort((a, b) => (a.kind === b.kind ? 0 : a.kind === "directory" ? -1 : 1) || a.path.localeCompare(b.path));
|
|
3544
3549
|
return { entries: entries.slice(0, 100), limited: paths.length > 100 };
|
|
3545
3550
|
}
|
|
3546
|
-
var
|
|
3551
|
+
var MissingFileSearchDependencyError;
|
|
3552
|
+
var init_workspace_files = __esm(() => {
|
|
3553
|
+
MissingFileSearchDependencyError = class MissingFileSearchDependencyError extends Error {
|
|
3554
|
+
constructor() {
|
|
3555
|
+
super("Install fd (or fdfind) to search workspace files.");
|
|
3556
|
+
this.name = "MissingFileSearchDependencyError";
|
|
3557
|
+
}
|
|
3558
|
+
};
|
|
3559
|
+
});
|
|
3547
3560
|
|
|
3548
3561
|
// packages/tui/src/file-completion.ts
|
|
3549
3562
|
import { CombinedAutocompleteProvider } from "@earendil-works/pi-tui";
|
|
@@ -3582,7 +3595,7 @@ function fileCompletion(commands, root, notice) {
|
|
|
3582
3595
|
const items = [];
|
|
3583
3596
|
for (const item of result.items) {
|
|
3584
3597
|
const path = item.value.slice(1).replace(/^"|"$/g, "").replace(/\/$/, "");
|
|
3585
|
-
if (path.split(
|
|
3598
|
+
if (path.split(/[\\/]/).includes(".git"))
|
|
3586
3599
|
continue;
|
|
3587
3600
|
try {
|
|
3588
3601
|
await workspaceTarget(root, path);
|
|
@@ -3855,14 +3868,63 @@ var init_client = __esm(() => {
|
|
|
3855
3868
|
init_ws_listener();
|
|
3856
3869
|
});
|
|
3857
3870
|
|
|
3871
|
+
// packages/gateway/src/port-file.ts
|
|
3872
|
+
import { readFile as readFile4, rm, writeFile } from "fs/promises";
|
|
3873
|
+
import { join as join7 } from "path";
|
|
3874
|
+
function gatewayPortFilePath(nekoDir, serverId) {
|
|
3875
|
+
return join7(nekoDir, `${serverId}.port`);
|
|
3876
|
+
}
|
|
3877
|
+
async function writeGatewayPortFile(nekoDir, serverId, wsUrl) {
|
|
3878
|
+
await writeFile(gatewayPortFilePath(nekoDir, serverId), `${wsUrl}
|
|
3879
|
+
`, { mode: 384 });
|
|
3880
|
+
}
|
|
3881
|
+
async function readGatewayPortFile(nekoDir, serverId) {
|
|
3882
|
+
let content;
|
|
3883
|
+
try {
|
|
3884
|
+
content = await readFile4(gatewayPortFilePath(nekoDir, serverId), "utf8");
|
|
3885
|
+
} catch {
|
|
3886
|
+
return;
|
|
3887
|
+
}
|
|
3888
|
+
const url = content.trim();
|
|
3889
|
+
return url.startsWith("ws://") || url.startsWith("wss://") ? url : undefined;
|
|
3890
|
+
}
|
|
3891
|
+
async function removeGatewayPortFile(nekoDir, serverId) {
|
|
3892
|
+
await rm(gatewayPortFilePath(nekoDir, serverId), { force: true });
|
|
3893
|
+
}
|
|
3894
|
+
var init_port_file = () => {};
|
|
3895
|
+
|
|
3896
|
+
// packages/gateway/src/file-search-help.ts
|
|
3897
|
+
function fileSearchInstallHelp(platform = process.platform) {
|
|
3898
|
+
const docs = "https://github.com/sharkdp/fd#installation";
|
|
3899
|
+
let install;
|
|
3900
|
+
switch (platform) {
|
|
3901
|
+
case "win32":
|
|
3902
|
+
install = "\u8BF7\u6267\u884C\uFF1Awinget install sharkdp.fd\u3002";
|
|
3903
|
+
break;
|
|
3904
|
+
case "darwin":
|
|
3905
|
+
install = "\u8BF7\u6267\u884C\uFF1Abrew install fd\u3002";
|
|
3906
|
+
break;
|
|
3907
|
+
case "linux":
|
|
3908
|
+
install = `Debian/Ubuntu \u8BF7\u6267\u884C\uFF1Asudo apt install fd-find\u3002\u5176\u4ED6\u53D1\u884C\u7248\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
|
|
3909
|
+
break;
|
|
3910
|
+
default:
|
|
3911
|
+
install = `\u5B89\u88C5\u65B9\u6CD5\u8BF7\u53C2\u8003\uFF1A${docs} \u3002`;
|
|
3912
|
+
}
|
|
3913
|
+
return `\u672A\u627E\u5230 fd \u6216 fdfind\uFF0C\u6682\u65F6\u65E0\u6CD5\u4F7F\u7528 @ \u6587\u4EF6\u8865\u5168\u3002\u8BF7\u5728\u8FD0\u884C neko \u7684\u7535\u8111\u4E0A\u5B89\u88C5\u3002${install}\u5B89\u88C5\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u5E76\u91CD\u542F neko\u3002`;
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3858
3916
|
// packages/gateway/src/workspaces.ts
|
|
3859
3917
|
import { constants as constants3 } from "fs";
|
|
3860
3918
|
import { access, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
3861
|
-
import { dirname as dirname5, isAbsolute as isAbsolute3, join as
|
|
3919
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join8 } from "path";
|
|
3862
3920
|
async function browseWorkspaces(options) {
|
|
3863
|
-
|
|
3921
|
+
const requested = options.path.replace(/^\/+([A-Za-z]:[\\/])/, "$1");
|
|
3922
|
+
if (process.platform === "win32" && (requested === "/" || requested === "\\")) {
|
|
3923
|
+
return { path: "/", parent: null, entries: await listDrives(), next: null };
|
|
3924
|
+
}
|
|
3925
|
+
if (!isAbsolute3(requested))
|
|
3864
3926
|
throw new Error("\u8BF7\u9009\u62E9\u5B8C\u6574\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84");
|
|
3865
|
-
const path = accessibleWorkspace(
|
|
3927
|
+
const path = accessibleWorkspace(requested);
|
|
3866
3928
|
const query = options.query?.toLowerCase() ?? "";
|
|
3867
3929
|
const key = JSON.stringify([path, query, options.showHidden === true]);
|
|
3868
3930
|
let offset = 0;
|
|
@@ -3881,7 +3943,7 @@ async function browseWorkspaces(options) {
|
|
|
3881
3943
|
let next = offset;
|
|
3882
3944
|
for (;next < entries.length && directories.length < 100; next++) {
|
|
3883
3945
|
const entry = entries[next];
|
|
3884
|
-
const target =
|
|
3946
|
+
const target = join8(path, entry.name);
|
|
3885
3947
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
3886
3948
|
continue;
|
|
3887
3949
|
try {
|
|
@@ -3900,6 +3962,17 @@ async function browseWorkspaces(options) {
|
|
|
3900
3962
|
next: next < entries.length ? JSON.stringify({ key, offset: next }) : null
|
|
3901
3963
|
};
|
|
3902
3964
|
}
|
|
3965
|
+
async function listDrives() {
|
|
3966
|
+
const drives = [];
|
|
3967
|
+
for (const letter of "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
3968
|
+
const root = `${letter}:\\`;
|
|
3969
|
+
try {
|
|
3970
|
+
await access(root);
|
|
3971
|
+
drives.push({ name: `${letter}:`, path: root });
|
|
3972
|
+
} catch {}
|
|
3973
|
+
}
|
|
3974
|
+
return drives;
|
|
3975
|
+
}
|
|
3903
3976
|
var init_workspaces = __esm(() => {
|
|
3904
3977
|
init_src();
|
|
3905
3978
|
});
|
|
@@ -3907,6 +3980,7 @@ var init_workspaces = __esm(() => {
|
|
|
3907
3980
|
// packages/gateway/src/server-services.ts
|
|
3908
3981
|
import {
|
|
3909
3982
|
createRemoteServiceEndpoint,
|
|
3983
|
+
RemoteServiceError,
|
|
3910
3984
|
RemoteServiceProvider,
|
|
3911
3985
|
replicatedState
|
|
3912
3986
|
} from "@earendil-works/chord";
|
|
@@ -4005,6 +4079,10 @@ async function createNekoServerServices(options) {
|
|
|
4005
4079
|
if (!roots.some((w) => w.path === input.workspaceRoot))
|
|
4006
4080
|
throw new Error("Unknown workspace.");
|
|
4007
4081
|
return await searchWorkspaceFiles(input.workspaceRoot, input.query, AbortSignal.any([controller.signal, ...ctx.abortSignal ? [ctx.abortSignal] : []]));
|
|
4082
|
+
} catch (error) {
|
|
4083
|
+
if (error instanceof MissingFileSearchDependencyError)
|
|
4084
|
+
throw new RemoteServiceError("service_not_allowed", fileSearchInstallHelp());
|
|
4085
|
+
throw error;
|
|
4008
4086
|
} finally {
|
|
4009
4087
|
fileQueries--;
|
|
4010
4088
|
if (fileQuery === controller)
|
|
@@ -4198,7 +4276,7 @@ import { open as open2 } from "fs/promises";
|
|
|
4198
4276
|
import { basename as basename4, extname } from "path";
|
|
4199
4277
|
import {
|
|
4200
4278
|
createRemoteServiceEndpoint as createRemoteServiceEndpoint2,
|
|
4201
|
-
RemoteServiceError,
|
|
4279
|
+
RemoteServiceError as RemoteServiceError2,
|
|
4202
4280
|
RemoteServiceProvider as RemoteServiceProvider2,
|
|
4203
4281
|
replicatedState as replicatedState2
|
|
4204
4282
|
} from "@earendil-works/chord";
|
|
@@ -4270,7 +4348,7 @@ async function openNekoSessionHandle(options) {
|
|
|
4270
4348
|
try {
|
|
4271
4349
|
return action();
|
|
4272
4350
|
} catch (error) {
|
|
4273
|
-
throw new
|
|
4351
|
+
throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
|
|
4274
4352
|
}
|
|
4275
4353
|
};
|
|
4276
4354
|
const createProvider = () => {
|
|
@@ -4327,7 +4405,7 @@ async function openNekoSessionHandle(options) {
|
|
|
4327
4405
|
try {
|
|
4328
4406
|
await session.compact(instructions);
|
|
4329
4407
|
} catch (error) {
|
|
4330
|
-
throw new
|
|
4408
|
+
throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
|
|
4331
4409
|
}
|
|
4332
4410
|
},
|
|
4333
4411
|
beginImage: async (input) => safeUpload(() => uploads.begin(input)),
|
|
@@ -4336,15 +4414,15 @@ async function openNekoSessionHandle(options) {
|
|
|
4336
4414
|
releaseImage: async (id) => uploads.release(id),
|
|
4337
4415
|
getImage: async (input) => {
|
|
4338
4416
|
if (!Number.isSafeInteger(input.offset) || input.offset < 0 || !Number.isSafeInteger(input.contentIndex) || input.contentIndex < 0)
|
|
4339
|
-
throw new
|
|
4417
|
+
throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
|
|
4340
4418
|
const entry = history.get(input.entryId);
|
|
4341
4419
|
const content = entry?.model?.[0]?.content;
|
|
4342
4420
|
const image = Array.isArray(content) ? content[input.contentIndex] : undefined;
|
|
4343
4421
|
if (image?.type !== "image")
|
|
4344
|
-
throw new
|
|
4422
|
+
throw new RemoteServiceError2("service_invalid_value", "Image is not part of this conversation");
|
|
4345
4423
|
const bytes = Buffer.from(image.data, "base64");
|
|
4346
4424
|
if (input.offset > bytes.length)
|
|
4347
|
-
throw new
|
|
4425
|
+
throw new RemoteServiceError2("service_invalid_value", "Invalid image range");
|
|
4348
4426
|
return {
|
|
4349
4427
|
data: bytes.subarray(input.offset, input.offset + IMAGE_CHUNK_BYTES).toString("base64"),
|
|
4350
4428
|
total: bytes.length,
|
|
@@ -4364,8 +4442,8 @@ async function openNekoSessionHandle(options) {
|
|
|
4364
4442
|
uploads.release(id);
|
|
4365
4443
|
} catch (error) {
|
|
4366
4444
|
if (error instanceof Error && error.message.startsWith("DEEPSEEK_KEY_REQUIRED:"))
|
|
4367
|
-
throw new
|
|
4368
|
-
throw new
|
|
4445
|
+
throw new RemoteServiceError2("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
|
|
4446
|
+
throw new RemoteServiceError2("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
|
|
4369
4447
|
}
|
|
4370
4448
|
await onActivity();
|
|
4371
4449
|
},
|
|
@@ -4384,7 +4462,7 @@ async function openNekoSessionHandle(options) {
|
|
|
4384
4462
|
configure: async (change) => {
|
|
4385
4463
|
const applied = await runtime.configureConversation(session.id, change);
|
|
4386
4464
|
if (!applied) {
|
|
4387
|
-
throw new
|
|
4465
|
+
throw new RemoteServiceError2("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
|
|
4388
4466
|
}
|
|
4389
4467
|
}
|
|
4390
4468
|
});
|
|
@@ -4440,8 +4518,8 @@ var init_session_host = __esm(() => {
|
|
|
4440
4518
|
|
|
4441
4519
|
// packages/gateway/src/gateway.ts
|
|
4442
4520
|
import { randomUUID } from "crypto";
|
|
4443
|
-
import { mkdir as mkdir2, readFile as
|
|
4444
|
-
import { basename as basename5, dirname as dirname6, join as
|
|
4521
|
+
import { mkdir as mkdir2, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
|
|
4522
|
+
import { basename as basename5, dirname as dirname6, join as join9 } from "path";
|
|
4445
4523
|
import { BACKGROUND_CONTEXT as BACKGROUND_CONTEXT3 } from "@earendil-works/chord/context";
|
|
4446
4524
|
import { isServerId } from "@earendil-works/pi-protocol";
|
|
4447
4525
|
import { Server, SessionNotFoundError } from "@earendil-works/pi-server";
|
|
@@ -4452,10 +4530,10 @@ async function startGateway(options = {}) {
|
|
|
4452
4530
|
const model = options.model ?? fileConfig.defaultModel ?? DEFAULT_MODEL;
|
|
4453
4531
|
const storagePath = options.storagePath ?? fileConfig.storagePath ?? DEFAULT_STORAGE_PATH;
|
|
4454
4532
|
const host = options.host ?? fileConfig.gateway?.host ?? DEFAULT_HOST;
|
|
4455
|
-
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? DEFAULT_PORT;
|
|
4533
|
+
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? (process.platform === "win32" ? 0 : DEFAULT_PORT);
|
|
4456
4534
|
const nekoDir = dirname6(storagePath);
|
|
4457
4535
|
const serverId = options.serverId ?? await loadOrCreateServerId(nekoDir);
|
|
4458
|
-
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath
|
|
4536
|
+
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null : join9(nekoDir, `${serverId}.sock`);
|
|
4459
4537
|
const runtime = await createNekoRuntime({
|
|
4460
4538
|
context,
|
|
4461
4539
|
storage: options.storage ?? "sqlite",
|
|
@@ -4476,6 +4554,7 @@ async function startGateway(options = {}) {
|
|
|
4476
4554
|
port,
|
|
4477
4555
|
socketPath,
|
|
4478
4556
|
serverId,
|
|
4557
|
+
nekoDir,
|
|
4479
4558
|
model,
|
|
4480
4559
|
onError: options.onError
|
|
4481
4560
|
});
|
|
@@ -4485,7 +4564,7 @@ async function startGateway(options = {}) {
|
|
|
4485
4564
|
}
|
|
4486
4565
|
}
|
|
4487
4566
|
async function bindGateway(runtime, options) {
|
|
4488
|
-
const { context, host, port, socketPath, serverId, model } = options;
|
|
4567
|
+
const { context, host, port, socketPath, serverId, nekoDir, model } = options;
|
|
4489
4568
|
const modelLabel = `${model.provider}/${model.modelId}`;
|
|
4490
4569
|
const serverServices = await createNekoServerServices({ runtime, context });
|
|
4491
4570
|
let wsListener;
|
|
@@ -4518,6 +4597,10 @@ async function bindGateway(runtime, options) {
|
|
|
4518
4597
|
await serverServices.dispose().catch(() => {});
|
|
4519
4598
|
throw toStartupError(error, { socketPath, port, host, serverId });
|
|
4520
4599
|
}
|
|
4600
|
+
const portFile = process.platform === "win32" && wsListener !== undefined ? { nekoDir, serverId } : undefined;
|
|
4601
|
+
if (portFile !== undefined && wsListener !== undefined) {
|
|
4602
|
+
await writeGatewayPortFile(portFile.nekoDir, portFile.serverId, `ws://${host}:${wsListener.port}`);
|
|
4603
|
+
}
|
|
4521
4604
|
let closed = false;
|
|
4522
4605
|
return {
|
|
4523
4606
|
serverId,
|
|
@@ -4529,6 +4612,8 @@ async function bindGateway(runtime, options) {
|
|
|
4529
4612
|
if (closed)
|
|
4530
4613
|
return;
|
|
4531
4614
|
closed = true;
|
|
4615
|
+
if (portFile !== undefined)
|
|
4616
|
+
await removeGatewayPortFile(portFile.nekoDir, portFile.serverId).catch(() => {});
|
|
4532
4617
|
await server.close();
|
|
4533
4618
|
await serverServices.dispose();
|
|
4534
4619
|
await runtime.close();
|
|
@@ -4538,7 +4623,8 @@ async function bindGateway(runtime, options) {
|
|
|
4538
4623
|
function toStartupError(error, info) {
|
|
4539
4624
|
const message = error instanceof Error ? error.message : String(error);
|
|
4540
4625
|
const existingServerId = info.socketPath === null ? undefined : serverIdFromSocketPath(info.socketPath);
|
|
4541
|
-
const
|
|
4626
|
+
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";
|
|
4627
|
+
const connectHint = `\u5982\u9700\u8FDE\u63A5\u5DF2\u6709\u5B9E\u4F8B\uFF1A${existingAddress}\uFF0C\u6216\u505C\u6389\u5360\u7528\u65B9\u540E\u91CD\u8BD5\u3002`;
|
|
4542
4628
|
if (message.includes("already running")) {
|
|
4543
4629
|
return new Error(`neko gateway \u542F\u52A8\u5931\u8D25\uFF1A${message}
|
|
4544
4630
|
\u8BE5\u5730\u5740\u5DF2\u88AB\u4E00\u4E2A\u6D3B\u8DC3\u7F51\u5173\u5360\u7528\uFF0C\u4E0D\u80FD\u542F\u52A8\u7B2C\u4E8C\u4E2A\u5B9E\u4F8B\u3002
|
|
@@ -4558,21 +4644,22 @@ function serverIdFromSocketPath(socketPath) {
|
|
|
4558
4644
|
return isServerId(candidate) ? candidate : undefined;
|
|
4559
4645
|
}
|
|
4560
4646
|
async function loadOrCreateServerId(nekoDir) {
|
|
4561
|
-
const file =
|
|
4647
|
+
const file = join9(nekoDir, SERVER_ID_FILENAME);
|
|
4562
4648
|
try {
|
|
4563
|
-
const existing = (await
|
|
4649
|
+
const existing = (await readFile5(file, "utf8")).trim();
|
|
4564
4650
|
if (existing !== "")
|
|
4565
4651
|
return existing;
|
|
4566
4652
|
} catch {}
|
|
4567
4653
|
const created = randomUUID();
|
|
4568
4654
|
await mkdir2(nekoDir, { recursive: true, mode: 448 });
|
|
4569
|
-
await
|
|
4655
|
+
await writeFile2(file, `${created}
|
|
4570
4656
|
`, { mode: 384 });
|
|
4571
4657
|
return created;
|
|
4572
4658
|
}
|
|
4573
4659
|
var DEFAULT_HOST = "127.0.0.1", DEFAULT_PORT = 8321, SERVER_ID_FILENAME = "gateway-server-id";
|
|
4574
4660
|
var init_gateway = __esm(() => {
|
|
4575
4661
|
init_src();
|
|
4662
|
+
init_port_file();
|
|
4576
4663
|
init_server_services();
|
|
4577
4664
|
init_session_host();
|
|
4578
4665
|
init_ws_listener();
|
|
@@ -4610,6 +4697,7 @@ var init_src2 = __esm(() => {
|
|
|
4610
4697
|
init_client();
|
|
4611
4698
|
init_contract();
|
|
4612
4699
|
init_gateway();
|
|
4700
|
+
init_port_file();
|
|
4613
4701
|
init_process();
|
|
4614
4702
|
init_ws_listener();
|
|
4615
4703
|
});
|
|
@@ -4617,15 +4705,27 @@ var init_src2 = __esm(() => {
|
|
|
4617
4705
|
// packages/tui/src/gateway.ts
|
|
4618
4706
|
import { spawn as spawn2 } from "child_process";
|
|
4619
4707
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
4620
|
-
import { basename as basename6, dirname as dirname7, join as
|
|
4708
|
+
import { basename as basename6, dirname as dirname7, join as join10 } from "path";
|
|
4621
4709
|
import { isServerId as isServerId2 } from "@earendil-works/pi-protocol";
|
|
4622
4710
|
async function connectToGateway(options = {}) {
|
|
4623
4711
|
const config = loadNekoConfig(options.configDir);
|
|
4624
4712
|
const storagePath = options.storagePath ?? config.storagePath ?? DEFAULT_STORAGE_PATH;
|
|
4625
4713
|
const nekoDir = dirname7(storagePath);
|
|
4626
|
-
const fileServerId = readServerId(
|
|
4627
|
-
|
|
4628
|
-
|
|
4714
|
+
const fileServerId = readServerId(join10(nekoDir, SERVER_ID_FILENAME2));
|
|
4715
|
+
if (process.platform === "win32") {
|
|
4716
|
+
if (fileServerId !== undefined) {
|
|
4717
|
+
const wsUrl = await readGatewayPortFile(nekoDir, fileServerId);
|
|
4718
|
+
if (wsUrl !== undefined) {
|
|
4719
|
+
try {
|
|
4720
|
+
const client = await connectNekoGateway({ transport: "ws", url: wsUrl, serverId: fileServerId });
|
|
4721
|
+
return { client, socketPath: undefined, serverId: fileServerId, spawned: false, close: () => client.close() };
|
|
4722
|
+
} catch {}
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
return spawnAndConnect({ ...options, storagePath, socketPath: undefined, nekoDir, fileServerId });
|
|
4726
|
+
}
|
|
4727
|
+
const socketPath = options.socketPath ?? config.gateway?.socketPath ?? (fileServerId !== undefined ? join10(nekoDir, `${fileServerId}.sock`) : undefined);
|
|
4728
|
+
const serverIdFor = (path) => fileServerId ?? serverIdFromSocketPath2(path) ?? readServerId(join10(nekoDir, SERVER_ID_FILENAME2));
|
|
4629
4729
|
if (socketPath !== undefined && existsSync2(socketPath)) {
|
|
4630
4730
|
const serverId = serverIdFor(socketPath);
|
|
4631
4731
|
if (serverId !== undefined) {
|
|
@@ -4653,13 +4753,42 @@ async function spawnAndConnect(plan) {
|
|
|
4653
4753
|
}
|
|
4654
4754
|
}
|
|
4655
4755
|
async function connectSpawned(plan, child, outputTail, spawnError) {
|
|
4756
|
+
const timeoutMs = plan.spawnTimeoutMs ?? 20000;
|
|
4757
|
+
if (process.platform === "win32") {
|
|
4758
|
+
const serverId = plan.fileServerId ?? await waitForValue(() => readServerId(join10(plan.nekoDir, SERVER_ID_FILENAME2)), "gateway serverId file", timeoutMs, child, outputTail, spawnError);
|
|
4759
|
+
const wsUrl = await waitForValue(() => readGatewayPortFile(plan.nekoDir, serverId), "gateway ready (port file)", timeoutMs, child, outputTail, spawnError);
|
|
4760
|
+
let lastError;
|
|
4761
|
+
for (let attempt = 0;attempt < CONNECT_RETRIES; attempt++) {
|
|
4762
|
+
if (child.exitCode !== null || spawnError() !== undefined)
|
|
4763
|
+
break;
|
|
4764
|
+
try {
|
|
4765
|
+
const client = await connectNekoGateway({ transport: "ws", url: wsUrl, serverId });
|
|
4766
|
+
return {
|
|
4767
|
+
client,
|
|
4768
|
+
socketPath: undefined,
|
|
4769
|
+
serverId,
|
|
4770
|
+
spawned: true,
|
|
4771
|
+
async close() {
|
|
4772
|
+
await client.close().catch(() => {});
|
|
4773
|
+
await stopGatewayProcess(child);
|
|
4774
|
+
await removeGatewayPortFile(plan.nekoDir, serverId).catch(() => {});
|
|
4775
|
+
}
|
|
4776
|
+
};
|
|
4777
|
+
} catch (error) {
|
|
4778
|
+
lastError = error;
|
|
4779
|
+
await sleep(CONNECT_RETRY_MS);
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
throw new Error(`Gateway connection failed: ${lastError instanceof Error ? lastError.message : String(lastError)}
|
|
4783
|
+
${outputTail()}`);
|
|
4784
|
+
}
|
|
4656
4785
|
let socketPath = plan.socketPath;
|
|
4657
4786
|
if (socketPath === undefined) {
|
|
4658
|
-
const generated = await waitForValue(() => readServerId(
|
|
4659
|
-
socketPath =
|
|
4787
|
+
const generated = await waitForValue(() => readServerId(join10(plan.nekoDir, SERVER_ID_FILENAME2)), "gateway serverId file", timeoutMs, child, outputTail, spawnError);
|
|
4788
|
+
socketPath = join10(plan.nekoDir, `${generated}.sock`);
|
|
4660
4789
|
}
|
|
4661
|
-
await waitForValue(() => existsSync2(socketPath) ? true : undefined, `gateway ready (socket: ${socketPath})`,
|
|
4662
|
-
const serverId = plan.fileServerId ?? serverIdFromSocketPath2(socketPath) ?? readServerId(
|
|
4790
|
+
await waitForValue(() => existsSync2(socketPath) ? true : undefined, `gateway ready (socket: ${socketPath})`, timeoutMs, child, outputTail, spawnError);
|
|
4791
|
+
const serverId = plan.fileServerId ?? serverIdFromSocketPath2(socketPath) ?? readServerId(join10(plan.nekoDir, SERVER_ID_FILENAME2));
|
|
4663
4792
|
if (serverId === undefined) {
|
|
4664
4793
|
throw new Error(`Cannot determine gateway serverId (socket: ${socketPath})`);
|
|
4665
4794
|
}
|
|
@@ -4688,6 +4817,16 @@ async function connectSpawned(plan, child, outputTail, spawnError) {
|
|
|
4688
4817
|
${outputTail()}`);
|
|
4689
4818
|
}
|
|
4690
4819
|
function gatewayArgs(plan) {
|
|
4820
|
+
if (process.platform === "win32") {
|
|
4821
|
+
return [
|
|
4822
|
+
"--no-socket",
|
|
4823
|
+
"--storage",
|
|
4824
|
+
plan.storagePath,
|
|
4825
|
+
...plan.credentialsPath ? ["--credentials", plan.credentialsPath] : [],
|
|
4826
|
+
...plan.configDir ? ["--cwd", plan.configDir, "--config", plan.configDir] : [],
|
|
4827
|
+
...plan.model !== undefined ? ["--model", `${plan.model.provider}/${plan.model.modelId}`] : []
|
|
4828
|
+
];
|
|
4829
|
+
}
|
|
4691
4830
|
return [
|
|
4692
4831
|
"--port",
|
|
4693
4832
|
"0",
|
|
@@ -4732,7 +4871,7 @@ async function waitForValue(poll, what, timeoutMs, child, outputTail, spawnError
|
|
|
4732
4871
|
const error = spawnError();
|
|
4733
4872
|
if (error !== undefined)
|
|
4734
4873
|
throw new Error(`Gateway process failed to start: ${error.message}${formatTail(outputTail())}`);
|
|
4735
|
-
const value = poll();
|
|
4874
|
+
const value = await poll();
|
|
4736
4875
|
if (value !== undefined)
|
|
4737
4876
|
return value;
|
|
4738
4877
|
if (child.exitCode !== null) {
|
|
@@ -4761,19 +4900,19 @@ var init_gateway2 = __esm(() => {
|
|
|
4761
4900
|
|
|
4762
4901
|
// packages/tui/src/image-input.ts
|
|
4763
4902
|
import { execFile } from "child_process";
|
|
4764
|
-
import { readFile as
|
|
4903
|
+
import { readFile as readFile6, stat as stat3 } from "fs/promises";
|
|
4765
4904
|
import { homedir as homedir5 } from "os";
|
|
4766
4905
|
import { basename as basename7, resolve as resolve5 } from "path";
|
|
4767
4906
|
import { promisify } from "util";
|
|
4768
4907
|
import { isKeyRelease as isKeyRelease4, isKeyRepeat as isKeyRepeat2, matchesKey as matchesKey4 } from "@earendil-works/pi-tui";
|
|
4769
|
-
function imagePaths(text) {
|
|
4908
|
+
function imagePaths(text, platform = process.platform) {
|
|
4770
4909
|
const paths = [];
|
|
4771
4910
|
let word = "", quote = "", escaped = false;
|
|
4772
4911
|
for (const c of text.trim()) {
|
|
4773
4912
|
if (escaped) {
|
|
4774
4913
|
word += c;
|
|
4775
4914
|
escaped = false;
|
|
4776
|
-
} else if (c === "\\" && quote !== "'") {
|
|
4915
|
+
} else if (c === "\\" && quote !== "'" && platform !== "win32") {
|
|
4777
4916
|
escaped = true;
|
|
4778
4917
|
} else if (quote) {
|
|
4779
4918
|
if (c === quote)
|
|
@@ -4801,7 +4940,7 @@ async function readImageFile(path, cwd) {
|
|
|
4801
4940
|
const info = await stat3(full);
|
|
4802
4941
|
if (!info.isFile() || info.size > 20 * 1024 * 1024)
|
|
4803
4942
|
throw new Error("Image must be a file no larger than 20 MiB");
|
|
4804
|
-
const bytes = await
|
|
4943
|
+
const bytes = await readFile6(full);
|
|
4805
4944
|
const after = await stat3(full);
|
|
4806
4945
|
if (bytes.length !== info.size || after.size !== info.size || after.mtimeMs !== info.mtimeMs)
|
|
4807
4946
|
throw new Error("Image changed while reading");
|
|
@@ -5206,7 +5345,7 @@ class NekoTuiApp {
|
|
|
5206
5345
|
this.sendImages(text, "followUp");
|
|
5207
5346
|
}
|
|
5208
5347
|
async addImagePaths(paths) {
|
|
5209
|
-
return paths.map((path) => this.addImage(readImageFile(path, this.#options.cwd ?? process.cwd()), path.split(
|
|
5348
|
+
return paths.map((path) => this.addImage(readImageFile(path, this.#options.cwd ?? process.cwd()), path.split(/[\\/]/).at(-1) ?? "Image")).join(" ");
|
|
5210
5349
|
}
|
|
5211
5350
|
addImage(file, name) {
|
|
5212
5351
|
const ref = `[Image ${++this.#imageCounter}: ${name.replace(/[\]\r\n]/g, "")}]`;
|
|
@@ -5836,7 +5975,7 @@ Options:
|
|
|
5836
5975
|
--cwd <dir> Workspace (default: launch directory; not an access sandbox)
|
|
5837
5976
|
--model <ref> provider/modelId for a spawned gateway
|
|
5838
5977
|
--storage <path> SQLite storage (default: ~/.neko/neko.sqlite)
|
|
5839
|
-
--socket-path <path> Gateway Unix socket
|
|
5978
|
+
--socket-path <path> Gateway Unix socket\uFF08POSIX\uFF1BWindows \u7ECF\u7AEF\u53E3\u6587\u4EF6\u53D1\u73B0 ws \u5730\u5740\uFF09
|
|
5840
5979
|
--theme <mode> light / dark / auto (overrides NEKO_THEME)
|
|
5841
5980
|
-h, --help Show help
|
|
5842
5981
|
|
|
@@ -5873,16 +6012,53 @@ var init_open_browser = () => {};
|
|
|
5873
6012
|
// packages/web/src/gateway-spawn.ts
|
|
5874
6013
|
import { spawn as spawn3 } from "child_process";
|
|
5875
6014
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5876
|
-
import { mkdir as mkdir3, readFile as
|
|
5877
|
-
import { dirname as dirname8, join as
|
|
6015
|
+
import { mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
6016
|
+
import { dirname as dirname8, join as join11 } from "path";
|
|
5878
6017
|
async function ensureNekoGateway(options) {
|
|
5879
6018
|
const nekoDir = dirname8(options.storagePath);
|
|
5880
6019
|
const serverId = options.serverId ?? await loadOrCreateServerId2(nekoDir);
|
|
5881
|
-
|
|
6020
|
+
if (process.platform === "win32") {
|
|
6021
|
+
const discovered = await readGatewayPortFile(nekoDir, serverId);
|
|
6022
|
+
if (discovered !== undefined && await probeWs(discovered, serverId)) {
|
|
6023
|
+
return makeHandle(serverId, undefined, discovered, null, nekoDir);
|
|
6024
|
+
}
|
|
6025
|
+
const child = spawnGatewayProcess(options, null, serverId);
|
|
6026
|
+
pipeChildLogs(child, options.onLog);
|
|
6027
|
+
let spawnError;
|
|
6028
|
+
child.on("error", (error) => {
|
|
6029
|
+
spawnError = error;
|
|
6030
|
+
});
|
|
6031
|
+
try {
|
|
6032
|
+
const timeoutAt = Date.now() + (options.spawnTimeoutMs ?? 30000);
|
|
6033
|
+
let lastError = new Error("gateway \u672A\u5C31\u7EEA");
|
|
6034
|
+
while (Date.now() < timeoutAt) {
|
|
6035
|
+
if (spawnError !== undefined)
|
|
6036
|
+
throw spawnError;
|
|
6037
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
6038
|
+
throw new Error(`neko gateway \u5B50\u8FDB\u7A0B\u63D0\u524D\u9000\u51FA\uFF08code ${child.exitCode}\uFF09\uFF0C\u8BE6\u89C1\u4E0A\u65B9\u65E5\u5FD7\u3002\u7AEF\u53E3\u6587\u4EF6\u53EF\u80FD\u88AB\u5176\u4ED6\u5B9E\u4F8B\u5360\u7528\u3002`);
|
|
6039
|
+
}
|
|
6040
|
+
const wsUrl = await readGatewayPortFile(nekoDir, serverId);
|
|
6041
|
+
if (wsUrl !== undefined) {
|
|
6042
|
+
try {
|
|
6043
|
+
if (await probeWs(wsUrl, serverId))
|
|
6044
|
+
return makeHandle(serverId, undefined, wsUrl, child, nekoDir);
|
|
6045
|
+
} catch (error) {
|
|
6046
|
+
lastError = error;
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
await sleep2(PROBE_INTERVAL_MS);
|
|
6050
|
+
}
|
|
6051
|
+
throw new Error(`\u7B49\u5F85 neko gateway \u5C31\u7EEA\u8D85\u65F6\uFF08${options.spawnTimeoutMs ?? 30000}ms\uFF09\uFF1A${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
6052
|
+
} catch (error) {
|
|
6053
|
+
await stopGatewayProcess(child);
|
|
6054
|
+
throw error;
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
const socketPath = options.socketPath ?? join11(nekoDir, `${serverId}.sock`);
|
|
5882
6058
|
const host = options.host ?? "127.0.0.1";
|
|
5883
6059
|
const port = options.port ?? 8321;
|
|
5884
6060
|
if (await probeGateway(socketPath, serverId)) {
|
|
5885
|
-
return makeHandle(serverId, socketPath, host
|
|
6061
|
+
return makeHandle(serverId, socketPath, `ws://${host}:${port}`, null, nekoDir);
|
|
5886
6062
|
}
|
|
5887
6063
|
const child = spawnGatewayProcess(options, socketPath, serverId);
|
|
5888
6064
|
pipeChildLogs(child, options.onLog);
|
|
@@ -5901,7 +6077,7 @@ async function ensureNekoGateway(options) {
|
|
|
5901
6077
|
}
|
|
5902
6078
|
try {
|
|
5903
6079
|
if (await probeGateway(socketPath, serverId))
|
|
5904
|
-
return makeHandle(serverId, socketPath, host
|
|
6080
|
+
return makeHandle(serverId, socketPath, `ws://${host}:${port}`, child, nekoDir);
|
|
5905
6081
|
} catch (error) {
|
|
5906
6082
|
lastError = error;
|
|
5907
6083
|
}
|
|
@@ -5917,10 +6093,7 @@ function spawnGatewayProcess(options, socketPath, serverId) {
|
|
|
5917
6093
|
const args = [
|
|
5918
6094
|
"--host",
|
|
5919
6095
|
options.host ?? "127.0.0.1",
|
|
5920
|
-
"--port",
|
|
5921
|
-
String(options.port ?? 8321),
|
|
5922
|
-
"--socket-path",
|
|
5923
|
-
socketPath,
|
|
6096
|
+
...process.platform === "win32" ? ["--no-socket"] : ["--port", String(options.port ?? 8321), "--socket-path", socketPath ?? ""],
|
|
5924
6097
|
"--server-id",
|
|
5925
6098
|
serverId,
|
|
5926
6099
|
"--storage",
|
|
@@ -5959,28 +6132,40 @@ async function probeGateway(socketPath, serverId) {
|
|
|
5959
6132
|
return false;
|
|
5960
6133
|
}
|
|
5961
6134
|
}
|
|
5962
|
-
function
|
|
6135
|
+
async function probeWs(wsUrl, serverId) {
|
|
6136
|
+
try {
|
|
6137
|
+
const probe = await connectNekoGateway({ transport: "ws", url: wsUrl, serverId });
|
|
6138
|
+
await probe.close();
|
|
6139
|
+
return true;
|
|
6140
|
+
} catch {
|
|
6141
|
+
return false;
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
function makeHandle(serverId, socketPath, wsUrl, spawned, nekoDir) {
|
|
5963
6145
|
return {
|
|
5964
6146
|
serverId,
|
|
5965
6147
|
socketPath,
|
|
5966
|
-
wsUrl
|
|
6148
|
+
wsUrl,
|
|
5967
6149
|
spawned,
|
|
5968
6150
|
async close() {
|
|
5969
|
-
if (spawned !== null)
|
|
6151
|
+
if (spawned !== null) {
|
|
5970
6152
|
await stopGatewayProcess(spawned);
|
|
6153
|
+
if (process.platform === "win32")
|
|
6154
|
+
await removeGatewayPortFile(nekoDir, serverId).catch(() => {});
|
|
6155
|
+
}
|
|
5971
6156
|
}
|
|
5972
6157
|
};
|
|
5973
6158
|
}
|
|
5974
6159
|
async function loadOrCreateServerId2(nekoDir) {
|
|
5975
|
-
const file =
|
|
6160
|
+
const file = join11(nekoDir, SERVER_ID_FILENAME3);
|
|
5976
6161
|
try {
|
|
5977
|
-
const existing = (await
|
|
6162
|
+
const existing = (await readFile7(file, "utf8")).trim();
|
|
5978
6163
|
if (existing !== "")
|
|
5979
6164
|
return existing;
|
|
5980
6165
|
} catch {}
|
|
5981
6166
|
const created = randomUUID2();
|
|
5982
6167
|
await mkdir3(nekoDir, { recursive: true, mode: 448 });
|
|
5983
|
-
await
|
|
6168
|
+
await writeFile3(file, `${created}
|
|
5984
6169
|
`, { mode: 384 });
|
|
5985
6170
|
return created;
|
|
5986
6171
|
}
|
|
@@ -6215,7 +6400,7 @@ var USAGE2 = `\u7528\u6CD5: main.ts [\u9009\u9879]
|
|
|
6215
6400
|
--port <n> \u9759\u6001 HTTP \u7AEF\u53E3\uFF08\u9ED8\u8BA4 8322\uFF09
|
|
6216
6401
|
--host <ip> \u9759\u6001 HTTP \u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF09
|
|
6217
6402
|
--gateway-port <n> \u7F51\u5173 WebSocket \u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\uFF09
|
|
6218
|
-
--socket-path <path> \u7F51\u5173 Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF09
|
|
6403
|
+
--socket-path <path> \u7F51\u5173 Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF1B\u4EC5 POSIX\uFF09
|
|
6219
6404
|
--server-id <id> \u7F51\u5173\u903B\u8F91\u8EAB\u4EFD\uFF08\u9ED8\u8BA4\u8BFB\u53D6/\u751F\u6210 <\u6570\u636E\u76EE\u5F55>/gateway-server-id\uFF09
|
|
6220
6405
|
--storage <path> SQLite \u5B58\u50A8\u6587\u4EF6\uFF08\u9ED8\u8BA4 ~/.neko/neko.sqlite\uFF09
|
|
6221
6406
|
--cwd <dir> \u4F1A\u8BDD\u5DE5\u5177\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
@@ -6269,8 +6454,8 @@ var init_main2 = __esm(async () => {
|
|
|
6269
6454
|
}
|
|
6270
6455
|
});
|
|
6271
6456
|
|
|
6272
|
-
// packages/gateway/src/
|
|
6273
|
-
function
|
|
6457
|
+
// packages/gateway/src/cli-args.ts
|
|
6458
|
+
function parseGatewayArgs(argv) {
|
|
6274
6459
|
let port;
|
|
6275
6460
|
const options = {};
|
|
6276
6461
|
for (let i = 0;i < argv.length; i++) {
|
|
@@ -6304,6 +6489,9 @@ function parseArgs3(argv) {
|
|
|
6304
6489
|
options.socketPath = next;
|
|
6305
6490
|
i++;
|
|
6306
6491
|
break;
|
|
6492
|
+
case "--no-socket":
|
|
6493
|
+
options.socketPath = null;
|
|
6494
|
+
break;
|
|
6307
6495
|
case "--server-id":
|
|
6308
6496
|
if (next === undefined)
|
|
6309
6497
|
throw new Error("--server-id \u9700\u8981\u53C2\u6570");
|
|
@@ -6344,11 +6532,6 @@ function parseArgs3(argv) {
|
|
|
6344
6532
|
options.configDir = next;
|
|
6345
6533
|
i++;
|
|
6346
6534
|
break;
|
|
6347
|
-
case "-h":
|
|
6348
|
-
case "--help":
|
|
6349
|
-
console.log(USAGE3);
|
|
6350
|
-
process.exit(0);
|
|
6351
|
-
break;
|
|
6352
6535
|
default:
|
|
6353
6536
|
throw new Error(`\u672A\u77E5\u9009\u9879: ${arg}`);
|
|
6354
6537
|
}
|
|
@@ -6357,12 +6540,13 @@ function parseArgs3(argv) {
|
|
|
6357
6540
|
options.port = port;
|
|
6358
6541
|
return options;
|
|
6359
6542
|
}
|
|
6360
|
-
var
|
|
6543
|
+
var GATEWAY_USAGE = `\u7528\u6CD5: main.ts [\u9009\u9879]
|
|
6361
6544
|
|
|
6362
6545
|
\u9009\u9879:
|
|
6363
|
-
--host <ip> WebSocket \u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u4EC5\u672C\u673A\
|
|
6364
|
-
--port <n> WebSocket \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\
|
|
6365
|
-
--socket-path <path> Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF09
|
|
6546
|
+
--host <ip> WebSocket \u76D1\u542C\u5730\u5740\uFF08\u9ED8\u8BA4 127.0.0.1\uFF0C\u4EC5\u672C\u673A\uFF09
|
|
6547
|
+
--port <n> WebSocket \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\uFF0CWindows \u9ED8\u8BA4\u968F\u673A\uFF1B0/null \u7981\u7528 WS\uFF09
|
|
6548
|
+
--socket-path <path> Unix socket \u8DEF\u5F84\uFF08\u9ED8\u8BA4 <\u6570\u636E\u76EE\u5F55>/<serverId>.sock\uFF1BWindows \u9ED8\u8BA4\u7981\u7528\uFF09
|
|
6549
|
+
--no-socket \u7981\u7528 Unix socket \u4F20\u8F93\uFF08\u4EC5 WebSocket\uFF09
|
|
6366
6550
|
--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
|
|
6367
6551
|
--cwd <dir> \u4F1A\u8BDD\u5DE5\u5177\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
6368
6552
|
--model <ref> \u6A21\u578B\uFF0C\u683C\u5F0F provider/modelId\uFF08\u9ED8\u8BA4 deepseek/deepseek-flash\uFF09
|
|
@@ -6371,7 +6555,10 @@ var USAGE3 = `\u7528\u6CD5: main.ts [\u9009\u9879]
|
|
|
6371
6555
|
--config <dir> neko.config.json \u67E5\u627E\u8D77\u59CB\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09
|
|
6372
6556
|
-h, --help \u663E\u793A\u6B64\u5E2E\u52A9
|
|
6373
6557
|
|
|
6374
|
-
\u53EF\u901A\u8FC7\u524D\u7AEF\u8BBE\u7F6E\u6216 DEEPSEEK_API_KEY \u914D\u7F6E\u51ED\u636E\u3002
|
|
6558
|
+
\u53EF\u901A\u8FC7\u524D\u7AEF\u8BBE\u7F6E\u6216 DEEPSEEK_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
|
|
6559
|
+
|
|
6560
|
+
// packages/gateway/src/main.ts
|
|
6561
|
+
var argv, options, gateway, stopping = false, stop = () => {
|
|
6375
6562
|
if (stopping)
|
|
6376
6563
|
return;
|
|
6377
6564
|
stopping = true;
|
|
@@ -6382,7 +6569,12 @@ var USAGE3 = `\u7528\u6CD5: main.ts [\u9009\u9879]
|
|
|
6382
6569
|
};
|
|
6383
6570
|
var init_main3 = __esm(async () => {
|
|
6384
6571
|
init_gateway();
|
|
6385
|
-
|
|
6572
|
+
argv = process.argv.slice(2);
|
|
6573
|
+
if (argv.includes("-h") || argv.includes("--help")) {
|
|
6574
|
+
console.log(GATEWAY_USAGE);
|
|
6575
|
+
process.exit(0);
|
|
6576
|
+
}
|
|
6577
|
+
options = parseGatewayArgs(argv);
|
|
6386
6578
|
gateway = await startGateway({
|
|
6387
6579
|
...options,
|
|
6388
6580
|
onError: (error) => console.error("gateway error:", error.message)
|
|
@@ -6398,12 +6590,12 @@ var init_main3 = __esm(async () => {
|
|
|
6398
6590
|
});
|
|
6399
6591
|
|
|
6400
6592
|
// packages/cli/src/main.ts
|
|
6401
|
-
var
|
|
6593
|
+
var USAGE3 = `\u7528\u6CD5: neko <command> [options]
|
|
6402
6594
|
|
|
6403
6595
|
\u547D\u4EE4:
|
|
6404
6596
|
tui \u542F\u52A8\u7EC8\u7AEF\u4EA4\u4E92\u754C\u9762\uFF08\u81EA\u52A8\u62C9\u8D77 gateway\uFF09
|
|
6405
6597
|
web \u542F\u52A8 Web UI \u9759\u6001\u6258\u7BA1\uFF08\u81EA\u52A8\u62C9\u8D77 gateway\uFF09
|
|
6406
|
-
serve \u4EC5\u542F\u52A8 gateway\
|
|
6598
|
+
serve \u4EC5\u542F\u52A8 gateway\uFF08WebSocket\uFF1BPOSIX \u53E6\u6709 Unix socket\uFF09
|
|
6407
6599
|
|
|
6408
6600
|
\u5404\u547D\u4EE4\u7684\u9009\u9879\u900F\u4F20\uFF0C\u8BE6\u89C1 neko <command> --help\u3002
|
|
6409
6601
|
\u53EF\u901A\u8FC7 TUI /login\u3001Web \u8BBE\u7F6E\u9875\u6216 DEEPSEEK_API_KEY / TAVILY_API_KEY \u914D\u7F6E\u51ED\u636E\u3002`;
|
|
@@ -6423,16 +6615,16 @@ switch (command2) {
|
|
|
6423
6615
|
}
|
|
6424
6616
|
case "--version":
|
|
6425
6617
|
case "-v":
|
|
6426
|
-
console.log("0.
|
|
6618
|
+
console.log("0.2.1");
|
|
6427
6619
|
break;
|
|
6428
6620
|
case undefined:
|
|
6429
6621
|
case "-h":
|
|
6430
6622
|
case "--help":
|
|
6431
|
-
console.log(
|
|
6623
|
+
console.log(USAGE3);
|
|
6432
6624
|
break;
|
|
6433
6625
|
default:
|
|
6434
6626
|
console.error(`\u672A\u77E5\u547D\u4EE4: ${command2}
|
|
6435
6627
|
|
|
6436
|
-
${
|
|
6628
|
+
${USAGE3}`);
|
|
6437
6629
|
process.exitCode = 1;
|
|
6438
6630
|
}
|