@mininglamp-oss/octo-runtime 1.1.0 → 1.1.2

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.
Files changed (149) hide show
  1. package/README.md +4 -2
  2. package/apps/cli/README.md +3 -1
  3. package/apps/cli/dist/build-info.json +2 -2
  4. package/apps/cli/dist/extras.d.ts +4 -2
  5. package/apps/cli/dist/extras.js +20 -21
  6. package/apps/cli/dist/help.js +3 -0
  7. package/apps/cli/dist/logs.d.ts +10 -0
  8. package/apps/cli/dist/logs.js +186 -0
  9. package/apps/cli/dist/main.d.ts +1 -0
  10. package/apps/cli/dist/main.js +94 -19
  11. package/apps/cli/dist/{default_adapters.json → manifest.json} +10 -0
  12. package/apps/cli/dist/module-manifest.d.ts +3 -0
  13. package/apps/cli/dist/module-manifest.js +11 -0
  14. package/apps/cli/dist/openclaw.js +30 -6
  15. package/apps/cli/dist/output.js +3 -0
  16. package/apps/cli/dist/playboard.js +10 -8
  17. package/apps/cli/dist/status-output.d.ts +10 -0
  18. package/apps/cli/dist/status-output.js +65 -0
  19. package/apps/cli/dist/worker.d.ts +1 -0
  20. package/apps/cli/dist/worker.js +18 -0
  21. package/dist/public/host.d.ts +1 -0
  22. package/node_modules/@mininglamp-oss/octo-runtime-acp-client/package.json +3 -3
  23. package/node_modules/@mininglamp-oss/octo-runtime-adapter-acp/package.json +4 -4
  24. package/node_modules/@mininglamp-oss/octo-runtime-adapter-antigravity/package.json +3 -3
  25. package/node_modules/@mininglamp-oss/octo-runtime-adapter-claude-code/package.json +3 -3
  26. package/node_modules/@mininglamp-oss/octo-runtime-adapter-codebuddy/package.json +4 -4
  27. package/node_modules/@mininglamp-oss/octo-runtime-adapter-codex/package.json +3 -3
  28. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.d.ts +54 -0
  29. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/adapter.js +399 -0
  30. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.d.ts +14 -0
  31. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/client.js +156 -0
  32. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.d.ts +29 -0
  33. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/index.js +273 -0
  34. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.d.ts +27 -0
  35. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/dist/interactions.js +161 -0
  36. package/node_modules/@mininglamp-oss/octo-runtime-adapter-grok-build/package.json +17 -0
  37. package/node_modules/@mininglamp-oss/octo-runtime-adapter-hermes/package.json +2 -2
  38. package/node_modules/@mininglamp-oss/octo-runtime-adapter-kimi-code/package.json +2 -2
  39. package/node_modules/@mininglamp-oss/octo-runtime-adapter-mcode/package.json +2 -2
  40. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.d.ts +24 -0
  41. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/compatibility.js +181 -0
  42. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.d.ts +9 -0
  43. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/gateway.js +24 -8
  44. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/dist/index.js +229 -225
  45. package/node_modules/@mininglamp-oss/octo-runtime-adapter-openclaw/package.json +3 -3
  46. package/node_modules/@mininglamp-oss/octo-runtime-adapter-opencode/package.json +4 -4
  47. package/node_modules/@mininglamp-oss/octo-runtime-adapter-qodercli/package.json +2 -2
  48. package/node_modules/@mininglamp-oss/octo-runtime-adapter-qoderclicn/package.json +2 -2
  49. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.d.ts +4 -0
  50. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/approval.js +5 -6
  51. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.d.ts +1 -0
  52. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/index.js +1 -0
  53. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.d.ts +33 -0
  54. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/module-manifest.js +118 -0
  55. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log-worker.js +7 -5
  56. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.d.ts +3 -0
  57. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/runtime-log.js +45 -10
  58. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.d.ts +11 -0
  59. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/dist/service-status.js +70 -0
  60. package/node_modules/@mininglamp-oss/octo-runtime-adapter-sdk/package.json +5 -3
  61. package/node_modules/@mininglamp-oss/octo-runtime-adapter-zcode-gui/package.json +3 -3
  62. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/build-info.json +2 -2
  63. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.d.ts +4 -2
  64. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/extras.js +20 -21
  65. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/help.js +3 -0
  66. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.d.ts +10 -0
  67. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/logs.js +186 -0
  68. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.d.ts +1 -0
  69. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/main.js +94 -19
  70. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/{default_adapters.json → manifest.json} +10 -0
  71. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.d.ts +3 -0
  72. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/module-manifest.js +11 -0
  73. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/openclaw.js +30 -6
  74. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/output.js +3 -0
  75. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/playboard.js +10 -8
  76. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.d.ts +10 -0
  77. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/status-output.js +65 -0
  78. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.d.ts +1 -0
  79. package/node_modules/@mininglamp-oss/octo-runtime-cli/dist/worker.js +18 -0
  80. package/node_modules/@mininglamp-oss/octo-runtime-cli/package.json +23 -9
  81. package/node_modules/@mininglamp-oss/octo-runtime-cross-service/package.json +2 -2
  82. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.d.ts +1 -1
  83. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/dist/model-input.js +13 -9
  84. package/node_modules/@mininglamp-oss/octo-runtime-executor-core/package.json +3 -3
  85. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.d.ts +16 -0
  86. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/heartbeat.js +59 -0
  87. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.d.ts +1 -0
  88. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/invocation-manifest.js +4 -1
  89. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.d.ts +6 -0
  90. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/manifest-adapters.js +88 -0
  91. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.d.ts +1 -2
  92. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/module-specifier.js +2 -9
  93. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.d.ts +7 -2
  94. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/process.js +138 -9
  95. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/runner.js +3 -92
  96. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/serve.js +36 -12
  97. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.d.ts +4 -1
  98. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/server.js +14 -6
  99. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/service.js +2 -1
  100. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.d.ts +2 -2
  101. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/dist/supervisor.js +57 -34
  102. package/node_modules/@mininglamp-oss/octo-runtime-executor-kernel/package.json +8 -16
  103. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
  104. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/application/im-channel.js +18 -18
  105. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/contracts.d.ts +1 -0
  106. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/execution/dispatcher.js +24 -0
  107. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/contexts.js +2 -2
  108. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.d.ts +5 -2
  109. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/mentions.js +19 -5
  110. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/dist/transport/normalize.js +1 -0
  111. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-gateway/package.json +6 -6
  112. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
  113. package/node_modules/@mininglamp-oss/octo-runtime-extra-channel-wecom/package.json +5 -5
  114. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/config-schema.json +2 -2
  115. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/index.js +1 -1
  116. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.d.ts +4 -1
  117. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/dist/store.js +54 -4
  118. package/node_modules/@mininglamp-oss/octo-runtime-extra-webui/package.json +2 -2
  119. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.d.ts +8 -1
  120. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-config.js +27 -12
  121. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/extra-service.js +39 -2
  122. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/dist/index.d.ts +1 -0
  123. package/node_modules/@mininglamp-oss/octo-runtime-host-sdk/package.json +4 -4
  124. package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +1 -1
  125. package/node_modules/import-meta-resolve/index.d.ts +19 -0
  126. package/node_modules/import-meta-resolve/index.d.ts.map +1 -0
  127. package/node_modules/import-meta-resolve/index.js +47 -0
  128. package/node_modules/import-meta-resolve/lib/errors.d.ts +24 -0
  129. package/node_modules/import-meta-resolve/lib/errors.d.ts.map +1 -0
  130. package/node_modules/import-meta-resolve/lib/errors.js +504 -0
  131. package/node_modules/import-meta-resolve/lib/get-format.d.ts +13 -0
  132. package/node_modules/import-meta-resolve/lib/get-format.d.ts.map +1 -0
  133. package/node_modules/import-meta-resolve/lib/get-format.js +159 -0
  134. package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts +31 -0
  135. package/node_modules/import-meta-resolve/lib/package-json-reader.d.ts.map +1 -0
  136. package/node_modules/import-meta-resolve/lib/package-json-reader.js +177 -0
  137. package/node_modules/import-meta-resolve/lib/resolve.d.ts +29 -0
  138. package/node_modules/import-meta-resolve/lib/resolve.d.ts.map +1 -0
  139. package/node_modules/import-meta-resolve/lib/resolve.js +1301 -0
  140. package/node_modules/import-meta-resolve/lib/utils.d.ts +6 -0
  141. package/node_modules/import-meta-resolve/lib/utils.d.ts.map +1 -0
  142. package/node_modules/import-meta-resolve/lib/utils.js +47 -0
  143. package/node_modules/import-meta-resolve/license +74 -0
  144. package/node_modules/import-meta-resolve/package.json +78 -0
  145. package/node_modules/import-meta-resolve/readme.md +252 -0
  146. package/package.json +30 -26
  147. package/packages/octo-runtime-extra-channel-gateway/config-schema.json +2 -2
  148. package/packages/octo-runtime-extra-channel-wecom/config-schema.json +2 -2
  149. package/packages/octo-runtime-extra-webui/config-schema.json +2 -2
@@ -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
+ }
@@ -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>;
@@ -1,16 +1,20 @@
1
1
  import net from "node:net";
2
2
  import { randomUUID } from "node:crypto";
3
- import { access, mkdir } from "node:fs/promises";
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, extraRegistration } from "@mininglamp-oss/octo-runtime-host-sdk";
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 { initializeCliOpenClaw, syncCliOpenClawToolEnvironment } from "./openclaw.js";
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) || registrations.some((entry) => entry.name === 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.resolve("@mininglamp-oss/octo-runtime-executor-kernel/runner")),
177
- manifest: flags.manifest
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 = extraRegistration(await readExtra(paths, component));
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: { directory: join(home, "logs") },
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
- !["home", "profile", "manifest", "json", "help", "version", "autostart"].includes(match[1]) ||
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) ? "true" : (match[2] ?? args[++i]);
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])))
@@ -16,6 +16,11 @@
16
16
  "module": "@mininglamp-oss/octo-runtime-adapter-codebuddy",
17
17
  "export": "codebuddyAdapter"
18
18
  },
19
+ {
20
+ "type": "grok-build",
21
+ "module": "@mininglamp-oss/octo-runtime-adapter-grok-build",
22
+ "export": "grokBuildAdapter"
23
+ },
19
24
  {
20
25
  "type": "hermes",
21
26
  "module": "@mininglamp-oss/octo-runtime-adapter-hermes",
@@ -61,5 +66,10 @@
61
66
  "module": "@mininglamp-oss/octo-runtime-adapter-antigravity",
62
67
  "export": "antigravityAdapter"
63
68
  }
69
+ ],
70
+ "extras": [
71
+ { "name": "channel-gateway", "module": "@mininglamp-oss/octo-runtime-extra-channel-gateway" },
72
+ { "name": "channel-wecom", "module": "@mininglamp-oss/octo-runtime-extra-channel-wecom" },
73
+ { "name": "webui", "module": "@mininglamp-oss/octo-runtime-extra-webui" }
64
74
  ]
65
75
  }
@@ -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
- validateImBots(bots);
105
+ if (!Array.isArray(bots) || bots.length > 64)
106
+ throw new Error("IM_CONFIG_LIST_REQUIRED");
107
107
  let values;
108
- for (const bot of bots) {
109
- if (bot.enabled === false || bot.adapter.adapter_type !== "openclaw")
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 = configuredCredential(bot);
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
- values = Object.fromEntries(octoCliEnvironment(bot.api_url, token).env_variables.map(({ key, value }) => [key, value]));
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 };
@@ -102,6 +102,9 @@ export function describeError(error, fallback = "COMMAND_FAILED") {
102
102
  SERVICE_NOT_INSTALLED: "Run this component's install command, then start it.",
103
103
  NOT_INSTALLED: "Run this component's install command, then start it.",
104
104
  EXTRA_NOT_REGISTERED: "Save this Extra's declaration with extraset before starting it.",
105
+ EXTRA_MANIFEST_DECLARATION_REQUIRED: "Add this Extra's name and module to the selected Home manifest.json (extras array), then retry.",
106
+ EXTRA_MODULE_MIGRATION_REQUIRED: "The saved module differs from manifest.json. Review both declarations, then save matching module text or omit module from the Extra configuration.",
107
+ MANIFEST_INVALID: "Check manifest.json: version must be 1 and adapters/extras must be valid arrays without duplicate keys.",
105
108
  MANAGER_UNAVAILABLE: "Run doctor and check the current user's native service manager.",
106
109
  COMPONENT_UNAVAILABLE: "Check component status and the service logs; the IPC or worker is not ready.",
107
110
  SERVICE_PROCESS_EXITED: "The service exited unexpectedly. Check its logs and configuration.",
@@ -1,7 +1,7 @@
1
1
  import { defaultRuntimeHome, runtimePaths, readJsonFile } from "./state.js";
2
- import { initializeCliOpenClaw } from "./openclaw.js";
2
+ import { loadCliManifest } from "./module-manifest.js";
3
+ import { resolveAdapterModules } from "@mininglamp-oss/octo-runtime-adapter-sdk/module-manifest";
3
4
  import { readAdapterConfigurations, createKernelService } from "@mininglamp-oss/octo-runtime-executor-kernel";
4
- import { resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { componentEndpoint } from "@mininglamp-oss/octo-runtime-host-sdk";
7
7
  import { mkdtemp, rm } from "node:fs/promises";
@@ -67,15 +67,17 @@ export async function runPlayboard(options) {
67
67
  const temporary = await mkdtemp(join(tmpdir(), "octo-playboard-"));
68
68
  try {
69
69
  const paths = runtimePaths(home, profile);
70
- await initializeCliOpenClaw(paths.profileRoot, options.env ?? process.env);
70
+ const manifest = await loadCliManifest(home, options.manifest);
71
71
  const configuration = await readJsonFile(paths.profileConfig);
72
72
  kernel = createKernelService({
73
73
  endpoint: process.platform === "win32" ? componentEndpoint(temporary) : join(temporary, "k.sock"),
74
- workerEntrypoint: fileURLToPath(import.meta.resolve("@mininglamp-oss/octo-runtime-executor-kernel/runner")),
75
- manifest: options.manifest
76
- ? resolve(options.manifest)
77
- : fileURLToPath(new URL("./default_adapters.json", import.meta.url)),
78
- env: options.env ?? process.env,
74
+ workerEntrypoint: fileURLToPath(new URL("./worker.js", import.meta.url)),
75
+ adapterModules: resolveAdapterModules(manifest),
76
+ env: {
77
+ ...(options.env ?? process.env),
78
+ OCTO_RUNTIME_CLI_PROFILE_ROOT: paths.profileRoot,
79
+ OCTO_RUNTIME_CLI_SYNC_OPENCLAW_TOOLS: "0"
80
+ },
79
81
  adapterConfigurations: readAdapterConfigurations(paths.profileRoot, configuration?.adapters)
80
82
  });
81
83
  await kernel.initialize({ signal: controller.signal });
@@ -0,0 +1,10 @@
1
+ import type { Writable } from "node:stream";
2
+ import { type ExtraStatusRecord } from "@mininglamp-oss/octo-runtime-adapter-sdk";
3
+ /** Bound stdout buffering without blocking Extras; report dropped records when the pipe drains. */
4
+ export declare class StatusOutput {
5
+ #private;
6
+ private readonly output;
7
+ constructor(output: Writable);
8
+ readonly write: (record: ExtraStatusRecord) => void;
9
+ readonly close: () => void;
10
+ }
@@ -0,0 +1,65 @@
1
+ import { extraStatusRecord } from "@mininglamp-oss/octo-runtime-adapter-sdk";
2
+ /** Bound stdout buffering without blocking Extras; report dropped records when the pipe drains. */
3
+ export class StatusOutput {
4
+ output;
5
+ #queue = [];
6
+ #bytes = 0;
7
+ #blocked = false;
8
+ #closed = false;
9
+ #dropped = 0;
10
+ #extra = "extra";
11
+ constructor(output) {
12
+ this.output = output;
13
+ output.on("error", this.close);
14
+ }
15
+ write = (record) => {
16
+ if (this.#closed)
17
+ return;
18
+ this.#extra = record.extra_type;
19
+ const line = `${JSON.stringify(record)}\n`;
20
+ if (this.#queue.length >= 128 || this.#bytes + Buffer.byteLength(line) > 1024 * 1024) {
21
+ this.#dropped = Math.min(Number.MAX_SAFE_INTEGER, this.#dropped + 1);
22
+ return;
23
+ }
24
+ this.#queue.push(line);
25
+ this.#bytes += Buffer.byteLength(line);
26
+ this.#flush();
27
+ };
28
+ #drain = () => {
29
+ this.#blocked = false;
30
+ this.#flush();
31
+ };
32
+ #flush() {
33
+ if (this.#closed || this.#blocked)
34
+ return;
35
+ while (this.#queue.length) {
36
+ const line = this.#queue.shift();
37
+ this.#bytes -= Buffer.byteLength(line);
38
+ try {
39
+ if (!this.output.write(line)) {
40
+ this.#blocked = true;
41
+ this.output.once("drain", this.#drain);
42
+ return;
43
+ }
44
+ }
45
+ catch {
46
+ this.close();
47
+ return;
48
+ }
49
+ }
50
+ if (this.#dropped) {
51
+ const record = extraStatusRecord(this.#extra, "warn", "service.output_dropped", {
52
+ dropped_count: this.#dropped
53
+ });
54
+ this.#dropped = 0;
55
+ this.write(record);
56
+ }
57
+ }
58
+ close = () => {
59
+ this.#closed = true;
60
+ this.#queue = [];
61
+ this.#bytes = 0;
62
+ this.output.off("drain", this.#drain);
63
+ this.output.off("error", this.close);
64
+ };
65
+ }