@actiondock/core 2.15.0 → 2.17.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/catalog/discovery.d.ts +3 -3
- package/dist/catalog/graph.d.ts +8 -6
- package/dist/catalog/graph.js +62 -197
- package/dist/catalog/resolve-playbook.d.ts +2 -2
- package/dist/catalog/resolve-playbook.js +53 -12
- package/dist/execution/service.js +6 -0
- package/dist/execution/types.d.ts +2 -2
- package/dist/host/host.js +16 -5
- package/dist/host/routing.d.ts +1 -1
- package/dist/host/routing.js +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +0 -1
- package/dist/invocation/types.d.ts +5 -1
- package/dist/invocation/types.js +5 -0
- package/dist/package/runtime.d.ts +2 -2
- package/dist/package/runtime.js +9 -5
- package/dist/package/static-index.d.ts +6 -4
- package/dist/package/static-index.js +60 -12
- package/dist/package/types.d.ts +5 -5
- package/dist/project/index.d.ts +0 -1
- package/dist/project/index.js +0 -1
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +2 -4
- package/dist/project/loader.js +34 -38
- package/dist/project/manifest.d.ts +0 -2
- package/dist/project/manifest.js +1 -17
- package/dist/project/types.d.ts +0 -31
- package/dist/registry/registry.js +15 -27
- package/dist/runtime/context.d.ts +3 -3
- 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/runtime/standalone-commands.js +2 -8
- package/dist/runtime/standalone.d.ts +0 -2
- 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/utils/semver.d.ts +1 -2
- package/dist/utils/semver.js +5 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/dist/project/skill-dir.d.ts +0 -72
- package/dist/project/skill-dir.js +0 -244
|
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync, readFileSync, renameSync, statSync } from "node:
|
|
|
2
2
|
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
|
|
5
|
-
import { inspectLocalPackageDirectory, isExplicitPath } from "../project/skill-dir.js";
|
|
6
5
|
import { getActionDockHome, getPackageSlug } from "../utils/index.js";
|
|
7
6
|
import { ActionDockError, MANIFEST_NOT_FOUND, REGISTRY_CORRUPTED } from "../errors.js";
|
|
8
7
|
import { withRegistryLock } from "./lock.js";
|
|
@@ -400,42 +399,31 @@ export function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
|
|
|
400
399
|
const resolvedPath = resolve(baseDir, packageIdOrPath);
|
|
401
400
|
// 检查 packageIdOrPath 是否为磁盘上现存的目录或文件路径
|
|
402
401
|
if (existsSync(resolvedPath)) {
|
|
403
|
-
let info;
|
|
404
402
|
try {
|
|
405
|
-
info = statSync(resolvedPath);
|
|
406
|
-
}
|
|
407
|
-
catch (err) {
|
|
408
|
-
if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
|
|
409
|
-
throw err;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (info) {
|
|
403
|
+
const info = statSync(resolvedPath);
|
|
413
404
|
const targetDir = info.isDirectory() ? resolvedPath : dirname(resolvedPath);
|
|
414
|
-
// 普通开发包根目录:直接包含 actiondock.json 清单。
|
|
415
|
-
// 普通清单根目录定位与封闭 Skill 执行校验分离,不能被当成只含一个包的 Skill 闭包拒绝。
|
|
416
405
|
if (existsSync(join(targetDir, "actiondock.json"))) {
|
|
417
|
-
loadProjectConfig(targetDir);
|
|
418
406
|
return targetDir;
|
|
419
407
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return localSkill.root;
|
|
424
|
-
}
|
|
425
|
-
// 具体文件查找所属工程根目录:仅当 packageIdOrPath 指向具体文件(非目录)时,
|
|
426
|
-
// 才尝试向上查找所属工程根目录;目录形态绝不向上回退,防止显式目录越界命中父级工程。
|
|
427
|
-
if (!info.isDirectory()) {
|
|
428
|
-
const parentRoot = findProjectRoot(targetDir);
|
|
429
|
-
if (parentRoot) {
|
|
430
|
-
loadProjectConfig(parentRoot);
|
|
431
|
-
return parentRoot;
|
|
432
|
-
}
|
|
408
|
+
const parentRoot = findProjectRoot(targetDir);
|
|
409
|
+
if (parentRoot) {
|
|
410
|
+
return parentRoot;
|
|
433
411
|
}
|
|
434
412
|
}
|
|
413
|
+
catch {
|
|
414
|
+
// stat 失败(如并发删除):忽略并继续走标识符解析路径
|
|
415
|
+
}
|
|
435
416
|
}
|
|
436
417
|
// 显式路径形态但未解析成功的场景:
|
|
437
418
|
// scoped package 标识符(如 @team/tools)虽含 '/' 但属于包 ID 而非文件路径
|
|
438
|
-
|
|
419
|
+
const isScopedPackage = packageIdOrPath.startsWith("@");
|
|
420
|
+
const isExplicitPath = !isScopedPackage &&
|
|
421
|
+
(packageIdOrPath.startsWith(".") ||
|
|
422
|
+
packageIdOrPath.startsWith("/") ||
|
|
423
|
+
packageIdOrPath.startsWith("~") ||
|
|
424
|
+
packageIdOrPath.includes("/") ||
|
|
425
|
+
packageIdOrPath.includes("\\"));
|
|
426
|
+
if (isExplicitPath) {
|
|
439
427
|
// 显式路径不存在或不是 ActionDock 项目时必须失败,禁止静默回退
|
|
440
428
|
return null;
|
|
441
429
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActionContext, ActionRef, Config, Logger, ProcessAPI, ProgressReporter, StateStore } from "@actiondock/sdk";
|
|
2
|
-
import type { ProjectConfig
|
|
2
|
+
import type { ProjectConfig } from "../project/types.js";
|
|
3
3
|
import type { RuntimeStorage } from "../storage/types.js";
|
|
4
4
|
import { ProcessManager, type ProcessOwner } from "../process/index.js";
|
|
5
5
|
/**
|
|
@@ -17,7 +17,7 @@ export declare class RuntimeConfig implements Config {
|
|
|
17
17
|
private storage;
|
|
18
18
|
private projectConfig?;
|
|
19
19
|
private globalStorage?;
|
|
20
|
-
constructor(storage: RuntimeStorage, overrides?: Record<string, unknown>, projectConfig?: ProjectConfig
|
|
20
|
+
constructor(storage: RuntimeStorage, overrides?: Record<string, unknown>, projectConfig?: ProjectConfig, globalStorage?: RuntimeStorage);
|
|
21
21
|
get<T = unknown>(key: string): T | undefined;
|
|
22
22
|
get<T = unknown>(key: string, defaultValue: T): T;
|
|
23
23
|
has(key: string): boolean;
|
|
@@ -70,7 +70,7 @@ export interface ContextOptions {
|
|
|
70
70
|
storage: RuntimeStorage;
|
|
71
71
|
globalStorage?: RuntimeStorage;
|
|
72
72
|
overrides?: Record<string, unknown>;
|
|
73
|
-
projectConfig?: ProjectConfig
|
|
73
|
+
projectConfig?: ProjectConfig;
|
|
74
74
|
parentRunId?: string;
|
|
75
75
|
runId?: string;
|
|
76
76
|
rootRunId?: string;
|
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,
|
|
@@ -29,7 +29,7 @@ export async function handleList(ctx, subArgs) {
|
|
|
29
29
|
const effectiveIntent = intent || (positionalPatterns.length > 0 ? positionalPatterns.join("|") : undefined);
|
|
30
30
|
const actions = await ctx.service.discovery.listActions();
|
|
31
31
|
const list = actions.map((a) => ({
|
|
32
|
-
id:
|
|
32
|
+
id: a.id,
|
|
33
33
|
description: a.description || "",
|
|
34
34
|
}));
|
|
35
35
|
const filterRes = filterWithFallbackInfo(list, effectiveIntent, [(a) => a.id, (a) => a.description], !noFallback);
|
|
@@ -66,7 +66,7 @@ export async function handleDescribe(ctx, subArgs) {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
const payload = buildActionDescribePayload(action, {
|
|
69
|
-
packageId:
|
|
69
|
+
packageId: ctx.options.packageId,
|
|
70
70
|
});
|
|
71
71
|
if (isJson) {
|
|
72
72
|
ctx.writeOut(JSON.stringify(payload, null, 2));
|
|
@@ -274,9 +274,6 @@ function isServiceCapabilityError(err) {
|
|
|
274
274
|
code === "TARGET_CAPABILITY_UNAVAILABLE");
|
|
275
275
|
}
|
|
276
276
|
export async function handleConfig(ctx, subArgs) {
|
|
277
|
-
if (ctx.options.multiPackage) {
|
|
278
|
-
return ctx.emitError(subArgs.includes("--json"), "CAPABILITY_UNAVAILABLE", "Multi-package entries do not support config/state management; configure actions using --config and --data-dir.", ExitCode.INVALID_ARGUMENT);
|
|
279
|
-
}
|
|
280
277
|
const sub = subArgs[0] || "list";
|
|
281
278
|
if (!ctx.service.management) {
|
|
282
279
|
ctx.writeErr("Error: management port is not enabled on this service");
|
|
@@ -353,9 +350,6 @@ function decodeStateKeyArg(key) {
|
|
|
353
350
|
}
|
|
354
351
|
}
|
|
355
352
|
export async function handleState(ctx, subArgs) {
|
|
356
|
-
if (ctx.options.multiPackage) {
|
|
357
|
-
return ctx.emitError(subArgs.includes("--json"), "CAPABILITY_UNAVAILABLE", "Multi-package entries do not support config/state management; configure actions using --config and --data-dir.", ExitCode.INVALID_ARGUMENT);
|
|
358
|
-
}
|
|
359
353
|
const sub = subArgs[0] || "list";
|
|
360
354
|
let namespace;
|
|
361
355
|
let isAll = false;
|
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/utils/semver.d.ts
CHANGED
|
@@ -6,11 +6,10 @@ export interface SemVer {
|
|
|
6
6
|
minor: number;
|
|
7
7
|
patch: number;
|
|
8
8
|
prerelease?: string;
|
|
9
|
-
build?: string;
|
|
10
9
|
}
|
|
11
10
|
/**
|
|
12
11
|
* 解析基础 SemVer 版本字符串(单一事实源)。
|
|
13
|
-
* 仅接受可选 v/=
|
|
12
|
+
* 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
|
|
14
13
|
* 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
|
|
15
14
|
*
|
|
16
15
|
* 本模块不依赖任何 Node 内建与 core 领域模块,可在无 loader 的原生
|
package/dist/utils/semver.js
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 解析基础 SemVer 版本字符串(单一事实源)。
|
|
3
|
-
* 仅接受可选 v/=
|
|
3
|
+
* 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
|
|
4
4
|
* 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
|
|
5
5
|
*
|
|
6
6
|
* 本模块不依赖任何 Node 内建与 core 领域模块,可在无 loader 的原生
|
|
7
7
|
* Node 直接以 .ts 形态导入(工程脚本域复用此处单一事实源)。
|
|
8
8
|
*/
|
|
9
9
|
export function parseSemVer(v) {
|
|
10
|
-
const match = v.trim().replace(/^[v=]/, "").match(/^(
|
|
10
|
+
const match = v.trim().replace(/^[v=]/, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
11
11
|
if (!match)
|
|
12
12
|
return null;
|
|
13
|
-
const [major, minor, patch] = match.slice(1, 4).map(Number);
|
|
14
|
-
if (![major, minor, patch].every(Number.isSafeInteger))
|
|
15
|
-
return null;
|
|
16
|
-
if (match[4]?.split(".").some((id) => /^\d+$/.test(id) && id.length > 1 && id.startsWith("0")))
|
|
17
|
-
return null;
|
|
18
13
|
return {
|
|
19
|
-
major,
|
|
20
|
-
minor,
|
|
21
|
-
patch,
|
|
14
|
+
major: parseInt(match[1], 10),
|
|
15
|
+
minor: parseInt(match[2], 10),
|
|
16
|
+
patch: parseInt(match[3], 10),
|
|
22
17
|
prerelease: match[4],
|
|
23
|
-
...(match[5] === undefined ? {} : { build: match[5] }),
|
|
24
18
|
};
|
|
25
19
|
}
|
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.17.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.17.0",
|
|
63
63
|
"ajv": "^8.17.1",
|
|
64
64
|
"ajv-formats": "^3.0.1",
|
|
65
65
|
"undici": "^7.29.1"
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { ProjectConfig } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* 本地包条目;是否构成封闭集合由校验入口保证。
|
|
4
|
-
*/
|
|
5
|
-
export interface LocalPackageEntry {
|
|
6
|
-
packageId: string;
|
|
7
|
-
packageRoot: string;
|
|
8
|
-
projectConfig: ProjectConfig;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* 本地技能 / 包目录发现视图,不代表已经通过封闭集合校验。
|
|
12
|
-
*/
|
|
13
|
-
export interface LocalSkillDirectory {
|
|
14
|
-
type: "single" | "composite";
|
|
15
|
-
root: string;
|
|
16
|
-
packages: LocalPackageEntry[];
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* 判定路径字符串是否为显式文件/目录路径形态(而非包标识符)。
|
|
20
|
-
*
|
|
21
|
-
* 规则:
|
|
22
|
-
* - 以 .、/、~ 开头;
|
|
23
|
-
* - 包含 / 或 \\,且不是 scoped 包(@scope/name 仅含一个 /);
|
|
24
|
-
* - scoped 包若包含反斜杠或多于一个 /,亦视作路径。
|
|
25
|
-
*/
|
|
26
|
-
export declare function isExplicitPath(p: string): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* 对明确的本地封闭包集合实施一致性校验。
|
|
29
|
-
*
|
|
30
|
-
* 校验规则:
|
|
31
|
-
* - 重复包标识防护:集合内不得存在相同 packageId;
|
|
32
|
-
* - 依赖存在性与版本兼容性:声明的直接依赖必须在集合内存在且满足语义版本约束(复用 PackageGraphBuilder);
|
|
33
|
-
* - 跨包 uses 目标完整性:uses 声明引用的目标包与目标 Action 必须在集合内存在。
|
|
34
|
-
*
|
|
35
|
-
* @param packages 封闭包条目集合
|
|
36
|
-
*/
|
|
37
|
-
export declare function validateLocalSkillPackages(packages: Array<{
|
|
38
|
-
packageId: string;
|
|
39
|
-
packageRoot: string;
|
|
40
|
-
projectConfig: ProjectConfig;
|
|
41
|
-
}>): void;
|
|
42
|
-
/**
|
|
43
|
-
* 发现本地 Skill / 包目录结构(支持单包根目录与 packages/* 复合套件布局)。
|
|
44
|
-
* 仅执行物理布局发现与清单加载,不执行跨包封闭依赖集合校验。
|
|
45
|
-
*
|
|
46
|
-
* @param directoryPath 待检查的目录路径(相对路径将基于 cwd 解析)
|
|
47
|
-
* @param cwd 解析基准工作目录(默认为 process.cwd())
|
|
48
|
-
*/
|
|
49
|
-
export declare function discoverLocalSkillDirectory(directoryPath: string, cwd?: string): LocalSkillDirectory | null;
|
|
50
|
-
/**
|
|
51
|
-
* 查询指定路径的本地包或技能目录(供 info / 查询命令使用)。
|
|
52
|
-
* 单包不要求 node_modules 依赖闭包;复合目录仍要求封闭集合完整。
|
|
53
|
-
*
|
|
54
|
-
* @param directoryPath 待检查的目录路径(相对路径将基于 cwd 解析)
|
|
55
|
-
* @param cwd 解析基准工作目录(默认为 process.cwd())
|
|
56
|
-
*/
|
|
57
|
-
export declare function queryLocalPackageDirectory(directoryPath: string, cwd?: string): LocalSkillDirectory | null;
|
|
58
|
-
/**
|
|
59
|
-
* 探测指定路径是否为本地 Skill / 包目录(支持单包根目录与 packages/* 复合套件布局)。
|
|
60
|
-
*
|
|
61
|
-
* 封闭包装配与校验规则:
|
|
62
|
-
* - 符号链接按目录布局正常解析;Action 与规程入口的文件边界由相应加载器校验;
|
|
63
|
-
* - 单包布局:目录根部存在 actiondock.json,解析为包含单一包的 LocalSkillDirectory;
|
|
64
|
-
* - 复合布局:目录根部存在 packages/ 目录,遍历其直接子目录,收集包含 actiondock.json 的子包;
|
|
65
|
-
* - 集合一致性校验:通过 validateLocalSkillPackages 严格核查依赖闭包、版本兼容与 uses 目标;
|
|
66
|
-
* - 异常透传:坏清单或权限异常原样抛出,严禁静默回退 Registry;
|
|
67
|
-
* - 若非候选技能目录,返回 null。
|
|
68
|
-
*
|
|
69
|
-
* @param directoryPath 待检查的目录路径(相对路径将基于 cwd 解析)
|
|
70
|
-
* @param cwd 解析基准工作目录(默认为 process.cwd())
|
|
71
|
-
*/
|
|
72
|
-
export declare function inspectLocalPackageDirectory(directoryPath: string, cwd?: string): LocalSkillDirectory | null;
|