@actiondock/core 2.0.12 → 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 +1 -1
- package/dist/catalog/action-index.js +93 -0
- package/dist/catalog/action-resolver.d.ts +2 -2
- package/dist/catalog/action-resolver.js +95 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -0
- package/dist/catalog/location-registry.d.ts +5 -5
- package/dist/catalog/location-registry.js +138 -0
- package/dist/catalog/package-catalog.d.ts +2 -2
- package/dist/catalog/package-catalog.js +76 -0
- package/dist/catalog/types.d.ts +2 -2
- package/dist/catalog/types.js +1 -0
- package/dist/doctor/doctor.d.ts +2 -1
- package/dist/doctor/doctor.js +613 -0
- package/dist/doctor/index.d.ts +2 -2
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/types.js +1 -0
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +123 -0
- package/dist/execution/index.d.ts +2 -2
- package/dist/execution/index.js +2 -0
- package/dist/execution/service.d.ts +46 -41
- package/dist/execution/service.js +633 -0
- package/dist/execution/types.d.ts +59 -2
- package/dist/execution/types.js +1 -0
- package/dist/export/index.d.ts +1 -1
- package/dist/export/index.js +1 -0
- package/dist/export/templates.d.ts +45 -7
- package/dist/export/templates.js +621 -0
- package/dist/filter/index.d.ts +1 -1
- package/dist/filter/index.js +1 -0
- package/dist/filter/intent.js +123 -0
- package/dist/host/host.d.ts +55 -0
- package/dist/host/host.js +740 -0
- package/dist/host/index.d.ts +2 -0
- package/dist/host/index.js +2 -0
- package/dist/host/types.d.ts +81 -0
- package/dist/host/types.js +1 -0
- package/dist/index.d.ts +21 -16
- package/dist/index.js +20 -8458
- package/dist/ipc/diagnostic.d.ts +65 -0
- package/dist/ipc/diagnostic.js +129 -0
- package/dist/ipc/host.d.ts +7 -0
- package/dist/ipc/host.js +93 -0
- package/dist/ipc/index.d.ts +4 -0
- package/dist/ipc/index.js +4 -0
- package/dist/ipc/target.d.ts +56 -0
- package/dist/ipc/target.js +297 -0
- package/dist/ipc/types.d.ts +42 -0
- package/dist/ipc/types.js +1 -0
- package/dist/platform/default.d.ts +32 -0
- package/dist/platform/default.js +40 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/index.js +3 -0
- package/dist/platform/node-fs.d.ts +37 -0
- package/dist/platform/node-fs.js +81 -0
- package/dist/platform/types.d.ts +104 -0
- package/dist/platform/types.js +1 -0
- package/dist/profile/client.d.ts +58 -12
- package/dist/profile/client.js +467 -0
- package/dist/profile/index.d.ts +3 -3
- package/dist/profile/index.js +3 -0
- package/dist/profile/manager.d.ts +2 -2
- package/dist/profile/manager.js +294 -0
- package/dist/profile/types.d.ts +1 -1
- package/dist/profile/types.js +1 -0
- package/dist/project/closure.js +68 -0
- package/dist/project/digest.d.ts +29 -0
- package/dist/project/digest.js +324 -0
- package/dist/project/index.d.ts +10 -5
- package/dist/project/index.js +10 -0
- package/dist/project/init.d.ts +3 -4
- package/dist/project/init.js +175 -0
- package/dist/project/loader.d.ts +21 -27
- package/dist/project/loader.js +484 -0
- package/dist/project/lockfile.d.ts +55 -0
- package/dist/project/lockfile.js +175 -0
- package/dist/project/manifest.d.ts +6 -28
- package/dist/project/manifest.js +220 -0
- package/dist/project/resolver.d.ts +116 -0
- package/dist/project/resolver.js +376 -0
- package/dist/project/transactions.d.ts +103 -0
- package/dist/project/transactions.js +1203 -0
- package/dist/project/types-generator.d.ts +33 -0
- package/dist/project/types-generator.js +168 -0
- package/dist/project/types.d.ts +58 -82
- package/dist/project/types.js +1 -0
- package/dist/registry/index.d.ts +5 -2
- package/dist/registry/index.js +5 -0
- package/dist/registry/lock.d.ts +27 -0
- package/dist/registry/lock.js +93 -0
- package/dist/registry/registry.d.ts +32 -11
- package/dist/registry/registry.js +611 -0
- package/dist/registry/resolve.d.ts +62 -0
- package/dist/registry/resolve.js +195 -0
- package/dist/registry/scan.d.ts +38 -0
- package/dist/registry/scan.js +135 -0
- package/dist/registry/types.d.ts +1 -1
- package/dist/registry/types.js +1 -0
- package/dist/runtime/action-collection.d.ts +27 -0
- package/dist/runtime/action-collection.js +57 -0
- package/dist/runtime/clock.d.ts +0 -2
- package/dist/runtime/clock.js +17 -0
- package/dist/runtime/context.d.ts +16 -5
- package/dist/runtime/context.js +242 -0
- package/dist/runtime/env.d.ts +3 -1
- package/dist/runtime/env.js +159 -0
- package/dist/runtime/events.d.ts +46 -14
- package/dist/runtime/events.js +604 -0
- package/dist/runtime/execution-manager.d.ts +1 -1
- package/dist/runtime/execution-manager.js +66 -0
- package/dist/runtime/index.d.ts +10 -9
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/module-loader.d.ts +0 -8
- package/dist/runtime/module-loader.js +16 -0
- package/dist/runtime/process.d.ts +2 -4
- package/dist/runtime/process.js +146 -0
- package/dist/runtime/runner.d.ts +117 -10
- package/dist/runtime/runner.js +984 -0
- package/dist/runtime/standalone.d.ts +68 -19
- package/dist/runtime/standalone.js +536 -0
- package/dist/schema/validator.js +116 -0
- package/dist/server/body.js +93 -0
- package/dist/server/index.d.ts +5 -6
- package/dist/server/index.js +5 -0
- package/dist/server/routes/actions.d.ts +2 -2
- package/dist/server/routes/actions.js +298 -0
- package/dist/server/routes/common.d.ts +28 -17
- package/dist/server/routes/common.js +104 -0
- package/dist/server/routes/config.d.ts +2 -1
- package/dist/server/routes/config.js +173 -0
- package/dist/server/routes/doctor.d.ts +2 -2
- package/dist/server/routes/doctor.js +63 -0
- package/dist/server/routes/health.d.ts +2 -2
- package/dist/server/routes/health.js +35 -0
- package/dist/server/routes/index.d.ts +9 -9
- package/dist/server/routes/index.js +9 -0
- package/dist/server/routes/info.d.ts +4 -2
- package/dist/server/routes/info.js +158 -0
- package/dist/server/routes/playbooks.d.ts +2 -2
- package/dist/server/routes/playbooks.js +134 -0
- package/dist/server/routes/runs.d.ts +2 -2
- package/dist/server/routes/runs.js +343 -0
- package/dist/server/routes/state.d.ts +2 -1
- package/dist/server/routes/state.js +169 -0
- package/dist/server/security.d.ts +11 -4
- package/dist/server/security.js +101 -0
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +355 -0
- package/dist/server/types.d.ts +27 -8
- package/dist/server/types.js +1 -0
- package/dist/storage/data-dir-lock.d.ts +119 -0
- package/dist/storage/data-dir-lock.js +1016 -0
- package/dist/storage/driver.d.ts +4 -10
- package/dist/storage/driver.js +59 -0
- package/dist/storage/index.d.ts +30 -10
- package/dist/storage/index.js +98 -0
- package/dist/storage/lazy.d.ts +8 -0
- package/dist/storage/lazy.js +44 -0
- package/dist/storage/mask.d.ts +12 -5
- package/dist/storage/mask.js +48 -0
- package/dist/storage/params.d.ts +13 -0
- package/dist/storage/params.js +32 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +689 -0
- package/dist/storage/types.d.ts +57 -6
- package/dist/storage/types.js +14 -0
- package/dist/target/index.d.ts +4 -0
- package/dist/target/index.js +4 -0
- package/dist/target/local.d.ts +52 -0
- package/dist/target/local.js +343 -0
- package/dist/target/remote.d.ts +76 -0
- package/dist/target/remote.js +839 -0
- package/dist/target/target.d.ts +6 -0
- package/dist/target/target.js +35 -0
- package/dist/target/types.d.ts +246 -0
- package/dist/target/types.js +34 -0
- package/dist/utils/index.d.ts +22 -2
- package/dist/utils/index.js +180 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +4 -0
- package/package.json +6 -7
- package/dist/build/index.d.ts +0 -1
- package/dist/build/templates.d.ts +0 -19
- package/dist/server/runtime-registry.d.ts +0 -78
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import { ActionResolver } from "../catalog/action-resolver.js";
|
|
2
|
+
import { cancelRemoteRun, clearRemoteRuns, clearRemoteState, deleteRemoteConfig, deleteRemoteStateKey, executeRemoteAction, fetchRemoteActionShow, fetchRemoteActions, fetchRemoteConfig, fetchRemoteInfo, fetchRemotePlaybookShow, fetchRemotePlaybooks, fetchRemoteRun, fetchRemoteRuns, fetchRemoteStateList, getRemoteStateKey, setRemoteConfig, setRemoteStateKey, assertSecureTransport, } from "../profile/client.js";
|
|
3
|
+
import { normalizeServerUrl } from "../profile/manager.js";
|
|
4
|
+
import { isTerminalRunStatus } from "../storage/types.js";
|
|
5
|
+
import { ACTIONDOCK_PROTOCOL_VERSION, TargetError, TARGET_PROTOCOL_UNSUPPORTED, TARGET_CAPABILITY_UNAVAILABLE, TARGET_RESULT_UNKNOWN, } from "./types.js";
|
|
6
|
+
import { ACTION_CANCELLED, EXECUTION_FAILED, STATE_KEY_NOT_FOUND, TIMEOUT } from "../errors.js";
|
|
7
|
+
/**
|
|
8
|
+
* 读取并解析远端 SSE 事件流。
|
|
9
|
+
*/
|
|
10
|
+
export async function* streamRemoteEvents(serverUrl, runId, token, options) {
|
|
11
|
+
assertSecureTransport(serverUrl, token, options?.allowInsecureHttp);
|
|
12
|
+
const base = normalizeServerUrl(serverUrl);
|
|
13
|
+
const candidateUrls = [
|
|
14
|
+
`${base}/api/v2/runs/${encodeURIComponent(runId)}/events`,
|
|
15
|
+
`${base}/api/v2/runs/${encodeURIComponent(runId)}/stream`,
|
|
16
|
+
`${base}/api/v1/runs/${encodeURIComponent(runId)}/stream`,
|
|
17
|
+
];
|
|
18
|
+
const headers = {
|
|
19
|
+
Accept: "text/event-stream",
|
|
20
|
+
};
|
|
21
|
+
if (token && token.trim()) {
|
|
22
|
+
headers.Authorization = `Bearer ${token.trim()}`;
|
|
23
|
+
}
|
|
24
|
+
if (options?.after !== undefined) {
|
|
25
|
+
headers["Last-Event-ID"] = String(options.after);
|
|
26
|
+
}
|
|
27
|
+
let res;
|
|
28
|
+
for (const url of candidateUrls) {
|
|
29
|
+
try {
|
|
30
|
+
const resp = await fetch(url, {
|
|
31
|
+
headers,
|
|
32
|
+
signal: options?.signal,
|
|
33
|
+
});
|
|
34
|
+
if (resp.status === 410) {
|
|
35
|
+
let errJson;
|
|
36
|
+
try {
|
|
37
|
+
errJson = await resp.json();
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
const err = new Error(errJson?.error?.message || "Event cursor has expired");
|
|
41
|
+
err.code = errJson?.error?.code || "EVENT_CURSOR_EXPIRED";
|
|
42
|
+
err.details = errJson?.error?.details;
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
if (resp.ok && resp.body) {
|
|
46
|
+
res = resp;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
if (resp.status !== 404) {
|
|
50
|
+
res = resp;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err?.code === "EVENT_CURSOR_EXPIRED") {
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
// 忽略单次网络连接异常并尝试备选路由
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!res || !res.ok || !res.body) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const reader = res.body.getReader();
|
|
65
|
+
const decoder = new TextDecoder();
|
|
66
|
+
let buffer = "";
|
|
67
|
+
let eventType = "message";
|
|
68
|
+
let eventId;
|
|
69
|
+
let dataLines = [];
|
|
70
|
+
function dispatchCurrentEvent() {
|
|
71
|
+
if (dataLines.length === 0) {
|
|
72
|
+
eventType = "message";
|
|
73
|
+
eventId = undefined;
|
|
74
|
+
dataLines = [];
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const dataStr = dataLines.join("\n");
|
|
78
|
+
const currentType = eventType;
|
|
79
|
+
const currentId = eventId;
|
|
80
|
+
eventType = "message";
|
|
81
|
+
eventId = undefined;
|
|
82
|
+
dataLines = [];
|
|
83
|
+
if (!dataStr) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const data = JSON.parse(dataStr);
|
|
88
|
+
if (currentType === "finish") {
|
|
89
|
+
return {
|
|
90
|
+
eventId: currentId ?? data.eventId,
|
|
91
|
+
type: "finish",
|
|
92
|
+
runId,
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
result: data.result || data,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return {
|
|
99
|
+
eventId: currentId ?? data.eventId,
|
|
100
|
+
type: (currentType || "message"),
|
|
101
|
+
runId,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
...data,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// 忽略非 JSON 数据行
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function processLine(line) {
|
|
113
|
+
// 遇到空行分发事件
|
|
114
|
+
if (line === "") {
|
|
115
|
+
return dispatchCurrentEvent();
|
|
116
|
+
}
|
|
117
|
+
// 以冒号开头的为注释忽略
|
|
118
|
+
if (line.startsWith(":")) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
let field = line;
|
|
122
|
+
let value = "";
|
|
123
|
+
const colonIdx = line.indexOf(":");
|
|
124
|
+
if (colonIdx !== -1) {
|
|
125
|
+
field = line.slice(0, colonIdx);
|
|
126
|
+
const rawVal = line.slice(colonIdx + 1);
|
|
127
|
+
value = rawVal.startsWith(" ") ? rawVal.slice(1) : rawVal;
|
|
128
|
+
}
|
|
129
|
+
if (field === "event") {
|
|
130
|
+
eventType = value;
|
|
131
|
+
}
|
|
132
|
+
else if (field === "data") {
|
|
133
|
+
dataLines.push(value);
|
|
134
|
+
}
|
|
135
|
+
else if (field === "id") {
|
|
136
|
+
if (!value.includes("\0")) {
|
|
137
|
+
eventId = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
while (true) {
|
|
144
|
+
if (options?.signal?.aborted)
|
|
145
|
+
break;
|
|
146
|
+
const { done, value } = await reader.read();
|
|
147
|
+
if (value) {
|
|
148
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
149
|
+
}
|
|
150
|
+
else if (done) {
|
|
151
|
+
buffer += decoder.decode();
|
|
152
|
+
}
|
|
153
|
+
let pos = 0;
|
|
154
|
+
while (pos < buffer.length) {
|
|
155
|
+
const cr = buffer.indexOf("\r", pos);
|
|
156
|
+
const lf = buffer.indexOf("\n", pos);
|
|
157
|
+
let nextSepPos = -1;
|
|
158
|
+
let sepLen = 0;
|
|
159
|
+
if (cr !== -1 && (lf === -1 || cr < lf)) {
|
|
160
|
+
if (cr === buffer.length - 1) {
|
|
161
|
+
if (!done) {
|
|
162
|
+
// 遇到未完结的 \r 暂存等待下个 chunk
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
nextSepPos = cr;
|
|
167
|
+
sepLen = 1;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
if (buffer[cr + 1] === "\n") {
|
|
172
|
+
nextSepPos = cr;
|
|
173
|
+
sepLen = 2;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
nextSepPos = cr;
|
|
177
|
+
sepLen = 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (lf !== -1 && (cr === -1 || lf < cr)) {
|
|
182
|
+
nextSepPos = lf;
|
|
183
|
+
sepLen = 1;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
const line = buffer.slice(pos, nextSepPos);
|
|
189
|
+
pos = nextSepPos + sepLen;
|
|
190
|
+
const evt = processLine(line);
|
|
191
|
+
if (evt)
|
|
192
|
+
yield evt;
|
|
193
|
+
}
|
|
194
|
+
buffer = buffer.slice(pos);
|
|
195
|
+
if (done) {
|
|
196
|
+
if (buffer.length > 0) {
|
|
197
|
+
const line = buffer;
|
|
198
|
+
buffer = "";
|
|
199
|
+
const evt = processLine(line);
|
|
200
|
+
if (evt)
|
|
201
|
+
yield evt;
|
|
202
|
+
}
|
|
203
|
+
// 流读取完成时若有剩余待分发事件则进行分发
|
|
204
|
+
const remainingEvt = dispatchCurrentEvent();
|
|
205
|
+
if (remainingEvt)
|
|
206
|
+
yield remainingEvt;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
if (err.name === "AbortError" || options?.signal?.aborted) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
try {
|
|
219
|
+
reader.releaseLock();
|
|
220
|
+
}
|
|
221
|
+
catch { }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 远程 ActionDockTarget 门面实现。
|
|
226
|
+
* 内部封装基于 HTTP/SSE 协议与远端 ActionDock 服务端的通信管道。
|
|
227
|
+
*/
|
|
228
|
+
export class RemoteActionDockTarget {
|
|
229
|
+
serverUrl;
|
|
230
|
+
token;
|
|
231
|
+
timeoutMs;
|
|
232
|
+
baseTimeoutMs;
|
|
233
|
+
allowInsecureHttp;
|
|
234
|
+
constructor(options) {
|
|
235
|
+
this.serverUrl = options.serverUrl;
|
|
236
|
+
this.token = options.token;
|
|
237
|
+
this.timeoutMs = options.timeoutMs;
|
|
238
|
+
this.baseTimeoutMs = options.baseTimeoutMs ?? 60000;
|
|
239
|
+
this.allowInsecureHttp = options.allowInsecureHttp;
|
|
240
|
+
}
|
|
241
|
+
async info() {
|
|
242
|
+
let raw;
|
|
243
|
+
try {
|
|
244
|
+
raw = await fetchRemoteInfo(this.serverUrl, this.token, {
|
|
245
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
wrapRemoteError(err);
|
|
250
|
+
}
|
|
251
|
+
const protocolVersion = (raw && typeof raw === "object" && raw.protocolVersion) || ACTIONDOCK_PROTOCOL_VERSION;
|
|
252
|
+
if (protocolVersion && typeof protocolVersion === "string") {
|
|
253
|
+
const [remoteMajor] = protocolVersion.split(".");
|
|
254
|
+
const [currentMajor] = ACTIONDOCK_PROTOCOL_VERSION.split(".");
|
|
255
|
+
if (remoteMajor !== currentMajor) {
|
|
256
|
+
throw new TargetError(TARGET_PROTOCOL_UNSUPPORTED, `TARGET_PROTOCOL_UNSUPPORTED: Remote server protocol version '${protocolVersion}' is incompatible with expected '${ACTIONDOCK_PROTOCOL_VERSION}'`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let packages = [];
|
|
260
|
+
if (Array.isArray(raw)) {
|
|
261
|
+
packages = raw;
|
|
262
|
+
}
|
|
263
|
+
else if (raw && typeof raw === "object") {
|
|
264
|
+
if (Array.isArray(raw.packages)) {
|
|
265
|
+
packages = raw.packages;
|
|
266
|
+
}
|
|
267
|
+
else if (raw.packages && typeof raw.packages === "object") {
|
|
268
|
+
packages = Object.values(raw.packages);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
id: (raw && typeof raw === "object" && raw.id) || "remote-target",
|
|
273
|
+
name: (raw && typeof raw === "object" && raw.name) || "Remote ActionDock Server",
|
|
274
|
+
protocolVersion,
|
|
275
|
+
packages,
|
|
276
|
+
capabilities: (raw && typeof raw === "object" && Array.isArray(raw.capabilities))
|
|
277
|
+
? raw.capabilities
|
|
278
|
+
: ["actions", "playbooks", "runs", "events", "management.config", "management.state"],
|
|
279
|
+
idempotencyPolicy: raw && typeof raw === "object" ? raw.idempotencyPolicy : undefined,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async listPackages() {
|
|
283
|
+
const info = await this.info();
|
|
284
|
+
return info.packages;
|
|
285
|
+
}
|
|
286
|
+
async listActions(options) {
|
|
287
|
+
const rawList = await fetchRemoteActions(this.serverUrl, this.token, options?.query, {
|
|
288
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
289
|
+
});
|
|
290
|
+
let summaries = rawList.map((item) => ({
|
|
291
|
+
id: item.id,
|
|
292
|
+
description: item.description,
|
|
293
|
+
tags: item.tags,
|
|
294
|
+
inputSchema: item.inputSchema,
|
|
295
|
+
outputSchema: item.outputSchema,
|
|
296
|
+
packageId: item.packageId,
|
|
297
|
+
}));
|
|
298
|
+
if (options?.prefix) {
|
|
299
|
+
summaries = summaries.filter((s) => s.id.startsWith(options.prefix));
|
|
300
|
+
}
|
|
301
|
+
if (options?.tags && options.tags.length > 0) {
|
|
302
|
+
summaries = summaries.filter((s) => options.tags.every((t) => s.tags?.includes(t)));
|
|
303
|
+
}
|
|
304
|
+
return summaries;
|
|
305
|
+
}
|
|
306
|
+
async describeAction(ref) {
|
|
307
|
+
let parsed;
|
|
308
|
+
try {
|
|
309
|
+
parsed = ActionResolver.parseRef(ref);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
parsed = typeof ref === "object" ? ref : { actionId: ref };
|
|
313
|
+
}
|
|
314
|
+
const actionId = parsed.packageId
|
|
315
|
+
? `${parsed.packageId}/${parsed.actionId}`
|
|
316
|
+
: parsed.actionId;
|
|
317
|
+
const raw = await fetchRemoteActionShow(this.serverUrl, actionId, this.token, {
|
|
318
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
319
|
+
});
|
|
320
|
+
return {
|
|
321
|
+
id: raw.id,
|
|
322
|
+
description: raw.description,
|
|
323
|
+
inputSchema: raw.inputSchema,
|
|
324
|
+
outputSchema: raw.outputSchema,
|
|
325
|
+
tags: raw.tags,
|
|
326
|
+
annotations: raw.annotations,
|
|
327
|
+
uses: raw.uses,
|
|
328
|
+
entry: raw.entry,
|
|
329
|
+
filePath: raw.filePath,
|
|
330
|
+
packageId: raw.packageId ?? parsed.packageId,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
async listPlaybooks(options) {
|
|
334
|
+
const rawList = await fetchRemotePlaybooks(this.serverUrl, this.token, {
|
|
335
|
+
...options,
|
|
336
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
337
|
+
});
|
|
338
|
+
return rawList.map((item) => ({
|
|
339
|
+
id: item.id,
|
|
340
|
+
description: item.description,
|
|
341
|
+
actions: item.actions,
|
|
342
|
+
packageId: item.packageId,
|
|
343
|
+
filePath: item.filePath,
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
async describePlaybook(id) {
|
|
347
|
+
const raw = await fetchRemotePlaybookShow(this.serverUrl, id, this.token, {
|
|
348
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
349
|
+
});
|
|
350
|
+
const parsedPkgId = id.includes("/") ? id.slice(0, id.lastIndexOf("/")) : undefined;
|
|
351
|
+
return {
|
|
352
|
+
id: raw.id,
|
|
353
|
+
description: raw.description,
|
|
354
|
+
actions: raw.actions,
|
|
355
|
+
filePath: raw.filePath,
|
|
356
|
+
content: raw.content,
|
|
357
|
+
packageId: raw.packageId ?? parsedPkgId,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async runAction(ref, input, options) {
|
|
361
|
+
let parsed;
|
|
362
|
+
try {
|
|
363
|
+
parsed = ActionResolver.parseRef(ref);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
parsed = typeof ref === "object" ? ref : { actionId: ref };
|
|
367
|
+
}
|
|
368
|
+
const actionId = parsed.packageId
|
|
369
|
+
? `${parsed.packageId}/${parsed.actionId}`
|
|
370
|
+
: parsed.actionId;
|
|
371
|
+
return executeRemoteAction(this.serverUrl, actionId, input, {
|
|
372
|
+
configOverrides: options?.config,
|
|
373
|
+
token: this.token,
|
|
374
|
+
timeoutMs: options?.timeoutMs ?? this.timeoutMs,
|
|
375
|
+
signal: options?.signal,
|
|
376
|
+
requestId: options?.requestId,
|
|
377
|
+
async: false,
|
|
378
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async startAction(ref, input, options) {
|
|
382
|
+
let parsed;
|
|
383
|
+
try {
|
|
384
|
+
parsed = ActionResolver.parseRef(ref);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
parsed = typeof ref === "object" ? ref : { actionId: ref };
|
|
388
|
+
}
|
|
389
|
+
const actionId = parsed.packageId
|
|
390
|
+
? `${parsed.packageId}/${parsed.actionId}`
|
|
391
|
+
: parsed.actionId;
|
|
392
|
+
const res = await executeRemoteAction(this.serverUrl, actionId, input, {
|
|
393
|
+
configOverrides: options?.config,
|
|
394
|
+
token: this.token,
|
|
395
|
+
timeoutMs: options?.timeoutMs ?? this.timeoutMs,
|
|
396
|
+
signal: options?.signal,
|
|
397
|
+
requestId: options?.requestId,
|
|
398
|
+
async: true,
|
|
399
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
400
|
+
});
|
|
401
|
+
if (!res.ok) {
|
|
402
|
+
return {
|
|
403
|
+
runId: res.runId,
|
|
404
|
+
status: "failed",
|
|
405
|
+
result: Promise.resolve(res),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const runId = res.runId || res.data?.runId;
|
|
409
|
+
const status = res.status || res.data?.status || "running";
|
|
410
|
+
return {
|
|
411
|
+
runId,
|
|
412
|
+
status,
|
|
413
|
+
result: this.waitForRunCompletion(runId, options?.signal, options?.timeoutMs),
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 等待远端运行抵达终态:优先监听 SSE 事件流,失败后按指数退避轮询运行详情。
|
|
418
|
+
*
|
|
419
|
+
* @param runId 运行标识
|
|
420
|
+
* @param signal 外部取消信号
|
|
421
|
+
* @param timeoutMs 运行自身声明的超时(等待上限取该值与默认上限中的较大者)
|
|
422
|
+
*/
|
|
423
|
+
async waitForRunCompletion(runId, signal, timeoutMs) {
|
|
424
|
+
const startTime = Date.now();
|
|
425
|
+
const maxWaitMs = Math.max(this.baseTimeoutMs, timeoutMs ?? 0);
|
|
426
|
+
if (signal?.aborted) {
|
|
427
|
+
return {
|
|
428
|
+
ok: false,
|
|
429
|
+
runId,
|
|
430
|
+
error: {
|
|
431
|
+
code: ACTION_CANCELLED,
|
|
432
|
+
message: "Action execution was cancelled",
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const internalController = new AbortController();
|
|
437
|
+
let sseTimedOut = false;
|
|
438
|
+
let timer;
|
|
439
|
+
if (maxWaitMs > 0 && maxWaitMs !== Infinity) {
|
|
440
|
+
timer = setTimeout(() => {
|
|
441
|
+
sseTimedOut = true;
|
|
442
|
+
internalController.abort();
|
|
443
|
+
}, maxWaitMs);
|
|
444
|
+
}
|
|
445
|
+
const onAbort = () => {
|
|
446
|
+
internalController.abort();
|
|
447
|
+
};
|
|
448
|
+
if (signal) {
|
|
449
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
450
|
+
}
|
|
451
|
+
// 优先尝试监听 SSE 事件流终态事件
|
|
452
|
+
try {
|
|
453
|
+
for await (const evt of this.events(runId, { signal: internalController.signal })) {
|
|
454
|
+
if (evt.type === "finish") {
|
|
455
|
+
const res = evt.result || evt.data || evt;
|
|
456
|
+
if (typeof res?.ok === "boolean") {
|
|
457
|
+
return res;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
if (!signal?.aborted && !sseTimedOut) {
|
|
464
|
+
// SSE 通道异常视为不可用:记录后按指数退避进入轮询兜底
|
|
465
|
+
console.warn(`[ActionDock] SSE event stream unavailable for run '${runId}', falling back to polling: ${err?.message || String(err)}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
if (timer) {
|
|
470
|
+
clearTimeout(timer);
|
|
471
|
+
}
|
|
472
|
+
if (signal) {
|
|
473
|
+
signal.removeEventListener("abort", onAbort);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (signal?.aborted) {
|
|
477
|
+
return {
|
|
478
|
+
ok: false,
|
|
479
|
+
runId,
|
|
480
|
+
error: {
|
|
481
|
+
code: ACTION_CANCELLED,
|
|
482
|
+
message: "Action execution was cancelled",
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
const remainingWaitMs = Math.max(0, maxWaitMs - (Date.now() - startTime));
|
|
487
|
+
if (remainingWaitMs <= 0) {
|
|
488
|
+
const run = await this.getRun(runId);
|
|
489
|
+
if (run && isTerminalRunStatus(run.status)) {
|
|
490
|
+
return this.formatTerminalRunResult(run, runId);
|
|
491
|
+
}
|
|
492
|
+
const waitedMs = Date.now() - startTime;
|
|
493
|
+
return {
|
|
494
|
+
ok: false,
|
|
495
|
+
runId,
|
|
496
|
+
error: {
|
|
497
|
+
code: TIMEOUT,
|
|
498
|
+
message: `Timed out waiting for run '${runId}' completion after ${waitedMs}ms`,
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
// 剩余时间继续通过 pollRunCompletion 进行轮询兜底
|
|
503
|
+
return this.pollRunCompletion(runId, signal, remainingWaitMs, startTime, maxWaitMs);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* 指数退避轮询远端运行详情直至终态、取消或超时上限。
|
|
507
|
+
*/
|
|
508
|
+
async pollRunCompletion(runId, signal, timeoutMs, startTime = Date.now(), totalMaxWaitMs) {
|
|
509
|
+
const maxWaitMs = totalMaxWaitMs ?? Math.max(this.baseTimeoutMs, timeoutMs ?? 0);
|
|
510
|
+
const remainingWaitMs = Math.max(0, maxWaitMs - (Date.now() - startTime));
|
|
511
|
+
let delayMs = Math.min(150, Math.max(10, Math.floor((remainingWaitMs || maxWaitMs) / 4)));
|
|
512
|
+
const maxDelayMs = 2000;
|
|
513
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
514
|
+
if (signal?.aborted) {
|
|
515
|
+
return {
|
|
516
|
+
ok: false,
|
|
517
|
+
runId,
|
|
518
|
+
error: {
|
|
519
|
+
code: ACTION_CANCELLED,
|
|
520
|
+
message: "Action execution was cancelled",
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
const run = await this.getRun(runId);
|
|
525
|
+
if (run && isTerminalRunStatus(run.status)) {
|
|
526
|
+
return this.formatTerminalRunResult(run, runId);
|
|
527
|
+
}
|
|
528
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
529
|
+
delayMs = Math.min(delayMs * 2, maxDelayMs);
|
|
530
|
+
}
|
|
531
|
+
const waitedMs = Date.now() - startTime;
|
|
532
|
+
return {
|
|
533
|
+
ok: false,
|
|
534
|
+
runId,
|
|
535
|
+
error: {
|
|
536
|
+
code: TIMEOUT,
|
|
537
|
+
message: `Timed out waiting for run '${runId}' completion after ${waitedMs}ms`,
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
formatTerminalRunResult(run, runId) {
|
|
542
|
+
if (run.status === "success") {
|
|
543
|
+
return { ok: true, runId, data: run.output ?? null };
|
|
544
|
+
}
|
|
545
|
+
if (run.status === "interrupted") {
|
|
546
|
+
return {
|
|
547
|
+
ok: false,
|
|
548
|
+
runId,
|
|
549
|
+
error: run.error || {
|
|
550
|
+
code: "RUN_INTERRUPTED",
|
|
551
|
+
message: `Run '${runId}' was interrupted`,
|
|
552
|
+
},
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
ok: false,
|
|
557
|
+
runId,
|
|
558
|
+
error: run.error || {
|
|
559
|
+
code: EXECUTION_FAILED,
|
|
560
|
+
message: `Run finished with status ${run.status}`,
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
async listRuns(options) {
|
|
565
|
+
try {
|
|
566
|
+
const res = await fetchRemoteRuns(this.serverUrl, this.token, {
|
|
567
|
+
packageId: options?.packageId,
|
|
568
|
+
actionId: options?.actionId,
|
|
569
|
+
status: options?.status,
|
|
570
|
+
intent: options?.intent,
|
|
571
|
+
limit: options?.limit,
|
|
572
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
573
|
+
});
|
|
574
|
+
return res.items || [];
|
|
575
|
+
}
|
|
576
|
+
catch (err) {
|
|
577
|
+
wrapRemoteError(err);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async clearRuns(options) {
|
|
581
|
+
try {
|
|
582
|
+
const res = await clearRemoteRuns(this.serverUrl, this.token, {
|
|
583
|
+
...options,
|
|
584
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
585
|
+
});
|
|
586
|
+
return res.clearedCount ?? 0;
|
|
587
|
+
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
wrapRemoteError(err);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async getRun(runId) {
|
|
593
|
+
try {
|
|
594
|
+
return await fetchRemoteRun(this.serverUrl, runId, this.token, {
|
|
595
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
const msg = String(err?.message || "");
|
|
600
|
+
if (msg.includes("404") || msg.includes("not found") || msg.includes("RUN_NOT_FOUND")) {
|
|
601
|
+
return undefined;
|
|
602
|
+
}
|
|
603
|
+
throw err;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async cancelRun(runId, reason) {
|
|
607
|
+
try {
|
|
608
|
+
const res = await cancelRemoteRun(this.serverUrl, runId, this.token, reason, {
|
|
609
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
610
|
+
});
|
|
611
|
+
return { outcome: "requested", runId: res.runId };
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
const msg = String(err?.message || "");
|
|
615
|
+
const code = String(err?.code || "");
|
|
616
|
+
if (code === "RUN_ALREADY_FINISHED" ||
|
|
617
|
+
msg.includes("already finished") ||
|
|
618
|
+
msg.includes("RUN_ALREADY_FINISHED")) {
|
|
619
|
+
let status = err?.errorData?.status || err?.details?.status;
|
|
620
|
+
if (!status) {
|
|
621
|
+
try {
|
|
622
|
+
const run = await this.getRun(runId);
|
|
623
|
+
if (run?.status) {
|
|
624
|
+
status = run.status;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch { }
|
|
628
|
+
}
|
|
629
|
+
return { outcome: "already_terminal", runId, status: status || "failed" };
|
|
630
|
+
}
|
|
631
|
+
if (code === "RUN_NOT_FOUND" ||
|
|
632
|
+
msg.includes("not found") ||
|
|
633
|
+
msg.includes("RUN_NOT_FOUND") ||
|
|
634
|
+
msg.includes("404")) {
|
|
635
|
+
return { outcome: "not_found", runId };
|
|
636
|
+
}
|
|
637
|
+
throw err;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
async *events(runId, options) {
|
|
641
|
+
yield* streamRemoteEvents(this.serverUrl, runId, this.token, {
|
|
642
|
+
...options,
|
|
643
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
async getConfig(packageId, key) {
|
|
647
|
+
try {
|
|
648
|
+
const list = await this.listConfig(packageId);
|
|
649
|
+
const found = list.find((c) => c.key === key);
|
|
650
|
+
if (found) {
|
|
651
|
+
return found;
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
key,
|
|
655
|
+
configured: false,
|
|
656
|
+
secret: false,
|
|
657
|
+
source: "default",
|
|
658
|
+
value: undefined,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
catch (err) {
|
|
662
|
+
wrapRemoteError(err);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async setConfig(packageId, key, value) {
|
|
666
|
+
try {
|
|
667
|
+
await setRemoteConfig(this.serverUrl, key, value, this.token, packageId || undefined, {
|
|
668
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
catch (err) {
|
|
672
|
+
wrapRemoteError(err);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
async deleteConfig(packageId, key) {
|
|
676
|
+
try {
|
|
677
|
+
const res = await deleteRemoteConfig(this.serverUrl, key, this.token, packageId || undefined, {
|
|
678
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
679
|
+
});
|
|
680
|
+
return Boolean(res?.deleted ?? true);
|
|
681
|
+
}
|
|
682
|
+
catch (err) {
|
|
683
|
+
wrapRemoteError(err);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
async listConfig(packageId) {
|
|
687
|
+
try {
|
|
688
|
+
const res = await fetchRemoteConfig(this.serverUrl, this.token, packageId || undefined, {
|
|
689
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
690
|
+
});
|
|
691
|
+
if (Array.isArray(res))
|
|
692
|
+
return res;
|
|
693
|
+
if (Array.isArray(res?.items))
|
|
694
|
+
return res.items;
|
|
695
|
+
if (Array.isArray(res?.config))
|
|
696
|
+
return res.config;
|
|
697
|
+
const values = res?.values || {};
|
|
698
|
+
const declared = res?.declared || {};
|
|
699
|
+
const defaultSource = packageId === "global" ? "global" : "package";
|
|
700
|
+
return Object.entries(values).map(([k, v]) => ({
|
|
701
|
+
key: k,
|
|
702
|
+
configured: v !== undefined,
|
|
703
|
+
secret: Boolean(declared[k]?.secret),
|
|
704
|
+
source: defaultSource,
|
|
705
|
+
value: v,
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
catch (err) {
|
|
709
|
+
wrapRemoteError(err);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
async getState(packageId, actionId, key, options) {
|
|
713
|
+
try {
|
|
714
|
+
const res = await getRemoteStateKey(this.serverUrl, key, this.token, {
|
|
715
|
+
package: packageId || undefined,
|
|
716
|
+
action: actionId || undefined,
|
|
717
|
+
namespace: options?.namespace,
|
|
718
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
719
|
+
});
|
|
720
|
+
if (res === undefined)
|
|
721
|
+
return undefined;
|
|
722
|
+
if (options?.detail) {
|
|
723
|
+
return res;
|
|
724
|
+
}
|
|
725
|
+
return (res?.value !== undefined ? res.value : res);
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
if (isRemoteStateKeyNotFound(err)) {
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
wrapRemoteError(err);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async setState(packageId, actionId, key, value, options) {
|
|
735
|
+
try {
|
|
736
|
+
await setRemoteStateKey(this.serverUrl, key, value, this.token, {
|
|
737
|
+
package: packageId || undefined,
|
|
738
|
+
action: actionId || undefined,
|
|
739
|
+
namespace: options?.namespace,
|
|
740
|
+
ttl: options?.ttl,
|
|
741
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
catch (err) {
|
|
745
|
+
wrapRemoteError(err);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async deleteState(packageId, actionId, key, options) {
|
|
749
|
+
try {
|
|
750
|
+
const res = await deleteRemoteStateKey(this.serverUrl, key, this.token, {
|
|
751
|
+
package: packageId || undefined,
|
|
752
|
+
action: actionId || undefined,
|
|
753
|
+
namespace: options?.namespace,
|
|
754
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
755
|
+
});
|
|
756
|
+
return Boolean(res?.deleted ?? true);
|
|
757
|
+
}
|
|
758
|
+
catch (err) {
|
|
759
|
+
if (isRemoteStateKeyNotFound(err)) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
wrapRemoteError(err);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async listStateKeys(packageId, actionId, options) {
|
|
766
|
+
try {
|
|
767
|
+
const res = await fetchRemoteStateList(this.serverUrl, this.token, {
|
|
768
|
+
package: packageId || undefined,
|
|
769
|
+
action: actionId || undefined,
|
|
770
|
+
namespace: options?.namespace,
|
|
771
|
+
prefix: options?.prefix,
|
|
772
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
773
|
+
});
|
|
774
|
+
return res.keys || [];
|
|
775
|
+
}
|
|
776
|
+
catch (err) {
|
|
777
|
+
wrapRemoteError(err);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async clearState(packageId, actionId, options) {
|
|
781
|
+
try {
|
|
782
|
+
const res = await clearRemoteState(this.serverUrl, this.token, {
|
|
783
|
+
package: packageId || undefined,
|
|
784
|
+
action: actionId || undefined,
|
|
785
|
+
namespace: options?.namespace,
|
|
786
|
+
prefix: options?.prefix,
|
|
787
|
+
all: options?.all,
|
|
788
|
+
allowInsecureHttp: this.allowInsecureHttp,
|
|
789
|
+
});
|
|
790
|
+
return res.clearedCount ?? 0;
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
wrapRemoteError(err);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
async listStateEntries(packageId, options) {
|
|
797
|
+
throw new TargetError(TARGET_CAPABILITY_UNAVAILABLE, "TARGET_CAPABILITY_UNAVAILABLE: listStateEntries is not supported on remote target");
|
|
798
|
+
}
|
|
799
|
+
async close(_options) {
|
|
800
|
+
// 远程 Target 无本地资源需要释放
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* 判定远端状态键访问异常是否为键不存在。
|
|
805
|
+
*
|
|
806
|
+
* 优先读取传输层透传的结构化错误码(fetchRemoteJson 会将响应体 error.code
|
|
807
|
+
* 附加到抛出异常的 code 字段),仅当旧版服务器未透传 code 时回退到
|
|
808
|
+
* HTTP 状态与消息文本兼容嗅探。
|
|
809
|
+
*/
|
|
810
|
+
function isRemoteStateKeyNotFound(err) {
|
|
811
|
+
if (err?.code === STATE_KEY_NOT_FOUND) {
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
const msg = String(err?.message || "");
|
|
815
|
+
return err?.status === 404 || msg.includes("404") || msg.includes("not found");
|
|
816
|
+
}
|
|
817
|
+
function wrapRemoteError(err) {
|
|
818
|
+
const msg = String(err?.message || "");
|
|
819
|
+
const code = err?.code || "";
|
|
820
|
+
if (code === "CAPABILITY_UNAVAILABLE" ||
|
|
821
|
+
code === "TARGET_CAPABILITY_UNAVAILABLE" ||
|
|
822
|
+
msg.includes("CAPABILITY_UNAVAILABLE") ||
|
|
823
|
+
msg.includes("TARGET_CAPABILITY_UNAVAILABLE") ||
|
|
824
|
+
msg.includes("Management APIs are not enabled")) {
|
|
825
|
+
throw new TargetError(TARGET_CAPABILITY_UNAVAILABLE, `TARGET_CAPABILITY_UNAVAILABLE: Management APIs are not enabled on remote target`, { originalMessage: msg });
|
|
826
|
+
}
|
|
827
|
+
if (code === "PROTOCOL_UNSUPPORTED" ||
|
|
828
|
+
code === "TARGET_PROTOCOL_UNSUPPORTED" ||
|
|
829
|
+
msg.includes("PROTOCOL_UNSUPPORTED") ||
|
|
830
|
+
msg.includes("TARGET_PROTOCOL_UNSUPPORTED")) {
|
|
831
|
+
throw new TargetError(TARGET_PROTOCOL_UNSUPPORTED, `TARGET_PROTOCOL_UNSUPPORTED: ${msg}`, { originalMessage: msg });
|
|
832
|
+
}
|
|
833
|
+
if (code === "TARGET_RESULT_UNKNOWN" ||
|
|
834
|
+
code === "RESULT_UNKNOWN" ||
|
|
835
|
+
msg.includes("TARGET_RESULT_UNKNOWN")) {
|
|
836
|
+
throw new TargetError(TARGET_RESULT_UNKNOWN, `TARGET_RESULT_UNKNOWN: ${msg}`, { originalMessage: msg });
|
|
837
|
+
}
|
|
838
|
+
throw err;
|
|
839
|
+
}
|