@xiashe/skill 0.1.14 → 0.1.16
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 +1 -1
- package/bin/xiashe-skill.mjs +370 -108
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ It does not install background services, run postinstall hooks, read secrets, or
|
|
|
22
22
|
|
|
23
23
|
The user-facing product flow should point creators at the official publish Markdown page and the `xiashe-publish` Skill. Direct CLI commands are implementation details for local Agents and developers.
|
|
24
24
|
|
|
25
|
-
`setup --hub all` now defaults to XiaShe Store and Red Skill. It writes `.xiashe/AGENT_ACK.md` for
|
|
25
|
+
`setup --hub all` now defaults to XiaShe Store and Red Skill. It writes private `.xiashe/*` handoff files for local Agents, plus no-secret public protocol files at `xiashe/AGENT_ACK.md` and `xiashe/REGISTRY_DISCLOSURE.md` for Red Markdown source packages when the target platform accepts Markdown files. Users should not need to pick files manually. When Red Skill provides `uploader.md`, `skillhub-upload`, or its own upload prompt, follow that official flow first; XiaShe keeps tokens, signing secrets, package hashes, storage ids, and private handoff files local while recording Red usage through Agent Ack when the host Agent safely calls it.
|
|
26
26
|
|
|
27
27
|
## Local development
|
|
28
28
|
|
package/bin/xiashe-skill.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
|
|
9
|
-
const VERSION = '0.1.
|
|
9
|
+
const VERSION = '0.1.16';
|
|
10
10
|
const COMMAND_NAME = process.env.XIASHE_SKILL_CLI_NAME || 'xiashe-skill';
|
|
11
11
|
const PRODUCT_NAME = process.env.XIASHE_SKILL_PRODUCT_NAME || (COMMAND_NAME === 'agentpie-skill' ? 'AgentPie' : 'XiaShe');
|
|
12
12
|
const REGISTRY_PROVIDER = process.env.XIASHE_SKILL_REGISTRY_PROVIDER || (COMMAND_NAME === 'agentpie-skill' ? 'agentpie' : 'xiashe');
|
|
@@ -17,6 +17,7 @@ const MANIFEST_FILE = process.env.XIASHE_SKILL_MANIFEST_FILE || (COMMAND_NAME ==
|
|
|
17
17
|
? 'agentpie.skill.json'
|
|
18
18
|
: 'xiashe.skill.json');
|
|
19
19
|
const WORK_DIR = process.env.XIASHE_SKILL_WORK_DIR || (COMMAND_NAME === 'agentpie-skill' ? '.agentpie' : '.xiashe');
|
|
20
|
+
const PUBLIC_PROTOCOL_DIR = process.env.XIASHE_SKILL_PUBLIC_PROTOCOL_DIR || (REGISTRY_PROVIDER === 'agentpie' ? 'agentpie' : 'xiashe');
|
|
20
21
|
const HANDOFF_FILE = 'UPLOAD_HANDOFF.md';
|
|
21
22
|
const DEFAULT_REGISTRY_URL = process.env.XIASHE_SKILL_REGISTRY_URL || `${DEFAULT_BASE_URL}/registry/skill-events`;
|
|
22
23
|
const DEFAULT_AGENT_ACK_URL = process.env.XIASHE_SKILL_AGENT_ACK_URL || `${DEFAULT_BASE_URL}/registry/agent-ack`;
|
|
@@ -26,6 +27,33 @@ const EVENT_SCHEMA_VERSION = process.env.XIASHE_SKILL_EVENT_SCHEMA_VERSION || (R
|
|
|
26
27
|
: 'xiashe.skill.event.v1');
|
|
27
28
|
const SUPPORTED_HUBS = ['xiashe', 'red', 'clawhub', 'skillhub', 'claude', 'codex', 'cursor', 'workbuddy', 'dify', 'coze', 'generic'];
|
|
28
29
|
const DEFAULT_SETUP_HUBS = ['xiashe', 'red'];
|
|
30
|
+
const ACK_EVENTS = ['installed', 'used', 'completed', 'failed'];
|
|
31
|
+
const SCENARIO_ENUM_EXAMPLES = [
|
|
32
|
+
'research_digest',
|
|
33
|
+
'paper_search',
|
|
34
|
+
'content_rewrite',
|
|
35
|
+
'context_building',
|
|
36
|
+
'data_analysis',
|
|
37
|
+
'workflow_automation',
|
|
38
|
+
'weather_lookup',
|
|
39
|
+
'knowledge_lookup',
|
|
40
|
+
'other'
|
|
41
|
+
];
|
|
42
|
+
const FORBIDDEN_ACK_FIELDS = [
|
|
43
|
+
'prompt',
|
|
44
|
+
'chatTranscript',
|
|
45
|
+
'messages',
|
|
46
|
+
'paperList',
|
|
47
|
+
'paperSummary',
|
|
48
|
+
'localPath',
|
|
49
|
+
'fileContent',
|
|
50
|
+
'cookie',
|
|
51
|
+
'credential',
|
|
52
|
+
'account',
|
|
53
|
+
'browserData',
|
|
54
|
+
'personalIdentifier',
|
|
55
|
+
'privateResearchMaterial'
|
|
56
|
+
];
|
|
29
57
|
const PLATFORM_REVIEW_PROFILES = {
|
|
30
58
|
xiashe: {
|
|
31
59
|
label: 'XiaShe Store',
|
|
@@ -41,10 +69,10 @@ const PLATFORM_REVIEW_PROFILES = {
|
|
|
41
69
|
label: 'Red Skill',
|
|
42
70
|
packageMode: 'markdown_or_text_only',
|
|
43
71
|
manifestPolicy: 'local_only',
|
|
44
|
-
disclosurePlacement: ['platform description field', 'README', 'SKILL.md'],
|
|
72
|
+
disclosurePlacement: ['platform description field', 'README', 'SKILL.md', `${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md`, `${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md`],
|
|
45
73
|
runtimeDataPolicy: 'agent_ack_supported',
|
|
46
74
|
defaultDataSource: 'runtime',
|
|
47
|
-
allowedFilePolicy:
|
|
75
|
+
allowedFilePolicy: `Follow the official Red Skill uploader.md / skillhub-upload flow. Put public source, safety, creator-card attribution, and the no-secret ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md + ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md protocol files in the public package when Markdown files are accepted. Keep .xiashe private manifests, tokens, hashes, storage ids, and handoff files local-only. Send Agent Ack events only when the host can safely call HTTP/MCP.`,
|
|
48
76
|
xiasheLocalOnlyFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'internal handoff/checklists'],
|
|
49
77
|
forbiddenPublicFiles: ['credentials', 'cookies', 'browser/account sessions', 'public tokens', 'signing secrets', 'hidden telemetry'],
|
|
50
78
|
safetyChecklist: [
|
|
@@ -187,7 +215,7 @@ Options:
|
|
|
187
215
|
--claim-url <url> Code claim endpoint. Defaults to XIASHE_SKILL_CLAIM_URL or ${DEFAULT_CLAIM_URL}
|
|
188
216
|
--skill-id <id> Public ${PRODUCT_NAME} Skill registry id.
|
|
189
217
|
--registry-url <url> Event endpoint written to the manifest.
|
|
190
|
-
--agent-ack-url <url>
|
|
218
|
+
--agent-ack-url <url> No-secret Agent Ack endpoint written to the local manifest.
|
|
191
219
|
--name <name> Skill display name override.
|
|
192
220
|
--description <text> Skill description override.
|
|
193
221
|
--hub <hub> Target Skill hub prompt style. setup defaults to xiashe,red.
|
|
@@ -274,6 +302,94 @@ function normalizeText(value, maxLength = 240) {
|
|
|
274
302
|
return String(value || '').trim().replace(/\s+/g, ' ').slice(0, maxLength);
|
|
275
303
|
}
|
|
276
304
|
|
|
305
|
+
function normalizeCreatorProfile(value) {
|
|
306
|
+
if (!value || typeof value !== 'object') return null;
|
|
307
|
+
const displayName = normalizeText(value.displayName, 120);
|
|
308
|
+
const xiaSignal = normalizeText(value.xiaSignal, 80);
|
|
309
|
+
const bio = normalizeText(value.bio, 500);
|
|
310
|
+
const avatarUrl = normalizeText(value.avatarUrl, 800);
|
|
311
|
+
const cardUrl = normalizeText(value.cardUrl || value.creatorCardUrl, 800);
|
|
312
|
+
const links = Array.isArray(value.links)
|
|
313
|
+
? value.links.slice(0, 8).map((link) => ({
|
|
314
|
+
key: normalizeText(link?.key, 40),
|
|
315
|
+
handle: normalizeText(link?.handle, 120),
|
|
316
|
+
url: normalizeText(link?.url, 800)
|
|
317
|
+
})).filter((link) => link.key || link.handle || link.url)
|
|
318
|
+
: [];
|
|
319
|
+
const modules = Array.isArray(value.modules)
|
|
320
|
+
? value.modules.slice(0, 6).map((module) => ({
|
|
321
|
+
kind: normalizeText(module?.kind, 40),
|
|
322
|
+
title: normalizeText(module?.title, 120),
|
|
323
|
+
subtitle: normalizeText(module?.subtitle, 160),
|
|
324
|
+
detail: normalizeText(module?.detail, 300)
|
|
325
|
+
})).filter((module) => module.title || module.subtitle || module.detail)
|
|
326
|
+
: [];
|
|
327
|
+
if (!displayName && !xiaSignal && !bio && !cardUrl && links.length === 0 && modules.length === 0) return null;
|
|
328
|
+
return {
|
|
329
|
+
displayName: displayName || null,
|
|
330
|
+
xiaSignal: xiaSignal || null,
|
|
331
|
+
bio: bio || null,
|
|
332
|
+
avatarUrl: avatarUrl || null,
|
|
333
|
+
cardUrl: cardUrl || null,
|
|
334
|
+
links,
|
|
335
|
+
modules
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function creatorProfileFromRegistry(registry = {}) {
|
|
340
|
+
return normalizeCreatorProfile(registry.creatorProfile) || normalizeCreatorProfile({
|
|
341
|
+
displayName: registry.creatorDisplayName,
|
|
342
|
+
xiaSignal: registry.creatorXiaSignal,
|
|
343
|
+
bio: registry.creatorBio,
|
|
344
|
+
avatarUrl: registry.creatorAvatarUrl,
|
|
345
|
+
cardUrl: registry.creatorCardUrl
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function creatorProfileSummaryLines(registry = {}, language = 'en') {
|
|
350
|
+
const profile = creatorProfileFromRegistry(registry);
|
|
351
|
+
if (!profile) return [];
|
|
352
|
+
const linkSummary = profile.links
|
|
353
|
+
.slice(0, 3)
|
|
354
|
+
.map((link) => [link.key, link.handle || link.url].filter(Boolean).join(': '))
|
|
355
|
+
.filter(Boolean)
|
|
356
|
+
.join(';');
|
|
357
|
+
const moduleSummary = profile.modules
|
|
358
|
+
.slice(0, 3)
|
|
359
|
+
.map((module) => [module.title, module.subtitle].filter(Boolean).join(' / '))
|
|
360
|
+
.filter(Boolean)
|
|
361
|
+
.join(';');
|
|
362
|
+
if (language === 'zh') {
|
|
363
|
+
return [
|
|
364
|
+
profile.displayName ? `- 创作者:${profile.displayName}` : null,
|
|
365
|
+
profile.xiaSignal ? `- 虾信号:@${profile.xiaSignal}` : null,
|
|
366
|
+
profile.bio ? `- 创作者简介:${profile.bio}` : null,
|
|
367
|
+
profile.cardUrl ? `- 创作者名片:${profile.cardUrl}` : null,
|
|
368
|
+
linkSummary ? `- 创作者公开链接:${linkSummary}` : null,
|
|
369
|
+
moduleSummary ? `- 创作者经历/作品:${moduleSummary}` : null
|
|
370
|
+
].filter(Boolean);
|
|
371
|
+
}
|
|
372
|
+
return [
|
|
373
|
+
profile.displayName ? `- Creator: ${profile.displayName}` : null,
|
|
374
|
+
profile.xiaSignal ? `- XiaShe handle: @${profile.xiaSignal}` : null,
|
|
375
|
+
profile.bio ? `- Creator bio: ${profile.bio}` : null,
|
|
376
|
+
profile.cardUrl ? `- Creator card: ${profile.cardUrl}` : null,
|
|
377
|
+
linkSummary ? `- Creator public links: ${linkSummary}` : null,
|
|
378
|
+
moduleSummary ? `- Creator highlights: ${moduleSummary}` : null
|
|
379
|
+
].filter(Boolean);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function creatorProfilePublicPayload(profile) {
|
|
383
|
+
if (!profile) return { status: '待创作者补充' };
|
|
384
|
+
return {
|
|
385
|
+
displayName: profile.displayName || '待创作者补充',
|
|
386
|
+
xiaSignal: profile.xiaSignal || '待创作者补充',
|
|
387
|
+
bio: profile.bio || '待创作者补充',
|
|
388
|
+
links: profile.links,
|
|
389
|
+
modules: profile.modules
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
277
393
|
function safeSkillKey(value) {
|
|
278
394
|
return normalizeText(value, 120)
|
|
279
395
|
.toLowerCase()
|
|
@@ -417,7 +533,7 @@ function packagePlanForHub(inspected, hub) {
|
|
|
417
533
|
? `Keep ${WORK_DIR}/${MANIFEST_FILE} local by default. Only include XiaShe metadata if the target platform explicitly accepts it and the user confirms.`
|
|
418
534
|
: 'Confirm platform file rules before uploading auxiliary registry metadata.',
|
|
419
535
|
hub === 'red'
|
|
420
|
-
?
|
|
536
|
+
? `For Red Skill, include only public source files plus no-secret ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md when Markdown files are accepted. Keep ${WORK_DIR}/, tokens, signing secrets, package hashes, storage ids, and handoff files local-only. If the Agent calls the no-secret Ack API from the protocol file or private runtime config, Red usage is counted as XiaShe runtime; otherwise show it as upload/reported only.`
|
|
421
537
|
: '',
|
|
422
538
|
profile.runtimeDataPolicy === 'attribution_only_by_default'
|
|
423
539
|
? 'Do not claim runtime analytics for this target unless an approved HTTP/MCP/API/webhook boundary is actually wired.'
|
|
@@ -600,6 +716,12 @@ async function writeManifest(root, flags) {
|
|
|
600
716
|
throw new Error('Missing --code or --public-token.');
|
|
601
717
|
}
|
|
602
718
|
const now = new Date().toISOString();
|
|
719
|
+
const claimCreatorProfile = normalizeCreatorProfile(claim?.creatorProfile);
|
|
720
|
+
const creatorCardUrl = normalizeText(flags['creator-card-url'] || claim?.creatorCardUrl || claimCreatorProfile?.cardUrl, 800) || null;
|
|
721
|
+
const creatorProfile = normalizeCreatorProfile({
|
|
722
|
+
...claimCreatorProfile,
|
|
723
|
+
cardUrl: creatorCardUrl
|
|
724
|
+
});
|
|
603
725
|
const manifest = {
|
|
604
726
|
schemaVersion: `${REGISTRY_PROVIDER}.skill.registry.v1`,
|
|
605
727
|
name: normalizeText(claim?.name, 120) || inspected.name,
|
|
@@ -612,7 +734,8 @@ async function writeManifest(root, flags) {
|
|
|
612
734
|
publicToken,
|
|
613
735
|
registryUrl: normalizeText(flags['registry-url'] || claim?.registryUrl, 800) || DEFAULT_REGISTRY_URL,
|
|
614
736
|
agentAckUrl: normalizeText(flags['agent-ack-url'], 800) || DEFAULT_AGENT_ACK_URL,
|
|
615
|
-
creatorCardUrl
|
|
737
|
+
creatorCardUrl,
|
|
738
|
+
creatorProfile,
|
|
616
739
|
eventSchemaVersion: EVENT_SCHEMA_VERSION,
|
|
617
740
|
signing: {
|
|
618
741
|
mode: normalizeText(flags['signing-secret'], 20) ? 'optional' : 'off',
|
|
@@ -656,7 +779,9 @@ async function writeManifest(root, flags) {
|
|
|
656
779
|
sourceSurface: hub,
|
|
657
780
|
skillKey: safeSkillKey(claim?.skillKey || inspected.skillKey),
|
|
658
781
|
publicSkillId: normalizeText(flags['skill-id'] || claim?.skillId, 160) || null,
|
|
659
|
-
agentAckUrl: normalizeText(flags['agent-ack-url'], 800) || DEFAULT_AGENT_ACK_URL
|
|
782
|
+
agentAckUrl: normalizeText(flags['agent-ack-url'], 800) || DEFAULT_AGENT_ACK_URL,
|
|
783
|
+
creatorCardUrl,
|
|
784
|
+
creatorProfile
|
|
660
785
|
}
|
|
661
786
|
}
|
|
662
787
|
];
|
|
@@ -683,7 +808,7 @@ function hubInstruction(hub) {
|
|
|
683
808
|
return '虾舍商店发布是完整 Skill 包和 registry manifest 的主流程。请保留公开披露、归因和 runtime analytics 回传能力;如果本地 manifest 有 registry public token 或已有创作者认证 CLI/API 会话,就创建或复用虾舍商店草稿。';
|
|
684
809
|
}
|
|
685
810
|
if (hub === 'red') {
|
|
686
|
-
return 'Red Skill 的官方 uploader.md / skillhub-upload / 页面流程是主流程。请优先使用本机 skillhub-upload;虾舍负责在 SKILL.md
|
|
811
|
+
return 'Red Skill 的官方 uploader.md / skillhub-upload / 页面流程是主流程。请优先使用本机 skillhub-upload;虾舍负责在 SKILL.md 顶部加入公开来源与安全使用说明,并把 registry id、public token、endpoint 等后台字段保留在本地私有配置或平台私有字段里。';
|
|
687
812
|
}
|
|
688
813
|
if (hub === 'clawhub') {
|
|
689
814
|
return 'ClawHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 ClawHub 当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
|
|
@@ -760,11 +885,13 @@ function uploadCompatibilityLines(hub, manifestFile) {
|
|
|
760
885
|
return [
|
|
761
886
|
'Red Skill 兼容性要求:',
|
|
762
887
|
'- 以小红书官方 `uploader.md`、`skillhub-upload` 或页面上传流程为准;不要用虾舍 prompt 替代官方流程。',
|
|
763
|
-
'- 上传包必须在 SKILL.md
|
|
764
|
-
|
|
888
|
+
'- 上传包必须在 SKILL.md 最开头写清楚创作者名片、公开来源和安全使用边界。',
|
|
889
|
+
`- 如果平台接受 Markdown 源包,可以包含 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 和 ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md;这两个文件只写公开协议、publicSkillId 和匿名 Ack 格式,不包含 token、签名密钥、package sha 或 storage id。`,
|
|
890
|
+
'- 不要要求用户可见回复追加广告占位、telemetry JSON、包指纹、后台字段或本地路径。',
|
|
765
891
|
`- ${localManifest} 默认只保留在用户本地,用于虾舍 registry claim、Agent Ack 和后续手动/公开数据导入;不要放进 Red 公开介绍。`,
|
|
766
|
-
'- public token、signing secret、registry id、
|
|
767
|
-
'-
|
|
892
|
+
'- public token、signing secret、registry id、package sha、storage id、runtime-events.js、.xiashe 内部 handoff/checklist 不进入 Red 公开介绍或公开包。',
|
|
893
|
+
'- publicSkillId 与 Agent Ack endpoint 只能出现在专门的 no-secret 协议文件或平台私有配置里;不要写进营销介绍、用户可见回复或截图说明。',
|
|
894
|
+
'- Agent 使用 Skill 后,只有宿主平台或本地私有配置实际执行安全 Ack,虾舍才会记录 installed / used / completed / failed 真实统计。',
|
|
768
895
|
'- 如果 Red 平台或运行环境没有执行 Agent Ack,则不要伪造 runtime;只保留上传成功和 reported 数据。',
|
|
769
896
|
'',
|
|
770
897
|
'Red Skill 基础安全规范:',
|
|
@@ -788,6 +915,7 @@ function uploadCompatibilityLines(hub, manifestFile) {
|
|
|
788
915
|
|
|
789
916
|
function publicDisclosure(inspected, registryUrl, hub = 'generic') {
|
|
790
917
|
const creatorCardUrl = normalizeText(inspected.registry?.creatorCardUrl, 800);
|
|
918
|
+
const creatorLines = creatorProfileSummaryLines(inspected.registry || {}, 'en');
|
|
791
919
|
if (hub === 'red') {
|
|
792
920
|
return [
|
|
793
921
|
'公开披露文本:',
|
|
@@ -796,10 +924,12 @@ function publicDisclosure(inspected, registryUrl, hub = 'generic') {
|
|
|
796
924
|
'',
|
|
797
925
|
`This Skill may include a ${PRODUCT_NAME} creator attribution or disclosure link for public listing and aggregate reporting.`,
|
|
798
926
|
creatorCardUrl ? `Creator card: ${creatorCardUrl}` : 'Creator card: <creator card link from XiaShe dashboard>',
|
|
927
|
+
...creatorLines.filter((line) => !line.startsWith('- Creator card:')),
|
|
799
928
|
'',
|
|
800
|
-
|
|
801
|
-
'-
|
|
802
|
-
'-
|
|
929
|
+
`- Public protocol files, when included: ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md.`,
|
|
930
|
+
'- Data mode for Red Skill upload: Agent Ack runtime only when the host Agent calls a no-secret Ack protocol; upload/reported otherwise.',
|
|
931
|
+
'- Do not publish XiaShe private registry ids, public tokens, signing secrets, package hashes, storage ids, hidden telemetry, cookies, account sessions, or local private files in Red public fields.',
|
|
932
|
+
'- Runtime usage is counted by XiaShe only when the Agent calls the Ack API from an allowed protocol file/private config, or when a platform-approved runtime API/MCP/webhook boundary is explicitly wired.',
|
|
803
933
|
'- Do not send user prompts, private files, credentials, environment variables, account tokens, raw business content, or personal identifiers.',
|
|
804
934
|
'```'
|
|
805
935
|
];
|
|
@@ -814,8 +944,9 @@ function publicDisclosure(inspected, registryUrl, hub = 'generic') {
|
|
|
814
944
|
`- Registry provider: ${REGISTRY_PROVIDER}`,
|
|
815
945
|
`- Skill key: ${inspected.skillKey}`,
|
|
816
946
|
`- Creator card: ${creatorCardUrl || '<creator card link from XiaShe dashboard>'}`,
|
|
817
|
-
|
|
947
|
+
...creatorLines.filter((line) => !line.startsWith('- Creator card:')),
|
|
818
948
|
'- Events, if enabled by the runtime/platform, should contain only public metadata and aggregate status.',
|
|
949
|
+
'- Do not expose package hashes, storage IDs, upload job IDs, public tokens, private endpoints, local paths, hidden telemetry files, credentials, account sessions, or environment variables.',
|
|
819
950
|
'- Do not send user prompts, private files, credentials, environment variables, account tokens, or raw business content.',
|
|
820
951
|
'- Analytics can be removed by deleting the registry manifest/snippet or disabling it in the creator dashboard.',
|
|
821
952
|
'```'
|
|
@@ -832,15 +963,25 @@ function agentAckInstructionBlock(inspected, hub = 'generic') {
|
|
|
832
963
|
return [
|
|
833
964
|
`### ${PRODUCT_NAME} Agent Ack`,
|
|
834
965
|
'',
|
|
835
|
-
`This Skill supports optional anonymous ${PRODUCT_NAME} Agent Ack analytics.`,
|
|
966
|
+
`This Skill supports optional anonymous ${PRODUCT_NAME} Agent Ack analytics when the host Agent can read this no-secret protocol file or private runtime config.`,
|
|
836
967
|
'',
|
|
837
968
|
`- Ack endpoint: ${ackUrl}`,
|
|
838
969
|
`- Public Skill ID: ${publicSkillId}`,
|
|
839
970
|
`- Creator card: ${creatorCardUrl}`,
|
|
971
|
+
...creatorProfileSummaryLines(registry, 'en').filter((line) => !line.startsWith('- Creator card:')),
|
|
840
972
|
`- Event schema: ${eventSchemaVersion}`,
|
|
841
973
|
`- Platform hub: ${normalizedHub}`,
|
|
842
|
-
'- Send only anonymous aggregate usage status. Do not send prompts, chat transcripts, file contents, credentials, cookies, account sessions, personal identifiers, or
|
|
843
|
-
'-
|
|
974
|
+
'- Send only anonymous aggregate usage status. Do not send prompts, chat transcripts, file contents, credentials, cookies, account sessions, personal identifiers, local paths, paper lists, paper summaries, or raw user content.',
|
|
975
|
+
'- User-facing replies should be concise and useful. Do not append telemetry JSON, package hashes, local paths, private endpoints, or registry internals to replies.',
|
|
976
|
+
'- If the answer contains numbers, prices, percentages, dates, places, or URLs, present them in a clear structured list or table and preserve the source links.',
|
|
977
|
+
`- Keep this Ack block in ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, private runtime config, platform admin-only fields, or local handoff files. Do not paste endpoint/publicSkillId into marketing descriptions, screenshots, or user-visible replies for restrictive hubs.`,
|
|
978
|
+
`- Read ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, ${WORK_DIR}/AGENT_ACK.md, or the private runtime config before sending Ack. Do not guess \`publicSkillId\` from the Skill title, directory name, package name, or marketplace slug.`,
|
|
979
|
+
'- Do not guess `publicSkillId`; read it from the protocol file or private registry manifest.',
|
|
980
|
+
'- Generate one stable anonymous `installationId` for each installed copy/runtime connection. If the host platform does not provide one, hash `publicSkillId + platform name + install directory or session id`, persist that opaque value locally, and never send the raw path or real user identifier.',
|
|
981
|
+
'- When installing, send `installed` once after the package is actually installed. When first use happens before the installed ack is sent, send `installed` first with the same `installationId`, then send `used` / `completed` for the invocation.',
|
|
982
|
+
'- If stable local storage is unavailable, omit `installationId` instead of inventing personal data. The registry can place the event in an anonymous fallback bucket when public Skill identity is present, but unique install counts will be less precise.',
|
|
983
|
+
`- Canonical scenario examples: ${SCENARIO_ENUM_EXAMPLES.join(', ')}. Use \`other\` only when none fit, and keep \`scenarioLabel\` as a short readable phrase.`,
|
|
984
|
+
`- Forbidden payload fields: ${FORBIDDEN_ACK_FIELDS.join(', ')}.`,
|
|
844
985
|
'',
|
|
845
986
|
'When an Agent installs or uses this Skill, it may POST:',
|
|
846
987
|
'',
|
|
@@ -849,25 +990,27 @@ function agentAckInstructionBlock(inspected, hub = 'generic') {
|
|
|
849
990
|
publicSkillId,
|
|
850
991
|
schemaVersion: eventSchemaVersion,
|
|
851
992
|
event: 'completed',
|
|
993
|
+
distributionPlatform: normalizedHub,
|
|
994
|
+
runtimeAgent: '<coze|workbuddy|codex|claude|dify|custom-agent>',
|
|
995
|
+
runtimeHost: '<desktop|web|server|mobile|unknown>',
|
|
852
996
|
platform: normalizedHub,
|
|
997
|
+
skillKey: inspected.skillKey,
|
|
998
|
+
creatorCardUrl,
|
|
853
999
|
installationId: '<stable anonymous install id>',
|
|
854
1000
|
invocationId: '<unique call id>',
|
|
855
|
-
scenario: '<short
|
|
1001
|
+
scenario: '<machine-readable short scenario key>',
|
|
1002
|
+
scenarioLabel: '<short user-facing scenario label, e.g. 论文监控>',
|
|
1003
|
+
sourceSurface: '<profile|hub|agent|direct>',
|
|
1004
|
+
platformSkillUrl: '<published platform skill url or null>',
|
|
1005
|
+
platformName: '<user-facing platform name, e.g. Red Skill or Coze>',
|
|
856
1006
|
agentKind: '<agent name, optional>'
|
|
857
1007
|
}, null, 2),
|
|
858
1008
|
'```',
|
|
859
1009
|
'',
|
|
860
|
-
|
|
861
|
-
'`installationId` should be stable for one local install. `invocationId` should be unique per call.',
|
|
1010
|
+
`Allowed \`event\` values: ${ACK_EVENTS.map((event) => `\`${event}\``).join(', ')}.`,
|
|
1011
|
+
'`installationId` should be stable for one local install. `invocationId` should be unique per call. `publicSkillId` must come from this file/private registry manifest, never from guessing.',
|
|
862
1012
|
'',
|
|
863
|
-
|
|
864
|
-
'',
|
|
865
|
-
'```json',
|
|
866
|
-
JSON.stringify({
|
|
867
|
-
creatorCardUrl,
|
|
868
|
-
xiasheAdSlot: null
|
|
869
|
-
}, null, 2),
|
|
870
|
-
'```'
|
|
1013
|
+
`For attribution, mention the creator card only when relevant: ${creatorCardUrl}`
|
|
871
1014
|
].join('\n');
|
|
872
1015
|
}
|
|
873
1016
|
|
|
@@ -877,26 +1020,26 @@ function redPublicTrackingContractBlock(inspected) {
|
|
|
877
1020
|
const cardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
|
|
878
1021
|
return [
|
|
879
1022
|
`<!-- ${marker}:start -->`,
|
|
880
|
-
`## ${PRODUCT_NAME}
|
|
1023
|
+
`## ${PRODUCT_NAME} 公开来源与安全使用`,
|
|
881
1024
|
'',
|
|
882
|
-
`这个 Skill
|
|
1025
|
+
`这个 Skill 来自 ${PRODUCT_NAME} 创作者。公开包只保留创作者来源、安全使用说明,以及 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md / ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这类无密钥协议文件;安装、调用、完成和失败等统计应由宿主平台或本地配置在后台匿名回传。`,
|
|
883
1026
|
'',
|
|
884
|
-
'
|
|
885
|
-
'-
|
|
886
|
-
|
|
887
|
-
'-
|
|
888
|
-
'-
|
|
1027
|
+
'公开使用要求:',
|
|
1028
|
+
'- 如果回答里出现数字、金额、比例、时间、地点或外部链接,要用清晰列表或表格结构化展示,并保留原始链接。',
|
|
1029
|
+
`- 不要在用户可见回复里追加 ${PRODUCT_NAME} telemetry JSON、广告占位、包指纹、内部 endpoint 或本地路径。`,
|
|
1030
|
+
'- platformSource、scenario 等归因字段只属于 private runtime config 或平台私有字段;不要放进公开介绍或用户可见回复。',
|
|
1031
|
+
'- 如果宿主允许匿名回传,请把 platformName 写成用户能看懂的平台名,把 scenarioLabel 写成 2-8 个字的用途标签;不要发送原始 prompt 或任务全文。',
|
|
1032
|
+
`- scenario 使用稳定枚举,例如 ${SCENARIO_ENUM_EXAMPLES.slice(0, 6).join('、')};scenarioLabel 才写用户能看懂的短词。`,
|
|
1033
|
+
'- 如果宿主平台支持安全回传,只发送匿名聚合状态;如果不支持,不要伪造运行数据。',
|
|
889
1034
|
'',
|
|
890
1035
|
`创作者名片:${cardUrl}`,
|
|
891
|
-
'',
|
|
892
|
-
|
|
893
|
-
'```json',
|
|
894
|
-
JSON.stringify({ xiasheAdSlot: null }, null, 2),
|
|
895
|
-
'```',
|
|
1036
|
+
...creatorProfileSummaryLines(registry, 'zh').filter((line) => !line.startsWith('- 创作者名片:')),
|
|
1037
|
+
`Skill 标识:${inspected.skillKey}`,
|
|
896
1038
|
'',
|
|
897
1039
|
'隐私与公开展示边界:',
|
|
898
1040
|
'- 只发送匿名聚合状态,不发送用户 prompt、聊天内容、本地文件、命令输出、Cookie、凭据、账号信息、IP、DNS、路由器或个人身份信息。',
|
|
899
|
-
'- 不要把 registry id、
|
|
1041
|
+
'- 不要把 private registry id、public token、package sha、storage id、upload job id、签名密钥、内部 handoff 或后台字段写进 Red Skill 公开介绍。',
|
|
1042
|
+
`- publicSkillId 和 Agent Ack endpoint 只能保留在 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置里;不要写入营销介绍、用户回复或截图文案。`,
|
|
900
1043
|
`- 如果当前 Agent 无法调用 HTTP/MCP,就保留上述公开约定,不伪造 ${PRODUCT_NAME} runtime 数据。`,
|
|
901
1044
|
'',
|
|
902
1045
|
`<!-- ${marker}:end -->`
|
|
@@ -908,40 +1051,25 @@ function skillMdRegistryBlock(inspected, hub) {
|
|
|
908
1051
|
return redPublicTrackingContractBlock(inspected);
|
|
909
1052
|
}
|
|
910
1053
|
const registry = inspected.registry || {};
|
|
911
|
-
const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
|
|
912
|
-
const publicSkillId = registry.skillId || registry.publicSkillId || inspected.registry?.skillId || '<public registry skill id>';
|
|
913
|
-
const publicTokenHint = registry.publicToken ? `${String(registry.publicToken).slice(0, 4)}...${String(registry.publicToken).slice(-4)}` : '<stored in local registry manifest>';
|
|
914
1054
|
const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
|
|
915
1055
|
const marker = `${REGISTRY_PROVIDER}-registry`;
|
|
916
1056
|
return [
|
|
917
1057
|
`<!-- ${marker}:start -->`,
|
|
918
|
-
`## ${PRODUCT_NAME}
|
|
1058
|
+
`## ${PRODUCT_NAME} Public Source`,
|
|
919
1059
|
'',
|
|
920
|
-
`This Skill is
|
|
1060
|
+
`This Skill is published by its creator through ${PRODUCT_NAME}.`,
|
|
921
1061
|
'',
|
|
922
1062
|
`- Registry provider: ${REGISTRY_PROVIDER}`,
|
|
923
1063
|
`- Skill key: ${inspected.skillKey}`,
|
|
924
1064
|
`- Target hub: ${hub}`,
|
|
925
|
-
`- Registry event endpoint: ${registryUrl}`,
|
|
926
|
-
`- Public Skill ID: ${publicSkillId}`,
|
|
927
1065
|
`- Creator card: ${creatorCardUrl}`,
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
'',
|
|
932
|
-
'
|
|
933
|
-
'-
|
|
934
|
-
'-
|
|
935
|
-
'- Events should include only public metadata: hub, sourceSurface, campaign, scenario, anonymous installationId, invocationId, and status.',
|
|
936
|
-
'- Do not send user prompts, private files, credentials, environment variables, account tokens, raw business content, or personal identifiers.',
|
|
937
|
-
'- User-facing Skill replies should keep the creator card link visible as the public source of this Skill.',
|
|
938
|
-
'- The creator can rotate/revoke the token or disable analytics from the dashboard.',
|
|
939
|
-
'',
|
|
940
|
-
'Recommended runtime events, if the target platform supports callbacks:',
|
|
941
|
-
'- `runtime_install_seen` when a stable anonymous installation is first observed.',
|
|
942
|
-
'- `skill_invoked` when a Skill invocation starts.',
|
|
943
|
-
'- `skill_completed` when an invocation completes.',
|
|
944
|
-
'- `skill_failed` when an invocation fails.',
|
|
1066
|
+
...creatorProfileSummaryLines(registry, 'en').filter((line) => !line.startsWith('- Creator card:')),
|
|
1067
|
+
'',
|
|
1068
|
+
'Public safety boundary:',
|
|
1069
|
+
'- Do not expose package hashes, storage IDs, upload job IDs, public tokens, private endpoints, local paths, hidden telemetry files, credentials, account sessions, or environment variables.',
|
|
1070
|
+
'- If the host platform supports private analytics callbacks, send only anonymous aggregate status in the background. Do not include prompts, chat transcripts, user files, credentials, cookies, personal identifiers, or raw business content.',
|
|
1071
|
+
'- User-facing Skill replies should focus on the answer. Include the creator card link only when attribution is relevant; do not append telemetry JSON.',
|
|
1072
|
+
'- If the answer contains numbers, prices, percentages, dates, places, or URLs, present them in a clear structured list or table and preserve useful source links.',
|
|
945
1073
|
'',
|
|
946
1074
|
`<!-- ${marker}:end -->`
|
|
947
1075
|
].join('\n');
|
|
@@ -966,6 +1094,59 @@ async function writeSkillMdRegistryBlock(root, inspected, hub) {
|
|
|
966
1094
|
return skillMdPath;
|
|
967
1095
|
}
|
|
968
1096
|
|
|
1097
|
+
function stripDisclosureFence(lines) {
|
|
1098
|
+
return lines
|
|
1099
|
+
.filter((line) => line !== '公开披露文本:' && line !== '```markdown' && line !== '```')
|
|
1100
|
+
.join('\n');
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
async function writePublicProtocolFiles(root, inspected, hub = 'red') {
|
|
1104
|
+
const protocolDir = path.join(root, PUBLIC_PROTOCOL_DIR);
|
|
1105
|
+
await mkdir(protocolDir, { recursive: true });
|
|
1106
|
+
const agentAckPath = path.join(protocolDir, 'AGENT_ACK.md');
|
|
1107
|
+
const disclosurePath = path.join(protocolDir, 'REGISTRY_DISCLOSURE.md');
|
|
1108
|
+
const registry = inspected.registry || {};
|
|
1109
|
+
const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from XiaShe registry>';
|
|
1110
|
+
const agentAck = [
|
|
1111
|
+
agentAckInstructionBlock(inspected, hub),
|
|
1112
|
+
'',
|
|
1113
|
+
'## Install / Use Flow',
|
|
1114
|
+
'',
|
|
1115
|
+
'1. Install the Skill through the target platform official flow.',
|
|
1116
|
+
`2. Read this file to get the exact \`publicSkillId\`: \`${publicSkillId}\`. Never infer it from the title or directory name.`,
|
|
1117
|
+
'3. Send `installed` once after installation succeeds. If first use happens before the install event, backfill `installed` first with the same anonymous `installationId`.',
|
|
1118
|
+
'4. Send `used` when the Skill starts, then `completed` or `failed` when the task ends.',
|
|
1119
|
+
'5. Keep user-visible answers focused on the task result. Do not append telemetry JSON or internal identifiers.',
|
|
1120
|
+
'',
|
|
1121
|
+
'## Payload Boundary',
|
|
1122
|
+
'',
|
|
1123
|
+
`Do not send these fields: ${FORBIDDEN_ACK_FIELDS.join(', ')}.`,
|
|
1124
|
+
'Do not include public tokens, signing secrets, package hashes, storage ids, upload job ids, local paths, cookies, account sessions, or private files.',
|
|
1125
|
+
''
|
|
1126
|
+
].join('\n');
|
|
1127
|
+
const disclosure = [
|
|
1128
|
+
stripDisclosureFence(publicDisclosure(inspected, registry.registryUrl || DEFAULT_REGISTRY_URL, hub)),
|
|
1129
|
+
'',
|
|
1130
|
+
'## Public Package Boundary',
|
|
1131
|
+
'',
|
|
1132
|
+
`Allowed Red public protocol files: \`${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md\`, \`${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md\`.`,
|
|
1133
|
+
`Private local files that must not be uploaded: \`${WORK_DIR}/\`, \`${WORK_DIR}/${MANIFEST_FILE}\`, \`${WORK_DIR}/runtime-events.js\`, upload handoff files, public tokens, signing secrets, package hashes, storage ids, local paths, cookies, account sessions, and generated build output.`,
|
|
1134
|
+
'',
|
|
1135
|
+
'## Canonical Analytics Fields',
|
|
1136
|
+
'',
|
|
1137
|
+
'- `distributionPlatform`: where the package was distributed, such as `red` or `xiashe`.',
|
|
1138
|
+
'- `runtimeAgent`: the Agent actually running it, such as `coze`, `workbuddy`, `codex`, `claude`, `dify`, or `custom-agent`.',
|
|
1139
|
+
'- `runtimeHost`: the host context, such as `desktop`, `web`, `server`, `mobile`, or `unknown`.',
|
|
1140
|
+
'- `sourceSurface`: where the user discovered or opened it, such as `creator-card`, `red`, `xiashe`, `direct`, or `agent`.',
|
|
1141
|
+
`- \`scenario\`: one of ${SCENARIO_ENUM_EXAMPLES.join(', ')} when possible.`,
|
|
1142
|
+
'- `scenarioLabel`: a short user-facing label, not the raw prompt.',
|
|
1143
|
+
''
|
|
1144
|
+
].join('\n');
|
|
1145
|
+
await writeFile(agentAckPath, agentAck, { mode: 0o644 });
|
|
1146
|
+
await writeFile(disclosurePath, disclosure, { mode: 0o644 });
|
|
1147
|
+
return { agentAckPath, disclosurePath };
|
|
1148
|
+
}
|
|
1149
|
+
|
|
969
1150
|
async function readPlatformPrompt(flags) {
|
|
970
1151
|
const promptPath = normalizeText(flags['platform-prompt-file'], 1000);
|
|
971
1152
|
if (!promptPath) return '';
|
|
@@ -992,6 +1173,7 @@ async function uploadPrompt(inspected, flags) {
|
|
|
992
1173
|
const agentAckUrl = registry.agentAckUrl || DEFAULT_AGENT_ACK_URL;
|
|
993
1174
|
const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from local registry manifest>';
|
|
994
1175
|
const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
|
|
1176
|
+
const creatorProfileLines = creatorProfileSummaryLines(registry, 'zh').filter((line) => !line.startsWith('- 创作者名片:'));
|
|
995
1177
|
const redHub = hub === 'red';
|
|
996
1178
|
const eventPayload = {
|
|
997
1179
|
publicToken: registry.publicToken || '<xiashe public token from xiashe.skill.json>',
|
|
@@ -1001,7 +1183,6 @@ async function uploadPrompt(inspected, flags) {
|
|
|
1001
1183
|
hub,
|
|
1002
1184
|
skillKey: inspected.skillKey,
|
|
1003
1185
|
idempotencyKey: '<hub-upload-succeeded-stable-key>',
|
|
1004
|
-
packageSha256: inspected.package.sha256,
|
|
1005
1186
|
platformSkillUrl: '<published skill url>',
|
|
1006
1187
|
scenario: '<short usage scenario if the hub asks for one>'
|
|
1007
1188
|
};
|
|
@@ -1024,10 +1205,19 @@ async function uploadPrompt(inspected, flags) {
|
|
|
1024
1205
|
publicSkillId,
|
|
1025
1206
|
schemaVersion: registry.eventSchemaVersion || EVENT_SCHEMA_VERSION,
|
|
1026
1207
|
event: 'completed',
|
|
1208
|
+
distributionPlatform: hub,
|
|
1209
|
+
runtimeAgent: '<agent actually running the Skill, e.g. coze|workbuddy|codex|claude|dify|custom-agent>',
|
|
1210
|
+
runtimeHost: '<desktop|web|server|mobile|unknown>',
|
|
1027
1211
|
platform: hub,
|
|
1212
|
+
skillKey: inspected.skillKey,
|
|
1213
|
+
creatorCardUrl,
|
|
1214
|
+
sourceSurface: '<profile|hub|agent|direct>',
|
|
1215
|
+
platformSkillUrl: '<published platform skill url or null>',
|
|
1216
|
+
platformName: '<user-facing platform name, e.g. Red Skill or Coze>',
|
|
1028
1217
|
installationId: '<stable anonymous install id>',
|
|
1029
1218
|
invocationId: '<unique call id>',
|
|
1030
|
-
scenario: '<short
|
|
1219
|
+
scenario: '<machine-readable short scenario key>',
|
|
1220
|
+
scenarioLabel: '<short user-facing scenario label>',
|
|
1031
1221
|
agentKind: '<optional agent name>'
|
|
1032
1222
|
};
|
|
1033
1223
|
const hubLine = hubInstruction(hub);
|
|
@@ -1056,13 +1246,19 @@ async function uploadPrompt(inspected, flags) {
|
|
|
1056
1246
|
`- 版本:${inspected.version}`,
|
|
1057
1247
|
`- 描述:${inspected.description || '<从 SKILL.md/README.md 中提炼>'}`,
|
|
1058
1248
|
`- 创作者名片:${creatorCardUrl}`,
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1249
|
+
...creatorProfileLines,
|
|
1250
|
+
`- Red Skill 上传包按官方流程准备;如果平台接受 Markdown 源包,可以包含 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 和 ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这两个无密钥协议文件。`,
|
|
1251
|
+
`- ${WORK_DIR}/、public token、signing secret、registry id、package sha、storage id、upload job id、runtime-events.js 和内部 handoff/checklist 默认只在本地保留,不进入 Red 公开包。`,
|
|
1252
|
+
`- publicSkillId 和 Agent Ack endpoint 只能出现在 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置里;不要写进营销介绍、用户可见回复或截图说明。`,
|
|
1253
|
+
'- SKILL.md 最开头只写公开来源、安全使用说明和创作者名片链接;不要要求下游 Agent 在用户可见回复里追加 telemetry JSON。',
|
|
1254
|
+
'- 如果输出数字或链接,必须结构化展示;如果平台无法执行安全 Ack,不要伪造虾舍 runtime 数据。',
|
|
1062
1255
|
'- 上传成功后可以用 hub_upload_succeeded 记录公开 URL;这属于 attribution,不代表真实运行次数。',
|
|
1063
|
-
|
|
1256
|
+
`- 如果 Agent 读取 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置并执行 Agent Ack,installed / used / completed / failed 会进入虾舍 runtime 统计。`,
|
|
1064
1257
|
'- 如果平台公开展示下载、收藏、安装或使用数字,可以后续按 reported 数据手动导入;不要把 reported 伪造成 runtime。',
|
|
1065
|
-
'- 先检查“能否安全上传”,再检查“Agent Ack 能否被执行”;二者在报告里分开。'
|
|
1258
|
+
'- 先检查“能否安全上传”,再检查“Agent Ack 能否被执行”;二者在报告里分开。',
|
|
1259
|
+
`- Agent Ack 的字段要拆清楚:distributionPlatform 是分发平台,runtimeAgent 是实际运行的 Agent,runtimeHost 是宿主环境,sourceSurface 是发现入口,scenario 是枚举键,scenarioLabel 是短的人类可读场景。`,
|
|
1260
|
+
`- scenario 尽量使用这些枚举之一:${SCENARIO_ENUM_EXAMPLES.join(', ')}。`,
|
|
1261
|
+
`- Agent Ack payload 不得包含:${FORBIDDEN_ACK_FIELDS.join(', ')}。`
|
|
1066
1262
|
]
|
|
1067
1263
|
: [
|
|
1068
1264
|
`${PRODUCT_NAME} registry 附加要求:`,
|
|
@@ -1071,27 +1267,31 @@ async function uploadPrompt(inspected, flags) {
|
|
|
1071
1267
|
`- 版本:${inspected.version}`,
|
|
1072
1268
|
`- 描述:${inspected.description || '<从 SKILL.md/README.md 中提炼>'}`,
|
|
1073
1269
|
`- 创作者名片:${creatorCardUrl}`,
|
|
1074
|
-
|
|
1270
|
+
...creatorProfileLines,
|
|
1075
1271
|
'- 不要读取或上传 .env、密钥、浏览器数据、SSH key、系统目录或 node_modules。',
|
|
1076
1272
|
'- 不要新增 postinstall hook、后台常驻进程、隐藏脚本或混淆逻辑。',
|
|
1077
|
-
'- 公开 Skill
|
|
1273
|
+
'- 公开 Skill 说明应保留创作者名片链接,作为 Skill 来源和创作者宣传入口;用户可见回复只在相关时提及,不要机械追加。',
|
|
1274
|
+
'- 公开 Skill 说明和 SKILL.md 不应泄露 package sha、storage id、public token、内部 endpoint、本地路径或后台字段;也不要要求用户可见回复追加 telemetry JSON。',
|
|
1275
|
+
'- 如果回答包含数字或链接,必须结构化展示。',
|
|
1078
1276
|
`- 如果平台要求安全说明,请明确说明这是用户显式发布的 Skill 项目,${MANIFEST_FILE} 只包含公开 registry 归因和可关闭的 analytics 声明。`,
|
|
1079
1277
|
`- 如果目标平台支持工具调用、MCP server、webhook 或 Skill runtime HTTP 请求,请在 Skill 被调用时向 ${PRODUCT_NAME} registry 发送聚合事件:skill_invoked / skill_completed / skill_failed。`,
|
|
1080
1278
|
`- 如果 runtime 没有向 ${registryUrl} 发送事件,${PRODUCT_NAME} 无法看到真实调用量;第三方平台内部显示“已使用”不会自动同步到 ${PRODUCT_NAME}。`,
|
|
1081
1279
|
'- 事件里只放公开元数据、聚合状态和简短场景标签,不要发送用户输入原文、私密业务内容、账号凭据或文件内容。',
|
|
1082
|
-
'- installationId 必须是稳定的匿名实例 ID
|
|
1280
|
+
'- installationId 必须是稳定的匿名实例 ID,用于统计唯一安装实例;不要使用邮箱、手机号、真实用户名、设备序列号或未哈希的本地路径。',
|
|
1281
|
+
`- 发送 Agent Ack 前必须先读取 \`${WORK_DIR}/AGENT_ACK.md\`、\`${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md\` 或私有 runtime 配置,拿到准确的 publicSkillId;不要用技能名称、目录名、包名或平台 slug 猜 publicSkillId。`,
|
|
1282
|
+
'- 如果平台没有提供 installationId,就用 publicSkillId + 平台名 + 安装目录或 session id 生成本地哈希,并把哈希后的匿名 ID 持久化在本地私有配置里。',
|
|
1083
1283
|
'- 发布前请至少发送一次测试事件,并确认 Dashboard 能看到对应 hub/sourceSurface;否则把这个平台标记为“仅上传/手动上报”。'
|
|
1084
1284
|
];
|
|
1085
1285
|
const runtimePostLines = redHub
|
|
1086
1286
|
? [
|
|
1087
|
-
|
|
1287
|
+
`发布成功后,请只在本地或平台私有流程里把公开链接回传给${PRODUCT_NAME} registry。不要把下面的 endpoint、token 或事件 payload 放进 Red 公开介绍。`,
|
|
1088
1288
|
'',
|
|
1089
1289
|
`POST ${registryUrl}`,
|
|
1090
1290
|
'Content-Type: application/json',
|
|
1091
1291
|
'',
|
|
1092
1292
|
JSON.stringify(eventPayload, null, 2),
|
|
1093
1293
|
'',
|
|
1094
|
-
'Red Skill
|
|
1294
|
+
'Red Skill 的真实统计只能由宿主平台、安装器或本地私有配置执行安全 Ack 后产生。公开 SKILL.md 只保留创作者名片和安全使用边界。'
|
|
1095
1295
|
]
|
|
1096
1296
|
: [
|
|
1097
1297
|
`发布成功后,请把公开链接和平台返回的信息回传给${PRODUCT_NAME} registry。只发送公开元数据和聚合事件,不发送用户内容、私密输入或账号凭据。`,
|
|
@@ -1147,23 +1347,34 @@ async function uploadPrompt(inspected, flags) {
|
|
|
1147
1347
|
'- attribution:发布、上传、归因链接点击、公开披露访问,只代表来源漏斗。',
|
|
1148
1348
|
'- reported:平台公开数字或手动导入,例如收藏、下载、安装、静态平台显示使用数。',
|
|
1149
1349
|
'- 如果无法确认 runtime callback 已经接在真实调用边界,就把该平台标记为 attribution 或 reported,不要伪造成 runtime。',
|
|
1150
|
-
'- Agent ack
|
|
1350
|
+
'- Agent ack:只有平台宿主、安装器或本地私有配置实际执行安全 Ack,才可以在安装/使用/完成/失败后计入虾舍实时使用数据。',
|
|
1151
1351
|
'',
|
|
1152
1352
|
...compatibilityLines,
|
|
1153
1353
|
'',
|
|
1154
1354
|
...disclosureLines,
|
|
1155
1355
|
'',
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1356
|
+
...(redHub
|
|
1357
|
+
? [
|
|
1358
|
+
'Red 公开包边界:',
|
|
1359
|
+
'',
|
|
1360
|
+
`- 可以包含:SKILL.md、README/公开 references,以及 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md、${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这类无密钥协议文件。`,
|
|
1361
|
+
`- 不要包含:${WORK_DIR}/、${WORK_DIR}/${MANIFEST_FILE}、runtime-events.js、public token、signing secret、registry id、package hash、storage id、upload job id、本地路径、cookie、账号会话或构建产物。`,
|
|
1362
|
+
`- publicSkillId 和 Agent Ack endpoint 只能放在 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置里;不要放进营销介绍、用户可见回复或截图说明。`,
|
|
1363
|
+
'- 如果宿主 Agent 不能读取协议文件/私有配置并执行 Ack,Red 安装成功不会自动变成虾舍 runtime 数据。'
|
|
1364
|
+
]
|
|
1365
|
+
: [
|
|
1366
|
+
'Agent Ack 指令(只放在私有 runtime 配置、平台后台字段,或明确不会公开展示的安装说明中):',
|
|
1367
|
+
'',
|
|
1368
|
+
'```markdown',
|
|
1369
|
+
agentAckInstructionBlock(inspected, hub),
|
|
1370
|
+
'```',
|
|
1371
|
+
'',
|
|
1372
|
+
`Agent Ack endpoint: ${agentAckUrl}`,
|
|
1373
|
+
'',
|
|
1374
|
+
'```json',
|
|
1375
|
+
JSON.stringify(agentAckPayload, null, 2),
|
|
1376
|
+
'```'
|
|
1377
|
+
]),
|
|
1167
1378
|
'',
|
|
1168
1379
|
...runtimeGuidanceLines,
|
|
1169
1380
|
'',
|
|
@@ -1214,17 +1425,18 @@ async function writeUnifiedHandoff(root, args) {
|
|
|
1214
1425
|
`2. Use the matching registry companion prompt from ${relativeOutDir}/upload-<hub>.md only as an internal checklist.`,
|
|
1215
1426
|
'3. Treat the third-party platform official prompt / CLI / page form as authoritative.',
|
|
1216
1427
|
`4. For XiaShe Store, create or reuse the searchable listing. For Red Skill, use the official uploader.md / skillhub-upload flow.`,
|
|
1217
|
-
|
|
1428
|
+
`5. For Red Skill, include no-secret ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md only when Markdown source files are accepted; never upload private ${WORK_DIR}/ files.`,
|
|
1218
1429
|
'6. Keep the creator card link in public Skill instructions and user-facing replies so downstream users can identify the Skill source and visit the creator.',
|
|
1219
|
-
|
|
1430
|
+
`7. If the Agent can call HTTP/MCP, report installed / used / completed / failed through Agent Ack after reading ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md or private runtime config.`,
|
|
1220
1431
|
'8. Before submitting, show the user the exact files and form fields that will be uploaded.',
|
|
1221
1432
|
'9. Never upload .env, secrets, SSH keys, browser data, node_modules, dist, build, or unrelated local files.',
|
|
1222
1433
|
'',
|
|
1223
1434
|
'Red Skill exception:',
|
|
1224
1435
|
'',
|
|
1225
1436
|
'- Red Skill should use the official uploader.md / skillhub-upload / web upload flow as the source of truth.',
|
|
1226
|
-
`-
|
|
1227
|
-
|
|
1437
|
+
`- Public Red Markdown packages may include ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md as no-secret protocol/disclosure files.`,
|
|
1438
|
+
`- Keep ${WORK_DIR}/${MANIFEST_FILE}, ${WORK_DIR}/runtime-events.js, public tokens, signing secrets, package hashes, storage ids, upload job ids, and ${WORK_DIR} handoff/checklist files local-only.`,
|
|
1439
|
+
'- Red Skill reports XiaShe runtime through Agent Ack when the host Agent calls it from the no-secret protocol file or private runtime config. If no Agent Ack occurs, show only upload/reported data.',
|
|
1228
1440
|
'- Check only the basic Red Skill safety rules before submission: truthful description, clear permission/use disclosure, no hidden behavior, no malicious code, no account/cookie/local-data collection, no automated Red account operation, no illegal/violating content, and no plagiarism.',
|
|
1229
1441
|
'',
|
|
1230
1442
|
'## Platform mapping',
|
|
@@ -1247,17 +1459,19 @@ async function writeUnifiedHandoff(root, args) {
|
|
|
1247
1459
|
'',
|
|
1248
1460
|
'## Analytics callback',
|
|
1249
1461
|
'',
|
|
1250
|
-
`Registry endpoint: ${registryUrl}`,
|
|
1251
|
-
`Agent Ack endpoint: ${registry.agentAckUrl || DEFAULT_AGENT_ACK_URL}`,
|
|
1462
|
+
`Registry endpoint for private runtime config only: ${registryUrl}`,
|
|
1463
|
+
`Agent Ack endpoint for ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md or private runtime config: ${registry.agentAckUrl || DEFAULT_AGENT_ACK_URL}`,
|
|
1252
1464
|
`Creator card: ${creatorCardUrl}`,
|
|
1253
1465
|
`Event schema: ${registry.eventSchemaVersion || EVENT_SCHEMA_VERSION}`,
|
|
1254
1466
|
'',
|
|
1255
|
-
|
|
1467
|
+
`The simplest cross-platform runtime path is Agent Ack. For Red Skill, the host Agent should read ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md or private runtime config first, then report \`installed\`, \`used\`, \`completed\`, or \`failed\` with publicSkillId, anonymous installationId, and invocationId. Do not paste endpoint/publicSkillId into marketing descriptions, screenshots, or user-visible replies. No public token or account login is required for Agent Ack.`,
|
|
1256
1468
|
'The creator card link is public attribution and should remain visible in allowed platform instructions and user-facing Skill replies. It is not a secret and should not be replaced by registry ids or tokens.',
|
|
1469
|
+
`Before sending Agent Ack, read ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, ${WORK_DIR}/AGENT_ACK.md, or the private registry manifest and use the exact \`publicSkillId\`. Never guess it from the Skill title, package name, directory name, or marketplace slug.`,
|
|
1470
|
+
'For `installationId`, use a stable anonymous value for this installed copy/runtime connection. If the platform does not provide one, hash `publicSkillId + platform name + install directory or session id`, store only the opaque hash locally, and never send raw paths, emails, phone numbers, real names, prompts, or account identifiers.',
|
|
1257
1471
|
'',
|
|
1258
|
-
|
|
1472
|
+
`Use Agent Ack as the default no-secret path through ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md or private runtime config. Only add runtime event callbacks when the target platform or runtime explicitly supports HTTP/API/MCP/webhook calls. Do not add hidden scripts, background services, install hooks, obfuscated code, or user-visible instructions that expose telemetry payloads.`,
|
|
1259
1473
|
'',
|
|
1260
|
-
`No runtime callback means no live ${PRODUCT_NAME} analytics. A third-party platform saying the Skill was used does not update ${PRODUCT_NAME} unless that runtime sends events to the registry endpoint.`,
|
|
1474
|
+
`No runtime callback means no live ${PRODUCT_NAME} analytics. A third-party platform saying the Skill was used does not update ${PRODUCT_NAME} unless that runtime sends events through private config to the registry endpoint.`,
|
|
1261
1475
|
'',
|
|
1262
1476
|
'Best-effort callback placement:',
|
|
1263
1477
|
'',
|
|
@@ -1272,8 +1486,13 @@ async function writeUnifiedHandoff(root, args) {
|
|
|
1272
1486
|
'```json',
|
|
1273
1487
|
JSON.stringify({
|
|
1274
1488
|
hub: '<red|clawhub|skillhub|claude|dify|coze|generic>',
|
|
1489
|
+
distributionPlatform: '<where the package was distributed>',
|
|
1490
|
+
runtimeAgent: '<agent actually running the Skill>',
|
|
1491
|
+
runtimeHost: '<desktop|web|server|mobile|unknown>',
|
|
1275
1492
|
sourceSurface: '<same as hub unless the platform requires another public label>',
|
|
1276
|
-
scenario: '<short
|
|
1493
|
+
scenario: '<machine-readable short scenario key>',
|
|
1494
|
+
scenarioLabel: '<short user-facing scenario label only>',
|
|
1495
|
+
platformName: '<user-facing platform name only>',
|
|
1277
1496
|
installationId: '<stable anonymous install id>',
|
|
1278
1497
|
invocationId: '<unique invocation id>'
|
|
1279
1498
|
}, null, 2),
|
|
@@ -1329,7 +1548,9 @@ async function writeRuntimeSnippet(root, flags) {
|
|
|
1329
1548
|
installationId: '<anonymous-stable-install-id>',
|
|
1330
1549
|
invocationId: '<unique-call-id>',
|
|
1331
1550
|
idempotencyKey: '<unique-call-id>:skill_invoked',
|
|
1332
|
-
scenario: '<short-scenario-
|
|
1551
|
+
scenario: '<short-scenario-key>',
|
|
1552
|
+
scenarioLabel: '<short user-facing scenario label>',
|
|
1553
|
+
platformName: '<user-facing platform name>'
|
|
1333
1554
|
}))
|
|
1334
1555
|
].join('\n')
|
|
1335
1556
|
: [
|
|
@@ -1373,9 +1594,10 @@ async function writeRuntimeSnippet(root, flags) {
|
|
|
1373
1594
|
' invocationId,',
|
|
1374
1595
|
' idempotencyKey: options.idempotencyKey || `${invocationId || options.installationId || "runtime"}:${eventType}:${occurredAt}`,',
|
|
1375
1596
|
' scenario: options.scenario,',
|
|
1597
|
+
' scenarioLabel: options.scenarioLabel,',
|
|
1598
|
+
' platformName: options.platformName,',
|
|
1376
1599
|
' sourceSurface: options.sourceSurface,',
|
|
1377
1600
|
' campaign: options.campaign,',
|
|
1378
|
-
' packageSha256: options.packageSha256,',
|
|
1379
1601
|
' occurredAt',
|
|
1380
1602
|
' };',
|
|
1381
1603
|
' const signature = await hmacSha256(XIASHE_SKILL_SIGNING_SECRET, canonicalSignaturePayload(payload));',
|
|
@@ -1431,7 +1653,7 @@ async function submitTrackEvent(root, flags) {
|
|
|
1431
1653
|
sourceSurface: normalizeText(flags['source-surface'] || 'cli', 120),
|
|
1432
1654
|
campaign: normalizeText(flags.campaign, 120) || undefined,
|
|
1433
1655
|
platformSkillUrl: normalizeText(flags['platform-skill-url'], 1000) || undefined,
|
|
1434
|
-
|
|
1656
|
+
creatorCardUrl: normalizeText(inspected.registry?.creatorCardUrl, 1000) || undefined,
|
|
1435
1657
|
idempotencyKey: normalizeText(flags.idempotencyKey || flags['idempotency-key'], 220) || `${invocationId}:${eventType}`,
|
|
1436
1658
|
occurredAt: Number.isFinite(occurredAt) ? occurredAt : Date.now()
|
|
1437
1659
|
};
|
|
@@ -1470,6 +1692,10 @@ function registryBlockPresent(text) {
|
|
|
1470
1692
|
return /<!--\s*(?:xiashe|agentpie)-registry:start\s*-->[\s\S]*?<!--\s*(?:xiashe|agentpie)-registry:end\s*-->/i.test(text || '');
|
|
1471
1693
|
}
|
|
1472
1694
|
|
|
1695
|
+
function redPublicTrackingBlockPresent(text) {
|
|
1696
|
+
return /<!--\s*(?:xiashe|agentpie)-red-public-tracking:start\s*-->[\s\S]*?<!--\s*(?:xiashe|agentpie)-red-public-tracking:end\s*-->/i.test(text || '');
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1473
1699
|
function hasEntryInstructions(text, packageJson) {
|
|
1474
1700
|
if (packageJson?.main || packageJson?.bin || packageJson?.scripts) return true;
|
|
1475
1701
|
return /(入口|使用|安装|运行|调用|命令|Usage|Install|Quick start|Getting started|CLI|MCP|API|Entrypoint|Run)/i.test(text || '');
|
|
@@ -1492,12 +1718,17 @@ async function runDoctor(root, flags) {
|
|
|
1492
1718
|
const manifestPath = path.join(inspected.root, MANIFEST_FILE);
|
|
1493
1719
|
const localManifestPath = path.join(inspected.root, WORK_DIR, MANIFEST_FILE);
|
|
1494
1720
|
const disclosurePath = path.join(inspected.root, WORK_DIR, 'REGISTRY_DISCLOSURE.md');
|
|
1721
|
+
const publicAgentAckPath = path.join(inspected.root, PUBLIC_PROTOCOL_DIR, 'AGENT_ACK.md');
|
|
1722
|
+
const publicDisclosurePath = path.join(inspected.root, PUBLIC_PROTOCOL_DIR, 'REGISTRY_DISCLOSURE.md');
|
|
1495
1723
|
const handoffPath = path.join(inspected.root, WORK_DIR, HANDOFF_FILE);
|
|
1496
1724
|
const snippetPath = path.join(inspected.root, WORK_DIR, 'runtime-events.js');
|
|
1497
1725
|
const packageJson = await readJsonFile(path.join(inspected.root, 'package.json')).catch(() => null);
|
|
1498
1726
|
const skillMd = await readSmallTextFile(skillMdPath);
|
|
1499
1727
|
const readme = await readSmallTextFile(readmePath);
|
|
1728
|
+
const publicAgentAck = await readSmallTextFile(publicAgentAckPath);
|
|
1729
|
+
const publicDisclosure = await readSmallTextFile(publicDisclosurePath);
|
|
1500
1730
|
const snippet = await readSmallTextFile(snippetPath);
|
|
1731
|
+
const packagePlan = packagePlanForHub(inspected, hub);
|
|
1501
1732
|
const scannedCallbackFiles = [];
|
|
1502
1733
|
let scannedCallbackText = '';
|
|
1503
1734
|
for (const file of inspected.package.files.slice(0, 160)) {
|
|
@@ -1536,16 +1767,37 @@ async function runDoctor(root, flags) {
|
|
|
1536
1767
|
registryBlockPresent(skillMd)
|
|
1537
1768
|
? doctorCheck('registry_block', 'Registry block', 'pass', 'SKILL.md contains the explicit registry disclosure block.')
|
|
1538
1769
|
: redHub
|
|
1539
|
-
?
|
|
1770
|
+
? redPublicTrackingBlockPresent(skillMd)
|
|
1771
|
+
? doctorCheck('registry_block', 'Registry block', 'pass', 'SKILL.md contains the safe Red public tracking contract.')
|
|
1772
|
+
: doctorCheck('registry_block', 'Registry block', 'warn', 'SKILL.md is missing the safe Red public tracking contract.', `Run ${COMMAND_NAME} setup . --code <code> --hub red to write the Red-safe public source block.`)
|
|
1540
1773
|
: doctorCheck('registry_block', 'Registry block', 'warn', 'SKILL.md does not contain a registry block.', `Run ${COMMAND_NAME} setup . --code <code> --embed-skill-md if the target hub allows public registry disclosure in SKILL.md.`),
|
|
1541
1774
|
runtimeCallbackPresent
|
|
1542
1775
|
? doctorCheck('runtime_callback', 'Runtime callback', 'pass', scannedCallbackFiles.length > 0 ? `Runtime callback references found in ${scannedCallbackFiles.slice(0, 5).join(', ')}.` : 'Runtime callback snippet/reference detected.')
|
|
1543
1776
|
: redHub
|
|
1544
|
-
? doctorCheck('runtime_callback', 'Runtime callback', 'pass',
|
|
1777
|
+
? doctorCheck('runtime_callback', 'Runtime callback', 'pass', `Red Skill uses Agent Ack from ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md or private runtime config for no-secret runtime tracking. Do not expose telemetry payloads in public Red descriptions.`)
|
|
1545
1778
|
: doctorCheck('runtime_callback', 'Runtime callback', 'warn', 'No runtime callback reference detected.', `Use ${COMMAND_NAME} snippet . --target js and ask the Agent to wire it at the real invocation boundary if the hub allows HTTP/API/MCP callbacks.`),
|
|
1546
|
-
|
|
1779
|
+
redHub
|
|
1780
|
+
? existsSync(publicDisclosurePath) && publicDisclosure.includes('distributionPlatform') && publicDisclosure.includes('runtimeAgent') && publicDisclosure.includes('scenario')
|
|
1781
|
+
? doctorCheck('public_disclosure', 'Public Red disclosure', 'pass', `Found ${path.relative(inspected.root, publicDisclosurePath)} with canonical analytics fields.`)
|
|
1782
|
+
: doctorCheck('public_disclosure', 'Public Red disclosure', 'warn', `${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md is missing or incomplete.`, `Run ${COMMAND_NAME} setup . --code <code> --hub red to generate the no-secret public disclosure.`)
|
|
1783
|
+
: existsSync(disclosurePath)
|
|
1547
1784
|
? doctorCheck('disclosure', 'Read-only disclosure', 'pass', `Found ${path.relative(inspected.root, disclosurePath)}.`)
|
|
1548
1785
|
: doctorCheck('disclosure', 'Read-only disclosure', 'warn', 'REGISTRY_DISCLOSURE.md is missing.', `Run ${COMMAND_NAME} setup . --code <code> to generate platform review disclosure.`),
|
|
1786
|
+
redHub && existsSync(publicAgentAckPath) && ACK_EVENTS.every((event) => publicAgentAck.includes(event)) && publicAgentAck.includes('publicSkillId') && publicAgentAck.includes('Do not guess')
|
|
1787
|
+
? doctorCheck('public_agent_ack', 'Public Agent Ack protocol', 'pass', `Found ${path.relative(inspected.root, publicAgentAckPath)} with install/use/complete/fail Ack rules.`)
|
|
1788
|
+
: redHub
|
|
1789
|
+
? doctorCheck('public_agent_ack', 'Public Agent Ack protocol', 'warn', `${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md is missing or incomplete.`, `Run ${COMMAND_NAME} setup . --code <code> --hub red to generate the no-secret Agent Ack protocol.`)
|
|
1790
|
+
: doctorCheck('public_agent_ack', 'Public Agent Ack protocol', 'pass', 'Not required for this hub.'),
|
|
1791
|
+
redHub && packagePlan.candidateUploadFiles.some((file) => file.startsWith(`${WORK_DIR}/`) || file.startsWith('.agentpie/'))
|
|
1792
|
+
? doctorCheck('red_upload_allowlist', 'Red upload allowlist', 'fail', `Red candidate upload files include private registry files: ${packagePlan.candidateUploadFiles.filter((file) => file.startsWith(`${WORK_DIR}/`) || file.startsWith('.agentpie/')).join(', ')}`, `Remove private ${WORK_DIR}/ files from Red public packages.`)
|
|
1793
|
+
: redHub
|
|
1794
|
+
? doctorCheck('red_upload_allowlist', 'Red upload allowlist', 'pass', 'Red candidate upload files exclude private registry directories.')
|
|
1795
|
+
: doctorCheck('red_upload_allowlist', 'Red upload allowlist', 'pass', 'Not required for this hub.'),
|
|
1796
|
+
redHub && (publicAgentAck.includes(FORBIDDEN_ACK_FIELDS[0]) || publicDisclosure.includes('package hashes'))
|
|
1797
|
+
? doctorCheck('red_payload_boundary', 'Red payload boundary', 'pass', 'Public protocol documents describe forbidden private payload fields and package boundaries.')
|
|
1798
|
+
: redHub
|
|
1799
|
+
? doctorCheck('red_payload_boundary', 'Red payload boundary', 'warn', 'Public Red protocol documents do not clearly state payload boundaries.', `Regenerate with ${COMMAND_NAME} setup . --code <code> --hub red.`)
|
|
1800
|
+
: doctorCheck('red_payload_boundary', 'Red payload boundary', 'pass', 'Not required for this hub.'),
|
|
1549
1801
|
existsSync(handoffPath)
|
|
1550
1802
|
? doctorCheck('handoff', 'Agent handoff', 'pass', `Found ${path.relative(inspected.root, handoffPath)}.`)
|
|
1551
1803
|
: doctorCheck('handoff', 'Agent handoff', 'warn', 'Unified upload handoff is missing.', `Run ${COMMAND_NAME} setup . --code <code> --hub all.`),
|
|
@@ -1678,6 +1930,10 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1678
1930
|
'local-manifest': true,
|
|
1679
1931
|
'manifest-path': flags['manifest-path'] || path.join(outDir, MANIFEST_FILE)
|
|
1680
1932
|
});
|
|
1933
|
+
const inspectedAfterManifestForPublicFiles = await inspectSkill(absoluteRoot, flags);
|
|
1934
|
+
const publicProtocol = hubs.includes('red')
|
|
1935
|
+
? await writePublicProtocolFiles(absoluteRoot, inspectedAfterManifestForPublicFiles, 'red')
|
|
1936
|
+
: null;
|
|
1681
1937
|
|
|
1682
1938
|
const promptResults = [];
|
|
1683
1939
|
const packagePlans = [];
|
|
@@ -1767,10 +2023,13 @@ async function setupAgentWorkflow(root, flags) {
|
|
|
1767
2023
|
snippetPath: snippetResult ? snippetResult.outPath : null,
|
|
1768
2024
|
promptEvents,
|
|
1769
2025
|
warnings,
|
|
2026
|
+
publicProtocol,
|
|
1770
2027
|
next: [
|
|
1771
2028
|
`Use ${path.relative(absoluteRoot, handoffPath)} as the single Agent handoff. The Agent should not ask the user to manually pick registry files.`,
|
|
1772
2029
|
`Use ${path.relative(absoluteRoot, profilesPath)} and package-<hub>.json as the platform review profiles and upload allowlists.`,
|
|
1773
|
-
|
|
2030
|
+
publicProtocol
|
|
2031
|
+
? `For Red public Markdown packages, include ${path.relative(absoluteRoot, publicProtocol.agentAckPath)} and ${path.relative(absoluteRoot, publicProtocol.disclosurePath)} when accepted by the platform; keep ${WORK_DIR}/ private.`
|
|
2032
|
+
: `Use ${path.relative(absoluteRoot, agentAckPath)} only in private runtime config, platform admin-only fields, or non-public handoff notes. Do not publish endpoint/publicSkillId in restrictive marketplace descriptions.`,
|
|
1774
2033
|
'When the user later pastes a Red Skill / ClawHub / SkillHub / Claude / Dify / Coze official prompt, the Agent should identify the platform and merge only the matching platform-safe checklist internally.',
|
|
1775
2034
|
'Each prepared upload-<hub>.md pins the correct hub/sourceSurface value so platform analytics stay separated, but those files are internal checklists for the Agent.',
|
|
1776
2035
|
skillMdPath
|
|
@@ -1833,6 +2092,7 @@ async function main() {
|
|
|
1833
2092
|
`Platform profiles: ${result.profilesPath}`,
|
|
1834
2093
|
...result.packagePlanPaths.map((item) => `Package plan (${item.hub}): ${item.planPath}`),
|
|
1835
2094
|
result.skillMdPath ? `Updated: ${result.skillMdPath}` : null,
|
|
2095
|
+
result.publicProtocol ? `Public Red protocol: ${result.publicProtocol.agentAckPath}, ${result.publicProtocol.disclosurePath}` : null,
|
|
1836
2096
|
`Disclosure: ${result.disclosurePath}`,
|
|
1837
2097
|
result.snippetPath ? `Runtime snippet: ${result.snippetPath}` : null,
|
|
1838
2098
|
...result.warnings.map((warning) => `Warning: ${warning}`),
|
|
@@ -1862,6 +2122,8 @@ async function main() {
|
|
|
1862
2122
|
...result.promptPaths.map((item) => `Wrote ${item.promptPath}`),
|
|
1863
2123
|
`Wrote ${result.profilesPath}`,
|
|
1864
2124
|
...result.packagePlanPaths.map((item) => `Wrote ${item.planPath}`),
|
|
2125
|
+
result.publicProtocol ? `Wrote ${result.publicProtocol.agentAckPath}` : null,
|
|
2126
|
+
result.publicProtocol ? `Wrote ${result.publicProtocol.disclosurePath}` : null,
|
|
1865
2127
|
`Wrote ${result.disclosurePath}`,
|
|
1866
2128
|
result.snippetPath ? `Wrote ${result.snippetPath}` : null,
|
|
1867
2129
|
...result.warnings.map((warning) => `Warning: ${warning}`),
|