@actiondock/builder 2.7.0 → 2.8.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/README.md +4 -5
- package/dist/archive.d.ts +0 -16
- package/dist/archive.js +35 -202
- package/dist/build.d.ts +1 -0
- package/dist/build.js +26 -61
- package/dist/dependency-check.js +36 -89
- package/dist/export-batch.js +4 -6
- package/dist/export-composite.js +8 -22
- package/dist/export-node.js +1 -1
- package/dist/export-source.js +11 -61
- package/dist/exporter.js +3 -9
- package/dist/fs-utils.d.ts +3 -2
- package/dist/fs-utils.js +9 -66
- package/dist/index.d.ts +3 -10
- package/dist/index.js +5 -16
- package/dist/manifest.d.ts +26 -0
- package/dist/manifest.js +67 -2
- package/dist/pack.js +37 -47
- package/dist/planner.js +73 -102
- package/dist/skill/index.d.ts +1 -0
- package/dist/skill/index.js +1 -0
- package/dist/skill/templates.d.ts +60 -0
- package/dist/skill/templates.js +507 -0
- package/dist/skill-md.d.ts +2 -1
- package/dist/skill-md.js +2 -1
- package/dist/stage-sources.d.ts +25 -0
- package/dist/stage-sources.js +51 -0
- package/dist/types.d.ts +18 -23
- package/dist/types.js +10 -1
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @actiondock/builder
|
|
2
2
|
|
|
3
|
-
ActionDock 2.
|
|
3
|
+
ActionDock 2.x 构建规划、目录交付与技能导出包。
|
|
4
4
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
8
8
|
|
|
9
|
-
`@actiondock/builder` 负责静态分析依赖关系、构建 Node.js 目录交付产物、打包标准 npm 压缩包以及将工具和规程导出为可分发的 Agent Skill
|
|
9
|
+
`@actiondock/builder` 负责静态分析依赖关系、构建 Node.js 目录交付产物、打包标准 npm 压缩包以及将工具和规程导出为可分发的 Agent Skill 资产。在 ActionDock 2.x 中,Skill 模板生成与规程渲染能力已统一归位并入本包作为内置支持。
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -29,7 +29,6 @@ ActionDock 2.0 构建规划、目录交付与技能导出包。
|
|
|
29
29
|
- 预置生产依赖:支持 `--vendor-deps` 选项,将锁定的生产依赖物化至产物目录内,实现离线自包含运行。
|
|
30
30
|
- 可重现构建检验:支持 `--require-reproducible` 选项,当存在不受控的生命周期安装脚本时严格报错阻断,保证构建确定性。
|
|
31
31
|
- 标准归档打包:支持 `--archive` 选项,自动将构建目录打包为标准 `.zip` 压缩归档文件。
|
|
32
|
-
- 原单文件二进制编译选项(`--target` 与 `--bytecode`)已彻底删除,统一采用 Node.js 目录交付格式。
|
|
33
32
|
|
|
34
33
|
### packProject npm 包打包
|
|
35
34
|
|
|
@@ -39,9 +38,9 @@ ActionDock 2.0 构建规划、目录交付与技能导出包。
|
|
|
39
38
|
- 预检模式:支持 `--dry-run` 选项,完整校验打包清单、动作列表与文件总数,无需实际写入磁盘。
|
|
40
39
|
- 完整性校验:自动计算并输出打包产物的字节大小与 SHA-256 校验和。
|
|
41
40
|
|
|
42
|
-
### SkillExporter
|
|
41
|
+
### SkillExporter 与技能导出
|
|
43
42
|
|
|
44
|
-
[SkillExporter](./src/exporter.ts)
|
|
43
|
+
[SkillExporter](./src/exporter.ts) 与 `exportSkill` 导出函数将原子 Action 与操作规程 Playbook 打包导出为面向主流 AI 智能体生态的标准 Agent Skill 资产:
|
|
45
44
|
|
|
46
45
|
- 生成 SKILL.md 指令:提取 Playbook 规程内容与工具元数据,合成符合智能体规范的操作指南文档与声明头部。
|
|
47
46
|
- 源码模式技能导出:使用 `--mode source`(默认模式),保留 TypeScript 源码与依赖声明,适合具备 Node.js 运行环境的智能体容器直接运行。
|
package/dist/archive.d.ts
CHANGED
|
@@ -4,14 +4,6 @@ export declare function dosDateTime(ms: number): {
|
|
|
4
4
|
date: number;
|
|
5
5
|
time: number;
|
|
6
6
|
};
|
|
7
|
-
/**
|
|
8
|
-
* 将目录打包为标准 zip 归档(PKZIP APPNOTE 结构)。
|
|
9
|
-
* 直接流式分块写入磁盘,避免在内存中积压全量归档内容。
|
|
10
|
-
*
|
|
11
|
-
* @param dir 待打包目录(目录名即归档内根文件夹名)
|
|
12
|
-
* @param outPath 输出 zip 文件路径
|
|
13
|
-
*/
|
|
14
|
-
export declare function createZipArchive(dir: string, outPath: string): void;
|
|
15
7
|
/**
|
|
16
8
|
* 向可写流安全写入数据块,严格处理背压与流异常。
|
|
17
9
|
* 当 stream.write 返回 false 时挂起等待 drain 事件触发;
|
|
@@ -23,14 +15,6 @@ export declare function writeToStream(stream: Writable, chunk: Buffer): Promise<
|
|
|
23
15
|
* 逐文件流式读取与增量压缩写入磁盘,避免在内存中积压全量原文件与压缩内容。
|
|
24
16
|
*/
|
|
25
17
|
export declare function createZipArchiveAsync(dir: string, outPath: string): Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
* 将目录打包为标准 tar.gz 归档(USTAR + gzip)。
|
|
28
|
-
*
|
|
29
|
-
* @param dir 待打包目录(目录名即归档内根文件夹名)
|
|
30
|
-
* @param outPath 输出 tar.gz 文件路径
|
|
31
|
-
*/
|
|
32
|
-
export declare function createTarGzArchive(dir: string, outPath: string): void;
|
|
33
|
-
/** tar.gz 异步流式归档配置选项 */
|
|
34
18
|
export interface TarGzArchiveOptions {
|
|
35
19
|
/** 自定义可读流创建器,默认使用 node:fs 的 createReadStream */
|
|
36
20
|
createReadStream?: (path: string) => Readable;
|
package/dist/archive.js
CHANGED
|
@@ -1,71 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createDeflateRaw,
|
|
3
|
-
import { basename, join
|
|
1
|
+
import { createReadStream, createWriteStream, existsSync, statSync } from "node:fs";
|
|
2
|
+
import { createDeflateRaw, createGzip } from "node:zlib";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
4
|
import { Transform, Writable } from "node:stream";
|
|
5
5
|
import { pipeline } from "node:stream/promises";
|
|
6
|
-
import {
|
|
7
|
-
/**
|
|
8
|
-
|
|
6
|
+
import { traverseDirectory } from "@actiondock/core/project";
|
|
7
|
+
/**
|
|
8
|
+
* 递归收集目录内全部条目。
|
|
9
|
+
* 目录遍历统一复用 core 的 traverseDirectory 单一事实源,
|
|
10
|
+
* 自带安全路径边界校验、软链接越界跳过与循环拦截。
|
|
11
|
+
*/
|
|
12
|
+
function collectEntries(dir) {
|
|
9
13
|
if (!existsSync(dir))
|
|
10
14
|
return [];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const fullPath = join(dir, name);
|
|
29
|
-
try {
|
|
30
|
-
assertPathWithinRoot(baseDir, fullPath, "archive path");
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
let real;
|
|
36
|
-
try {
|
|
37
|
-
real = existsSync(fullPath) ? realpathSync(fullPath) : fullPath;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
assertPathWithinRoot(baseDir, real, "archive path");
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
// 忽略并跳过指向 baseDir 外部的软链接
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (visited.has(real)) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
visited.add(real);
|
|
53
|
-
try {
|
|
54
|
-
const stat = statSync(fullPath);
|
|
55
|
-
if (stat.isDirectory()) {
|
|
56
|
-
entries.push({ relPath: relative(baseDir, fullPath).split(sep).join("/"), isDir: true });
|
|
57
|
-
entries.push(...collectEntries(fullPath, baseDir, visited));
|
|
58
|
-
}
|
|
59
|
-
else if (stat.isFile()) {
|
|
60
|
-
entries.push({ relPath: relative(baseDir, fullPath).split(sep).join("/"), isDir: false });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// 忽略无法访问或损坏的文件/符号链接
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
15
|
+
return traverseDirectory(dir, { includeDirs: true }).map((entry) => ({
|
|
16
|
+
relPath: entry.relPath,
|
|
17
|
+
isDir: entry.isDir,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 归档条目可执行位启发式:bin/ 路径前缀约定为可执行产物。
|
|
22
|
+
* 与磁盘 mode 权限位任一命中即按 0755 落归档元数据。
|
|
23
|
+
*/
|
|
24
|
+
const EXECUTABLE_PATH_HINT = "bin/";
|
|
25
|
+
/**
|
|
26
|
+
* 计算归档条目的 Unix 权限位:目录固定 0755;文件按「磁盘 mode 含执行位
|
|
27
|
+
* 或相对路径命中 bin/ 启发式」判定为 0755,其余 0644。
|
|
28
|
+
*/
|
|
29
|
+
function computeUnixMode(relPath, mode, isDir) {
|
|
30
|
+
if (isDir) {
|
|
31
|
+
return 0o40755;
|
|
67
32
|
}
|
|
68
|
-
|
|
33
|
+
const isExec = Boolean(mode & 0o111) ||
|
|
34
|
+
relPath.includes(EXECUTABLE_PATH_HINT) ||
|
|
35
|
+
relPath.startsWith(EXECUTABLE_PATH_HINT);
|
|
36
|
+
return isExec ? 0o100755 : 0o100644;
|
|
69
37
|
}
|
|
70
38
|
/* ------------------------------------------------------------------ */
|
|
71
39
|
/* zip 格式写入 */
|
|
@@ -88,9 +56,6 @@ function updateCrc32(crc, buf) {
|
|
|
88
56
|
}
|
|
89
57
|
return crc;
|
|
90
58
|
}
|
|
91
|
-
function crc32(buf) {
|
|
92
|
-
return (updateCrc32(0xffffffff, buf) ^ 0xffffffff) >>> 0;
|
|
93
|
-
}
|
|
94
59
|
/** Unix 毫秒时间戳转 DOS 时间格式(date/time 各 16 位) */
|
|
95
60
|
export function dosDateTime(ms) {
|
|
96
61
|
const d = new Date(ms);
|
|
@@ -104,104 +69,6 @@ export function dosDateTime(ms) {
|
|
|
104
69
|
const date = ((year - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate();
|
|
105
70
|
return { date, time };
|
|
106
71
|
}
|
|
107
|
-
/**
|
|
108
|
-
* 将目录打包为标准 zip 归档(PKZIP APPNOTE 结构)。
|
|
109
|
-
* 直接流式分块写入磁盘,避免在内存中积压全量归档内容。
|
|
110
|
-
*
|
|
111
|
-
* @param dir 待打包目录(目录名即归档内根文件夹名)
|
|
112
|
-
* @param outPath 输出 zip 文件路径
|
|
113
|
-
*/
|
|
114
|
-
export function createZipArchive(dir, outPath) {
|
|
115
|
-
const rootName = basename(dir);
|
|
116
|
-
const entries = collectEntries(dir);
|
|
117
|
-
const fd = openSync(outPath, "w");
|
|
118
|
-
const centralRecords = [];
|
|
119
|
-
let offset = 0;
|
|
120
|
-
try {
|
|
121
|
-
for (const entry of entries) {
|
|
122
|
-
// 归档内路径带根目录前缀;目录条目名以 / 结尾
|
|
123
|
-
const name = `${rootName}/${entry.relPath}${entry.isDir ? "/" : ""}`;
|
|
124
|
-
const nameBuf = Buffer.from(name, "utf8");
|
|
125
|
-
const stat = statSync(join(dir, entry.relPath));
|
|
126
|
-
const { date, time } = dosDateTime(stat.mtimeMs);
|
|
127
|
-
const content = entry.isDir ? Buffer.alloc(0) : readFileSync(join(dir, entry.relPath));
|
|
128
|
-
let method = 0;
|
|
129
|
-
let payload = content;
|
|
130
|
-
if (!entry.isDir && content.length > 0) {
|
|
131
|
-
const deflated = deflateRawSync(content, { level: 9 });
|
|
132
|
-
if (deflated.length < content.length) {
|
|
133
|
-
method = 8;
|
|
134
|
-
payload = deflated;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
const crc = crc32(content);
|
|
138
|
-
// Local File Header(30 字节 + 文件名)
|
|
139
|
-
const local = Buffer.alloc(30);
|
|
140
|
-
local.writeUInt32LE(0x04034b50, 0);
|
|
141
|
-
local.writeUInt16LE(20, 4);
|
|
142
|
-
local.writeUInt16LE(0x0800, 6); // UTF-8 文件名标志
|
|
143
|
-
local.writeUInt16LE(method, 8);
|
|
144
|
-
local.writeUInt16LE(time, 10);
|
|
145
|
-
local.writeUInt16LE(date, 12);
|
|
146
|
-
local.writeUInt32LE(crc, 14);
|
|
147
|
-
local.writeUInt32LE(payload.length, 18);
|
|
148
|
-
local.writeUInt32LE(content.length, 22);
|
|
149
|
-
local.writeUInt16LE(nameBuf.length, 26);
|
|
150
|
-
local.writeUInt16LE(0, 28);
|
|
151
|
-
writeSync(fd, local);
|
|
152
|
-
writeSync(fd, nameBuf);
|
|
153
|
-
if (payload.length > 0) {
|
|
154
|
-
writeSync(fd, payload);
|
|
155
|
-
}
|
|
156
|
-
// Central Directory Header(46 字节 + 文件名)
|
|
157
|
-
const central = Buffer.alloc(46);
|
|
158
|
-
central.writeUInt32LE(0x02014b50, 0);
|
|
159
|
-
central.writeUInt16LE((3 << 8) | 20, 4); // version made by: Unix + ZIP 2.0
|
|
160
|
-
central.writeUInt16LE(20, 6); // version needed
|
|
161
|
-
central.writeUInt16LE(0x0800, 8);
|
|
162
|
-
central.writeUInt16LE(method, 10);
|
|
163
|
-
central.writeUInt16LE(time, 12);
|
|
164
|
-
central.writeUInt16LE(date, 14);
|
|
165
|
-
central.writeUInt32LE(crc, 16);
|
|
166
|
-
central.writeUInt32LE(payload.length, 20);
|
|
167
|
-
central.writeUInt32LE(content.length, 24);
|
|
168
|
-
central.writeUInt16LE(nameBuf.length, 28);
|
|
169
|
-
central.writeUInt16LE(0, 30); // extra len
|
|
170
|
-
central.writeUInt16LE(0, 32); // comment len
|
|
171
|
-
central.writeUInt16LE(0, 34); // disk start
|
|
172
|
-
central.writeUInt16LE(0, 36); // internal attrs
|
|
173
|
-
// 外部属性:Unix 权限左移 16 位,目录附加 MS-DOS 目录位
|
|
174
|
-
const isExec = !entry.isDir &&
|
|
175
|
-
(Boolean(stat.mode & 0o111) || entry.relPath.includes("bin/") || entry.relPath.startsWith("bin/"));
|
|
176
|
-
const fileMode = isExec ? 0o100755 : 0o100644;
|
|
177
|
-
const extAttrs = ((entry.isDir ? 0o40755 : fileMode) << 16) | (entry.isDir ? 0x10 : 0);
|
|
178
|
-
central.writeUInt32LE(extAttrs >>> 0, 38);
|
|
179
|
-
central.writeUInt32LE(offset, 42);
|
|
180
|
-
centralRecords.push({ header: Buffer.concat([central, nameBuf]), localOffset: offset });
|
|
181
|
-
offset += local.length + nameBuf.length + payload.length;
|
|
182
|
-
}
|
|
183
|
-
const centralDirOffset = offset;
|
|
184
|
-
let centralDirSize = 0;
|
|
185
|
-
for (const record of centralRecords) {
|
|
186
|
-
writeSync(fd, record.header);
|
|
187
|
-
centralDirSize += record.header.length;
|
|
188
|
-
}
|
|
189
|
-
// End of Central Directory Record(22 字节)
|
|
190
|
-
const eocd = Buffer.alloc(22);
|
|
191
|
-
eocd.writeUInt32LE(0x06054b50, 0);
|
|
192
|
-
eocd.writeUInt16LE(0, 4);
|
|
193
|
-
eocd.writeUInt16LE(0, 6);
|
|
194
|
-
eocd.writeUInt16LE(centralRecords.length, 8);
|
|
195
|
-
eocd.writeUInt16LE(centralRecords.length, 10);
|
|
196
|
-
eocd.writeUInt32LE(centralDirSize, 12);
|
|
197
|
-
eocd.writeUInt32LE(centralDirOffset, 16);
|
|
198
|
-
eocd.writeUInt16LE(0, 20); // comment len
|
|
199
|
-
writeSync(fd, eocd);
|
|
200
|
-
}
|
|
201
|
-
finally {
|
|
202
|
-
closeSync(fd);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
72
|
/**
|
|
206
73
|
* 向可写流安全写入数据块,严格处理背压与流异常。
|
|
207
74
|
* 当 stream.write 返回 false 时挂起等待 drain 事件触发;
|
|
@@ -267,10 +134,8 @@ export async function createZipArchiveAsync(dir, outPath) {
|
|
|
267
134
|
const stat = statSync(fullPath);
|
|
268
135
|
const { date, time } = dosDateTime(stat.mtimeMs);
|
|
269
136
|
// 外部属性:Unix 权限左移 16 位,目录附加 MS-DOS 目录位
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
const fileMode = isExec ? 0o100755 : 0o100644;
|
|
273
|
-
const extAttrs = ((entry.isDir ? 0o40755 : fileMode) << 16) | (entry.isDir ? 0x10 : 0);
|
|
137
|
+
const fileMode = computeUnixMode(entry.relPath, stat.mode, entry.isDir);
|
|
138
|
+
const extAttrs = (fileMode << 16) | (entry.isDir ? 0x10 : 0);
|
|
274
139
|
const localOffset = offset;
|
|
275
140
|
const isDeflatedFile = !entry.isDir && stat.size > 0;
|
|
276
141
|
const method = isDeflatedFile ? 8 : 0;
|
|
@@ -429,37 +294,6 @@ function tarHeader(path, size, mtimeSec, isDir, modeOrExec = false) {
|
|
|
429
294
|
buf.write(" ", 155, 1, "ascii");
|
|
430
295
|
return buf;
|
|
431
296
|
}
|
|
432
|
-
/** 将字节数补齐到 512 的整数倍 */
|
|
433
|
-
function pad512(length) {
|
|
434
|
-
const rem = length % 512;
|
|
435
|
-
return rem === 0 ? Buffer.alloc(0) : Buffer.alloc(512 - rem);
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* 将目录打包为标准 tar.gz 归档(USTAR + gzip)。
|
|
439
|
-
*
|
|
440
|
-
* @param dir 待打包目录(目录名即归档内根文件夹名)
|
|
441
|
-
* @param outPath 输出 tar.gz 文件路径
|
|
442
|
-
*/
|
|
443
|
-
export function createTarGzArchive(dir, outPath) {
|
|
444
|
-
const rootName = basename(dir);
|
|
445
|
-
const entries = collectEntries(dir);
|
|
446
|
-
const chunks = [];
|
|
447
|
-
for (const entry of entries) {
|
|
448
|
-
const path = `${rootName}/${entry.relPath}${entry.isDir ? "/" : ""}`;
|
|
449
|
-
const fullPath = join(dir, entry.relPath);
|
|
450
|
-
const stat = statSync(fullPath);
|
|
451
|
-
const content = entry.isDir ? Buffer.alloc(0) : readFileSync(fullPath);
|
|
452
|
-
const isExec = !entry.isDir &&
|
|
453
|
-
(Boolean(stat.mode & 0o111) || entry.relPath.includes("bin/") || entry.relPath.startsWith("bin/"));
|
|
454
|
-
chunks.push(tarHeader(path, content.length, Math.floor(stat.mtimeMs / 1000), entry.isDir, isExec));
|
|
455
|
-
if (!entry.isDir) {
|
|
456
|
-
chunks.push(content, pad512(content.length));
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
// 归档结尾:两个全零块
|
|
460
|
-
chunks.push(Buffer.alloc(1024));
|
|
461
|
-
writeFileSync(outPath, gzipSync(Buffer.concat(chunks), { level: 9 }));
|
|
462
|
-
}
|
|
463
297
|
/**
|
|
464
298
|
* 将目录异步流式打包为标准 tar.gz 归档(USTAR 与 gzip 流),避免在内存中积压全量原文件与压缩内容。
|
|
465
299
|
* 严格基于 Node.js 异步流背压机制与错误传播链路实现。
|
|
@@ -482,9 +316,8 @@ export async function createTarGzArchiveAsync(dir, outPath, options) {
|
|
|
482
316
|
const path = `${rootName}/${entry.relPath}${entry.isDir ? "/" : ""}`;
|
|
483
317
|
const fullPath = join(dir, entry.relPath);
|
|
484
318
|
const stat = statSync(fullPath);
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
const header = tarHeader(path, entry.isDir ? 0 : stat.size, Math.floor(stat.mtimeMs / 1000), entry.isDir, isExec);
|
|
319
|
+
const mode = computeUnixMode(entry.relPath, stat.mode, entry.isDir);
|
|
320
|
+
const header = tarHeader(path, entry.isDir ? 0 : stat.size, Math.floor(stat.mtimeMs / 1000), entry.isDir, entry.isDir ? "0000755" : mode === 0o100755);
|
|
488
321
|
await writeToStream(gzip, header);
|
|
489
322
|
if (!entry.isDir && stat.size > 0) {
|
|
490
323
|
const fileStream = getReadStream(fullPath);
|
package/dist/build.d.ts
CHANGED
package/dist/build.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
-
import { STANDALONE_ASYNC_UNSUPPORTED,
|
|
4
|
+
import { STANDALONE_ASYNC_UNSUPPORTED, } from "@actiondock/core";
|
|
5
|
+
import { getPackageSlug } from "@actiondock/core/project";
|
|
5
6
|
import { createZipArchiveAsync } from "./archive.js";
|
|
6
7
|
import { BuilderError } from "./errors.js";
|
|
7
8
|
import { collectRelativeFiles, getInternalDependencyVersion, replaceDirAtomic } from "./fs-utils.js";
|
|
8
9
|
import { assertValidManifestActionIds, serializePlanManifest } from "./manifest.js";
|
|
9
10
|
import { SelectionPlanner } from "./planner.js";
|
|
11
|
+
import { copyPlanEntries } from "./stage-sources.js";
|
|
12
|
+
import { isOwnAction } from "./types.js";
|
|
13
|
+
export { copyPlanEntries } from "./stage-sources.js";
|
|
10
14
|
/**
|
|
11
15
|
* 检查外部依赖中声明的生命周期安装脚本。
|
|
12
16
|
*/
|
|
@@ -67,7 +71,7 @@ function calculateDirectoryDigest(dir) {
|
|
|
67
71
|
*/
|
|
68
72
|
function generateNodeHostEntrySource(plan) {
|
|
69
73
|
// 仅包自有 Action 参与 import 与注册,与产物清单保持同一事实源
|
|
70
|
-
const ownActions = plan.actions.filter((a) =>
|
|
74
|
+
const ownActions = plan.actions.filter((a) => isOwnAction(a));
|
|
71
75
|
const imports = ownActions
|
|
72
76
|
.map((act, idx) => `import action_${idx} from ${JSON.stringify(`./${act.entry.replace(/\\/g, "/")}`)};`)
|
|
73
77
|
.join("\n");
|
|
@@ -101,11 +105,10 @@ function generateNodeHostEntrySource(plan) {
|
|
|
101
105
|
return `#!/usr/bin/env node
|
|
102
106
|
// AUTO-GENERATED HOST ENTRYPOINT BY ACTIONDOCK BUILDER. DO NOT EDIT.
|
|
103
107
|
import {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
serveParentIpc,
|
|
108
|
+
createActionDock,
|
|
109
|
+
createNodePlatform,
|
|
107
110
|
} from "@actiondock/core";
|
|
108
|
-
import {
|
|
111
|
+
import { serveParentIpc } from "@actiondock/core/server";
|
|
109
112
|
${imports}
|
|
110
113
|
|
|
111
114
|
let dataDir;
|
|
@@ -127,7 +130,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
const
|
|
133
|
+
const service = await createActionDock({
|
|
131
134
|
dataDir,
|
|
132
135
|
packages: [
|
|
133
136
|
{
|
|
@@ -147,10 +150,10 @@ const host = await createActionDockHost({
|
|
|
147
150
|
},
|
|
148
151
|
],
|
|
149
152
|
platform: createNodePlatform({ dataDir }),
|
|
153
|
+
autoLoadCurrentProject: false,
|
|
154
|
+
scanLinkedPackages: false,
|
|
150
155
|
});
|
|
151
|
-
|
|
152
|
-
const target = await createActionDockTarget({ type: "local", host });
|
|
153
|
-
await serveParentIpc(target);
|
|
156
|
+
await serveParentIpc(service);
|
|
154
157
|
`;
|
|
155
158
|
}
|
|
156
159
|
/**
|
|
@@ -162,11 +165,13 @@ function generateNodeSupervisorEntrySource(plan) {
|
|
|
162
165
|
import { spawn } from "node:child_process";
|
|
163
166
|
import { join } from "node:path";
|
|
164
167
|
import {
|
|
165
|
-
IpcActionDockTarget,
|
|
166
|
-
StandaloneDispatcher,
|
|
167
|
-
ExitCode,
|
|
168
168
|
STANDALONE_ASYNC_UNSUPPORTED,
|
|
169
169
|
} from "@actiondock/core";
|
|
170
|
+
import {
|
|
171
|
+
ExitCode,
|
|
172
|
+
IpcActionDockService,
|
|
173
|
+
StandaloneDispatcher,
|
|
174
|
+
} from "@actiondock/core/server";
|
|
170
175
|
|
|
171
176
|
const METADATA = {
|
|
172
177
|
packageId: ${JSON.stringify(plan.packageId)},
|
|
@@ -232,7 +237,7 @@ const child = spawn(process.execPath, [hostScript, ...argv], {
|
|
|
232
237
|
});
|
|
233
238
|
|
|
234
239
|
// 4. 标准输出通道物理隔离与受控限流排空
|
|
235
|
-
const
|
|
240
|
+
const service = new IpcActionDockService({
|
|
236
241
|
childProcess: child,
|
|
237
242
|
maxDiagnosticBytes: 512 * 1024,
|
|
238
243
|
maxDiagnosticRate: 64 * 1024,
|
|
@@ -244,7 +249,7 @@ const cleanup = async () => {
|
|
|
244
249
|
if (cleanedUp) return;
|
|
245
250
|
cleanedUp = true;
|
|
246
251
|
try {
|
|
247
|
-
await
|
|
252
|
+
await service.close();
|
|
248
253
|
} catch {}
|
|
249
254
|
};
|
|
250
255
|
|
|
@@ -262,7 +267,7 @@ const dispatcher = new StandaloneDispatcher({
|
|
|
262
267
|
packageId: METADATA.packageId,
|
|
263
268
|
version: METADATA.version,
|
|
264
269
|
description: METADATA.description,
|
|
265
|
-
|
|
270
|
+
service,
|
|
266
271
|
});
|
|
267
272
|
|
|
268
273
|
try {
|
|
@@ -282,45 +287,10 @@ try {
|
|
|
282
287
|
}
|
|
283
288
|
/**
|
|
284
289
|
* 拷贝 Action 源码、Playbook 规程与声明的代码文件/静态资产到暂存目录。
|
|
285
|
-
*
|
|
286
|
-
* 避免产物混入外部源文件与本包入口发生非确定性覆盖。
|
|
290
|
+
* 拷贝内核统一复用 stage-sources 的 copyPlanEntries,无差异化调优时直接全量拷贝。
|
|
287
291
|
*/
|
|
288
|
-
function stageSources(
|
|
289
|
-
|
|
290
|
-
const relativeActionImports = [];
|
|
291
|
-
for (const act of plan.actions) {
|
|
292
|
-
if (act.isExternal || act.id.includes("/")) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
if (existsSync(act.resolvedPath)) {
|
|
296
|
-
const destFile = join(stagingDir, act.entry);
|
|
297
|
-
mkdirSync(dirname(destFile), { recursive: true });
|
|
298
|
-
copyFileSync(act.resolvedPath, destFile);
|
|
299
|
-
relativeActionImports.push(`./${act.entry.replace(/\\/g, "/")}`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
// 拷贝 Playbook 规程文件
|
|
303
|
-
if (plan.playbooks.length > 0) {
|
|
304
|
-
const playbooksDir = plan.playbooksDir || "playbooks";
|
|
305
|
-
const playbooksDestDir = join(stagingDir, playbooksDir);
|
|
306
|
-
mkdirSync(playbooksDestDir, { recursive: true });
|
|
307
|
-
for (const pb of plan.playbooks) {
|
|
308
|
-
if (existsSync(pb.filePath)) {
|
|
309
|
-
const destPb = join(playbooksDestDir, basename(pb.filePath));
|
|
310
|
-
copyFileSync(pb.filePath, destPb);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
// 拷贝声明的代码文件与静态资产
|
|
315
|
-
for (const dep of plan.dependencies.modulesAndAssets) {
|
|
316
|
-
if ((dep.type === "asset" || dep.type === "module" || dep.type === "file") &&
|
|
317
|
-
existsSync(dep.resolvedPath)) {
|
|
318
|
-
const destPath = join(stagingDir, dep.path);
|
|
319
|
-
mkdirSync(dirname(destPath), { recursive: true });
|
|
320
|
-
copyFileSync(dep.resolvedPath, destPath);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return relativeActionImports;
|
|
292
|
+
function stageSources(stagingDir, plan) {
|
|
293
|
+
return copyPlanEntries(stagingDir, plan);
|
|
324
294
|
}
|
|
325
295
|
/**
|
|
326
296
|
* 生成裁剪后的 actiondock.json(项目元数据与清单的单一事实源)。
|
|
@@ -339,7 +309,6 @@ function writePkgJsonAndLockfiles(root, stagingDir, plan, pkgSlug) {
|
|
|
339
309
|
// 准备锁定的生产依赖
|
|
340
310
|
const productionDependencies = {
|
|
341
311
|
"@actiondock/core": getInternalDependencyVersion(),
|
|
342
|
-
"@actiondock/runtime-node": getInternalDependencyVersion(),
|
|
343
312
|
"@actiondock/sdk": getInternalDependencyVersion(),
|
|
344
313
|
};
|
|
345
314
|
for (const ext of plan.dependencies.external) {
|
|
@@ -450,7 +419,7 @@ function vendorDependencies(root, stagingDir, plan) {
|
|
|
450
419
|
}
|
|
451
420
|
}
|
|
452
421
|
// 复制 ActionDock 内部依赖
|
|
453
|
-
for (const internalPkg of ["@actiondock/core", "@actiondock/sdk"
|
|
422
|
+
for (const internalPkg of ["@actiondock/core", "@actiondock/sdk"]) {
|
|
454
423
|
for (const baseModules of sourceCandidates) {
|
|
455
424
|
const srcDep = join(baseModules, internalPkg);
|
|
456
425
|
if (existsSync(srcDep)) {
|
|
@@ -470,10 +439,6 @@ function vendorDependencies(root, stagingDir, plan) {
|
|
|
470
439
|
* @returns 构建产物结果描述
|
|
471
440
|
*/
|
|
472
441
|
export async function buildProject(options) {
|
|
473
|
-
// 彻底删除原有单文件二进制输出语义,明确拒绝并报错
|
|
474
|
-
if (options.target !== undefined || options.bytecode !== undefined) {
|
|
475
|
-
throw new BuilderError("Unsupported build mode: '--target' and '--bytecode' standalone single-file binary compilation have been removed in ActionDock 2.0. Directory-based Node.js builds are now the standard distribution format.", "UNSUPPORTED_BUILD_MODE");
|
|
476
|
-
}
|
|
477
442
|
const root = resolve(options.projectRoot);
|
|
478
443
|
const plan = SelectionPlanner.plan({
|
|
479
444
|
projectRoot: root,
|
|
@@ -512,7 +477,7 @@ export async function buildProject(options) {
|
|
|
512
477
|
mkdirSync(stagingDir, { recursive: true });
|
|
513
478
|
let platformInfo;
|
|
514
479
|
try {
|
|
515
|
-
stageSources(
|
|
480
|
+
stageSources(stagingDir, plan);
|
|
516
481
|
writeManifest(stagingDir, plan);
|
|
517
482
|
writePkgJsonAndLockfiles(root, stagingDir, plan, pkgSlug);
|
|
518
483
|
writeEntrypoints(stagingDir, plan);
|