@actiondock/core 2.10.0 → 2.12.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 +1 -2
- package/dist/catalog/action-catalog.js +3 -33
- package/dist/catalog/discovery.d.ts +0 -3
- package/dist/catalog/discovery.js +4 -4
- package/dist/catalog/graph.d.ts +0 -4
- package/dist/catalog/graph.js +8 -14
- package/dist/{profile/client-actions.d.ts → client/actions.d.ts} +1 -1
- package/dist/{profile/client-actions.js → client/actions.js} +2 -8
- package/dist/{profile/client-config.d.ts → client/config.d.ts} +1 -1
- package/dist/{profile/client-config.js → client/config.js} +1 -1
- package/dist/client/dispatcher.d.ts +4 -0
- package/dist/client/dispatcher.js +21 -0
- package/dist/client/health.d.ts +38 -0
- package/dist/{profile/client-health.js → client/health.js} +9 -6
- package/dist/client/index.d.ts +16 -0
- package/dist/client/index.js +20 -0
- package/dist/{profile/client-playbooks.d.ts → client/playbooks.d.ts} +1 -1
- package/dist/{profile/client-playbooks.js → client/playbooks.js} +1 -1
- package/dist/{profile/client-runs.d.ts → client/runs.d.ts} +4 -2
- package/dist/{profile/client-runs.js → client/runs.js} +7 -1
- package/dist/{profile/client-state.d.ts → client/state.d.ts} +1 -1
- package/dist/{profile/client-state.js → client/state.js} +1 -1
- package/dist/{profile/client-transport.d.ts → client/transport.d.ts} +29 -10
- package/dist/{profile/client-transport.js → client/transport.js} +125 -35
- package/dist/doctor/checks/index.d.ts +1 -1
- package/dist/doctor/checks/index.js +1 -1
- package/dist/doctor/checks/project.js +20 -88
- package/dist/doctor/checks/runtime.d.ts +0 -4
- package/dist/doctor/checks/runtime.js +0 -28
- package/dist/doctor/doctor.js +1 -2
- package/dist/errors.d.ts +53 -3
- package/dist/errors.js +53 -3
- package/dist/execution/service.d.ts +55 -37
- package/dist/execution/service.js +601 -212
- package/dist/execution/types.d.ts +24 -5
- package/dist/host/host.d.ts +42 -22
- package/dist/host/host.js +562 -149
- package/dist/host/routing.js +6 -6
- package/dist/host/types.d.ts +25 -13
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/input/advice.d.ts +0 -44
- package/dist/input/advice.js +7 -141
- package/dist/input/flat-materializer.js +1 -1
- package/dist/input/flat-parser.js +1 -1
- package/dist/input/index.d.ts +0 -1
- package/dist/input/index.js +0 -1
- package/dist/input/input-resolver.js +1 -1
- package/dist/input/validation-mapper.d.ts +1 -1
- package/dist/ipc/diagnostic.d.ts +8 -21
- package/dist/ipc/diagnostic.js +24 -57
- package/dist/ipc/host.js +21 -7
- package/dist/ipc/service.d.ts +14 -0
- package/dist/ipc/service.js +132 -26
- package/dist/ipc/types.d.ts +0 -4
- package/dist/package/index.d.ts +4 -5
- package/dist/package/index.js +1 -2
- package/dist/package/runtime.d.ts +94 -23
- package/dist/package/runtime.js +628 -274
- package/dist/package/static-index.d.ts +3 -6
- package/dist/package/static-index.js +11 -40
- package/dist/package/types.d.ts +19 -4
- package/dist/platform/index.d.ts +2 -1
- package/dist/platform/index.js +2 -1
- package/dist/platform/loader-hook.d.ts +32 -0
- package/dist/platform/loader-hook.js +90 -0
- package/dist/{node → platform}/module-loader.d.ts +13 -0
- package/dist/{node → platform}/module-loader.js +51 -4
- package/dist/platform/node.d.ts +19 -8
- package/dist/platform/node.js +50 -20
- package/dist/platform/types.d.ts +7 -47
- package/dist/process/context-process.d.ts +3 -18
- package/dist/process/context-process.js +3 -44
- package/dist/process/control.d.ts +21 -0
- package/dist/process/control.js +34 -0
- package/dist/process/converters.d.ts +23 -0
- package/dist/process/converters.js +92 -0
- package/dist/process/cursor.js +2 -22
- package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
- package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
- package/dist/process/index.d.ts +1 -3
- package/dist/process/index.js +1 -3
- package/dist/process/input-dispatcher.d.ts +4 -13
- package/dist/process/input-dispatcher.js +48 -69
- package/dist/process/managed-record.d.ts +3 -31
- package/dist/process/metadata-store.d.ts +0 -41
- package/dist/process/metadata-store.js +2 -341
- package/dist/process/process-driver.js +2 -2
- package/dist/process/process-manager.d.ts +8 -50
- package/dist/process/process-manager.js +28 -417
- package/dist/process/quotas.d.ts +14 -0
- package/dist/process/quotas.js +29 -0
- package/dist/process/run-executor.js +0 -1
- package/dist/process/terminal-output-cache.d.ts +20 -34
- package/dist/process/terminal-output-cache.js +43 -64
- package/dist/profile/index.d.ts +1 -2
- package/dist/profile/index.js +1 -2
- package/dist/profile/manager.d.ts +2 -4
- package/dist/profile/manager.js +10 -40
- package/dist/profile/types.d.ts +1 -17
- package/dist/project/digest.d.ts +5 -1
- package/dist/project/digest.js +81 -219
- package/dist/project/index.d.ts +4 -5
- package/dist/project/index.js +4 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +3 -3
- package/dist/project/loader.js +72 -50
- package/dist/project/lockfile.js +6 -5
- package/dist/project/manifest.js +5 -4
- package/dist/project/transactions.d.ts +4 -42
- package/dist/project/transactions.js +83 -138
- package/dist/project/types-generator.js +2 -1
- package/dist/project/types.d.ts +2 -0
- package/dist/registry/index.d.ts +0 -1
- package/dist/registry/index.js +0 -1
- package/dist/registry/lock.d.ts +2 -11
- package/dist/registry/lock.js +14 -183
- package/dist/registry/registry.d.ts +1 -6
- package/dist/registry/registry.js +5 -17
- package/dist/runtime/action-registry.d.ts +1 -5
- package/dist/runtime/action-registry.js +0 -12
- package/dist/runtime/context.js +2 -2
- package/dist/runtime/events.d.ts +11 -50
- package/dist/runtime/events.js +88 -368
- package/dist/runtime/index.d.ts +0 -4
- package/dist/runtime/index.js +0 -4
- package/dist/runtime/run-persistence.d.ts +2 -0
- package/dist/runtime/run-persistence.js +2 -1
- package/dist/runtime/standalone-commands.d.ts +18 -0
- package/dist/runtime/standalone-commands.js +466 -0
- package/dist/runtime/standalone-help.d.ts +20 -0
- package/dist/runtime/standalone-help.js +72 -0
- package/dist/runtime/standalone.d.ts +1 -44
- package/dist/runtime/standalone.js +32 -573
- package/dist/schema/validator.d.ts +2 -2
- package/dist/schema/validator.js +2 -9
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +0 -2
- package/dist/server/routes/actions.js +1 -1
- package/dist/server/routes/info.js +1 -1
- package/dist/server/routes/playbooks.js +1 -1
- package/dist/server/routes/runs.js +123 -16
- package/dist/server/routes/state.js +43 -18
- package/dist/server/security.d.ts +1 -1
- package/dist/server/security.js +2 -2
- package/dist/server/server.js +3 -7
- package/dist/service/factory.js +4 -3
- package/dist/service/index.d.ts +1 -1
- package/dist/service/index.js +1 -1
- package/dist/service/remote-errors.d.ts +1 -1
- package/dist/service/remote-errors.js +6 -6
- package/dist/service/remote-polling.d.ts +3 -0
- package/dist/service/remote-polling.js +10 -5
- package/dist/service/remote.d.ts +5 -1
- package/dist/service/remote.js +108 -17
- package/dist/service/sse-stream.js +2 -2
- package/dist/service/types.d.ts +22 -19
- package/dist/service/types.js +1 -24
- package/dist/storage/clock.d.ts +1 -1
- package/dist/storage/clock.js +1 -1
- package/dist/storage/data-dir-lock.d.ts +10 -22
- package/dist/storage/data-dir-lock.js +28 -56
- package/dist/storage/file-lock.d.ts +64 -0
- package/dist/storage/file-lock.js +447 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +5 -4
- package/dist/storage/run-liveness.d.ts +48 -0
- package/dist/storage/run-liveness.js +44 -0
- package/dist/storage/sqlite-config.d.ts +16 -0
- package/dist/storage/sqlite-config.js +59 -0
- package/dist/storage/sqlite-driver.d.ts +3 -1
- package/dist/storage/sqlite-driver.js +14 -4
- package/dist/storage/sqlite-events.d.ts +13 -0
- package/dist/storage/sqlite-events.js +48 -0
- package/dist/storage/sqlite-runs.d.ts +107 -0
- package/dist/storage/sqlite-runs.js +622 -0
- package/dist/storage/sqlite-schema.d.ts +5 -0
- package/dist/storage/sqlite-schema.js +143 -0
- package/dist/storage/sqlite-state.d.ts +33 -0
- package/dist/storage/sqlite-state.js +270 -0
- package/dist/storage/sqlite.d.ts +30 -57
- package/dist/storage/sqlite.js +84 -741
- package/dist/storage/types.d.ts +35 -2
- package/dist/utils/index.d.ts +18 -14
- package/dist/utils/index.js +37 -29
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +19 -0
- package/dist/utils/warning.d.ts +1 -0
- package/dist/utils/warning.js +52 -0
- package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
- package/dist/value-validator.js +330 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -12
- package/dist/execution/index.d.ts +0 -2
- package/dist/execution/index.js +0 -2
- package/dist/filter/index.d.ts +0 -1
- package/dist/filter/index.js +0 -1
- package/dist/graph/index.d.ts +0 -4
- package/dist/graph/index.js +0 -4
- package/dist/input/metadata.d.ts +0 -99
- package/dist/input/metadata.js +0 -99
- package/dist/invocation/index.d.ts +0 -2
- package/dist/invocation/index.js +0 -2
- package/dist/ipc/index.d.ts +0 -4
- package/dist/ipc/index.js +0 -4
- package/dist/json/index.d.ts +0 -1
- package/dist/json/index.js +0 -1
- package/dist/json/value-validator.js +0 -330
- package/dist/node/index.d.ts +0 -1
- package/dist/node/index.js +0 -1
- package/dist/platform/node-fs.d.ts +0 -37
- package/dist/platform/node-fs.js +0 -81
- package/dist/process/control-arbiter.d.ts +0 -108
- package/dist/process/control-arbiter.js +0 -250
- package/dist/process/process-executor.d.ts +0 -47
- package/dist/process/process-executor.js +0 -296
- package/dist/profile/client-health.d.ts +0 -14
- package/dist/profile/client-query.d.ts +0 -30
- package/dist/profile/client-query.js +0 -69
- package/dist/profile/client.d.ts +0 -26
- package/dist/profile/client.js +0 -31
- package/dist/project/closure.d.ts +0 -25
- package/dist/project/closure.js +0 -74
- package/dist/registry/store.d.ts +0 -42
- package/dist/registry/store.js +0 -43
- package/dist/runtime/clock.d.ts +0 -7
- package/dist/runtime/clock.js +0 -7
- package/dist/runtime/module-loader.d.ts +0 -2
- package/dist/runtime/module-loader.js +0 -1
- package/dist/runtime/process.d.ts +0 -8
- package/dist/runtime/process.js +0 -1
- package/dist/runtime/runner.d.ts +0 -234
- package/dist/runtime/runner.js +0 -609
- package/dist/schema/index.d.ts +0 -1
- package/dist/schema/index.js +0 -1
- package/dist/server/dispatcher.d.ts +0 -25
- package/dist/server/dispatcher.js +0 -54
- package/dist/service/local.d.ts +0 -27
- package/dist/service/local.js +0 -141
- package/dist/storage/driver.d.ts +0 -10
- package/dist/storage/driver.js +0 -10
- package/dist/storage/lazy.d.ts +0 -8
- package/dist/storage/lazy.js +0 -45
- package/dist/storage/lock-core.d.ts +0 -158
- package/dist/storage/lock-core.js +0 -872
- package/dist/utils/net.d.ts +0 -10
- package/dist/utils/net.js +0 -16
- package/dist/utils/process.d.ts +0 -10
- package/dist/utils/process.js +0 -21
- /package/dist/{filter → utils}/intent.d.ts +0 -0
- /package/dist/{filter → utils}/intent.js +0 -0
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
import { CONTROL_REVOKED, INPUT_CLOSED, INPUT_OUTCOME_UNKNOWN,
|
|
1
|
+
import { ActionDockError, CONTROL_REVOKED, INPUT_CLOSED, INPUT_OUTCOME_UNKNOWN, INVALID_STATE, PROCESS_QUARANTINED, ProcessError, } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* 断言进程仍处于可交互(非隔离、非终态)状态。
|
|
4
|
+
*/
|
|
5
|
+
function assertProcessOperable(proc) {
|
|
6
|
+
if (proc.info.control === "quarantined") {
|
|
7
|
+
throw new ProcessError(PROCESS_QUARANTINED, "Process is in quarantined state");
|
|
8
|
+
}
|
|
9
|
+
const state = proc.info.state;
|
|
10
|
+
if (state === "exited" || state === "failed" || state === "lost") {
|
|
11
|
+
throw new ProcessError(INPUT_CLOSED, `Process is no longer operable (state: ${state})`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 进程是否已进入不可交互的终态。
|
|
16
|
+
*/
|
|
17
|
+
function isProcessTerminal(proc) {
|
|
18
|
+
return proc.info.control === "quarantined" || proc.info.control === "closed";
|
|
19
|
+
}
|
|
2
20
|
/**
|
|
3
21
|
* 输入调度器。
|
|
4
22
|
*
|
|
@@ -11,74 +29,47 @@ import { CONTROL_REVOKED, INPUT_CLOSED, INPUT_OUTCOME_UNKNOWN, PROCESS_QUARANTIN
|
|
|
11
29
|
*/
|
|
12
30
|
export class InputDispatcher {
|
|
13
31
|
host;
|
|
14
|
-
|
|
15
|
-
constructor(host, arbiter) {
|
|
32
|
+
constructor(host) {
|
|
16
33
|
this.host = host;
|
|
17
|
-
this.arbiter = arbiter;
|
|
18
34
|
}
|
|
19
35
|
/**
|
|
20
|
-
*
|
|
36
|
+
* 入队写入操作:同步阶段完成授权校验与输入通道校验;
|
|
21
37
|
* 异步阶段落盘排队收据并推送队列,随后触发串行调度。
|
|
22
38
|
*
|
|
23
39
|
* 返回的 Promise 在收据成功入队后 resolve(state 为 queued);
|
|
24
|
-
*
|
|
40
|
+
* 落盘或推送失败时原样抛出。
|
|
25
41
|
*/
|
|
26
42
|
async enqueueWrite(proc, input) {
|
|
27
|
-
//
|
|
28
|
-
|
|
43
|
+
// 入队前校验进程仍处于可交互状态
|
|
44
|
+
assertProcessOperable(proc);
|
|
29
45
|
if (proc.inputClosed) {
|
|
30
46
|
throw new ProcessError(INPUT_CLOSED, "Input stream is closed");
|
|
31
47
|
}
|
|
32
48
|
const rawBytes = input.data;
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const receipt = {
|
|
51
|
-
requestId: input.requestId,
|
|
52
|
-
state: "queued",
|
|
53
|
-
};
|
|
54
|
-
await this.host.persistReceipt(input.key, receipt, input.payloadHash);
|
|
55
|
-
proc.inputQueue.push({
|
|
56
|
-
type: "write",
|
|
57
|
-
requestId: input.requestId,
|
|
58
|
-
token: input.token,
|
|
59
|
-
bytes: rawBytes,
|
|
60
|
-
receipt,
|
|
61
|
-
key: input.key,
|
|
62
|
-
payloadHash: input.payloadHash,
|
|
63
|
-
cancelEpoch: proc.cancelEpoch,
|
|
64
|
-
});
|
|
65
|
-
pendingBytesCommitted = true;
|
|
66
|
-
// 异步调度推进
|
|
67
|
-
queueMicrotask(() => void this.dispatchNext(proc));
|
|
68
|
-
return { ...receipt };
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
if (!pendingBytesCommitted) {
|
|
72
|
-
proc.pendingInputBytes = Math.max(0, proc.pendingInputBytes - dataSize);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
49
|
+
const receipt = {
|
|
50
|
+
requestId: input.requestId,
|
|
51
|
+
state: "queued",
|
|
52
|
+
};
|
|
53
|
+
await this.host.persistReceipt(input.key, receipt, input.payloadHash);
|
|
54
|
+
proc.inputQueue.push({
|
|
55
|
+
type: "write",
|
|
56
|
+
requestId: input.requestId,
|
|
57
|
+
bytes: rawBytes,
|
|
58
|
+
receipt,
|
|
59
|
+
key: input.key,
|
|
60
|
+
payloadHash: input.payloadHash,
|
|
61
|
+
cancelEpoch: proc.cancelEpoch,
|
|
62
|
+
});
|
|
63
|
+
// 异步调度推进
|
|
64
|
+
queueMicrotask(() => void this.dispatchNext(proc));
|
|
65
|
+
return { ...receipt };
|
|
75
66
|
}
|
|
76
67
|
/**
|
|
77
68
|
* 入队控制指令:异步阶段落盘排队收据并推送队列,随后触发串行调度。
|
|
78
69
|
*/
|
|
79
70
|
async enqueueControl(proc, input) {
|
|
80
|
-
//
|
|
81
|
-
|
|
71
|
+
// 入队前校验进程仍处于可交互状态
|
|
72
|
+
assertProcessOperable(proc);
|
|
82
73
|
const receipt = {
|
|
83
74
|
requestId: input.requestId,
|
|
84
75
|
state: "queued",
|
|
@@ -87,7 +78,6 @@ export class InputDispatcher {
|
|
|
87
78
|
proc.inputQueue.push({
|
|
88
79
|
type: "control",
|
|
89
80
|
requestId: input.requestId,
|
|
90
|
-
token: input.token,
|
|
91
81
|
action: input.action,
|
|
92
82
|
receipt,
|
|
93
83
|
key: input.key,
|
|
@@ -114,7 +104,6 @@ export class InputDispatcher {
|
|
|
114
104
|
void this.host.persistReceipt(op.key, op.receipt, op.payloadHash);
|
|
115
105
|
}
|
|
116
106
|
proc.inputQueue = [];
|
|
117
|
-
proc.pendingInputBytes = 0;
|
|
118
107
|
}
|
|
119
108
|
/**
|
|
120
109
|
* 串行调度执行输入队列操作。
|
|
@@ -130,11 +119,8 @@ export class InputDispatcher {
|
|
|
130
119
|
try {
|
|
131
120
|
while (proc.inputQueue.length > 0) {
|
|
132
121
|
const op = proc.inputQueue[0];
|
|
133
|
-
// dispatch
|
|
134
|
-
if (proc
|
|
135
|
-
!proc.currentGrant ||
|
|
136
|
-
proc.currentGrant.token !== op.token ||
|
|
137
|
-
new Date(proc.currentGrant.expiresAt).getTime() <= Date.now() ||
|
|
122
|
+
// dispatch 前校验进程状态与取消纪元
|
|
123
|
+
if (isProcessTerminal(proc) ||
|
|
138
124
|
op.cancelEpoch !== proc.cancelEpoch) {
|
|
139
125
|
if (op.cancelEpoch === proc.cancelEpoch) {
|
|
140
126
|
op.receipt.state = "failed";
|
|
@@ -143,9 +129,6 @@ export class InputDispatcher {
|
|
|
143
129
|
await this.host.persistReceipt(op.key, op.receipt, op.payloadHash);
|
|
144
130
|
}
|
|
145
131
|
proc.inputQueue.shift();
|
|
146
|
-
if (op.bytes && op.cancelEpoch === proc.cancelEpoch) {
|
|
147
|
-
proc.pendingInputBytes -= op.bytes.byteLength;
|
|
148
|
-
}
|
|
149
132
|
continue;
|
|
150
133
|
}
|
|
151
134
|
op.receipt.state = "dispatching";
|
|
@@ -153,7 +136,7 @@ export class InputDispatcher {
|
|
|
153
136
|
if (op.type === "write" && op.bytes) {
|
|
154
137
|
try {
|
|
155
138
|
if (!proc.handle) {
|
|
156
|
-
throw new
|
|
139
|
+
throw new ActionDockError(INVALID_STATE, "Missing driver handle");
|
|
157
140
|
}
|
|
158
141
|
await proc.handle.write(op.bytes);
|
|
159
142
|
// 写入返回后校验取消纪元与队首位置:已被 stop 或 quarantine 接管则放弃过期结算
|
|
@@ -174,15 +157,14 @@ export class InputDispatcher {
|
|
|
174
157
|
op.receipt.errorCode = INPUT_OUTCOME_UNKNOWN;
|
|
175
158
|
await this.host.persistReceipt(op.key, op.receipt, op.payloadHash);
|
|
176
159
|
proc.inputQueue.shift();
|
|
177
|
-
proc.
|
|
178
|
-
await this.host.quarantineProcess(proc.owner, proc.info.id, op.token, "Write failed with uncertain outcome");
|
|
160
|
+
await this.host.quarantineProcess(proc.owner, proc.info.id, "Write failed with uncertain outcome");
|
|
179
161
|
break;
|
|
180
162
|
}
|
|
181
163
|
}
|
|
182
164
|
else if (op.type === "control" && op.action) {
|
|
183
165
|
try {
|
|
184
166
|
if (!proc.handle) {
|
|
185
|
-
throw new
|
|
167
|
+
throw new ActionDockError(INVALID_STATE, "Missing driver handle");
|
|
186
168
|
}
|
|
187
169
|
if (op.action.type === "input-eof") {
|
|
188
170
|
if (proc.handle.sendInputEOF) {
|
|
@@ -228,9 +210,6 @@ export class InputDispatcher {
|
|
|
228
210
|
}
|
|
229
211
|
await this.host.persistReceipt(op.key, op.receipt, op.payloadHash);
|
|
230
212
|
proc.inputQueue.shift();
|
|
231
|
-
if (op.bytes) {
|
|
232
|
-
proc.pendingInputBytes -= op.bytes.byteLength;
|
|
233
|
-
}
|
|
234
213
|
}
|
|
235
214
|
}
|
|
236
215
|
finally {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Limits, OperationReceipt, ProcessControlAction, ProcessInfo } from "@actiondock/sdk";
|
|
2
2
|
import type { ProcessDriverHandle, ProcessHandle } from "./driver.js";
|
|
3
3
|
import type { ProcessRequestKey } from "./metadata-store.js";
|
|
4
4
|
import type { ProcessOutputLog } from "./output-log.js";
|
|
5
|
-
import type { EvictedOutputTombstone } from "./terminal-output-cache.js";
|
|
6
5
|
/**
|
|
7
6
|
* 受管进程归属所有者身份。
|
|
8
7
|
*/
|
|
@@ -18,7 +17,6 @@ export interface ProcessOwner {
|
|
|
18
17
|
export interface QueuedOperation {
|
|
19
18
|
type: "write" | "control";
|
|
20
19
|
requestId: string;
|
|
21
|
-
token: string;
|
|
22
20
|
bytes?: Uint8Array;
|
|
23
21
|
action?: ProcessControlAction;
|
|
24
22
|
/** 入队时进程的取消纪元,用于 stop 与 dispatch 竞态判定 */
|
|
@@ -27,24 +25,11 @@ export interface QueuedOperation {
|
|
|
27
25
|
key: ProcessRequestKey;
|
|
28
26
|
payloadHash: string;
|
|
29
27
|
}
|
|
30
|
-
/**
|
|
31
|
-
* 排队等待获取控制权的调用者条目。
|
|
32
|
-
*/
|
|
33
|
-
export interface AcquireWaiter {
|
|
34
|
-
requestId: string;
|
|
35
|
-
waitMs: number;
|
|
36
|
-
ttlMs: number;
|
|
37
|
-
runId?: string;
|
|
38
|
-
timer?: ReturnType<typeof setTimeout>;
|
|
39
|
-
onAbort?: () => void;
|
|
40
|
-
resolve: (grant: ControlGrant) => void;
|
|
41
|
-
reject: (err: unknown) => void;
|
|
42
|
-
}
|
|
43
28
|
/**
|
|
44
29
|
* 进程管理器内部维护的活跃受管进程记录。
|
|
45
30
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
31
|
+
* 该结构为进程管理器与输入调度器的共享事实源:
|
|
32
|
+
* 输入队列相关字段经调度器协作推进,禁止绕过调度器直接改写。
|
|
48
33
|
*/
|
|
49
34
|
export interface ManagedProcessRecord {
|
|
50
35
|
info: ProcessInfo;
|
|
@@ -53,25 +38,12 @@ export interface ManagedProcessRecord {
|
|
|
53
38
|
handle?: ProcessHandle & ProcessDriverHandle;
|
|
54
39
|
outputLog: ProcessOutputLog;
|
|
55
40
|
outputUnavailable?: boolean;
|
|
56
|
-
outputTombstone?: EvictedOutputTombstone;
|
|
57
|
-
controlEpoch: number;
|
|
58
41
|
/** 取消纪元:stop 接管输入队列时递增,用于让挂起中的 dispatch 放弃过期结算 */
|
|
59
42
|
cancelEpoch: number;
|
|
60
|
-
currentGrant?: {
|
|
61
|
-
token: string;
|
|
62
|
-
epoch: number;
|
|
63
|
-
ttlMs: number;
|
|
64
|
-
expiresAt: string;
|
|
65
|
-
runId?: string;
|
|
66
|
-
requestId: string;
|
|
67
|
-
};
|
|
68
|
-
ttlTimer?: ReturnType<typeof setTimeout>;
|
|
69
43
|
idleTimer?: ReturnType<typeof setTimeout>;
|
|
70
44
|
lifetimeTimer?: ReturnType<typeof setTimeout>;
|
|
71
45
|
drainTimer?: ReturnType<typeof setTimeout>;
|
|
72
46
|
inputQueue: QueuedOperation[];
|
|
73
|
-
pendingInputBytes: number;
|
|
74
|
-
acquireWaiters: AcquireWaiter[];
|
|
75
47
|
inputClosed: boolean;
|
|
76
48
|
isDispatching: boolean;
|
|
77
49
|
effectiveLimits: Required<Limits>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SqliteDriver } from "../storage/types.js";
|
|
2
1
|
/**
|
|
3
2
|
* 受管进程运行状态。
|
|
4
3
|
*/
|
|
@@ -163,43 +162,3 @@ export declare class MemoryProcessMetadataStore implements ProcessMetadataStore
|
|
|
163
162
|
initializeHost(hostEpoch: string): Promise<number>;
|
|
164
163
|
clear(): void;
|
|
165
164
|
}
|
|
166
|
-
/**
|
|
167
|
-
* SQLite 存储选项。
|
|
168
|
-
*/
|
|
169
|
-
export interface SqliteProcessMetadataStoreOptions {
|
|
170
|
-
dbPath?: string;
|
|
171
|
-
driver?: SqliteDriver;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 基于 SQLite 的受管进程元数据持久化存储实现。
|
|
175
|
-
*/
|
|
176
|
-
export declare class SqliteProcessMetadataStore implements ProcessMetadataStore {
|
|
177
|
-
private driver;
|
|
178
|
-
private statementCache;
|
|
179
|
-
private isClosed;
|
|
180
|
-
constructor(options?: SqliteProcessMetadataStoreOptions);
|
|
181
|
-
private initTables;
|
|
182
|
-
/**
|
|
183
|
-
* 旧库结构迁移:逐列探测并补充新增字段,重复执行安全(幂等)。
|
|
184
|
-
*/
|
|
185
|
-
private migrateSchema;
|
|
186
|
-
/**
|
|
187
|
-
* 枚举指定表的全部列定义。
|
|
188
|
-
*/
|
|
189
|
-
private listTableColumns;
|
|
190
|
-
private getStatement;
|
|
191
|
-
saveProcess(process: StoredProcessRecord): Promise<void>;
|
|
192
|
-
getProcess(processId: string): Promise<StoredProcessRecord | undefined>;
|
|
193
|
-
listProcesses(owner: ProcessOwnerFilter, pageToken?: string, limit?: number): Promise<{
|
|
194
|
-
processes: ProcessInfo[];
|
|
195
|
-
nextPageToken?: string;
|
|
196
|
-
}>;
|
|
197
|
-
updateProcessState(processId: string, patch: Partial<ProcessInfo>): Promise<void>;
|
|
198
|
-
recordRequest(key: ProcessRequestKey, receipt: OperationReceipt, payloadHash?: string): Promise<void>;
|
|
199
|
-
getRequest(key: ProcessRequestKey): Promise<{
|
|
200
|
-
receipt: OperationReceipt;
|
|
201
|
-
payloadHash?: string;
|
|
202
|
-
} | undefined>;
|
|
203
|
-
initializeHost(hostEpoch: string): Promise<number>;
|
|
204
|
-
close(): void;
|
|
205
|
-
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
import { createDefaultSqliteDriver } from "../storage/driver.js";
|
|
1
|
+
import { ActionDockError, PROCESS_NOT_FOUND } from "../errors.js";
|
|
4
2
|
/**
|
|
5
3
|
* 解析分页游标偏移量。
|
|
6
4
|
*/
|
|
@@ -69,7 +67,7 @@ export class MemoryProcessMetadataStore {
|
|
|
69
67
|
async updateProcessState(processId, patch) {
|
|
70
68
|
const target = this.processes.get(processId);
|
|
71
69
|
if (!target) {
|
|
72
|
-
throw new
|
|
70
|
+
throw new ActionDockError(PROCESS_NOT_FOUND, `Process '${processId}' not found`);
|
|
73
71
|
}
|
|
74
72
|
const ctrl = patch.controlState !== undefined ? patch.controlState : patch.control;
|
|
75
73
|
const updated = {
|
|
@@ -123,340 +121,3 @@ export class MemoryProcessMetadataStore {
|
|
|
123
121
|
this.requests.clear();
|
|
124
122
|
}
|
|
125
123
|
}
|
|
126
|
-
/**
|
|
127
|
-
* 将数据库行反序列化为进程模型。
|
|
128
|
-
*/
|
|
129
|
-
function mapRowToStoredProcess(row) {
|
|
130
|
-
return {
|
|
131
|
-
processId: row.process_id,
|
|
132
|
-
tenantId: row.tenant_id,
|
|
133
|
-
principalId: row.principal_id,
|
|
134
|
-
packageInstanceId: row.package_instance_id,
|
|
135
|
-
generationId: row.generation_id,
|
|
136
|
-
hostEpoch: row.host_epoch,
|
|
137
|
-
state: row.state,
|
|
138
|
-
controlState: row.control_state ?? undefined,
|
|
139
|
-
control: row.control_state ?? undefined,
|
|
140
|
-
ioConfig: row.io_config_json ? JSON.parse(row.io_config_json) : undefined,
|
|
141
|
-
capabilities: row.capabilities_json ? JSON.parse(row.capabilities_json) : undefined,
|
|
142
|
-
createdAt: row.created_at,
|
|
143
|
-
exitCode: row.exit_code !== null && row.exit_code !== undefined ? Number(row.exit_code) : undefined,
|
|
144
|
-
exitSignal: row.exit_signal ?? undefined,
|
|
145
|
-
endReason: row.end_reason ?? undefined,
|
|
146
|
-
outputClosed: Boolean(row.output_closed),
|
|
147
|
-
outputEndReason: row.output_end_reason ?? undefined,
|
|
148
|
-
inputClosed: Boolean(row.input_closed),
|
|
149
|
-
effectiveLimits: row.effective_limits_json ? JSON.parse(row.effective_limits_json) : undefined,
|
|
150
|
-
startRequestId: row.start_request_id ?? undefined,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* 基于 SQLite 的受管进程元数据持久化存储实现。
|
|
155
|
-
*/
|
|
156
|
-
export class SqliteProcessMetadataStore {
|
|
157
|
-
driver;
|
|
158
|
-
statementCache = new Map();
|
|
159
|
-
isClosed = false;
|
|
160
|
-
constructor(options = {}) {
|
|
161
|
-
const dbPath = options.dbPath || ":memory:";
|
|
162
|
-
if (dbPath !== ":memory:") {
|
|
163
|
-
const dir = dirname(dbPath);
|
|
164
|
-
if (!existsSync(dir)) {
|
|
165
|
-
try {
|
|
166
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
167
|
-
chmodSync(dir, 0o700);
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
// 忽略系统权限配置失败
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
this.driver = options.driver ?? createDefaultSqliteDriver(dbPath);
|
|
175
|
-
if (dbPath !== ":memory:" && existsSync(dbPath)) {
|
|
176
|
-
try {
|
|
177
|
-
chmodSync(dbPath, 0o600);
|
|
178
|
-
}
|
|
179
|
-
catch {
|
|
180
|
-
// 忽略文件权限配置异常
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
this.initTables();
|
|
184
|
-
}
|
|
185
|
-
initTables() {
|
|
186
|
-
this.driver.exec("PRAGMA journal_mode = WAL;");
|
|
187
|
-
this.driver.exec("PRAGMA synchronous = NORMAL;");
|
|
188
|
-
this.driver.exec(`
|
|
189
|
-
CREATE TABLE IF NOT EXISTS managed_processes (
|
|
190
|
-
process_id TEXT PRIMARY KEY,
|
|
191
|
-
tenant_id TEXT NOT NULL,
|
|
192
|
-
principal_id TEXT NOT NULL,
|
|
193
|
-
package_instance_id TEXT NOT NULL,
|
|
194
|
-
generation_id TEXT NOT NULL,
|
|
195
|
-
host_epoch TEXT NOT NULL,
|
|
196
|
-
state TEXT NOT NULL,
|
|
197
|
-
control_state TEXT,
|
|
198
|
-
io_config_json TEXT,
|
|
199
|
-
capabilities_json TEXT,
|
|
200
|
-
created_at TEXT NOT NULL,
|
|
201
|
-
exit_code INTEGER,
|
|
202
|
-
exit_signal TEXT,
|
|
203
|
-
end_reason TEXT,
|
|
204
|
-
output_closed INTEGER NOT NULL DEFAULT 0,
|
|
205
|
-
output_end_reason TEXT,
|
|
206
|
-
input_closed INTEGER NOT NULL DEFAULT 0,
|
|
207
|
-
effective_limits_json TEXT,
|
|
208
|
-
start_request_id TEXT
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
CREATE INDEX IF NOT EXISTS idx_processes_owner_created
|
|
212
|
-
ON managed_processes(tenant_id, principal_id, package_instance_id, generation_id, created_at DESC, process_id ASC);
|
|
213
|
-
|
|
214
|
-
CREATE INDEX IF NOT EXISTS idx_processes_host_state
|
|
215
|
-
ON managed_processes(host_epoch, state);
|
|
216
|
-
|
|
217
|
-
CREATE INDEX IF NOT EXISTS idx_processes_start_req
|
|
218
|
-
ON managed_processes(start_request_id);
|
|
219
|
-
|
|
220
|
-
CREATE TABLE IF NOT EXISTS process_requests (
|
|
221
|
-
host_epoch TEXT NOT NULL,
|
|
222
|
-
scope TEXT NOT NULL,
|
|
223
|
-
process_id TEXT NOT NULL DEFAULT '',
|
|
224
|
-
request_id TEXT NOT NULL,
|
|
225
|
-
receipt_json TEXT NOT NULL,
|
|
226
|
-
payload_hash TEXT,
|
|
227
|
-
created_at TEXT NOT NULL,
|
|
228
|
-
PRIMARY KEY (host_epoch, scope, process_id, request_id)
|
|
229
|
-
);
|
|
230
|
-
`);
|
|
231
|
-
this.migrateSchema();
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* 旧库结构迁移:逐列探测并补充新增字段,重复执行安全(幂等)。
|
|
235
|
-
*/
|
|
236
|
-
migrateSchema() {
|
|
237
|
-
const columns = new Set();
|
|
238
|
-
for (const row of this.listTableColumns("managed_processes")) {
|
|
239
|
-
columns.add(String(row.name));
|
|
240
|
-
}
|
|
241
|
-
if (!columns.has("input_closed")) {
|
|
242
|
-
this.driver.exec("ALTER TABLE managed_processes ADD COLUMN input_closed INTEGER NOT NULL DEFAULT 0;");
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* 枚举指定表的全部列定义。
|
|
247
|
-
*/
|
|
248
|
-
listTableColumns(tableName) {
|
|
249
|
-
try {
|
|
250
|
-
const stmt = this.driver.prepare(`PRAGMA table_info(${tableName})`);
|
|
251
|
-
return stmt.all() ?? [];
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
return [];
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
getStatement(sql) {
|
|
258
|
-
let stmt = this.statementCache.get(sql);
|
|
259
|
-
if (!stmt) {
|
|
260
|
-
stmt = this.driver.prepare(sql);
|
|
261
|
-
this.statementCache.set(sql, stmt);
|
|
262
|
-
}
|
|
263
|
-
return stmt;
|
|
264
|
-
}
|
|
265
|
-
async saveProcess(process) {
|
|
266
|
-
if (this.isClosed) {
|
|
267
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
268
|
-
}
|
|
269
|
-
const stmt = this.getStatement(`
|
|
270
|
-
INSERT INTO managed_processes (
|
|
271
|
-
process_id, tenant_id, principal_id, package_instance_id, generation_id,
|
|
272
|
-
host_epoch, state, control_state, io_config_json, capabilities_json,
|
|
273
|
-
created_at, exit_code, exit_signal, end_reason, output_closed,
|
|
274
|
-
output_end_reason, input_closed, effective_limits_json, start_request_id
|
|
275
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
276
|
-
ON CONFLICT(process_id) DO UPDATE SET
|
|
277
|
-
tenant_id = excluded.tenant_id,
|
|
278
|
-
principal_id = excluded.principal_id,
|
|
279
|
-
package_instance_id = excluded.package_instance_id,
|
|
280
|
-
generation_id = excluded.generation_id,
|
|
281
|
-
host_epoch = excluded.host_epoch,
|
|
282
|
-
state = excluded.state,
|
|
283
|
-
control_state = excluded.control_state,
|
|
284
|
-
io_config_json = excluded.io_config_json,
|
|
285
|
-
capabilities_json = excluded.capabilities_json,
|
|
286
|
-
created_at = excluded.created_at,
|
|
287
|
-
exit_code = excluded.exit_code,
|
|
288
|
-
exit_signal = excluded.exit_signal,
|
|
289
|
-
end_reason = excluded.end_reason,
|
|
290
|
-
output_closed = excluded.output_closed,
|
|
291
|
-
output_end_reason = excluded.output_end_reason,
|
|
292
|
-
input_closed = excluded.input_closed,
|
|
293
|
-
effective_limits_json = excluded.effective_limits_json,
|
|
294
|
-
start_request_id = excluded.start_request_id
|
|
295
|
-
`);
|
|
296
|
-
const createdAt = process.createdAt ?? new Date().toISOString();
|
|
297
|
-
const ctrlState = process.controlState ?? process.control ?? null;
|
|
298
|
-
const ioConfigJson = process.ioConfig !== undefined ? JSON.stringify(process.ioConfig) : null;
|
|
299
|
-
const capabilitiesJson = process.capabilities !== undefined ? JSON.stringify(process.capabilities) : null;
|
|
300
|
-
const effectiveLimitsJson = process.effectiveLimits !== undefined ? JSON.stringify(process.effectiveLimits) : null;
|
|
301
|
-
const outputClosed = process.outputClosed ? 1 : 0;
|
|
302
|
-
const inputClosed = process.inputClosed ? 1 : 0;
|
|
303
|
-
stmt.run(process.processId, process.tenantId, process.principalId, process.packageInstanceId, process.generationId, process.hostEpoch, process.state, ctrlState, ioConfigJson, capabilitiesJson, createdAt, process.exitCode !== undefined && process.exitCode !== null ? process.exitCode : null, process.exitSignal !== undefined ? process.exitSignal : null, process.endReason !== undefined ? process.endReason : null, outputClosed, process.outputEndReason !== undefined ? process.outputEndReason : null, inputClosed, effectiveLimitsJson, process.startRequestId !== undefined ? process.startRequestId : null);
|
|
304
|
-
}
|
|
305
|
-
async getProcess(processId) {
|
|
306
|
-
if (this.isClosed) {
|
|
307
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
308
|
-
}
|
|
309
|
-
const stmt = this.getStatement("SELECT * FROM managed_processes WHERE process_id = ?");
|
|
310
|
-
const row = stmt.get(processId);
|
|
311
|
-
if (!row) {
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
return mapRowToStoredProcess(row);
|
|
315
|
-
}
|
|
316
|
-
async listProcesses(owner, pageToken, limit) {
|
|
317
|
-
if (this.isClosed) {
|
|
318
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
319
|
-
}
|
|
320
|
-
const actualLimit = typeof limit === "number" && limit > 0 ? limit : 50;
|
|
321
|
-
const offset = parsePageToken(pageToken);
|
|
322
|
-
const stmt = this.getStatement(`
|
|
323
|
-
SELECT * FROM managed_processes
|
|
324
|
-
WHERE tenant_id = ? AND principal_id = ? AND package_instance_id = ? AND generation_id = ?
|
|
325
|
-
ORDER BY created_at DESC, process_id ASC
|
|
326
|
-
LIMIT ? OFFSET ?
|
|
327
|
-
`);
|
|
328
|
-
const rows = stmt.all(owner.tenantId, owner.principalId, owner.packageInstanceId, owner.generationId, actualLimit + 1, offset);
|
|
329
|
-
const hasMore = rows.length > actualLimit;
|
|
330
|
-
const resultRows = hasMore ? rows.slice(0, actualLimit) : rows;
|
|
331
|
-
const processes = resultRows.map(mapRowToStoredProcess);
|
|
332
|
-
const nextPageToken = hasMore ? String(offset + actualLimit) : undefined;
|
|
333
|
-
return { processes, nextPageToken };
|
|
334
|
-
}
|
|
335
|
-
async updateProcessState(processId, patch) {
|
|
336
|
-
if (this.isClosed) {
|
|
337
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
338
|
-
}
|
|
339
|
-
const sets = [];
|
|
340
|
-
const params = [];
|
|
341
|
-
if (patch.state !== undefined) {
|
|
342
|
-
sets.push("state = ?");
|
|
343
|
-
params.push(patch.state);
|
|
344
|
-
}
|
|
345
|
-
const ctrl = patch.controlState !== undefined ? patch.controlState : patch.control;
|
|
346
|
-
if (ctrl !== undefined) {
|
|
347
|
-
sets.push("control_state = ?");
|
|
348
|
-
params.push(ctrl);
|
|
349
|
-
}
|
|
350
|
-
if (patch.exitCode !== undefined) {
|
|
351
|
-
sets.push("exit_code = ?");
|
|
352
|
-
params.push(patch.exitCode !== null ? patch.exitCode : null);
|
|
353
|
-
}
|
|
354
|
-
if (patch.exitSignal !== undefined) {
|
|
355
|
-
sets.push("exit_signal = ?");
|
|
356
|
-
params.push(patch.exitSignal);
|
|
357
|
-
}
|
|
358
|
-
if (patch.endReason !== undefined) {
|
|
359
|
-
sets.push("end_reason = ?");
|
|
360
|
-
params.push(patch.endReason);
|
|
361
|
-
}
|
|
362
|
-
if (patch.outputClosed !== undefined) {
|
|
363
|
-
sets.push("output_closed = ?");
|
|
364
|
-
params.push(patch.outputClosed ? 1 : 0);
|
|
365
|
-
}
|
|
366
|
-
if (patch.outputEndReason !== undefined) {
|
|
367
|
-
sets.push("output_end_reason = ?");
|
|
368
|
-
params.push(patch.outputEndReason);
|
|
369
|
-
}
|
|
370
|
-
if (patch.inputClosed !== undefined) {
|
|
371
|
-
sets.push("input_closed = ?");
|
|
372
|
-
params.push(patch.inputClosed ? 1 : 0);
|
|
373
|
-
}
|
|
374
|
-
if (patch.effectiveLimits !== undefined) {
|
|
375
|
-
sets.push("effective_limits_json = ?");
|
|
376
|
-
params.push(patch.effectiveLimits !== null ? JSON.stringify(patch.effectiveLimits) : null);
|
|
377
|
-
}
|
|
378
|
-
if (patch.ioConfig !== undefined) {
|
|
379
|
-
sets.push("io_config_json = ?");
|
|
380
|
-
params.push(patch.ioConfig !== null ? JSON.stringify(patch.ioConfig) : null);
|
|
381
|
-
}
|
|
382
|
-
if (patch.capabilities !== undefined) {
|
|
383
|
-
sets.push("capabilities_json = ?");
|
|
384
|
-
params.push(patch.capabilities !== null ? JSON.stringify(patch.capabilities) : null);
|
|
385
|
-
}
|
|
386
|
-
if (patch.hostEpoch !== undefined) {
|
|
387
|
-
sets.push("host_epoch = ?");
|
|
388
|
-
params.push(patch.hostEpoch);
|
|
389
|
-
}
|
|
390
|
-
if (sets.length === 0) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
params.push(processId);
|
|
394
|
-
const sql = `UPDATE managed_processes SET ${sets.join(", ")} WHERE process_id = ?`;
|
|
395
|
-
const stmt = this.getStatement(sql);
|
|
396
|
-
const res = stmt.run(...params);
|
|
397
|
-
if (res.changes === 0) {
|
|
398
|
-
throw new Error(`Process '${processId}' not found`);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
async recordRequest(key, receipt, payloadHash) {
|
|
402
|
-
if (this.isClosed) {
|
|
403
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
404
|
-
}
|
|
405
|
-
const stmt = this.getStatement(`
|
|
406
|
-
INSERT INTO process_requests (
|
|
407
|
-
host_epoch, scope, process_id, request_id, receipt_json, payload_hash, created_at
|
|
408
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
409
|
-
ON CONFLICT(host_epoch, scope, process_id, request_id) DO UPDATE SET
|
|
410
|
-
receipt_json = excluded.receipt_json,
|
|
411
|
-
payload_hash = excluded.payload_hash,
|
|
412
|
-
created_at = excluded.created_at
|
|
413
|
-
`);
|
|
414
|
-
const processId = key.processId ?? "";
|
|
415
|
-
const receiptJson = JSON.stringify(receipt);
|
|
416
|
-
const createdAt = new Date().toISOString();
|
|
417
|
-
stmt.run(key.hostEpoch, key.scope, processId, key.requestId, receiptJson, payloadHash ?? null, createdAt);
|
|
418
|
-
}
|
|
419
|
-
async getRequest(key) {
|
|
420
|
-
if (this.isClosed) {
|
|
421
|
-
throw new Error("SqliteProcessMetadataStore is closed");
|
|
422
|
-
}
|
|
423
|
-
const stmt = this.getStatement(`
|
|
424
|
-
SELECT receipt_json, payload_hash FROM process_requests
|
|
425
|
-
WHERE host_epoch = ? AND scope = ? AND process_id = ? AND request_id = ?
|
|
426
|
-
`);
|
|
427
|
-
const processId = key.processId ?? "";
|
|
428
|
-
const row = stmt.get(key.hostEpoch, key.scope, processId, key.requestId);
|
|
429
|
-
if (!row) {
|
|
430
|
-
return undefined;
|
|
431
|
-
}
|
|
432
|
-
return {
|
|
433
|
-
receipt: JSON.parse(row.receipt_json),
|
|
434
|
-
payloadHash: row.payload_hash ?? undefined,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
async initializeHost(hostEpoch) {
|
|
438
|
-
if (this.isClosed) {
|
|
439
|
-
return 0;
|
|
440
|
-
}
|
|
441
|
-
const stmt = this.getStatement(`
|
|
442
|
-
UPDATE managed_processes
|
|
443
|
-
SET state = 'lost',
|
|
444
|
-
control_state = 'closed',
|
|
445
|
-
end_reason = 'host-lost',
|
|
446
|
-
output_closed = 1,
|
|
447
|
-
output_end_reason = 'host-lost'
|
|
448
|
-
WHERE host_epoch != ?
|
|
449
|
-
AND state IN ('starting', 'running', 'stopping')
|
|
450
|
-
`);
|
|
451
|
-
const res = stmt.run(hostEpoch);
|
|
452
|
-
return res.changes;
|
|
453
|
-
}
|
|
454
|
-
close() {
|
|
455
|
-
if (this.isClosed) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
this.isClosed = true;
|
|
459
|
-
this.statementCache.clear();
|
|
460
|
-
this.driver.close();
|
|
461
|
-
}
|
|
462
|
-
}
|