@mininglamp-oss/octo-runtime 1.1.0 → 1.1.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/README.md +4 -2
- package/apps/cli/README.md +3 -1
- package/apps/cli/dist/build-info.json +2 -2
- package/apps/cli/dist/extras.d.ts +4 -2
- package/apps/cli/dist/extras.js +20 -21
- package/apps/cli/dist/help.js +3 -0
- package/apps/cli/dist/logs.d.ts +10 -0
- package/apps/cli/dist/logs.js +186 -0
- package/apps/cli/dist/main.d.ts +1 -0
- package/apps/cli/dist/main.js +94 -19
- package/apps/cli/dist/{default_adapters.json → manifest.json} +5 -0
- package/apps/cli/dist/module-manifest.d.ts +3 -0
- package/apps/cli/dist/module-manifest.js +11 -0
- package/apps/cli/dist/openclaw.js +30 -6
- package/apps/cli/dist/output.js +3 -0
- package/apps/cli/dist/playboard.js +10 -8
- package/apps/cli/dist/status-output.d.ts +10 -0
- package/apps/cli/dist/status-output.js +65 -0
- package/apps/cli/dist/worker.d.ts +1 -0
- package/apps/cli/dist/worker.js +18 -0
- package/dist/public/host.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-acp-client/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-acp/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-antigravity/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-claude-code/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-codebuddy/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-codex/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-hermes/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-kimi-code/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-mcode/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-opencode/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-qodercli/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-qoderclicn/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.js +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.d.ts +33 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.js +118 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log-worker.js +7 -5
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.d.ts +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.js +45 -10
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.d.ts +11 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.js +70 -0
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/package.json +5 -3
- package/node_modules/@mininglamp-oss/octo-runtime-adapter-zcode-gui/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/build-info.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.d.ts +4 -2
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.js +20 -21
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/help.js +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.d.ts +10 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.js +186 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.js +94 -19
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/{default_adapters.json → manifest.json} +5 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.d.ts +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.js +11 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/openclaw.js +30 -6
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/output.js +3 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/playboard.js +10 -8
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.d.ts +10 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.js +65 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.js +18 -0
- package/node_modules/@mininglamp-oss/octo-runtime-cli/package.json +22 -9
- package/node_modules/@mininglamp-oss/octo-runtime-cross-service/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.d.ts +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.js +5 -5
- package/node_modules/@mininglamp-oss/octo-runtime-executor-core/package.json +3 -3
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.d.ts +16 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.js +59 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.js +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.d.ts +6 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.js +88 -0
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.d.ts +1 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.js +2 -9
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.d.ts +7 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.js +138 -9
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/runner.js +3 -92
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/serve.js +36 -12
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.d.ts +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.js +14 -6
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/service.js +2 -1
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.d.ts +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.js +57 -34
- package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/package.json +8 -16
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/application/im-channel.js +16 -17
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/package.json +6 -6
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/package.json +5 -5
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/config-schema.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/index.js +1 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.d.ts +4 -1
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.js +54 -4
- package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/package.json +2 -2
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.d.ts +8 -1
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.js +27 -12
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-service.js +39 -2
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/index.d.ts +1 -0
- package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/package.json +4 -4
- package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +1 -1
- package/node_modules/import-meta-resolve/index.d.ts +19 -0
- package/node_modules/import-meta-resolve/index.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/index.js +47 -0
- package/node_modules/import-meta-resolve/lib/errors.d.ts +24 -0
- package/node_modules/import-meta-resolve/lib/errors.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/errors.js +504 -0
- package/node_modules/import-meta-resolve/lib/get-format.d.ts +13 -0
- package/node_modules/import-meta-resolve/lib/get-format.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/get-format.js +159 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts +31 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/package-json-reader.js +177 -0
- package/node_modules/import-meta-resolve/lib/resolve.d.ts +29 -0
- package/node_modules/import-meta-resolve/lib/resolve.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/resolve.js +1301 -0
- package/node_modules/import-meta-resolve/lib/utils.d.ts +6 -0
- package/node_modules/import-meta-resolve/lib/utils.d.ts.map +1 -0
- package/node_modules/import-meta-resolve/lib/utils.js +47 -0
- package/node_modules/import-meta-resolve/license +74 -0
- package/node_modules/import-meta-resolve/package.json +78 -0
- package/node_modules/import-meta-resolve/readme.md +252 -0
- package/package.json +28 -26
- package/packages/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
- package/packages/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
- package/packages/octo-runtime-extra-webui/config-schema.json +2 -2
package/README.md
CHANGED
|
@@ -35,7 +35,9 @@ The Executor Kernel provides local execution. Channel Gateway and Loop Daemon ar
|
|
|
35
35
|
|
|
36
36
|
## Get Started
|
|
37
37
|
|
|
38
|
-
You need **Node.js 24.x** and an installed, authenticated Provider. Runtime does not install Provider software or sign in for you.
|
|
38
|
+
You need **Node.js 24.x or 26.x (LTS)** and an installed, authenticated Provider. Runtime does not install Provider software or sign in for you.
|
|
39
|
+
|
|
40
|
+
The supported range is `^24.0.0 || ^26.0.0`. Node.js 25 and future majors are outside this range. CI checks installation, build, types, automated tests, and packed CLI/SDK consumption on both supported majors. New LTS majors are added after the same checks pass; support is reviewed at each LTS transition and before a supported major reaches end of life. Removal is announced in release notes before changing the engine range.
|
|
39
41
|
|
|
40
42
|
### 1. Install
|
|
41
43
|
|
|
@@ -117,7 +119,7 @@ The developer documentation covers architecture, your first SDK call, Kernel IPC
|
|
|
117
119
|
|
|
118
120
|
## Development
|
|
119
121
|
|
|
120
|
-
Use Node.js 24.x and the pnpm version specified in `package.json`.
|
|
122
|
+
Use Node.js 24.x or 26.x and the pnpm version specified in `package.json`. `.nvmrc`, Node.js type definitions, and publishing workflows intentionally stay on Node.js 24, the oldest supported baseline; this does not restrict consumers to 24.
|
|
121
123
|
|
|
122
124
|
```bash
|
|
123
125
|
pnpm install
|
package/apps/cli/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
CLI 是 Host SDK 的一个宿主,负责自己的 JSON 配置与原生用户服务。桌面或其他 HostApp 使用 SDK 接入。
|
|
4
4
|
|
|
5
|
+
模块由 CLI 内置 `manifest.json` 与所选 Home 的 `manifest.json` 合并选择,均含 `adapters` 和 `extras`。默认发行全量;未启动 Extra 不导入,清单不保存业务凭据或启用设置。条目以原始清单文件为解析锚点,同名内置优先;显式 `--manifest` 完整替换两份默认来源。SDK 原生 import,不读取这些文件。双语说明见 [English](../../docs/en/concepts/module-loading.md) / [简体中文](../../docs/zh-CN/concepts/module-loading.md)。
|
|
6
|
+
|
|
5
7
|
## 命令
|
|
6
8
|
|
|
7
9
|
```text
|
|
@@ -53,7 +55,7 @@ octo-runtime channel-gateway restart
|
|
|
53
55
|
octo-runtime extraget channel-gateway
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
gateway.json
|
|
58
|
+
gateway.json 可使用 `{ "config": { "bots": [] } }`,模块由 manifest 声明。旧 `module` 可保留,但必须与有效清单文字完全一致;自定义 Extra 先加入 Home 清单,冲突明确提示迁移。每次整份替换,保存文件权限为 POSIX 0600。extraset 不打印凭据;extraget 按显式请求返回完整配置,路径写入 stderr。
|
|
57
59
|
|
|
58
60
|
## 临时执行
|
|
59
61
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ExtraRegistration, type StoredExtra } from "@mininglamp-oss/octo-runtime-host-sdk";
|
|
2
2
|
import { type RuntimePaths } from "./state.js";
|
|
3
|
+
import { type ModuleManifest } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
|
|
3
4
|
/** Generic full replacement: no platform-specific CLI fields or validation. */
|
|
4
5
|
export declare function setExtra(paths: RuntimePaths, name: string, input: NodeJS.ReadableStream): Promise<unknown>;
|
|
5
|
-
|
|
6
|
+
/** CLI 将模块目录与业务配置装配;仅 load 时解析/import,禁用项及未启动项不执行模块。 */
|
|
7
|
+
export declare function readExtra(paths: RuntimePaths, name: string, manifest: ModuleManifest): Promise<ExtraRegistration>;
|
|
6
8
|
export declare function getExtra(paths: RuntimePaths, name: string): Promise<StoredExtra>;
|
package/apps/cli/dist/extras.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { assertExtraName, assertStoredExtra,
|
|
2
|
+
import { assertExtraName, assertStoredExtra, readStoredExtra } from "@mininglamp-oss/octo-runtime-host-sdk";
|
|
3
3
|
import { withSetupLock, writeJsonAtomic } from "./state.js";
|
|
4
|
+
import { resolveModuleSpecifier } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
|
|
4
5
|
/** Generic full replacement: no platform-specific CLI fields or validation. */
|
|
5
6
|
export async function setExtra(paths, name, input) {
|
|
6
7
|
assertExtraName(name);
|
|
@@ -29,33 +30,31 @@ export async function setExtra(paths, name, input) {
|
|
|
29
30
|
return { saved: true, name, file, reload_required: true };
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
/** CLI 将模块目录与业务配置装配;仅 load 时解析/import,禁用项及未启动项不执行模块。 */
|
|
34
|
+
export async function readExtra(paths, name, manifest) {
|
|
33
35
|
assertExtraName(name);
|
|
34
|
-
const
|
|
35
|
-
const entry =
|
|
36
|
+
const stored = await getExtra(paths, name);
|
|
37
|
+
const entry = manifest.extras.find((item) => item.name === name);
|
|
36
38
|
if (!entry)
|
|
37
|
-
throw new Error("
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
await entry.load();
|
|
39
|
+
throw new Error("EXTRA_MANIFEST_DECLARATION_REQUIRED");
|
|
40
|
+
if (stored.module !== undefined && stored.module !== entry.module)
|
|
41
|
+
throw new Error("EXTRA_MODULE_MIGRATION_REQUIRED");
|
|
41
42
|
return {
|
|
42
43
|
name,
|
|
43
44
|
module: entry.module,
|
|
44
|
-
config:
|
|
45
|
-
|
|
45
|
+
config: stored.config,
|
|
46
|
+
enabled: stored.enabled,
|
|
47
|
+
load: () => import(resolveModuleSpecifier(entry.module, entry.sourceManifest))
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
export async function getExtra(paths, name) {
|
|
49
51
|
assertExtraName(name);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...(entry.enabled === undefined ? {} : { enabled: entry.enabled }),
|
|
59
|
-
config: entry.config
|
|
60
|
-
};
|
|
52
|
+
try {
|
|
53
|
+
return await readStoredExtra(join(paths.home, "extras"), name);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error.code === "ENOENT")
|
|
57
|
+
throw new Error("EXTRA_NOT_REGISTERED", { cause: error });
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
61
60
|
}
|
package/apps/cli/dist/help.js
CHANGED
|
@@ -63,9 +63,11 @@ Commands:
|
|
|
63
63
|
stop Stop this service, preserving its autostart setting
|
|
64
64
|
status Show native service, IPC and component status
|
|
65
65
|
run Run this component in the foreground
|
|
66
|
+
${component === "executor-kernel" ? "" : " logs Tail service status logs (no conversation content)\n"}
|
|
66
67
|
|
|
67
68
|
Flags: --home <path> --profile <name> --manifest <path> --json --help
|
|
68
69
|
Install: --autostart=true|false
|
|
70
|
+
${component === "executor-kernel" ? "" : "Logs: --lines <1..10000> (default 200) --follow --json (NDJSON)\n"}
|
|
69
71
|
Kernel and Extras have independent services and autostart settings.
|
|
70
72
|
An Extra may start before Kernel and connect when Kernel becomes available.`;
|
|
71
73
|
return `Octo Runtime CLI (${cliVersion})
|
|
@@ -75,6 +77,7 @@ Manage the local Agent runtime and independent services.
|
|
|
75
77
|
Usage:
|
|
76
78
|
octo-runtime executor-kernel uninstall|install|start|restart|stop|status|run
|
|
77
79
|
octo-runtime <extra-name> uninstall|install|start|restart|stop|status|run
|
|
80
|
+
octo-runtime <extra-name> logs [--lines 200] [--follow] [--json]
|
|
78
81
|
octo-runtime status [--json]
|
|
79
82
|
octo-runtime adapter list
|
|
80
83
|
octo-runtime adapter probe <type>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function showExtraLogs(options: {
|
|
2
|
+
home: string;
|
|
3
|
+
extra: string;
|
|
4
|
+
lines: number;
|
|
5
|
+
follow: boolean;
|
|
6
|
+
json: boolean;
|
|
7
|
+
output: (line: string, signal?: AbortSignal) => void | Promise<void>;
|
|
8
|
+
errorOutput: (line: string) => void;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { lstat, open, opendir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5
|
+
import { extraStatusRecord } from "@mininglamp-oss/octo-runtime-adapter-sdk";
|
|
6
|
+
const MAX_READ = 4 * 1024 * 1024;
|
|
7
|
+
const MAX_LINE = 4096;
|
|
8
|
+
function record(line, extra) {
|
|
9
|
+
if (Buffer.byteLength(line) > MAX_LINE)
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
const value = JSON.parse(line);
|
|
13
|
+
if (value.source !== "extra" || value.extra_type !== extra || typeof value.time !== "string")
|
|
14
|
+
return;
|
|
15
|
+
return extraStatusRecord(extra, value.level, value.event, value.data, value.time);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function files(home, extra) {
|
|
22
|
+
const directory = join(home, "logs", "extras");
|
|
23
|
+
for (const path of [join(home, "logs"), directory]) {
|
|
24
|
+
try {
|
|
25
|
+
const stat = await lstat(path);
|
|
26
|
+
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
27
|
+
throw new Error("LOG_DIRECTORY_NOT_REGULAR");
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.code === "ENOENT")
|
|
31
|
+
return [];
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const pattern = new RegExp(`^${extra}-status-\\d{8}T\\d{2}\\.log$`);
|
|
36
|
+
// Keep only three candidates while scanning, without loading an unbounded directory listing.
|
|
37
|
+
const selected = [];
|
|
38
|
+
let count = 0;
|
|
39
|
+
for await (const entry of await opendir(directory)) {
|
|
40
|
+
if (++count > 10000)
|
|
41
|
+
throw new Error("LOG_SCAN_LIMIT");
|
|
42
|
+
if (!pattern.test(entry.name) || !entry.isFile() || entry.isSymbolicLink())
|
|
43
|
+
continue;
|
|
44
|
+
selected.push(join(directory, entry.name));
|
|
45
|
+
selected.sort();
|
|
46
|
+
if (selected.length > 3)
|
|
47
|
+
selected.shift();
|
|
48
|
+
}
|
|
49
|
+
return selected;
|
|
50
|
+
}
|
|
51
|
+
async function read(path, start, maximum) {
|
|
52
|
+
const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
53
|
+
try {
|
|
54
|
+
const stat = await handle.stat();
|
|
55
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
56
|
+
throw new Error("LOG_NOT_REGULAR_FILE");
|
|
57
|
+
const offset = start === undefined ? Math.max(0, stat.size - maximum) : Math.min(start, stat.size);
|
|
58
|
+
const buffer = Buffer.alloc(Math.min(maximum, stat.size - offset));
|
|
59
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, offset);
|
|
60
|
+
return {
|
|
61
|
+
text: buffer.subarray(0, bytesRead).toString("utf8"),
|
|
62
|
+
offset,
|
|
63
|
+
end: offset + bytesRead,
|
|
64
|
+
identity: `${stat.dev}:${stat.ino}`,
|
|
65
|
+
size: stat.size
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
await handle.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export async function showExtraLogs(options) {
|
|
73
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(options.extra) ||
|
|
74
|
+
!Number.isInteger(options.lines) ||
|
|
75
|
+
options.lines < 1 ||
|
|
76
|
+
options.lines > 10000)
|
|
77
|
+
throw new Error("INVALID_ARGUMENTS");
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const stop = () => controller.abort();
|
|
80
|
+
if (options.follow) {
|
|
81
|
+
process.on("SIGINT", stop);
|
|
82
|
+
process.on("SIGTERM", stop);
|
|
83
|
+
options.signal?.addEventListener("abort", stop, { once: true });
|
|
84
|
+
if (options.signal?.aborted)
|
|
85
|
+
stop();
|
|
86
|
+
}
|
|
87
|
+
const cursors = new Map();
|
|
88
|
+
const emit = async (value) => {
|
|
89
|
+
const text = options.json
|
|
90
|
+
? JSON.stringify(value)
|
|
91
|
+
: `${value.time} ${value.level.toUpperCase()} ${value.extra_type} ${value.event}${Object.keys(value.data).length ? ` ${JSON.stringify(value.data)}` : ""}`;
|
|
92
|
+
await options.output(text, controller.signal);
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
const selected = await files(options.home, options.extra);
|
|
96
|
+
let remaining = MAX_READ;
|
|
97
|
+
const chunks = [];
|
|
98
|
+
// Spend the shared byte budget newest-first, then emit complete records in file order.
|
|
99
|
+
for (const path of [...selected].reverse()) {
|
|
100
|
+
try {
|
|
101
|
+
const value = await read(path, undefined, remaining);
|
|
102
|
+
remaining -= Buffer.byteLength(value.text);
|
|
103
|
+
const lines = value.text.split("\n");
|
|
104
|
+
if (value.offset > 0)
|
|
105
|
+
lines.shift();
|
|
106
|
+
const pending = lines.pop() ?? "";
|
|
107
|
+
cursors.set(path, {
|
|
108
|
+
offset: value.end,
|
|
109
|
+
identity: value.identity,
|
|
110
|
+
pending: pending.length <= MAX_LINE ? pending : "",
|
|
111
|
+
skipping: pending.length > MAX_LINE
|
|
112
|
+
});
|
|
113
|
+
chunks.unshift(lines
|
|
114
|
+
.map((line) => record(line, options.extra))
|
|
115
|
+
.filter((value) => !!value));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (error.code !== "ENOENT")
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const tail = chunks.flat().slice(-options.lines);
|
|
123
|
+
for (const value of tail)
|
|
124
|
+
await emit(value);
|
|
125
|
+
if (!tail.length)
|
|
126
|
+
options.errorOutput(`${options.extra}: no service status logs`);
|
|
127
|
+
if (!options.follow)
|
|
128
|
+
return;
|
|
129
|
+
while (!controller.signal.aborted) {
|
|
130
|
+
await delay(500, undefined, { signal: controller.signal });
|
|
131
|
+
const current = await files(options.home, options.extra);
|
|
132
|
+
for (const path of cursors.keys())
|
|
133
|
+
if (!current.includes(path))
|
|
134
|
+
cursors.delete(path);
|
|
135
|
+
let budget = MAX_READ;
|
|
136
|
+
for (const path of current) {
|
|
137
|
+
if (controller.signal.aborted || budget <= 0)
|
|
138
|
+
break;
|
|
139
|
+
try {
|
|
140
|
+
// Inode/size changes reset the cursor; preserve partial records across appends.
|
|
141
|
+
let cursor = cursors.get(path);
|
|
142
|
+
let value = await read(path, cursor?.offset ?? 0, budget);
|
|
143
|
+
if (!cursor || cursor.identity !== value.identity || cursor.offset > value.size) {
|
|
144
|
+
cursor = { offset: 0, identity: value.identity, pending: "", skipping: false };
|
|
145
|
+
if (value.offset !== 0)
|
|
146
|
+
value = await read(path, 0, budget);
|
|
147
|
+
}
|
|
148
|
+
budget -= Buffer.byteLength(value.text);
|
|
149
|
+
const lines = (cursor.pending + value.text).split("\n");
|
|
150
|
+
if (cursor.skipping) {
|
|
151
|
+
if (lines.length === 1) {
|
|
152
|
+
cursors.set(path, { ...cursor, offset: value.end });
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
lines.shift();
|
|
156
|
+
}
|
|
157
|
+
const pending = lines.pop() ?? "";
|
|
158
|
+
for (const line of lines) {
|
|
159
|
+
const value = record(line, options.extra);
|
|
160
|
+
if (value)
|
|
161
|
+
await emit(value);
|
|
162
|
+
}
|
|
163
|
+
cursors.set(path, {
|
|
164
|
+
offset: value.end,
|
|
165
|
+
identity: value.identity,
|
|
166
|
+
pending: pending.length <= MAX_LINE ? pending : "",
|
|
167
|
+
skipping: pending.length > MAX_LINE
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (error.code !== "ENOENT")
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (!controller.signal.aborted)
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
process.off("SIGINT", stop);
|
|
183
|
+
process.off("SIGTERM", stop);
|
|
184
|
+
options.signal?.removeEventListener("abort", stop);
|
|
185
|
+
}
|
|
186
|
+
}
|
package/apps/cli/dist/main.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export interface CliDependencies extends ServiceControlOptions {
|
|
|
7
7
|
sleep?: (ms: number) => Promise<void>;
|
|
8
8
|
/** CLI 入口选择前台组件的退出策略;仅确认命令类型,不表示服务 ready。 */
|
|
9
9
|
onComponentRun?: () => void;
|
|
10
|
+
logsSignal?: AbortSignal;
|
|
10
11
|
}
|
|
11
12
|
export declare function runCli(args: string[], dependencies?: CliDependencies): Promise<number>;
|
package/apps/cli/dist/main.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { once } from "node:events";
|
|
4
|
+
import { lstat, mkdir } from "node:fs/promises";
|
|
4
5
|
import { join, resolve } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { attachKernel, createKernelService, readAdapterConfigurations } from "@mininglamp-oss/octo-runtime-executor-kernel";
|
|
7
|
-
import { componentEndpoint, createExtraService
|
|
8
|
+
import { componentEndpoint, createExtraService } from "@mininglamp-oss/octo-runtime-host-sdk";
|
|
8
9
|
import { getExtra, readExtra, setExtra } from "./extras.js";
|
|
9
10
|
import { cliVersionInfo, helpText } from "./help.js";
|
|
10
11
|
import { runForegroundService } from "./foreground.js";
|
|
12
|
+
import { showExtraLogs } from "./logs.js";
|
|
13
|
+
import { StatusOutput } from "./status-output.js";
|
|
11
14
|
import { defaultRuntimeHome, runtimePaths, withSetupLock, readJsonFile } from "./state.js";
|
|
12
15
|
import { resolveServiceControl } from "./service-control.js";
|
|
13
|
-
import {
|
|
16
|
+
import { loadCliManifest } from "./module-manifest.js";
|
|
17
|
+
import { resolveAdapterModules } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
|
|
14
18
|
import { readExtraDirectory } from "@mininglamp-oss/octo-runtime-host-sdk";
|
|
15
19
|
import { describeError, formatStatus, recordStartupFailure, readStartupFailure } from "./output.js";
|
|
16
20
|
const components = ["executor-kernel", "channel-gateway"];
|
|
@@ -20,8 +24,11 @@ export async function runCli(args, dependencies = {}) {
|
|
|
20
24
|
let json = args.includes("--json");
|
|
21
25
|
let context = {};
|
|
22
26
|
let runningComponent;
|
|
27
|
+
let statusOutput;
|
|
23
28
|
try {
|
|
24
29
|
const { flags, positional } = parse(args);
|
|
30
|
+
if ((flags.lines !== undefined || flags.follow !== undefined) && positional[1] !== "logs")
|
|
31
|
+
throw new Error("INVALID_ARGUMENTS");
|
|
25
32
|
json = Boolean(flags.json);
|
|
26
33
|
if (flags.version || positional[0] === "version") {
|
|
27
34
|
output(cliVersionInfo);
|
|
@@ -37,16 +44,65 @@ export async function runCli(args, dependencies = {}) {
|
|
|
37
44
|
return 0;
|
|
38
45
|
}
|
|
39
46
|
const home = resolve(flags.home ?? defaultRuntimeHome(dependencies.env, dependencies.platform));
|
|
47
|
+
// Route logs before config/module/manager access so offline failures remain diagnosable.
|
|
48
|
+
if (positional[1] === "logs") {
|
|
49
|
+
const extra = positional[0];
|
|
50
|
+
context = { component: extra, action: "logs", logs: join(home, "logs", "extras") };
|
|
51
|
+
if (positional.length !== 2 ||
|
|
52
|
+
extra === "executor-kernel" ||
|
|
53
|
+
!/^[a-z][a-z0-9-]{0,63}$/.test(extra) ||
|
|
54
|
+
flags.autostart !== undefined)
|
|
55
|
+
throw new Error("INVALID_ARGUMENTS");
|
|
56
|
+
if (!builtinComponents.includes(extra)) {
|
|
57
|
+
const registration = await lstat(join(home, "extras", extra, "config.json")).catch((error) => {
|
|
58
|
+
if (error.code === "ENOENT")
|
|
59
|
+
return undefined;
|
|
60
|
+
throw error;
|
|
61
|
+
});
|
|
62
|
+
if (!registration?.isFile() || registration.isSymbolicLink())
|
|
63
|
+
throw new Error("EXTRA_NOT_FOUND");
|
|
64
|
+
}
|
|
65
|
+
if (flags.lines !== undefined && !/^[1-9][0-9]{0,4}$/.test(flags.lines))
|
|
66
|
+
throw new Error("INVALID_ARGUMENTS");
|
|
67
|
+
await showExtraLogs({
|
|
68
|
+
home,
|
|
69
|
+
extra,
|
|
70
|
+
lines: Number(flags.lines ?? 200),
|
|
71
|
+
follow: Boolean(flags.follow),
|
|
72
|
+
json,
|
|
73
|
+
output: dependencies.output ??
|
|
74
|
+
(async (line, signal) => {
|
|
75
|
+
if (!process.stdout.write(`${line}\n`))
|
|
76
|
+
await once(process.stdout, "drain", { signal });
|
|
77
|
+
}),
|
|
78
|
+
errorOutput: dependencies.errorOutput ??
|
|
79
|
+
((line) => {
|
|
80
|
+
process.stderr.write(`${line}\n`);
|
|
81
|
+
}),
|
|
82
|
+
signal: dependencies.logsSignal
|
|
83
|
+
});
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
40
86
|
const root = await readJsonFile(runtimePaths(home, "default").rootConfig);
|
|
41
87
|
const profile = flags.profile ?? root?.default_profile ?? "default";
|
|
42
88
|
const paths = runtimePaths(home, profile, dependencies.platform);
|
|
43
89
|
const endpoint = componentEndpoint(home, "executor-kernel", dependencies.platform);
|
|
44
90
|
context = { component: positional[0], action: positional[1], logs: join(home, "logs") };
|
|
45
91
|
const registrations = await readExtraDirectory(join(home, "extras"));
|
|
92
|
+
// 部署、停止及配置修复命令不启动模块,也不因尚未可读的启动清单而无法操作。
|
|
93
|
+
const needsManifest = positional[1] === "run" ||
|
|
94
|
+
(!builtinComponents.includes(positional[0]) &&
|
|
95
|
+
!registrations.some((entry) => entry.name === positional[0]) &&
|
|
96
|
+
["install", "start", "restart", "stop", "uninstall", "status"].includes(positional[1]));
|
|
97
|
+
const manifest = needsManifest
|
|
98
|
+
? await loadCliManifest(home, flags.manifest, dependencies.errorOutput ?? ((message) => process.stderr.write(`${message}\n`)))
|
|
99
|
+
: undefined;
|
|
46
100
|
const componentNames = [
|
|
47
101
|
...new Set([...components, ...registrations.map((entry) => entry.name)])
|
|
48
102
|
];
|
|
49
|
-
const isComponent = (name) => builtinComponents.includes(name) ||
|
|
103
|
+
const isComponent = (name) => builtinComponents.includes(name) ||
|
|
104
|
+
registrations.some((entry) => entry.name === name) ||
|
|
105
|
+
manifest?.extras.some((entry) => entry.name === name) === true;
|
|
50
106
|
if (positional.length === 1 && isComponent(positional[0])) {
|
|
51
107
|
output(helpText(positional[0]));
|
|
52
108
|
return 0;
|
|
@@ -159,41 +215,45 @@ export async function runCli(args, dependencies = {}) {
|
|
|
159
215
|
return 0;
|
|
160
216
|
}
|
|
161
217
|
if (action === "run") {
|
|
218
|
+
if (!manifest)
|
|
219
|
+
throw new Error("MANIFEST_INVALID");
|
|
162
220
|
runningComponent = component;
|
|
163
221
|
dependencies.onComponentRun?.();
|
|
164
222
|
await mkdir(home, { recursive: true, mode: 0o700 });
|
|
165
223
|
let handle;
|
|
166
224
|
if (component === "executor-kernel") {
|
|
167
|
-
// 显式清单不存在时在宿主边界保留 ENOENT,而非等待 worker 断开后丢失原因。
|
|
168
|
-
if (flags.manifest)
|
|
169
|
-
await access(resolve(flags.manifest));
|
|
170
|
-
await initializeCliOpenClaw(paths.profileRoot, dependencies.env ?? process.env);
|
|
171
|
-
await syncCliOpenClawToolEnvironment(paths.profileRoot, await readExtraDirectory(join(home, "extras")));
|
|
172
225
|
const config = await readJsonFile(paths.profileConfig);
|
|
173
226
|
handle = createKernelService({
|
|
174
227
|
endpoint,
|
|
175
228
|
stateDirectory: join(home, "run"),
|
|
176
|
-
workerEntrypoint: fileURLToPath(import.meta.
|
|
177
|
-
|
|
178
|
-
? resolve(flags.manifest)
|
|
179
|
-
: fileURLToPath(new URL("./default_adapters.json", import.meta.url)),
|
|
229
|
+
workerEntrypoint: fileURLToPath(new URL("./worker.js", import.meta.url)),
|
|
230
|
+
adapterModules: resolveAdapterModules(manifest),
|
|
180
231
|
adapterConfigurations: readAdapterConfigurations(paths.profileRoot, config?.adapters),
|
|
181
232
|
env: {
|
|
182
233
|
...(dependencies.env ?? process.env),
|
|
183
234
|
OCTO_RUNTIME_HOME: home,
|
|
235
|
+
OCTO_RUNTIME_CLI_PROFILE_ROOT: paths.profileRoot,
|
|
236
|
+
OCTO_RUNTIME_CLI_SYNC_OPENCLAW_TOOLS: "1",
|
|
184
237
|
OCTO_RUNTIME_LOGS_DIRECTORY: join(home, "logs")
|
|
185
238
|
}
|
|
186
239
|
});
|
|
187
240
|
}
|
|
188
241
|
else {
|
|
189
|
-
const registration =
|
|
242
|
+
const registration = await readExtra(paths, component, manifest);
|
|
190
243
|
const module = registration.enabled === false
|
|
191
244
|
? { name: registration.name, default: () => () => { } }
|
|
192
245
|
: await registration.load();
|
|
193
246
|
if (module.name !== registration.name)
|
|
194
247
|
throw new Error("EXTRA_NAME_MISMATCH");
|
|
248
|
+
if (!dependencies.output)
|
|
249
|
+
statusOutput = new StatusOutput(process.stdout);
|
|
195
250
|
handle = createExtraService({
|
|
196
|
-
logging: {
|
|
251
|
+
logging: {
|
|
252
|
+
directory: join(home, "logs"),
|
|
253
|
+
statusOutput: dependencies.output
|
|
254
|
+
? (record) => output(JSON.stringify(record))
|
|
255
|
+
: statusOutput.write
|
|
256
|
+
},
|
|
197
257
|
module,
|
|
198
258
|
config: registration.config,
|
|
199
259
|
dependencies: {},
|
|
@@ -202,7 +262,7 @@ export async function runCli(args, dependencies = {}) {
|
|
|
202
262
|
controlEndpoint: componentEndpoint(home, component, dependencies.platform)
|
|
203
263
|
});
|
|
204
264
|
}
|
|
205
|
-
return runForegroundService(handle, () => publish({ component, state: "ready" }, `${component}: ready`), async (error) => {
|
|
265
|
+
return await runForegroundService(handle, () => publish({ component, state: "ready" }, `${component}: ready`), async (error) => {
|
|
206
266
|
try {
|
|
207
267
|
await recordStartupFailure(join(home, "logs"), component, error);
|
|
208
268
|
}
|
|
@@ -292,7 +352,7 @@ export async function runCli(args, dependencies = {}) {
|
|
|
292
352
|
const failure = { ...describeError(error), ...context };
|
|
293
353
|
const code = failure.code;
|
|
294
354
|
if (json)
|
|
295
|
-
output(JSON.stringify({ error: failure }, null, 2));
|
|
355
|
+
output(JSON.stringify({ error: failure }, null, context.action === "logs" ? undefined : 2));
|
|
296
356
|
else
|
|
297
357
|
(dependencies.errorOutput ?? ((line) => process.stderr.write(`${line}\n`)))(`${[context.component, context.action].filter(Boolean).join(" ") || "octo-runtime"}: ${code}${failure.cause_code ? ` (${failure.cause_code})` : ""}\n${failure.message}${context.logs ? `\nLogs: ${context.logs}` : ""}`);
|
|
298
358
|
return ["SERVICE_NOT_INSTALLED", "NOT_INSTALLED"].includes(code)
|
|
@@ -305,6 +365,9 @@ export async function runCli(args, dependencies = {}) {
|
|
|
305
365
|
? 4
|
|
306
366
|
: 1;
|
|
307
367
|
}
|
|
368
|
+
finally {
|
|
369
|
+
statusOutput?.close();
|
|
370
|
+
}
|
|
308
371
|
}
|
|
309
372
|
async function componentStatus(home, component, platform, requireReady = false) {
|
|
310
373
|
const endpoint = componentEndpoint(home, component, platform);
|
|
@@ -372,11 +435,23 @@ function parse(args) {
|
|
|
372
435
|
}
|
|
373
436
|
const match = /^--([a-z-]+)(?:=(.*))?$/.exec(arg);
|
|
374
437
|
if (!match ||
|
|
375
|
-
![
|
|
438
|
+
![
|
|
439
|
+
"home",
|
|
440
|
+
"profile",
|
|
441
|
+
"manifest",
|
|
442
|
+
"json",
|
|
443
|
+
"help",
|
|
444
|
+
"version",
|
|
445
|
+
"autostart",
|
|
446
|
+
"lines",
|
|
447
|
+
"follow"
|
|
448
|
+
].includes(match[1]) ||
|
|
376
449
|
Object.hasOwn(flags, match[1]))
|
|
377
450
|
throw new Error("INVALID_ARGUMENTS");
|
|
378
451
|
const key = match[1];
|
|
379
|
-
flags[key] = ["json", "help", "version"].includes(key)
|
|
452
|
+
flags[key] = ["json", "help", "version", "follow"].includes(key)
|
|
453
|
+
? "true"
|
|
454
|
+
: (match[2] ?? args[++i]);
|
|
380
455
|
if (!flags[key] ||
|
|
381
456
|
flags[key].startsWith("--") ||
|
|
382
457
|
(key === "autostart" && !["true", "false"].includes(flags[key])))
|
|
@@ -61,5 +61,10 @@
|
|
|
61
61
|
"module": "@mininglamp-oss/octo-runtime-adapter-antigravity",
|
|
62
62
|
"export": "antigravityAdapter"
|
|
63
63
|
}
|
|
64
|
+
],
|
|
65
|
+
"extras": [
|
|
66
|
+
{ "name": "channel-gateway", "module": "@mininglamp-oss/octo-runtime-extra-channel-gateway" },
|
|
67
|
+
{ "name": "channel-wecom", "module": "@mininglamp-oss/octo-runtime-extra-channel-wecom" },
|
|
68
|
+
{ "name": "webui", "module": "@mininglamp-oss/octo-runtime-extra-webui" }
|
|
64
69
|
]
|
|
65
70
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ModuleManifest } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
|
|
2
|
+
/** CLI 独占默认目录与 Home 扩展策略;SDK 原生装配不读取这两份文件。 */
|
|
3
|
+
export declare function loadCliManifest(home: string, override?: string, report?: (message: string) => void): Promise<ModuleManifest>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { join, resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { mergeModuleManifests, readModuleManifest } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
|
|
4
|
+
/** CLI 独占默认目录与 Home 扩展策略;SDK 原生装配不读取这两份文件。 */
|
|
5
|
+
export async function loadCliManifest(home, override, report = (message) => console.error(message)) {
|
|
6
|
+
if (override)
|
|
7
|
+
return readModuleManifest(resolve(override), { legacy: true });
|
|
8
|
+
const builtin = await readModuleManifest(fileURLToPath(new URL("./manifest.json", import.meta.url)));
|
|
9
|
+
const extension = await readModuleManifest(join(home, "manifest.json"), { optional: true });
|
|
10
|
+
return mergeModuleManifests(builtin, extension, report);
|
|
11
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFile, mkdir, writeFile, rename, lstat, chmod, rm } from "node:fs/promises";
|
|
2
2
|
import { parseEnv, promisify } from "node:util";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
|
-
import { configuredCredential, validateImBots, octoCliEnvironment } from "@mininglamp-oss/octo-runtime-extra-channel-gateway";
|
|
5
4
|
import { randomUUID } from "node:crypto";
|
|
6
5
|
import { isIP } from "node:net";
|
|
7
6
|
import { homedir, networkInterfaces } from "node:os";
|
|
@@ -103,15 +102,40 @@ export async function syncCliOpenClawToolEnvironment(runtimeRoot, extras) {
|
|
|
103
102
|
if (!channel || channel.config.bots === undefined)
|
|
104
103
|
return { written: false, restartRequired: false };
|
|
105
104
|
const bots = channel.config.bots;
|
|
106
|
-
|
|
105
|
+
if (!Array.isArray(bots) || bots.length > 64)
|
|
106
|
+
throw new Error("IM_CONFIG_LIST_REQUIRED");
|
|
107
107
|
let values;
|
|
108
|
-
for (const
|
|
109
|
-
|
|
108
|
+
for (const value of bots) {
|
|
109
|
+
const bot = record(value);
|
|
110
|
+
if (!bot)
|
|
111
|
+
throw new Error("IM_CONFIG_INVALID");
|
|
112
|
+
if (bot.enabled === false || record(bot.adapter)?.adapter_type !== "openclaw")
|
|
110
113
|
continue;
|
|
111
|
-
const token =
|
|
114
|
+
const token = typeof bot.bf_token === "string" ? bot.bf_token.trim() : undefined;
|
|
112
115
|
if (!token)
|
|
113
116
|
throw new Error("IM_CREDENTIALS_REQUIRED");
|
|
114
|
-
|
|
117
|
+
// CLI 仅校验自己要写入的工具身份;完整 Bot 配置由启动后的 Extra 校验。
|
|
118
|
+
// 不为 Kernel 的 dotenv 同步导入整个业务 Extra,否则未启动 Extra 缺包也会阻塞 Kernel。
|
|
119
|
+
if (!/^bf_[A-Za-z0-9_-]+$/.test(token))
|
|
120
|
+
throw new Error("IM_BOT_TOKEN_INVALID");
|
|
121
|
+
let url;
|
|
122
|
+
try {
|
|
123
|
+
if (typeof bot.api_url !== "string")
|
|
124
|
+
throw new Error();
|
|
125
|
+
url = new URL(bot.api_url);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
throw new Error("IM_API_URL_INVALID");
|
|
129
|
+
}
|
|
130
|
+
if (url.username ||
|
|
131
|
+
url.password ||
|
|
132
|
+
url.search ||
|
|
133
|
+
url.hash ||
|
|
134
|
+
(url.protocol !== "https:" &&
|
|
135
|
+
!(url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))))
|
|
136
|
+
throw new Error("IM_API_URL_INVALID");
|
|
137
|
+
url.pathname = url.pathname.replace(/\/+$/, "").replace(/\/api$/, "");
|
|
138
|
+
values = { OCTO_BOT_TOKEN: token, OCTO_API_BASE_URL: url.href.replace(/\/+$/, "") };
|
|
115
139
|
}
|
|
116
140
|
if (!values)
|
|
117
141
|
return { written: false, restartRequired: false };
|