@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
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Optional metadata logging; bounded queues isolate filesystem writes in a dedicated worker. */
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
3
|
import { Worker } from "node:worker_threads";
|
|
4
|
+
import { extraStatusRecord } from "./service-status.js";
|
|
4
5
|
export const noopRuntimeLog = () => { };
|
|
5
6
|
const strings = new Set([
|
|
6
7
|
"run_id",
|
|
@@ -30,11 +31,15 @@ export function assertRuntimeLogging(options) {
|
|
|
30
31
|
if (options !== undefined &&
|
|
31
32
|
(!options || typeof options.directory !== "string" || !isAbsolute(options.directory)))
|
|
32
33
|
throw new TypeError("LOG_DIRECTORY_MUST_BE_ABSOLUTE");
|
|
34
|
+
if (options?.statusOutput !== undefined && typeof options.statusOutput !== "function")
|
|
35
|
+
throw new TypeError("LOG_STATUS_OUTPUT_INVALID");
|
|
33
36
|
}
|
|
34
37
|
/** F31:.octospec/tasks/gateway-august-feature-assessment/assessment.md */
|
|
35
38
|
export class RuntimeLogging {
|
|
36
39
|
#worker;
|
|
37
40
|
#pending = 0;
|
|
41
|
+
#statusPending = 0;
|
|
42
|
+
#statusOutput;
|
|
38
43
|
#closed = false;
|
|
39
44
|
#closing;
|
|
40
45
|
#done;
|
|
@@ -42,27 +47,31 @@ export class RuntimeLogging {
|
|
|
42
47
|
assertRuntimeLogging(options);
|
|
43
48
|
if (!options)
|
|
44
49
|
return;
|
|
50
|
+
this.#statusOutput = options.statusOutput;
|
|
45
51
|
try {
|
|
46
52
|
this.#worker = new Worker(new URL("./runtime-log-worker.js", import.meta.url), {
|
|
47
|
-
workerData: options
|
|
53
|
+
workerData: { directory: options.directory }
|
|
48
54
|
});
|
|
49
55
|
}
|
|
50
56
|
catch {
|
|
51
|
-
this.#closed = true;
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
59
|
this.#worker.on("message", (message) => {
|
|
55
|
-
if (message === "
|
|
56
|
-
|
|
60
|
+
if (typeof message === "object") {
|
|
61
|
+
if (message.written)
|
|
62
|
+
this.#statusPending = Math.max(0, this.#statusPending - 1);
|
|
63
|
+
else
|
|
64
|
+
this.#pending = Math.max(0, this.#pending - 1);
|
|
65
|
+
}
|
|
57
66
|
if (message === "closed")
|
|
58
67
|
this.#done?.();
|
|
59
68
|
});
|
|
60
69
|
this.#worker.on("error", () => {
|
|
61
|
-
this.#
|
|
70
|
+
this.#worker = undefined;
|
|
62
71
|
this.#done?.();
|
|
63
72
|
});
|
|
64
73
|
this.#worker.on("exit", () => {
|
|
65
|
-
this.#
|
|
74
|
+
this.#worker = undefined;
|
|
66
75
|
this.#done?.();
|
|
67
76
|
});
|
|
68
77
|
this.#worker.unref();
|
|
@@ -74,13 +83,39 @@ export class RuntimeLogging {
|
|
|
74
83
|
return (level, event, data) => this.#write("extra", name, level, event, data);
|
|
75
84
|
}
|
|
76
85
|
#write(source, extra, level, event, data) {
|
|
77
|
-
if (
|
|
86
|
+
if (this.#closed)
|
|
87
|
+
return;
|
|
88
|
+
if (extra) {
|
|
89
|
+
try {
|
|
90
|
+
const status = extraStatusRecord(extra, level, event, data);
|
|
91
|
+
if (status) {
|
|
92
|
+
// Sink and mirror fail independently; neither failure may affect service recovery.
|
|
93
|
+
try {
|
|
94
|
+
const result = this.#statusOutput?.(status);
|
|
95
|
+
// TypeScript permits async functions in void sinks; observe rejection without waiting.
|
|
96
|
+
if (result && typeof result.then === "function")
|
|
97
|
+
void Promise.resolve(result).catch(() => undefined);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* A host sink failure must not suppress the independent file mirror. */
|
|
101
|
+
}
|
|
102
|
+
if (this.#worker && this.#statusPending < 128) {
|
|
103
|
+
this.#worker.postMessage({ extra, status: true, line: `${JSON.stringify(status)}\n` });
|
|
104
|
+
this.#statusPending++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
/* Untrusted diagnostic getters must not escape into service execution. */
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!this.#worker || this.#pending >= 128)
|
|
78
113
|
return;
|
|
79
114
|
try {
|
|
80
115
|
if (!["info", "warn", "error"].includes(level) || !/^[a-z][a-z0-9_.-]{0,63}$/.test(event))
|
|
81
116
|
return;
|
|
82
117
|
const safe = {};
|
|
83
|
-
//
|
|
118
|
+
// Ignore unknown fields entirely instead of serializing or truncating arbitrary bodies.
|
|
84
119
|
for (const key of strings) {
|
|
85
120
|
const value = data?.[key];
|
|
86
121
|
if (typeof value === "string" &&
|
|
@@ -105,7 +140,7 @@ export class RuntimeLogging {
|
|
|
105
140
|
this.#pending++;
|
|
106
141
|
}
|
|
107
142
|
catch {
|
|
108
|
-
/*
|
|
143
|
+
/* Diagnostics cannot interrupt execution, including getter errors or an exited worker. */
|
|
109
144
|
}
|
|
110
145
|
}
|
|
111
146
|
close() {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RuntimeLogLevel } from "./runtime-log.js";
|
|
2
|
+
export interface ExtraStatusRecord {
|
|
3
|
+
time: string;
|
|
4
|
+
level: RuntimeLogLevel;
|
|
5
|
+
source: "extra";
|
|
6
|
+
extra_type: string;
|
|
7
|
+
event: string;
|
|
8
|
+
data: Record<string, string | number>;
|
|
9
|
+
}
|
|
10
|
+
/** Rebuild status records from a separate allowlist, including when reading persisted files. */
|
|
11
|
+
export declare function extraStatusRecord(extra: string, level: RuntimeLogLevel, event: string, data?: Readonly<Record<string, unknown>>, time?: string): ExtraStatusRecord | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const events = new Set([
|
|
2
|
+
"service.initializing",
|
|
3
|
+
"service.ready",
|
|
4
|
+
"service.closing",
|
|
5
|
+
"service.closed",
|
|
6
|
+
"service.failed",
|
|
7
|
+
"service.cleanup_incomplete",
|
|
8
|
+
"service.output_dropped",
|
|
9
|
+
"kernel.available",
|
|
10
|
+
"kernel.unavailable",
|
|
11
|
+
"channel.connecting",
|
|
12
|
+
"channel.connected",
|
|
13
|
+
"channel.disconnected",
|
|
14
|
+
"channel.connection_failed",
|
|
15
|
+
"channel.retry_started",
|
|
16
|
+
"channel.retry_scheduled",
|
|
17
|
+
"channel.stopped",
|
|
18
|
+
"channel.diagnostic"
|
|
19
|
+
]);
|
|
20
|
+
const states = new Set([
|
|
21
|
+
"created",
|
|
22
|
+
"initializing",
|
|
23
|
+
"ready",
|
|
24
|
+
"closing",
|
|
25
|
+
"closed",
|
|
26
|
+
"failed",
|
|
27
|
+
"connecting",
|
|
28
|
+
"online",
|
|
29
|
+
"retrying",
|
|
30
|
+
"stopped",
|
|
31
|
+
"available",
|
|
32
|
+
"unavailable"
|
|
33
|
+
]);
|
|
34
|
+
/** Rebuild status records from a separate allowlist, including when reading persisted files. */
|
|
35
|
+
export function extraStatusRecord(extra, level, event, data, time = new Date().toISOString()) {
|
|
36
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(extra) ||
|
|
37
|
+
!["info", "warn", "error"].includes(level) ||
|
|
38
|
+
!events.has(event))
|
|
39
|
+
return;
|
|
40
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(time) ||
|
|
41
|
+
!Number.isFinite(Date.parse(time)))
|
|
42
|
+
return;
|
|
43
|
+
if (event === "channel.diagnostic" && data?.code !== "IM_DISPOSE_FAILED")
|
|
44
|
+
return;
|
|
45
|
+
const safe = {};
|
|
46
|
+
for (const key of ["state", "code", "endpoint_id"]) {
|
|
47
|
+
const value = data?.[key];
|
|
48
|
+
if (typeof value !== "string")
|
|
49
|
+
continue;
|
|
50
|
+
if (key === "state"
|
|
51
|
+
? states.has(value)
|
|
52
|
+
: key === "endpoint_id"
|
|
53
|
+
? /^[a-f0-9]{64}$/.test(value)
|
|
54
|
+
: /^[A-Z][A-Z0-9_]{0,95}$/.test(value))
|
|
55
|
+
safe[key] = value;
|
|
56
|
+
}
|
|
57
|
+
for (const key of ["attempt", "delay_ms", "dropped_count"]) {
|
|
58
|
+
const value = data?.[key];
|
|
59
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0)
|
|
60
|
+
safe[key] = value;
|
|
61
|
+
}
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
time,
|
|
64
|
+
level,
|
|
65
|
+
source: "extra",
|
|
66
|
+
extra_type: extra,
|
|
67
|
+
event,
|
|
68
|
+
data: Object.freeze(safe)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mininglamp-oss/octo-runtime-adapter-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./dist/index.js",
|
|
8
|
-
"./process-cleanup": "./dist/process-cleanup.js"
|
|
8
|
+
"./process-cleanup": "./dist/process-cleanup.js",
|
|
9
|
+
"./module-manifest": "./dist/module-manifest.js"
|
|
9
10
|
},
|
|
10
11
|
"dependencies": {
|
|
11
|
-
"@mininglamp-oss/octo-runtime-protocol": "1.1.
|
|
12
|
+
"@mininglamp-oss/octo-runtime-protocol": "1.1.1",
|
|
13
|
+
"import-meta-resolve": "4.2.0"
|
|
12
14
|
},
|
|
13
15
|
"types": "./dist/index.d.ts",
|
|
14
16
|
"files": [
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mininglamp-oss/octo-runtime-adapter-zcode-gui",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./dist/index.js",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@mininglamp-oss/octo-runtime-protocol": "1.1.
|
|
9
|
-
"@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.
|
|
8
|
+
"@mininglamp-oss/octo-runtime-protocol": "1.1.1",
|
|
9
|
+
"@mininglamp-oss/octo-runtime-adapter-sdk": "1.1.1"
|
|
10
10
|
},
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"files": [
|
|
@@ -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>;
|
|
@@ -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
|
}
|
|
@@ -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>;
|