@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.
- 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 +9 -0
- package/dist/catalog/action-index.js +93 -0
- package/dist/catalog/action-resolver.d.ts +20 -0
- package/dist/catalog/action-resolver.js +95 -0
- package/dist/catalog/index.d.ts +5 -0
- package/dist/catalog/index.js +5 -0
- package/dist/catalog/location-registry.d.ts +15 -0
- package/dist/catalog/location-registry.js +138 -0
- package/dist/catalog/package-catalog.d.ts +7 -0
- package/dist/catalog/package-catalog.js +76 -0
- package/dist/catalog/types.d.ts +68 -0
- package/dist/catalog/types.js +1 -0
- package/dist/doctor/doctor.d.ts +7 -0
- package/dist/doctor/doctor.js +613 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/types.d.ts +23 -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 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/service.d.ts +79 -0
- package/dist/execution/service.js +633 -0
- package/dist/execution/types.d.ts +131 -0
- package/dist/execution/types.js +1 -0
- package/dist/export/index.d.ts +1 -0
- package/dist/export/index.js +1 -0
- package/dist/export/templates.d.ts +71 -0
- package/dist/export/templates.js +621 -0
- package/dist/filter/index.d.ts +1 -0
- package/dist/filter/index.js +1 -0
- package/dist/filter/intent.d.ts +33 -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 -0
- package/dist/index.js +20 -0
- 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 +162 -0
- package/dist/profile/client.js +467 -0
- package/dist/profile/index.d.ts +3 -0
- package/dist/profile/index.js +3 -0
- package/dist/profile/manager.d.ts +47 -0
- package/dist/profile/manager.js +294 -0
- package/dist/profile/types.d.ts +61 -0
- package/dist/profile/types.js +1 -0
- package/dist/project/closure.d.ts +25 -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 -0
- package/dist/project/index.js +10 -0
- package/dist/project/init.d.ts +26 -0
- package/dist/project/init.js +175 -0
- package/dist/project/loader.d.ts +99 -0
- 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 +22 -0
- 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 +107 -0
- package/dist/project/types.js +1 -0
- package/dist/registry/index.d.ts +5 -0
- 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 +45 -0
- 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 +117 -0
- 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 +19 -0
- package/dist/runtime/clock.js +17 -0
- package/dist/runtime/context.d.ts +86 -0
- package/dist/runtime/context.js +242 -0
- package/dist/runtime/env.d.ts +56 -0
- package/dist/runtime/env.js +159 -0
- package/dist/runtime/events.d.ts +93 -0
- package/dist/runtime/events.js +604 -0
- package/dist/runtime/execution-manager.d.ts +48 -0
- package/dist/runtime/execution-manager.js +66 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/module-loader.d.ts +34 -0
- package/dist/runtime/module-loader.js +16 -0
- package/dist/runtime/process.d.ts +9 -0
- package/dist/runtime/process.js +146 -0
- package/dist/runtime/runner.d.ts +293 -0
- package/dist/runtime/runner.js +984 -0
- package/dist/runtime/standalone.d.ts +89 -0
- package/dist/runtime/standalone.js +536 -0
- package/dist/schema/validator.d.ts +28 -0
- package/dist/schema/validator.js +116 -0
- package/dist/server/body.d.ts +40 -0
- package/dist/server/body.js +93 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +5 -0
- package/dist/server/routes/actions.d.ts +5 -0
- package/dist/server/routes/actions.js +298 -0
- package/dist/server/routes/common.d.ts +47 -0
- package/dist/server/routes/common.js +104 -0
- package/dist/server/routes/config.d.ts +6 -0
- package/dist/server/routes/config.js +173 -0
- package/dist/server/routes/doctor.d.ts +5 -0
- package/dist/server/routes/doctor.js +63 -0
- package/dist/server/routes/health.d.ts +5 -0
- package/dist/server/routes/health.js +35 -0
- package/dist/server/routes/index.d.ts +9 -0
- package/dist/server/routes/index.js +9 -0
- package/dist/server/routes/info.d.ts +7 -0
- package/dist/server/routes/info.js +158 -0
- package/dist/server/routes/playbooks.d.ts +5 -0
- package/dist/server/routes/playbooks.js +134 -0
- package/dist/server/routes/runs.d.ts +5 -0
- package/dist/server/routes/runs.js +343 -0
- package/dist/server/routes/state.d.ts +6 -0
- package/dist/server/routes/state.js +169 -0
- package/dist/server/security.d.ts +44 -0
- package/dist/server/security.js +101 -0
- package/dist/server/server.d.ts +15 -0
- package/dist/server/server.js +355 -0
- package/dist/server/types.d.ts +78 -0
- 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 +9 -0
- package/dist/storage/driver.js +59 -0
- package/dist/storage/index.d.ts +64 -0
- 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 +26 -0
- 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 +79 -0
- package/dist/storage/sqlite.js +689 -0
- package/dist/storage/types.d.ts +172 -0
- 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 +49 -0
- package/dist/utils/index.js +180 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.js +4 -0
- package/package.json +13 -13
- package/src/build/index.ts +0 -1
- package/src/build/templates.ts +0 -59
- package/src/catalog/action-index.ts +0 -92
- package/src/catalog/action-resolver.ts +0 -120
- package/src/catalog/index.ts +0 -5
- package/src/catalog/location-registry.ts +0 -134
- package/src/catalog/package-catalog.ts +0 -87
- package/src/catalog/types.ts +0 -75
- package/src/doctor/doctor.ts +0 -419
- package/src/doctor/index.ts +0 -2
- package/src/doctor/types.ts +0 -25
- package/src/execution/index.ts +0 -2
- package/src/execution/service.ts +0 -322
- package/src/execution/types.ts +0 -73
- package/src/export/index.ts +0 -1
- package/src/export/templates.ts +0 -590
- package/src/filter/index.ts +0 -1
- package/src/filter/intent.ts +0 -154
- package/src/index.ts +0 -16
- package/src/profile/client.ts +0 -552
- package/src/profile/index.ts +0 -3
- package/src/profile/manager.ts +0 -341
- package/src/profile/types.ts +0 -71
- package/src/project/index.ts +0 -4
- package/src/project/init.ts +0 -242
- package/src/project/loader.ts +0 -479
- package/src/project/manifest.ts +0 -299
- package/src/project/types.ts +0 -141
- package/src/registry/index.ts +0 -2
- package/src/registry/registry.ts +0 -940
- package/src/registry/types.ts +0 -127
- package/src/runtime/clock.ts +0 -41
- package/src/runtime/context.ts +0 -259
- package/src/runtime/env.ts +0 -172
- package/src/runtime/events.ts +0 -142
- package/src/runtime/execution-manager.ts +0 -74
- package/src/runtime/index.ts +0 -10
- package/src/runtime/module-loader.ts +0 -67
- package/src/runtime/process.ts +0 -245
- package/src/runtime/runner.ts +0 -527
- package/src/runtime/standalone.ts +0 -429
- package/src/schema/validator.ts +0 -61
- package/src/server/body.ts +0 -112
- package/src/server/index.ts +0 -7
- package/src/server/routes/actions.ts +0 -262
- package/src/server/routes/common.ts +0 -126
- package/src/server/routes/config.ts +0 -147
- package/src/server/routes/doctor.ts +0 -29
- package/src/server/routes/health.ts +0 -40
- package/src/server/routes/index.ts +0 -9
- package/src/server/routes/info.ts +0 -221
- package/src/server/routes/playbooks.ts +0 -109
- package/src/server/routes/runs.ts +0 -301
- package/src/server/routes/state.ts +0 -133
- package/src/server/runtime-registry.ts +0 -115
- package/src/server/security.ts +0 -115
- package/src/server/server.ts +0 -251
- package/src/server/types.ts +0 -57
- package/src/storage/driver.ts +0 -126
- package/src/storage/index.ts +0 -81
- package/src/storage/mask.ts +0 -34
- package/src/storage/sqlite.ts +0 -714
- package/src/storage/types.ts +0 -139
- package/src/utils/index.ts +0 -62
package/src/storage/sqlite.ts
DELETED
|
@@ -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
|
-
}
|