@actiondock/core 2.16.0 → 2.18.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/execution/service.js +6 -0
- package/dist/host/host.js +16 -5
- package/dist/index.d.ts +1 -1
- package/dist/invocation/types.d.ts +5 -1
- package/dist/invocation/types.js +5 -0
- package/dist/package/runtime.js +8 -2
- package/dist/project/init.js +2 -2
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/run-origin.d.ts +9 -0
- package/dist/runtime/run-origin.js +33 -0
- package/dist/runtime/run-persistence.d.ts +3 -1
- package/dist/runtime/run-persistence.js +3 -0
- package/dist/server/policy.d.ts +8 -0
- package/dist/server/policy.js +14 -0
- package/dist/server/routes/actions.js +5 -0
- package/dist/service/types.d.ts +3 -1
- package/dist/storage/sqlite-runs.js +9 -3
- package/dist/storage/sqlite-schema.js +5 -1
- package/dist/storage/utils.d.ts +5 -2
- package/dist/storage/utils.js +7 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -7,6 +7,7 @@ import { InMemoryEventSink } from "../runtime/events.js";
|
|
|
7
7
|
import { ActionDockError, ACTION_CANCELLED, ACTION_FAILED, ACTION_NOT_FOUND, ACTION_TIMEOUT, EXECUTION_CONCURRENCY_LIMIT, EXECUTION_FAILED, IDEMPOTENCY_CONFLICT, INPUT_NOT_JSON, INPUT_VALIDATION_FAILED, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, OUTPUT_NOT_JSON, OUTPUT_VALIDATION_FAILED, RUN_REPOSITORY_UNAVAILABLE, SERVICE_CLOSED, UNHANDLED_EXECUTION_ERROR, describeActionLoadFailure, } from "../errors.js";
|
|
8
8
|
import { resultStatusToRunStatus } from "../storage/types.js";
|
|
9
9
|
import { createDefaultProcessOwner, } from "../invocation/types.js";
|
|
10
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
10
11
|
import { ActionRegistry, isActionDefinitionObject, resolveAnonymousActionId, } from "../runtime/action-registry.js";
|
|
11
12
|
import { buildInitialRunRecord, createRunFinalizer, createRunOrThrow, tryCreateRun, } from "../runtime/run-persistence.js";
|
|
12
13
|
import { createActionContext } from "../runtime/context.js";
|
|
@@ -432,6 +433,7 @@ export class DefaultExecutionService {
|
|
|
432
433
|
process: opts.process || this.process,
|
|
433
434
|
owner: opts.owner,
|
|
434
435
|
requestId: opts.requestId,
|
|
436
|
+
origin: snapshotRunOrigin(opts.origin),
|
|
435
437
|
};
|
|
436
438
|
}
|
|
437
439
|
async raceExecution(args) {
|
|
@@ -548,6 +550,7 @@ export class DefaultExecutionService {
|
|
|
548
550
|
progress: context.progress,
|
|
549
551
|
process: effectiveProcess,
|
|
550
552
|
owner: effectiveOwner,
|
|
553
|
+
origin: context.origin,
|
|
551
554
|
};
|
|
552
555
|
return await invoker(childAction, childInput, invocationContext);
|
|
553
556
|
}
|
|
@@ -712,8 +715,10 @@ export class DefaultExecutionService {
|
|
|
712
715
|
message: `Action '${targetActionId}' not found in package '${targetPackageId}'`,
|
|
713
716
|
};
|
|
714
717
|
const rootRunId = context.rootRunId || context.parentRunId || runId;
|
|
718
|
+
const origin = snapshotRunOrigin(context.origin);
|
|
715
719
|
const initialRun = {
|
|
716
720
|
id: runId,
|
|
721
|
+
...(origin !== undefined ? { origin } : {}),
|
|
717
722
|
rootRunId,
|
|
718
723
|
parentRunId: context.parentRunId,
|
|
719
724
|
packageId: targetPackageId,
|
|
@@ -777,6 +782,7 @@ export class DefaultExecutionService {
|
|
|
777
782
|
runId,
|
|
778
783
|
rootRunId,
|
|
779
784
|
parentRunId: context.parentRunId,
|
|
785
|
+
origin: context.origin,
|
|
780
786
|
ownerId: context.principalId || context.tenantId || this.ownerId,
|
|
781
787
|
hostSessionId: context.hostSessionId || this.hostSessionId,
|
|
782
788
|
hostPid: this.hostPid,
|
package/dist/host/host.js
CHANGED
|
@@ -7,6 +7,7 @@ import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
|
|
|
7
7
|
import { hasPendingTransactions, isProjectLockHeld, recoverPendingTransactions } from "../project/transactions.js";
|
|
8
8
|
import { listLinkedPackages } from "../registry/registry.js";
|
|
9
9
|
import { InMemoryEventSink } from "../runtime/events.js";
|
|
10
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
10
11
|
import { ActionDockError, ACTION_NOT_FOUND, ACTION_PACKAGE_VERSION_CONFLICT, ACTION_SUBRUN_LIMIT, EXECUTION_FAILED, INVALID_ACTION_REF, PACKAGE_ID_CONFLICT, PACKAGE_NOT_FOUND, PROJECT_BUSY, PROJECT_RECOVERY_REQUIRED, SERVICE_CLOSED, UNDECLARED_ACTION_DEPENDENCY, } from "../errors.js";
|
|
11
12
|
import { InvocationPolicy } from "../invocation/policy.js";
|
|
12
13
|
import { createAbortError } from "../ipc/service.js";
|
|
@@ -135,6 +136,7 @@ export class DefaultActionDockHost {
|
|
|
135
136
|
};
|
|
136
137
|
this.execution = {
|
|
137
138
|
async run(ref, input, opts) {
|
|
139
|
+
const origin = snapshotRunOrigin(opts?.origin);
|
|
138
140
|
await self.ensureInitialized();
|
|
139
141
|
const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
|
|
140
142
|
const cleanOpts = {
|
|
@@ -142,10 +144,12 @@ export class DefaultActionDockHost {
|
|
|
142
144
|
timeoutMs: opts?.timeoutMs,
|
|
143
145
|
config: opts?.config,
|
|
144
146
|
requestId: opts?.requestId,
|
|
147
|
+
origin,
|
|
145
148
|
};
|
|
146
149
|
return self.runAction(actionRef, (input ?? {}), cleanOpts);
|
|
147
150
|
},
|
|
148
151
|
async start(ref, input, opts) {
|
|
152
|
+
const origin = snapshotRunOrigin(opts?.origin);
|
|
149
153
|
await self.ensureInitialized();
|
|
150
154
|
const actionRef = typeof ref === "string" ? parseActionRef(ref) : ref;
|
|
151
155
|
const cleanOpts = {
|
|
@@ -153,6 +157,7 @@ export class DefaultActionDockHost {
|
|
|
153
157
|
timeoutMs: opts?.timeoutMs,
|
|
154
158
|
config: opts?.config,
|
|
155
159
|
requestId: opts?.requestId,
|
|
160
|
+
origin,
|
|
156
161
|
};
|
|
157
162
|
return self.startAction(actionRef, (input ?? {}), cleanOpts);
|
|
158
163
|
},
|
|
@@ -634,6 +639,7 @@ export class DefaultActionDockHost {
|
|
|
634
639
|
progress: context.progress,
|
|
635
640
|
process: context.process,
|
|
636
641
|
owner: targetOwner,
|
|
642
|
+
origin: context.origin,
|
|
637
643
|
};
|
|
638
644
|
const ticket = await targetRuntime.startInvocation(targetActionId, childInput, subInvocationContext);
|
|
639
645
|
if (!ticket.result) {
|
|
@@ -752,14 +758,18 @@ export class DefaultActionDockHost {
|
|
|
752
758
|
return describeVisiblePlaybook(this.listRuntimes(), id, this.visibility());
|
|
753
759
|
}
|
|
754
760
|
async runAction(ref, input, options = {}) {
|
|
761
|
+
const origin = snapshotRunOrigin(options.origin);
|
|
762
|
+
const effectiveOptions = { ...options, origin };
|
|
755
763
|
await this.ensureInitialized();
|
|
756
|
-
const ticket = await this.startAction(ref, input,
|
|
764
|
+
const ticket = await this.startAction(ref, input, effectiveOptions);
|
|
757
765
|
if (!ticket.result) {
|
|
758
766
|
throw new ActionDockError(EXECUTION_FAILED, `Execution ticket for run '${ticket.runId}' has no result Promise`);
|
|
759
767
|
}
|
|
760
768
|
return ticket.result;
|
|
761
769
|
}
|
|
762
770
|
async startAction(ref, input, options = {}) {
|
|
771
|
+
const origin = snapshotRunOrigin(options.origin);
|
|
772
|
+
const effectiveOptions = { ...options, origin };
|
|
763
773
|
await this.ensureInitialized();
|
|
764
774
|
if (this.isClosed) {
|
|
765
775
|
throw new ActionDockError(SERVICE_CLOSED, "ActionDockHost is closed: new tasks rejected");
|
|
@@ -837,13 +847,14 @@ export class DefaultActionDockHost {
|
|
|
837
847
|
const rootContext = createRootInvocationContext({
|
|
838
848
|
targetPackage: targetRuntime.identity,
|
|
839
849
|
callStack: [rootKey],
|
|
840
|
-
signal:
|
|
841
|
-
timeoutMs:
|
|
842
|
-
config:
|
|
843
|
-
requestId:
|
|
850
|
+
signal: effectiveOptions.signal,
|
|
851
|
+
timeoutMs: effectiveOptions.timeoutMs,
|
|
852
|
+
config: effectiveOptions.config,
|
|
853
|
+
requestId: effectiveOptions.requestId,
|
|
844
854
|
hostSessionId: this.hostSessionId,
|
|
845
855
|
maxCallDepth: this.maxCallDepth,
|
|
846
856
|
process: this.options.process,
|
|
857
|
+
origin: effectiveOptions.origin,
|
|
847
858
|
});
|
|
848
859
|
// 调度目标包执行服务
|
|
849
860
|
return targetRuntime.startInvocation(targetActionId, input, rootContext);
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type { TerminalRunStatus } from "./storage/types.js";
|
|
|
14
14
|
export { parseDuration } from "./utils/index.js";
|
|
15
15
|
export { toSnakeUpperCase, resolveEnvValue } from "./runtime/env.js";
|
|
16
16
|
export type { ResolvedEnv } from "./runtime/env.js";
|
|
17
|
-
export type { ActionRef, ResolvedActionRef, ExecutionResult, RunRecord, ExecutionEvent, } from "@actiondock/sdk";
|
|
17
|
+
export type { ActionRef, ResolvedActionRef, ExecutionResult, RunRecord, RunOrigin, ExecutionEvent, } from "@actiondock/sdk";
|
|
18
18
|
export type { RunOptions, ExecutionTicket, CancelResult, } from "./execution/types.js";
|
|
19
19
|
export type { ActionSpec, ActionSummary, PlaybookSpec, PlaybookSummary, PackageInfo, ListActionsOptions, } from "./package/types.js";
|
|
20
20
|
export { createNodePlatform, } from "./platform/node.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionDefinition, ActionRef, Logger, ProcessAPI, ProgressReporter } from "@actiondock/sdk";
|
|
1
|
+
import type { ActionDefinition, ActionRef, Logger, ProcessAPI, ProgressReporter, RunOrigin } from "@actiondock/sdk";
|
|
2
2
|
import type { ProcessOwner } from "../process/index.js";
|
|
3
3
|
import { createPackageIdentity, type PackageIdentity } from "../runtime/identity.js";
|
|
4
4
|
export { createPackageIdentity };
|
|
@@ -75,6 +75,8 @@ export interface InvocationContext {
|
|
|
75
75
|
readonly process?: ProcessAPI;
|
|
76
76
|
/** 进程属主身份 */
|
|
77
77
|
readonly owner?: ProcessOwner;
|
|
78
|
+
/** 运行来源追踪快照 */
|
|
79
|
+
readonly origin?: RunOrigin;
|
|
78
80
|
}
|
|
79
81
|
/**
|
|
80
82
|
* 内部调用上下文选项契约。
|
|
@@ -121,6 +123,8 @@ export interface CreateRootInvocationContextOptions {
|
|
|
121
123
|
progress?: ProgressReporter;
|
|
122
124
|
/** 显式执行归属所有者契约 */
|
|
123
125
|
owner?: ProcessOwner;
|
|
126
|
+
/** 运行来源追踪快照 */
|
|
127
|
+
origin?: RunOrigin;
|
|
124
128
|
}
|
|
125
129
|
/**
|
|
126
130
|
* 构造合法的受信任根调用上下文(Root InvocationContext)。
|
package/dist/invocation/types.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPackageIdentity } from "../runtime/identity.js";
|
|
2
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
2
3
|
export { createPackageIdentity };
|
|
3
4
|
/**
|
|
4
5
|
* 构造默认进程归属所有者(ProcessOwner)的单一事实源工厂。
|
|
@@ -23,6 +24,7 @@ export function createInvocationContext(options) {
|
|
|
23
24
|
const runId = options.runId ?? crypto.randomUUID();
|
|
24
25
|
const rootRunId = options.rootRunId ?? runId;
|
|
25
26
|
const pkg = options.package;
|
|
27
|
+
const origin = snapshotRunOrigin(options.origin);
|
|
26
28
|
return {
|
|
27
29
|
runId,
|
|
28
30
|
rootRunId,
|
|
@@ -41,6 +43,7 @@ export function createInvocationContext(options) {
|
|
|
41
43
|
logger: options.logger,
|
|
42
44
|
progress: options.progress,
|
|
43
45
|
process: options.process,
|
|
46
|
+
origin,
|
|
44
47
|
owner: options.owner ??
|
|
45
48
|
createDefaultProcessOwner({
|
|
46
49
|
tenantId: options.tenantId,
|
|
@@ -75,6 +78,7 @@ export function createRootInvocationContext(options) {
|
|
|
75
78
|
owner: options.owner,
|
|
76
79
|
tenantId: options.tenantId,
|
|
77
80
|
principalId: options.principalId,
|
|
81
|
+
origin: options.origin,
|
|
78
82
|
});
|
|
79
83
|
return {
|
|
80
84
|
runId: base.runId,
|
|
@@ -93,5 +97,6 @@ export function createRootInvocationContext(options) {
|
|
|
93
97
|
progress: base.progress,
|
|
94
98
|
process: base.process,
|
|
95
99
|
owner: base.owner,
|
|
100
|
+
origin: base.origin,
|
|
96
101
|
};
|
|
97
102
|
}
|
package/dist/package/runtime.js
CHANGED
|
@@ -11,6 +11,7 @@ import { createPackageIdentity } from "../runtime/identity.js";
|
|
|
11
11
|
import { ACTION_NOT_FOUND, ActionDockError, CAPABILITY_UNAVAILABLE, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, NOT_FOUND, SERVICE_CLOSED, } from "../errors.js";
|
|
12
12
|
import { createAbortError } from "../ipc/service.js";
|
|
13
13
|
import { createRootInvocationContext } from "../invocation/types.js";
|
|
14
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
14
15
|
import { buildStaticActionMap, buildStaticPlaybookMap } from "./static-index.js";
|
|
15
16
|
import { applyActionSummaryFilters } from "./types.js";
|
|
16
17
|
function normalizeStateReadArgs(arg1, arg2, arg3) {
|
|
@@ -550,9 +551,12 @@ export class DefaultPackageRuntime {
|
|
|
550
551
|
maxCallDepth: this.options.maxCallDepth,
|
|
551
552
|
logger: this.options.logger,
|
|
552
553
|
process: this.options.process ?? this.platform.process,
|
|
554
|
+
origin: options?.origin,
|
|
553
555
|
});
|
|
554
556
|
}
|
|
555
557
|
async runAction(id, input, options) {
|
|
558
|
+
const origin = snapshotRunOrigin(options?.origin);
|
|
559
|
+
const effectiveOptions = { ...options, origin };
|
|
556
560
|
const service = await this.prepareExecution();
|
|
557
561
|
let actionId = id;
|
|
558
562
|
if (actionId.includes("/")) {
|
|
@@ -563,10 +567,12 @@ export class DefaultPackageRuntime {
|
|
|
563
567
|
throw new ActionDockError(INVOCATION_UNSUPPORTED, `PackageRuntime only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
|
-
const context = this.buildRootInvocationContext(actionId,
|
|
570
|
+
const context = this.buildRootInvocationContext(actionId, effectiveOptions);
|
|
567
571
|
return service.execute(actionId, input, context);
|
|
568
572
|
}
|
|
569
573
|
async startAction(id, input, options) {
|
|
574
|
+
const origin = snapshotRunOrigin(options?.origin);
|
|
575
|
+
const effectiveOptions = { ...options, origin };
|
|
570
576
|
const service = await this.prepareExecution();
|
|
571
577
|
let actionId = id;
|
|
572
578
|
if (actionId.includes("/")) {
|
|
@@ -577,7 +583,7 @@ export class DefaultPackageRuntime {
|
|
|
577
583
|
throw new ActionDockError(INVOCATION_UNSUPPORTED, `PackageRuntime only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
|
|
578
584
|
}
|
|
579
585
|
}
|
|
580
|
-
const context = this.buildRootInvocationContext(actionId,
|
|
586
|
+
const context = this.buildRootInvocationContext(actionId, effectiveOptions);
|
|
581
587
|
return service.start(actionId, input, context);
|
|
582
588
|
}
|
|
583
589
|
async startInvocation(actionId, input, context) {
|
package/dist/project/init.js
CHANGED
|
@@ -86,10 +86,10 @@ export function initProject(targetDir, options = {}) {
|
|
|
86
86
|
node: ">=24.12.0",
|
|
87
87
|
},
|
|
88
88
|
dependencies: {
|
|
89
|
-
"@actiondock/sdk": "^2.
|
|
89
|
+
"@actiondock/sdk": "^2.18.0",
|
|
90
90
|
},
|
|
91
91
|
devDependencies: {
|
|
92
|
-
"@actiondock/testing": "^2.
|
|
92
|
+
"@actiondock/testing": "^2.18.0",
|
|
93
93
|
"@types/node": "^22.13.0",
|
|
94
94
|
"tsx": "^4.19.0",
|
|
95
95
|
"typescript": "^5.7.0",
|
package/dist/runtime/index.d.ts
CHANGED
package/dist/runtime/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunOrigin } from "@actiondock/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* 运行来源快照辅助函数(纯领域无副作用,单一事实源)。
|
|
4
|
+
*
|
|
5
|
+
* 仅安全提取 channel、viewName 与 authenticated 3个非秘密字段并冻结对象,
|
|
6
|
+
* 严禁携带额外凭证或扩展字段,防范对象后续被篡改。
|
|
7
|
+
* 若入参缺省则返回 undefined;非法来源严格校验报错,杜绝静默吞没。
|
|
8
|
+
*/
|
|
9
|
+
export declare function snapshotRunOrigin(origin?: unknown): RunOrigin | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ActionDockError, INVALID_ARGUMENT } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* 运行来源快照辅助函数(纯领域无副作用,单一事实源)。
|
|
4
|
+
*
|
|
5
|
+
* 仅安全提取 channel、viewName 与 authenticated 3个非秘密字段并冻结对象,
|
|
6
|
+
* 严禁携带额外凭证或扩展字段,防范对象后续被篡改。
|
|
7
|
+
* 若入参缺省则返回 undefined;非法来源严格校验报错,杜绝静默吞没。
|
|
8
|
+
*/
|
|
9
|
+
export function snapshotRunOrigin(origin) {
|
|
10
|
+
if (origin === undefined || origin === null) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (typeof origin !== "object" || Array.isArray(origin)) {
|
|
14
|
+
throw new ActionDockError(INVALID_ARGUMENT, "Invalid run origin: expected an object");
|
|
15
|
+
}
|
|
16
|
+
const raw = origin;
|
|
17
|
+
const channel = raw.channel;
|
|
18
|
+
if (channel !== "http" && channel !== "mcp") {
|
|
19
|
+
throw new ActionDockError(INVALID_ARGUMENT, "Invalid run origin channel: expected 'http' or 'mcp'");
|
|
20
|
+
}
|
|
21
|
+
const viewName = raw.viewName;
|
|
22
|
+
if (typeof viewName !== "string" || viewName.length === 0) {
|
|
23
|
+
throw new ActionDockError(INVALID_ARGUMENT, "Invalid run origin viewName: expected a non-empty string");
|
|
24
|
+
}
|
|
25
|
+
if (typeof raw.authenticated !== "boolean") {
|
|
26
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Invalid run origin authenticated: expected boolean, got '${typeof raw.authenticated}'`);
|
|
27
|
+
}
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
channel,
|
|
30
|
+
viewName,
|
|
31
|
+
authenticated: raw.authenticated,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeError, RunRecord } from "@actiondock/sdk";
|
|
1
|
+
import type { RuntimeError, RunOrigin, RunRecord } from "@actiondock/sdk";
|
|
2
2
|
import type { RuntimeStorage, TerminalRunStatus } from "../storage/types.js";
|
|
3
3
|
/**
|
|
4
4
|
* 运行记录落库参数(RunRecord 构造与写入的领域入参)。
|
|
@@ -13,6 +13,8 @@ export interface InitialRunRecordInput {
|
|
|
13
13
|
rootRunId: string;
|
|
14
14
|
/** 父级运行 ID(嵌套调用场景下建立调用链树) */
|
|
15
15
|
parentRunId?: string;
|
|
16
|
+
/** 运行来源追踪快照 */
|
|
17
|
+
origin?: RunOrigin;
|
|
16
18
|
/** 执行归属所有者标识 */
|
|
17
19
|
ownerId?: string;
|
|
18
20
|
/** 执行宿主会话标识 */
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ActionDockError, RUN_PERSISTENCE_FAILED, RUN_REPOSITORY_UNAVAILABLE } from "../errors.js";
|
|
2
|
+
import { snapshotRunOrigin } from "./run-origin.js";
|
|
2
3
|
/**
|
|
3
4
|
* 构建运行初始记录(RunRecord 组装单一事实源)。
|
|
4
5
|
*/
|
|
5
6
|
export function buildInitialRunRecord(params, status, error) {
|
|
6
7
|
const { runId, rootRunId, parentRunId, ownerId, hostSessionId, hostPid, packageInstanceId, generationId, targetPackageId, targetActionId, startedAt, input, runnerPackageId, runnerPackageInstanceId, runnerGenerationId, runnerHostSessionId, } = params;
|
|
8
|
+
const origin = snapshotRunOrigin(params.origin);
|
|
7
9
|
const record = {
|
|
8
10
|
id: runId,
|
|
11
|
+
...(origin !== undefined ? { origin } : {}),
|
|
9
12
|
rootRunId,
|
|
10
13
|
parentRunId,
|
|
11
14
|
packageId: targetPackageId,
|
package/dist/server/policy.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RunOrigin } from "@actiondock/sdk";
|
|
1
2
|
import { ActionDockError } from "../errors.js";
|
|
2
3
|
import type { McpEndpointHandler } from "./mcp-endpoint.js";
|
|
3
4
|
import type { EffectiveServerPolicy, ServerOptions, ServerViewOptions } from "./types.js";
|
|
@@ -136,3 +137,10 @@ export declare function assertActionAllowedByPolicy(action: {
|
|
|
136
137
|
* @param policy 当前生效的服务端策略
|
|
137
138
|
*/
|
|
138
139
|
export declare function assertPackageAllowedByPolicy(packageId: string, policy?: EffectiveServerPolicy): void;
|
|
140
|
+
/**
|
|
141
|
+
* 构造受信任的服务端执行来源快照。
|
|
142
|
+
*
|
|
143
|
+
* 仅挑选 channel、viewName(默认 "default")与 authenticated(视图配置非空有效令牌)。
|
|
144
|
+
* 严禁暴露令牌原文、片段或摘要。
|
|
145
|
+
*/
|
|
146
|
+
export declare function createServerRunOrigin(policy: Pick<EffectiveServerPolicy, "viewName" | "token">, channel: RunOrigin["channel"]): RunOrigin;
|
package/dist/server/policy.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
1
2
|
import { parseActionRef } from "../catalog/resolve-action.js";
|
|
2
3
|
import { ActionDockError, ACTION_FORBIDDEN, PACKAGE_NOT_ALLOWED } from "../errors.js";
|
|
3
4
|
import { safeEqual } from "./security.js";
|
|
@@ -366,3 +367,16 @@ export function assertPackageAllowedByPolicy(packageId, policy) {
|
|
|
366
367
|
throw new PackageNotAllowedError(packageId);
|
|
367
368
|
}
|
|
368
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* 构造受信任的服务端执行来源快照。
|
|
372
|
+
*
|
|
373
|
+
* 仅挑选 channel、viewName(默认 "default")与 authenticated(视图配置非空有效令牌)。
|
|
374
|
+
* 严禁暴露令牌原文、片段或摘要。
|
|
375
|
+
*/
|
|
376
|
+
export function createServerRunOrigin(policy, channel) {
|
|
377
|
+
return snapshotRunOrigin({
|
|
378
|
+
channel,
|
|
379
|
+
viewName: policy.viewName || "default",
|
|
380
|
+
authenticated: Boolean(policy.token?.trim()),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
@@ -2,6 +2,7 @@ import { parseActionRef } from "../../catalog/resolve-action.js";
|
|
|
2
2
|
import { filterByIntent } from "../../utils/intent.js";
|
|
3
3
|
import { ACTION_EXECUTION_ERROR, ACTION_FORBIDDEN, ACTION_NOT_FOUND, ACTION_START_FAILED, ACTION_TIMEOUT, ACTIONS_LIST_ERROR, IDEMPOTENCY_CONFLICT, INPUT_VALIDATION_FAILED, INVALID_JSON, PACKAGE_FORBIDDEN, PACKAGE_NOT_FOUND, REQUEST_TOO_LARGE, } from "../../errors.js";
|
|
4
4
|
import { InvalidJsonError, readJsonBody, RequestTooLargeError } from "../body.js";
|
|
5
|
+
import { createServerRunOrigin } from "../policy.js";
|
|
5
6
|
import { filterActionsByPolicy, getSubPath, isActionAllowedByPolicy, isPackageAllowedByPolicy, jsonResponse, } from "./common.js";
|
|
6
7
|
/**
|
|
7
8
|
* 处理 Action 相关的 HTTP 路由(列表、规范查询、同步执行与异步启动)。
|
|
@@ -257,10 +258,12 @@ export async function handleActionsRoutes(ctx) {
|
|
|
257
258
|
}
|
|
258
259
|
if (isAsync) {
|
|
259
260
|
try {
|
|
261
|
+
const origin = createServerRunOrigin(policy, "http");
|
|
260
262
|
const ticket = await service.execution.start(actionRefObj, input, {
|
|
261
263
|
timeoutMs,
|
|
262
264
|
config: configOverrides,
|
|
263
265
|
requestId,
|
|
266
|
+
origin,
|
|
264
267
|
});
|
|
265
268
|
if (ticket.status === "failed") {
|
|
266
269
|
const res = await ticket.result;
|
|
@@ -296,11 +299,13 @@ export async function handleActionsRoutes(ctx) {
|
|
|
296
299
|
}
|
|
297
300
|
// 同步执行模式
|
|
298
301
|
try {
|
|
302
|
+
const origin = createServerRunOrigin(policy, "http");
|
|
299
303
|
const result = await service.execution.run(actionRefObj, input, {
|
|
300
304
|
signal: req.signal,
|
|
301
305
|
timeoutMs,
|
|
302
306
|
config: configOverrides,
|
|
303
307
|
requestId,
|
|
308
|
+
origin,
|
|
304
309
|
});
|
|
305
310
|
let status = 200;
|
|
306
311
|
if (!result.ok) {
|
package/dist/service/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionRef, ExecutionEvent, ExecutionResult, JsonValue, RunRecord } from "@actiondock/sdk";
|
|
1
|
+
import type { ActionRef, ExecutionEvent, ExecutionResult, JsonValue, RunOrigin, RunRecord } from "@actiondock/sdk";
|
|
2
2
|
import type { ActionSpec, ActionSummary, ListActionsOptions, PackageInfo, PackageRuntime, PackageRuntimeOptions, PlaybookSpec, PlaybookSummary } from "../package/types.js";
|
|
3
3
|
import type { CancelResult, ExecutionTicket } from "../execution/types.js";
|
|
4
4
|
import type { ActionDockHostOptions } from "../host/types.js";
|
|
@@ -18,6 +18,8 @@ export interface RunOptions {
|
|
|
18
18
|
config?: Record<string, JsonValue>;
|
|
19
19
|
/** 幂等请求标识 */
|
|
20
20
|
requestId?: string;
|
|
21
|
+
/** 运行来源追踪快照 */
|
|
22
|
+
origin?: RunOrigin;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
23
25
|
* 统一协议版本常量。
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isTerminalRunStatus, DEFAULT_MAX_RUNS_PER_PACKAGE, DEFAULT_MIN_RETAIN_RUNS, DEFAULT_RUNS_RETENTION_MS } from "./types.js";
|
|
2
2
|
import { safeParseStoredJson } from "./utils.js";
|
|
3
|
+
import { snapshotRunOrigin } from "../runtime/run-origin.js";
|
|
3
4
|
import { isRunHostDead } from "./run-liveness.js";
|
|
4
5
|
import { ActionDockError, STORAGE_CLOSED, STORED_ERROR_DECODE_FAILED } from "../errors.js";
|
|
5
6
|
export class SqliteRunsStore {
|
|
@@ -146,15 +147,17 @@ export class SqliteRunsStore {
|
|
|
146
147
|
createRun(record) {
|
|
147
148
|
const stmt = this.getStatement(`
|
|
148
149
|
INSERT INTO runs (
|
|
149
|
-
id, root_run_id, parent_run_id, package_id, package_instance_id,
|
|
150
|
+
id, root_run_id, parent_run_id, origin_json, package_id, package_instance_id,
|
|
150
151
|
action_id, generation_id, owner_id, host_session_id, host_pid, heartbeat_at,
|
|
151
152
|
status, input_json, output_json,
|
|
152
153
|
error_json, started_at, finished_at, duration_ms
|
|
153
154
|
)
|
|
154
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
155
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
155
156
|
`);
|
|
156
157
|
const rootRunId = record.rootRunId || record.id;
|
|
157
158
|
const parentRunId = record.parentRunId || null;
|
|
159
|
+
const origin = snapshotRunOrigin(record.origin);
|
|
160
|
+
const originJson = origin ? JSON.stringify(origin) : null;
|
|
158
161
|
const packageInstanceId = record.packageInstanceId || record.packageId || this.packageId;
|
|
159
162
|
const generationId = record.generationId || "1";
|
|
160
163
|
const ownerId = record.ownerId || "local";
|
|
@@ -168,7 +171,7 @@ export class SqliteRunsStore {
|
|
|
168
171
|
const errorJson = record.error ? JSON.stringify(record.error) : null;
|
|
169
172
|
const finishedAt = record.finishedAt || null;
|
|
170
173
|
const durationMs = typeof record.durationMs === "number" ? record.durationMs : null;
|
|
171
|
-
stmt.run(record.id, rootRunId, parentRunId, record.packageId || this.packageId, packageInstanceId, record.actionId ?? "", generationId, ownerId, hostSessionId, hostPid, heartbeatAt, record.status, inputJson, outputJson, errorJson, startedIso, finishedAt, durationMs);
|
|
174
|
+
stmt.run(record.id, rootRunId, parentRunId, originJson, record.packageId || this.packageId, packageInstanceId, record.actionId ?? "", generationId, ownerId, hostSessionId, hostPid, heartbeatAt, record.status, inputJson, outputJson, errorJson, startedIso, finishedAt, durationMs);
|
|
172
175
|
this.runInsertCount++;
|
|
173
176
|
const nowMs = this.clock.now().getTime();
|
|
174
177
|
if (this.runInsertCount >= SqliteRunsStore.RUNS_CLEANUP_BATCH_THRESHOLD &&
|
|
@@ -595,8 +598,11 @@ export class SqliteRunsStore {
|
|
|
595
598
|
? error
|
|
596
599
|
: { code: STORED_ERROR_DECODE_FAILED, message: String(error) }
|
|
597
600
|
: undefined;
|
|
601
|
+
const rawOrigin = safeParseStoredJson(row.origin_json, `runs.origin db=${this.dbPath} package=${this.packageId} run=${row.id}`, { strict: true });
|
|
602
|
+
const origin = snapshotRunOrigin(rawOrigin);
|
|
598
603
|
return {
|
|
599
604
|
id: row.id,
|
|
605
|
+
...(origin !== undefined ? { origin } : {}),
|
|
600
606
|
rootRunId: row.root_run_id || row.id,
|
|
601
607
|
parentRunId: row.parent_run_id || undefined,
|
|
602
608
|
packageId: row.package_id,
|
|
@@ -49,6 +49,7 @@ export function initSchema(driver, dbPath, recoverOrphans, recoverDeadSessionFn,
|
|
|
49
49
|
id TEXT PRIMARY KEY,
|
|
50
50
|
root_run_id TEXT NOT NULL,
|
|
51
51
|
parent_run_id TEXT,
|
|
52
|
+
origin_json TEXT,
|
|
52
53
|
package_id TEXT NOT NULL,
|
|
53
54
|
package_instance_id TEXT NOT NULL,
|
|
54
55
|
action_id TEXT NOT NULL,
|
|
@@ -103,8 +104,11 @@ export function initSchema(driver, dbPath, recoverOrphans, recoverDeadSessionFn,
|
|
|
103
104
|
}
|
|
104
105
|
// 确保存量旧版本数据库补充新增的列或表
|
|
105
106
|
if (version !== 0) {
|
|
107
|
+
const columns = driver.prepare("PRAGMA table_info(runs);").all();
|
|
108
|
+
if (Array.isArray(columns) && !columns.some((c) => c.name === "origin_json")) {
|
|
109
|
+
driver.exec("ALTER TABLE runs ADD COLUMN origin_json TEXT;");
|
|
110
|
+
}
|
|
106
111
|
try {
|
|
107
|
-
const columns = driver.prepare("PRAGMA table_info(runs);").all();
|
|
108
112
|
if (Array.isArray(columns) && !columns.some((c) => c.name === "host_session_id")) {
|
|
109
113
|
driver.exec("ALTER TABLE runs ADD COLUMN host_session_id TEXT;");
|
|
110
114
|
}
|
package/dist/storage/utils.d.ts
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @param raw 存储层原始 JSON 字符串
|
|
19
19
|
* @param context 存储路径上下文描述(包含数据库位置、包标识与键名)
|
|
20
|
-
* @
|
|
20
|
+
* @param options 严格模式下解析失败直接报错,且不输出原始载荷片段
|
|
21
|
+
* @returns 解析成功返回解析后的值;入参为空返回 undefined;默认模式解析失败返回原始字符串
|
|
21
22
|
*/
|
|
22
|
-
export declare function safeParseStoredJson<T = unknown>(raw: string | null | undefined, context: string
|
|
23
|
+
export declare function safeParseStoredJson<T = unknown>(raw: string | null | undefined, context: string, options?: {
|
|
24
|
+
strict?: boolean;
|
|
25
|
+
}): T | undefined;
|
package/dist/storage/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActionDockError, RUN_REPOSITORY_UNAVAILABLE } from "../errors.js";
|
|
1
2
|
/**
|
|
2
3
|
* 存储层 JSON 载荷统一安全解析工具。
|
|
3
4
|
*
|
|
@@ -17,9 +18,10 @@
|
|
|
17
18
|
*
|
|
18
19
|
* @param raw 存储层原始 JSON 字符串
|
|
19
20
|
* @param context 存储路径上下文描述(包含数据库位置、包标识与键名)
|
|
20
|
-
* @
|
|
21
|
+
* @param options 严格模式下解析失败直接报错,且不输出原始载荷片段
|
|
22
|
+
* @returns 解析成功返回解析后的值;入参为空返回 undefined;默认模式解析失败返回原始字符串
|
|
21
23
|
*/
|
|
22
|
-
export function safeParseStoredJson(raw, context) {
|
|
24
|
+
export function safeParseStoredJson(raw, context, options) {
|
|
23
25
|
if (raw === null || raw === undefined) {
|
|
24
26
|
return undefined;
|
|
25
27
|
}
|
|
@@ -27,6 +29,9 @@ export function safeParseStoredJson(raw, context) {
|
|
|
27
29
|
return JSON.parse(raw);
|
|
28
30
|
}
|
|
29
31
|
catch (err) {
|
|
32
|
+
if (options?.strict) {
|
|
33
|
+
throw new ActionDockError(RUN_REPOSITORY_UNAVAILABLE, `Corrupted stored JSON at ${context}`);
|
|
34
|
+
}
|
|
30
35
|
const reason = err instanceof Error ? err.message : String(err);
|
|
31
36
|
console.warn(`[actiondock] stored JSON corrupted at ${context}: ${reason}`);
|
|
32
37
|
return raw;
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actiondock/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"test": "node ../../scripts/run-tests.ts"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@actiondock/sdk": "^2.
|
|
62
|
+
"@actiondock/sdk": "^2.18.0",
|
|
63
63
|
"ajv": "^8.17.1",
|
|
64
64
|
"ajv-formats": "^3.0.1",
|
|
65
65
|
"undici": "^7.29.1"
|