@actiondock/core 2.0.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 +50 -0
- package/package.json +51 -0
- package/src/build/builder.ts +205 -0
- package/src/build/index.ts +2 -0
- package/src/build/templates.ts +59 -0
- package/src/doctor/doctor.ts +332 -0
- package/src/doctor/index.ts +2 -0
- package/src/doctor/types.ts +25 -0
- package/src/export/index.ts +2 -0
- package/src/export/skill.ts +349 -0
- package/src/export/templates.ts +258 -0
- package/src/filter/index.ts +1 -0
- package/src/filter/intent.ts +154 -0
- package/src/index.ts +13 -0
- package/src/profile/client.ts +302 -0
- package/src/profile/index.ts +3 -0
- package/src/profile/manager.ts +341 -0
- package/src/profile/types.ts +71 -0
- package/src/project/index.ts +3 -0
- package/src/project/init.ts +194 -0
- package/src/project/loader.ts +382 -0
- package/src/project/types.ts +62 -0
- package/src/registry/index.ts +2 -0
- package/src/registry/registry.ts +703 -0
- package/src/registry/types.ts +127 -0
- package/src/runtime/context.ts +232 -0
- package/src/runtime/env.ts +172 -0
- package/src/runtime/execution-manager.ts +74 -0
- package/src/runtime/index.ts +5 -0
- package/src/runtime/runner.ts +368 -0
- package/src/runtime/standalone.ts +429 -0
- package/src/schema/validator.ts +61 -0
- package/src/server/body.ts +112 -0
- package/src/server/index.ts +6 -0
- package/src/server/runtime-registry.ts +80 -0
- package/src/server/security.ts +115 -0
- package/src/server/server.ts +572 -0
- package/src/server/types.ts +42 -0
- package/src/storage/index.ts +64 -0
- package/src/storage/mask.ts +34 -0
- package/src/storage/sqlite.ts +578 -0
- package/src/storage/types.ts +111 -0
- package/src/utils/index.ts +60 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type CheckStatus = "ok" | "warn" | "error";
|
|
2
|
+
|
|
3
|
+
export interface DoctorCheckItem {
|
|
4
|
+
id: string;
|
|
5
|
+
category: "runtime" | "storage" | "registry" | "project";
|
|
6
|
+
name: string;
|
|
7
|
+
status: CheckStatus;
|
|
8
|
+
message: string;
|
|
9
|
+
detail?: string;
|
|
10
|
+
fix?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DoctorReport {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
hasProject: boolean;
|
|
16
|
+
projectRoot?: string;
|
|
17
|
+
packageId?: string;
|
|
18
|
+
summary: {
|
|
19
|
+
total: number;
|
|
20
|
+
ok: number;
|
|
21
|
+
warn: number;
|
|
22
|
+
error: number;
|
|
23
|
+
};
|
|
24
|
+
checks: DoctorCheckItem[];
|
|
25
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { buildProject } from "../build/builder";
|
|
4
|
+
import {
|
|
5
|
+
discoverPlaybookFiles,
|
|
6
|
+
loadActionFileMap,
|
|
7
|
+
loadActions,
|
|
8
|
+
loadPlaybooks,
|
|
9
|
+
loadProjectConfig,
|
|
10
|
+
} from "../project/loader";
|
|
11
|
+
import type { ProjectConfig } from "../project/types";
|
|
12
|
+
import { getPackageSlug } from "../utils";
|
|
13
|
+
import { generateSkillJson, generateSkillMd, generateSourceSkillMd, generateStandaloneSkillMd } from "./templates";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 导出 Agent Skill 产物时的配置选项。
|
|
17
|
+
*/
|
|
18
|
+
export interface ExportSkillOptions {
|
|
19
|
+
/** 源码项目根目录 */
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
/** 导出模式:source (源码模式) 或 standalone (独立二进制模式) */
|
|
22
|
+
mode?: "source" | "standalone";
|
|
23
|
+
/** 是否强制独立二进制模式 */
|
|
24
|
+
standalone?: boolean;
|
|
25
|
+
/** 二进制编译目标架构(默认 host) */
|
|
26
|
+
target?: string;
|
|
27
|
+
/** 导出目录路径(默认 dist/skills/) */
|
|
28
|
+
outDir?: string;
|
|
29
|
+
/** 是否自动压缩打包为 .skill.tar.gz 归档包 */
|
|
30
|
+
archive?: boolean;
|
|
31
|
+
/** 按需挑选的 Playbook 列表(触发 Playbook-driven 最小化依赖 Tree-shaking 导出) */
|
|
32
|
+
playbooks?: string[];
|
|
33
|
+
/** 显式挑选导出的 Action ID 清单 */
|
|
34
|
+
actions?: string[];
|
|
35
|
+
/** 独立模式下是否开启代码混淆压缩 */
|
|
36
|
+
minify?: boolean;
|
|
37
|
+
/** 独立模式下是否编译为字节码 */
|
|
38
|
+
bytecode?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Skill 导出完成后的产物描述结果。
|
|
43
|
+
*/
|
|
44
|
+
export interface ExportSkillResult {
|
|
45
|
+
/** 所属 Package ID */
|
|
46
|
+
packageId: string;
|
|
47
|
+
/** 版本号 */
|
|
48
|
+
version: string;
|
|
49
|
+
/** 导出模式 */
|
|
50
|
+
mode: "source" | "standalone";
|
|
51
|
+
/** 目标架构 */
|
|
52
|
+
target: string;
|
|
53
|
+
/** 生成的 Skill 目录绝对路径 */
|
|
54
|
+
skillDir: string;
|
|
55
|
+
/** 若开启了 archive,生成的 tar.gz 归档文件绝对路径 */
|
|
56
|
+
archivePath?: string;
|
|
57
|
+
/** 导出的 Action 数量 */
|
|
58
|
+
actionsCount: number;
|
|
59
|
+
/** 导出的 Playbook 数量 */
|
|
60
|
+
playbooksCount: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 将 ActionDock 项目导出为兼容各大 AI Agent 平台(如 Claude Desktop / Antigravity / Open-Interpreter 等)的通用 Agent Skill 规范包。
|
|
65
|
+
*
|
|
66
|
+
* 支持两种模式:
|
|
67
|
+
* 1. source 源码模式:导出 SKILL.md + actiondock.json + 源码文件,配合 CLI 直接执行。
|
|
68
|
+
* 2. standalone 独立模式:导出 SKILL.md + 单文件独立二进制可执行文件,目标环境零依赖即跑。
|
|
69
|
+
*
|
|
70
|
+
* 支持 Playbook 驱动的最小化 Tree-shaking 依赖裁剪。
|
|
71
|
+
*
|
|
72
|
+
* @param options 导出配置选项
|
|
73
|
+
* @returns 导出产物详细结果
|
|
74
|
+
*/
|
|
75
|
+
export async function exportSkill(
|
|
76
|
+
options: ExportSkillOptions
|
|
77
|
+
): Promise<ExportSkillResult> {
|
|
78
|
+
const root = resolve(options.projectRoot);
|
|
79
|
+
const config = loadProjectConfig(root);
|
|
80
|
+
const mode: "source" | "standalone" =
|
|
81
|
+
options.standalone || options.mode === "standalone" ? "standalone" : "source";
|
|
82
|
+
const target = options.target || "host";
|
|
83
|
+
|
|
84
|
+
const actionsMap = await loadActions(root, config.actionsDir);
|
|
85
|
+
const playbooksMap = loadPlaybooks(root, config.playbooksDir);
|
|
86
|
+
|
|
87
|
+
// Map each action ID to its corresponding source file path
|
|
88
|
+
const actionFileMap = await loadActionFileMap(root, config.actionsDir);
|
|
89
|
+
|
|
90
|
+
let selectedPlaybooks = Array.from(playbooksMap.values());
|
|
91
|
+
let selectedActions = Array.from(actionsMap.values());
|
|
92
|
+
|
|
93
|
+
// 1. If playbooks are explicitly specified (Playbook-driven minimal export)
|
|
94
|
+
if (options.playbooks && options.playbooks.length > 0) {
|
|
95
|
+
const specifiedPlaybookIds = new Set(options.playbooks);
|
|
96
|
+
const pbList = [];
|
|
97
|
+
for (const id of specifiedPlaybookIds) {
|
|
98
|
+
const pb = playbooksMap.get(id);
|
|
99
|
+
if (!pb) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Playbook '${id}' specified in export options not found in project`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
pbList.push(pb);
|
|
105
|
+
}
|
|
106
|
+
selectedPlaybooks = pbList;
|
|
107
|
+
|
|
108
|
+
// If actions were not explicitly specified, derive required actions from selected playbooks
|
|
109
|
+
if (!options.actions || options.actions.length === 0) {
|
|
110
|
+
const requiredActions = new Set<string>();
|
|
111
|
+
for (const pb of selectedPlaybooks) {
|
|
112
|
+
if (pb.actions) {
|
|
113
|
+
for (const act of pb.actions) {
|
|
114
|
+
requiredActions.add(act);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (requiredActions.size > 0) {
|
|
119
|
+
for (const actId of requiredActions) {
|
|
120
|
+
if (!actionsMap.has(actId)) {
|
|
121
|
+
console.warn(
|
|
122
|
+
`[WARN] Action '${actId}' referenced in playbook is not found in project`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
selectedActions = Array.from(actionsMap.values()).filter((a) =>
|
|
127
|
+
requiredActions.has(a.id)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 2. If actions are explicitly specified (Action-driven export)
|
|
134
|
+
if (options.actions && options.actions.length > 0) {
|
|
135
|
+
const specifiedActionIds = new Set(options.actions);
|
|
136
|
+
for (const actId of specifiedActionIds) {
|
|
137
|
+
if (!actionsMap.has(actId)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Action '${actId}' specified in export options not found in project`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
selectedActions = Array.from(actionsMap.values()).filter((a) =>
|
|
144
|
+
specifiedActionIds.has(a.id)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// If playbooks were not explicitly specified, tree-shake playbooks whose required actions are not included
|
|
148
|
+
if (!options.playbooks || options.playbooks.length === 0) {
|
|
149
|
+
selectedPlaybooks = selectedPlaybooks.filter((pb) => {
|
|
150
|
+
if (!pb.actions || pb.actions.length === 0) return true;
|
|
151
|
+
return pb.actions.every((a) => specifiedActionIds.has(a));
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const pkgSlug = getPackageSlug(config.id);
|
|
157
|
+
|
|
158
|
+
const targetSuffix = mode === "standalone" && target !== "host" ? `-${target}` : "";
|
|
159
|
+
const skillFolderName = `${pkgSlug}-skill${targetSuffix}`;
|
|
160
|
+
const defaultSkillDir = join(root, "dist", skillFolderName);
|
|
161
|
+
const skillDir = resolve(options.outDir || defaultSkillDir);
|
|
162
|
+
|
|
163
|
+
const playbooksDestDir = join(skillDir, "playbooks");
|
|
164
|
+
|
|
165
|
+
if (mode === "source") {
|
|
166
|
+
// ----------------------------------------------------
|
|
167
|
+
// SOURCE SKILL EXPORT (Default)
|
|
168
|
+
// Structure: SKILL.md + actiondock.json + package.json + actions/* + playbooks/*
|
|
169
|
+
// ----------------------------------------------------
|
|
170
|
+
const actionsDestDir = join(skillDir, "actions");
|
|
171
|
+
mkdirSync(actionsDestDir, { recursive: true });
|
|
172
|
+
if (selectedPlaybooks.length > 0) {
|
|
173
|
+
mkdirSync(playbooksDestDir, { recursive: true });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 1. Generate SKILL.md for Source Package
|
|
177
|
+
const skillMd = generateSourceSkillMd(
|
|
178
|
+
config,
|
|
179
|
+
selectedActions,
|
|
180
|
+
selectedPlaybooks
|
|
181
|
+
);
|
|
182
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
183
|
+
|
|
184
|
+
// 2. Export tailored actiondock.json
|
|
185
|
+
const exportedConfig: Partial<ProjectConfig> = {
|
|
186
|
+
id: config.id,
|
|
187
|
+
name: config.name,
|
|
188
|
+
version: config.version,
|
|
189
|
+
description: config.description,
|
|
190
|
+
actionsDir: "actions",
|
|
191
|
+
playbooksDir: "playbooks",
|
|
192
|
+
};
|
|
193
|
+
if (config.config) {
|
|
194
|
+
exportedConfig.config = config.config;
|
|
195
|
+
}
|
|
196
|
+
writeFileSync(
|
|
197
|
+
join(skillDir, "actiondock.json"),
|
|
198
|
+
JSON.stringify(exportedConfig, null, 2) + "\n",
|
|
199
|
+
"utf-8"
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// 3. Export package.json
|
|
203
|
+
const projectPkgJsonPath = join(root, "package.json");
|
|
204
|
+
if (existsSync(projectPkgJsonPath)) {
|
|
205
|
+
try {
|
|
206
|
+
const rawPkg = readFileSync(projectPkgJsonPath, "utf-8");
|
|
207
|
+
const parsedPkg = JSON.parse(rawPkg);
|
|
208
|
+
const exportedPkg = {
|
|
209
|
+
name: parsedPkg.name || pkgSlug,
|
|
210
|
+
version: config.version || parsedPkg.version || "0.1.0",
|
|
211
|
+
description: config.description || parsedPkg.description,
|
|
212
|
+
type: "module",
|
|
213
|
+
dependencies: parsedPkg.dependencies || {
|
|
214
|
+
"@actiondock/sdk": "^2.0.0",
|
|
215
|
+
},
|
|
216
|
+
devDependencies: parsedPkg.devDependencies,
|
|
217
|
+
};
|
|
218
|
+
writeFileSync(
|
|
219
|
+
join(skillDir, "package.json"),
|
|
220
|
+
JSON.stringify(exportedPkg, null, 2) + "\n",
|
|
221
|
+
"utf-8"
|
|
222
|
+
);
|
|
223
|
+
} catch {
|
|
224
|
+
copyFileSync(projectPkgJsonPath, join(skillDir, "package.json"));
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
const minimalPkg = {
|
|
228
|
+
name: pkgSlug,
|
|
229
|
+
version: config.version,
|
|
230
|
+
description: config.description,
|
|
231
|
+
type: "module",
|
|
232
|
+
dependencies: {
|
|
233
|
+
"@actiondock/sdk": "^2.0.0",
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
writeFileSync(
|
|
237
|
+
join(skillDir, "package.json"),
|
|
238
|
+
JSON.stringify(minimalPkg, null, 2) + "\n",
|
|
239
|
+
"utf-8"
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 4. Copy tsconfig.json if available
|
|
244
|
+
const tsconfigPath = join(root, "tsconfig.json");
|
|
245
|
+
if (existsSync(tsconfigPath)) {
|
|
246
|
+
copyFileSync(tsconfigPath, join(skillDir, "tsconfig.json"));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 5. Copy selected action source files
|
|
250
|
+
for (const act of selectedActions) {
|
|
251
|
+
const entry = actionFileMap.get(act.id);
|
|
252
|
+
const srcPath = entry?.filePath;
|
|
253
|
+
if (srcPath && existsSync(srcPath)) {
|
|
254
|
+
copyFileSync(srcPath, join(actionsDestDir, basename(srcPath)));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 6. Copy selected playbooks
|
|
259
|
+
for (const pb of selectedPlaybooks) {
|
|
260
|
+
if (pb.filePath && existsSync(pb.filePath)) {
|
|
261
|
+
const filename = basename(pb.filePath);
|
|
262
|
+
copyFileSync(pb.filePath, join(playbooksDestDir, filename));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
// ----------------------------------------------------
|
|
267
|
+
// STANDALONE SKILL EXPORT (--standalone)
|
|
268
|
+
// Structure: SKILL.md + actiondock.skill.json + bin/<binary> + playbooks/*
|
|
269
|
+
// ----------------------------------------------------
|
|
270
|
+
const binDir = join(skillDir, "bin");
|
|
271
|
+
mkdirSync(binDir, { recursive: true });
|
|
272
|
+
if (selectedPlaybooks.length > 0) {
|
|
273
|
+
mkdirSync(playbooksDestDir, { recursive: true });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const binaryName = pkgSlug;
|
|
277
|
+
const binaryPath = join(binDir, binaryName);
|
|
278
|
+
|
|
279
|
+
// Build standalone binary with selected actions
|
|
280
|
+
const buildRes = await buildProject({
|
|
281
|
+
projectRoot: root,
|
|
282
|
+
target: options.target,
|
|
283
|
+
outfile: binaryPath,
|
|
284
|
+
actions: selectedActions.map((a) => a.id),
|
|
285
|
+
minify: options.minify,
|
|
286
|
+
bytecode: options.bytecode,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const actualBinaryName = basename(buildRes.executablePath);
|
|
290
|
+
|
|
291
|
+
// Generate SKILL.md for Standalone Binary
|
|
292
|
+
const skillMd = generateStandaloneSkillMd(
|
|
293
|
+
config,
|
|
294
|
+
selectedActions,
|
|
295
|
+
selectedPlaybooks,
|
|
296
|
+
`./bin/${actualBinaryName}`
|
|
297
|
+
);
|
|
298
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
299
|
+
|
|
300
|
+
// Generate actiondock.skill.json
|
|
301
|
+
const skillJson = generateSkillJson(
|
|
302
|
+
config,
|
|
303
|
+
selectedActions,
|
|
304
|
+
actualBinaryName,
|
|
305
|
+
target
|
|
306
|
+
);
|
|
307
|
+
writeFileSync(join(skillDir, "actiondock.skill.json"), skillJson, "utf-8");
|
|
308
|
+
|
|
309
|
+
// Copy selected playbooks
|
|
310
|
+
for (const pb of selectedPlaybooks) {
|
|
311
|
+
if (pb.filePath && existsSync(pb.filePath)) {
|
|
312
|
+
const filename = basename(pb.filePath);
|
|
313
|
+
copyFileSync(pb.filePath, join(playbooksDestDir, filename));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
let archivePath: string | undefined;
|
|
319
|
+
if (options.archive) {
|
|
320
|
+
const zipName = `${skillFolderName}.zip`;
|
|
321
|
+
archivePath = join(dirname(skillDir), zipName);
|
|
322
|
+
const zipProc = Bun.spawnSync(
|
|
323
|
+
["zip", "-r", archivePath, basename(skillDir)],
|
|
324
|
+
{
|
|
325
|
+
cwd: dirname(skillDir),
|
|
326
|
+
stdout: "pipe",
|
|
327
|
+
stderr: "pipe",
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
if (zipProc.exitCode !== 0) {
|
|
331
|
+
console.warn(
|
|
332
|
+
`[WARN] Failed to create zip archive: ${zipProc.stderr.toString()}`
|
|
333
|
+
);
|
|
334
|
+
archivePath = undefined;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
packageId: config.id,
|
|
340
|
+
version: config.version,
|
|
341
|
+
mode,
|
|
342
|
+
target,
|
|
343
|
+
skillDir,
|
|
344
|
+
archivePath,
|
|
345
|
+
actionsCount: selectedActions.length,
|
|
346
|
+
playbooksCount: selectedPlaybooks.length,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import type { ActionDefinition } from "@actiondock/sdk";
|
|
3
|
+
import type { PlaybookDefinition, ProjectConfig } from "../project/types";
|
|
4
|
+
|
|
5
|
+
function getCleanSkillMetadata(config: ProjectConfig) {
|
|
6
|
+
const cleanName = config.id.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
7
|
+
const desc = config.description || `AI Agent skill for ${config.name} (${config.id})`;
|
|
8
|
+
return { cleanName, desc };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function renderActionListMarkdown(
|
|
12
|
+
actions: ActionDefinition[],
|
|
13
|
+
options: { packageId?: string } = {}
|
|
14
|
+
): string {
|
|
15
|
+
return actions
|
|
16
|
+
.map((a) => {
|
|
17
|
+
const aDesc = a.description ? ` - ${a.description}` : "";
|
|
18
|
+
let params = "";
|
|
19
|
+
if (
|
|
20
|
+
a.inputSchema &&
|
|
21
|
+
typeof a.inputSchema === "object" &&
|
|
22
|
+
(a.inputSchema as any).properties
|
|
23
|
+
) {
|
|
24
|
+
const props = Object.keys((a.inputSchema as any).properties);
|
|
25
|
+
const req = (a.inputSchema as any).required || [];
|
|
26
|
+
params = `\n - 参数列表: ${props
|
|
27
|
+
.map((p) => (req.includes(p) ? `\`${p}\` (必填)` : `\`${p}\``))
|
|
28
|
+
.join(", ")}`;
|
|
29
|
+
}
|
|
30
|
+
const idLabel = options.packageId
|
|
31
|
+
? `\`${options.packageId}/${a.id}\` (或 \`${a.id}\`)`
|
|
32
|
+
: `\`${a.id}\``;
|
|
33
|
+
return `* ${idLabel}${aDesc}${params}`;
|
|
34
|
+
})
|
|
35
|
+
.join("\n");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderPlaybookSectionMarkdown(playbooks: PlaybookDefinition[]): string {
|
|
39
|
+
if (playbooks.length === 0) return "";
|
|
40
|
+
const list = playbooks
|
|
41
|
+
.map((p) => {
|
|
42
|
+
const rel = `./playbooks/${basename(p.filePath)}`;
|
|
43
|
+
return `* **${p.id}** (\`${rel}\`): ${p.description || "任务指南"}`;
|
|
44
|
+
})
|
|
45
|
+
.join("\n");
|
|
46
|
+
return `
|
|
47
|
+
## 任务指南 (Playbook SOPs)
|
|
48
|
+
|
|
49
|
+
Playbook 为复杂任务提供逐步操作规程。详细 SOP 请阅读对应 Markdown 文档:
|
|
50
|
+
|
|
51
|
+
${list}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function generateSourceSkillMd(
|
|
56
|
+
config: ProjectConfig,
|
|
57
|
+
actions: ActionDefinition[],
|
|
58
|
+
playbooks: PlaybookDefinition[]
|
|
59
|
+
): string {
|
|
60
|
+
const { cleanName, desc } = getCleanSkillMetadata(config);
|
|
61
|
+
const pkgId = config.id;
|
|
62
|
+
const firstAction = actions[0]?.id || "sample.greet";
|
|
63
|
+
|
|
64
|
+
const actionListMd = renderActionListMarkdown(actions, { packageId: pkgId });
|
|
65
|
+
const playbookSection = renderPlaybookSectionMarkdown(playbooks);
|
|
66
|
+
|
|
67
|
+
return `---
|
|
68
|
+
name: ${cleanName}
|
|
69
|
+
description: ${desc}
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
# ${config.name} (${config.id})
|
|
73
|
+
|
|
74
|
+
${desc}
|
|
75
|
+
|
|
76
|
+
## ActionDock 运行时 (ActionDock Runtime)
|
|
77
|
+
|
|
78
|
+
本技能为 **ActionDock 源码型 Package (Source Skill)**。AI Agent 可直接通过已安装的 ActionDock 命令行工具 (\`ac\`) 执行其中的 Action。
|
|
79
|
+
|
|
80
|
+
### 注册与链接 (Idempotent Setup)
|
|
81
|
+
|
|
82
|
+
在初次调用或初始化时,将包含本 \`SKILL.md\` 的目录解析为 \`<skill_root>\` 并完成注册:
|
|
83
|
+
|
|
84
|
+
\`\`\`bash
|
|
85
|
+
ac link "<skill_root>"
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
> \`ac link\` 天然具备幂等性,同一 Package 多次执行会直接更新路径,可安全重复调用。
|
|
89
|
+
|
|
90
|
+
### 执行 Action (统一推荐 Package-Qualified ID)
|
|
91
|
+
|
|
92
|
+
为避免多技能之间的 Action ID 命名冲突,建议统一使用带有 Package 前缀的完全限定 ID:
|
|
93
|
+
|
|
94
|
+
\`\`\`bash
|
|
95
|
+
# 格式:ac run <package-id>/<action-id> --input '<json>'
|
|
96
|
+
ac run ${pkgId}/${firstAction} --input '{"param": "value"}'
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
> **免注册本地执行 (Direct Execution Alternative)**:
|
|
100
|
+
> 若 Agent 工作目录已位于本 Skill 根目录,亦可直接免 link 执行:
|
|
101
|
+
> \`\`\`bash
|
|
102
|
+
> cd <skill_root>
|
|
103
|
+
> ac run <action-id> --input '<json>'
|
|
104
|
+
> \`\`\`
|
|
105
|
+
|
|
106
|
+
所有 Action 执行结果均在 \`stdout\` 输出标准格式的 JSON Envelope:
|
|
107
|
+
\`\`\`json
|
|
108
|
+
{
|
|
109
|
+
"ok": true,
|
|
110
|
+
"runId": "01J...",
|
|
111
|
+
"data": { ... }
|
|
112
|
+
}
|
|
113
|
+
\`\`\`
|
|
114
|
+
日志与诊断信息输出至 \`stderr\`。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Action 目录
|
|
119
|
+
|
|
120
|
+
${actionListMd}
|
|
121
|
+
${playbookSection}
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 运行时配置与持久化状态
|
|
125
|
+
|
|
126
|
+
如需检查或配置该 Package 的运行时参数与持久化数据:
|
|
127
|
+
|
|
128
|
+
\`\`\`bash
|
|
129
|
+
# 查看与设置配置项
|
|
130
|
+
ac config list --package ${pkgId}
|
|
131
|
+
ac config set KEY VALUE --package ${pkgId}
|
|
132
|
+
|
|
133
|
+
# 查看与检索状态数据
|
|
134
|
+
ac state list --package ${pkgId}
|
|
135
|
+
ac state get KEY --package ${pkgId}
|
|
136
|
+
\`\`\`
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function generateStandaloneSkillMd(
|
|
141
|
+
config: ProjectConfig,
|
|
142
|
+
actions: ActionDefinition[],
|
|
143
|
+
playbooks: PlaybookDefinition[],
|
|
144
|
+
binaryRelPath = "./bin/action-bin"
|
|
145
|
+
): string {
|
|
146
|
+
const { cleanName, desc } = getCleanSkillMetadata(config);
|
|
147
|
+
const firstAction = actions[0]?.id || "sample.greet";
|
|
148
|
+
|
|
149
|
+
const actionListMd = renderActionListMarkdown(actions);
|
|
150
|
+
const playbookSection = renderPlaybookSectionMarkdown(playbooks);
|
|
151
|
+
|
|
152
|
+
return `---
|
|
153
|
+
name: ${cleanName}
|
|
154
|
+
description: ${desc}
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
# ${config.name} (${config.id})
|
|
158
|
+
|
|
159
|
+
${desc}
|
|
160
|
+
|
|
161
|
+
## 如何调用 Action
|
|
162
|
+
|
|
163
|
+
使用 Skill 目录中自带的独立可执行文件 \`${binaryRelPath}\` 即可完成工具发现与调用。
|
|
164
|
+
**该工具无需在系统预先安装任何依赖**(无需安装 Node.js、Bun、Python 或 Java)。
|
|
165
|
+
|
|
166
|
+
### 发现可用 Action 清单
|
|
167
|
+
\`\`\`bash
|
|
168
|
+
${binaryRelPath} list --json
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
### 查看 Action 结构与入参 Schema
|
|
172
|
+
\`\`\`bash
|
|
173
|
+
${binaryRelPath} describe <action-id> --json
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
### 执行 Action
|
|
177
|
+
\`\`\`bash
|
|
178
|
+
${binaryRelPath} run <action-id> --input '{"param": "value"}'
|
|
179
|
+
|
|
180
|
+
# 示例:
|
|
181
|
+
${binaryRelPath} run ${firstAction} --input '{"param": "value"}'
|
|
182
|
+
\`\`\`
|
|
183
|
+
|
|
184
|
+
所有 Action 执行结果均在 \`stdout\` 输出标准格式的 JSON 结果:
|
|
185
|
+
\`\`\`json
|
|
186
|
+
{
|
|
187
|
+
"ok": true,
|
|
188
|
+
"runId": "01J...",
|
|
189
|
+
"data": { ... }
|
|
190
|
+
}
|
|
191
|
+
\`\`\`
|
|
192
|
+
日志与诊断信息输出至 \`stderr\`。
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Action 目录
|
|
197
|
+
|
|
198
|
+
${actionListMd}
|
|
199
|
+
${playbookSection}
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 运行时配置与持久化状态
|
|
203
|
+
|
|
204
|
+
独立二进制会自动管理其本地 SQLite 数据库。如需检查或配置:
|
|
205
|
+
|
|
206
|
+
\`\`\`bash
|
|
207
|
+
# 查看与设置配置项
|
|
208
|
+
${binaryRelPath} config list
|
|
209
|
+
${binaryRelPath} config set KEY VALUE
|
|
210
|
+
|
|
211
|
+
# 查看与检索状态数据
|
|
212
|
+
${binaryRelPath} state list
|
|
213
|
+
${binaryRelPath} state get KEY
|
|
214
|
+
\`\`\`
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function generateSkillMd(
|
|
219
|
+
config: ProjectConfig,
|
|
220
|
+
actions: ActionDefinition[],
|
|
221
|
+
playbooks: PlaybookDefinition[],
|
|
222
|
+
optionsOrBinaryPath: string | { mode?: "source" | "standalone"; binaryRelPath?: string } = "./bin/action-bin"
|
|
223
|
+
): string {
|
|
224
|
+
if (typeof optionsOrBinaryPath === "string") {
|
|
225
|
+
return generateStandaloneSkillMd(config, actions, playbooks, optionsOrBinaryPath);
|
|
226
|
+
}
|
|
227
|
+
if (optionsOrBinaryPath.mode === "source") {
|
|
228
|
+
return generateSourceSkillMd(config, actions, playbooks);
|
|
229
|
+
}
|
|
230
|
+
return generateStandaloneSkillMd(config, actions, playbooks, optionsOrBinaryPath.binaryRelPath || "./bin/action-bin");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function generateSkillJson(
|
|
234
|
+
config: ProjectConfig,
|
|
235
|
+
actions: ActionDefinition[],
|
|
236
|
+
binaryName: string,
|
|
237
|
+
target: string
|
|
238
|
+
): string {
|
|
239
|
+
const manifest = {
|
|
240
|
+
schemaVersion: "2.0.0",
|
|
241
|
+
packageId: config.id,
|
|
242
|
+
name: config.name,
|
|
243
|
+
version: config.version,
|
|
244
|
+
description: config.description,
|
|
245
|
+
target,
|
|
246
|
+
executable: `./bin/${binaryName}`,
|
|
247
|
+
actions: actions.map((a) => ({
|
|
248
|
+
id: a.id,
|
|
249
|
+
description: a.description,
|
|
250
|
+
inputSchema: a.inputSchema,
|
|
251
|
+
outputSchema: a.outputSchema,
|
|
252
|
+
})),
|
|
253
|
+
exportedAt: new Date().toISOString(),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return JSON.stringify(manifest, null, 2) + "\n";
|
|
257
|
+
}
|
|
258
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./intent";
|