@actiondock/core 2.18.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/client/config.d.ts +17 -14
  2. package/dist/client/config.js +112 -14
  3. package/dist/client/index.d.ts +1 -1
  4. package/dist/client/index.js +1 -1
  5. package/dist/client/runs.d.ts +9 -0
  6. package/dist/client/runs.js +7 -0
  7. package/dist/errors.d.ts +3 -1
  8. package/dist/errors.js +2 -0
  9. package/dist/execution/service.js +8 -5
  10. package/dist/execution/types.d.ts +3 -3
  11. package/dist/host/host.d.ts +9 -1
  12. package/dist/host/host.js +266 -45
  13. package/dist/host/types.d.ts +3 -0
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.js +1 -1
  16. package/dist/input/config-override.d.ts +5 -0
  17. package/dist/input/config-override.js +23 -0
  18. package/dist/input/input-resolver.d.ts +24 -7
  19. package/dist/input/input-resolver.js +73 -5
  20. package/dist/invocation/bindings.d.ts +53 -0
  21. package/dist/invocation/bindings.js +248 -0
  22. package/dist/invocation/policy.d.ts +5 -1
  23. package/dist/invocation/policy.js +16 -9
  24. package/dist/invocation/remote.d.ts +40 -0
  25. package/dist/invocation/remote.js +489 -0
  26. package/dist/invocation/types.d.ts +2 -0
  27. package/dist/invocation/types.js +1 -0
  28. package/dist/package/index.d.ts +1 -1
  29. package/dist/package/runtime.js +105 -15
  30. package/dist/platform/types.d.ts +48 -1
  31. package/dist/project/config-declarations.d.ts +61 -0
  32. package/dist/project/config-declarations.js +297 -0
  33. package/dist/project/index.d.ts +5 -2
  34. package/dist/project/index.js +5 -2
  35. package/dist/project/init.js +2 -2
  36. package/dist/project/loader.js +4 -0
  37. package/dist/project/manifest.js +10 -0
  38. package/dist/project/types.d.ts +30 -0
  39. package/dist/runtime/context.d.ts +6 -3
  40. package/dist/runtime/context.js +60 -13
  41. package/dist/runtime/env.js +15 -1
  42. package/dist/runtime/standalone-commands.d.ts +2 -2
  43. package/dist/runtime/standalone-commands.js +113 -47
  44. package/dist/runtime/standalone-help.d.ts +1 -0
  45. package/dist/runtime/standalone-help.js +15 -9
  46. package/dist/runtime/standalone.js +2 -2
  47. package/dist/server/policy.d.ts +24 -107
  48. package/dist/server/policy.js +134 -291
  49. package/dist/server/routes/actions.js +10 -8
  50. package/dist/server/routes/common.d.ts +5 -20
  51. package/dist/server/routes/common.js +22 -58
  52. package/dist/server/routes/config.js +65 -7
  53. package/dist/server/routes/doctor.d.ts +1 -0
  54. package/dist/server/routes/doctor.js +4 -3
  55. package/dist/server/routes/info.js +100 -13
  56. package/dist/server/routes/playbooks.js +1 -3
  57. package/dist/server/routes/runs.js +112 -18
  58. package/dist/server/server.d.ts +2 -1
  59. package/dist/server/server.js +38 -55
  60. package/dist/server/types.d.ts +6 -6
  61. package/dist/service/factory.js +1 -0
  62. package/dist/service/remote.js +28 -29
  63. package/dist/service/types.d.ts +34 -5
  64. package/dist/service/types.js +8 -0
  65. package/dist/storage/mask.d.ts +33 -6
  66. package/dist/storage/mask.js +298 -16
  67. package/dist/storage/sqlite-runs.d.ts +5 -17
  68. package/dist/storage/sqlite-runs.js +127 -5
  69. package/dist/storage/sqlite.d.ts +5 -17
  70. package/dist/storage/sqlite.js +2 -2
  71. package/dist/storage/types.d.ts +31 -16
  72. package/dist/version.d.ts +1 -1
  73. package/dist/version.js +1 -1
  74. package/package.json +2 -2
@@ -3,7 +3,66 @@ import { isTerminalRunStatus } from "../../storage/types.js";
3
3
  import { createAbortError } from "../../ipc/service.js";
4
4
  import { parseDuration } from "../../utils/index.js";
5
5
  import { readJsonBody } from "../body.js";
6
- import { getSubPath, isActionAllowedByPolicy, isPackageAllowedByPolicy, jsonResponse } from "./common.js";
6
+ import { parseActionRef } from "../../catalog/resolve-action.js";
7
+ import { getSubPath, isActionAllowedByPolicy, isFullAccessGranted, isPackageAllowedByPolicy, isPackageVisibleByPolicy, jsonResponse } from "./common.js";
8
+ /** 清理范围必须落在整包或精确动作授权内,全局清理必须显式拥有全部授权。 */
9
+ function resolveRunCleanupScope(packageId, actionId, policy) {
10
+ if (isFullAccessGranted(policy)) {
11
+ if (packageId && !isPackageVisibleByPolicy(packageId, policy)) {
12
+ return {
13
+ ok: false,
14
+ reason: `Package '${packageId}' is not in the allowed package list`,
15
+ };
16
+ }
17
+ return { ok: true, filter: { packageId, actionId } };
18
+ }
19
+ // 单动作清理必须解析出完整身份,避免鉴权与实际清理范围不一致。
20
+ if (actionId) {
21
+ let parsed;
22
+ try {
23
+ parsed = parseActionRef(actionId);
24
+ }
25
+ catch {
26
+ return { ok: false, reason: `Invalid action reference '${actionId}'` };
27
+ }
28
+ // 全限定引用与显式 packageId 归一化后不一致必须拒绝,防止鉴权与过滤范围错位
29
+ if (parsed.packageId && packageId && parsed.packageId !== packageId) {
30
+ return {
31
+ ok: false,
32
+ reason: `Action reference '${parsed.packageId}/${parsed.actionId}' does not match the requested package scope '${packageId}'`,
33
+ };
34
+ }
35
+ const targetPkgId = parsed.packageId || packageId;
36
+ if (!targetPkgId) {
37
+ return {
38
+ ok: false,
39
+ reason: "Restricted access policy requires a fully qualified 'packageId/actionId' action reference for run cleanup",
40
+ };
41
+ }
42
+ if (!isActionAllowedByPolicy({ packageId: targetPkgId, actionId: parsed.actionId }, policy)) {
43
+ return {
44
+ ok: false,
45
+ reason: `Action '${targetPkgId}/${parsed.actionId}' is not in the allowed action list`,
46
+ };
47
+ }
48
+ // 以归一化完整引用传递,确保底层过滤范围与鉴权引用一致且不扩大
49
+ return { ok: true, filter: { packageId: targetPkgId, actionId: parsed.actionId } };
50
+ }
51
+ // 无 actionId:必须有明确包范围,且该范围必须是整包授权
52
+ if (!packageId) {
53
+ return {
54
+ ok: false,
55
+ reason: "Restricted access policy requires an explicit 'packageId' or fully qualified 'actionId' scope for run cleanup",
56
+ };
57
+ }
58
+ if (!isPackageAllowedByPolicy(packageId, policy)) {
59
+ return {
60
+ ok: false,
61
+ reason: `Package '${packageId}' is not fully granted; run cleanup requires a fully granted package or an explicitly granted 'packageId/actionId' reference`,
62
+ };
63
+ }
64
+ return { ok: true, filter: { packageId } };
65
+ }
7
66
  /**
8
67
  * 处理历史运行记录查询、清理、详情及 SSE 流式日志接口。
9
68
  */
@@ -16,6 +75,13 @@ export async function handleRunsRoutes(ctx) {
16
75
  const status = url.searchParams.get("status") || undefined;
17
76
  const actionId = url.searchParams.get("actionId") || undefined;
18
77
  const packageId = url.searchParams.get("packageId") || undefined;
78
+ const channel = url.searchParams.get("channel") || undefined;
79
+ const view = url.searchParams.get("view") || undefined;
80
+ const since = url.searchParams.get("since") || undefined;
81
+ const until = url.searchParams.get("until") || undefined;
82
+ const rootRunId = url.searchParams.get("rootRunId") || undefined;
83
+ const parentRunId = url.searchParams.get("parentRunId") || undefined;
84
+ const withTotal = url.searchParams.get("withTotal") === "true";
19
85
  const intent = url.searchParams.get("intent") || undefined;
20
86
  // requestId 反查:支持 requestId 与 requestIds 重复查询参数,将每一个参数值作为原样字符串保存
21
87
  const requestIds = [
@@ -47,7 +113,7 @@ export async function handleRunsRoutes(ctx) {
47
113
  limit = Math.min(Math.max(parsedLimit, 1), 500);
48
114
  }
49
115
  }
50
- if (packageId && !isPackageAllowedByPolicy(packageId, policy)) {
116
+ if (packageId && !isPackageVisibleByPolicy(packageId, policy)) {
51
117
  return jsonResponse({
52
118
  ok: false,
53
119
  error: {
@@ -56,7 +122,7 @@ export async function handleRunsRoutes(ctx) {
56
122
  },
57
123
  }, 403, corsHeaders);
58
124
  }
59
- if (actionId && !isActionAllowedByPolicy({ packageId, actionId }, policy)) {
125
+ if (actionId && (packageId || actionId.includes("/")) && !isActionAllowedByPolicy({ packageId, actionId }, policy)) {
60
126
  return jsonResponse({
61
127
  ok: false,
62
128
  error: {
@@ -65,19 +131,32 @@ export async function handleRunsRoutes(ctx) {
65
131
  },
66
132
  }, 403, corsHeaders);
67
133
  }
134
+ // 下推的是包可见性并集,仍须逐条校验动作,不能丢弃单动作授权包的记录。
135
+ const visiblePackageIds = new Set([
136
+ ...(policy.packageAllowlist || []).filter((id) => id !== "*"),
137
+ ...(policy.actionAllowlist || []).map((ref) => parseActionRef(ref).packageId),
138
+ ]);
139
+ const backendPackageFilter = isFullAccessGranted(policy) ? undefined : [...visiblePackageIds];
140
+ if (backendPackageFilter?.length === 0) {
141
+ return jsonResponse({ ok: true, items: [], total: 0, limit, offset }, 200, corsHeaders);
142
+ }
68
143
  const queryOptions = {
69
144
  actionId,
70
145
  status,
71
146
  packageId,
147
+ channel,
148
+ view,
149
+ since,
150
+ until,
151
+ rootRunId,
152
+ parentRunId,
153
+ withTotal,
72
154
  intent,
73
155
  requestIds: requestIds.length > 0 ? requestIds : undefined,
74
156
  signal: req.signal,
75
- packageAllowlist: policy.packageAllowlist && policy.packageAllowlist.length > 0
76
- ? policy.packageAllowlist
77
- : undefined,
157
+ packageAllowlist: backendPackageFilter,
78
158
  };
79
- const hasPolicyFilter = Boolean((policy.packageAllowlist && policy.packageAllowlist.length > 0) ||
80
- (policy.actionAllowlist && policy.actionAllowlist.length > 0));
159
+ const hasPolicyFilter = !isFullAccessGranted(policy);
81
160
  let runs;
82
161
  let total;
83
162
  if (hasPolicyFilter) {
@@ -102,7 +181,9 @@ export async function handleRunsRoutes(ctx) {
102
181
  break;
103
182
  }
104
183
  for (const run of batch) {
105
- const packageAllowed = isPackageAllowedByPolicy(run.packageId, policy);
184
+ // 逐条以实际 packageId/actionId 完整引用判定授权:
185
+ // 包可见性拦无关包,动作授权保留单动作授权目标(如 B/read)
186
+ const packageAllowed = isPackageVisibleByPolicy(run.packageId, policy);
106
187
  const actionAllowed = isActionAllowedByPolicy({ packageId: run.packageId, actionId: run.actionId }, policy);
107
188
  if (packageAllowed && actionAllowed) {
108
189
  if (matchedCount >= offset && pagedRuns.length < limit) {
@@ -153,6 +234,8 @@ export async function handleRunsRoutes(ctx) {
153
234
  const responseData = {
154
235
  ok: true,
155
236
  items: runs,
237
+ limit,
238
+ offset,
156
239
  };
157
240
  if (typeof total === "number") {
158
241
  responseData.total = total;
@@ -202,20 +285,22 @@ export async function handleRunsRoutes(ctx) {
202
285
  keep = parsed;
203
286
  }
204
287
  }
205
- if (packageId && !isPackageAllowedByPolicy(packageId, policy)) {
288
+ const scopeCheck = resolveRunCleanupScope(packageId, actionId, policy);
289
+ if (!scopeCheck.ok) {
206
290
  return jsonResponse({
207
291
  ok: false,
208
292
  error: {
209
293
  code: PACKAGE_FORBIDDEN,
210
- message: `Package '${packageId}' is not in the allowed package list`,
294
+ message: scopeCheck.reason,
211
295
  },
212
296
  }, 403, corsHeaders);
213
297
  }
298
+ const clearedScope = scopeCheck.filter;
214
299
  let clearedCount = 0;
215
300
  if (service.runs.clear) {
216
301
  clearedCount = await service.runs.clear({
217
- packageId,
218
- actionId,
302
+ packageId: clearedScope.packageId,
303
+ actionId: clearedScope.actionId,
219
304
  status,
220
305
  olderThanMs,
221
306
  keep,
@@ -228,7 +313,17 @@ export async function handleRunsRoutes(ctx) {
228
313
  }
229
314
  }
230
315
  // 2.1 Runs Clean Expired: POST /api/v2/runs/clean-expired, POST /runs/clean-expired
316
+ // 此操作无法按包或动作收窄范围,只允许显式的全部授权。
231
317
  if (subpath === "/runs/clean-expired" && req.method === "POST") {
318
+ if (!isFullAccessGranted(policy)) {
319
+ return jsonResponse({
320
+ ok: false,
321
+ error: {
322
+ code: PACKAGE_FORBIDDEN,
323
+ message: "Expired run cleanup cannot be scoped under a restricted access policy",
324
+ },
325
+ }, 403, corsHeaders);
326
+ }
232
327
  try {
233
328
  let body = {};
234
329
  if (req.headers.get("content-type")?.includes("json")) {
@@ -252,7 +347,7 @@ export async function handleRunsRoutes(ctx) {
252
347
  if (!run) {
253
348
  return jsonResponse({ ok: false, error: { code: RUN_NOT_FOUND, message: `Run '${runId}' not found` } }, 404, corsHeaders);
254
349
  }
255
- if (!isPackageAllowedByPolicy(run.packageId, policy)) {
350
+ if (!isPackageVisibleByPolicy(run.packageId, policy)) {
256
351
  return jsonResponse({
257
352
  ok: false,
258
353
  error: {
@@ -427,7 +522,7 @@ export async function handleRunsRoutes(ctx) {
427
522
  },
428
523
  }, 404, corsHeaders);
429
524
  }
430
- if (!isPackageAllowedByPolicy(run.packageId, policy)) {
525
+ if (!isPackageVisibleByPolicy(run.packageId, policy)) {
431
526
  return jsonResponse({
432
527
  ok: false,
433
528
  error: {
@@ -451,11 +546,10 @@ export async function handleRunsRoutes(ctx) {
451
546
  const runCancelMatch = subpath.match(/^\/runs\/([^/]+)\/cancel$/);
452
547
  if (runCancelMatch && req.method === "POST") {
453
548
  const runId = decodeURIComponent(runCancelMatch[1]);
454
- if ((policy.packageAllowlist && policy.packageAllowlist.length > 0) ||
455
- (policy.actionAllowlist && policy.actionAllowlist.length > 0)) {
549
+ if (!isFullAccessGranted(policy)) {
456
550
  const run = await service.runs.get(runId);
457
551
  if (run) {
458
- if (!isPackageAllowedByPolicy(run.packageId, policy)) {
552
+ if (!isPackageVisibleByPolicy(run.packageId, policy)) {
459
553
  return jsonResponse({
460
554
  ok: false,
461
555
  error: {
@@ -1,3 +1,4 @@
1
+ import type { ActionDockService } from "../service/types.js";
1
2
  import type { ActionDockServerInstance, CoreHttpServerInstance, ServerOptions, ServerTlsOptions } from "./types.js";
2
3
  /**
3
4
  * 规范化主机地址用于拼接 URL。
@@ -12,4 +13,4 @@ export declare function launchHttpServer(port: number, host: string, fetchHandle
12
13
  * 启动 ActionDock 原生轻量级 HTTP 服务端。
13
14
  * 面向 ActionDockService 服务端口,负责中间件流转、认证拦截与路由分发。
14
15
  */
15
- export declare function startActionDockServer(options?: ServerOptions): Promise<ActionDockServerInstance>;
16
+ export declare function startActionDockServer(arg1?: ServerOptions | ActionDockService, arg2?: ServerOptions): Promise<ActionDockServerInstance>;
@@ -3,8 +3,8 @@ import { NodeHttpServer } from "./http-server.js";
3
3
  import { createActionDock } from "../service/factory.js";
4
4
  import { ActionDockError, ACCESS_DENIED, NOT_FOUND, SERVICE_ERROR, UNAUTHORIZED } from "../errors.js";
5
5
  import { parseActionRef } from "../catalog/resolve-action.js";
6
- import { findProjectRoot } from "../project/loader.js";
7
- import { listLinkedPackages, resolvePackageRoot } from "../registry/registry.js";
6
+ import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
7
+ import { resolvePackageRoot } from "../registry/registry.js";
8
8
  import { handleActionsRoutes, handleConfigRoutes, handleDoctorRoute, handleHealthRoute, handleInfoRoute, handlePlaybooksRoutes, handleRunsRoutes, handleStateRoutes, jsonResponse, } from "./routes/index.js";
9
9
  import { isLoopbackHost, resolveCorsHeaders, verifyBearerToken } from "./security.js";
10
10
  import { createMcpEndpointHandler } from "./mcp-endpoint.js";
@@ -40,8 +40,19 @@ export async function launchHttpServer(port, host, fetchHandler, tls) {
40
40
  * 启动 ActionDock 原生轻量级 HTTP 服务端。
41
41
  * 面向 ActionDockService 服务端口,负责中间件流转、认证拦截与路由分发。
42
42
  */
43
- export async function startActionDockServer(options = {}) {
44
- const hostString = options.hostname ?? options.host ?? "127.0.0.1";
43
+ export async function startActionDockServer(arg1, arg2) {
44
+ let options;
45
+ if (arg1 && typeof arg1 === "object" && ("discovery" in arg1 || "execution" in arg1 || "runs" in arg1)) {
46
+ options = { ...(arg2 || {}), service: arg1 };
47
+ }
48
+ else {
49
+ options = arg1 || {};
50
+ }
51
+ const hostString = typeof options.hostname === "string"
52
+ ? options.hostname
53
+ : typeof options.host === "string"
54
+ ? options.host
55
+ : "127.0.0.1";
45
56
  const port = options.port ?? 5177;
46
57
  const host = hostString;
47
58
  const token = options.token;
@@ -59,70 +70,42 @@ export async function startActionDockServer(options = {}) {
59
70
  throw new ActionDockError(ACCESS_DENIED, "Authentication token is required when binding to a non-loopback address. Use --allow-insecure-no-auth to override.");
60
71
  }
61
72
  let serviceInstance = options.service;
62
- // 若未传 service,服务端统一通过 createActionDock() 实例化,不再接受或处理外部传入的 Host 实体
73
+ // 包授权与单动作引用都参与自建服务的装载;外部注入服务自行负责能力装配。
63
74
  if (!serviceInstance) {
64
- const scanLinkedPackages = options.scanLinkedPackages ?? !projectRoot;
75
+ const declaredId = projectRoot ? loadProjectConfig(projectRoot).id : undefined;
76
+ const referencedPackages = new Set();
77
+ for (const view of views.values()) {
78
+ for (const pkgId of view.policy.packageAllowlist || []) {
79
+ if (pkgId !== "*")
80
+ referencedPackages.add(pkgId);
81
+ }
82
+ for (const action of view.policy.actionAllowlist || []) {
83
+ referencedPackages.add(parseActionRef(action).packageId);
84
+ }
85
+ }
86
+ const extraPackages = [];
87
+ for (const pkgId of referencedPackages) {
88
+ if (pkgId === declaredId)
89
+ continue;
90
+ const root = resolvePackageRoot(pkgId, projectRoot || undefined, customHome);
91
+ if (root && !extraPackages.some((pkg) => pkg.packageRoot === root)) {
92
+ extraPackages.push({ packageRoot: root });
93
+ }
94
+ }
65
95
  serviceInstance = await createActionDock({
66
96
  projectRoot: projectRoot || undefined,
97
+ packages: extraPackages.length > 0 ? extraPackages : undefined,
67
98
  customHome,
68
99
  platform: options.platform,
69
100
  dataDir: options.dataDir,
70
101
  inMemory: options.inMemory,
71
- scanLinkedPackages,
102
+ scanLinkedPackages: options.scanLinkedPackages ?? !projectRoot,
72
103
  enableManagement: options.enableManagement,
73
104
  });
74
105
  }
75
106
  if (!serviceInstance) {
76
107
  throw new ActionDockError(SERVICE_ERROR, "Failed to initialize ActionDockService for server");
77
108
  }
78
- // 2. 聚合所有 views 声明的依赖包与动作,确保依赖闭包满足
79
- const roots = [];
80
- if (projectRoot) {
81
- roots.push(projectRoot);
82
- }
83
- const aggregatedPackageAllowlist = new Set();
84
- const aggregatedActionAllowlist = new Set();
85
- for (const v of views.values()) {
86
- if (v.policy.packageAllowlist) {
87
- for (const pkg of v.policy.packageAllowlist) {
88
- aggregatedPackageAllowlist.add(pkg);
89
- }
90
- }
91
- if (v.policy.actionAllowlist) {
92
- for (const act of v.policy.actionAllowlist) {
93
- aggregatedActionAllowlist.add(act);
94
- }
95
- }
96
- }
97
- if (aggregatedPackageAllowlist.size > 0) {
98
- for (const pkgId of aggregatedPackageAllowlist) {
99
- const r = resolvePackageRoot(pkgId, projectRoot || undefined, customHome);
100
- if (r && !roots.includes(r)) {
101
- roots.push(r);
102
- }
103
- }
104
- }
105
- else if (!projectRoot) {
106
- for (const pkg of listLinkedPackages(customHome)) {
107
- if (!roots.includes(pkg.path)) {
108
- roots.push(pkg.path);
109
- }
110
- }
111
- }
112
- if (aggregatedActionAllowlist.size > 0) {
113
- for (const actRef of aggregatedActionAllowlist) {
114
- try {
115
- const parsed = parseActionRef(actRef);
116
- if (parsed.packageId) {
117
- const r = resolvePackageRoot(parsed.packageId, projectRoot || undefined, customHome);
118
- if (r && !roots.includes(r)) {
119
- roots.push(r);
120
- }
121
- }
122
- }
123
- catch { }
124
- }
125
- }
126
109
  // 3. 为每个启用 MCP 的视图独立装配 MCP 处理器
127
110
  if (options.enableMcp !== false) {
128
111
  for (const v of views.values()) {
@@ -26,9 +26,9 @@ export interface ServerViewOptions {
26
26
  name?: string;
27
27
  /** 该视图独立的 Bearer Token 鉴权令牌 */
28
28
  token?: string;
29
- /** 该视图允许访问执行的 Package ID 白名单列表(为空允许全部) */
29
+ /** 整包授权列表,与动作授权取并集;空列表不授予权限,独立 * 显式授权全部 */
30
30
  packageAllowlist?: string[];
31
- /** 该视图允许访问执行的 Action 标识白名单列表(为空允许全部,支持短名或 packageId/actionId) */
31
+ /** 全限定 packageId/actionId 精确动作授权列表;空列表不授予权限 */
32
32
  actionAllowlist?: string[];
33
33
  /** 该视图是否开启配置与状态管理路由能力(默认关闭) */
34
34
  enableManagement?: boolean;
@@ -45,9 +45,9 @@ export interface EffectiveServerPolicy {
45
45
  viewName?: string;
46
46
  /** 当前生效的 Bearer Token 鉴权令牌 */
47
47
  token?: string;
48
- /** 当前生效的 Package ID 白名单列表 */
48
+ /** 当前生效的整包授权列表,独立 * 代表全部 */
49
49
  packageAllowlist?: string[];
50
- /** 当前生效的 Action 标识白名单列表 */
50
+ /** 当前生效的全限定精确动作授权列表,与整包授权取并集 */
51
51
  actionAllowlist?: string[];
52
52
  /** 当前生效的管理能力开关 */
53
53
  enableManagement?: boolean;
@@ -80,9 +80,9 @@ export interface ServerOptions {
80
80
  allowInsecureNoAuth?: boolean;
81
81
  /** 允许跨域请求的 CORS Origin 白名单列表 */
82
82
  corsOrigins?: string[];
83
- /** 允许访问执行的 Package ID 白名单列表(为空允许全部) */
83
+ /** 整包授权列表,与动作授权取并集;空列表不授予权限,独立 * 显式授权全部 */
84
84
  packageAllowlist?: string[];
85
- /** 允许访问执行的 Action 标识白名单列表(为空允许全部,支持短名或 packageId/actionId) */
85
+ /** 全限定 packageId/actionId 精确动作授权列表;空列表不授予权限 */
86
86
  actionAllowlist?: string[];
87
87
  /** 最大允许的请求体字节限制(默认 1MB,防 DoS) */
88
88
  maxBodyBytes?: number;
@@ -10,6 +10,7 @@ export async function createActionDock(options = {}) {
10
10
  const inMemory = options.inMemory ?? options.runtimeOptions.inMemory;
11
11
  const host = await createActionDockHost({
12
12
  packages: [options.runtimeOptions],
13
+ actionBindings: options.actionBindings ?? options.hostOptions?.actionBindings,
13
14
  autoLoadCurrentProject: false,
14
15
  scanLinkedPackages: false,
15
16
  inMemory,
@@ -1,7 +1,8 @@
1
1
  import { parseActionRef } from "../catalog/resolve-action.js";
2
+ import { assertConcreteConfigKey } from "../project/config-declarations.js";
2
3
  import { ActionDockError, ACTION_CANCELLED, CAPABILITY_UNAVAILABLE, INVALID_ARGUMENT, TIMEOUT, } from "../errors.js";
3
4
  import { createAbortError } from "../ipc/service.js";
4
- import { cancelRemoteRun, clearRemoteRuns, cleanExpiredRemoteRuns, clearRemoteState, deleteRemoteConfig, deleteRemoteStateKey, executeRemoteAction, fetchRemoteActionShow, fetchRemoteActions, fetchRemoteConfig, fetchRemoteInfo, fetchRemotePlaybookShow, fetchRemotePlaybooks, fetchRemoteRun, fetchRemoteRuns, fetchRemoteStateList, getRemoteStateKey, setRemoteConfig, setRemoteStateKey, } from "../client/index.js";
5
+ import { cancelRemoteRun, clearRemoteRuns, cleanExpiredRemoteRuns, clearRemoteState, deleteRemoteConfig, deleteRemoteStateKey, executeRemoteAction, fetchRemoteActionShow, fetchRemoteActions, fetchRemoteConfig, fetchRemoteConfigItem, fetchRemoteInfo, fetchRemotePlaybookShow, fetchRemotePlaybooks, fetchRemoteRun, fetchRemoteRuns, fetchRemoteStateList, getRemoteStateKey, setRemoteConfig, setRemoteStateKey, } from "../client/index.js";
5
6
  import { isRemoteStateKeyNotFound, wrapRemoteError } from "./remote-errors.js";
6
7
  import { formatTerminalRunResult, pollRunCompletion } from "./remote-polling.js";
7
8
  import { streamRemoteEvents } from "./sse-stream.js";
@@ -259,6 +260,13 @@ export class RemoteActionDockService {
259
260
  packageId: query?.packageId,
260
261
  actionId: query?.actionId,
261
262
  status: query?.status,
263
+ channel: query?.channel,
264
+ view: query?.view,
265
+ since: query?.since,
266
+ until: query?.until,
267
+ rootRunId: query?.rootRunId,
268
+ parentRunId: query?.parentRunId,
269
+ withTotal: query?.withTotal,
262
270
  intent: query?.intent,
263
271
  limit: batchLimit,
264
272
  offset,
@@ -307,6 +315,13 @@ export class RemoteActionDockService {
307
315
  packageId: query?.packageId,
308
316
  actionId: query?.actionId,
309
317
  status: query?.status,
318
+ channel: query?.channel,
319
+ view: query?.view,
320
+ since: query?.since,
321
+ until: query?.until,
322
+ rootRunId: query?.rootRunId,
323
+ parentRunId: query?.parentRunId,
324
+ withTotal: true,
310
325
  intent: query?.intent,
311
326
  limit: 1,
312
327
  offset: 0,
@@ -435,19 +450,15 @@ export class RemoteActionDockService {
435
450
  config: {
436
451
  async get(packageId, key) {
437
452
  self.assertNotClosed();
453
+ assertConcreteConfigKey(key);
438
454
  try {
439
- const list = await self.management.config.list(packageId);
440
- const found = list.find((c) => c.key === key);
441
- if (found) {
442
- return found;
443
- }
444
- return {
445
- key,
446
- configured: false,
447
- secret: false,
448
- source: "default",
449
- value: undefined,
450
- };
455
+ const response = await fetchRemoteConfigItem(self.serverUrl, key, self.token, packageId || undefined, {
456
+ allowInsecureHttp: self.allowInsecureHttp,
457
+ insecure: self.insecure,
458
+ dispatcher: self.dispatcher,
459
+ });
460
+ // 严格单项读取已保证恰一项且键匹配,直接消费,不伪造未命中视图掩盖契约错误。
461
+ return response.items[0];
451
462
  }
452
463
  catch (err) {
453
464
  wrapRemoteError(err);
@@ -455,6 +466,7 @@ export class RemoteActionDockService {
455
466
  },
456
467
  async set(packageId, key, value) {
457
468
  self.assertNotClosed();
469
+ assertConcreteConfigKey(key);
458
470
  try {
459
471
  await setRemoteConfig(self.serverUrl, key, value, self.token, packageId || undefined, {
460
472
  allowInsecureHttp: self.allowInsecureHttp,
@@ -468,6 +480,7 @@ export class RemoteActionDockService {
468
480
  },
469
481
  async delete(packageId, key) {
470
482
  self.assertNotClosed();
483
+ assertConcreteConfigKey(key);
471
484
  try {
472
485
  const res = await deleteRemoteConfig(self.serverUrl, key, self.token, packageId || undefined, {
473
486
  allowInsecureHttp: self.allowInsecureHttp,
@@ -488,22 +501,8 @@ export class RemoteActionDockService {
488
501
  insecure: self.insecure,
489
502
  dispatcher: self.dispatcher,
490
503
  });
491
- if (Array.isArray(res))
492
- return res;
493
- if (Array.isArray(res?.items))
494
- return res.items;
495
- if (Array.isArray(res?.config))
496
- return res.config;
497
- const values = res?.values || {};
498
- const declared = res?.declared || {};
499
- const defaultSource = packageId === "global" ? "global" : "package";
500
- return Object.entries(values).map(([k, v]) => ({
501
- key: k,
502
- configured: v !== undefined,
503
- secret: Boolean(declared[k]?.secret),
504
- source: defaultSource,
505
- value: v,
506
- }));
504
+ // 归一化边界已强校验完整视图,直接消费,不再叠加兜底。
505
+ return res.items;
507
506
  }
508
507
  catch (err) {
509
508
  wrapRemoteError(err);
@@ -4,6 +4,7 @@ import type { CancelResult, ExecutionTicket } from "../execution/types.js";
4
4
  import type { ActionDockHostOptions } from "../host/types.js";
5
5
  import type { RuntimePlatform } from "../platform/types.js";
6
6
  import type { StateEntry } from "../storage/types.js";
7
+ import type { ActionBindings } from "../invocation/bindings.js";
7
8
  import { PROTOCOL_UNSUPPORTED, SERVICE_RESULT_UNKNOWN, SERVICE_CLOSED } from "../errors.js";
8
9
  /**
9
10
  * 公开运行控制选项(RunOptions)。
@@ -29,6 +30,16 @@ export declare const ACTIONDOCK_PROTOCOL_VERSION = "2.0";
29
30
  * 服务端/通信协议错误码常量(单一事实源引用自 errors.ts)。
30
31
  */
31
32
  export { PROTOCOL_UNSUPPORTED, SERVICE_RESULT_UNKNOWN, SERVICE_CLOSED };
33
+ /**
34
+ * 配置来源契约(单一事实源)。
35
+ * 仅表达当前生效配置值的实际来源,不混入传输方式或作用域显示别名:
36
+ * override 为运行时固定覆盖或单次调用覆盖,package 为包级持久化,global 为全局持久化,
37
+ * env 为环境变量命中,default 为清单声明且真实生效的默认值。
38
+ * 无任何生效值时为 null,不得用 default 伪装。
39
+ */
40
+ export declare const CONFIG_SOURCES: readonly ["override", "package", "global", "env", "default"];
41
+ /** 配置来源类型;由来源元组派生,枚举值单一事实源。 */
42
+ export type ConfigSource = (typeof CONFIG_SOURCES)[number];
32
43
  /**
33
44
  * 配置项安全视图契约。
34
45
  * 屏蔽敏感配置原文,仅暴露是否已配置、是否为秘密及实际解析来源。
@@ -36,14 +47,16 @@ export { PROTOCOL_UNSUPPORTED, SERVICE_RESULT_UNKNOWN, SERVICE_CLOSED };
36
47
  export interface ConfigValueView {
37
48
  /** 配置项键名 */
38
49
  key: string;
39
- /** 是否已显式配置或存在有效值 */
50
+ /** 是否来自显式来源(override/package/global/env);default 与未配置(source null)均为 false */
40
51
  configured: boolean;
41
- /** 是否为秘密敏感配置 */
52
+ /** 声明含敏感字段或实际发生保守隐藏;不代表整个值不可见 */
42
53
  secret: boolean;
43
- /** 配置值实际解析来源 */
44
- source: string;
45
- /** 非秘密配置的实际数据值 */
54
+ /** 配置值实际解析来源;无任何生效值时为 null */
55
+ source: ConfigSource | null;
56
+ /** 管理侧安全值;顶层敏感时不提供原值 */
46
57
  value?: JsonValue;
58
+ /** 标准 JSON Pointer 隐藏路径;根为 "",未发生隐藏时为空 */
59
+ redactedPaths: string[];
47
60
  }
48
61
  /**
49
62
  * 状态作用域与筛选控制选项。
@@ -72,6 +85,18 @@ export interface ListRunsOptions {
72
85
  actionId?: string;
73
86
  /** 运行终态状态筛选 */
74
87
  status?: string;
88
+ /** 运行通道筛选(如 http、mcp) */
89
+ channel?: string;
90
+ /** 服务视图名称筛选(如 default、admin) */
91
+ view?: string;
92
+ /** 起始时间筛选(UTC ISO 8601 或时间戳字符串,匹配 startedAt >= since) */
93
+ since?: string;
94
+ /** 截止时间筛选(UTC ISO 8601 或时间戳字符串,匹配 startedAt <= until) */
95
+ until?: string;
96
+ /** 根运行标识筛选 */
97
+ rootRunId?: string;
98
+ /** 父运行标识筛选 */
99
+ parentRunId?: string;
75
100
  /** 模糊意图检索模式 */
76
101
  intent?: string;
77
102
  /** 幂等请求标识集合(经 idempotency_keys 反查关联运行记录) */
@@ -80,6 +105,8 @@ export interface ListRunsOptions {
80
105
  offset?: number;
81
106
  /** 最大返回记录条数限制 */
82
107
  limit?: number;
108
+ /** 是否需要附带总数统计 */
109
+ withTotal?: boolean;
83
110
  /** 取消信号,用于中止读取请求 */
84
111
  signal?: AbortSignal;
85
112
  /** 包白名单过滤集合(可选) */
@@ -240,6 +267,8 @@ export interface ActionDockService {
240
267
  * 创建 ActionDock 本地服务选项。
241
268
  */
242
269
  export interface CreateActionDockOptions {
270
+ /** 按调用方包覆盖运行时命名绑定,不改变动作引用与 uses 声明。 */
271
+ actionBindings?: Readonly<Record<string, ActionBindings>>;
243
272
  /** 模式类型(可选) */
244
273
  type?: "local";
245
274
  /** 单包 PackageRuntime 初始化配置 */
@@ -7,3 +7,11 @@ export const ACTIONDOCK_PROTOCOL_VERSION = "2.0";
7
7
  * 服务端/通信协议错误码常量(单一事实源引用自 errors.ts)。
8
8
  */
9
9
  export { PROTOCOL_UNSUPPORTED, SERVICE_RESULT_UNKNOWN, SERVICE_CLOSED };
10
+ /**
11
+ * 配置来源契约(单一事实源)。
12
+ * 仅表达当前生效配置值的实际来源,不混入传输方式或作用域显示别名:
13
+ * override 为运行时固定覆盖或单次调用覆盖,package 为包级持久化,global 为全局持久化,
14
+ * env 为环境变量命中,default 为清单声明且真实生效的默认值。
15
+ * 无任何生效值时为 null,不得用 default 伪装。
16
+ */
17
+ export const CONFIG_SOURCES = ["override", "package", "global", "env", "default"];