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/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({ cwd: executionWorkspace(cwd) })
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 Error("Install fd (or fdfind) to search workspace files.");
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 init_workspace_files = () => {};
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("/").includes(".git"))
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 join7 } from "path";
3919
+ import { dirname as dirname5, isAbsolute as isAbsolute3, join as join8 } from "path";
3862
3920
  async function browseWorkspaces(options) {
3863
- if (!isAbsolute3(options.path))
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(options.path);
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 = join7(path, entry.name);
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 RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Image upload failed");
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 RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot compact");
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 RemoteServiceError("service_invalid_value", "Invalid image range");
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 RemoteServiceError("service_invalid_value", "Image is not part of this conversation");
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 RemoteServiceError("service_invalid_value", "Invalid image range");
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 RemoteServiceError("service_invalid_value", "DEEPSEEK_KEY_REQUIRED: Configure DeepSeek in /login or Web settings.");
4368
- throw new RemoteServiceError("service_invalid_value", error instanceof Error ? error.message : "Cannot submit message");
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 RemoteServiceError("service_invalid_value", `Session ${String(session.id)} is unavailable or archived`);
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 readFile4, writeFile } from "fs/promises";
4444
- import { basename as basename5, dirname as dirname6, join as join8 } from "path";
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 ?? join8(nekoDir, `${serverId}.sock`);
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 connectHint = existingServerId !== undefined ? `\u5982\u9700\u8FDE\u63A5\u5DF2\u6709\u5B9E\u4F8B\uFF1AUnix socket ${info.socketPath}\uFF08serverId: ${existingServerId}\uFF09\uFF0C\u6216\u505C\u6389\u5360\u7528\u65B9\u540E\u91CD\u8BD5\u3002` : `\u5982\u9700\u8FDE\u63A5\u5DF2\u6709\u5B9E\u4F8B\uFF1AUnix socket ${info.socketPath ?? "\uFF08\u672A\u542F\u7528\uFF09"}\uFF0C\u6216\u505C\u6389\u5360\u7528\u65B9\u540E\u91CD\u8BD5\u3002`;
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 = join8(nekoDir, SERVER_ID_FILENAME);
4647
+ const file = join9(nekoDir, SERVER_ID_FILENAME);
4562
4648
  try {
4563
- const existing = (await readFile4(file, "utf8")).trim();
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 writeFile(file, `${created}
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 join9 } from "path";
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(join9(nekoDir, SERVER_ID_FILENAME2));
4627
- const socketPath = options.socketPath ?? config.gateway?.socketPath ?? (fileServerId !== undefined ? join9(nekoDir, `${fileServerId}.sock`) : undefined);
4628
- const serverIdFor = (path) => fileServerId ?? serverIdFromSocketPath2(path) ?? readServerId(join9(nekoDir, SERVER_ID_FILENAME2));
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(join9(plan.nekoDir, SERVER_ID_FILENAME2)), "gateway serverId file", plan.spawnTimeoutMs ?? 20000, child, outputTail, spawnError);
4659
- socketPath = join9(plan.nekoDir, `${generated}.sock`);
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})`, plan.spawnTimeoutMs ?? 20000, child, outputTail, spawnError);
4662
- const serverId = plan.fileServerId ?? serverIdFromSocketPath2(socketPath) ?? readServerId(join9(plan.nekoDir, SERVER_ID_FILENAME2));
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 readFile5, stat as stat3 } from "fs/promises";
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 readFile5(full);
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("/").at(-1) ?? "Image")).join(" ");
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 readFile6, writeFile as writeFile2 } from "fs/promises";
5877
- import { dirname as dirname8, join as join10 } from "path";
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
- const socketPath = options.socketPath ?? join10(nekoDir, `${serverId}.sock`);
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, port, null);
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, port, child);
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 makeHandle(serverId, socketPath, host, port, spawned) {
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: `ws://${host}:${port}`,
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 = join10(nekoDir, SERVER_ID_FILENAME3);
6160
+ const file = join11(nekoDir, SERVER_ID_FILENAME3);
5976
6161
  try {
5977
- const existing = (await readFile6(file, "utf8")).trim();
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 writeFile2(file, `${created}
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/main.ts
6273
- function parseArgs3(argv) {
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 USAGE3 = `\u7528\u6CD5: main.ts [\u9009\u9879]
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\uFF1B0/null \u7981\u7528 WS\uFF09
6364
- --port <n> WebSocket \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8321\uFF1B0 = \u968F\u673A\u5206\u914D\uFF09
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`, options, gateway, stopping = false, stop = () => {
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
- options = parseArgs3(process.argv.slice(2));
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 USAGE4 = `\u7528\u6CD5: neko <command> [options]
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\uFF08Unix socket + WebSocket\uFF09
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.1.1");
6618
+ console.log("0.2.1");
6427
6619
  break;
6428
6620
  case undefined:
6429
6621
  case "-h":
6430
6622
  case "--help":
6431
- console.log(USAGE4);
6623
+ console.log(USAGE3);
6432
6624
  break;
6433
6625
  default:
6434
6626
  console.error(`\u672A\u77E5\u547D\u4EE4: ${command2}
6435
6627
 
6436
- ${USAGE4}`);
6628
+ ${USAGE3}`);
6437
6629
  process.exitCode = 1;
6438
6630
  }