@actiondock/core 2.5.1 → 2.6.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/dist/app/app.d.ts +14 -1
- package/dist/app/app.js +88 -222
- package/dist/app/static-index.d.ts +39 -0
- package/dist/app/static-index.js +164 -0
- package/dist/app/types.d.ts +5 -0
- package/dist/catalog/action-index.js +6 -4
- package/dist/catalog/package-catalog.js +2 -1
- package/dist/doctor/checks/index.d.ts +5 -0
- package/dist/doctor/checks/index.js +5 -0
- package/dist/doctor/checks/project-locate.d.ts +6 -0
- package/dist/doctor/checks/project-locate.js +36 -0
- package/dist/doctor/checks/project.d.ts +6 -0
- package/dist/doctor/checks/project.js +383 -0
- package/dist/doctor/checks/registry.d.ts +13 -0
- package/dist/doctor/checks/registry.js +207 -0
- package/dist/doctor/checks/runtime.d.ts +13 -0
- package/dist/doctor/checks/runtime.js +90 -0
- package/dist/doctor/checks/storage.d.ts +5 -0
- package/dist/doctor/checks/storage.js +34 -0
- package/dist/doctor/context.d.ts +35 -0
- package/dist/doctor/context.js +11 -0
- package/dist/doctor/doctor.d.ts +5 -0
- package/dist/doctor/doctor.js +33 -602
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/execution/service.js +14 -9
- package/dist/host/host.d.ts +33 -0
- package/dist/host/host.js +222 -362
- package/dist/host/routing.d.ts +92 -0
- package/dist/host/routing.js +238 -0
- package/dist/host/types.d.ts +8 -0
- package/dist/ipc/host.js +38 -0
- package/dist/platform/types.d.ts +7 -0
- package/dist/process/control-arbiter.d.ts +107 -0
- package/dist/process/control-arbiter.js +251 -0
- package/dist/process/diagnostics-sink.d.ts +26 -0
- package/dist/process/diagnostics-sink.js +35 -0
- package/dist/process/index.d.ts +7 -0
- package/dist/process/index.js +7 -0
- package/dist/process/input-dispatcher.d.ts +88 -0
- package/dist/process/input-dispatcher.js +240 -0
- package/dist/process/managed-record.d.ts +78 -0
- package/dist/process/managed-record.js +1 -0
- package/dist/process/process-manager.d.ts +15 -56
- package/dist/process/process-manager.js +119 -707
- package/dist/process/reservation-table.d.ts +39 -0
- package/dist/process/reservation-table.js +80 -0
- package/dist/process/run-executor.d.ts +16 -0
- package/dist/process/run-executor.js +155 -0
- package/dist/process/terminal-output-cache.d.ts +66 -0
- package/dist/process/terminal-output-cache.js +132 -0
- package/dist/profile/client-actions.d.ts +56 -0
- package/dist/profile/client-actions.js +215 -0
- package/dist/profile/client-config.d.ts +20 -0
- package/dist/profile/client-config.js +51 -0
- package/dist/profile/client-health.d.ts +14 -0
- package/dist/profile/client-health.js +68 -0
- package/dist/profile/client-playbooks.d.ts +19 -0
- package/dist/profile/client-playbooks.js +36 -0
- package/dist/profile/client-query.d.ts +30 -0
- package/dist/profile/client-query.js +70 -0
- package/dist/profile/client-runs.d.ts +42 -0
- package/dist/profile/client-runs.js +58 -0
- package/dist/profile/client-state.d.ts +56 -0
- package/dist/profile/client-state.js +83 -0
- package/dist/profile/client-transport.d.ts +61 -0
- package/dist/profile/client-transport.js +155 -0
- package/dist/profile/client.d.ts +23 -142
- package/dist/profile/client.js +28 -588
- package/dist/profile/manager.js +1 -1
- package/dist/project/closure.d.ts +4 -4
- package/dist/project/closure.js +11 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +0 -25
- package/dist/project/loader.js +1 -154
- package/dist/project/transactions.d.ts +12 -14
- package/dist/project/transactions.js +60 -836
- package/dist/registry/registry.js +6 -4
- package/dist/registry/resolve.js +1 -1
- package/dist/runtime/action-registry.d.ts +45 -0
- package/dist/runtime/action-registry.js +108 -0
- package/dist/runtime/clock.d.ts +5 -17
- package/dist/runtime/clock.js +5 -15
- package/dist/runtime/context.d.ts +3 -0
- package/dist/runtime/context.js +17 -56
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/package-runner-factory.d.ts +82 -0
- package/dist/runtime/package-runner-factory.js +186 -0
- package/dist/runtime/run-persistence.d.ts +85 -0
- package/dist/runtime/run-persistence.js +111 -0
- package/dist/runtime/runner.d.ts +23 -33
- package/dist/runtime/runner.js +92 -286
- package/dist/runtime/standalone.d.ts +11 -1
- package/dist/runtime/standalone.js +137 -25
- package/dist/server/dispatcher.d.ts +8 -3
- package/dist/server/dispatcher.js +16 -40
- package/dist/server/routes/runs.js +6 -4
- package/dist/server/security.d.ts +1 -7
- package/dist/server/security.js +2 -13
- package/dist/storage/clock.d.ts +25 -0
- package/dist/storage/clock.js +23 -0
- package/dist/storage/data-dir-lock.d.ts +3 -36
- package/dist/storage/data-dir-lock.js +48 -881
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.js +8 -2
- package/dist/storage/lazy.js +1 -0
- package/dist/storage/lock-core.d.ts +162 -0
- package/dist/storage/lock-core.js +885 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +81 -89
- package/dist/storage/types.d.ts +27 -5
- package/dist/storage/types.js +21 -0
- package/dist/storage/utils.d.ts +22 -0
- package/dist/storage/utils.js +34 -0
- package/dist/target/local.d.ts +1 -0
- package/dist/target/local.js +23 -17
- package/dist/target/remote-errors.d.ts +21 -0
- package/dist/target/remote-errors.js +51 -0
- package/dist/target/remote-polling.d.ts +35 -0
- package/dist/target/remote-polling.js +104 -0
- package/dist/target/remote.d.ts +4 -0
- package/dist/target/remote.js +108 -301
- package/dist/target/sse-parser.d.ts +35 -0
- package/dist/target/sse-parser.js +163 -0
- package/dist/target/target.js +2 -0
- package/dist/target/types.d.ts +6 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/net.d.ts +10 -0
- package/dist/utils/net.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { ACTION_CANCELLED, ACTION_TIMEOUT, NETWORK_ERROR } from "../errors.js";
|
|
3
|
+
import { getInsecureDispatcher } from "../server/dispatcher.js";
|
|
4
|
+
import { normalizeServerUrl } from "./manager.js";
|
|
5
|
+
import { assertSecureTransport, buildHeaders, fetchWithProtocolFallback, } from "./client-transport.js";
|
|
6
|
+
import { buildQueryString, fetchRemoteJson } from "./client-query.js";
|
|
7
|
+
/**
|
|
8
|
+
* 执行远端 Action:支持幂等标识、异步触发、外部取消信号与本地超时守卫组合。
|
|
9
|
+
*/
|
|
10
|
+
export async function executeRemoteAction(serverUrl, actionId, input = {}, configOverridesOrOptions, tokenArg) {
|
|
11
|
+
// Parse options / backwards compatibility
|
|
12
|
+
let configOverrides;
|
|
13
|
+
let token = tokenArg;
|
|
14
|
+
let timeoutMs;
|
|
15
|
+
let signal;
|
|
16
|
+
let isAsync = false;
|
|
17
|
+
let requestId;
|
|
18
|
+
let allowInsecureHttp;
|
|
19
|
+
let insecure;
|
|
20
|
+
let dispatcher;
|
|
21
|
+
if (configOverridesOrOptions && typeof configOverridesOrOptions === "object") {
|
|
22
|
+
if ("token" in configOverridesOrOptions ||
|
|
23
|
+
"timeoutMs" in configOverridesOrOptions ||
|
|
24
|
+
"signal" in configOverridesOrOptions ||
|
|
25
|
+
"async" in configOverridesOrOptions ||
|
|
26
|
+
"requestId" in configOverridesOrOptions ||
|
|
27
|
+
"configOverrides" in configOverridesOrOptions ||
|
|
28
|
+
"allowInsecureHttp" in configOverridesOrOptions ||
|
|
29
|
+
"insecure" in configOverridesOrOptions ||
|
|
30
|
+
"dispatcher" in configOverridesOrOptions) {
|
|
31
|
+
const opts = configOverridesOrOptions;
|
|
32
|
+
configOverrides = opts.configOverrides;
|
|
33
|
+
token = opts.token ?? tokenArg;
|
|
34
|
+
timeoutMs = opts.timeoutMs;
|
|
35
|
+
signal = opts.signal;
|
|
36
|
+
isAsync = Boolean(opts.async);
|
|
37
|
+
requestId = opts.requestId;
|
|
38
|
+
allowInsecureHttp = opts.allowInsecureHttp;
|
|
39
|
+
insecure = opts.insecure;
|
|
40
|
+
dispatcher = opts.dispatcher;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
configOverrides = configOverridesOrOptions;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
assertSecureTransport(serverUrl, token, { allowInsecureHttp, insecure });
|
|
47
|
+
const executionPayload = {};
|
|
48
|
+
if (isAsync) {
|
|
49
|
+
executionPayload.mode = "async";
|
|
50
|
+
}
|
|
51
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
52
|
+
executionPayload.timeoutMs = timeoutMs;
|
|
53
|
+
}
|
|
54
|
+
if (requestId) {
|
|
55
|
+
executionPayload.requestId = requestId;
|
|
56
|
+
}
|
|
57
|
+
// 组合外部取消信号与本地超时守卫:服务端僵死时仍能在 timeoutMs 内本地中断,避免永久挂起
|
|
58
|
+
let timeoutTimer;
|
|
59
|
+
let localTimedOut = false;
|
|
60
|
+
try {
|
|
61
|
+
const headers = {
|
|
62
|
+
...buildHeaders(token),
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
};
|
|
65
|
+
if (requestId) {
|
|
66
|
+
headers["Idempotency-Key"] = requestId;
|
|
67
|
+
headers["x-request-id"] = requestId;
|
|
68
|
+
}
|
|
69
|
+
const reqBody = JSON.stringify({
|
|
70
|
+
input,
|
|
71
|
+
config: configOverrides,
|
|
72
|
+
execution: Object.keys(executionPayload).length > 0 ? executionPayload : undefined,
|
|
73
|
+
});
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const onExternalAbort = () => controller.abort(signal?.reason);
|
|
76
|
+
if (signal) {
|
|
77
|
+
if (signal.aborted) {
|
|
78
|
+
controller.abort(signal.reason);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) {
|
|
85
|
+
timeoutTimer = setTimeout(() => {
|
|
86
|
+
localTimedOut = true;
|
|
87
|
+
controller.abort();
|
|
88
|
+
}, timeoutMs);
|
|
89
|
+
}
|
|
90
|
+
const fetchInit = {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers,
|
|
93
|
+
body: reqBody,
|
|
94
|
+
signal: controller.signal,
|
|
95
|
+
};
|
|
96
|
+
if (dispatcher) {
|
|
97
|
+
fetchInit.dispatcher = dispatcher;
|
|
98
|
+
}
|
|
99
|
+
else if (insecure) {
|
|
100
|
+
fetchInit.dispatcher = getInsecureDispatcher();
|
|
101
|
+
}
|
|
102
|
+
let res;
|
|
103
|
+
try {
|
|
104
|
+
res = await fetchWithProtocolFallback(normalizeServerUrl(serverUrl), `/api/v2/actions/${encodeURIComponent(actionId)}/run`, fetchInit);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (timeoutTimer !== undefined) {
|
|
108
|
+
clearTimeout(timeoutTimer);
|
|
109
|
+
}
|
|
110
|
+
if (signal) {
|
|
111
|
+
signal.removeEventListener("abort", onExternalAbort);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const data = (await res.json().catch(() => null));
|
|
115
|
+
if (data && typeof data === "object" && typeof data.ok === "boolean") {
|
|
116
|
+
return data;
|
|
117
|
+
}
|
|
118
|
+
if (!res.ok) {
|
|
119
|
+
// 错误信封严禁伪造 runId:与任何真实运行无关的标识会让调用方查询永远 not_found
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
runId: "",
|
|
123
|
+
error: {
|
|
124
|
+
code: res.status === 401 ? "UNAUTHORIZED" : "REMOTE_EXECUTION_FAILED",
|
|
125
|
+
message: `Remote server HTTP ${res.status}: ${res.statusText}`,
|
|
126
|
+
details: data,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
runId: randomUUID(),
|
|
133
|
+
data,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
// 本地超时守卫触发时归类为超时;外部信号中止时归类为取消
|
|
138
|
+
if (localTimedOut) {
|
|
139
|
+
return {
|
|
140
|
+
ok: false,
|
|
141
|
+
runId: "",
|
|
142
|
+
error: {
|
|
143
|
+
code: ACTION_TIMEOUT,
|
|
144
|
+
message: `Remote execution exceeded local timeout of ${timeoutMs}ms`,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (err.name === "AbortError" || signal?.aborted) {
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
runId: "",
|
|
152
|
+
error: {
|
|
153
|
+
code: ACTION_CANCELLED,
|
|
154
|
+
message: "Action execution was cancelled",
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
runId: "",
|
|
161
|
+
error: {
|
|
162
|
+
code: NETWORK_ERROR,
|
|
163
|
+
message: `Failed to connect to remote ActionDock server at ${serverUrl}: ${err.message}`,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 拉取远端 Action 列表(可按意图过滤)。
|
|
170
|
+
*/
|
|
171
|
+
export async function fetchRemoteActions(serverUrl, token, intent, options) {
|
|
172
|
+
return fetchRemoteJson(serverUrl, `/api/v2/actions${buildQueryString({ intent })}`, token, {
|
|
173
|
+
errorPrefix: "Failed to fetch remote actions",
|
|
174
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
175
|
+
insecure: options?.insecure,
|
|
176
|
+
dispatcher: options?.dispatcher,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 拉取单个远端 Action 的详细契约描述。
|
|
181
|
+
*/
|
|
182
|
+
export async function fetchRemoteActionShow(serverUrl, actionId, token, options) {
|
|
183
|
+
return fetchRemoteJson(serverUrl, `/api/v2/actions/${encodeURIComponent(actionId)}`, token, {
|
|
184
|
+
errorPrefix: `Failed to fetch remote action '${actionId}'`,
|
|
185
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
186
|
+
insecure: options?.insecure,
|
|
187
|
+
dispatcher: options?.dispatcher,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 拉取远端服务信息总览(可按意图、包名过滤并附目录树)。
|
|
192
|
+
*/
|
|
193
|
+
export async function fetchRemoteInfo(serverUrl, token, options) {
|
|
194
|
+
return fetchRemoteJson(serverUrl, `/api/v2/info${buildQueryString({
|
|
195
|
+
intent: options?.intent,
|
|
196
|
+
package: options?.package,
|
|
197
|
+
tree: options?.tree ? "true" : undefined,
|
|
198
|
+
})}`, token, {
|
|
199
|
+
errorPrefix: "Failed to fetch remote info",
|
|
200
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
201
|
+
insecure: options?.insecure,
|
|
202
|
+
dispatcher: options?.dispatcher,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 拉取远端环境体检报告(可定向指定包)。
|
|
207
|
+
*/
|
|
208
|
+
export async function fetchRemoteDoctor(serverUrl, token, targetPackage, options) {
|
|
209
|
+
return fetchRemoteJson(serverUrl, `/api/v2/doctor${buildQueryString({ package: targetPackage })}`, token, {
|
|
210
|
+
errorPrefix: "Failed to fetch remote doctor report",
|
|
211
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
212
|
+
insecure: options?.insecure,
|
|
213
|
+
dispatcher: options?.dispatcher,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RemoteClientRequestOptions } from "./client-transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端配置域端点。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 拉取远端配置视图(可定向指定包)。
|
|
7
|
+
*/
|
|
8
|
+
export declare function fetchRemoteConfig(serverUrl: string, token?: string, packageId?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
9
|
+
/**
|
|
10
|
+
* 写入单个远端配置键值。
|
|
11
|
+
*/
|
|
12
|
+
export declare function setRemoteConfig(serverUrl: string, key: string, value: unknown, token?: string, packageId?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
13
|
+
/**
|
|
14
|
+
* 删除单个远端配置键。
|
|
15
|
+
*/
|
|
16
|
+
export declare function deleteRemoteConfig(serverUrl: string, key: string, token?: string, packageId?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
17
|
+
/**
|
|
18
|
+
* 拉取远端配置环境变量检查结果(可定向指定包)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function fetchRemoteConfigEnv(serverUrl: string, token?: string, packageId?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { buildQueryString, fetchRemoteJson } from "./client-query.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端配置域端点。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 拉取远端配置视图(可定向指定包)。
|
|
7
|
+
*/
|
|
8
|
+
export async function fetchRemoteConfig(serverUrl, token, packageId, options) {
|
|
9
|
+
return fetchRemoteJson(serverUrl, `/api/v2/config${buildQueryString({ package: packageId })}`, token, {
|
|
10
|
+
errorPrefix: "Failed to fetch remote config",
|
|
11
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
12
|
+
insecure: options?.insecure,
|
|
13
|
+
dispatcher: options?.dispatcher,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 写入单个远端配置键值。
|
|
18
|
+
*/
|
|
19
|
+
export async function setRemoteConfig(serverUrl, key, value, token, packageId, options) {
|
|
20
|
+
return fetchRemoteJson(serverUrl, "/api/v2/config", token, {
|
|
21
|
+
method: "PUT",
|
|
22
|
+
body: { key, value, package: packageId },
|
|
23
|
+
errorPrefix: `Failed to set remote config '${key}'`,
|
|
24
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
25
|
+
insecure: options?.insecure,
|
|
26
|
+
dispatcher: options?.dispatcher,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 删除单个远端配置键。
|
|
31
|
+
*/
|
|
32
|
+
export async function deleteRemoteConfig(serverUrl, key, token, packageId, options) {
|
|
33
|
+
return fetchRemoteJson(serverUrl, `/api/v2/config/${encodeURIComponent(key)}${buildQueryString({ package: packageId })}`, token, {
|
|
34
|
+
method: "DELETE",
|
|
35
|
+
errorPrefix: `Failed to delete remote config '${key}'`,
|
|
36
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
37
|
+
insecure: options?.insecure,
|
|
38
|
+
dispatcher: options?.dispatcher,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 拉取远端配置环境变量检查结果(可定向指定包)。
|
|
43
|
+
*/
|
|
44
|
+
export async function fetchRemoteConfigEnv(serverUrl, token, packageId, options) {
|
|
45
|
+
return fetchRemoteJson(serverUrl, `/api/v2/config/env${buildQueryString({ package: packageId })}`, token, {
|
|
46
|
+
errorPrefix: "Failed to fetch remote config env checks",
|
|
47
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
48
|
+
insecure: options?.insecure,
|
|
49
|
+
dispatcher: options?.dispatcher,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RemoteHealthResult } from "./types.js";
|
|
2
|
+
import { type RemoteClientRequestOptions } from "./client-transport.js";
|
|
3
|
+
/**
|
|
4
|
+
* 远端健康检查域端点。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 探测指定远端 ActionDock 服务的健康状态与网络延迟。
|
|
8
|
+
*
|
|
9
|
+
* @param serverUrl 目标服务端地址
|
|
10
|
+
* @param token 鉴权 Token(可选)
|
|
11
|
+
* @param timeoutMs 探测超时时间(默认 5000ms)
|
|
12
|
+
* @param options 传输与安全控制选项
|
|
13
|
+
*/
|
|
14
|
+
export declare function checkRemoteHealth(serverUrl: string, token?: string, timeoutMs?: number, options?: RemoteClientRequestOptions): Promise<RemoteHealthResult>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { normalizeServerUrl } from "./manager.js";
|
|
2
|
+
import { assertSecureTransport, buildHeaders, fetchWithProtocolFallback, } from "./client-transport.js";
|
|
3
|
+
import { getInsecureDispatcher } from "../server/dispatcher.js";
|
|
4
|
+
/**
|
|
5
|
+
* 远端健康检查域端点。
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 探测指定远端 ActionDock 服务的健康状态与网络延迟。
|
|
9
|
+
*
|
|
10
|
+
* @param serverUrl 目标服务端地址
|
|
11
|
+
* @param token 鉴权 Token(可选)
|
|
12
|
+
* @param timeoutMs 探测超时时间(默认 5000ms)
|
|
13
|
+
* @param options 传输与安全控制选项
|
|
14
|
+
*/
|
|
15
|
+
export async function checkRemoteHealth(serverUrl, token, timeoutMs = 5000, options) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
let timer;
|
|
18
|
+
try {
|
|
19
|
+
assertSecureTransport(serverUrl, token, {
|
|
20
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
21
|
+
insecure: options?.insecure,
|
|
22
|
+
});
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
25
|
+
const fetchInit = {
|
|
26
|
+
method: "GET",
|
|
27
|
+
headers: buildHeaders(token),
|
|
28
|
+
signal: controller.signal,
|
|
29
|
+
};
|
|
30
|
+
if (options?.dispatcher) {
|
|
31
|
+
fetchInit.dispatcher = options.dispatcher;
|
|
32
|
+
}
|
|
33
|
+
else if (options?.insecure) {
|
|
34
|
+
fetchInit.dispatcher = getInsecureDispatcher();
|
|
35
|
+
}
|
|
36
|
+
const res = await fetchWithProtocolFallback(normalizeServerUrl(serverUrl), "/api/v2/health", fetchInit);
|
|
37
|
+
const latencyMs = Date.now() - startTime;
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
const text = await res.text().catch(() => "");
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
latencyMs,
|
|
43
|
+
error: `Server responded with status ${res.status}: ${text || res.statusText}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const data = (await res.json().catch(() => ({})));
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
status: data.status || "healthy",
|
|
50
|
+
version: data.version,
|
|
51
|
+
uptime: data.uptime,
|
|
52
|
+
latencyMs,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const latencyMs = Date.now() - startTime;
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
latencyMs,
|
|
60
|
+
error: err.name === "AbortError" ? "Connection timed out" : err.message,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
if (timer !== undefined) {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RemoteClientRequestOptions } from "./client-transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端 Playbook 规程域端点。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 拉取远端 Playbook 列表(可按意图与包名过滤)。
|
|
7
|
+
*/
|
|
8
|
+
export declare function fetchRemotePlaybooks(serverUrl: string, token?: string, options?: {
|
|
9
|
+
intent?: string;
|
|
10
|
+
package?: string;
|
|
11
|
+
} & RemoteClientRequestOptions): Promise<Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
description: string;
|
|
14
|
+
actions: string[];
|
|
15
|
+
packageId: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
}>>;
|
|
18
|
+
export declare function fetchRemotePlaybookShow(playbookId: string, token?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
19
|
+
export declare function fetchRemotePlaybookShow(serverUrl: string, playbookId: string, token?: string, options?: RemoteClientRequestOptions): Promise<any>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { buildQueryString, fetchRemoteJson } from "./client-query.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端 Playbook 规程域端点。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 拉取远端 Playbook 列表(可按意图与包名过滤)。
|
|
7
|
+
*/
|
|
8
|
+
export async function fetchRemotePlaybooks(serverUrl, token, options) {
|
|
9
|
+
return fetchRemoteJson(serverUrl, `/api/v2/playbooks${buildQueryString({
|
|
10
|
+
intent: options?.intent,
|
|
11
|
+
package: options?.package,
|
|
12
|
+
})}`, token, {
|
|
13
|
+
errorPrefix: "Failed to fetch remote playbooks",
|
|
14
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
15
|
+
insecure: options?.insecure,
|
|
16
|
+
dispatcher: options?.dispatcher,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export async function fetchRemotePlaybookShow(serverUrlOrPlaybookId, playbookIdOrToken, tokenOrOptions, optionsArg) {
|
|
20
|
+
let serverUrl = serverUrlOrPlaybookId;
|
|
21
|
+
let playbookId = playbookIdOrToken || "";
|
|
22
|
+
let token;
|
|
23
|
+
let options = optionsArg;
|
|
24
|
+
if (typeof tokenOrOptions === "object") {
|
|
25
|
+
options = tokenOrOptions;
|
|
26
|
+
}
|
|
27
|
+
else if (typeof tokenOrOptions === "string") {
|
|
28
|
+
token = tokenOrOptions;
|
|
29
|
+
}
|
|
30
|
+
return fetchRemoteJson(serverUrl, `/api/v2/playbooks/${encodeURIComponent(playbookId)}`, token, {
|
|
31
|
+
errorPrefix: `Failed to fetch remote playbook '${playbookId}'`,
|
|
32
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
33
|
+
insecure: options?.insecure,
|
|
34
|
+
dispatcher: options?.dispatcher,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type RemoteClientRequestOptions } from "./client-transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端端点函数共享的请求与查询构造层。
|
|
4
|
+
*
|
|
5
|
+
* 职责单一聚焦:JSON 响应解析与错误信封透传、查询参数序列化,
|
|
6
|
+
* 供各按域端点模块复用,杜绝端点函数内手工拼 query 样板。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 执行远端请求并将响应解析为 JSON,统一处理错误信封透传。
|
|
10
|
+
* 非 2xx 响应或显式 ok=false 信封抛出携带 code/status/details 的 Error。
|
|
11
|
+
*/
|
|
12
|
+
export declare function fetchRemoteJson<T = any>(serverUrl: string, path: string, token?: string, options?: {
|
|
13
|
+
method?: string;
|
|
14
|
+
body?: unknown;
|
|
15
|
+
errorPrefix?: string;
|
|
16
|
+
} & RemoteClientRequestOptions): Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* 由可选参数字典构造查询串(无参数时返回空串)。
|
|
19
|
+
*
|
|
20
|
+
* 序列化规则与既有端点行为严格一致:
|
|
21
|
+
* - undefined 与空串一律跳过(对应原有 if (options?.xxx) 真值守卫);
|
|
22
|
+
* - 需保留「存在但为空」语义的参数(如 namespace,服务端区分空串与未传递)
|
|
23
|
+
* 由调用方显式处理,不经由此助手序列化空串。
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildQueryString(params: Record<string, string | number | boolean | undefined>): string;
|
|
26
|
+
/**
|
|
27
|
+
* 构造保留「存在但为空」语义的查询串:仅跳过 undefined,空串照常序列化。
|
|
28
|
+
* 适用 namespace 类参数:服务端以参数是否存在区分空命名空间与默认命名空间。
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildQueryStringPreservingEmpty(params: Record<string, string | number | boolean | undefined>): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createRemoteFetch } from "./client-transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端端点函数共享的请求与查询构造层。
|
|
4
|
+
*
|
|
5
|
+
* 职责单一聚焦:JSON 响应解析与错误信封透传、查询参数序列化,
|
|
6
|
+
* 供各按域端点模块复用,杜绝端点函数内手工拼 query 样板。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 执行远端请求并将响应解析为 JSON,统一处理错误信封透传。
|
|
10
|
+
* 非 2xx 响应或显式 ok=false 信封抛出携带 code/status/details 的 Error。
|
|
11
|
+
*/
|
|
12
|
+
export async function fetchRemoteJson(serverUrl, path, token, options = {}) {
|
|
13
|
+
const remoteFetch = createRemoteFetch(serverUrl, token, {
|
|
14
|
+
allowInsecureHttp: options.allowInsecureHttp,
|
|
15
|
+
insecure: options.insecure,
|
|
16
|
+
dispatcher: options.dispatcher,
|
|
17
|
+
});
|
|
18
|
+
const res = await remoteFetch(path, {
|
|
19
|
+
method: options.method,
|
|
20
|
+
body: options.body,
|
|
21
|
+
});
|
|
22
|
+
const data = (await res.json().catch(() => ({})));
|
|
23
|
+
if (!res.ok || (options.method === "POST" && data && data.ok === false)) {
|
|
24
|
+
const errorPrefix = options.errorPrefix || "Remote request failed";
|
|
25
|
+
const msg = data?.error?.message || `${errorPrefix} (${res.status}): ${res.statusText}`;
|
|
26
|
+
const err = new Error(msg);
|
|
27
|
+
if (data?.error?.code) {
|
|
28
|
+
err.code = data.error.code;
|
|
29
|
+
}
|
|
30
|
+
err.status = res.status;
|
|
31
|
+
err.errorData = data?.error;
|
|
32
|
+
err.details = data?.error?.details ?? data?.error;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 由可选参数字典构造查询串(无参数时返回空串)。
|
|
39
|
+
*
|
|
40
|
+
* 序列化规则与既有端点行为严格一致:
|
|
41
|
+
* - undefined 与空串一律跳过(对应原有 if (options?.xxx) 真值守卫);
|
|
42
|
+
* - 需保留「存在但为空」语义的参数(如 namespace,服务端区分空串与未传递)
|
|
43
|
+
* 由调用方显式处理,不经由此助手序列化空串。
|
|
44
|
+
*/
|
|
45
|
+
export function buildQueryString(params) {
|
|
46
|
+
const search = new URLSearchParams();
|
|
47
|
+
for (const [key, value] of Object.entries(params)) {
|
|
48
|
+
if (value === undefined || value === "") {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
search.set(key, String(value));
|
|
52
|
+
}
|
|
53
|
+
const qs = search.toString();
|
|
54
|
+
return qs ? `?${qs}` : "";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 构造保留「存在但为空」语义的查询串:仅跳过 undefined,空串照常序列化。
|
|
58
|
+
* 适用 namespace 类参数:服务端以参数是否存在区分空命名空间与默认命名空间。
|
|
59
|
+
*/
|
|
60
|
+
export function buildQueryStringPreservingEmpty(params) {
|
|
61
|
+
const search = new URLSearchParams();
|
|
62
|
+
for (const [key, value] of Object.entries(params)) {
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
search.set(key, String(value));
|
|
67
|
+
}
|
|
68
|
+
const qs = search.toString();
|
|
69
|
+
return qs ? `?${qs}` : "";
|
|
70
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { RunRecord } from "@actiondock/sdk";
|
|
2
|
+
import type { RemoteClientRequestOptions } from "./client-transport.js";
|
|
3
|
+
/**
|
|
4
|
+
* 远端运行记录域端点。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 拉取单条远端运行记录。
|
|
8
|
+
*/
|
|
9
|
+
export declare function fetchRemoteRun(serverUrl: string, runId: string, token?: string, options?: RemoteClientRequestOptions): Promise<RunRecord>;
|
|
10
|
+
/**
|
|
11
|
+
* 取消远端运行。
|
|
12
|
+
*/
|
|
13
|
+
export declare function cancelRemoteRun(serverUrl: string, runId: string, token?: string, reason?: string, options?: RemoteClientRequestOptions): Promise<{
|
|
14
|
+
ok: boolean;
|
|
15
|
+
runId: string;
|
|
16
|
+
status: string;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* 分页查询远端运行记录列表。
|
|
20
|
+
*/
|
|
21
|
+
export declare function fetchRemoteRuns(serverUrl: string, token?: string, options?: {
|
|
22
|
+
status?: string;
|
|
23
|
+
actionId?: string;
|
|
24
|
+
packageId?: string;
|
|
25
|
+
intent?: string;
|
|
26
|
+
limit?: number;
|
|
27
|
+
} & RemoteClientRequestOptions): Promise<{
|
|
28
|
+
ok: boolean;
|
|
29
|
+
total: number;
|
|
30
|
+
items: RunRecord[];
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* 清空远端运行记录(可按包、Action、状态过滤清除范围)。
|
|
34
|
+
*/
|
|
35
|
+
export declare function clearRemoteRuns(serverUrl: string, token?: string, options?: {
|
|
36
|
+
packageId?: string;
|
|
37
|
+
actionId?: string;
|
|
38
|
+
status?: string;
|
|
39
|
+
} & RemoteClientRequestOptions): Promise<{
|
|
40
|
+
ok: boolean;
|
|
41
|
+
clearedCount: number;
|
|
42
|
+
}>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { buildQueryString, fetchRemoteJson } from "./client-query.js";
|
|
2
|
+
/**
|
|
3
|
+
* 远端运行记录域端点。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 拉取单条远端运行记录。
|
|
7
|
+
*/
|
|
8
|
+
export async function fetchRemoteRun(serverUrl, runId, token, options) {
|
|
9
|
+
return fetchRemoteJson(serverUrl, `/api/v2/runs/${encodeURIComponent(runId)}`, token, {
|
|
10
|
+
errorPrefix: `Failed to fetch remote run '${runId}'`,
|
|
11
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
12
|
+
insecure: options?.insecure,
|
|
13
|
+
dispatcher: options?.dispatcher,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 取消远端运行。
|
|
18
|
+
*/
|
|
19
|
+
export async function cancelRemoteRun(serverUrl, runId, token, reason, options) {
|
|
20
|
+
return fetchRemoteJson(serverUrl, `/api/v2/runs/${encodeURIComponent(runId)}/cancel`, token, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
body: { reason },
|
|
23
|
+
errorPrefix: `Failed to cancel remote run '${runId}'`,
|
|
24
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
25
|
+
insecure: options?.insecure,
|
|
26
|
+
dispatcher: options?.dispatcher,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 分页查询远端运行记录列表。
|
|
31
|
+
*/
|
|
32
|
+
export async function fetchRemoteRuns(serverUrl, token, options) {
|
|
33
|
+
return fetchRemoteJson(serverUrl, `/api/v2/runs${buildQueryString({
|
|
34
|
+
status: options?.status,
|
|
35
|
+
actionId: options?.actionId,
|
|
36
|
+
packageId: options?.packageId,
|
|
37
|
+
intent: options?.intent,
|
|
38
|
+
limit: options?.limit,
|
|
39
|
+
})}`, token, {
|
|
40
|
+
errorPrefix: "Failed to fetch remote runs",
|
|
41
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
42
|
+
insecure: options?.insecure,
|
|
43
|
+
dispatcher: options?.dispatcher,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 清空远端运行记录(可按包、Action、状态过滤清除范围)。
|
|
48
|
+
*/
|
|
49
|
+
export async function clearRemoteRuns(serverUrl, token, options) {
|
|
50
|
+
return fetchRemoteJson(serverUrl, "/api/v2/runs/clear", token, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: options || {},
|
|
53
|
+
errorPrefix: "Failed to clear remote runs",
|
|
54
|
+
allowInsecureHttp: options?.allowInsecureHttp,
|
|
55
|
+
insecure: options?.insecure,
|
|
56
|
+
dispatcher: options?.dispatcher,
|
|
57
|
+
});
|
|
58
|
+
}
|