@actiondock/core 2.0.12 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -30
- package/dist/app/app.d.ts +73 -0
- package/dist/app/app.js +700 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +2 -0
- package/dist/app/types.d.ts +283 -0
- package/dist/app/types.js +1 -0
- package/dist/catalog/action-index.d.ts +1 -1
- package/dist/catalog/action-index.js +93 -0
- package/dist/catalog/action-resolver.d.ts +2 -2
- package/dist/catalog/action-resolver.js +95 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -0
- package/dist/catalog/location-registry.d.ts +5 -5
- package/dist/catalog/location-registry.js +138 -0
- package/dist/catalog/package-catalog.d.ts +2 -2
- package/dist/catalog/package-catalog.js +76 -0
- package/dist/catalog/types.d.ts +2 -2
- package/dist/catalog/types.js +1 -0
- package/dist/doctor/doctor.d.ts +2 -1
- package/dist/doctor/doctor.js +613 -0
- package/dist/doctor/index.d.ts +2 -2
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/types.js +1 -0
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +123 -0
- package/dist/execution/index.d.ts +2 -2
- package/dist/execution/index.js +2 -0
- package/dist/execution/service.d.ts +46 -41
- package/dist/execution/service.js +633 -0
- package/dist/execution/types.d.ts +59 -2
- package/dist/execution/types.js +1 -0
- package/dist/export/index.d.ts +1 -1
- package/dist/export/index.js +1 -0
- package/dist/export/templates.d.ts +45 -7
- package/dist/export/templates.js +631 -0
- package/dist/filter/index.d.ts +1 -1
- package/dist/filter/index.js +1 -0
- package/dist/filter/intent.js +123 -0
- package/dist/host/host.d.ts +55 -0
- package/dist/host/host.js +740 -0
- package/dist/host/index.d.ts +2 -0
- package/dist/host/index.js +2 -0
- package/dist/host/types.d.ts +81 -0
- package/dist/host/types.js +1 -0
- package/dist/index.d.ts +21 -16
- package/dist/index.js +20 -8458
- package/dist/ipc/diagnostic.d.ts +65 -0
- package/dist/ipc/diagnostic.js +129 -0
- package/dist/ipc/host.d.ts +7 -0
- package/dist/ipc/host.js +93 -0
- package/dist/ipc/index.d.ts +4 -0
- package/dist/ipc/index.js +4 -0
- package/dist/ipc/target.d.ts +56 -0
- package/dist/ipc/target.js +297 -0
- package/dist/ipc/types.d.ts +42 -0
- package/dist/ipc/types.js +1 -0
- package/dist/platform/default.d.ts +32 -0
- package/dist/platform/default.js +40 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/index.js +3 -0
- package/dist/platform/node-fs.d.ts +37 -0
- package/dist/platform/node-fs.js +81 -0
- package/dist/platform/types.d.ts +104 -0
- package/dist/platform/types.js +1 -0
- package/dist/profile/client.d.ts +58 -12
- package/dist/profile/client.js +467 -0
- package/dist/profile/index.d.ts +3 -3
- package/dist/profile/index.js +3 -0
- package/dist/profile/manager.d.ts +2 -2
- package/dist/profile/manager.js +294 -0
- package/dist/profile/types.d.ts +1 -1
- package/dist/profile/types.js +1 -0
- package/dist/project/closure.js +68 -0
- package/dist/project/digest.d.ts +29 -0
- package/dist/project/digest.js +324 -0
- package/dist/project/index.d.ts +10 -5
- package/dist/project/index.js +10 -0
- package/dist/project/init.d.ts +3 -4
- package/dist/project/init.js +175 -0
- package/dist/project/loader.d.ts +21 -27
- package/dist/project/loader.js +484 -0
- package/dist/project/lockfile.d.ts +55 -0
- package/dist/project/lockfile.js +175 -0
- package/dist/project/manifest.d.ts +6 -28
- package/dist/project/manifest.js +220 -0
- package/dist/project/resolver.d.ts +116 -0
- package/dist/project/resolver.js +376 -0
- package/dist/project/transactions.d.ts +103 -0
- package/dist/project/transactions.js +1203 -0
- package/dist/project/types-generator.d.ts +33 -0
- package/dist/project/types-generator.js +168 -0
- package/dist/project/types.d.ts +58 -82
- package/dist/project/types.js +1 -0
- package/dist/registry/index.d.ts +5 -2
- package/dist/registry/index.js +5 -0
- package/dist/registry/lock.d.ts +27 -0
- package/dist/registry/lock.js +93 -0
- package/dist/registry/registry.d.ts +32 -11
- package/dist/registry/registry.js +611 -0
- package/dist/registry/resolve.d.ts +62 -0
- package/dist/registry/resolve.js +195 -0
- package/dist/registry/scan.d.ts +38 -0
- package/dist/registry/scan.js +135 -0
- package/dist/registry/types.d.ts +1 -1
- package/dist/registry/types.js +1 -0
- package/dist/runtime/action-collection.d.ts +27 -0
- package/dist/runtime/action-collection.js +57 -0
- package/dist/runtime/clock.d.ts +0 -2
- package/dist/runtime/clock.js +17 -0
- package/dist/runtime/context.d.ts +16 -5
- package/dist/runtime/context.js +242 -0
- package/dist/runtime/env.d.ts +3 -1
- package/dist/runtime/env.js +159 -0
- package/dist/runtime/events.d.ts +46 -14
- package/dist/runtime/events.js +604 -0
- package/dist/runtime/execution-manager.d.ts +1 -1
- package/dist/runtime/execution-manager.js +66 -0
- package/dist/runtime/index.d.ts +10 -9
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/module-loader.d.ts +0 -8
- package/dist/runtime/module-loader.js +16 -0
- package/dist/runtime/process.d.ts +2 -4
- package/dist/runtime/process.js +146 -0
- package/dist/runtime/runner.d.ts +117 -10
- package/dist/runtime/runner.js +984 -0
- package/dist/runtime/standalone.d.ts +68 -19
- package/dist/runtime/standalone.js +536 -0
- package/dist/schema/validator.js +122 -0
- package/dist/server/body.js +93 -0
- package/dist/server/index.d.ts +5 -6
- package/dist/server/index.js +5 -0
- package/dist/server/routes/actions.d.ts +2 -2
- package/dist/server/routes/actions.js +298 -0
- package/dist/server/routes/common.d.ts +28 -17
- package/dist/server/routes/common.js +104 -0
- package/dist/server/routes/config.d.ts +2 -1
- package/dist/server/routes/config.js +173 -0
- package/dist/server/routes/doctor.d.ts +2 -2
- package/dist/server/routes/doctor.js +63 -0
- package/dist/server/routes/health.d.ts +2 -2
- package/dist/server/routes/health.js +35 -0
- package/dist/server/routes/index.d.ts +9 -9
- package/dist/server/routes/index.js +9 -0
- package/dist/server/routes/info.d.ts +4 -2
- package/dist/server/routes/info.js +158 -0
- package/dist/server/routes/playbooks.d.ts +2 -2
- package/dist/server/routes/playbooks.js +134 -0
- package/dist/server/routes/runs.d.ts +2 -2
- package/dist/server/routes/runs.js +343 -0
- package/dist/server/routes/state.d.ts +2 -1
- package/dist/server/routes/state.js +169 -0
- package/dist/server/security.d.ts +11 -4
- package/dist/server/security.js +101 -0
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +355 -0
- package/dist/server/types.d.ts +27 -8
- package/dist/server/types.js +1 -0
- package/dist/storage/data-dir-lock.d.ts +119 -0
- package/dist/storage/data-dir-lock.js +1016 -0
- package/dist/storage/driver.d.ts +4 -10
- package/dist/storage/driver.js +59 -0
- package/dist/storage/index.d.ts +30 -10
- package/dist/storage/index.js +98 -0
- package/dist/storage/lazy.d.ts +8 -0
- package/dist/storage/lazy.js +44 -0
- package/dist/storage/mask.d.ts +12 -5
- package/dist/storage/mask.js +48 -0
- package/dist/storage/params.d.ts +13 -0
- package/dist/storage/params.js +32 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +689 -0
- package/dist/storage/types.d.ts +57 -6
- package/dist/storage/types.js +14 -0
- package/dist/target/index.d.ts +4 -0
- package/dist/target/index.js +4 -0
- package/dist/target/local.d.ts +52 -0
- package/dist/target/local.js +343 -0
- package/dist/target/remote.d.ts +76 -0
- package/dist/target/remote.js +839 -0
- package/dist/target/target.d.ts +6 -0
- package/dist/target/target.js +35 -0
- package/dist/target/types.d.ts +246 -0
- package/dist/target/types.js +34 -0
- package/dist/utils/index.d.ts +22 -2
- package/dist/utils/index.js +180 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +4 -0
- package/package.json +6 -7
- package/dist/build/index.d.ts +0 -1
- package/dist/build/templates.d.ts +0 -19
- package/dist/server/runtime-registry.d.ts +0 -78
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { resolveEnvValue } from "./env.js";
|
|
3
|
+
import { DefaultProcessExecutor } from "./process.js";
|
|
4
|
+
/**
|
|
5
|
+
* 生产级配置解析器实现。
|
|
6
|
+
* 严格践行 5 层配置解析优先级链:
|
|
7
|
+
* 1. CLI 临时参数覆写 (Overrides)
|
|
8
|
+
* 2. 包级持久化存储 (Package SQLite: ~/.actiondock/data/<package-id>/runtime.db)
|
|
9
|
+
* 3. 全局共享持久化存储 (Global SQLite: ~/.actiondock/global.db)
|
|
10
|
+
* 4. 操作系统环境变量 (process.env: 显式绑定 / 包名前缀 / SNAKE_CASE / 类型转换)
|
|
11
|
+
* 5. 项目默认配置 (actiondock.json 中的 default 字段)
|
|
12
|
+
* 6. 代码级默认回退值 (fallback)
|
|
13
|
+
*/
|
|
14
|
+
export class RuntimeConfig {
|
|
15
|
+
overrides;
|
|
16
|
+
storage;
|
|
17
|
+
projectConfig;
|
|
18
|
+
globalStorage;
|
|
19
|
+
constructor(storage, overrides = {}, projectConfig, globalStorage) {
|
|
20
|
+
this.storage = storage;
|
|
21
|
+
this.overrides = new Map(Object.entries(overrides));
|
|
22
|
+
this.projectConfig = projectConfig;
|
|
23
|
+
this.globalStorage = globalStorage;
|
|
24
|
+
}
|
|
25
|
+
get(key, defaultValue) {
|
|
26
|
+
// 1. CLI 临时参数覆盖
|
|
27
|
+
if (this.overrides.has(key)) {
|
|
28
|
+
return this.overrides.get(key);
|
|
29
|
+
}
|
|
30
|
+
// 2. 本地项目 SQLite 存储
|
|
31
|
+
const stored = this.storage.getConfig(key);
|
|
32
|
+
if (stored !== undefined) {
|
|
33
|
+
return stored;
|
|
34
|
+
}
|
|
35
|
+
// 3. 全局 SQLite 存储 (~/.actiondock/global.db)
|
|
36
|
+
if (this.globalStorage) {
|
|
37
|
+
try {
|
|
38
|
+
const globalStored = this.globalStorage.getConfig(key);
|
|
39
|
+
if (globalStored !== undefined) {
|
|
40
|
+
return globalStored;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// 忽略全局存储读取异常
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 4. 环境变量(支持包名前缀、SNAKE_CASE 转换与类型自动推断)
|
|
48
|
+
const itemDef = this.projectConfig?.config?.[key];
|
|
49
|
+
const envResolved = resolveEnvValue(key, itemDef, this.projectConfig?.id);
|
|
50
|
+
if (envResolved !== undefined) {
|
|
51
|
+
return envResolved.value;
|
|
52
|
+
}
|
|
53
|
+
// 5. 项目声明的默认值 (actiondock.json)
|
|
54
|
+
if (itemDef?.default !== undefined) {
|
|
55
|
+
return itemDef.default;
|
|
56
|
+
}
|
|
57
|
+
// 6. 调用方传入的代码级回退默认值
|
|
58
|
+
return defaultValue;
|
|
59
|
+
}
|
|
60
|
+
has(key) {
|
|
61
|
+
if (this.overrides.has(key))
|
|
62
|
+
return true;
|
|
63
|
+
if (this.storage.getConfig(key) !== undefined)
|
|
64
|
+
return true;
|
|
65
|
+
if (this.globalStorage?.getConfig(key) !== undefined)
|
|
66
|
+
return true;
|
|
67
|
+
const itemDef = this.projectConfig?.config?.[key];
|
|
68
|
+
if (resolveEnvValue(key, itemDef, this.projectConfig?.id) !== undefined)
|
|
69
|
+
return true;
|
|
70
|
+
if (itemDef?.default !== undefined)
|
|
71
|
+
return true;
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 结构化解析指定配置项的来源与取值视图。
|
|
76
|
+
*
|
|
77
|
+
* 作为五层优先级链的唯一事实源读取视图,供 app 层 getConfig 安全视图直接委托,
|
|
78
|
+
* 避免重复实现解析链导致行为漂移。
|
|
79
|
+
*/
|
|
80
|
+
describe(key) {
|
|
81
|
+
// 1. CLI 临时参数覆写
|
|
82
|
+
if (this.overrides.has(key)) {
|
|
83
|
+
return { source: "overrides", value: this.overrides.get(key) };
|
|
84
|
+
}
|
|
85
|
+
// 2. 包级持久化存储
|
|
86
|
+
const stored = this.storage.getConfig(key);
|
|
87
|
+
if (stored !== undefined) {
|
|
88
|
+
return { source: "package", value: stored };
|
|
89
|
+
}
|
|
90
|
+
// 3. 全局持久化存储
|
|
91
|
+
if (this.globalStorage) {
|
|
92
|
+
try {
|
|
93
|
+
const globalStored = this.globalStorage.getConfig(key);
|
|
94
|
+
if (globalStored !== undefined) {
|
|
95
|
+
return { source: "global", value: globalStored };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// 忽略全局存储读取异常
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 4. 环境变量
|
|
103
|
+
const itemDef = this.projectConfig?.config?.[key];
|
|
104
|
+
const envResolved = resolveEnvValue(key, itemDef, this.projectConfig?.id);
|
|
105
|
+
if (envResolved !== undefined) {
|
|
106
|
+
return { source: "env", value: envResolved.value };
|
|
107
|
+
}
|
|
108
|
+
// 5. 项目声明的默认值
|
|
109
|
+
return { source: "default", value: itemDef?.default };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 生产级状态存储实现,将状态持久化到 SQLite 中,完整支持命名空间分段隔离与 TTL。
|
|
114
|
+
*/
|
|
115
|
+
export class RuntimeStateStore {
|
|
116
|
+
storage;
|
|
117
|
+
namespace;
|
|
118
|
+
constructor(storage, namespace = "") {
|
|
119
|
+
this.storage = storage;
|
|
120
|
+
this.namespace = namespace;
|
|
121
|
+
}
|
|
122
|
+
async get(key) {
|
|
123
|
+
if (this.namespace) {
|
|
124
|
+
return this.storage.getState(this.namespace, key);
|
|
125
|
+
}
|
|
126
|
+
const val = await this.storage.getState("", key);
|
|
127
|
+
if (val !== undefined) {
|
|
128
|
+
return val;
|
|
129
|
+
}
|
|
130
|
+
const found = await this.storage.findState(key);
|
|
131
|
+
return found?.value;
|
|
132
|
+
}
|
|
133
|
+
async set(key, value, ttl) {
|
|
134
|
+
return this.storage.setState(this.namespace, key, value, ttl);
|
|
135
|
+
}
|
|
136
|
+
async delete(key) {
|
|
137
|
+
return this.storage.deleteState(this.namespace, key);
|
|
138
|
+
}
|
|
139
|
+
async clear(prefix = "") {
|
|
140
|
+
return this.storage.clearState({
|
|
141
|
+
namespace: this.namespace,
|
|
142
|
+
prefix: prefix || undefined,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async keys(prefix = "") {
|
|
146
|
+
return this.storage.listStateKeys(this.namespace, prefix);
|
|
147
|
+
}
|
|
148
|
+
scope(namespace) {
|
|
149
|
+
const nextNs = this.namespace
|
|
150
|
+
? `${this.namespace}:${namespace}`
|
|
151
|
+
: namespace;
|
|
152
|
+
return new RuntimeStateStore(this.storage, nextNs);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 标准 Stderr 日志记录器实现。
|
|
157
|
+
* 遵循 Unix 哲学:所有诊断与追踪日志输出至 stderr,确保 stdout 纯净保留 JSON 信封。
|
|
158
|
+
*/
|
|
159
|
+
export class StderrLogger {
|
|
160
|
+
prefix;
|
|
161
|
+
constructor(prefix = "") {
|
|
162
|
+
this.prefix = prefix ? `[${prefix}] ` : "";
|
|
163
|
+
}
|
|
164
|
+
format(level, message, data) {
|
|
165
|
+
const time = new Date().toISOString().slice(11, 19);
|
|
166
|
+
const base = `[${time}] [${level.toUpperCase()}] ${this.prefix}${message}`;
|
|
167
|
+
if (data !== undefined) {
|
|
168
|
+
let formattedData;
|
|
169
|
+
try {
|
|
170
|
+
formattedData = typeof data === "object" && data !== null ? JSON.stringify(data) : String(data);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
try {
|
|
174
|
+
formattedData = String(data);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
formattedData = "[Unserializable Data]";
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return `${base} ${formattedData}`;
|
|
181
|
+
}
|
|
182
|
+
return base;
|
|
183
|
+
}
|
|
184
|
+
debug(message, data) {
|
|
185
|
+
process.stderr.write(this.format("debug", message, data) + "\n");
|
|
186
|
+
}
|
|
187
|
+
info(message, data) {
|
|
188
|
+
process.stderr.write(this.format("info", message, data) + "\n");
|
|
189
|
+
}
|
|
190
|
+
warn(message, data) {
|
|
191
|
+
process.stderr.write(this.format("warn", message, data) + "\n");
|
|
192
|
+
}
|
|
193
|
+
error(message, data) {
|
|
194
|
+
process.stderr.write(this.format("error", message, data) + "\n");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 构建并装配传递给 Action 的完整 ActionContext 运行时上下文。
|
|
199
|
+
*
|
|
200
|
+
* @param options 上下文构建参数
|
|
201
|
+
* @returns 组装完毕的 ActionContext 实例
|
|
202
|
+
*/
|
|
203
|
+
export function createActionContext(options) {
|
|
204
|
+
const config = new RuntimeConfig(options.storage, options.overrides, options.projectConfig, options.globalStorage);
|
|
205
|
+
const state = new RuntimeStateStore(options.storage);
|
|
206
|
+
const log = options.logger || new StderrLogger();
|
|
207
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
208
|
+
const currentRunId = options.runId || randomUUID();
|
|
209
|
+
const currentRootRunId = options.rootRunId || options.parentRunId || currentRunId;
|
|
210
|
+
const invoke = async (action, input) => {
|
|
211
|
+
if (typeof action !== "string" &&
|
|
212
|
+
(!action || typeof action !== "object" || typeof action.run === "function" || !("actionId" in action))) {
|
|
213
|
+
const err = new Error("INVALID_ACTION_REF: ctx.actions.invoke strictly accepts only ActionRef or string, passing ActionDefinition or function is prohibited");
|
|
214
|
+
err.code = "INVALID_ACTION_REF";
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
if (options.onActionInvoke) {
|
|
218
|
+
return (await options.onActionInvoke(action, input, currentRunId));
|
|
219
|
+
}
|
|
220
|
+
throw new Error("ActionInvoker not configured with an invocation delegate");
|
|
221
|
+
};
|
|
222
|
+
const invokerFn = (ref, input) => invoke(ref, input);
|
|
223
|
+
const invoker = Object.assign(invokerFn, { invoke });
|
|
224
|
+
const processApi = options.process || new DefaultProcessExecutor();
|
|
225
|
+
const progressApi = options.progress || {
|
|
226
|
+
report() { },
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
config,
|
|
230
|
+
state,
|
|
231
|
+
actions: invoker,
|
|
232
|
+
process: processApi,
|
|
233
|
+
log,
|
|
234
|
+
progress: progressApi,
|
|
235
|
+
signal,
|
|
236
|
+
run: {
|
|
237
|
+
id: currentRunId,
|
|
238
|
+
rootId: currentRootRunId,
|
|
239
|
+
parentId: options.parentRunId,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
package/dist/runtime/env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConfigItemDefinition, ConfigValueType } from "../project/types";
|
|
1
|
+
import type { ConfigItemDefinition, ConfigValueType } from "../project/types.js";
|
|
2
2
|
/**
|
|
3
3
|
* 从环境变量成功解析后的结构体。
|
|
4
4
|
*/
|
|
@@ -7,6 +7,8 @@ export interface ResolvedEnv {
|
|
|
7
7
|
value: unknown;
|
|
8
8
|
/** 命中的具体环境变量键名 */
|
|
9
9
|
envKey: string;
|
|
10
|
+
/** 命中的具体环境变量键名(别名) */
|
|
11
|
+
matchedKey: string;
|
|
10
12
|
/** 数据来源标识 */
|
|
11
13
|
source: "env";
|
|
12
14
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { getPackageSlug } from "../utils/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* 将任意命名格式的字符串转换为标准的大写蛇形命名(UPPER_SNAKE_CASE)。
|
|
4
|
+
* 兼容处理 camelCase、kebab-case、点号分隔、斜杠以及 @ 作用域符号。
|
|
5
|
+
*
|
|
6
|
+
* @param str 输入字符串,如 "apiKey"、"@team4u/github-tools"
|
|
7
|
+
* @returns 转换后的字符串,如 "API_KEY"、"TEAM4U_GITHUB_TOOLS"
|
|
8
|
+
*/
|
|
9
|
+
export function toSnakeUpperCase(str) {
|
|
10
|
+
return str
|
|
11
|
+
.replace(/^@/, "")
|
|
12
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
13
|
+
.replace(/[-.\s/@]+/g, "_")
|
|
14
|
+
.replace(/^_+|_+$/g, "")
|
|
15
|
+
.toUpperCase();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 当未显式声明类型时,根据默认值的值类型智能推断目标配置类型。
|
|
19
|
+
*
|
|
20
|
+
* @param defaultVal 默认值
|
|
21
|
+
* @returns 推断出的 ConfigValueType 或 undefined
|
|
22
|
+
*/
|
|
23
|
+
export function inferTypeFromDefault(defaultVal) {
|
|
24
|
+
if (defaultVal === undefined || defaultVal === null)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (typeof defaultVal === "boolean")
|
|
27
|
+
return "boolean";
|
|
28
|
+
if (typeof defaultVal === "number")
|
|
29
|
+
return "number";
|
|
30
|
+
if (Array.isArray(defaultVal))
|
|
31
|
+
return "array";
|
|
32
|
+
if (typeof defaultVal === "object")
|
|
33
|
+
return "object";
|
|
34
|
+
if (typeof defaultVal === "string")
|
|
35
|
+
return "string";
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 将从 process.env 获取的原始字符串安全转换为目标类型。
|
|
40
|
+
*
|
|
41
|
+
* 支持类型:
|
|
42
|
+
* - boolean: "true"/"1"/"yes"/"on" -> true; "false"/"0"/"no"/"off" -> false
|
|
43
|
+
* - number: 自动转换为数字,非数字时回退为原始字符串
|
|
44
|
+
* - object / array: 自动尝试 JSON.parse 反序列化
|
|
45
|
+
* - 自动探测: 当 targetType 未指定时,自动识别布尔值及 JSON 对象/数组
|
|
46
|
+
*
|
|
47
|
+
* @param rawValue process.env 中的原始字符串
|
|
48
|
+
* @param targetType 期望转换的目标类型
|
|
49
|
+
*/
|
|
50
|
+
export function coerceEnvValue(rawValue, targetType) {
|
|
51
|
+
if (targetType === "boolean") {
|
|
52
|
+
const lower = rawValue.trim().toLowerCase();
|
|
53
|
+
if (["true", "1", "yes", "on"].includes(lower))
|
|
54
|
+
return true;
|
|
55
|
+
if (["false", "0", "no", "off"].includes(lower))
|
|
56
|
+
return false;
|
|
57
|
+
return Boolean(rawValue);
|
|
58
|
+
}
|
|
59
|
+
if (targetType === "number") {
|
|
60
|
+
const trimmed = rawValue.trim();
|
|
61
|
+
const num = Number(trimmed);
|
|
62
|
+
return isNaN(num) ? rawValue : num;
|
|
63
|
+
}
|
|
64
|
+
if (targetType === "object" || targetType === "array") {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(rawValue);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return rawValue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (targetType === "string") {
|
|
73
|
+
return rawValue;
|
|
74
|
+
}
|
|
75
|
+
// 智能自动探测
|
|
76
|
+
const trimmed = rawValue.trim();
|
|
77
|
+
const lower = trimmed.toLowerCase();
|
|
78
|
+
if (lower === "true")
|
|
79
|
+
return true;
|
|
80
|
+
if (lower === "false")
|
|
81
|
+
return false;
|
|
82
|
+
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
|
|
83
|
+
(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(trimmed);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// 保持为字符串
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return rawValue;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 从操作系统环境变量 (process.env) 中解析配置值。
|
|
95
|
+
* 严格遵循多层回退候选链:
|
|
96
|
+
* 1. actiondock.json 中声明的显式环境变量绑定 (itemDef.env)
|
|
97
|
+
* 2. 带包名前缀的变量: `ACTIONDOCK_<PACKAGE>_<KEY>` 以及 `<PACKAGE>_<KEY>`
|
|
98
|
+
* 3. 标准大写蛇形变量: `<KEY>` (SNAKE_CASE)
|
|
99
|
+
* 4. 原始键名原样匹配
|
|
100
|
+
*
|
|
101
|
+
* @param key 配置键名
|
|
102
|
+
* @param itemDef 配置项元数据定义
|
|
103
|
+
* @param packageId 所属包 ID
|
|
104
|
+
* @returns 命中的 ResolvedEnv 对象或 undefined
|
|
105
|
+
*/
|
|
106
|
+
export function resolveEnvValue(key, itemDef, packageId) {
|
|
107
|
+
if (typeof process === "undefined" || !process.env) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const candidateKeys = new Set();
|
|
111
|
+
// 1. actiondock.json 中显式声明的 env 变量名
|
|
112
|
+
if (itemDef?.env) {
|
|
113
|
+
if (Array.isArray(itemDef.env)) {
|
|
114
|
+
for (const e of itemDef.env) {
|
|
115
|
+
if (typeof e === "string" && e.trim()) {
|
|
116
|
+
candidateKeys.add(e.trim());
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else if (typeof itemDef.env === "string" && itemDef.env.trim()) {
|
|
121
|
+
candidateKeys.add(itemDef.env.trim());
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// 2. 包名前缀的环境变量 (例如 ACTIONDOCK_GITHUB_TOOLS_API_KEY / GITHUB_TOOLS__API_KEY)
|
|
125
|
+
const snakeKey = toSnakeUpperCase(key);
|
|
126
|
+
if (packageId) {
|
|
127
|
+
const cleanPkg = toSnakeUpperCase(packageId);
|
|
128
|
+
candidateKeys.add(`ACTIONDOCK_${cleanPkg}_${snakeKey}`);
|
|
129
|
+
candidateKeys.add(`ACTIONDOCK_${cleanPkg}__${snakeKey}`);
|
|
130
|
+
candidateKeys.add(`${cleanPkg}_${snakeKey}`);
|
|
131
|
+
candidateKeys.add(`${cleanPkg}__${snakeKey}`);
|
|
132
|
+
const slug = toSnakeUpperCase(getPackageSlug(packageId));
|
|
133
|
+
if (slug && slug !== cleanPkg) {
|
|
134
|
+
candidateKeys.add(`ACTIONDOCK_${slug}_${snakeKey}`);
|
|
135
|
+
candidateKeys.add(`ACTIONDOCK_${slug}__${snakeKey}`);
|
|
136
|
+
candidateKeys.add(`${slug}_${snakeKey}`);
|
|
137
|
+
candidateKeys.add(`${slug}__${snakeKey}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 3. 标准大写蛇形变量名 (例如 API_KEY)
|
|
141
|
+
candidateKeys.add(snakeKey);
|
|
142
|
+
// 4. 原始键名 (例如 apiKey)
|
|
143
|
+
candidateKeys.add(key);
|
|
144
|
+
// 按优先级顺序依次检索环境变量
|
|
145
|
+
const effectiveType = itemDef?.type || inferTypeFromDefault(itemDef?.default);
|
|
146
|
+
for (const envKey of candidateKeys) {
|
|
147
|
+
const rawVal = process.env[envKey];
|
|
148
|
+
if (rawVal !== undefined) {
|
|
149
|
+
const coerced = coerceEnvValue(rawVal, effectiveType);
|
|
150
|
+
return {
|
|
151
|
+
value: coerced,
|
|
152
|
+
envKey,
|
|
153
|
+
matchedKey: envKey,
|
|
154
|
+
source: "env",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
package/dist/runtime/events.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { ExecutionEvent } from "@actiondock/sdk";
|
|
2
|
+
export interface EventSinkSubscribeOptions {
|
|
3
|
+
after?: number | string;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
maxQueueSize?: number;
|
|
6
|
+
}
|
|
2
7
|
export interface EventSink {
|
|
3
8
|
emit(event: ExecutionEvent): void;
|
|
4
|
-
subscribe(runId: string, options?:
|
|
5
|
-
after?: number;
|
|
6
|
-
signal?: AbortSignal;
|
|
7
|
-
}): AsyncIterable<ExecutionEvent>;
|
|
9
|
+
subscribe(runId: string, options?: EventSinkSubscribeOptions): AsyncIterable<ExecutionEvent>;
|
|
8
10
|
clear(runId: string): void;
|
|
11
|
+
close?(): void;
|
|
9
12
|
}
|
|
10
13
|
export interface InMemoryEventSinkOptions {
|
|
11
14
|
/** 单个运行最大缓冲事件数(默认 1024) */
|
|
@@ -14,14 +17,18 @@ export interface InMemoryEventSinkOptions {
|
|
|
14
17
|
maxBytesPerRun?: number;
|
|
15
18
|
/** 全局最大缓存运行数(默认 500) */
|
|
16
19
|
maxRuns?: number;
|
|
20
|
+
/** 慢订阅者事件队列背压有界上限(默认 1024) */
|
|
21
|
+
maxSubscriberQueueSize?: number;
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
24
|
* 进程内有界事件缓冲区实现。
|
|
20
25
|
* 遵循设计规范:
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
26
|
+
* - 单运行双配额保护:上限 1024 条事件或 1MiB 字节数,优先淘汰日志与进度,保护终态。
|
|
27
|
+
* - 全局有界回收:限制最大运行数(默认 500),溢出时优先淘汰已完成运行。
|
|
28
|
+
* - 原子订阅衔接:先注册实时队列再回放历史快照,去重并杜绝并发丢事件窗口。
|
|
29
|
+
* - 严格生命周期清理:迭代退出或中断时彻底注销监听器与 AbortSignal 事件。
|
|
30
|
+
* - 慢订阅者背压保护:队列超出上限时推送终止事件并切断通道,主运行不受阻。
|
|
31
|
+
* - 游标断点续传:支持全局持久化 eventId 与序列号,过期游标返回 EVENT_CURSOR_EXPIRED。
|
|
25
32
|
*/
|
|
26
33
|
export declare class InMemoryEventSink implements EventSink {
|
|
27
34
|
private runs;
|
|
@@ -29,15 +36,38 @@ export declare class InMemoryEventSink implements EventSink {
|
|
|
29
36
|
private wakeups;
|
|
30
37
|
private evictions;
|
|
31
38
|
private evictedRuns;
|
|
39
|
+
private globalEventSeq;
|
|
32
40
|
private maxEventsPerRun;
|
|
33
41
|
private maxBytesPerRun;
|
|
34
42
|
private maxRuns;
|
|
43
|
+
private maxSubscriberQueueSize;
|
|
35
44
|
constructor(options?: InMemoryEventSinkOptions);
|
|
36
45
|
emit(event: ExecutionEvent): void;
|
|
37
|
-
subscribe(runId: string, options?:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
subscribe(runId: string, options?: EventSinkSubscribeOptions): AsyncIterable<ExecutionEvent>;
|
|
47
|
+
/**
|
|
48
|
+
* 断言请求游标仍处于保留窗口内(过期则抛出 EVENT_CURSOR_EXPIRED 错误)。
|
|
49
|
+
*/
|
|
50
|
+
private assertCursorRetained;
|
|
51
|
+
/**
|
|
52
|
+
* 记录已交付事件并推进游标(已交付序号与最后确认游标)。
|
|
53
|
+
*/
|
|
54
|
+
private trackDeliveredEvent;
|
|
55
|
+
/**
|
|
56
|
+
* 终止谓词:finish 事件或背压终止事件交付后订阅应立即结束。
|
|
57
|
+
*/
|
|
58
|
+
private isTerminalDelivery;
|
|
59
|
+
/**
|
|
60
|
+
* 从实时队列取出下一条应交付的事件(跳过已交付序号),并同步推进游标。
|
|
61
|
+
*/
|
|
62
|
+
private takeNextDeliverable;
|
|
63
|
+
/**
|
|
64
|
+
* 挂起等待下一条实时事件、通道终止或运行淘汰唤醒(严格注销监听避免泄漏)。
|
|
65
|
+
*/
|
|
66
|
+
private waitForNextEvent;
|
|
67
|
+
/**
|
|
68
|
+
* 手动修剪已终态运行中早于指定序列号的历史事件(用于模拟过期清理)。
|
|
69
|
+
*/
|
|
70
|
+
pruneEvents(runId: string, upToSequence: number): void;
|
|
41
71
|
clear(runId: string): void;
|
|
42
72
|
/**
|
|
43
73
|
* 获取指定运行的事件统计信息(调试与测试用)。
|
|
@@ -56,6 +86,8 @@ export declare class InMemoryEventSink implements EventSink {
|
|
|
56
86
|
* 优先淘汰已终态运行;若无终态运行则淘汰最旧活动运行,严格保证 runs.size <= maxRuns。
|
|
57
87
|
*/
|
|
58
88
|
private evictOldestRun;
|
|
89
|
+
/**
|
|
90
|
+
* 关闭事件接收器并唤醒所有活跃订阅者退出。
|
|
91
|
+
*/
|
|
92
|
+
close(): void;
|
|
59
93
|
}
|
|
60
|
-
export declare function getDefaultEventSink(): EventSink;
|
|
61
|
-
export declare function setDefaultEventSink(sink: EventSink): void;
|