@actiondock/core 2.0.11 → 2.2.0

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 (258) hide show
  1. package/README.md +61 -30
  2. package/dist/app/app.d.ts +73 -0
  3. package/dist/app/app.js +700 -0
  4. package/dist/app/index.d.ts +2 -0
  5. package/dist/app/index.js +2 -0
  6. package/dist/app/types.d.ts +283 -0
  7. package/dist/app/types.js +1 -0
  8. package/dist/catalog/action-index.d.ts +9 -0
  9. package/dist/catalog/action-index.js +93 -0
  10. package/dist/catalog/action-resolver.d.ts +20 -0
  11. package/dist/catalog/action-resolver.js +95 -0
  12. package/dist/catalog/index.d.ts +5 -0
  13. package/dist/catalog/index.js +5 -0
  14. package/dist/catalog/location-registry.d.ts +15 -0
  15. package/dist/catalog/location-registry.js +138 -0
  16. package/dist/catalog/package-catalog.d.ts +7 -0
  17. package/dist/catalog/package-catalog.js +76 -0
  18. package/dist/catalog/types.d.ts +68 -0
  19. package/dist/catalog/types.js +1 -0
  20. package/dist/doctor/doctor.d.ts +7 -0
  21. package/dist/doctor/doctor.js +613 -0
  22. package/dist/doctor/index.d.ts +2 -0
  23. package/dist/doctor/index.js +2 -0
  24. package/dist/doctor/types.d.ts +23 -0
  25. package/dist/doctor/types.js +1 -0
  26. package/dist/errors.d.ts +122 -0
  27. package/dist/errors.js +123 -0
  28. package/dist/execution/index.d.ts +2 -0
  29. package/dist/execution/index.js +2 -0
  30. package/dist/execution/service.d.ts +79 -0
  31. package/dist/execution/service.js +633 -0
  32. package/dist/execution/types.d.ts +131 -0
  33. package/dist/execution/types.js +1 -0
  34. package/dist/export/index.d.ts +1 -0
  35. package/dist/export/index.js +1 -0
  36. package/dist/export/templates.d.ts +71 -0
  37. package/dist/export/templates.js +621 -0
  38. package/dist/filter/index.d.ts +1 -0
  39. package/dist/filter/index.js +1 -0
  40. package/dist/filter/intent.d.ts +33 -0
  41. package/dist/filter/intent.js +123 -0
  42. package/dist/host/host.d.ts +55 -0
  43. package/dist/host/host.js +740 -0
  44. package/dist/host/index.d.ts +2 -0
  45. package/dist/host/index.js +2 -0
  46. package/dist/host/types.d.ts +81 -0
  47. package/dist/host/types.js +1 -0
  48. package/dist/index.d.ts +21 -0
  49. package/dist/index.js +20 -0
  50. package/dist/ipc/diagnostic.d.ts +65 -0
  51. package/dist/ipc/diagnostic.js +129 -0
  52. package/dist/ipc/host.d.ts +7 -0
  53. package/dist/ipc/host.js +93 -0
  54. package/dist/ipc/index.d.ts +4 -0
  55. package/dist/ipc/index.js +4 -0
  56. package/dist/ipc/target.d.ts +56 -0
  57. package/dist/ipc/target.js +297 -0
  58. package/dist/ipc/types.d.ts +42 -0
  59. package/dist/ipc/types.js +1 -0
  60. package/dist/platform/default.d.ts +32 -0
  61. package/dist/platform/default.js +40 -0
  62. package/dist/platform/index.d.ts +3 -0
  63. package/dist/platform/index.js +3 -0
  64. package/dist/platform/node-fs.d.ts +37 -0
  65. package/dist/platform/node-fs.js +81 -0
  66. package/dist/platform/types.d.ts +104 -0
  67. package/dist/platform/types.js +1 -0
  68. package/dist/profile/client.d.ts +162 -0
  69. package/dist/profile/client.js +467 -0
  70. package/dist/profile/index.d.ts +3 -0
  71. package/dist/profile/index.js +3 -0
  72. package/dist/profile/manager.d.ts +47 -0
  73. package/dist/profile/manager.js +294 -0
  74. package/dist/profile/types.d.ts +61 -0
  75. package/dist/profile/types.js +1 -0
  76. package/dist/project/closure.d.ts +25 -0
  77. package/dist/project/closure.js +68 -0
  78. package/dist/project/digest.d.ts +29 -0
  79. package/dist/project/digest.js +324 -0
  80. package/dist/project/index.d.ts +10 -0
  81. package/dist/project/index.js +10 -0
  82. package/dist/project/init.d.ts +26 -0
  83. package/dist/project/init.js +175 -0
  84. package/dist/project/loader.d.ts +99 -0
  85. package/dist/project/loader.js +484 -0
  86. package/dist/project/lockfile.d.ts +55 -0
  87. package/dist/project/lockfile.js +175 -0
  88. package/dist/project/manifest.d.ts +22 -0
  89. package/dist/project/manifest.js +220 -0
  90. package/dist/project/resolver.d.ts +116 -0
  91. package/dist/project/resolver.js +376 -0
  92. package/dist/project/transactions.d.ts +103 -0
  93. package/dist/project/transactions.js +1203 -0
  94. package/dist/project/types-generator.d.ts +33 -0
  95. package/dist/project/types-generator.js +168 -0
  96. package/dist/project/types.d.ts +107 -0
  97. package/dist/project/types.js +1 -0
  98. package/dist/registry/index.d.ts +5 -0
  99. package/dist/registry/index.js +5 -0
  100. package/dist/registry/lock.d.ts +27 -0
  101. package/dist/registry/lock.js +93 -0
  102. package/dist/registry/registry.d.ts +45 -0
  103. package/dist/registry/registry.js +611 -0
  104. package/dist/registry/resolve.d.ts +62 -0
  105. package/dist/registry/resolve.js +195 -0
  106. package/dist/registry/scan.d.ts +38 -0
  107. package/dist/registry/scan.js +135 -0
  108. package/dist/registry/types.d.ts +117 -0
  109. package/dist/registry/types.js +1 -0
  110. package/dist/runtime/action-collection.d.ts +27 -0
  111. package/dist/runtime/action-collection.js +57 -0
  112. package/dist/runtime/clock.d.ts +19 -0
  113. package/dist/runtime/clock.js +17 -0
  114. package/dist/runtime/context.d.ts +86 -0
  115. package/dist/runtime/context.js +242 -0
  116. package/dist/runtime/env.d.ts +56 -0
  117. package/dist/runtime/env.js +159 -0
  118. package/dist/runtime/events.d.ts +93 -0
  119. package/dist/runtime/events.js +604 -0
  120. package/dist/runtime/execution-manager.d.ts +48 -0
  121. package/dist/runtime/execution-manager.js +66 -0
  122. package/dist/runtime/index.d.ts +10 -0
  123. package/dist/runtime/index.js +10 -0
  124. package/dist/runtime/module-loader.d.ts +34 -0
  125. package/dist/runtime/module-loader.js +16 -0
  126. package/dist/runtime/process.d.ts +9 -0
  127. package/dist/runtime/process.js +146 -0
  128. package/dist/runtime/runner.d.ts +293 -0
  129. package/dist/runtime/runner.js +984 -0
  130. package/dist/runtime/standalone.d.ts +89 -0
  131. package/dist/runtime/standalone.js +536 -0
  132. package/dist/schema/validator.d.ts +28 -0
  133. package/dist/schema/validator.js +116 -0
  134. package/dist/server/body.d.ts +40 -0
  135. package/dist/server/body.js +93 -0
  136. package/dist/server/index.d.ts +5 -0
  137. package/dist/server/index.js +5 -0
  138. package/dist/server/routes/actions.d.ts +5 -0
  139. package/dist/server/routes/actions.js +298 -0
  140. package/dist/server/routes/common.d.ts +47 -0
  141. package/dist/server/routes/common.js +104 -0
  142. package/dist/server/routes/config.d.ts +6 -0
  143. package/dist/server/routes/config.js +173 -0
  144. package/dist/server/routes/doctor.d.ts +5 -0
  145. package/dist/server/routes/doctor.js +63 -0
  146. package/dist/server/routes/health.d.ts +5 -0
  147. package/dist/server/routes/health.js +35 -0
  148. package/dist/server/routes/index.d.ts +9 -0
  149. package/dist/server/routes/index.js +9 -0
  150. package/dist/server/routes/info.d.ts +7 -0
  151. package/dist/server/routes/info.js +158 -0
  152. package/dist/server/routes/playbooks.d.ts +5 -0
  153. package/dist/server/routes/playbooks.js +134 -0
  154. package/dist/server/routes/runs.d.ts +5 -0
  155. package/dist/server/routes/runs.js +343 -0
  156. package/dist/server/routes/state.d.ts +6 -0
  157. package/dist/server/routes/state.js +169 -0
  158. package/dist/server/security.d.ts +44 -0
  159. package/dist/server/security.js +101 -0
  160. package/dist/server/server.d.ts +15 -0
  161. package/dist/server/server.js +355 -0
  162. package/dist/server/types.d.ts +78 -0
  163. package/dist/server/types.js +1 -0
  164. package/dist/storage/data-dir-lock.d.ts +119 -0
  165. package/dist/storage/data-dir-lock.js +1016 -0
  166. package/dist/storage/driver.d.ts +9 -0
  167. package/dist/storage/driver.js +59 -0
  168. package/dist/storage/index.d.ts +64 -0
  169. package/dist/storage/index.js +98 -0
  170. package/dist/storage/lazy.d.ts +8 -0
  171. package/dist/storage/lazy.js +44 -0
  172. package/dist/storage/mask.d.ts +26 -0
  173. package/dist/storage/mask.js +48 -0
  174. package/dist/storage/params.d.ts +13 -0
  175. package/dist/storage/params.js +32 -0
  176. package/dist/storage/sqlite.d.ts +79 -0
  177. package/dist/storage/sqlite.js +689 -0
  178. package/dist/storage/types.d.ts +172 -0
  179. package/dist/storage/types.js +14 -0
  180. package/dist/target/index.d.ts +4 -0
  181. package/dist/target/index.js +4 -0
  182. package/dist/target/local.d.ts +52 -0
  183. package/dist/target/local.js +343 -0
  184. package/dist/target/remote.d.ts +76 -0
  185. package/dist/target/remote.js +839 -0
  186. package/dist/target/target.d.ts +6 -0
  187. package/dist/target/target.js +35 -0
  188. package/dist/target/types.d.ts +246 -0
  189. package/dist/target/types.js +34 -0
  190. package/dist/utils/index.d.ts +49 -0
  191. package/dist/utils/index.js +180 -0
  192. package/dist/version.d.ts +4 -0
  193. package/dist/version.js +4 -0
  194. package/package.json +13 -13
  195. package/src/build/index.ts +0 -1
  196. package/src/build/templates.ts +0 -59
  197. package/src/catalog/action-index.ts +0 -92
  198. package/src/catalog/action-resolver.ts +0 -120
  199. package/src/catalog/index.ts +0 -5
  200. package/src/catalog/location-registry.ts +0 -134
  201. package/src/catalog/package-catalog.ts +0 -87
  202. package/src/catalog/types.ts +0 -75
  203. package/src/doctor/doctor.ts +0 -419
  204. package/src/doctor/index.ts +0 -2
  205. package/src/doctor/types.ts +0 -25
  206. package/src/execution/index.ts +0 -2
  207. package/src/execution/service.ts +0 -322
  208. package/src/execution/types.ts +0 -73
  209. package/src/export/index.ts +0 -1
  210. package/src/export/templates.ts +0 -590
  211. package/src/filter/index.ts +0 -1
  212. package/src/filter/intent.ts +0 -154
  213. package/src/index.ts +0 -16
  214. package/src/profile/client.ts +0 -552
  215. package/src/profile/index.ts +0 -3
  216. package/src/profile/manager.ts +0 -341
  217. package/src/profile/types.ts +0 -71
  218. package/src/project/index.ts +0 -4
  219. package/src/project/init.ts +0 -242
  220. package/src/project/loader.ts +0 -479
  221. package/src/project/manifest.ts +0 -299
  222. package/src/project/types.ts +0 -141
  223. package/src/registry/index.ts +0 -2
  224. package/src/registry/registry.ts +0 -940
  225. package/src/registry/types.ts +0 -127
  226. package/src/runtime/clock.ts +0 -41
  227. package/src/runtime/context.ts +0 -259
  228. package/src/runtime/env.ts +0 -172
  229. package/src/runtime/events.ts +0 -142
  230. package/src/runtime/execution-manager.ts +0 -74
  231. package/src/runtime/index.ts +0 -10
  232. package/src/runtime/module-loader.ts +0 -67
  233. package/src/runtime/process.ts +0 -245
  234. package/src/runtime/runner.ts +0 -527
  235. package/src/runtime/standalone.ts +0 -429
  236. package/src/schema/validator.ts +0 -61
  237. package/src/server/body.ts +0 -112
  238. package/src/server/index.ts +0 -7
  239. package/src/server/routes/actions.ts +0 -262
  240. package/src/server/routes/common.ts +0 -126
  241. package/src/server/routes/config.ts +0 -147
  242. package/src/server/routes/doctor.ts +0 -29
  243. package/src/server/routes/health.ts +0 -40
  244. package/src/server/routes/index.ts +0 -9
  245. package/src/server/routes/info.ts +0 -221
  246. package/src/server/routes/playbooks.ts +0 -109
  247. package/src/server/routes/runs.ts +0 -301
  248. package/src/server/routes/state.ts +0 -133
  249. package/src/server/runtime-registry.ts +0 -115
  250. package/src/server/security.ts +0 -115
  251. package/src/server/server.ts +0 -251
  252. package/src/server/types.ts +0 -57
  253. package/src/storage/driver.ts +0 -126
  254. package/src/storage/index.ts +0 -81
  255. package/src/storage/mask.ts +0 -34
  256. package/src/storage/sqlite.ts +0 -714
  257. package/src/storage/types.ts +0 -139
  258. package/src/utils/index.ts +0 -62
@@ -1,714 +0,0 @@
1
- import { chmodSync, existsSync, mkdirSync } from "node:fs";
2
- import { dirname } from "node:path";
3
- import type { JsonValue, RuntimeError, RunRecord } from "@actiondock/sdk";
4
- import { type Clock, getSystemClock } from "../runtime/clock";
5
- import { createDefaultSqliteDriver } from "./driver";
6
- import type {
7
- RuntimeStorage,
8
- SqliteDriver,
9
- SqliteStatement,
10
- StateEntry,
11
- StorageOptions,
12
- TerminalRunStatus,
13
- } from "./types";
14
-
15
- /**
16
- * 统一 SQLite 运行时存储实现。
17
- * 通过 SqliteDriver 抽象驱动,解耦底层具体运行时引擎(Node.js / Bun)。
18
- */
19
- export class SqliteRuntimeStorage implements RuntimeStorage {
20
- private driver: SqliteDriver;
21
- private packageId: string;
22
- private clock: Clock;
23
- private isClosed = false;
24
- private statementCache = new Map<string, SqliteStatement>();
25
-
26
- get isOpen(): boolean {
27
- return !this.isClosed;
28
- }
29
-
30
- get closed(): boolean {
31
- return this.isClosed;
32
- }
33
-
34
- constructor(options: StorageOptions) {
35
- this.packageId = options.packageId;
36
- this.clock = options.clock ?? getSystemClock();
37
- const dbPath = options.dbPath || ":memory:";
38
-
39
- if (dbPath !== ":memory:") {
40
- const dir = dirname(dbPath);
41
- if (!existsSync(dir)) {
42
- try {
43
- mkdirSync(dir, { recursive: true, mode: 0o700 });
44
- chmodSync(dir, 0o700);
45
- } catch {
46
- // 忽略系统权限设置失败
47
- }
48
- }
49
- }
50
-
51
- this.driver = options.driver ?? createDefaultSqliteDriver(dbPath);
52
-
53
- if (dbPath !== ":memory:" && existsSync(dbPath)) {
54
- try {
55
- chmodSync(dbPath, 0o600);
56
- } catch {
57
- // 忽略文件权限设置异常
58
- }
59
- }
60
- this.init();
61
- }
62
-
63
- /**
64
- * 初始化数据库结构并执行无损版本升级。
65
- */
66
- private init(): void {
67
- this.driver.exec("PRAGMA journal_mode = WAL;");
68
- this.driver.exec("PRAGMA synchronous = NORMAL;");
69
-
70
- // 读取 Schema 版本号
71
- const versionRes = this.driver.prepare("PRAGMA user_version;").get<{
72
- user_version: number;
73
- }>();
74
- const version = versionRes?.user_version ?? 0;
75
-
76
- if (version === 0) {
77
- this.driver.transaction(() => {
78
- this.driver.exec(`
79
- CREATE TABLE IF NOT EXISTS config (
80
- package_id TEXT NOT NULL,
81
- key TEXT NOT NULL,
82
- value_json TEXT,
83
- updated_at TEXT NOT NULL,
84
- PRIMARY KEY (package_id, key)
85
- );
86
-
87
- CREATE TABLE IF NOT EXISTS state (
88
- package_id TEXT NOT NULL,
89
- namespace TEXT NOT NULL,
90
- key TEXT NOT NULL,
91
- value_json TEXT,
92
- updated_at TEXT NOT NULL,
93
- expires_at TEXT,
94
- PRIMARY KEY (package_id, namespace, key)
95
- );
96
-
97
- CREATE TABLE IF NOT EXISTS runs (
98
- id TEXT PRIMARY KEY,
99
- root_run_id TEXT NOT NULL,
100
- parent_run_id TEXT,
101
- package_id TEXT NOT NULL,
102
- package_instance_id TEXT NOT NULL,
103
- action_id TEXT NOT NULL,
104
- generation_id TEXT NOT NULL,
105
- owner_id TEXT NOT NULL,
106
- status TEXT NOT NULL,
107
- input_json TEXT,
108
- output_json TEXT,
109
- error_json TEXT,
110
- started_at TEXT NOT NULL,
111
- finished_at TEXT,
112
- duration_ms INTEGER
113
- );
114
-
115
- CREATE INDEX IF NOT EXISTS idx_runs_action ON runs(package_id, action_id);
116
- CREATE INDEX IF NOT EXISTS idx_runs_root ON runs(root_run_id);
117
- CREATE INDEX IF NOT EXISTS idx_runs_started ON runs(started_at DESC);
118
- CREATE INDEX IF NOT EXISTS idx_state_expires ON state(expires_at);
119
-
120
- PRAGMA user_version = 3;
121
- `);
122
- });
123
- } else if (version < 2) {
124
- this.driver.transaction(() => {
125
- try {
126
- const columns = this.driver
127
- .prepare("PRAGMA table_info(state);")
128
- .all<{ name: string }>();
129
- const hasExpiresAt = columns.some((c) => c.name === "expires_at");
130
- if (!hasExpiresAt) {
131
- this.driver.exec("ALTER TABLE state ADD COLUMN expires_at TEXT;");
132
- }
133
- } catch {
134
- // 忽略表已存在或字段已添加
135
- }
136
- this.driver.exec(
137
- "CREATE INDEX IF NOT EXISTS idx_state_expires ON state(expires_at);"
138
- );
139
- this.driver.exec("PRAGMA user_version = 2;");
140
- });
141
- }
142
-
143
- // 升级至版本 3:补齐运行记录调用链与实例字段
144
- if (version < 3) {
145
- this.driver.transaction(() => {
146
- try {
147
- const columns = this.driver
148
- .prepare("PRAGMA table_info(runs);")
149
- .all<{ name: string }>();
150
- const columnNames = new Set(columns.map((c) => c.name));
151
-
152
- if (!columnNames.has("root_run_id")) {
153
- this.driver.exec("ALTER TABLE runs ADD COLUMN root_run_id TEXT DEFAULT '';");
154
- this.driver.exec("UPDATE runs SET root_run_id = id WHERE root_run_id = '' OR root_run_id IS NULL;");
155
- }
156
- if (!columnNames.has("package_instance_id")) {
157
- this.driver.exec("ALTER TABLE runs ADD COLUMN package_instance_id TEXT DEFAULT '';");
158
- }
159
- if (!columnNames.has("generation_id")) {
160
- this.driver.exec("ALTER TABLE runs ADD COLUMN generation_id TEXT DEFAULT '1';");
161
- }
162
- if (!columnNames.has("owner_id")) {
163
- this.driver.exec("ALTER TABLE runs ADD COLUMN owner_id TEXT DEFAULT 'local';");
164
- }
165
- if (!columnNames.has("duration_ms")) {
166
- this.driver.exec("ALTER TABLE runs ADD COLUMN duration_ms INTEGER;");
167
- }
168
- } catch {
169
- // 忽略已存在的字段
170
- }
171
- this.driver.exec("CREATE INDEX IF NOT EXISTS idx_runs_root ON runs(root_run_id);");
172
- this.driver.exec("PRAGMA user_version = 3;");
173
- });
174
- }
175
- }
176
-
177
- /**
178
- * 获取或复用预编译 SQL 语句缓存。
179
- */
180
- private getStatement(sql: string): SqliteStatement {
181
- let stmt = this.statementCache.get(sql);
182
- if (!stmt) {
183
- stmt = this.driver.prepare(sql);
184
- this.statementCache.set(sql, stmt);
185
- }
186
- return stmt;
187
- }
188
-
189
- // --- Config 配置管理 ---
190
-
191
- getConfig<T = unknown>(key: string): T | undefined {
192
- const stmt = this.getStatement(
193
- "SELECT value_json FROM config WHERE package_id = ? AND key = ?"
194
- );
195
- const row = stmt.get<{ value_json: string }>(this.packageId, key);
196
- if (!row || row.value_json === undefined || row.value_json === null) {
197
- return undefined;
198
- }
199
- try {
200
- return JSON.parse(row.value_json) as T;
201
- } catch {
202
- return row.value_json as unknown as T;
203
- }
204
- }
205
-
206
- listConfig(): Record<string, unknown> {
207
- const stmt = this.getStatement(
208
- "SELECT key, value_json FROM config WHERE package_id = ?"
209
- );
210
- const rows = stmt.all<{ key: string; value_json: string }>(this.packageId);
211
- const result: Record<string, unknown> = {};
212
- for (const row of rows) {
213
- try {
214
- result[row.key] = JSON.parse(row.value_json);
215
- } catch {
216
- result[row.key] = row.value_json;
217
- }
218
- }
219
- return result;
220
- }
221
-
222
- setConfig(key: string, value: unknown): void {
223
- const stmt = this.getStatement(`
224
- INSERT INTO config (package_id, key, value_json, updated_at)
225
- VALUES (?, ?, ?, ?)
226
- ON CONFLICT(package_id, key) DO UPDATE SET
227
- value_json = excluded.value_json,
228
- updated_at = excluded.updated_at
229
- `);
230
- const valJson = JSON.stringify(value);
231
- const now = this.clock.now().toISOString();
232
- stmt.run(this.packageId, key, valJson, now);
233
- }
234
-
235
- deleteConfig(key: string): boolean {
236
- const stmt = this.getStatement(
237
- "DELETE FROM config WHERE package_id = ? AND key = ?"
238
- );
239
- const res = stmt.run(this.packageId, key);
240
- return res.changes > 0;
241
- }
242
-
243
- // --- State 状态管理 ---
244
-
245
- async getState<T = unknown>(namespace: string, key: string): Promise<T | undefined> {
246
- const stmt = this.getStatement(
247
- "SELECT value_json, expires_at FROM state WHERE package_id = ? AND namespace = ? AND key = ?"
248
- );
249
- const row = stmt.get<{ value_json: string; expires_at?: string }>(
250
- this.packageId,
251
- namespace,
252
- key
253
- );
254
- if (!row || row.value_json === undefined || row.value_json === null) {
255
- return undefined;
256
- }
257
-
258
- if (row.expires_at) {
259
- const expires = new Date(row.expires_at).getTime();
260
- if (this.clock.now().getTime() >= expires) {
261
- this.deleteState(namespace, key).catch(() => {});
262
- return undefined;
263
- }
264
- }
265
-
266
- try {
267
- return JSON.parse(row.value_json) as T;
268
- } catch {
269
- return row.value_json as unknown as T;
270
- }
271
- }
272
-
273
- async findState<T = unknown>(
274
- targetKey: string,
275
- namespace?: string
276
- ): Promise<StateEntry | undefined> {
277
- let ns = namespace;
278
- let actualKey = targetKey;
279
-
280
- if (!ns && targetKey.includes(":")) {
281
- const parts = targetKey.split(":");
282
- ns = parts[0];
283
- actualKey = parts.slice(1).join(":");
284
- }
285
-
286
- if (ns) {
287
- const val = await this.getState<T>(ns, actualKey);
288
- if (val === undefined) return undefined;
289
-
290
- const stmt = this.getStatement(
291
- "SELECT updated_at, expires_at FROM state WHERE package_id = ? AND namespace = ? AND key = ?"
292
- );
293
- const row = stmt.get<{ updated_at: string; expires_at?: string }>(
294
- this.packageId,
295
- ns,
296
- actualKey
297
- );
298
-
299
- return {
300
- packageId: this.packageId,
301
- namespace: ns,
302
- key: actualKey,
303
- fullKey: targetKey,
304
- value: val,
305
- updatedAt: row?.updated_at || this.clock.now().toISOString(),
306
- expiresAt: row?.expires_at,
307
- };
308
- }
309
-
310
- const val = await this.getState<T>("", actualKey);
311
- if (val !== undefined) {
312
- const stmt = this.getStatement(
313
- "SELECT updated_at, expires_at FROM state WHERE package_id = ? AND namespace = ? AND key = ?"
314
- );
315
- const row = stmt.get<{ updated_at: string; expires_at?: string }>(
316
- this.packageId,
317
- "",
318
- actualKey
319
- );
320
- return {
321
- packageId: this.packageId,
322
- namespace: "",
323
- key: actualKey,
324
- fullKey: actualKey,
325
- value: val,
326
- updatedAt: row?.updated_at || this.clock.now().toISOString(),
327
- expiresAt: row?.expires_at,
328
- };
329
- }
330
-
331
- const stmt = this.getStatement(
332
- "SELECT namespace, key, value_json, updated_at, expires_at FROM state WHERE package_id = ? AND key = ?"
333
- );
334
- const rows = stmt.all<{
335
- namespace: string;
336
- key: string;
337
- value_json: string;
338
- updated_at: string;
339
- expires_at?: string;
340
- }>(this.packageId, actualKey);
341
-
342
- const now = this.clock.now().getTime();
343
- for (const row of rows) {
344
- if (row.expires_at && now >= new Date(row.expires_at).getTime()) {
345
- continue;
346
- }
347
- let parsedVal: unknown;
348
- try {
349
- parsedVal = JSON.parse(row.value_json);
350
- } catch {
351
- parsedVal = row.value_json;
352
- }
353
- return {
354
- packageId: this.packageId,
355
- namespace: row.namespace,
356
- key: row.key,
357
- fullKey: row.namespace ? `${row.namespace}:${row.key}` : row.key,
358
- value: parsedVal,
359
- updatedAt: row.updated_at,
360
- expiresAt: row.expires_at,
361
- };
362
- }
363
-
364
- return undefined;
365
- }
366
-
367
- async setState<T = unknown>(
368
- namespace: string,
369
- key: string,
370
- value: T,
371
- ttl?: number
372
- ): Promise<void> {
373
- const stmt = this.getStatement(`
374
- INSERT INTO state (package_id, namespace, key, value_json, updated_at, expires_at)
375
- VALUES (?, ?, ?, ?, ?, ?)
376
- ON CONFLICT(package_id, namespace, key) DO UPDATE SET
377
- value_json = excluded.value_json,
378
- updated_at = excluded.updated_at,
379
- expires_at = excluded.expires_at
380
- `);
381
- const valJson = JSON.stringify(value);
382
- const now = this.clock.now();
383
- const updatedAt = now.toISOString();
384
-
385
- let expiresAt: string | null = null;
386
- if (typeof ttl === "number" && ttl > 0) {
387
- expiresAt = new Date(now.getTime() + ttl * 1000).toISOString();
388
- }
389
-
390
- stmt.run(this.packageId, namespace, key, valJson, updatedAt, expiresAt);
391
- }
392
-
393
- async deleteState(namespace: string, key: string): Promise<boolean> {
394
- const stmt = this.getStatement(
395
- "DELETE FROM state WHERE package_id = ? AND namespace = ? AND key = ?"
396
- );
397
- const res = stmt.run(this.packageId, namespace, key);
398
- return res.changes > 0;
399
- }
400
-
401
- async deleteStateSmart(targetKey: string, namespace?: string): Promise<boolean> {
402
- let ns = namespace;
403
- let actualKey = targetKey;
404
-
405
- if (!ns && targetKey.includes(":")) {
406
- const parts = targetKey.split(":");
407
- ns = parts[0];
408
- actualKey = parts.slice(1).join(":");
409
- }
410
-
411
- if (ns !== undefined) {
412
- return this.deleteState(ns, actualKey);
413
- }
414
-
415
- const deletedRoot = await this.deleteState("", actualKey);
416
- if (deletedRoot) return true;
417
-
418
- const stmt = this.getStatement(
419
- "DELETE FROM state WHERE package_id = ? AND key = ?"
420
- );
421
- const res = stmt.run(this.packageId, actualKey);
422
- return res.changes > 0;
423
- }
424
-
425
- async clearState(
426
- options: { namespace?: string; all?: boolean; prefix?: string } = {}
427
- ): Promise<number> {
428
- let sql = "DELETE FROM state WHERE package_id = ?";
429
- const params: any[] = [this.packageId];
430
-
431
- if (options.namespace !== undefined) {
432
- sql += " AND namespace = ?";
433
- params.push(options.namespace);
434
- }
435
-
436
- if (options.prefix) {
437
- sql += " AND key LIKE ? ESCAPE '\\'";
438
- const escapedPrefix = options.prefix.replace(/([%_\\])/g, "\\$1");
439
- params.push(`${escapedPrefix}%`);
440
- }
441
-
442
- const stmt = this.getStatement(sql);
443
- const res = stmt.run(...params);
444
- return res.changes;
445
- }
446
-
447
- async listStateKeys(
448
- namespace?: string | null,
449
- prefix?: string
450
- ): Promise<string[]> {
451
- let sql = "SELECT namespace, key, expires_at FROM state WHERE package_id = ?";
452
- const params: any[] = [this.packageId];
453
-
454
- if (namespace !== null && namespace !== undefined) {
455
- sql += " AND namespace = ?";
456
- params.push(namespace);
457
- }
458
-
459
- if (prefix) {
460
- sql += " AND key LIKE ? ESCAPE '\\'";
461
- const escapedPrefix = prefix.replace(/([%_\\])/g, "\\$1");
462
- params.push(`${escapedPrefix}%`);
463
- }
464
-
465
- const stmt = this.getStatement(sql);
466
- const rows = stmt.all<{
467
- namespace: string;
468
- key: string;
469
- expires_at?: string;
470
- }>(...params);
471
-
472
- const now = this.clock.now().getTime();
473
- const result: string[] = [];
474
-
475
- for (const row of rows) {
476
- if (row.expires_at && now >= new Date(row.expires_at).getTime()) {
477
- continue;
478
- }
479
- if (namespace !== null && namespace !== undefined) {
480
- result.push(row.key);
481
- } else {
482
- result.push(row.namespace ? `${row.namespace}:${row.key}` : row.key);
483
- }
484
- }
485
-
486
- return result;
487
- }
488
-
489
- async listStateEntries(
490
- options: { namespace?: string; prefix?: string } = {}
491
- ): Promise<StateEntry[]> {
492
- let sql = "SELECT namespace, key, value_json, updated_at, expires_at FROM state WHERE package_id = ?";
493
- const params: any[] = [this.packageId];
494
-
495
- if (options.namespace !== undefined) {
496
- sql += " AND namespace = ?";
497
- params.push(options.namespace);
498
- }
499
-
500
- if (options.prefix) {
501
- sql += " AND key LIKE ? ESCAPE '\\'";
502
- const escapedPrefix = options.prefix.replace(/([%_\\])/g, "\\$1");
503
- params.push(`${escapedPrefix}%`);
504
- }
505
-
506
- const stmt = this.getStatement(sql);
507
- const rows = stmt.all<{
508
- namespace: string;
509
- key: string;
510
- value_json: string;
511
- updated_at: string;
512
- expires_at?: string;
513
- }>(...params);
514
-
515
- const now = this.clock.now().getTime();
516
- const results: StateEntry[] = [];
517
-
518
- for (const row of rows) {
519
- if (row.expires_at && now >= new Date(row.expires_at).getTime()) {
520
- continue;
521
- }
522
-
523
- let parsedVal: unknown;
524
- try {
525
- parsedVal = JSON.parse(row.value_json);
526
- } catch {
527
- parsedVal = row.value_json;
528
- }
529
-
530
- results.push({
531
- packageId: this.packageId,
532
- namespace: row.namespace,
533
- key: row.key,
534
- fullKey: row.namespace ? `${row.namespace}:${row.key}` : row.key,
535
- value: parsedVal,
536
- updatedAt: row.updated_at,
537
- expiresAt: row.expires_at,
538
- });
539
- }
540
-
541
- return results;
542
- }
543
-
544
- // --- Runs 运行记录管理 ---
545
-
546
- createRun(record: RunRecord | any): void {
547
- const stmt = this.getStatement(`
548
- INSERT INTO runs (
549
- id, root_run_id, parent_run_id, package_id, package_instance_id,
550
- action_id, generation_id, owner_id, status, input_json, output_json,
551
- error_json, started_at, finished_at, duration_ms
552
- )
553
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
554
- `);
555
-
556
- const rootRunId = record.rootRunId || record.id;
557
- const parentRunId = record.parentRunId || null;
558
- const packageInstanceId = record.packageInstanceId || record.packageId || this.packageId;
559
- const generationId = record.generationId || "1";
560
- const ownerId = record.ownerId || "local";
561
- const inputJson = record.input !== undefined ? JSON.stringify(record.input) : null;
562
- const outputJson = record.output !== undefined ? JSON.stringify(record.output) : null;
563
- const errorJson = record.error ? JSON.stringify(record.error) : null;
564
- const finishedAt = record.finishedAt || null;
565
- const durationMs = typeof record.durationMs === "number" ? record.durationMs : null;
566
-
567
- stmt.run(
568
- record.id,
569
- rootRunId,
570
- parentRunId,
571
- record.packageId || this.packageId,
572
- packageInstanceId,
573
- record.actionId,
574
- generationId,
575
- ownerId,
576
- record.status,
577
- inputJson,
578
- outputJson,
579
- errorJson,
580
- record.startedAt,
581
- finishedAt,
582
- durationMs
583
- );
584
- }
585
-
586
- updateRun(
587
- id: string,
588
- status: TerminalRunStatus,
589
- output?: unknown,
590
- error?: RuntimeError,
591
- finishedAt?: string
592
- ): void {
593
- if (this.isClosed) return;
594
- try {
595
- const stmt = this.getStatement(`
596
- UPDATE runs
597
- SET status = ?, output_json = ?, error_json = ?, finished_at = ?
598
- WHERE id = ?
599
- `);
600
- stmt.run(
601
- status,
602
- output !== undefined ? JSON.stringify(output) : null,
603
- error ? JSON.stringify(error) : null,
604
- finishedAt || this.clock.now().toISOString(),
605
- id
606
- );
607
- } catch (err) {
608
- if (this.isClosed) return;
609
- console.warn(`[SqliteRuntimeStorage] Failed to update run "${id}":`, err);
610
- throw err;
611
- }
612
- }
613
-
614
- getRun(id: string): RunRecord | null {
615
- const stmt = this.getStatement(
616
- "SELECT * FROM runs WHERE id = ? AND package_id = ?"
617
- );
618
- const row = stmt.get<any>(id, this.packageId);
619
- if (!row) return null;
620
- return this.mapRunRecord(row);
621
- }
622
-
623
- listRuns(options: { actionId?: string; limit?: number } = {}): RunRecord[] {
624
- const limit = options.limit || 50;
625
- let rows: any[];
626
- if (options.actionId) {
627
- const stmt = this.getStatement(`
628
- SELECT * FROM runs
629
- WHERE package_id = ? AND action_id = ?
630
- ORDER BY started_at DESC
631
- LIMIT ?
632
- `);
633
- rows = stmt.all(this.packageId, options.actionId, limit);
634
- } else {
635
- const stmt = this.getStatement(`
636
- SELECT * FROM runs
637
- WHERE package_id = ?
638
- ORDER BY started_at DESC
639
- LIMIT ?
640
- `);
641
- rows = stmt.all(this.packageId, limit);
642
- }
643
- return rows.map((r) => this.mapRunRecord(r));
644
- }
645
-
646
- clearRuns(options: { actionId?: string; status?: string } = {}): number {
647
- let sql = "DELETE FROM runs WHERE package_id = ?";
648
- const params: any[] = [this.packageId];
649
- if (options.actionId) {
650
- sql += " AND action_id = ?";
651
- params.push(options.actionId);
652
- }
653
- if (options.status) {
654
- sql += " AND status = ?";
655
- params.push(options.status);
656
- }
657
- const stmt = this.getStatement(sql);
658
- const res = stmt.run(...params);
659
- return res.changes;
660
- }
661
-
662
- private mapRunRecord(row: any): RunRecord {
663
- let input: JsonValue | undefined;
664
- let output: JsonValue | undefined;
665
- let error: RuntimeError | undefined;
666
-
667
- try {
668
- input = row.input_json ? JSON.parse(row.input_json) : undefined;
669
- } catch {
670
- input = row.input_json;
671
- }
672
-
673
- try {
674
- output = row.output_json ? JSON.parse(row.output_json) : undefined;
675
- } catch {
676
- output = row.output_json;
677
- }
678
-
679
- try {
680
- error = row.error_json ? JSON.parse(row.error_json) : undefined;
681
- } catch {
682
- error = undefined;
683
- }
684
-
685
- return {
686
- id: row.id,
687
- rootRunId: row.root_run_id || row.id,
688
- parentRunId: row.parent_run_id || undefined,
689
- packageId: row.package_id,
690
- packageInstanceId: row.package_instance_id || row.package_id,
691
- actionId: row.action_id,
692
- generationId: row.generation_id || "1",
693
- ownerId: row.owner_id || "local",
694
- status: row.status,
695
- input,
696
- output,
697
- error,
698
- startedAt: row.started_at,
699
- finishedAt: row.finished_at || undefined,
700
- durationMs: typeof row.duration_ms === "number" ? row.duration_ms : undefined,
701
- };
702
- }
703
-
704
- close(): void {
705
- if (this.isClosed) return;
706
- this.isClosed = true;
707
- this.statementCache.clear();
708
- try {
709
- this.driver.close();
710
- } catch {
711
- // 忽略重复关闭异常
712
- }
713
- }
714
- }