@actiondock/builder 2.10.0 → 2.11.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/archive.js +2 -1
- package/dist/build.d.ts +25 -2
- package/dist/build.js +99 -309
- package/dist/dependency-check.js +3 -3
- package/dist/dependency-resolver.d.ts +17 -0
- package/dist/dependency-resolver.js +69 -0
- package/dist/entry-generators.d.ts +15 -0
- package/dist/entry-generators.js +211 -0
- package/dist/export-composite.js +19 -1
- package/dist/export-source.d.ts +6 -0
- package/dist/export-source.js +24 -4
- package/dist/exporter.d.ts +1 -10
- package/dist/exporter.js +1 -16
- package/dist/fs-utils.js +2 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/lockfile-resolver.d.ts +9 -0
- package/dist/lockfile-resolver.js +56 -0
- package/dist/npm-pack.d.ts +6 -0
- package/dist/npm-pack.js +73 -0
- package/dist/pack.d.ts +2 -2
- package/dist/pack.js +8 -212
- package/dist/planner.js +40 -162
- package/dist/skill/custom-parser.d.ts +28 -0
- package/dist/skill/custom-parser.js +61 -0
- package/dist/skill/index.d.ts +1 -0
- package/dist/skill/index.js +1 -0
- package/dist/skill/templates.d.ts +28 -29
- package/dist/skill/templates.js +74 -144
- package/dist/skill-md.d.ts +3 -14
- package/dist/skill-md.js +5 -11
- package/dist/types.d.ts +19 -20
- package/dist/vendor.d.ts +5 -0
- package/dist/vendor.js +76 -0
- package/package.json +3 -3
- package/dist/export-batch.d.ts +0 -13
- package/dist/export-batch.js +0 -37
- package/dist/export-node.d.ts +0 -11
- package/dist/export-node.js +0 -48
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { STANDALONE_ASYNC_UNSUPPORTED } from "@actiondock/core";
|
|
2
|
+
import { serializePlanManifest } from "./manifest.js";
|
|
3
|
+
/**
|
|
4
|
+
* 生成 Host 子进程入口脚本源码(负责运行 ActionDockHost,通过 Node IPC 暴露 Target)。
|
|
5
|
+
*
|
|
6
|
+
* 按需装配契约:
|
|
7
|
+
* - 不再静态导入全部包内 Action(消除发现路径的业务模块顶层副作用);
|
|
8
|
+
* - 仅携带清单元数据,执行时由现有执行服务通过清单 entry 与 NodeModuleLoader
|
|
9
|
+
* 按需加载业务实现;跨包外部 Action 不混入本包源码,依赖调用继续经 Host 路由;
|
|
10
|
+
* - 入口路径以产物包根目录为基准(import.meta.dirname),不依赖调用者当前工作目录。
|
|
11
|
+
*/
|
|
12
|
+
export function generateNodeHostEntrySource(plan) {
|
|
13
|
+
// 复用 SelectionPlan 的清单序列化单一事实源(含包内动作筛选与 Schema 字段映射),
|
|
14
|
+
// 不单独复制一份 Schema 字段映射
|
|
15
|
+
const manifestActions = serializePlanManifest(plan).actions;
|
|
16
|
+
return `#!/usr/bin/env node
|
|
17
|
+
// AUTO-GENERATED HOST ENTRYPOINT BY ACTIONDOCK BUILDER. DO NOT EDIT.
|
|
18
|
+
import "@actiondock/core/warning";
|
|
19
|
+
import {
|
|
20
|
+
createActionDock,
|
|
21
|
+
createNodePlatform,
|
|
22
|
+
} from "@actiondock/core";
|
|
23
|
+
import { serveParentIpc } from "@actiondock/core/server";
|
|
24
|
+
|
|
25
|
+
// 产物包根目录:以入口文件自身位置为基准,不依赖调用者当前工作目录
|
|
26
|
+
const packageRoot = import.meta.dirname;
|
|
27
|
+
|
|
28
|
+
let dataDir;
|
|
29
|
+
const configOverrides = {};
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
for (let i = 0; i < args.length; i++) {
|
|
32
|
+
if (args[i] === "--data-dir" && i + 1 < args.length) {
|
|
33
|
+
dataDir = args[++i];
|
|
34
|
+
} else if (args[i].startsWith("--data-dir=")) {
|
|
35
|
+
dataDir = args[i].slice(11);
|
|
36
|
+
} else if (args[i] === "--config" && i + 1 < args.length) {
|
|
37
|
+
const raw = args[++i];
|
|
38
|
+
const [k, ...v] = raw.split("=");
|
|
39
|
+
if (k) configOverrides[k] = v.join("=");
|
|
40
|
+
} else if (args[i].startsWith("--config=")) {
|
|
41
|
+
const raw = args[i].slice(9);
|
|
42
|
+
const [k, ...v] = raw.split("=");
|
|
43
|
+
if (k) configOverrides[k] = v.join("=");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 复用既有命令分发结果确定访问性质:发现命令(list/describe/show)为旁观初始化,
|
|
48
|
+
// 不创建数据库、不取目录锁、不收割遗留运行记录;执行与写入命令为持有者初始化
|
|
49
|
+
const command = args.find((a) => !a.startsWith("-")) || "";
|
|
50
|
+
const DISCOVERY_COMMANDS = new Set(["list", "describe", "show"]);
|
|
51
|
+
const recoverOrphans = DISCOVERY_COMMANDS.has(command) ? false : undefined;
|
|
52
|
+
|
|
53
|
+
const service = await createActionDock({
|
|
54
|
+
dataDir,
|
|
55
|
+
...(recoverOrphans === false ? { recoverOrphans: false } : {}),
|
|
56
|
+
packages: [
|
|
57
|
+
{
|
|
58
|
+
packageRoot,
|
|
59
|
+
dataDir,
|
|
60
|
+
configOverrides,
|
|
61
|
+
projectConfig: {
|
|
62
|
+
id: ${JSON.stringify(plan.packageId)},
|
|
63
|
+
name: ${JSON.stringify(plan.packageName)},
|
|
64
|
+
version: ${JSON.stringify(plan.version)},
|
|
65
|
+
description: ${JSON.stringify(plan.description || "")},
|
|
66
|
+
config: ${JSON.stringify(plan.configDefs || {})},
|
|
67
|
+
actions: ${JSON.stringify(manifestActions)},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
platform: createNodePlatform({ dataDir }),
|
|
72
|
+
autoLoadCurrentProject: false,
|
|
73
|
+
scanLinkedPackages: false,
|
|
74
|
+
});
|
|
75
|
+
await serveParentIpc(service);
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 生成轻量监督父进程脚本源码(负责参数解析、诊断日志限流、退出码管理与标准输出隔离)。
|
|
80
|
+
*/
|
|
81
|
+
export function generateNodeSupervisorEntrySource(plan) {
|
|
82
|
+
return `#!/usr/bin/env node
|
|
83
|
+
// AUTO-GENERATED SUPERVISOR ENTRYPOINT BY ACTIONDOCK BUILDER. DO NOT EDIT.
|
|
84
|
+
import "@actiondock/core/warning";
|
|
85
|
+
import { spawn } from "node:child_process";
|
|
86
|
+
import { join } from "node:path";
|
|
87
|
+
import {
|
|
88
|
+
STANDALONE_ASYNC_UNSUPPORTED,
|
|
89
|
+
} from "@actiondock/core";
|
|
90
|
+
|
|
91
|
+
import {
|
|
92
|
+
ExitCode,
|
|
93
|
+
IpcActionDockService,
|
|
94
|
+
StandaloneDispatcher,
|
|
95
|
+
} from "@actiondock/core/server";
|
|
96
|
+
|
|
97
|
+
const METADATA = {
|
|
98
|
+
packageId: ${JSON.stringify(plan.packageId)},
|
|
99
|
+
version: ${JSON.stringify(plan.version)},
|
|
100
|
+
description: ${JSON.stringify(plan.description || "")},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const argv = process.argv.slice(2);
|
|
104
|
+
|
|
105
|
+
// 1. 监督进程接管参数校验,明确拒绝 --async
|
|
106
|
+
if (argv.includes("--async")) {
|
|
107
|
+
const isJson = argv.includes("--json");
|
|
108
|
+
if (isJson) {
|
|
109
|
+
console.log(
|
|
110
|
+
JSON.stringify(
|
|
111
|
+
{
|
|
112
|
+
ok: false,
|
|
113
|
+
error: {
|
|
114
|
+
code: ${JSON.stringify(STANDALONE_ASYNC_UNSUPPORTED)},
|
|
115
|
+
message:
|
|
116
|
+
"Async execution is not supported in standalone single-execution binaries. Use 'ad serve' or remote target.",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
null,
|
|
120
|
+
2
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} else {
|
|
124
|
+
console.error(
|
|
125
|
+
"Error [" + STANDALONE_ASYNC_UNSUPPORTED + "]: Async execution is not supported in standalone single-execution binaries."
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
process.exit(ExitCode.FAILURE);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 2. 静态元数据快速返回
|
|
132
|
+
if (argv.includes("-v") || argv.includes("-V") || argv.includes("--version") || argv[0] === "version") {
|
|
133
|
+
console.log(\`\${METADATA.packageId} v\${METADATA.version}\`);
|
|
134
|
+
process.exit(ExitCode.SUCCESS);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (argv.includes("-h") || argv.includes("--help") || argv[0] === "help") {
|
|
138
|
+
console.log(\`\${METADATA.packageId} (v\${METADATA.version})\`);
|
|
139
|
+
if (METADATA.description) console.log(METADATA.description + "\\n");
|
|
140
|
+
console.log("Usage:");
|
|
141
|
+
console.log(" <cmd> list [--json] List available actions");
|
|
142
|
+
console.log(" <cmd> describe <id> [--json] Show action details and schemas");
|
|
143
|
+
console.log(" <cmd> run <id> [--input '<json>'] Execute action with JSON input");
|
|
144
|
+
console.log(" <cmd> config list/get/set/delete Manage package configuration");
|
|
145
|
+
console.log(" <cmd> state list/get/set/delete Manage shared state store");
|
|
146
|
+
console.log("\\nGlobal options:");
|
|
147
|
+
console.log(" --data-dir <path> Custom runtime database directory");
|
|
148
|
+
console.log(" --config <KEY=val> Temporary config override");
|
|
149
|
+
process.exit(ExitCode.SUCCESS);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 3. 建立物理隔离监督边界,启动运行 ActionDockHost 的独立子进程
|
|
153
|
+
const hostScript = join(import.meta.dirname, "entry-host.js");
|
|
154
|
+
const warningFlags = process.env.ACTIONDOCK_SILENCE_WARNINGS !== "0" ? ["--no-warnings=ExperimentalWarning"] : [];
|
|
155
|
+
const child = spawn(process.execPath, [...warningFlags, hostScript, ...argv], {
|
|
156
|
+
cwd: process.cwd(),
|
|
157
|
+
env: process.env,
|
|
158
|
+
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// 4. 标准输出通道物理隔离与受控限流排空
|
|
162
|
+
|
|
163
|
+
const service = new IpcActionDockService({
|
|
164
|
+
childProcess: child,
|
|
165
|
+
diagnosticTarget: process.stderr,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
let cleanedUp = false;
|
|
169
|
+
const cleanup = async () => {
|
|
170
|
+
if (cleanedUp) return;
|
|
171
|
+
cleanedUp = true;
|
|
172
|
+
try {
|
|
173
|
+
await service.close();
|
|
174
|
+
} catch (err) {
|
|
175
|
+
// 防御与透明:服务关闭失败时告警输出至标准错误流,不阻塞退出流程
|
|
176
|
+
process.stderr.write(\`[Supervisor] Warning: Cleanup failed: \${err?.message || String(err)}\\n\`);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
process.once("SIGINT", async () => {
|
|
181
|
+
await cleanup();
|
|
182
|
+
process.exit(ExitCode.SIGINT);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
process.once("SIGTERM", async () => {
|
|
186
|
+
await cleanup();
|
|
187
|
+
process.exit(143);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const dispatcher = new StandaloneDispatcher({
|
|
191
|
+
packageId: METADATA.packageId,
|
|
192
|
+
version: METADATA.version,
|
|
193
|
+
description: METADATA.description,
|
|
194
|
+
service,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const exitCode = await dispatcher.dispatch(argv);
|
|
199
|
+
await cleanup();
|
|
200
|
+
process.exit(exitCode);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
if (err?.code === "HOST_PROCESS_EXITED") {
|
|
203
|
+
console.error("[Supervisor] Host process exited unexpectedly:", err.message);
|
|
204
|
+
} else {
|
|
205
|
+
console.error("[Supervisor] Error:", err?.message || err);
|
|
206
|
+
}
|
|
207
|
+
await cleanup();
|
|
208
|
+
process.exit(ExitCode.FAILURE);
|
|
209
|
+
}
|
|
210
|
+
`;
|
|
211
|
+
}
|
package/dist/export-composite.js
CHANGED
|
@@ -2,7 +2,8 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSyn
|
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import { loadProjectConfig, } from "@actiondock/core";
|
|
4
4
|
import { getPackageSlug, loadManifest, loadPlaybooks, } from "@actiondock/core/project";
|
|
5
|
-
import { generateCompositeSkillMd } from "./skill/index.js";
|
|
5
|
+
import { generateCompositeSkillMd, RUNTIME_REFERENCE_REL_PATH } from "./skill/index.js";
|
|
6
|
+
import { writeRuntimeReferenceFile } from "./export-source.js";
|
|
6
7
|
import { BuilderError } from "./errors.js";
|
|
7
8
|
import { collectRelativeFiles, getInternalDependencyVersion, replaceDirAtomic } from "./fs-utils.js";
|
|
8
9
|
import { allocatePackageDirName, sanitizeExportDependencies } from "./manifest.js";
|
|
@@ -128,6 +129,16 @@ export async function exportCompositeImpl(options, exportSubpackage) {
|
|
|
128
129
|
if (existsSync(targetFile)) {
|
|
129
130
|
console.warn(`[WARN] Existing SKILL.md will be overwritten: ${targetFile}`);
|
|
130
131
|
}
|
|
132
|
+
// 就地重生成只修改 SKILL.md:若参考文件尚未物化则同步写出,避免产生悬空链接
|
|
133
|
+
const referencePath = join(dirname(targetFile), RUNTIME_REFERENCE_REL_PATH);
|
|
134
|
+
if (!existsSync(referencePath)) {
|
|
135
|
+
writeRuntimeReferenceFile(dirname(targetFile), {
|
|
136
|
+
dependencyStepLabel: "安装复合技能聚合依赖",
|
|
137
|
+
relinkStepLabel: "完成安装后重新链接复合技能",
|
|
138
|
+
compositeLinkNote: true,
|
|
139
|
+
invocationStyle: "global-ad",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
131
142
|
writeFileSync(targetFile, compositeSkillMd, "utf-8");
|
|
132
143
|
return {
|
|
133
144
|
bundleName: options.bundleName,
|
|
@@ -148,6 +159,13 @@ export async function exportCompositeImpl(options, exportSubpackage) {
|
|
|
148
159
|
}
|
|
149
160
|
else {
|
|
150
161
|
writeFileSync(join(stagingDir, "SKILL.md"), compositeSkillMd, "utf-8");
|
|
162
|
+
// 仅在使用自动模板时生成复合运行参考文件(含复合链接语义说明)
|
|
163
|
+
writeRuntimeReferenceFile(stagingDir, {
|
|
164
|
+
dependencyStepLabel: "安装复合技能聚合依赖",
|
|
165
|
+
relinkStepLabel: "完成安装后重新链接复合技能",
|
|
166
|
+
compositeLinkNote: true,
|
|
167
|
+
invocationStyle: "global-ad",
|
|
168
|
+
});
|
|
151
169
|
}
|
|
152
170
|
// 聚合所有子包依赖生成复合根目录 package.json
|
|
153
171
|
writeCompositePkgJson(stagingDir, packagesDestDir, packageInfos, bundleSlug, options);
|
package/dist/export-source.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderRuntimeReferenceContent } from "./skill/templates.js";
|
|
1
2
|
import type { SelectionPlan, SkillExporterOptions } from "./types.js";
|
|
2
3
|
/**
|
|
3
4
|
* 源码型 Skill 导出装配层。
|
|
@@ -9,3 +10,8 @@ import type { SelectionPlan, SkillExporterOptions } from "./types.js";
|
|
|
9
10
|
* 源码型 Skill 的暂存阶段:生成 SKILL.md、actiondock.json、package.json 并拷贝源码。
|
|
10
11
|
*/
|
|
11
12
|
export declare function stageSourceSkill(root: string, skillDir: string, plan: SelectionPlan, options: SkillExporterOptions, pkgSlug: string): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* 写出运行参考文件 references/actiondock-runtime.md。
|
|
15
|
+
* 与项目声明资产同路径冲突时报告错误,不覆盖用户文件。
|
|
16
|
+
*/
|
|
17
|
+
export declare function writeRuntimeReferenceFile(destDir: string, options: Parameters<typeof renderRuntimeReferenceContent>[0]): void;
|
package/dist/export-source.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, readFileSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { basename, join } from "node:path";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
3
4
|
import { generateSourceSkillMd } from "./skill/index.js";
|
|
5
|
+
import { RUNTIME_REFERENCE_REL_PATH, renderRuntimeReferenceContent, } from "./skill/templates.js";
|
|
4
6
|
import { BuilderError } from "./errors.js";
|
|
5
7
|
import { getInternalDependencyVersion } from "./fs-utils.js";
|
|
6
8
|
import { assertValidManifestActionIds, sanitizeExportDependencies, serializePlanManifest, } from "./manifest.js";
|
|
7
9
|
import { copyPlanEntries } from "./stage-sources.js";
|
|
8
|
-
import { buildConfigForTemplates,
|
|
10
|
+
import { buildConfigForTemplates, writeSkillMd, } from "./skill-md.js";
|
|
9
11
|
/**
|
|
10
12
|
* 源码型 Skill 导出装配层。
|
|
11
13
|
*
|
|
@@ -19,7 +21,7 @@ export function stageSourceSkill(root, skillDir, plan, options, pkgSlug) {
|
|
|
19
21
|
const actionsDir = plan.actionsDir || "actions";
|
|
20
22
|
const playbooksDir = plan.playbooksDir || "playbooks";
|
|
21
23
|
const configForTemplates = buildConfigForTemplates(plan, actionsDir, playbooksDir);
|
|
22
|
-
const usedExistingSkillMd = writeSkillMd(root, skillDir,
|
|
24
|
+
const usedExistingSkillMd = writeSkillMd(root, skillDir, { skipSkillMd: options.skipSkillMd, skillMdPath: options.skillMdPath, pkgSlug }, () => generateSourceSkillMd(configForTemplates, plan.actions, plan.playbooks));
|
|
23
25
|
// 导出精简后的 actiondock.json 项目配置(项目元数据与清单的单一事实源)
|
|
24
26
|
const exportedConfig = serializePlanManifest(plan, {
|
|
25
27
|
omitEmptyConfig: true,
|
|
@@ -40,11 +42,29 @@ export function stageSourceSkill(root, skillDir, plan, options, pkgSlug) {
|
|
|
40
42
|
copyPlanEntries(skillDir, plan, {
|
|
41
43
|
playbookRelPath: (pb) => join(playbooksDir, basename(pb.filePath)),
|
|
42
44
|
});
|
|
45
|
+
// 声明资产拷贝完成后再物化默认运行参考文件:仅在使用自动模板时生成;
|
|
46
|
+
// 与项目声明资产同路径冲突时报错不覆盖(拷贝在前,冲突检测能真实命中)
|
|
47
|
+
if (usedExistingSkillMd === undefined && !options.skipSkillMd) {
|
|
48
|
+
writeRuntimeReferenceFile(skillDir, {
|
|
49
|
+
dependencyStepLabel: "安装技能源码依赖",
|
|
50
|
+
relinkStepLabel: "完成安装后重新链接本技能",
|
|
51
|
+
invocationStyle: "global-ad",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
43
54
|
return usedExistingSkillMd;
|
|
44
55
|
}
|
|
45
56
|
/**
|
|
46
|
-
*
|
|
57
|
+
* 写出运行参考文件 references/actiondock-runtime.md。
|
|
58
|
+
* 与项目声明资产同路径冲突时报告错误,不覆盖用户文件。
|
|
47
59
|
*/
|
|
60
|
+
export function writeRuntimeReferenceFile(destDir, options) {
|
|
61
|
+
const referencePath = join(destDir, RUNTIME_REFERENCE_REL_PATH);
|
|
62
|
+
if (existsSync(referencePath)) {
|
|
63
|
+
throw new BuilderError(`Runtime reference file conflict: '${referencePath}' already exists (declared asset or user file). Refusing to overwrite user files.`);
|
|
64
|
+
}
|
|
65
|
+
mkdirSync(dirname(referencePath), { recursive: true });
|
|
66
|
+
writeFileSync(referencePath, renderRuntimeReferenceContent(options), "utf-8");
|
|
67
|
+
}
|
|
48
68
|
function writeSourceSkillPkgJson(root, skillDir, plan, pkgSlug) {
|
|
49
69
|
let exportedPkg;
|
|
50
70
|
const projectPkgPath = join(root, "package.json");
|
package/dist/exporter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CompositeSkillExportOptions, CompositeSkillExportResult, SkillExporterOptions, SkillExportResult } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Agent Skill 导出器门面。
|
|
4
4
|
* 仅承担编排职责:单包模式分派、复合/批量流程装配与结果组装,
|
|
@@ -17,10 +17,6 @@ export declare class SkillExporter {
|
|
|
17
17
|
* 源码型 Skill 导出:暂存目录内完成清单、说明书与源码装配后原子替换目标目录。
|
|
18
18
|
*/
|
|
19
19
|
private exportSourceSkill;
|
|
20
|
-
/**
|
|
21
|
-
* 批量导出多个 Skill。
|
|
22
|
-
*/
|
|
23
|
-
exportBatch(options: BatchSkillExportOptions): Promise<BatchSkillExportResult>;
|
|
24
20
|
/**
|
|
25
21
|
* 复合模式导出:将多个包聚合为一个统一的复合技能目录。
|
|
26
22
|
*/
|
|
@@ -32,11 +28,6 @@ export declare class SkillExporter {
|
|
|
32
28
|
* 需要复用导出器或定制行为时可直接实例化 SkillExporter。
|
|
33
29
|
*/
|
|
34
30
|
export declare function exportSkill(options: SkillExporterOptions): Promise<SkillExportResult>;
|
|
35
|
-
/**
|
|
36
|
-
* 批量导出多个 Skill 产物的顶层公共入口函数。
|
|
37
|
-
* 内部创建 SkillExporter 实例并委托其实例批量导出方法。
|
|
38
|
-
*/
|
|
39
|
-
export declare function exportSkillBatch(options: BatchSkillExportOptions): Promise<BatchSkillExportResult>;
|
|
40
31
|
/**
|
|
41
32
|
* 导出复合技能套件的顶层公共入口函数。
|
|
42
33
|
* 内部创建 SkillExporter 实例并委托其实例复合导出方法。
|
package/dist/exporter.js
CHANGED
|
@@ -8,9 +8,8 @@ import { SelectionPlanner } from "./planner.js";
|
|
|
8
8
|
import { isOwnAction } from "./types.js";
|
|
9
9
|
import { createArchive, resolveArchiveFormat } from "./export-archive.js";
|
|
10
10
|
import { stageSourceSkill } from "./export-source.js";
|
|
11
|
-
import { exportNodeSkill } from "./
|
|
11
|
+
import { exportNodeSkill } from "./build.js";
|
|
12
12
|
import { exportCompositeImpl } from "./export-composite.js";
|
|
13
|
-
import { exportBatchImpl } from "./export-batch.js";
|
|
14
13
|
/**
|
|
15
14
|
* Agent Skill 导出器门面。
|
|
16
15
|
* 仅承担编排职责:单包模式分派、复合/批量流程装配与结果组装,
|
|
@@ -118,12 +117,6 @@ export class SkillExporter {
|
|
|
118
117
|
usedExistingSkillMd,
|
|
119
118
|
};
|
|
120
119
|
}
|
|
121
|
-
/**
|
|
122
|
-
* 批量导出多个 Skill。
|
|
123
|
-
*/
|
|
124
|
-
async exportBatch(options) {
|
|
125
|
-
return exportBatchImpl(options, (opts) => this.export(opts));
|
|
126
|
-
}
|
|
127
120
|
/**
|
|
128
121
|
* 复合模式导出:将多个包聚合为一个统一的复合技能目录。
|
|
129
122
|
*/
|
|
@@ -140,14 +133,6 @@ export async function exportSkill(options) {
|
|
|
140
133
|
const exporter = new SkillExporter();
|
|
141
134
|
return exporter.export(options);
|
|
142
135
|
}
|
|
143
|
-
/**
|
|
144
|
-
* 批量导出多个 Skill 产物的顶层公共入口函数。
|
|
145
|
-
* 内部创建 SkillExporter 实例并委托其实例批量导出方法。
|
|
146
|
-
*/
|
|
147
|
-
export async function exportSkillBatch(options) {
|
|
148
|
-
const exporter = new SkillExporter();
|
|
149
|
-
return exporter.exportBatch(options);
|
|
150
|
-
}
|
|
151
136
|
/**
|
|
152
137
|
* 导出复合技能套件的顶层公共入口函数。
|
|
153
138
|
* 内部创建 SkillExporter 实例并委托其实例复合导出方法。
|
package/dist/fs-utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cpSync, existsSync, renameSync, rmSync } from "node:fs";
|
|
2
2
|
import { ACTIONDOCK_VERSION } from "@actiondock/core";
|
|
3
3
|
import { traverseDirectory } from "@actiondock/core/project";
|
|
4
|
+
import { BuilderError } from "./errors.js";
|
|
4
5
|
/**
|
|
5
6
|
* builder 包内共享的文件系统基础设施。
|
|
6
7
|
* 消除 pack / build / exporter 三个业务模块中成体系的重复实现,
|
|
@@ -112,7 +113,7 @@ export async function replaceDirAtomic(stagingDir, targetDir) {
|
|
|
112
113
|
await moveDirAtomic(backupDir, targetDir);
|
|
113
114
|
}
|
|
114
115
|
catch (rollbackErr) {
|
|
115
|
-
throw new
|
|
116
|
+
throw new BuilderError(`Failed to promote directory to '${targetDir}' (${promoteErr instanceof Error ? promoteErr.message : String(promoteErr)}), and rollback from backup '${backupDir}' also failed: ${rollbackErr?.message || String(rollbackErr)}`, "FS_PROMOTE_FAILED");
|
|
116
117
|
}
|
|
117
118
|
throw promoteErr;
|
|
118
119
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* 专注于暴露核心构建主接口与必要契约,低层内部工具作为包内模块封装。
|
|
4
4
|
*/
|
|
5
5
|
export { BuilderError, PlannerError } from "./errors.js";
|
|
6
|
-
export { buildProject } from "./build.js";
|
|
6
|
+
export { buildProject, buildProjectWithPlan } from "./build.js";
|
|
7
7
|
export { packProject } from "./pack.js";
|
|
8
|
-
export { exportCompositeSkill, exportSkill,
|
|
8
|
+
export { exportCompositeSkill, exportSkill, SkillExporter, } from "./exporter.js";
|
|
9
|
+
export { allocatePackageDirName } from "./manifest.js";
|
|
9
10
|
export { SelectionPlanner } from "./planner.js";
|
|
10
|
-
export type { ArchiveFormat,
|
|
11
|
+
export type { ArchiveFormat, BuildOptions, BuildPlan, BuildProjectWithPlanOptions, BuildResult, CompositeSkillExportOptions, CompositeSkillExportResult, ExportSkillOptions, ExportSkillResult, PackOptions, PackResult, SelectionPlan, SelectionPlannerOptions, SkillExporterOptions, SkillExportResult, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
// 错误模型
|
|
6
6
|
export { BuilderError, PlannerError } from "./errors.js";
|
|
7
7
|
// 构建、打包与导出主接口
|
|
8
|
-
export { buildProject } from "./build.js";
|
|
8
|
+
export { buildProject, buildProjectWithPlan } from "./build.js";
|
|
9
9
|
export { packProject } from "./pack.js";
|
|
10
|
-
export { exportCompositeSkill, exportSkill,
|
|
10
|
+
export { exportCompositeSkill, exportSkill, SkillExporter, } from "./exporter.js";
|
|
11
|
+
export { allocatePackageDirName } from "./manifest.js";
|
|
11
12
|
// 构建规划器
|
|
12
13
|
export { SelectionPlanner } from "./planner.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LockfileInfo } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* 支持的包管理器锁文件候选列表。
|
|
4
|
+
*/
|
|
5
|
+
export declare const KNOWN_LOCKFILES: string[];
|
|
6
|
+
/**
|
|
7
|
+
* 读取并计算项目锁文件元数据及 SHA-256 摘要。
|
|
8
|
+
*/
|
|
9
|
+
export declare function computeLockfileInfo(projectRoot: string, preferredLockfile?: string): LockfileInfo | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { assertPathWithinRoot } from "@actiondock/core/project";
|
|
5
|
+
import { PlannerError } from "./errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* 支持的包管理器锁文件候选列表。
|
|
8
|
+
*/
|
|
9
|
+
export const KNOWN_LOCKFILES = [
|
|
10
|
+
"package-lock.json",
|
|
11
|
+
"npm-shrinkwrap.json",
|
|
12
|
+
"bun.lockb",
|
|
13
|
+
"bun.lock",
|
|
14
|
+
"pnpm-lock.yaml",
|
|
15
|
+
"yarn.lock",
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* 读取并计算项目锁文件元数据及 SHA-256 摘要。
|
|
19
|
+
*/
|
|
20
|
+
export function computeLockfileInfo(projectRoot, preferredLockfile) {
|
|
21
|
+
if (preferredLockfile) {
|
|
22
|
+
const lockPath = resolve(projectRoot, preferredLockfile);
|
|
23
|
+
assertPathWithinRoot(projectRoot, lockPath, "lockfile");
|
|
24
|
+
if (existsSync(lockPath) && statSync(lockPath).isFile()) {
|
|
25
|
+
const content = readFileSync(lockPath);
|
|
26
|
+
const sha256 = createHash("sha256").update(content).digest("hex");
|
|
27
|
+
return {
|
|
28
|
+
name: basename(lockPath),
|
|
29
|
+
path: lockPath,
|
|
30
|
+
sha256,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
throw new PlannerError(`Specified lockfile not found on disk: ${preferredLockfile}`, "LOCKFILE_NOT_FOUND");
|
|
34
|
+
}
|
|
35
|
+
for (const lockFileName of KNOWN_LOCKFILES) {
|
|
36
|
+
const lockPath = join(projectRoot, lockFileName);
|
|
37
|
+
if (existsSync(lockPath)) {
|
|
38
|
+
try {
|
|
39
|
+
if (statSync(lockPath).isFile()) {
|
|
40
|
+
const content = readFileSync(lockPath);
|
|
41
|
+
const sha256 = createHash("sha256").update(content).digest("hex");
|
|
42
|
+
return {
|
|
43
|
+
name: lockFileName,
|
|
44
|
+
path: lockPath,
|
|
45
|
+
sha256,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
// 存在但不可读:显式报错而非静默跳过,否则构建在无锁文件摘要状态下继续,可复现性校验形同虚设
|
|
51
|
+
throw new PlannerError(`Lockfile '${lockFileName}' exists but could not be read in ${projectRoot}: ${err?.message || String(err)}`, "LOCKFILE_READ_ERROR");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 在暂存目录调用带有 --ignore-scripts 的 npm pack 生成标准 npm 压缩包(.tgz)。
|
|
3
|
+
* 优先解析 --json 结构化输出,失败时回退到尾部行文件名解析。
|
|
4
|
+
* Windows 兼容:npm 是 .cmd 脚本,无 shell 直接 spawn 会 ENOENT/EINVAL。
|
|
5
|
+
*/
|
|
6
|
+
export declare function runNpmPack(stagingPkgDir: string, packDestination: string, fallbackName: string): Promise<string>;
|
package/dist/npm-pack.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { BuilderError } from "./errors.js";
|
|
5
|
+
/**
|
|
6
|
+
* 在暂存目录调用带有 --ignore-scripts 的 npm pack 生成标准 npm 压缩包(.tgz)。
|
|
7
|
+
* 优先解析 --json 结构化输出,失败时回退到尾部行文件名解析。
|
|
8
|
+
* Windows 兼容:npm 是 .cmd 脚本,无 shell 直接 spawn 会 ENOENT/EINVAL。
|
|
9
|
+
*/
|
|
10
|
+
export async function runNpmPack(stagingPkgDir, packDestination, fallbackName) {
|
|
11
|
+
const { stdout } = await new Promise((resolvePromise, rejectPromise) => {
|
|
12
|
+
const child = spawn("npm", ["pack", "--ignore-scripts", "--json", "--pack-destination", packDestination], {
|
|
13
|
+
cwd: stagingPkgDir,
|
|
14
|
+
shell: process.platform === "win32",
|
|
15
|
+
});
|
|
16
|
+
let stdoutBuf = "";
|
|
17
|
+
let stderrBuf = "";
|
|
18
|
+
child.stdout.on("data", (chunk) => (stdoutBuf += chunk));
|
|
19
|
+
child.stderr.on("data", (chunk) => (stderrBuf += chunk));
|
|
20
|
+
child.on("error", rejectPromise);
|
|
21
|
+
child.on("close", (code) => {
|
|
22
|
+
if (code === 0) {
|
|
23
|
+
resolvePromise({ stdout: stdoutBuf, stderr: stderrBuf });
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
rejectPromise(new BuilderError(`npm pack exited with code ${code}. Output:\n${stdoutBuf}\n${stderrBuf}`));
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
// 优先解析 --json 结构化输出中的压缩包文件名
|
|
31
|
+
let generatedName;
|
|
32
|
+
const trimmed = stdout.trim();
|
|
33
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(trimmed);
|
|
36
|
+
let items;
|
|
37
|
+
if (Array.isArray(parsed)) {
|
|
38
|
+
items = parsed;
|
|
39
|
+
}
|
|
40
|
+
else if (typeof parsed === "object" && parsed !== null) {
|
|
41
|
+
// npm >= 11 的 pack --json 输出为按包名索引的对象,取全部包含 filename 的条目
|
|
42
|
+
items = Object.values(parsed).filter((entry) => typeof entry === "object" && entry !== null && "filename" in entry);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
items = [];
|
|
46
|
+
}
|
|
47
|
+
// 多包输出时取最后一个 filename(npm 按依赖顺序逐包输出,末尾为当前项目产物)
|
|
48
|
+
const filenames = items
|
|
49
|
+
.map((item) => item.filename)
|
|
50
|
+
.filter((name) => typeof name === "string" && name.length > 0);
|
|
51
|
+
const filename = filenames[filenames.length - 1];
|
|
52
|
+
if (filename !== undefined) {
|
|
53
|
+
generatedName = filename;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// 回退到尾部行解析
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!generatedName) {
|
|
61
|
+
const packLines = trimmed
|
|
62
|
+
.split("\n")
|
|
63
|
+
.map((line) => line.trim())
|
|
64
|
+
.filter((line) => line.length > 0);
|
|
65
|
+
const tgzLine = packLines.slice().reverse().find((line) => line.endsWith(".tgz"));
|
|
66
|
+
generatedName = tgzLine || packLines.pop() || fallbackName;
|
|
67
|
+
}
|
|
68
|
+
const generatedTarball = join(packDestination, generatedName);
|
|
69
|
+
if (!existsSync(generatedTarball)) {
|
|
70
|
+
throw new BuilderError(`npm pack did not produce expected tarball at ${generatedTarball}. Output:\n${stdout}`);
|
|
71
|
+
}
|
|
72
|
+
return generatedTarball;
|
|
73
|
+
}
|
package/dist/pack.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { PackOptions, PackResult } from "./types.js";
|
|
|
3
3
|
* 对 Action Package 执行打包并生成标准 npm Action 压缩包(.tgz)。
|
|
4
4
|
*
|
|
5
5
|
* 职责:
|
|
6
|
-
* -
|
|
7
|
-
* - 严格校验导出的 Manifest
|
|
6
|
+
* - 依托 Node 24 原生类型擦除特性,直接按规范收集并打包源码(.ts / .js 等)及资产文件到临时暂存目录。
|
|
7
|
+
* - 严格校验导出的 Manifest(不得包含 actionsDir 或 playbooksDir 等废弃目录字段)。
|
|
8
8
|
* - 在暂存目录调用带有 --ignore-scripts 的 npm pack 生成标准 npm 压缩包(.tgz),严禁使用正则替换与自建 tar 压缩。
|
|
9
9
|
*
|
|
10
10
|
* @param options 打包参数选项
|