@heybox/hb-sdk 0.5.9 → 0.5.10
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/cli-chunks/{create-zJEmBOvP.cjs → create-D1W2pkHZ.cjs} +1 -1
- package/dist/cli-chunks/{dev-DsqPSgeJ.cjs → dev-DwbE-c1x.cjs} +1 -1
- package/dist/cli-chunks/{doctor-DZkEwLT9.cjs → doctor-Bx71WFjM.cjs} +1 -1
- package/dist/cli-chunks/{index-BpVGoWHu.cjs → index-BBatNb1l.cjs} +13 -13
- package/dist/cli-chunks/{index-CU-8dVKo.cjs → index-D5bZjpH9.cjs} +2 -2
- package/dist/cli-chunks/{login-DhKNZpl4.cjs → login-BX0YmVyi.cjs} +2 -2
- package/dist/cli-chunks/{remote-D0izsd5c.cjs → remote-YDmSxyYi.cjs} +3 -3
- package/dist/cli-chunks/{session-O_NdH1Z2.cjs → session-BxZBXDZ-.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/package.json +3 -1
- package/skill/SKILL.md +5 -3
- package/skill/references/llms-index.md +1 -0
- package/skill/scripts/sync-agent-skills-payload.mjs +359 -0
- package/skill/skill.json +2 -2
|
@@ -5,7 +5,7 @@ var fs = require('node:fs/promises');
|
|
|
5
5
|
var path = require('node:path');
|
|
6
6
|
var require$$0 = require('fs');
|
|
7
7
|
var require$$1 = require('path');
|
|
8
|
-
var index = require('./index-
|
|
8
|
+
var index = require('./index-BBatNb1l.cjs');
|
|
9
9
|
require('node:module');
|
|
10
10
|
require('os');
|
|
11
11
|
require('readline');
|
|
@@ -9,7 +9,7 @@ var node_url = require('node:url');
|
|
|
9
9
|
var net = require('node:net');
|
|
10
10
|
var node_http = require('node:http');
|
|
11
11
|
var browser = require('./browser-RAy8e8cV.cjs');
|
|
12
|
-
var index = require('./index-
|
|
12
|
+
var index = require('./index-BBatNb1l.cjs');
|
|
13
13
|
require('node:process');
|
|
14
14
|
require('node:buffer');
|
|
15
15
|
require('node:util');
|
|
@@ -4,7 +4,7 @@ var fs$1 = require('node:fs');
|
|
|
4
4
|
var fs = require('node:fs/promises');
|
|
5
5
|
var os = require('node:os');
|
|
6
6
|
var path = require('node:path');
|
|
7
|
-
var index = require('./index-
|
|
7
|
+
var index = require('./index-BBatNb1l.cjs');
|
|
8
8
|
require('node:module');
|
|
9
9
|
require('path');
|
|
10
10
|
require('os');
|
|
@@ -234,19 +234,19 @@ function requireArgument () {
|
|
|
234
234
|
|
|
235
235
|
var command = {};
|
|
236
236
|
|
|
237
|
-
const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-
|
|
237
|
+
const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BBatNb1l.cjs', document.baseURI).href)));
|
|
238
238
|
function __require$4() { return require$5("node:events"); }
|
|
239
239
|
|
|
240
|
-
const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-
|
|
240
|
+
const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BBatNb1l.cjs', document.baseURI).href)));
|
|
241
241
|
function __require$3() { return require$4("node:child_process"); }
|
|
242
242
|
|
|
243
|
-
const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-
|
|
243
|
+
const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BBatNb1l.cjs', document.baseURI).href)));
|
|
244
244
|
function __require$2() { return require$3("node:path"); }
|
|
245
245
|
|
|
246
|
-
const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-
|
|
246
|
+
const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BBatNb1l.cjs', document.baseURI).href)));
|
|
247
247
|
function __require$1() { return require$2("node:fs"); }
|
|
248
248
|
|
|
249
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-
|
|
249
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-BBatNb1l.cjs', document.baseURI).href)));
|
|
250
250
|
function __require() { return require$1("node:process"); }
|
|
251
251
|
|
|
252
252
|
var help = {};
|
|
@@ -13094,7 +13094,7 @@ function readErrorMessage(error, options = {}) {
|
|
|
13094
13094
|
}
|
|
13095
13095
|
|
|
13096
13096
|
const CLI_VERSION_PLACEHOLDER = ['__HB', 'SDK', 'CLI', 'VERSION__'].join('_');
|
|
13097
|
-
const BUILT_CLI_VERSION = '0.5.
|
|
13097
|
+
const BUILT_CLI_VERSION = '0.5.10';
|
|
13098
13098
|
const PACKAGE_JSON_CANDIDATES = [
|
|
13099
13099
|
path.resolve(__dirname, '..', '..', 'package.json'),
|
|
13100
13100
|
path.resolve(__dirname, '..', 'package.json'),
|
|
@@ -13374,31 +13374,31 @@ function createCommandLoggerResolver(options) {
|
|
|
13374
13374
|
};
|
|
13375
13375
|
}
|
|
13376
13376
|
const defaultClearLoginStatus = async (...args) => {
|
|
13377
|
-
const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-
|
|
13377
|
+
const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-BX0YmVyi.cjs'); });
|
|
13378
13378
|
return clearLoginStatus(...args);
|
|
13379
13379
|
};
|
|
13380
13380
|
const defaultLoginToHeybox = async (...args) => {
|
|
13381
|
-
const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-
|
|
13381
|
+
const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-BX0YmVyi.cjs'); });
|
|
13382
13382
|
return loginToHeybox(...args);
|
|
13383
13383
|
};
|
|
13384
13384
|
const defaultPrintLoginStatus = async (...args) => {
|
|
13385
|
-
const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-
|
|
13385
|
+
const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-BX0YmVyi.cjs'); });
|
|
13386
13386
|
return printLoginStatus(...args);
|
|
13387
13387
|
};
|
|
13388
13388
|
const defaultRunCreateCommand = async (...args) => {
|
|
13389
|
-
const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-
|
|
13389
|
+
const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-D1W2pkHZ.cjs'); });
|
|
13390
13390
|
return runCreateCommand(...args);
|
|
13391
13391
|
};
|
|
13392
13392
|
const defaultRunDevCommand = async (...args) => {
|
|
13393
|
-
const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-
|
|
13393
|
+
const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-DwbE-c1x.cjs'); });
|
|
13394
13394
|
return runDevCommand(...args);
|
|
13395
13395
|
};
|
|
13396
13396
|
const defaultRunDoctorCommand = async (...args) => {
|
|
13397
|
-
const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-
|
|
13397
|
+
const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-Bx71WFjM.cjs'); });
|
|
13398
13398
|
return runDoctorCommand(...args);
|
|
13399
13399
|
};
|
|
13400
13400
|
const defaultRunRemoteCommand = async (...args) => {
|
|
13401
|
-
const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-
|
|
13401
|
+
const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-YDmSxyYi.cjs'); }).then(function (n) { return n.remote; });
|
|
13402
13402
|
return runRemoteCommand(...args);
|
|
13403
13403
|
};
|
|
13404
13404
|
function resolveStandaloneLogger(options, verbose) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index$2 = require('./index-
|
|
3
|
+
var index$2 = require('./index-BBatNb1l.cjs');
|
|
4
4
|
var require$$0$2 = require('fs');
|
|
5
5
|
var require$$2$1 = require('crypto');
|
|
6
6
|
var require$$1$2 = require('path');
|
|
7
7
|
var require$$0$3 = require('assert');
|
|
8
8
|
var require$$4$2 = require('events');
|
|
9
9
|
var require$$1$1 = require('util');
|
|
10
|
-
var remote = require('./remote-
|
|
10
|
+
var remote = require('./remote-YDmSxyYi.cjs');
|
|
11
11
|
var require$$0$5 = require('net');
|
|
12
12
|
var require$$0$4 = require('url');
|
|
13
13
|
var require$$2$2 = require('http');
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
var promises = require('node:readline/promises');
|
|
4
4
|
var node_crypto = require('node:crypto');
|
|
5
5
|
var node_http = require('node:http');
|
|
6
|
-
var session = require('./session-
|
|
6
|
+
var session = require('./session-BxZBXDZ-.cjs');
|
|
7
7
|
var browser = require('./browser-RAy8e8cV.cjs');
|
|
8
|
-
var index = require('./index-
|
|
8
|
+
var index = require('./index-BBatNb1l.cjs');
|
|
9
9
|
require('node:path');
|
|
10
10
|
require('fs');
|
|
11
11
|
require('constants');
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var promises = require('node:readline/promises');
|
|
4
|
-
var session = require('./session-
|
|
4
|
+
var session = require('./session-BxZBXDZ-.cjs');
|
|
5
5
|
var childProcess = require('node:child_process');
|
|
6
6
|
var fs = require('node:fs');
|
|
7
7
|
var fs$1 = require('node:fs/promises');
|
|
8
8
|
var path = require('node:path');
|
|
9
|
-
var index = require('./index-
|
|
9
|
+
var index = require('./index-BBatNb1l.cjs');
|
|
10
10
|
|
|
11
11
|
var re = {exports: {}};
|
|
12
12
|
|
|
@@ -3111,7 +3111,7 @@ async function createDefaultCosClient(uploadToken) {
|
|
|
3111
3111
|
};
|
|
3112
3112
|
}
|
|
3113
3113
|
async function loadCosConstructor() {
|
|
3114
|
-
const cosModule = await Promise.resolve().then(function () { return require('./index-
|
|
3114
|
+
const cosModule = await Promise.resolve().then(function () { return require('./index-D5bZjpH9.cjs'); }).then(function (n) { return n.index; });
|
|
3115
3115
|
return cosModule.default;
|
|
3116
3116
|
}
|
|
3117
3117
|
function formatSize(bytes) {
|
package/dist/cli.cjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heybox/hb-sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
4
4
|
"description": "",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -117,6 +117,8 @@
|
|
|
117
117
|
"build:types": "tsc -p tsconfig.dts.json",
|
|
118
118
|
"check:boundary": "node scripts/check-boundary.cjs",
|
|
119
119
|
"check:docs-sync": "node scripts/check-docs-sync.cjs",
|
|
120
|
+
"sync:agent-skills-payload": "node skill/scripts/sync-agent-skills-payload.mjs",
|
|
121
|
+
"check:agent-skills-payload": "node skill/scripts/sync-agent-skills-payload.mjs --check",
|
|
120
122
|
"clean": "rimraf ./dist && rimraf ./types",
|
|
121
123
|
"test:unit": "NODE_OPTIONS='--conditions=heybox' vitest run",
|
|
122
124
|
"test:unit:coverage": "NODE_OPTIONS='--conditions=heybox' vitest run --coverage",
|
package/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hb-sdk
|
|
3
|
-
description: Uses @heybox/hb-sdk and its hb-sdk CLI for Heybox external mini-program iframe pages, local mock development, template creation, skill version checks,
|
|
3
|
+
description: Uses @heybox/hb-sdk and its hb-sdk CLI for Heybox external mini-program iframe pages, local mock development, template creation, skill version checks, host/runtime protocol integration, and mini-program listing/audit/compliance review. Use for ready/auth/user/share/viewport/storage/network APIs, lifecycle events, hb-sdk create/dev/login/doctor, mock runtime debugging, custom SDK instances, or @heybox/hb-sdk/protocol contracts. Don't use for raw postMessage bridge construction in app code, direct cookie/token access, internal Heybox client protocols, separate mock runtimes, or non-Heybox SDKs.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hb-sdk Agent Procedure
|
|
@@ -12,8 +12,9 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
12
12
|
1. If the task is iframe mini-program business code, use the root package import path `@heybox/hb-sdk`.
|
|
13
13
|
2. If the task is parent-container runtime, bridge-server, protocol contract, or `@heybox/hb-sdk-runtime` work, use `@heybox/hb-sdk/protocol` only for shared constants and types.
|
|
14
14
|
3. If the task is project scaffolding, local Vite startup, browser mock runtime, CLI login cache, Agent Skill version diagnosis, or CLI troubleshooting, use the `hb-sdk` CLI workflow.
|
|
15
|
-
4. If the task
|
|
16
|
-
5. If the task
|
|
15
|
+
4. If the task is reviewing a mini-program for submission, listing, audit, publishing, content compliance, data/privacy compliance, runtime quality, or icon/cover design requirements, use the online publishing rules workflow.
|
|
16
|
+
5. If the task asks for direct login-state extraction, cookies, tokens, raw Heybox client protocols, or internal hb-sdk package paths, refuse that approach and use the public SDK or CLI boundary instead.
|
|
17
|
+
6. If the task is not about Heybox external mini-program SDK usage, CLI usage, mock runtime, protocol contracts, or mini-program listing/audit/compliance review, do not apply this skill.
|
|
17
18
|
|
|
18
19
|
## Step 2: Load only the needed reference
|
|
19
20
|
|
|
@@ -26,6 +27,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
26
27
|
7. For generated documentation provenance and deeper API lookup paths, read `references/llms-index.md`.
|
|
27
28
|
8. For smoke-test expectations, positive examples, and anti-examples, read `references/examples.md`.
|
|
28
29
|
9. For evaluating whether another agent followed this skill, read `references/smoke-evaluation.md`.
|
|
30
|
+
10. For mini-program listing/audit/compliance review, fetch the online rules Markdown from `https://open.xiaoheihe.cn/docs/hb_sdk/llms/guide/mini-program-publishing-rules.md`. Do not rely on a bundled `references/` copy of this document; if the URL is unavailable or returns the docs app shell instead of Markdown, report that the online rules are unavailable and do not complete the review from stale local text.
|
|
29
31
|
|
|
30
32
|
## Step 3: Follow import rules
|
|
31
33
|
|
|
@@ -35,6 +35,7 @@ This site documents the iframe-side SDK for external mini programs. For AI retri
|
|
|
35
35
|
- [错误处理](./llms/guide/error-handling.md): SDK 对外抛出的标准错误类型是 `HbMiniProgramSDKError`。
|
|
36
36
|
- [安装与运行环境](./llms/guide/installation.md): 如果工坊小程序只是普通展示页面,不调用黑盒开放能力,也不监听 SDK 生命周期事件,可以不安装、不引用、不初始化 `@heybox/hb-sdk`。父容器仍可能在 URL 上注入 `hb_mini_bridge_nonce`,业务页可以直接忽略。
|
|
37
37
|
- [事件与生命周期](./llms/guide/lifecycle.md): SDK 通过 `on` 监听父容器派发的小程序生命周期和业务事件。
|
|
38
|
+
- [小程序工坊上架规则 v2(试行)](./llms/guide/mini-program-publishing-rules.md): 本规则用于说明小程序工坊的小程序发布、上架和持续在线要求。通过审核不等于获得推荐或公开展示;工坊广场、推荐位、搜索等流量分发规则另行制定。
|
|
38
39
|
- [快速开始](./llms/guide/quick-start.md): 如果页面不需要黑盒开放能力,可以不接入 SDK。这是一条需要用户能力时的最短接入路径:等待 SDK 完成握手,然后读取当前用户登录态。
|
|
39
40
|
## Recipes
|
|
40
41
|
- [独立 SDK 实例](./llms/recipes/custom-instance.md): 大多数业务页使用默认单例即可。只有在需要隔离上下文时,再创建独立实例。
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
import { HB_SDK_PACKAGE_NAME } from './skill-metadata.mjs';
|
|
7
|
+
|
|
8
|
+
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const HB_SDK_ROOT = path.resolve(SCRIPT_DIR, '../..');
|
|
10
|
+
const REPO_ROOT = path.resolve(HB_SDK_ROOT, '../..');
|
|
11
|
+
const WELL_KNOWN_ROOT = path.join('.well-known', 'agent-skills');
|
|
12
|
+
const AGENT_SKILL_PACKAGES = [
|
|
13
|
+
{
|
|
14
|
+
packageName: HB_SDK_PACKAGE_NAME,
|
|
15
|
+
skillDir: 'skill',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
20
|
+
const TEXT_FILE_RE = /\.(?:md|mjs|js|cjs|ts|tsx|json|txt|sh|yaml|yml)$/i;
|
|
21
|
+
const PREFERRED_ORDER = new Map([
|
|
22
|
+
['SKILL.md', 0],
|
|
23
|
+
['skill.json', 1],
|
|
24
|
+
['references/api-root.md', 100],
|
|
25
|
+
['references/api-protocol.md', 101],
|
|
26
|
+
['references/cli.md', 102],
|
|
27
|
+
['references/recipes.md', 103],
|
|
28
|
+
['references/safety-boundaries.md', 104],
|
|
29
|
+
['references/llms-index.md', 105],
|
|
30
|
+
['references/examples.md', 106],
|
|
31
|
+
['references/smoke-evaluation.md', 107],
|
|
32
|
+
['scripts/sync-references.mjs', 200],
|
|
33
|
+
['scripts/check-references.mjs', 201],
|
|
34
|
+
['scripts/validate-skill.mjs', 202],
|
|
35
|
+
['scripts/package-skill.mjs', 203],
|
|
36
|
+
['scripts/package-skill.sh', 204],
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
function parseCliArgs(argv) {
|
|
40
|
+
const args = argv.slice(2);
|
|
41
|
+
let outDir = path.resolve(process.cwd(), '.agent-skills-payload');
|
|
42
|
+
let check = false;
|
|
43
|
+
|
|
44
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
45
|
+
const arg = args[index];
|
|
46
|
+
if (arg === '--check') {
|
|
47
|
+
check = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (arg === '--out-dir') {
|
|
51
|
+
const value = args[index + 1];
|
|
52
|
+
if (!value) {
|
|
53
|
+
throw new Error('--out-dir requires a path argument');
|
|
54
|
+
}
|
|
55
|
+
outDir = path.resolve(process.cwd(), value);
|
|
56
|
+
index += 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { outDir, check };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toPosixPath(value) {
|
|
64
|
+
return value.split(path.sep).join('/');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function assertSafeRelativePath(relativePath, context) {
|
|
68
|
+
const normalized = path.posix.normalize(relativePath);
|
|
69
|
+
|
|
70
|
+
if (path.isAbsolute(relativePath) || normalized.startsWith('../') || normalized === '..') {
|
|
71
|
+
throw new Error(`${context} has unsafe path: ${relativePath}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (normalized !== relativePath) {
|
|
75
|
+
throw new Error(`${context} must be normalized: ${relativePath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (relativePath.includes('/../') || relativePath.endsWith('/..')) {
|
|
79
|
+
throw new Error(`${context} cannot contain parent traversal: ${relativePath}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (/\.zip$/i.test(relativePath)) {
|
|
83
|
+
throw new Error(`${context} must not include zip archives: ${relativePath}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseFrontmatter(skillText, skillPath) {
|
|
88
|
+
const match = skillText.match(FRONTMATTER_RE);
|
|
89
|
+
if (!match) {
|
|
90
|
+
throw new Error(`${skillPath} is missing YAML frontmatter`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const metadata = {};
|
|
94
|
+
for (const rawLine of match[1].split('\n')) {
|
|
95
|
+
const line = rawLine.trim();
|
|
96
|
+
if (!line || line.startsWith('#') || line.startsWith('metadata:')) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const keyValue = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
101
|
+
if (!keyValue) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const [, key, rawValue] = keyValue;
|
|
106
|
+
metadata[key] = rawValue.replace(/^['"]|['"]$/g, '').trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return metadata;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function exists(filePath) {
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(filePath);
|
|
115
|
+
return true;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function readJsonFile(filePath) {
|
|
122
|
+
return JSON.parse(await fs.readFile(filePath, 'utf8'));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function resolvePackageRoot(packageSource) {
|
|
126
|
+
if (packageSource.packageName !== HB_SDK_PACKAGE_NAME) {
|
|
127
|
+
throw new Error(`Unsupported agent skill package: ${packageSource.packageName}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!(await exists(path.join(HB_SDK_ROOT, 'package.json')))) {
|
|
131
|
+
throw new Error(`Cannot resolve ${packageSource.packageName}: missing package root at ${toPosixPath(HB_SDK_ROOT)}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return HB_SDK_ROOT;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function listFilesRecursive(rootDir, relativeBase = '', options = {}) {
|
|
138
|
+
if (!(await exists(rootDir))) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
143
|
+
const files = [];
|
|
144
|
+
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
if (!options.includeHidden && entry.name.startsWith('.')) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const absolutePath = path.join(rootDir, entry.name);
|
|
151
|
+
const relativePath = path.posix.join(relativeBase, entry.name);
|
|
152
|
+
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
files.push(...(await listFilesRecursive(absolutePath, relativePath, options)));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (entry.isFile()) {
|
|
159
|
+
files.push(relativePath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return files;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function sortSkillFiles(files) {
|
|
167
|
+
return [...files].sort((left, right) => {
|
|
168
|
+
const leftOrder = PREFERRED_ORDER.get(left) ?? 10_000;
|
|
169
|
+
const rightOrder = PREFERRED_ORDER.get(right) ?? 10_000;
|
|
170
|
+
return leftOrder - rightOrder || left.localeCompare(right);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function discoverSkill(packageSource) {
|
|
175
|
+
const packageRoot = await resolvePackageRoot(packageSource);
|
|
176
|
+
const skillDir = path.join(packageRoot, packageSource.skillDir);
|
|
177
|
+
const skillMarkdownPath = path.join(skillDir, 'SKILL.md');
|
|
178
|
+
|
|
179
|
+
if (!(await exists(skillMarkdownPath))) {
|
|
180
|
+
throw new Error(`${packageSource.packageName} must provide ${packageSource.skillDir}/SKILL.md.`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const skillMarkdown = await fs.readFile(skillMarkdownPath, 'utf8');
|
|
184
|
+
const frontmatter = parseFrontmatter(skillMarkdown, skillMarkdownPath);
|
|
185
|
+
const skillName = frontmatter.name;
|
|
186
|
+
|
|
187
|
+
if (!skillName) {
|
|
188
|
+
throw new Error(`${skillMarkdownPath} frontmatter must include name`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!frontmatter.description) {
|
|
192
|
+
throw new Error(`${skillMarkdownPath} frontmatter must include description`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const files = ['SKILL.md'];
|
|
196
|
+
const skillJsonPath = path.join(skillDir, 'skill.json');
|
|
197
|
+
let metadata = null;
|
|
198
|
+
|
|
199
|
+
if (await exists(skillJsonPath)) {
|
|
200
|
+
const relativeFile = 'skill.json';
|
|
201
|
+
assertSafeRelativePath(relativeFile, `${skillName} source file`);
|
|
202
|
+
files.push(relativeFile);
|
|
203
|
+
metadata = await readJsonFile(skillJsonPath);
|
|
204
|
+
if (metadata.name !== skillName) {
|
|
205
|
+
throw new Error(`${skillJsonPath} name (${metadata.name}) must match SKILL.md frontmatter (${skillName})`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const topLevelDir of ['references', 'scripts', 'assets']) {
|
|
210
|
+
const nestedFiles = await listFilesRecursive(path.join(skillDir, topLevelDir), topLevelDir);
|
|
211
|
+
for (const relativeFile of nestedFiles) {
|
|
212
|
+
if (!TEXT_FILE_RE.test(relativeFile)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
assertSafeRelativePath(relativeFile, `${skillName} source file`);
|
|
216
|
+
files.push(relativeFile);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
sourceDir: skillDir,
|
|
222
|
+
name: skillName,
|
|
223
|
+
description: frontmatter.description,
|
|
224
|
+
files: sortSkillFiles(files),
|
|
225
|
+
metadata,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function discoverSkills() {
|
|
230
|
+
const skills = [];
|
|
231
|
+
|
|
232
|
+
for (const packageSource of AGENT_SKILL_PACKAGES) {
|
|
233
|
+
skills.push(await discoverSkill(packageSource));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (skills.length === 0) {
|
|
237
|
+
throw new Error('No configured agent skills found.');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return skills;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function createSkillIndex(skills) {
|
|
244
|
+
return {
|
|
245
|
+
skills: skills.map((skill) => ({
|
|
246
|
+
name: skill.name,
|
|
247
|
+
description: skill.description,
|
|
248
|
+
...(skill.metadata
|
|
249
|
+
? {
|
|
250
|
+
version: skill.metadata.skillVersion,
|
|
251
|
+
sdk: skill.metadata.sdk,
|
|
252
|
+
source: skill.metadata.source,
|
|
253
|
+
integrity: skill.metadata.integrity,
|
|
254
|
+
}
|
|
255
|
+
: {}),
|
|
256
|
+
files: skill.files,
|
|
257
|
+
})),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function addIndexFile(outputFiles, baseDir, skills) {
|
|
262
|
+
const indexPath = path.join(baseDir, WELL_KNOWN_ROOT, 'index.json');
|
|
263
|
+
const index = createSkillIndex(skills);
|
|
264
|
+
|
|
265
|
+
outputFiles.set(indexPath, Buffer.from(`${JSON.stringify(index, null, 2)}\n`, 'utf8'));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function addSkillFiles(outputFiles, baseDir, skills) {
|
|
269
|
+
for (const skill of skills) {
|
|
270
|
+
for (const relativeFile of skill.files) {
|
|
271
|
+
assertSafeRelativePath(relativeFile, `${skill.name} generated file`);
|
|
272
|
+
const sourcePath = path.join(skill.sourceDir, ...relativeFile.split('/'));
|
|
273
|
+
const targetPath = path.join(baseDir, WELL_KNOWN_ROOT, skill.name, ...relativeFile.split('/'));
|
|
274
|
+
outputFiles.set(targetPath, await fs.readFile(sourcePath));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function buildExpectedOutput(skills, targetRoot) {
|
|
280
|
+
const outputFiles = new Map();
|
|
281
|
+
|
|
282
|
+
await addIndexFile(outputFiles, targetRoot, skills);
|
|
283
|
+
await addSkillFiles(outputFiles, targetRoot, skills);
|
|
284
|
+
|
|
285
|
+
return outputFiles;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function listExistingGeneratedFiles(targetRoot) {
|
|
289
|
+
if (!(await exists(targetRoot))) {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const relativeFiles = await listFilesRecursive(targetRoot, '', { includeHidden: true });
|
|
294
|
+
return relativeFiles.map((relativeFile) => path.join(targetRoot, ...relativeFile.split('/')));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function syncFiles(expectedOutput, targetRoot) {
|
|
298
|
+
await fs.rm(targetRoot, { recursive: true, force: true });
|
|
299
|
+
|
|
300
|
+
for (const [targetPath, content] of [...expectedOutput.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
301
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
302
|
+
await fs.writeFile(targetPath, content);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function checkFiles(expectedOutput, targetRoot) {
|
|
307
|
+
const expectedPaths = new Set(expectedOutput.keys());
|
|
308
|
+
const existingPaths = new Set(await listExistingGeneratedFiles(targetRoot));
|
|
309
|
+
const problems = [];
|
|
310
|
+
|
|
311
|
+
for (const expectedPath of [...expectedPaths].sort()) {
|
|
312
|
+
if (!existingPaths.has(expectedPath)) {
|
|
313
|
+
problems.push(`missing ${path.relative(REPO_ROOT, expectedPath)}`);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const actual = await fs.readFile(expectedPath);
|
|
318
|
+
const expected = expectedOutput.get(expectedPath);
|
|
319
|
+
if (!actual.equals(expected)) {
|
|
320
|
+
problems.push(`stale ${path.relative(REPO_ROOT, expectedPath)}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const existingPath of [...existingPaths].sort()) {
|
|
325
|
+
if (!expectedPaths.has(existingPath)) {
|
|
326
|
+
problems.push(`extra ${path.relative(REPO_ROOT, existingPath)}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (problems.length > 0) {
|
|
331
|
+
throw new Error(`Agent skills payload is out of sync:\n${problems.map((problem) => `- ${problem}`).join('\n')}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function main() {
|
|
336
|
+
const { outDir, check } = parseCliArgs(process.argv);
|
|
337
|
+
const skills = await discoverSkills();
|
|
338
|
+
const expectedOutput = await buildExpectedOutput(skills, outDir);
|
|
339
|
+
|
|
340
|
+
if (check) {
|
|
341
|
+
await checkFiles(expectedOutput, outDir);
|
|
342
|
+
console.log(
|
|
343
|
+
`Agent skills payload is in sync at ${toPosixPath(path.relative(REPO_ROOT, outDir))} (${expectedOutput.size} files, ${skills.length} skills).`,
|
|
344
|
+
);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
await syncFiles(expectedOutput, outDir);
|
|
349
|
+
console.log(
|
|
350
|
+
`Synced ${skills.length} skills to ${toPosixPath(path.relative(REPO_ROOT, outDir))} (${expectedOutput.size} files).`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
355
|
+
main().catch((error) => {
|
|
356
|
+
console.error(error.message);
|
|
357
|
+
process.exitCode = 1;
|
|
358
|
+
});
|
|
359
|
+
}
|
package/skill/skill.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hb-sdk",
|
|
3
|
-
"skillVersion": "0.5.9+skill.
|
|
3
|
+
"skillVersion": "0.5.9+skill.02be68ce07ce",
|
|
4
4
|
"sdk": {
|
|
5
5
|
"package": "@heybox/hb-sdk",
|
|
6
6
|
"version": "0.5.9",
|
|
7
7
|
"compatibility": "0.5.9"
|
|
8
8
|
},
|
|
9
9
|
"source": "https://open.xiaoheihe.cn/agent-skills/hb-sdk",
|
|
10
|
-
"integrity": "sha256-
|
|
10
|
+
"integrity": "sha256-02be68ce07ce1cf0ecba354d764a47977e4fc3c189b5e4aed3cee1f844fddfd4"
|
|
11
11
|
}
|