@koishi-ce/koishi 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @koishi-ce/koishi
2
+
3
+ **简体中文** | [English](#english)
4
+
5
+ Koishi 的社区再发行版(Community Edition)主包与命令行宿主,来自 [Koishi-CE/koishi](https://github.com/Koishi-CE/koishi)——将上游 [koishijs/koishi](https://github.com/koishijs/koishi) 与 [koishijs/webui](https://github.com/koishijs/webui) 合并重构的单一 Bun workspace 单仓库。本包入口与上游 `koishi` 主包同构:合并再导出 [`@koishi-ce/core`](https://github.com/Koishi-CE/koishi/tree/main/packages/node/core) 与 [`@koishi-ce/loader`](https://github.com/Koishi-CE/koishi/tree/main/packages/node/loader),并内置 `koishi` 命令与守护进程。
6
+
7
+ 命令名保持为 `koishi`,与上游用法一致。本项目与 Koishijs 官方组织无从属关系,感谢原作者 Shigma 及所有上游贡献者,相关声明见 [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE) 与 [docs/process/upstream.md](https://github.com/Koishi-CE/koishi/blob/main/docs/process/upstream.md)。
8
+
9
+ ## 安装与启动
10
+
11
+ ```bash
12
+ npm install -g @koishi-ce/koishi
13
+ koishi start
14
+ ```
15
+
16
+ 更推荐用脚手架创建完整项目(自带控制台、市场与热重载配置):
17
+
18
+ ```bash
19
+ bunx create-koishi-ce my-app
20
+ ```
21
+
22
+ ## 命令行
23
+
24
+ | 命令 | 说明 |
25
+ | --- | --- |
26
+ | `koishi start [file]`(别名 `run`) | 启动应用,`file` 为配置文件路径 |
27
+
28
+ 选项:
29
+
30
+ | 选项 | 默认值 | 说明 |
31
+ | --- | --- | --- |
32
+ | `--debug [namespace]` | - | 开启调试日志,可限定命名空间 |
33
+ | `--log-level [level]` | 2 | 日志等级 |
34
+ | `--log-time [format]` | - | 日志时间戳格式 |
35
+
36
+ 不带子命令运行 `koishi` 时输出帮助。
37
+
38
+ ## 守护进程机制
39
+
40
+ `koishi start` 是守护父进程:以 Bun.spawn 拉起工作进程并保持 IPC 心跳,心跳超时强制终止。退出码约定:51 = 工作进程请求整体重启(配置文件或框架依赖变更),52 = 请求退出。工作进程内由 loader 读取配置、预检服务器端口(被占则干净退出)、创建应用并挂载 daemon 插件。
41
+
42
+ ## 编程式用法
43
+
44
+ ```ts
45
+ import { App, defineConfig, NodeLoader } from "@koishi-ce/koishi";
46
+ ```
47
+
48
+ 对 `koishi` / `@koishijs/core` / `@koishijs/loader` 三个上游名的兼容由 [`@koishi-ce/koishi-shim`](https://github.com/Koishi-CE/koishi/tree/main/packages/shim) 以 npm alias 承担,社区插件无需改动即可在 CE 项目中运行,详见 [packages/shim/README.md](https://github.com/Koishi-CE/koishi/blob/main/packages/shim/README.md)。
49
+
50
+ ## 许可证
51
+
52
+ [MIT](https://github.com/Koishi-CE/koishi/blob/main/LICENSE)。本包是上游 koishijs/koishi 的社区再分发,版权归属见 [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE)。
53
+
54
+ ---
55
+
56
+ ## English
57
+
58
+ The Community Edition main package and CLI host of Koishi, from [Koishi-CE/koishi](https://github.com/Koishi-CE/koishi) — a single Bun-workspace monorepo merging the upstream [koishijs/koishi](https://github.com/koishijs/koishi) and [koishijs/webui](https://github.com/koishijs/webui). Its entry mirrors the upstream `koishi` package: a merged re-export of `@koishi-ce/core` and `@koishi-ce/loader`, plus the `koishi` command and a daemon. Not affiliated with the Koishijs organization; see [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE) and [docs/process/upstream.md](https://github.com/Koishi-CE/koishi/blob/main/docs/process/upstream.md).
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ npm install -g @koishi-ce/koishi
64
+ koishi start
65
+ ```
66
+
67
+ Or scaffold a full project (console, market and HMR preconfigured):
68
+
69
+ ```bash
70
+ bunx create-koishi-ce my-app
71
+ ```
72
+
73
+ ## CLI
74
+
75
+ `koishi start [file]` (alias `run`) starts the app, with options `--debug [namespace]`, `--log-level [level]` (default 2) and `--log-time [format]`. Running `koishi` without a subcommand prints help.
76
+
77
+ The `start` command is a daemon parent: it spawns the worker via Bun.spawn with IPC heartbeats. Exit code 51 asks for a full restart, 52 asks to quit. The worker reads the config through the loader, prechecks server ports, creates the app and mounts the daemon plugin.
78
+
79
+ ## Programmatic usage
80
+
81
+ ```ts
82
+ import { App, defineConfig, NodeLoader } from "@koishi-ce/koishi";
83
+ ```
84
+
85
+ Compatibility with the upstream names `koishi` / `@koishijs/core` / `@koishijs/loader` is provided by `@koishi-ce/koishi-shim` via npm aliases, so community plugins run unmodified in CE projects — see [packages/shim/README.md](https://github.com/Koishi-CE/koishi/blob/main/packages/shim/README.md).
86
+
87
+ ## License
88
+
89
+ [MIT](https://github.com/Koishi-CE/koishi/blob/main/LICENSE). Community redistribution of upstream koishijs/koishi; see [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE) for attribution.
package/lib/cli/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env bun
2
2
  import { cac } from "cac";
3
3
  import { hyphenate, isInteger } from "@koishi-ce/utils";
4
- import kleur from "kleur";
4
+ import pc from "picocolors";
5
5
  //#region package.json
6
6
  var package_default = {
7
7
  name: "@koishi-ce/koishi",
8
8
  type: "module",
9
9
  description: "Cross-Platform Chatbot Framework Made with Love",
10
- version: "1.0.3",
10
+ version: "1.0.6",
11
11
  main: "lib/index.mjs",
12
12
  module: "lib/index.mjs",
13
13
  types: "lib/index.d.ts",
@@ -58,9 +58,8 @@ var package_default = {
58
58
  "@koishi-ce/plugin-proxy-agent": "workspace:*",
59
59
  "@koishi-ce/plugin-server": "workspace:*",
60
60
  "@koishi-ce/utils": "workspace:*",
61
- "@satorijs/core": "^4.6.0",
62
61
  "cac": "^7.0.0",
63
- "kleur": "^4.1.5"
62
+ "picocolors": "^1.1.1"
64
63
  }
65
64
  };
66
65
  //#endregion
@@ -106,14 +105,14 @@ function createWorker(options) {
106
105
  if (message.type === "start") {
107
106
  config = message.body;
108
107
  timer = config.heartbeatTimeout ? setTimeout(() => {
109
- console.log(kleur.red("daemon: heartbeat timeout"));
108
+ console.log(pc.red("daemon: heartbeat timeout"));
110
109
  child.kill("SIGKILL");
111
110
  }, config.heartbeatTimeout) : void 0;
112
111
  } else if (message.type === "shared") process.env["KOISHI_SHARED"] = message.body;
113
112
  else if (message.type === "heartbeat" && timer && config.heartbeatTimeout) {
114
113
  clearTimeout(timer);
115
114
  timer = setTimeout(() => {
116
- console.log(kleur.red("daemon: heartbeat timeout"));
115
+ console.log(pc.red("daemon: heartbeat timeout"));
117
116
  child.kill("SIGKILL");
118
117
  }, config.heartbeatTimeout);
119
118
  }
@@ -164,7 +163,7 @@ function start_default(cli) {
164
163
  cli.command("start [file]", "start a koishi bot").alias("run").allowUnknownOptions().option("--debug [namespace]", "specify debug namespace").option("--log-level [level]", "specify log level (default: 2)").option("--log-time [format]", "show timestamp in logs").action((file, options) => {
165
164
  const { logLevel, debug, logTime, ...rest } = options;
166
165
  if (logLevel !== void 0 && (!isInteger(logLevel) || logLevel < 0)) {
167
- console.warn(`${kleur.red("error")} log level should be a positive integer.`);
166
+ console.warn(`${pc.red("error")} log level should be a positive integer.`);
168
167
  process.exit(1);
169
168
  }
170
169
  setEnvArg("KOISHI_LOG_TIME", logTime);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@koishi-ce/koishi",
3
3
  "type": "module",
4
4
  "description": "Cross-Platform Chatbot Framework Made with Love",
5
- "version": "1.0.3",
5
+ "version": "1.0.6",
6
6
  "main": "lib/index.mjs",
7
7
  "module": "lib/index.mjs",
8
8
  "types": "lib/index.d.ts",
@@ -61,14 +61,13 @@
61
61
  }
62
62
  },
63
63
  "dependencies": {
64
- "@koishi-ce/core": "^1.0.0",
65
- "@koishi-ce/loader": "^1.0.3",
64
+ "@koishi-ce/core": "^1.0.1",
65
+ "@koishi-ce/loader": "^1.0.5",
66
66
  "@koishi-ce/plugin-http": "^1.0.0",
67
67
  "@koishi-ce/plugin-proxy-agent": "^1.0.0",
68
- "@koishi-ce/plugin-server": "^1.0.0",
68
+ "@koishi-ce/plugin-server": "^1.0.1",
69
69
  "@koishi-ce/utils": "^1.0.0",
70
- "@satorijs/core": "^4.6.0",
71
70
  "cac": "^7.0.0",
72
- "kleur": "^4.1.5"
71
+ "picocolors": "^1.1.1"
73
72
  }
74
73
  }
package/src/cli/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ // SPDX-License-Identifier: MIT
4
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
5
+ // Copyright (c) 2026-present Koishi-CE contributors.
6
+
3
7
  /**
4
8
  * Koishi 命令行入口。
5
9
  *
package/src/cli/start.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * `koishi start` 子命令实现:守护进程(daemon)侧的父进程逻辑。
3
7
  *
@@ -8,7 +12,7 @@
8
12
 
9
13
  import { hyphenate, isInteger } from "@koishi-ce/utils";
10
14
  import type { CAC } from "cac";
11
- import kleur from "kleur";
15
+ import pc from "picocolors";
12
16
  import type { Config } from "../worker/daemon.ts";
13
17
 
14
18
  /** 子进程通过 IPC 通道发来的消息类型(并集) */
@@ -16,7 +20,9 @@ type Event = Event.Start | Event.Env | Event.Heartbeat;
16
20
  /** 单个命令行选项的取值形态 */
17
21
  type WorkerOption = boolean | string | string[] | undefined;
18
22
  /** 传递给 worker 的完整选项表;`--` 键对应 cac 收集的透传参数 */
19
- type WorkerOptions = Record<string, WorkerOption> & { "--"?: string[] };
23
+ type WorkerOptions = Record<string, WorkerOption> & {
24
+ "--"?: string[];
25
+ };
20
26
 
21
27
  /** 子进程 IPC 消息的具体结构定义 */
22
28
  namespace Event {
@@ -50,7 +56,9 @@ process.env["KOISHI_SHARED"] = JSON.stringify({
50
56
  * 单字母键转为 `-x`,其余转为 `--kebab-case`。
51
57
  */
52
58
  function toArg(key: string) {
53
- return key.length === 1 ? `-${key}` : `--${hyphenate(key)}`;
59
+ return key.length === 1
60
+ ? `-${key}`
61
+ : `--${hyphenate(key)}`;
54
62
  }
55
63
 
56
64
  /**
@@ -63,21 +71,23 @@ function toArg(key: string) {
63
71
  */
64
72
  function createWorker(options: WorkerOptions) {
65
73
  // 将选项对象还原为 Node/Bun 可识别的 execArgv 数组
66
- const execArgv = Object.entries(options).flatMap<string>(([key, value]) => {
67
- if (key === "--") return [];
68
- key = toArg(key);
69
- if (value === true) {
70
- return [key];
71
- } else if (value === false) {
72
- // 布尔假值转为 --no-xxx 形式
73
- return [`--no-${key.slice(2)}`];
74
- } else if (Array.isArray(value)) {
75
- // 数组值展开为多组 "键 值"
76
- return value.flatMap((value) => [key, value]);
77
- } else {
78
- return [key, String(value)];
79
- }
80
- });
74
+ const execArgv = Object.entries(options).flatMap<string>(
75
+ ([key, value]) => {
76
+ if (key === "--") return [];
77
+ key = toArg(key);
78
+ if (value === true) {
79
+ return [key];
80
+ } else if (value === false) {
81
+ // 布尔假值转为 --no-xxx 形式
82
+ return [`--no-${key.slice(2)}`];
83
+ } else if (Array.isArray(value)) {
84
+ // 数组值展开为多组 "键 值"
85
+ return value.flatMap((value) => [key, value]);
86
+ } else {
87
+ return [key, String(value)];
88
+ }
89
+ },
90
+ );
81
91
  execArgv.push(...(options["--"] ?? []));
82
92
 
83
93
  // worker 入口为构建产物 index.mjs,而非本 TS 源文件
@@ -92,7 +102,9 @@ function createWorker(options: WorkerOptions) {
92
102
  timer = config.heartbeatTimeout
93
103
  ? setTimeout(() => {
94
104
  // eslint-disable-next-line no-console
95
- console.log(kleur.red("daemon: heartbeat timeout"));
105
+ console.log(
106
+ pc.red("daemon: heartbeat timeout"),
107
+ );
96
108
  child.kill("SIGKILL");
97
109
  }, config.heartbeatTimeout)
98
110
  : undefined;
@@ -106,25 +118,28 @@ function createWorker(options: WorkerOptions) {
106
118
  clearTimeout(timer);
107
119
  timer = setTimeout(() => {
108
120
  // eslint-disable-next-line no-console
109
- console.log(kleur.red("daemon: heartbeat timeout"));
121
+ console.log(pc.red("daemon: heartbeat timeout"));
110
122
  child.kill("SIGKILL");
111
123
  }, config.heartbeatTimeout);
112
124
  }
113
125
  };
114
126
 
115
- child = Bun.spawn([process.execPath, worker, ...execArgv], {
116
- ipc: handleMessage,
117
- // Bun.spawn 的 stdio 默认为 ignore,须显式继承输出通道,
118
- // 否则 worker 的全部日志都会被丢弃
119
- stdout: "inherit",
120
- stderr: "inherit",
121
- onExit: (_, code, signal) => {
122
- if (shouldExit(code, signal)) {
123
- process.exit(code ?? 1);
124
- }
125
- createWorker(options);
127
+ child = Bun.spawn(
128
+ [process.execPath, worker, ...execArgv],
129
+ {
130
+ ipc: handleMessage,
131
+ // Bun.spawn 的 stdio 默认为 ignore,须显式继承输出通道,
132
+ // 否则 worker 的全部日志都会被丢弃
133
+ stdout: "inherit",
134
+ stderr: "inherit",
135
+ onExit: (_, code, signal) => {
136
+ if (shouldExit(code, signal)) {
137
+ process.exit(code ?? 1);
138
+ }
139
+ createWorker(options);
140
+ },
126
141
  },
127
- });
142
+ );
128
143
 
129
144
  /**
130
145
  * 判断子进程退出后父进程应跟随退出还是重新拉起。
@@ -132,7 +147,10 @@ function createWorker(options: WorkerOptions) {
132
147
  * 退出码约定:0 表示正常退出;51 表示请求重启(如 loader 的整进程重载);
133
148
  * 52 表示请求退出;收到信号一律视为外部终止,跟随退出。
134
149
  */
135
- function shouldExit(code: number | null, signal: number | null) {
150
+ function shouldExit(
151
+ code: number | null,
152
+ signal: number | null,
153
+ ) {
136
154
  // 尚未收到 start 消息即退出,说明启动失败
137
155
  if (!config) return true;
138
156
 
@@ -172,15 +190,24 @@ export default function (cli: CAC) {
172
190
  .command("start [file]", "start a koishi bot")
173
191
  .alias("run")
174
192
  .allowUnknownOptions()
175
- .option("--debug [namespace]", "specify debug namespace")
176
- .option("--log-level [level]", "specify log level (default: 2)")
193
+ .option(
194
+ "--debug [namespace]",
195
+ "specify debug namespace",
196
+ )
197
+ .option(
198
+ "--log-level [level]",
199
+ "specify log level (default: 2)",
200
+ )
177
201
  .option("--log-time [format]", "show timestamp in logs")
178
202
  .action((file, options) => {
179
203
  const { logLevel, debug, logTime, ...rest } = options;
180
- if (logLevel !== undefined && (!isInteger(logLevel) || logLevel < 0)) {
204
+ if (
205
+ logLevel !== undefined &&
206
+ (!isInteger(logLevel) || logLevel < 0)
207
+ ) {
181
208
  // eslint-disable-next-line no-console
182
209
  console.warn(
183
- `${kleur.red("error")} log level should be a positive integer.`,
210
+ `${pc.red("error")} log level should be a positive integer.`,
184
211
  );
185
212
  process.exit(1);
186
213
  }
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * `@koishi-ce/koishi` 包入口。
3
7
  *
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * daemon 插件:worker 子进程侧的守护逻辑。
3
7
  *
@@ -21,8 +25,12 @@ export const Config: Schema<Config> = Schema.object({
21
25
  autoRestart: Schema.boolean()
22
26
  .description("在运行时崩溃自动重启。")
23
27
  .default(true),
24
- heartbeatInterval: Schema.number().description("心跳发送间隔。").default(0),
25
- heartbeatTimeout: Schema.number().description("心跳超时时间。").default(0),
28
+ heartbeatInterval: Schema.number()
29
+ .description("心跳发送间隔。")
30
+ .default(0),
31
+ heartbeatTimeout: Schema.number()
32
+ .description("心跳超时时间。")
33
+ .default(0),
26
34
  })
27
35
  .description("守护设置")
28
36
  .hidden();
@@ -46,7 +54,9 @@ export function apply(ctx: Context, config: Config = {}) {
46
54
  process.send?.({ type: "exit" });
47
55
  }
48
56
  ctx.logger("app").info(`terminated by ${signal}`);
49
- ctx.parallel("exit", signal).finally(() => process.exit());
57
+ ctx
58
+ .parallel("exit", signal)
59
+ .finally(() => process.exit());
50
60
  }
51
61
 
52
62
  ctx.on("ready", () => {
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * worker 子进程入口:真正启动 Koishi 应用的地方。
3
7
  *
@@ -10,7 +14,13 @@
10
14
  import { existsSync, readFileSync } from "node:fs";
11
15
  import net from "node:net";
12
16
  import { dirname, resolve } from "node:path";
13
- import { Context, type Dict, Logger, Schema, Time } from "@koishi-ce/core";
17
+ import {
18
+ Context,
19
+ type Dict,
20
+ Logger,
21
+ Schema,
22
+ Time,
23
+ } from "@koishi-ce/core";
14
24
  import Loader, { resolvePlugin } from "@koishi-ce/loader";
15
25
  import * as daemon from "./daemon.ts";
16
26
  import * as logger from "./logger.ts";
@@ -83,14 +93,19 @@ interface ServerPort {
83
93
  }
84
94
 
85
95
  /** 从模块入口路径向上查找最近的 package.json,返回其 name 字段 */
86
- function locatePackageName(filename: string): string | undefined {
96
+ function locatePackageName(
97
+ filename: string,
98
+ ): string | undefined {
87
99
  let dir = dirname(filename);
88
100
  while (true) {
89
101
  const file = resolve(dir, "package.json");
90
102
  if (existsSync(file)) {
91
103
  try {
92
- const manifest = JSON.parse(readFileSync(file, "utf8"));
93
- if (typeof manifest?.name === "string") return manifest.name;
104
+ const manifest = JSON.parse(
105
+ readFileSync(file, "utf8"),
106
+ );
107
+ if (typeof manifest?.name === "string")
108
+ return manifest.name;
94
109
  } catch {}
95
110
  }
96
111
  const parent = dirname(dir);
@@ -103,9 +118,17 @@ function locatePackageName(filename: string): string | undefined {
103
118
  * 遍历插件配置表(含 group 嵌套),收集服务器插件声明的端口区间。
104
119
  * 引用键格式与 loader 一致:首个冒号前为插件名;`$` 开头为元属性。
105
120
  */
106
- function collectServerPorts(plugins: Dict, baseDir: string, out: ServerPort[]) {
121
+ function collectServerPorts(
122
+ plugins: Dict,
123
+ baseDir: string,
124
+ out: ServerPort[],
125
+ ) {
107
126
  for (const [key, source] of Object.entries(plugins)) {
108
- if (key.startsWith("$") || source === null || typeof source !== "object") {
127
+ if (
128
+ key.startsWith("$") ||
129
+ source === null ||
130
+ typeof source !== "object"
131
+ ) {
109
132
  continue;
110
133
  }
111
134
  const [name = ""] = key.split(":", 1);
@@ -114,14 +137,21 @@ function collectServerPorts(plugins: Dict, baseDir: string, out: ServerPort[]) {
114
137
  continue;
115
138
  }
116
139
  try {
117
- const pkgName = locatePackageName(resolvePlugin(name, baseDir));
118
- if (!pkgName || !serverPackages.has(pkgName)) continue;
119
- const { host, port, maxPort } = source as Record<string, unknown>;
140
+ const pkgName = locatePackageName(
141
+ resolvePlugin(name, baseDir),
142
+ );
143
+ if (!pkgName || !serverPackages.has(pkgName))
144
+ continue;
145
+ const { host, port, maxPort } = source as Record<
146
+ string,
147
+ unknown
148
+ >;
120
149
  if (typeof port !== "number") continue;
121
150
  out.push({
122
151
  host: typeof host === "string" ? host : "127.0.0.1",
123
152
  port,
124
- maxPort: typeof maxPort === "number" ? maxPort : port,
153
+ maxPort:
154
+ typeof maxPort === "number" ? maxPort : port,
125
155
  });
126
156
  } catch {}
127
157
  }
@@ -132,7 +162,9 @@ function probePort(port: number, host: string) {
132
162
  return new Promise<boolean>((promiseResolve) => {
133
163
  const server = net.createServer();
134
164
  server.once("error", () => promiseResolve(false));
135
- server.once("listening", () => server.close(() => promiseResolve(true)));
165
+ server.once("listening", () =>
166
+ server.close(() => promiseResolve(true)),
167
+ );
136
168
  server.listen(port, host);
137
169
  });
138
170
  }
@@ -148,14 +180,19 @@ async function checkPorts(plugins: Dict, baseDir: string) {
148
180
  collectServerPorts(plugins, baseDir, ports);
149
181
  for (const { host, port, maxPort } of ports) {
150
182
  let available = false;
151
- for (let current = port; current <= maxPort; current++) {
183
+ for (
184
+ let current = port;
185
+ current <= maxPort;
186
+ current++
187
+ ) {
152
188
  if (await probePort(current, host)) {
153
189
  available = true;
154
190
  break;
155
191
  }
156
192
  }
157
193
  if (available) continue;
158
- const range = port === maxPort ? `${port}` : `${port}-${maxPort}`;
194
+ const range =
195
+ port === maxPort ? `${port}` : `${port}-${maxPort}`;
159
196
  new Logger("app").error(
160
197
  `端口 ${range} 已被占用(可能已有 Koishi 实例在运行),启动中止`,
161
198
  );
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * 日志配置的解析与应用(worker 侧)。
3
7
  *
@@ -5,7 +9,12 @@
5
9
  * 完成全局 Logger 的等级、时间格式等设定。CLI 传入的环境变量优先级高于配置文件。
6
10
  */
7
11
 
8
- import { Context, defineProperty, Logger, Schema } from "@koishi-ce/core";
12
+ import {
13
+ Context,
14
+ defineProperty,
15
+ Logger,
16
+ Schema,
17
+ } from "@koishi-ce/core";
9
18
 
10
19
  /**
11
20
  * 配置文件形态的日志等级表。
@@ -29,11 +38,15 @@ function normalizeLevels(
29
38
  config: LogLevelConfig,
30
39
  base: number,
31
40
  ): Logger.LevelConfig {
32
- const result: Logger.LevelConfig = { base: config.base ?? base };
41
+ const result: Logger.LevelConfig = {
42
+ base: config.base ?? base,
43
+ };
33
44
  for (const [name, level] of Object.entries(config)) {
34
45
  if (name === "base") continue;
35
46
  result[name] =
36
- typeof level === "number" ? level : normalizeLevels(level, result.base);
47
+ typeof level === "number"
48
+ ? level
49
+ : normalizeLevels(level, result.base);
37
50
  }
38
51
  return result;
39
52
  }
@@ -50,7 +63,9 @@ export interface Config {
50
63
 
51
64
  export const Config: Schema<Config> = Schema.object({
52
65
  levels: Schema.any().description("默认的日志输出等级。"),
53
- showDiff: Schema.boolean().description("标注相邻两次日志输出的时间差。"),
66
+ showDiff: Schema.boolean().description(
67
+ "标注相邻两次日志输出的时间差。",
68
+ ),
54
69
  showTime: Schema.union([Boolean, String])
55
70
  .default(true)
56
71
  .description("输出日志所使用的时间格式。"),
@@ -97,7 +112,10 @@ export function prepare(config: Config = {}) {
97
112
  }
98
113
 
99
114
  /** 递归为所有子命名空间补全 base 等级(未显式设置时继承父级) */
100
- function ensureBaseLevel(config: Logger.LevelConfig, base: number) {
115
+ function ensureBaseLevel(
116
+ config: Logger.LevelConfig,
117
+ base: number,
118
+ ) {
101
119
  config.base ??= base;
102
120
  Object.values(config).forEach((value) => {
103
121
  if (typeof value !== "object") return;
@@ -109,7 +127,9 @@ export function prepare(config: Config = {}) {
109
127
 
110
128
  // KOISHI_DEBUG 指定的各个命名空间一律开启 DEBUG 级输出
111
129
  if (process.env["KOISHI_DEBUG"]) {
112
- for (const name of process.env["KOISHI_DEBUG"].split(",")) {
130
+ for (const name of process.env["KOISHI_DEBUG"].split(
131
+ ",",
132
+ )) {
113
133
  new Logger(name).level = Logger.DEBUG;
114
134
  }
115
135
  }
package/readme.md DELETED
@@ -1,26 +0,0 @@
1
- <div align="center">
2
- <a href="https://koishi.chat/" target="_blank">
3
- <img width="160" src="https://koishi.chat/logo.png" alt="logo">
4
- </a>
5
- <h1 id="koishi"><a href="https://github.com/Koishi-CE/koishi" target="_blank">Koishi CE</a></h1>
6
-
7
- [![npm](https://img.shields.io/npm/v/@koishi-ce/koishi?style=flat-square)](https://www.npmjs.com/package/@koishi-ce/koishi)
8
- [![GitHub](https://img.shields.io/github/license/Koishi-CE/koishi?style=flat-square)](https://github.com/Koishi-CE/koishi/blob/main/LICENSE)
9
-
10
- </div>
11
-
12
- `@koishi-ce/koishi` 是 [Koishi](https://koishi.chat) 的社区再发行版 (Community Edition) 命令行工具,来自 [Koishi-CE/koishi](https://github.com/Koishi-CE/koishi) —— 将 Koishi 核心与 webui 合并重构的单一 Bun workspace 单仓库。
13
-
14
- 命令名保持为 `koishi`,与上游用法一致:
15
-
16
- ```bash
17
- npm install -g @koishi-ce/koishi
18
- ```
19
-
20
- > 本项目与 Koishijs 官方组织无从属关系。感谢原作者 Shigma 及所有上游贡献者,相关声明见仓库根目录的 NOTICE 与 UPSTREAM 文件。
21
-
22
- ## 许可证
23
-
24
- 本包基于 [MIT](./LICENSE) 协议开源。
25
-
26
- Copyright © 2019-2023, Shigma; Koishi-CE contributors