@xiashe/skill 0.1.21 → 0.1.23

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.
@@ -1,19 +1,97 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { createHash, createHmac } from 'node:crypto';
4
- import { existsSync } from 'node:fs';
4
+ import { existsSync, readFileSync } from 'node:fs';
5
5
  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
+ import { fileURLToPath } from 'node:url';
8
9
  import { promisify } from 'node:util';
9
10
  import { gzip } from 'node:zlib';
10
11
 
11
- const VERSION = '0.1.21';
12
+ function parseEnvFile(filePath) {
13
+ if (!existsSync(filePath)) return {};
14
+ const parsed = {};
15
+ const raw = readFileSync(filePath, 'utf8');
16
+ for (const line of raw.split(/\r?\n/)) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed || trimmed.startsWith('#')) continue;
19
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
20
+ if (!match) continue;
21
+ let value = match[2].trim();
22
+ if (
23
+ (value.startsWith('"') && value.endsWith('"')) ||
24
+ (value.startsWith("'") && value.endsWith("'"))
25
+ ) {
26
+ value = value.slice(1, -1);
27
+ }
28
+ parsed[match[1]] = value;
29
+ }
30
+ return parsed;
31
+ }
32
+
33
+ function nearestEnvFiles() {
34
+ const files = [];
35
+ let cursor = process.cwd();
36
+ for (let index = 0; index < 8; index += 1) {
37
+ files.push(path.join(cursor, '.env.local'));
38
+ files.push(path.join(cursor, 'convex', '.env.local'));
39
+ const next = path.dirname(cursor);
40
+ if (next === cursor) break;
41
+ cursor = next;
42
+ }
43
+ return files;
44
+ }
45
+
46
+ function loadLocalEnvDefaults() {
47
+ const cliDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
48
+ const candidates = [
49
+ path.join(cliDir, '.env.local'),
50
+ path.join(cliDir, 'convex', '.env.local'),
51
+ ...nearestEnvFiles()
52
+ ];
53
+ const env = {};
54
+ for (const filePath of candidates) {
55
+ Object.assign(env, parseEnvFile(filePath));
56
+ }
57
+ return env;
58
+ }
59
+
60
+ function normalizeBaseUrl(value) {
61
+ const text = String(value || '').trim().replace(/\/+$/, '');
62
+ return /^https?:\/\//i.test(text) ? text : '';
63
+ }
64
+
65
+ function isTransportError(error) {
66
+ const message = error instanceof Error ? `${error.name || ''} ${error.message || ''}` : String(error || '');
67
+ return /abort|timeout|fetch failed|network|enotfound|eai_again|econnrefused|econnreset|etimedout|unable to verify|certificate/i.test(message);
68
+ }
69
+
70
+ const LOCAL_ENV_DEFAULTS = loadLocalEnvDefaults();
71
+ const VERSION = process.env.XIASHE_SKILL_CLI_VERSION || '0.1.23';
12
72
  const COMMAND_NAME = process.env.XIASHE_SKILL_CLI_NAME || 'xiashe-skill';
13
73
  const PRODUCT_NAME = process.env.XIASHE_SKILL_PRODUCT_NAME || (COMMAND_NAME === 'agentpie-skill' ? 'AgentPie' : 'XiaShe');
14
74
  const REGISTRY_PROVIDER = process.env.XIASHE_SKILL_REGISTRY_PROVIDER || (COMMAND_NAME === 'agentpie-skill' ? 'agentpie' : 'xiashe');
75
+ const LOCAL_AGENTPIE_BASE_URL = normalizeBaseUrl(
76
+ process.env.AGENTPIE_SKILL_DEFAULT_BASE_URL ||
77
+ LOCAL_ENV_DEFAULTS.AGENTPIE_SKILL_DEFAULT_BASE_URL ||
78
+ process.env.AGENTPIE_SKILL_REGISTRY_PUBLIC_URL ||
79
+ LOCAL_ENV_DEFAULTS.AGENTPIE_SKILL_REGISTRY_PUBLIC_URL ||
80
+ process.env.AGENTPIE_ACTIONS_URL ||
81
+ LOCAL_ENV_DEFAULTS.AGENTPIE_ACTIONS_URL ||
82
+ process.env.CREATOR_SKILL_REGISTRY_PUBLIC_URL ||
83
+ LOCAL_ENV_DEFAULTS.CREATOR_SKILL_REGISTRY_PUBLIC_URL ||
84
+ process.env.AGENTPIE_MCP_PUBLIC_URL ||
85
+ LOCAL_ENV_DEFAULTS.AGENTPIE_MCP_PUBLIC_URL ||
86
+ process.env.PUBLIC_CONVEX_SITE_URL ||
87
+ LOCAL_ENV_DEFAULTS.PUBLIC_CONVEX_SITE_URL ||
88
+ process.env.CONVEX_SITE_URL ||
89
+ LOCAL_ENV_DEFAULTS.CONVEX_SITE_URL ||
90
+ process.env.EXPO_PUBLIC_CONVEX_SITE_URL ||
91
+ LOCAL_ENV_DEFAULTS.EXPO_PUBLIC_CONVEX_SITE_URL
92
+ );
15
93
  const DEFAULT_BASE_URL = process.env.XIASHE_SKILL_DEFAULT_BASE_URL || (COMMAND_NAME === 'agentpie-skill'
16
- ? 'https://actions.agentpie.app'
94
+ ? LOCAL_AGENTPIE_BASE_URL || 'https://agentpie.app'
17
95
  : 'https://actions.xiashe.chat');
18
96
  const MANIFEST_FILE = process.env.XIASHE_SKILL_MANIFEST_FILE || (COMMAND_NAME === 'agentpie-skill'
19
97
  ? 'agentpie.skill.json'
@@ -24,18 +102,17 @@ const HANDOFF_FILE = 'UPLOAD_HANDOFF.md';
24
102
  const DEFAULT_REGISTRY_URL = process.env.XIASHE_SKILL_REGISTRY_URL || `${DEFAULT_BASE_URL}/registry/skill-events`;
25
103
  const DEFAULT_AGENT_ACK_URL = process.env.XIASHE_SKILL_AGENT_ACK_URL || `${DEFAULT_BASE_URL}/registry/agent-ack`;
26
104
  const DEFAULT_CLAIM_URL = process.env.XIASHE_SKILL_CLAIM_URL || `${DEFAULT_BASE_URL}/registry/skill/claim`;
27
- const DEFAULT_PACKAGE_UPLOAD_URL = process.env.XIASHE_SKILL_PACKAGE_UPLOAD_URL || `${DEFAULT_BASE_URL}/registry/skill/package-upload-url`;
28
- const DEFAULT_PACKAGE_ARTIFACT_URL = process.env.XIASHE_SKILL_PACKAGE_ARTIFACT_URL || `${DEFAULT_BASE_URL}/registry/skill/package-artifact`;
29
105
  const EVENT_SCHEMA_VERSION = process.env.XIASHE_SKILL_EVENT_SCHEMA_VERSION || (REGISTRY_PROVIDER === 'agentpie'
30
106
  ? 'agentpie.skill.event.v1'
31
107
  : 'xiashe.skill.event.v1');
32
108
  const RUNTIME_SCHEMA_VERSION = process.env.XIASHE_SKILL_RUNTIME_SCHEMA_VERSION || (REGISTRY_PROVIDER === 'agentpie'
33
109
  ? 'agentpie.skill.runtime.v1'
34
110
  : 'xiashe.skill.runtime.v1');
35
- const SUPPORTED_HUBS = ['xiashe', 'red', 'clawhub', 'skillhub', 'claude', 'codex', 'cursor', 'workbuddy', 'dify', 'coze', 'generic'];
36
- const DEFAULT_SETUP_HUBS = ['xiashe', 'red'];
111
+ const FIRST_PARTY_HUB = REGISTRY_PROVIDER === 'agentpie' ? 'agentpie' : 'xiashe';
112
+ const SUPPORTED_HUBS = ['agentpie', 'xiashe', 'red', 'clawhub', 'skillhub', 'claude', 'codex', 'cursor', 'workbuddy', 'dify', 'coze', 'generic'];
113
+ const DEFAULT_SETUP_HUBS = [FIRST_PARTY_HUB, 'red'];
37
114
  const ACK_EVENTS = ['installed', 'used', 'completed', 'failed'];
38
- const RUNTIME_DISTRIBUTION_PLATFORMS = ['xiaohongshu-redskill', 'xiashe', 'coze-store', 'local', 'unknown'];
115
+ const RUNTIME_DISTRIBUTION_PLATFORMS = ['xiaohongshu-redskill', 'agentpie', 'xiashe', 'coze-store', 'local', 'unknown'];
39
116
  const RUNTIME_AGENTS = ['codex', 'workbuddy', 'coze', 'claude', 'cursor', 'dify', 'custom-agent', 'unknown'];
40
117
  const RUNTIME_HOSTS = ['codex-desktop', 'coze-app', 'web-chat', 'desktop', 'web', 'server', 'mobile', 'unknown'];
41
118
  const RUNTIME_SOURCE_SURFACES = ['install', 'first-use-backfill', 'chat-use', 'manual-test', 'creator-card', 'dashboard', 'unknown'];
@@ -79,6 +156,16 @@ const FORBIDDEN_ACK_FIELDS = [
79
156
  'privateResearchMaterial'
80
157
  ];
81
158
  const PLATFORM_REVIEW_PROFILES = {
159
+ agentpie: {
160
+ label: 'AgentPie Registry',
161
+ packageMode: 'full_skill_package',
162
+ manifestPolicy: 'upload_allowed',
163
+ disclosurePlacement: ['Skill public page', 'README', `${WORK_DIR}/REGISTRY_DISCLOSURE.md`],
164
+ runtimeDataPolicy: 'runtime_capable',
165
+ defaultDataSource: 'runtime',
166
+ allowedFilePolicy: 'Upload the complete reviewed Skill package, including the local registry manifest.',
167
+ forbiddenPublicFiles: ['.env', 'credentials', 'node_modules', 'dist', 'build', 'browser profiles', 'SSH keys']
168
+ },
82
169
  xiashe: {
83
170
  label: 'XiaShe Store',
84
171
  packageMode: 'full_skill_package',
@@ -96,8 +183,8 @@ const PLATFORM_REVIEW_PROFILES = {
96
183
  disclosurePlacement: ['platform description field', 'README', 'SKILL.md', `${PUBLIC_PROTOCOL_DIR}/runtime.yaml`, `${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md`, `${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md`],
97
184
  runtimeDataPolicy: 'agent_ack_supported',
98
185
  defaultDataSource: 'runtime',
99
- 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}/runtime.yaml + ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md + ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md protocol files in the public package when Markdown/YAML 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.`,
100
- xiasheLocalOnlyFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'internal handoff/checklists'],
186
+ 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}/runtime.yaml + ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md + ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md protocol files in the public package when Markdown/YAML files are accepted. Keep ${WORK_DIR} private manifests, tokens, hashes, storage ids, and handoff files local-only. Send Agent Ack events only when the host can safely call HTTP/MCP.`,
187
+ localOnlyRegistryFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'internal handoff/checklists'],
101
188
  forbiddenPublicFiles: ['credentials', 'cookies', 'browser/account sessions', 'public tokens', 'signing secrets', 'hidden telemetry'],
102
189
  safetyChecklist: [
103
190
  '功能描述真实、清晰、有实际使用价值,不夸大或误导用户。',
@@ -116,7 +203,7 @@ const PLATFORM_REVIEW_PROFILES = {
116
203
  disclosurePlacement: ['SKILL.md', 'README', 'platform description field'],
117
204
  runtimeDataPolicy: 'agent_ack_supported',
118
205
  defaultDataSource: 'runtime',
119
- allowedFilePolicy: 'Follow ClawHub official file rules. Treat XiaShe registry files as local-only unless ClawHub explicitly accepts them. Add public Agent Ack instructions where ClawHub allows Skill instructions.',
206
+ allowedFilePolicy: `Follow ClawHub official file rules. Treat ${PRODUCT_NAME} registry files as local-only unless ClawHub explicitly accepts them. Add public Agent Ack instructions where ClawHub allows Skill instructions.`,
120
207
  forbiddenPublicFiles: [WORK_DIR, `${WORK_DIR}/${MANIFEST_FILE}`, `${WORK_DIR}/runtime-events.js`, 'public tokens', 'signing secrets']
121
208
  },
122
209
  skillhub: {
@@ -196,7 +283,7 @@ const PLATFORM_REVIEW_PROFILES = {
196
283
  disclosurePlacement: ['README', 'SKILL.md', 'platform description field'],
197
284
  runtimeDataPolicy: 'runtime_if_http_or_mcp',
198
285
  defaultDataSource: 'attribution',
199
- allowedFilePolicy: 'Follow the target platform official rules first; merge only safe XiaShe disclosure and callbacks.',
286
+ allowedFilePolicy: `Follow the target platform official rules first; merge only safe ${PRODUCT_NAME} disclosure and callbacks.`,
200
287
  forbiddenPublicFiles: ['public tokens in public docs', 'signing secrets', 'hidden background processes']
201
288
  }
202
289
  };
@@ -224,14 +311,15 @@ function usage() {
224
311
 
225
312
  Usage:
226
313
  ${COMMAND_NAME} inspect [skill-dir] [--json]
227
- ${COMMAND_NAME} publish [skill-dir] --code <dynamic-code> [--to xiashe,red]
228
- ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> [--hub all|xiashe|red]
229
- ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> [--hub all|xiashe|red]
314
+ ${COMMAND_NAME} publish [skill-dir] --code <dynamic-code> [--to ${FIRST_PARTY_HUB},red]
315
+ ${COMMAND_NAME} setup [skill-dir] --code <dynamic-code> [--hub all|${FIRST_PARTY_HUB}|red]
316
+ ${COMMAND_NAME} setup [skill-dir] --public-token <token> --skill-id <id> [--hub all|${FIRST_PARTY_HUB}|red]
230
317
  ${COMMAND_NAME} doctor [skill-dir] [--json]
318
+ ${COMMAND_NAME} preflight --claim-url <url> [--json]
231
319
  ${COMMAND_NAME} verify [skill-dir] [--hub <hub>] [--scenario <label>] [--dry-run]
232
320
  ${COMMAND_NAME} attach [skill-dir] --code <dynamic-code> [--name <name>]
233
321
  ${COMMAND_NAME} attach [skill-dir] --public-token <token> [--skill-id <id>] [--name <name>]
234
- ${COMMAND_NAME} prompt [skill-dir] --hub <xiashe|red> [--source-url <url>] [--out <file>]
322
+ ${COMMAND_NAME} prompt [skill-dir] --hub <${FIRST_PARTY_HUB}|red> [--source-url <url>] [--out <file>]
235
323
  ${COMMAND_NAME} snippet [skill-dir] [--target js|curl] [--out <file>]
236
324
  ${COMMAND_NAME} track [skill-dir] --event <event> [--hub <hub>] [--installation-id <id>] [--invocation-id <id>]
237
325
 
@@ -239,12 +327,13 @@ Options:
239
327
  --public-token <token> Public write token issued by ${PRODUCT_NAME} registry.
240
328
  --code <dynamic-code> One-time ${PRODUCT_NAME} dashboard code used to claim registry identity.
241
329
  --claim-url <url> Code claim endpoint. Defaults to XIASHE_SKILL_CLAIM_URL or ${DEFAULT_CLAIM_URL}
330
+ --registry-doctor-url <url> Registry preflight endpoint. Defaults to the claim URL origin.
242
331
  --skill-id <id> Public ${PRODUCT_NAME} Skill registry id.
243
332
  --registry-url <url> Event endpoint written to the manifest.
244
333
  --agent-ack-url <url> No-secret Agent Ack endpoint written to the local manifest.
245
334
  --name <name> Skill display name override.
246
335
  --description <text> Skill description override.
247
- --hub <hub> Target Skill hub prompt style. setup defaults to xiashe,red.
336
+ --hub <hub> Target Skill hub prompt style. setup defaults to ${DEFAULT_SETUP_HUBS.join(',')}.
248
337
  --to <hub[,hub]> High-level publish targets. Alias for --hub in publish mode.
249
338
  --event <event> Runtime event to submit for testing.
250
339
  --installation-id <id> Stable anonymous install id for runtime analytics.
@@ -267,11 +356,13 @@ Options:
267
356
  --source-url <url> User-provided source URL an Agent can use during third-party upload.
268
357
  --package-url <url> Deprecated alias for --source-url.
269
358
  --skill-version <version> Override the Skill release version without editing package.json.
270
- --package-upload-url <url> Package upload ticket endpoint. Defaults to ${DEFAULT_PACKAGE_UPLOAD_URL}
271
- --package-artifact-url <url> Package artifact attach endpoint. Defaults to ${DEFAULT_PACKAGE_ARTIFACT_URL}
359
+ --package-upload-url <url> Package upload ticket endpoint on the same registry origin.
360
+ --package-artifact-url <url> Package artifact attach endpoint on the same registry origin.
272
361
  --out <path> Output prompt or snippet file.
273
362
  --out-dir <path> Output directory for setup artifacts. Defaults to ${WORK_DIR}/.
274
- --no-xiashe-package-upload Skip automatic ${PRODUCT_NAME} complete package upload.
363
+ --no-package-upload Skip automatic ${PRODUCT_NAME} complete package upload.
364
+ --paid-skill Treat third-party packages as protected paid-Skill connectors only.
365
+ --protected-skill Alias for --paid-skill. Never upload full paid prompt/source to third-party hubs.
275
366
  --embed-skill-md Also write a clearly marked registry section into SKILL.md.
276
367
  --no-skill-md Do not write registry text into SKILL.md. Red hub embeds by default.
277
368
  --no-snippet Setup should skip writing the runtime analytics snippet.
@@ -290,7 +381,7 @@ function fail(message, code = 1) {
290
381
 
291
382
  function parseArgs(argv) {
292
383
  const args = { _: [], json: false, dryRun: false };
293
- const booleanFlags = new Set(['embed-skill-md', 'no-skill-md', 'no-snippet', 'no-track', 'no-xiashe-package-upload']);
384
+ const booleanFlags = new Set(['embed-skill-md', 'no-skill-md', 'no-snippet', 'no-track', 'no-package-upload', 'no-xiashe-package-upload', 'paid-skill', 'protected-skill']);
294
385
  for (let i = 0; i < argv.length; i += 1) {
295
386
  const value = argv[i];
296
387
  if (value === '--help' || value === '-h') {
@@ -445,7 +536,7 @@ function latestVersionEndpoint(inspectedOrRegistry = {}) {
445
536
 
446
537
  function creatorFooterTemplate(inspected, language = 'zh') {
447
538
  const registry = inspected.registry || {};
448
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
539
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
449
540
  const creatorName = normalizeText(registry.creatorProfile?.displayName || registry.creatorDisplayName || registry.creatorName, 120) || '待创作者补充';
450
541
  const creatorSignal = normalizeText(registry.creatorProfile?.xiaSignal || registry.creatorXiaSignal || registry.xiaSignal, 80);
451
542
  if (language === 'en') {
@@ -487,6 +578,9 @@ function normalizeHub(value) {
487
578
  if (hub === 'xiashechat' || hub === 'xia-she' || hub === 'xiashe-store') {
488
579
  return 'xiashe';
489
580
  }
581
+ if (hub === 'agent-pie' || hub === 'agentpieapp' || hub === 'agentpie-registry' || hub === 'agentpieregistry') {
582
+ return 'agentpie';
583
+ }
490
584
  if (hub === 'claudecode' || hub === 'claude-code') {
491
585
  return 'claude';
492
586
  }
@@ -540,8 +634,48 @@ function isLikelyEntrypointFile(filePath) {
540
634
  return /(^|\/)(package\.json|requirements\.txt|pyproject\.toml|deno\.json|mcp\.json|manifest\.json|skill\.json|index\.(js|mjs|cjs|ts|tsx|py)|main\.(js|mjs|cjs|ts|tsx|py)|server\.(js|mjs|cjs|ts|tsx|py))$/i.test(filePath);
541
635
  }
542
636
 
637
+ function truthyPaidValue(value) {
638
+ if (value === true) return true;
639
+ if (typeof value === 'number') return value > 0;
640
+ const text = normalizeText(value, 120).toLowerCase();
641
+ return [
642
+ 'paid',
643
+ 'protected',
644
+ 'requires_payment',
645
+ 'requires-entitlement',
646
+ 'entitlement',
647
+ 'alipay_ai_collect',
648
+ 'stripe',
649
+ 'charging',
650
+ '收费',
651
+ '付费'
652
+ ].includes(text);
653
+ }
654
+
655
+ function isProtectedPaidSkill(inspected) {
656
+ const registry = inspected.registry || {};
657
+ const candidates = [
658
+ inspected.isPaid,
659
+ inspected.requiresEntitlement,
660
+ inspected.billingModel,
661
+ inspected.monetizationMode,
662
+ inspected.accessMode,
663
+ registry.isPaid,
664
+ registry.paidAccess,
665
+ registry.requiresEntitlement,
666
+ registry.billingModel,
667
+ registry.monetizationMode,
668
+ registry.accessMode,
669
+ registry.payment?.required,
670
+ registry.payment?.channel
671
+ ];
672
+ return candidates.some(truthyPaidValue);
673
+ }
674
+
543
675
  function packagePlanForHub(inspected, hub) {
544
676
  const profile = reviewProfileForHub(hub);
677
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
678
+ const thirdPartyProtectedPackage = protectedPaidSkill && !['agentpie', 'xiashe'].includes(profile.hub);
545
679
  const localOnly = [];
546
680
  const candidateUploadFiles = [];
547
681
  const forbiddenPatterns = [
@@ -562,6 +696,14 @@ function packagePlanForHub(inspected, hub) {
562
696
  localOnly.push(file.path);
563
697
  continue;
564
698
  }
699
+ if (thirdPartyProtectedPackage) {
700
+ if (isPublicRuntimeProtocolFile(file.path) || /^README(\.[a-z0-9]+)?$/i.test(file.path)) {
701
+ candidateUploadFiles.push(file.path);
702
+ } else {
703
+ localOnly.push(file.path);
704
+ }
705
+ continue;
706
+ }
565
707
  if (profile.packageMode === 'full_skill_package') {
566
708
  candidateUploadFiles.push(file.path);
567
709
  continue;
@@ -591,6 +733,8 @@ function packagePlanForHub(inspected, hub) {
591
733
  return {
592
734
  hub: profile.hub,
593
735
  label: profile.label,
736
+ protectedPaidSkill,
737
+ thirdPartyProtectedPackage,
594
738
  reviewProfile: profile,
595
739
  dataSourcePolicy: {
596
740
  runtime: 'Only real Skill execution callbacks, MCP/HTTP/API Tool calls, webhooks, or external Agent ack.',
@@ -606,17 +750,24 @@ function packagePlanForHub(inspected, hub) {
606
750
  `${WORK_DIR}/runtime-events.js`,
607
751
  `${WORK_DIR}/platform-profiles.json`
608
752
  ])).sort(),
609
- disclosureTargets: profile.disclosurePlacement,
753
+ disclosureTargets: thirdPartyProtectedPackage
754
+ ? ['README', `${PUBLIC_PROTOCOL_DIR}/runtime.yaml`, `${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md`, `${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md`, 'platform private/admin field']
755
+ : profile.disclosurePlacement,
610
756
  forbiddenPatterns,
611
757
  safetyChecklist: profile.safetyChecklist || [],
612
758
  requiresUserConfirmation: true,
613
759
  notes: [
760
+ thirdPartyProtectedPackage
761
+ ? `Protected paid Skill mode is active. Third-party packages must be connector/bootstrap only: do not upload the complete prompt, source package, ${WORK_DIR}/ files, package URLs, storage IDs, or install artifacts. The complete protected Skill package is delivered only through the ${PRODUCT_NAME} Gateway after entitlement is verified.`
762
+ : '',
614
763
  profile.allowedFilePolicy,
615
764
  profile.manifestPolicy === 'local_only'
616
- ? `Keep ${WORK_DIR}/${MANIFEST_FILE} local by default. Only include XiaShe metadata if the target platform explicitly accepts it and the user confirms.`
765
+ ? `Keep ${WORK_DIR}/${MANIFEST_FILE} local by default. Only include ${PRODUCT_NAME} metadata if the target platform explicitly accepts it and the user confirms.`
617
766
  : 'Confirm platform file rules before uploading auxiliary registry metadata.',
618
767
  hub === 'red'
619
- ? `For Red Skill, include only public source files plus no-secret ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md, and ${PUBLIC_PROTOCOL_DIR}/runtime.yaml when Markdown/YAML 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.`
768
+ ? thirdPartyProtectedPackage
769
+ ? `For paid Red Skill distribution, publish only the ${PRODUCT_NAME} connector/bootstrap description and no-secret ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md. The external Agent must obtain a user-specific access session from ${PRODUCT_NAME}; after entitlement is confirmed, it must retrieve, verify, unpack, and read the complete protected Skill package locally. Do not mirror that package into Red.`
770
+ : `For Red Skill, include only public source files plus no-secret ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md, and ${PUBLIC_PROTOCOL_DIR}/runtime.yaml when Markdown/YAML 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 ${PRODUCT_NAME} runtime; otherwise show it as upload/reported only.`
620
771
  : '',
621
772
  profile.runtimeDataPolicy === 'attribution_only_by_default'
622
773
  ? 'Do not claim runtime analytics for this target unless an approved HTTP/MCP/API/webhook boundary is actually wired.'
@@ -678,12 +829,22 @@ async function inferSkillMetadata(root, flags = {}) {
678
829
  skillKey: safeSkillKey(flags['skill-key'] || manifest?.skillKey || packageJson?.name || name),
679
830
  description: normalizeText(flags.description || manifest?.description || packageJson?.description || firstParagraph || '', 500),
680
831
  version: normalizeText(flags['skill-version'] || packageJson?.version || manifest?.version || '0.1.0', 80),
832
+ isPaid: Boolean(flags['paid-skill'] || flags['protected-skill'] || manifest?.isPaid || manifest?.registry?.isPaid || manifest?.registry?.paidAccess),
833
+ requiresEntitlement: Boolean(flags['paid-skill'] || flags['protected-skill'] || manifest?.requiresEntitlement || manifest?.registry?.requiresEntitlement),
834
+ billingModel: normalizeText(flags['billing-model'] || manifest?.billingModel || manifest?.registry?.billingModel, 80) || null,
835
+ monetizationMode: normalizeText(flags['monetization-mode'] || manifest?.monetizationMode || manifest?.registry?.monetizationMode, 80) || null,
836
+ accessMode: normalizeText(flags['access-mode'] || manifest?.accessMode || manifest?.registry?.accessMode, 80) || null,
681
837
  registry: manifest?.registry || null
682
838
  };
683
839
  }
684
840
 
685
841
  function shouldIgnore(name) {
686
- return DEFAULT_IGNORE.has(name) || name.endsWith('~') || name.endsWith('.tmp') || name.endsWith('.log');
842
+ return DEFAULT_IGNORE.has(name) ||
843
+ name.startsWith('.env.') ||
844
+ /\.(pem|key)$/i.test(name) ||
845
+ name.endsWith('~') ||
846
+ name.endsWith('.tmp') ||
847
+ name.endsWith('.log');
687
848
  }
688
849
 
689
850
  async function walkFiles(root, current = root, out = []) {
@@ -729,6 +890,7 @@ async function postJson(url, body, timeoutMs = 20_000) {
729
890
  method: 'POST',
730
891
  headers: { 'content-type': 'application/json' },
731
892
  body: JSON.stringify(body),
893
+ redirect: 'error',
732
894
  signal: controller.signal
733
895
  });
734
896
  const text = await response.text();
@@ -742,18 +904,177 @@ async function postJson(url, body, timeoutMs = 20_000) {
742
904
  throw new Error(payload.error || payload.message || `HTTP ${response.status}`);
743
905
  }
744
906
  return payload;
907
+ } catch (error) {
908
+ const message = error instanceof Error ? error.message : String(error || 'unknown error');
909
+ if (!isTransportError(error)) {
910
+ throw error;
911
+ }
912
+ throw new Error(`Network request failed for ${url}: ${message}`);
913
+ } finally {
914
+ clearTimeout(timer);
915
+ }
916
+ }
917
+
918
+ function registryClaimEndpoint(claimUrl) {
919
+ const raw = normalizeText(claimUrl, 1_000) || DEFAULT_CLAIM_URL;
920
+ let url;
921
+ try {
922
+ url = new URL(raw);
923
+ } catch {
924
+ throw new Error('CREATOR_SKILL_REGISTRY_URL_INVALID: claim URL is not a valid URL.');
925
+ }
926
+ const pathname = url.pathname.replace(/\/+$/, '');
927
+ if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || pathname !== '/registry/skill/claim') {
928
+ throw new Error('CREATOR_SKILL_REGISTRY_URL_INVALID: claim URL must be a clean https://<trusted-host>/registry/skill/claim endpoint.');
929
+ }
930
+ const host = url.hostname.toLowerCase();
931
+ const isLocalhost = host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.endsWith('.localhost');
932
+ if (url.protocol !== 'https:' && !(isLocalhost && process.env.XIASHE_SKILL_ALLOW_INSECURE_LOCALHOST === '1')) {
933
+ throw new Error('CREATOR_SKILL_REGISTRY_URL_INSECURE: only HTTPS registry endpoints are accepted.');
934
+ }
935
+ return url;
936
+ }
937
+
938
+ function explicitClaimUrl(flags = {}) {
939
+ return normalizeText(
940
+ flags['claim-url'] ||
941
+ process.env.XIASHE_SKILL_CLAIM_URL ||
942
+ LOCAL_ENV_DEFAULTS.XIASHE_SKILL_CLAIM_URL,
943
+ 1_000
944
+ );
945
+ }
946
+
947
+ function claimUrlFromRegistryOrigin(registryOrigin) {
948
+ const origin = normalizeBaseUrl(registryOrigin);
949
+ return origin ? `${origin}/registry/skill/claim` : '';
950
+ }
951
+
952
+ function claimUrlForCode(flags = {}) {
953
+ const claimUrl = explicitClaimUrl(flags);
954
+ if (!claimUrl) {
955
+ throw new Error(
956
+ 'CREATOR_SKILL_CLAIM_URL_REQUIRED: a dynamic code must be used with the exact --claim-url issued by the dashboard. ' +
957
+ 'This prevents dev, CN, and global registry codes from being sent to the wrong environment.'
958
+ );
959
+ }
960
+ return claimUrl;
961
+ }
962
+
963
+ function claimUrlForExistingManifest(flags = {}, registry = {}) {
964
+ return (
965
+ explicitClaimUrl(flags) ||
966
+ claimUrlFromRegistryOrigin(registry.registryOrigin) ||
967
+ DEFAULT_CLAIM_URL
968
+ );
969
+ }
970
+
971
+ function configuredRegistryOrigins() {
972
+ const supplied = [
973
+ process.env.XIASHE_SKILL_TRUSTED_REGISTRY_ORIGINS,
974
+ LOCAL_ENV_DEFAULTS.XIASHE_SKILL_TRUSTED_REGISTRY_ORIGINS,
975
+ DEFAULT_BASE_URL
976
+ ]
977
+ .filter(Boolean)
978
+ .flatMap((value) => String(value).split(','))
979
+ .map((value) => normalizeBaseUrl(value))
980
+ .filter(Boolean)
981
+ .map((value) => new URL(value).origin.toLowerCase());
982
+ return new Set(supplied);
983
+ }
984
+
985
+ function isTrustedRegistryClaimUrl(claimUrl) {
986
+ const endpoint = registryClaimEndpoint(claimUrl);
987
+ const host = endpoint.hostname.toLowerCase();
988
+ const isOfficialHost =
989
+ host === 'xiashe.chat' || host.endsWith('.xiashe.chat') ||
990
+ host === 'agentpie.app' || host.endsWith('.agentpie.app');
991
+ const isLocalhost = host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.endsWith('.localhost');
992
+ const trustedOrigins = configuredRegistryOrigins();
993
+ if (!isOfficialHost && !isLocalhost && !trustedOrigins.has(endpoint.origin.toLowerCase())) {
994
+ throw new Error('CREATOR_SKILL_REGISTRY_ORIGIN_UNTRUSTED: this dynamic code points to an untrusted registry origin. Use the exact dashboard command, or configure XIASHE_SKILL_TRUSTED_REGISTRY_ORIGINS for an approved self-hosted endpoint.');
995
+ }
996
+ return endpoint;
997
+ }
998
+
999
+ async function getJson(url, timeoutMs = 12_000) {
1000
+ const controller = new AbortController();
1001
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1002
+ try {
1003
+ const response = await fetch(url, { method: 'GET', redirect: 'error', signal: controller.signal });
1004
+ const text = await response.text();
1005
+ let payload = {};
1006
+ try {
1007
+ payload = text ? JSON.parse(text) : {};
1008
+ } catch {
1009
+ throw new Error(`Bad JSON response from ${url}`);
1010
+ }
1011
+ if (!response.ok || payload.ok === false) {
1012
+ throw new Error(payload.error || payload.message || `HTTP ${response.status}`);
1013
+ }
1014
+ return payload;
1015
+ } catch (error) {
1016
+ const message = error instanceof Error ? error.message : String(error || 'unknown error');
1017
+ if (!isTransportError(error)) throw error;
1018
+ throw new Error(`Network request failed for ${url}: ${message}`);
745
1019
  } finally {
746
1020
  clearTimeout(timer);
747
1021
  }
748
1022
  }
749
1023
 
750
- function urlForRegistryPath(baseUrl, pathname, fallback) {
751
- const base = normalizeText(baseUrl, 1_000) || fallback;
1024
+ async function preflightRegistryClaim(claimUrl, flags = {}) {
1025
+ const endpoint = isTrustedRegistryClaimUrl(claimUrl);
1026
+ const expectedOrigin = endpoint.origin.replace(/\/+$/, '');
1027
+ const doctorUrl = registryEndpointUrl(
1028
+ endpoint,
1029
+ '/registry/skill/doctor',
1030
+ flags['registry-doctor-url'],
1031
+ 'registry doctor'
1032
+ );
1033
+ const doctor = await getJson(doctorUrl, Number(flags['timeout-ms'] || 20_000));
1034
+ const registryOrigin = normalizeBaseUrl(doctor.registryOrigin);
1035
+ const protocolVersion = normalizeText(doctor.protocolVersion, 120);
1036
+ const environmentId = normalizeText(doctor.environmentId, 160);
1037
+ if (!registryOrigin || registryOrigin !== expectedOrigin) {
1038
+ throw new Error(`CREATOR_SKILL_REGISTRY_ENVIRONMENT_MISMATCH: claim endpoint ${expectedOrigin} does not match the registry doctor origin ${registryOrigin || 'missing'}. Do not consume this code; regenerate it from the same environment.`);
1039
+ }
1040
+ if (!protocolVersion || !environmentId) {
1041
+ throw new Error('CREATOR_SKILL_REGISTRY_DOCTOR_INCOMPLETE: update the registry backend before using a dynamic code.');
1042
+ }
1043
+ return {
1044
+ ok: true,
1045
+ claimUrl: endpoint.toString(),
1046
+ doctorUrl,
1047
+ registryOrigin,
1048
+ protocolVersion,
1049
+ environmentId,
1050
+ provider: normalizeText(doctor.provider, 80) || REGISTRY_PROVIDER
1051
+ };
1052
+ }
1053
+
1054
+ function registryEndpointUrl(claimEndpoint, pathname, suppliedUrl, label) {
1055
+ const endpoint = isTrustedRegistryClaimUrl(claimEndpoint);
1056
+ const expected = new URL(pathname, endpoint.origin);
1057
+ const raw = normalizeText(suppliedUrl, 1_000);
1058
+ if (!raw) return expected.toString();
1059
+ let candidate;
752
1060
  try {
753
- return new URL(pathname, base).toString();
1061
+ candidate = new URL(raw);
754
1062
  } catch {
755
- return fallback;
1063
+ throw new Error(`CREATOR_SKILL_REGISTRY_URL_INVALID: ${label} URL is not valid.`);
1064
+ }
1065
+ if (
1066
+ candidate.origin !== endpoint.origin ||
1067
+ candidate.pathname.replace(/\/+$/, '') !== expected.pathname.replace(/\/+$/, '') ||
1068
+ candidate.username ||
1069
+ candidate.password ||
1070
+ candidate.search ||
1071
+ candidate.hash
1072
+ ) {
1073
+ throw new Error(
1074
+ `CREATOR_SKILL_REGISTRY_ENDPOINT_MISMATCH: ${label} must stay on the exact registry origin and path bound to the dynamic code.`
1075
+ );
756
1076
  }
1077
+ return candidate.toString();
757
1078
  }
758
1079
 
759
1080
  function shouldIgnorePackageEntry(relativePath, entryName, isDirectory = false) {
@@ -838,11 +1159,14 @@ function tarHeader(file, size) {
838
1159
  const header = Buffer.alloc(512, 0);
839
1160
  const split = splitTarPath(file.relative);
840
1161
  header.write(split.name, 0, 100, 'utf8');
841
- header.write(octal(file.mode & 0o777 || 0o644, 8), 100, 8, 'ascii');
1162
+ // Keep archive bytes reproducible. Local mtimes and group permissions are
1163
+ // machine-specific and must not make a valid retry look like a new package.
1164
+ const normalizedMode = file.mode & 0o111 ? 0o755 : 0o644;
1165
+ header.write(octal(normalizedMode, 8), 100, 8, 'ascii');
842
1166
  header.write(octal(0, 8), 108, 8, 'ascii');
843
1167
  header.write(octal(0, 8), 116, 8, 'ascii');
844
1168
  header.write(octal(size, 12), 124, 12, 'ascii');
845
- header.write(octal(Math.floor(new Date(file.mtime).getTime() / 1000), 12), 136, 12, 'ascii');
1169
+ header.write(octal(0, 12), 136, 12, 'ascii');
846
1170
  header.fill(0x20, 148, 156);
847
1171
  header.write('0', 156, 1, 'ascii');
848
1172
  header.write('ustar', 257, 6, 'ascii');
@@ -877,7 +1201,10 @@ async function createTarGz(files) {
877
1201
  }
878
1202
  chunks.push(Buffer.alloc(1024, 0));
879
1203
  const tar = Buffer.concat(chunks);
880
- const gzipped = await gzipAsync(tar, { level: 9 });
1204
+ // Node's current gzip implementation defaults to deterministic metadata,
1205
+ // but make the invariant explicit so retries on another host have the same
1206
+ // archive checksum when the files are unchanged.
1207
+ const gzipped = await gzipAsync(tar, { level: 9, mtime: 0 });
881
1208
  if (gzipped.length > MAX_XIASHE_PACKAGE_BYTES) {
882
1209
  throw new Error(`Package artifact exceeds ${Math.round(MAX_XIASHE_PACKAGE_BYTES / (1024 * 1024))}MB.`);
883
1210
  }
@@ -896,10 +1223,10 @@ async function buildXiashePackageArtifact(root, inspected, flags = {}) {
896
1223
  const skillMarkdownPresent = files.some((file) => /^SKILL(\.[a-z0-9]+)?$/i.test(file.relative));
897
1224
  const manifestPresent = files.some((file) => file.relative === manifestRelative);
898
1225
  if (!skillMarkdownPresent) {
899
- throw new Error('SKILL.md is required before uploading the XiaShe complete package.');
1226
+ throw new Error(`SKILL.md is required before uploading the ${PRODUCT_NAME} complete package.`);
900
1227
  }
901
1228
  if (!manifestPresent) {
902
- throw new Error(`${manifestRelative} is required before uploading the XiaShe complete package.`);
1229
+ throw new Error(`${manifestRelative} is required before uploading the ${PRODUCT_NAME} complete package.`);
903
1230
  }
904
1231
  const artifact = await createTarGz(files);
905
1232
  const artifactDir = path.resolve(flags['package-out-dir'] || path.join(absoluteRoot, WORK_DIR, 'package-artifact'));
@@ -930,6 +1257,7 @@ async function uploadBytesToStorage(uploadUrl, bytes, contentType, timeoutMs = 6
930
1257
  method: 'POST',
931
1258
  headers: { 'content-type': contentType || 'application/octet-stream' },
932
1259
  body: bytes,
1260
+ redirect: 'error',
933
1261
  signal: controller.signal
934
1262
  });
935
1263
  const text = await response.text();
@@ -947,13 +1275,42 @@ async function uploadBytesToStorage(uploadUrl, bytes, contentType, timeoutMs = 6
947
1275
  throw new Error('Upload response did not include storageId.');
948
1276
  }
949
1277
  return { ...payload, storageId };
1278
+ } catch (error) {
1279
+ const message = error instanceof Error ? error.message : String(error || 'unknown error');
1280
+ if (!isTransportError(error)) {
1281
+ throw error;
1282
+ }
1283
+ throw new Error(`Package upload failed for ${uploadUrl}: ${message}`);
950
1284
  } finally {
951
1285
  clearTimeout(timer);
952
1286
  }
953
1287
  }
954
1288
 
1289
+ async function getPackagePublishStatus(claimUrl, publicToken, uploadJobId, idempotencyKey, flags = {}) {
1290
+ return await postJson(
1291
+ registryEndpointUrl(claimUrl, '/registry/skill/package-status', flags['package-status-url'], 'package status'),
1292
+ {
1293
+ publicToken,
1294
+ uploadJobId: normalizeText(uploadJobId, 160) || undefined,
1295
+ idempotencyKey: normalizeText(idempotencyKey, 220) || undefined
1296
+ },
1297
+ Number(flags['timeout-ms'] || 20_000)
1298
+ );
1299
+ }
1300
+
1301
+ async function waitForPackagePublish(claimUrl, publicToken, uploadJobId, idempotencyKey, flags = {}) {
1302
+ const timeoutMs = Math.max(5_000, Math.min(90_000, Number(flags['publish-status-timeout-ms'] || 45_000)));
1303
+ const deadline = Date.now() + timeoutMs;
1304
+ let status = await getPackagePublishStatus(claimUrl, publicToken, uploadJobId, idempotencyKey, flags);
1305
+ while (status.found && (status.state === 'queued' || status.state === 'processing') && Date.now() < deadline) {
1306
+ await new Promise((resolve) => setTimeout(resolve, 1_000));
1307
+ status = await getPackagePublishStatus(claimUrl, publicToken, uploadJobId, idempotencyKey, flags);
1308
+ }
1309
+ return status;
1310
+ }
1311
+
955
1312
  async function uploadXiashePackageArtifact(root, flags, manifestResult) {
956
- if (flags.dryRun || flags['no-xiashe-package-upload']) {
1313
+ if (flags.dryRun || flags['no-package-upload'] || flags['no-xiashe-package-upload']) {
957
1314
  return {
958
1315
  ok: true,
959
1316
  skipped: true,
@@ -964,39 +1321,93 @@ async function uploadXiashePackageArtifact(root, flags, manifestResult) {
964
1321
  const registry = manifestResult.manifest?.registry || inspected.registry || {};
965
1322
  const publicToken = normalizeText(registry.publicToken, 512);
966
1323
  if (!publicToken) {
967
- throw new Error('Cannot upload XiaShe package without registry publicToken.');
1324
+ throw new Error(`Cannot upload ${PRODUCT_NAME} package without registry publicToken.`);
968
1325
  }
969
1326
  const artifact = await buildXiashePackageArtifact(root, inspected, flags);
970
1327
  const registrySourceSha256 =
971
1328
  normalizeText(manifestResult.manifest?.sourceFingerprint?.sha256, 128) ||
972
1329
  normalizeText(inspected.package?.sha256, 128) ||
973
1330
  artifact.sourceSha256;
974
- const claimUrl = normalizeText(flags['claim-url'], 800) || DEFAULT_CLAIM_URL;
1331
+ // A resumed upload has no dynamic code to protect it. Bind it back to the
1332
+ // origin recorded in the private manifest instead of silently falling back
1333
+ // to the production registry, then verify that origin before any write.
1334
+ const claimUrl = claimUrlForExistingManifest(flags, registry);
1335
+ const registryPreflight = await preflightRegistryClaim(claimUrl, flags);
1336
+ const expectedRegistryOrigin = normalizeBaseUrl(registry.registryOrigin);
1337
+ const expectedEnvironmentId = normalizeText(registry.environmentId, 160);
1338
+ if (
1339
+ (expectedRegistryOrigin && expectedRegistryOrigin !== registryPreflight.registryOrigin) ||
1340
+ (expectedEnvironmentId && expectedEnvironmentId !== registryPreflight.environmentId)
1341
+ ) {
1342
+ throw new Error(
1343
+ 'CREATOR_SKILL_REGISTRY_ENVIRONMENT_MISMATCH: the local manifest belongs to a different registry environment. ' +
1344
+ 'Do not upload or claim a new code; switch to the original environment or start a new dashboard task.'
1345
+ );
1346
+ }
975
1347
  const uploadTicket =
976
1348
  manifestResult.claimPackageArtifactUpload ||
977
1349
  await postJson(
978
- normalizeText(flags['package-upload-url'], 1_000) || urlForRegistryPath(claimUrl, '/registry/skill/package-upload-url', DEFAULT_PACKAGE_UPLOAD_URL),
1350
+ registryEndpointUrl(claimUrl, '/registry/skill/package-upload-url', flags['package-upload-url'], 'package upload'),
979
1351
  {
980
1352
  publicToken,
981
1353
  idempotencyKey: registrySourceSha256
982
1354
  },
983
1355
  Number(flags['timeout-ms'] || 20_000)
984
1356
  );
985
- const uploadUrl = normalizeText(uploadTicket?.uploadUrl, 2_000);
1357
+ let activeUploadTicket = uploadTicket;
1358
+ let uploadUrl = normalizeText(activeUploadTicket?.uploadUrl, 2_000);
986
1359
  if (!uploadUrl) {
987
- throw new Error('XiaShe package upload URL was not returned by registry.');
1360
+ const priorStatus = await waitForPackagePublish(
1361
+ claimUrl,
1362
+ publicToken,
1363
+ normalizeText(activeUploadTicket?.uploadJobId, 160),
1364
+ registrySourceSha256,
1365
+ flags
1366
+ );
1367
+ if (priorStatus.found && priorStatus.state === 'succeeded') {
1368
+ return {
1369
+ ok: true,
1370
+ skipped: false,
1371
+ resumed: true,
1372
+ artifactPath: artifact.artifactPath,
1373
+ fileName: artifact.fileName,
1374
+ sizeBytes: artifact.sizeBytes,
1375
+ sourceSha256: registrySourceSha256,
1376
+ archiveSourceSha256: artifact.sourceSha256,
1377
+ artifactSha256: artifact.artifactSha256,
1378
+ fileCount: artifact.fileCount,
1379
+ storageId: priorStatus.storageId ?? null,
1380
+ publishStatus: priorStatus.status,
1381
+ uploadJobId: priorStatus.uploadJobId,
1382
+ attachResult: priorStatus
1383
+ };
1384
+ }
1385
+ if (priorStatus.found && (priorStatus.state === 'queued' || priorStatus.state === 'processing')) {
1386
+ throw new Error(`${PRODUCT_NAME} package publish is still processing (job ${priorStatus.uploadJobId}). Run the same publish command again to resume; it will not create a second job.`);
1387
+ }
1388
+ activeUploadTicket = await postJson(
1389
+ registryEndpointUrl(claimUrl, '/registry/skill/package-upload-url', flags['package-upload-url'], 'package upload'),
1390
+ { publicToken, idempotencyKey: registrySourceSha256 },
1391
+ Number(flags['timeout-ms'] || 20_000)
1392
+ );
1393
+ uploadUrl = normalizeText(activeUploadTicket?.uploadUrl, 2_000);
1394
+ if (!uploadUrl) {
1395
+ throw new Error(`${PRODUCT_NAME} package upload cannot resume yet. Query /registry/skill/package-status with the existing publicToken and uploadJobId; do not claim a new code.`);
1396
+ }
988
1397
  }
989
1398
  const bytes = await readFile(artifact.artifactPath);
990
1399
  const uploaded = await uploadBytesToStorage(uploadUrl, bytes, artifact.contentType, Number(flags['upload-timeout-ms'] || 90_000));
991
1400
  const attachResult = await postJson(
992
- normalizeText(flags['package-artifact-url'], 1_000) || urlForRegistryPath(claimUrl, '/registry/skill/package-artifact', DEFAULT_PACKAGE_ARTIFACT_URL),
1401
+ registryEndpointUrl(claimUrl, '/registry/skill/package-artifact', flags['package-artifact-url'], 'package artifact'),
993
1402
  {
994
1403
  publicToken,
995
1404
  storageId: uploaded.storageId,
996
- uploadJobId: normalizeText(uploadTicket?.uploadJobId, 160) || undefined,
1405
+ uploadJobId: normalizeText(activeUploadTicket?.uploadJobId, 160) || undefined,
997
1406
  idempotencyKey: registrySourceSha256,
998
1407
  fileName: artifact.fileName,
999
1408
  contentType: artifact.contentType,
1409
+ sourceSha256: registrySourceSha256,
1410
+ artifactSha256: artifact.artifactSha256,
1000
1411
  packageSha256: registrySourceSha256,
1001
1412
  packageFileCount: artifact.fileCount,
1002
1413
  packageTotalBytes: artifact.sourceTotalBytes,
@@ -1004,11 +1415,21 @@ async function uploadXiashePackageArtifact(root, flags, manifestResult) {
1004
1415
  manifestPresent: artifact.manifestPresent,
1005
1416
  skillMarkdownPresent: artifact.skillMarkdownPresent,
1006
1417
  creatorCardUrl: normalizeText(registry.creatorCardUrl, 800) || undefined,
1007
- platformSource: 'xiashe',
1418
+ platformSource: REGISTRY_PROVIDER,
1008
1419
  source: `${REGISTRY_PROVIDER}_skill_cli`
1009
1420
  },
1010
1421
  Number(flags['timeout-ms'] || 20_000)
1011
1422
  );
1423
+ const finalStatus = await waitForPackagePublish(
1424
+ claimUrl,
1425
+ publicToken,
1426
+ normalizeText(attachResult.uploadJobId || activeUploadTicket?.uploadJobId, 160),
1427
+ registrySourceSha256,
1428
+ flags
1429
+ );
1430
+ if (finalStatus.found && finalStatus.state === 'failed') {
1431
+ throw new Error(`${PRODUCT_NAME} package publish failed: ${normalizeText(finalStatus.lastError, 500) || 'unknown validation error'}. Rerun the same command after correcting the reported package issue; do not claim a new code.`);
1432
+ }
1012
1433
  return {
1013
1434
  ok: true,
1014
1435
  skipped: false,
@@ -1016,13 +1437,14 @@ async function uploadXiashePackageArtifact(root, flags, manifestResult) {
1016
1437
  fileName: artifact.fileName,
1017
1438
  sizeBytes: artifact.sizeBytes,
1018
1439
  sourceSha256: registrySourceSha256,
1019
- packageSourceSha256: artifact.sourceSha256,
1440
+ archiveSourceSha256: artifact.sourceSha256,
1020
1441
  artifactSha256: artifact.artifactSha256,
1021
1442
  fileCount: artifact.fileCount,
1022
1443
  storageId: uploaded.storageId,
1023
- publishStatus: attachResult.publishStatus ?? null,
1024
- uploadJobId: attachResult.uploadJobId ?? normalizeText(uploadTicket?.uploadJobId, 160) ?? null,
1025
- attachResult
1444
+ publishStatus: finalStatus.status ?? attachResult.publishStatus ?? null,
1445
+ uploadJobId: attachResult.uploadJobId ?? normalizeText(activeUploadTicket?.uploadJobId, 160) ?? null,
1446
+ attachResult,
1447
+ finalStatus
1026
1448
  };
1027
1449
  }
1028
1450
 
@@ -1051,9 +1473,19 @@ async function inspectSkill(root, flags = {}) {
1051
1473
  async function writeManifest(root, flags) {
1052
1474
  const inspected = await inspectSkill(root, flags);
1053
1475
  const code = normalizeText(flags.code, 80);
1476
+ const existingRegistry = inspected.registry || {};
1477
+ const suppliedPublicToken = normalizeText(flags['public-token'], 512);
1478
+ if (!code && suppliedPublicToken && !explicitClaimUrl(flags) && !claimUrlFromRegistryOrigin(existingRegistry.registryOrigin)) {
1479
+ throw new Error(
1480
+ 'CREATOR_SKILL_CLAIM_URL_REQUIRED: a new manifest created from --public-token must include --claim-url. ' +
1481
+ 'Existing manifests resume through their saved registry origin.'
1482
+ );
1483
+ }
1484
+ const claimUrl = code ? claimUrlForCode(flags) : claimUrlForExistingManifest(flags, existingRegistry);
1485
+ const registryPreflight = code ? await preflightRegistryClaim(claimUrl, flags) : null;
1054
1486
  const claim = code
1055
1487
  ? await postJson(
1056
- normalizeText(flags['claim-url'], 800) || DEFAULT_CLAIM_URL,
1488
+ registryPreflight.claimUrl,
1057
1489
  {
1058
1490
  code,
1059
1491
  skill: {
@@ -1061,6 +1493,9 @@ async function writeManifest(root, flags) {
1061
1493
  skillKey: inspected.skillKey,
1062
1494
  description: inspected.description,
1063
1495
  version: inspected.version,
1496
+ sourceSha256: inspected.package.sha256,
1497
+ // Compatibility with registries published before v2. The value is
1498
+ // still a source fingerprint, never a compressed archive hash.
1064
1499
  packageSha256: inspected.package.sha256,
1065
1500
  packageFileCount: inspected.package.fileCount,
1066
1501
  packageTotalBytes: inspected.package.totalBytes
@@ -1073,7 +1508,13 @@ async function writeManifest(root, flags) {
1073
1508
  Number(flags['timeout-ms'] || 20_000)
1074
1509
  )
1075
1510
  : null;
1076
- const existingRegistry = inspected.registry || {};
1511
+ if (claim && registryPreflight) {
1512
+ const claimEnvironment = normalizeText(claim.registry?.environmentId, 160);
1513
+ const claimOrigin = normalizeBaseUrl(claim.registry?.registryOrigin);
1514
+ if (claimEnvironment !== registryPreflight.environmentId || claimOrigin !== registryPreflight.registryOrigin) {
1515
+ throw new Error('CREATOR_SKILL_REGISTRY_ENVIRONMENT_MISMATCH: the claimed Skill identity was returned by a different registry environment. No package upload was attempted.');
1516
+ }
1517
+ }
1077
1518
  const publicToken = normalizeText(flags['public-token'] || claim?.publicToken || existingRegistry.publicToken, 512);
1078
1519
  if (!publicToken) {
1079
1520
  throw new Error('Missing --code or --public-token.');
@@ -1100,10 +1541,23 @@ async function writeManifest(root, flags) {
1100
1541
  skillKey: safeSkillKey(claim?.skillKey || inspected.skillKey),
1101
1542
  description: normalizeText(claim?.description, 500) || inspected.description,
1102
1543
  version: normalizeText(claim?.version, 80) || inspected.version,
1544
+ isPaid: Boolean(flags['paid-skill'] || flags['protected-skill'] || inspected.isPaid || existingRegistry.isPaid || existingRegistry.paidAccess),
1545
+ requiresEntitlement: Boolean(flags['paid-skill'] || flags['protected-skill'] || inspected.requiresEntitlement || existingRegistry.requiresEntitlement),
1546
+ billingModel: normalizeText(flags['billing-model'] || inspected.billingModel || existingRegistry.billingModel, 80) || null,
1547
+ monetizationMode: normalizeText(flags['monetization-mode'] || inspected.monetizationMode || existingRegistry.monetizationMode, 80) || null,
1548
+ accessMode: normalizeText(flags['access-mode'] || inspected.accessMode || existingRegistry.accessMode, 80) || null,
1103
1549
  registry: {
1104
1550
  provider: REGISTRY_PROVIDER,
1551
+ protocolVersion: normalizeText(claim?.registry?.protocolVersion || registryPreflight?.protocolVersion, 120) || null,
1552
+ environmentId: normalizeText(claim?.registry?.environmentId || registryPreflight?.environmentId, 160) || null,
1553
+ registryOrigin: normalizeBaseUrl(claim?.registry?.registryOrigin || registryPreflight?.registryOrigin) || null,
1105
1554
  skillId: normalizeText(flags['skill-id'] || claim?.skillId || existingRegistry.skillId, 160) || null,
1106
1555
  publicToken,
1556
+ isPaid: Boolean(flags['paid-skill'] || flags['protected-skill'] || inspected.isPaid || existingRegistry.isPaid || existingRegistry.paidAccess),
1557
+ requiresEntitlement: Boolean(flags['paid-skill'] || flags['protected-skill'] || inspected.requiresEntitlement || existingRegistry.requiresEntitlement),
1558
+ billingModel: normalizeText(flags['billing-model'] || inspected.billingModel || existingRegistry.billingModel, 80) || null,
1559
+ monetizationMode: normalizeText(flags['monetization-mode'] || inspected.monetizationMode || existingRegistry.monetizationMode, 80) || null,
1560
+ accessMode: normalizeText(flags['access-mode'] || inspected.accessMode || existingRegistry.accessMode, 80) || null,
1107
1561
  registryUrl: normalizeText(flags['registry-url'] || claim?.registryUrl || existingRegistry.registryUrl, 800) || DEFAULT_REGISTRY_URL,
1108
1562
  agentAckUrl: normalizeText(flags['agent-ack-url'] || existingRegistry.agentAckUrl, 800) || DEFAULT_AGENT_ACK_URL,
1109
1563
  creatorCardUrl,
@@ -1178,22 +1632,26 @@ async function writeManifest(root, flags) {
1178
1632
  ok: true,
1179
1633
  manifestPath,
1180
1634
  manifest,
1181
- claimPackageArtifactUpload: claim?.packageArtifactUpload ?? null
1635
+ claimPackageArtifactUpload: claim?.packageArtifactUpload ?? null,
1636
+ registryPreflight
1182
1637
  };
1183
1638
  }
1184
1639
 
1185
1640
  function hubInstruction(hub) {
1641
+ if (hub === 'agentpie') {
1642
+ return 'AgentPie Registry publish is the full Skill package and registry manifest flow. Keep public disclosure, attribution, and runtime analytics callbacks; if the local manifest has a registry public token or a creator-authenticated CLI/API session exists, create or reuse the AgentPie registry draft.';
1643
+ }
1186
1644
  if (hub === 'xiashe') {
1187
1645
  return '虾舍商店发布是完整 Skill 包和 registry manifest 的主流程。请保留公开披露、归因和 runtime analytics 回传能力;如果本地 manifest 有 registry public token 或已有创作者认证 CLI/API 会话,就创建或复用虾舍商店草稿。';
1188
1646
  }
1189
1647
  if (hub === 'red') {
1190
- return 'Red Skill 的官方 uploader.md / skillhub-upload / 页面流程是主流程。请优先使用本机 skillhub-upload;虾舍负责在 SKILL.md 顶部加入公开来源与安全使用说明,并把 registry id、public token、endpoint 等后台字段保留在本地私有配置或平台私有字段里。';
1648
+ return `Red Skill 的官方 uploader.md / skillhub-upload / 页面流程是主流程。请优先使用本机 skillhub-upload;${PRODUCT_NAME} 负责生成平台安全披露、connector/运行时协议和本地 registry handoff,并把 registry id、public token、endpoint 等后台字段保留在本地私有配置或平台私有字段里。`;
1191
1649
  }
1192
1650
  if (hub === 'clawhub') {
1193
- return 'ClawHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 ClawHub 当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
1651
+ return `ClawHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 ClawHub 当前要求,再把下面的 ${PRODUCT_NAME} registry 要求作为附加说明合并进去。`;
1194
1652
  }
1195
1653
  if (hub === 'skillhub') {
1196
- return 'SkillHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 SkillHub 当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
1654
+ return `SkillHub 的官方 CLI、prompt 或页面流程是主流程。请先确认 SkillHub 当前要求,再把下面的 ${PRODUCT_NAME} registry 要求作为附加说明合并进去。`;
1197
1655
  }
1198
1656
  if (hub === 'claude') {
1199
1657
  return 'Claude Skills 的官方上传/导入流程是主流程。请先确认 Claude 当前 Skill 格式和共享要求,再把下面的 registry 披露文本放入允许的说明文件。';
@@ -1213,45 +1671,56 @@ function hubInstruction(hub) {
1213
1671
  if (hub === 'coze') {
1214
1672
  return 'Coze Agent/Plugin/Workflow 的官方配置和发布流程是主流程。请先按 Coze 当前工具、插件或知识库要求配置,再把下面的 registry 回调作为可选 HTTP/API 步骤接入。';
1215
1673
  }
1216
- return '目标 Skill Hub 的官方 CLI、prompt 或页面流程是主流程。请先确认平台当前要求,再把下面的虾舍 registry 要求作为附加说明合并进去。';
1674
+ return `目标 Skill Hub 的官方 CLI、prompt 或页面流程是主流程。请先确认平台当前要求,再把下面的 ${PRODUCT_NAME} registry 要求作为附加说明合并进去。`;
1217
1675
  }
1218
1676
 
1219
1677
  function platformPromptPlaceholder(hub) {
1678
+ if (hub === 'agentpie') {
1679
+ return 'If the user is publishing to AgentPie Registry and the current environment has a creator-authenticated CLI/API session, create the AgentPie registry draft first; otherwise finish registry setup, disclosure, platform profile, and handoff files. Do not apply third-party marketplace limits to the AgentPie complete Skill package.';
1680
+ }
1220
1681
  if (hub === 'xiashe') {
1221
1682
  return '如果用户要发布到虾舍商店,并且当前环境已有创作者认证 CLI/API 会话,请优先创建虾舍商店草稿;如果没有认证会话,请完成 registry setup、披露、平台 profile 和 handoff 文件,不要要求 Agent 宿主登录或索取账号凭证。不要把第三方平台限制套用到虾舍完整 Skill 包。';
1222
1683
  }
1223
1684
  if (hub === 'red') {
1224
- return '如果本机有 skillhub-upload,请按小红书官方 CLI 流程 login --agent、打包并提交;否则请让用户从小红书创作服务平台复制 uploader.md 里的官方对话上传口令。不要用虾舍 prompt 替代 Red Skill 官方流程。';
1685
+ return `如果本机有 skillhub-upload,请按小红书官方 CLI 流程 login --agent、打包并提交;否则请让用户从小红书创作服务平台复制 uploader.md 里的官方对话上传口令。不要用 ${PRODUCT_NAME} prompt 替代 Red Skill 官方流程。`;
1225
1686
  }
1226
1687
  if (hub === 'clawhub') {
1227
- return '如果 ClawHub 提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用虾舍 prompt 替代 ClawHub 官方流程。';
1688
+ return `如果 ClawHub 提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用 ${PRODUCT_NAME} prompt 替代 ClawHub 官方流程。`;
1228
1689
  }
1229
1690
  if (hub === 'skillhub') {
1230
- return '如果 SkillHub 提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用虾舍 prompt 替代 SkillHub 官方流程。';
1691
+ return `如果 SkillHub 提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用 ${PRODUCT_NAME} prompt 替代 SkillHub 官方流程。`;
1231
1692
  }
1232
1693
  if (hub === 'claude') {
1233
- return '如果 Claude 提供官方 Skill 导入/上传说明,请优先使用官方说明;不要用虾舍 prompt 替代 Claude 官方流程。';
1694
+ return `如果 Claude 提供官方 Skill 导入/上传说明,请优先使用官方说明;不要用 ${PRODUCT_NAME} prompt 替代 Claude 官方流程。`;
1234
1695
  }
1235
1696
  if (hub === 'codex') {
1236
- return '如果 Codex 提供官方 Skill 或 Agent 能力安装说明,请优先使用官方说明;不要用虾舍 prompt 替代 Codex 官方流程。';
1697
+ return `如果 Codex 提供官方 Skill 或 Agent 能力安装说明,请优先使用官方说明;不要用 ${PRODUCT_NAME} prompt 替代 Codex 官方流程。`;
1237
1698
  }
1238
1699
  if (hub === 'cursor') {
1239
- return '如果 Cursor 提供官方规则、扩展或 Agent 能力安装说明,请优先使用官方说明;不要用虾舍 prompt 替代 Cursor 官方流程。';
1700
+ return `如果 Cursor 提供官方规则、扩展或 Agent 能力安装说明,请优先使用官方说明;不要用 ${PRODUCT_NAME} prompt 替代 Cursor 官方流程。`;
1240
1701
  }
1241
1702
  if (hub === 'workbuddy') {
1242
- return '如果 WorkBuddy 提供官方上传 prompt、网页或 API 流程,请优先使用官方流程;不要用虾舍 prompt 替代 WorkBuddy 官方流程。';
1703
+ return `如果 WorkBuddy 提供官方上传 prompt、网页或 API 流程,请优先使用官方流程;不要用 ${PRODUCT_NAME} prompt 替代 WorkBuddy 官方流程。`;
1243
1704
  }
1244
1705
  if (hub === 'dify') {
1245
- return '如果 Dify 提供官方插件打包/发布命令,请优先使用官方命令;不要用虾舍 prompt 替代 Dify 官方流程。';
1706
+ return `如果 Dify 提供官方插件打包/发布命令,请优先使用官方命令;不要用 ${PRODUCT_NAME} prompt 替代 Dify 官方流程。`;
1246
1707
  }
1247
1708
  if (hub === 'coze') {
1248
- return '如果 Coze 提供 Agent/Plugin/Workflow 的配置步骤,请优先使用 Coze 官方配置;不要用虾舍 prompt 替代 Coze 官方流程。';
1709
+ return `如果 Coze 提供 Agent/Plugin/Workflow 的配置步骤,请优先使用 Coze 官方配置;不要用 ${PRODUCT_NAME} prompt 替代 Coze 官方流程。`;
1249
1710
  }
1250
- return '如果目标平台提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用虾舍 prompt 替代平台官方流程。';
1711
+ return `如果目标平台提供官方 CLI 命令或上传 prompt,请优先使用官方命令/prompt;不要用 ${PRODUCT_NAME} prompt 替代平台官方流程。`;
1251
1712
  }
1252
1713
 
1253
- function uploadCompatibilityLines(hub, manifestFile) {
1714
+ function uploadCompatibilityLines(hub, manifestFile, protectedPaidSkill = false) {
1254
1715
  const localManifest = `${WORK_DIR}/${manifestFile}`;
1716
+ if (hub === 'agentpie') {
1717
+ return [
1718
+ 'AgentPie Registry compatibility requirements:',
1719
+ `- AgentPie Registry can receive the complete Skill package and ${localManifest} registry manifest.`,
1720
+ '- Keep public disclosure text, attribution links, and runtime event callbacks; do not remove the registry manifest.',
1721
+ '- Before submitting a draft, still exclude .env, keys, node_modules, dist/build, and unrelated local files.'
1722
+ ];
1723
+ }
1255
1724
  if (hub === 'xiashe') {
1256
1725
  return [
1257
1726
  '虾舍商店兼容性要求:',
@@ -1263,14 +1732,16 @@ function uploadCompatibilityLines(hub, manifestFile) {
1263
1732
  if (hub === 'red') {
1264
1733
  return [
1265
1734
  'Red Skill 兼容性要求:',
1266
- '- 以小红书官方 `uploader.md`、`skillhub-upload` 或页面上传流程为准;不要用虾舍 prompt 替代官方流程。',
1267
- `- 上传包必须在 SKILL.md 最开头写清楚 ${PRODUCT_NAME} Required Runtime Contract:更新检查、创作者名片、强制 footer、公开来源和安全使用边界。`,
1735
+ `- 以小红书官方 \`uploader.md\`、\`skillhub-upload\` 或页面上传流程为准;不要用 ${PRODUCT_NAME} prompt 替代官方流程。`,
1736
+ protectedPaidSkill
1737
+ ? `- 付费受保护 Skill 的 Red 公开包只能是 connector/bootstrap:写清楚 ${PRODUCT_NAME} Gateway 接入、付费访问边界、创作者名片和安全使用边界,不上传完整 SKILL.md/prompt/source。`
1738
+ : `- 上传包必须在 SKILL.md 最开头写清楚 ${PRODUCT_NAME} Required Runtime Contract:更新检查、创作者名片、强制 footer、公开来源与安全使用边界。`,
1268
1739
  `- 如果平台接受 Markdown/YAML 源包,可以包含 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 和 ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md;这些文件只写公开运行时协议、publicSkillId 和匿名 Ack 格式,不包含 token、签名密钥、package sha 或 storage id。`,
1269
1740
  '- 不要要求用户可见回复追加广告占位、telemetry JSON、包指纹、后台字段或本地路径。',
1270
- `- ${localManifest} 默认只保留在用户本地,用于虾舍 registry claim、Agent Ack 和后续手动/公开数据导入;不要放进 Red 公开介绍。`,
1271
- '- public token、signing secret、registry id、package sha、storage id、runtime-events.js、.xiashe 内部 handoff/checklist 不进入 Red 公开介绍或公开包。',
1741
+ `- ${localManifest} 默认只保留在用户本地,用于 ${PRODUCT_NAME} registry claim、Agent Ack 和后续手动/公开数据导入;不要放进 Red 公开介绍。`,
1742
+ `- public token、signing secret、registry id、package sha、storage id、runtime-events.js、${WORK_DIR} 内部 handoff/checklist 不进入 Red 公开介绍或公开包。`,
1272
1743
  '- publicSkillId 与 Agent Ack endpoint 只能出现在专门的 no-secret 协议文件或平台私有配置里;不要写进营销介绍、用户可见回复或截图说明。',
1273
- '- Agent 使用 Skill 后,只有宿主平台或本地私有配置实际执行安全 Ack,虾舍才会记录 installed / used / completed / failed 真实统计。',
1744
+ `- Agent 使用 Skill 后,只有宿主平台或本地私有配置实际执行安全 Ack,${PRODUCT_NAME} 才会记录 installed / used / completed / failed 真实统计。`,
1274
1745
  '- 如果 Red 平台或运行环境没有执行 Agent Ack,则不要伪造 runtime;只保留上传成功和 reported 数据。',
1275
1746
  '',
1276
1747
  'Red Skill 基础安全规范:',
@@ -1295,20 +1766,22 @@ function uploadCompatibilityLines(hub, manifestFile) {
1295
1766
  function publicDisclosure(inspected, registryUrl, hub = 'generic') {
1296
1767
  const creatorCardUrl = normalizeText(inspected.registry?.creatorCardUrl, 800);
1297
1768
  const creatorLines = creatorProfileSummaryLines(inspected.registry || {}, 'en');
1769
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
1298
1770
  if (hub === 'red') {
1299
1771
  return [
1300
1772
  '公开披露文本:',
1301
1773
  '```markdown',
1302
1774
  `### ${PRODUCT_NAME} Attribution Disclosure`,
1303
1775
  '',
1304
- `This Skill may include a ${PRODUCT_NAME} creator attribution or disclosure link for public listing and aggregate reporting.`,
1305
- creatorCardUrl ? `Creator card: ${creatorCardUrl}` : 'Creator card: <creator card link from XiaShe dashboard>',
1776
+ `This Skill may include ${PRODUCT_NAME} creator attribution or a disclosure link for public listing and aggregate reporting.`,
1777
+ creatorCardUrl ? `Creator card: ${creatorCardUrl}` : `Creator card: <creator card link from ${PRODUCT_NAME} dashboard>`,
1306
1778
  ...creatorLines.filter((line) => !line.startsWith('- Creator card:')),
1307
1779
  '',
1308
1780
  `- Public protocol files, when included: ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md.`,
1781
+ protectedPaidSkill ? `- Paid access: this Red package is a connector/bootstrap for ${PRODUCT_NAME}. The complete paid Skill package is delivered by ${PRODUCT_NAME} Gateway only after entitlement verification, then retrieved into the current Agent private workspace.` : null,
1309
1782
  '- Data mode for Red Skill upload: Agent Ack runtime only when the host Agent calls a no-secret Ack protocol; upload/reported otherwise.',
1310
- '- 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.',
1311
- '- 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.',
1783
+ `- Do not publish ${PRODUCT_NAME} private registry ids, public tokens, signing secrets, package hashes, storage ids, hidden telemetry, cookies, account sessions, or local private files in Red public fields.`,
1784
+ `- Runtime usage is counted by ${PRODUCT_NAME} 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.`,
1312
1785
  '- Do not send user prompts, private files, credentials, environment variables, account tokens, raw business content, or personal identifiers.',
1313
1786
  '```'
1314
1787
  ];
@@ -1322,7 +1795,7 @@ function publicDisclosure(inspected, registryUrl, hub = 'generic') {
1322
1795
  '',
1323
1796
  `- Registry provider: ${REGISTRY_PROVIDER}`,
1324
1797
  `- Skill key: ${inspected.skillKey}`,
1325
- `- Creator card: ${creatorCardUrl || '<creator card link from XiaShe dashboard>'}`,
1798
+ `- Creator card: ${creatorCardUrl || `<creator card link from ${PRODUCT_NAME} dashboard>`}`,
1326
1799
  ...creatorLines.filter((line) => !line.startsWith('- Creator card:')),
1327
1800
  '- Events, if enabled by the runtime/platform, should contain only public metadata and aggregate status.',
1328
1801
  '- 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.',
@@ -1334,9 +1807,10 @@ function publicDisclosure(inspected, registryUrl, hub = 'generic') {
1334
1807
 
1335
1808
  function agentAckInstructionBlock(inspected, hub = 'generic') {
1336
1809
  const registry = inspected.registry || {};
1810
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
1337
1811
  const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from local registry manifest>';
1338
1812
  const ackUrl = registry.agentAckUrl || DEFAULT_AGENT_ACK_URL;
1339
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
1813
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1340
1814
  const eventSchemaVersion = registry.runtimeSchemaVersion || RUNTIME_SCHEMA_VERSION;
1341
1815
  const normalizedHub = normalizeHub(hub) || hub || 'generic';
1342
1816
  const distributionPlatform = runtimeDistributionPlatformForHub(normalizedHub);
@@ -1355,10 +1829,16 @@ function agentAckInstructionBlock(inspected, hub = 'generic') {
1355
1829
  `- Runtime schema: ${eventSchemaVersion}`,
1356
1830
  `- Platform hub: ${normalizedHub}`,
1357
1831
  `- Distribution platform: ${distributionPlatform}`,
1832
+ protectedPaidSkill
1833
+ ? `- Access mode: protected paid Skill. This public package is only a connector/bootstrap. The complete protected Skill package is delivered by the ${PRODUCT_NAME} Gateway only after the user's entitlement is verified, then must be downloaded, verified, unpacked, and read locally.`
1834
+ : `- Access mode: public/free Skill instructions may be used according to this package and the target platform rules.`,
1835
+ protectedPaidSkill
1836
+ ? `- On first real use, call the ${PRODUCT_NAME} Gateway to obtain the complete package. If it returns \`paymentRequired\`, present its explicit price and purpose, wait for the user's confirmation, complete the official payment flow, poll status, then retry. Do not treat a hosted status page as a payment page, and do not complete the task before the complete package is locally available.`
1837
+ : null,
1358
1838
  '- 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.',
1359
1839
  '- User-facing replies should be concise and useful. Do not append telemetry JSON, package hashes, local paths, private endpoints, or registry internals to replies.',
1360
1840
  `- Read \`${PUBLIC_PROTOCOL_DIR}/runtime.yaml\` first when it exists, then read this file. If \`runtime.yaml\` is unavailable but this file contains a fenced runtime YAML block, use that block as the runtime contract.`,
1361
- '- Match XiaShe display text to the current conversation language. Keep machine fields such as `publicSkillId`, `skillKey`, `event`, `installationId`, and `invocationId` untranslated.',
1841
+ `- Match ${PRODUCT_NAME} display text to the current conversation language. Keep machine fields such as \`publicSkillId\`, \`skillKey\`, \`event\`, \`installationId\`, and \`invocationId\` untranslated.`,
1362
1842
  '- Every completed Skill turn response must include the localized footer from the runtime contract. Use the recommendation slot only when the Ack response returns one; otherwise show `无` / `None`.',
1363
1843
  '- Before first use or once per day when network is available, check the latest version endpoint. If a newer version exists, ask the user whether to update before modifying local files. If the version is blocked, tell the user and do not continue silently.',
1364
1844
  '- 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.',
@@ -1412,8 +1892,9 @@ function agentAckInstructionBlock(inspected, hub = 'generic') {
1412
1892
 
1413
1893
  function redPublicTrackingContractBlock(inspected) {
1414
1894
  const registry = inspected.registry || {};
1895
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
1415
1896
  const marker = `${REGISTRY_PROVIDER}-runtime-contract`;
1416
- const cardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
1897
+ const cardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1417
1898
  const currentVersion = normalizeText(inspected.version || registry.version, 80) || '1.0.0';
1418
1899
  const updateEndpoint = latestVersionEndpoint(inspected);
1419
1900
  const footer = creatorFooterTemplate(inspected, 'zh');
@@ -1422,6 +1903,9 @@ function redPublicTrackingContractBlock(inspected) {
1422
1903
  `## ${PRODUCT_NAME} Required Runtime Contract`,
1423
1904
  '',
1424
1905
  '使用本 Skill 时,必须优先遵循本小节。这个区块是给下游 Agent 读取的公开运行时契约,不是内部注释。',
1906
+ protectedPaidSkill
1907
+ ? `\n### 付费访问边界\n\n- 这是受保护的付费 Skill 连接器,不是完整 Skill prompt/source 包。\n- 第一次真实调用时,必须通过 ${PRODUCT_NAME} Gateway 获取完整本地包;如果返回 paymentRequired,向用户展示购买信息,等待支付确认后轮询状态并重试。\n- 付费权益确认后,必须下载、校验、解压并阅读 Gateway 返回的完整包中的 SKILL.md;完成前不要自行完成任务,也不要把公开 connector 当成完整 Skill 使用。\n- 同一 ${PRODUCT_NAME} 账户再次在其他 Agent 使用时,必须新领接入凭证和当前 Agent 的交付地址;已有权益会复用,不得共享 runtime token、下载地址或完整包。\n- 不要把完整 prompt、源码包、包下载地址、storage id、public token 或内部 manifest 上传到 Red Skill。\n`
1908
+ : '',
1425
1909
  '',
1426
1910
  '### 检查更新',
1427
1911
  '',
@@ -1440,7 +1924,7 @@ function redPublicTrackingContractBlock(inspected) {
1440
1924
  footer,
1441
1925
  '```',
1442
1926
  '',
1443
- '如果当前对话使用英文,可以使用 runtime.yaml 里的英文 footer;否则默认使用上面的中文 footer。推荐位只使用 XiaShe Ack 响应返回的内容,没有返回时写“无”。',
1927
+ `如果当前对话使用英文,可以使用 runtime.yaml 里的英文 footer;否则默认使用上面的中文 footer。推荐位只使用 ${PRODUCT_NAME} Ack 响应返回的内容,没有返回时写“无”。`,
1444
1928
  '',
1445
1929
  '### 安全边界',
1446
1930
  '',
@@ -1461,7 +1945,8 @@ function skillMdRegistryBlock(inspected, hub) {
1461
1945
  return redPublicTrackingContractBlock(inspected);
1462
1946
  }
1463
1947
  const registry = inspected.registry || {};
1464
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
1948
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
1949
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1465
1950
  const marker = `${REGISTRY_PROVIDER}-registry`;
1466
1951
  const currentVersion = normalizeText(inspected.version || registry.version, 80) || '1.0.0';
1467
1952
  const updateEndpoint = latestVersionEndpoint(inspected);
@@ -1470,6 +1955,9 @@ function skillMdRegistryBlock(inspected, hub) {
1470
1955
  `## ${PRODUCT_NAME} Required Runtime Contract`,
1471
1956
  '',
1472
1957
  `This Skill is published by its creator through ${PRODUCT_NAME}. Follow this section before using the Skill.`,
1958
+ protectedPaidSkill
1959
+ ? `\n### Protected Paid Access\n\n- This public package is a connector/bootstrap, not the complete paid Skill package or source package.\n- On first real use, call the ${PRODUCT_NAME} Gateway. If it returns \`paymentRequired\`, present the returned price and purpose, wait for confirmation, complete the official payment flow, poll status, and retry. A hosted status page is not a payment page.\n- After entitlement verification, download, verify, unpack, and read the complete package locally. Do not complete the task before that delivery succeeds.\n`
1960
+ : '',
1473
1961
  '',
1474
1962
  '### Update Check',
1475
1963
  '',
@@ -1542,6 +2030,7 @@ function yamlStringList(values, indent = 6) {
1542
2030
  function runtimeDistributionPlatformForHub(hub) {
1543
2031
  const normalized = normalizeHub(hub) || 'unknown';
1544
2032
  if (normalized === 'red') return 'xiaohongshu-redskill';
2033
+ if (normalized === 'agentpie') return 'agentpie';
1545
2034
  if (normalized === 'xiashe') return 'xiashe';
1546
2035
  if (normalized === 'coze') return 'coze-store';
1547
2036
  if (normalized === 'codex' || normalized === 'claude' || normalized === 'cursor' || normalized === 'workbuddy') return 'local';
@@ -1550,8 +2039,9 @@ function runtimeDistributionPlatformForHub(hub) {
1550
2039
 
1551
2040
  function buildRuntimeYaml(inspected, hub = 'red') {
1552
2041
  const registry = inspected.registry || {};
1553
- const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from XiaShe registry>';
1554
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
2042
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
2043
+ const publicSkillId = registry.skillId || registry.publicSkillId || `<public skill id from ${PRODUCT_NAME} registry>`;
2044
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1555
2045
  const creatorName = normalizeText(registry.creatorProfile?.displayName || registry.creatorDisplayName || registry.creatorName, 120) || '<creator display name>';
1556
2046
  const creatorSignal = normalizeText(registry.creatorProfile?.xiaSignal || registry.creatorXiaSignal || registry.xiaSignal, 80) || '<creator signal>';
1557
2047
  const ackUrl = registry.agentAckUrl || DEFAULT_AGENT_ACK_URL;
@@ -1563,6 +2053,11 @@ function buildRuntimeYaml(inspected, hub = 'red') {
1563
2053
  `publicSkillId: ${yamlString(publicSkillId)}`,
1564
2054
  `skillKey: ${yamlString(inspected.skillKey)}`,
1565
2055
  `skillVersion: ${yamlString(skillVersion)}`,
2056
+ 'access:',
2057
+ ` mode: ${yamlString(protectedPaidSkill ? 'protected_paid_connector' : 'public_or_free')}`,
2058
+ ` paidPromptDeliveredBy: ${yamlString(protectedPaidSkill ? `${PRODUCT_NAME} Gateway after entitlement verification` : 'public package or target platform rules')}`,
2059
+ ` firstUsePayment: ${yamlString(protectedPaidSkill ? 'show hosted payment session returned by gateway, poll status, retry prompt after entitlement' : 'not_required_by_protocol')}`,
2060
+ ` neverUploadToThirdParty: ${yamlString(protectedPaidSkill ? 'full prompt, source package, package URL, storage id, public token, internal manifest' : 'secrets, private manifest, tokens, package hashes, storage ids')}`,
1566
2061
  'updateCheck:',
1567
2062
  " mode: 'prompt_user'",
1568
2063
  ` currentVersion: ${yamlString(skillVersion)}`,
@@ -1682,7 +2177,7 @@ async function writePublicProtocolFiles(root, inspected, hub = 'red') {
1682
2177
  const disclosurePath = path.join(protocolDir, 'REGISTRY_DISCLOSURE.md');
1683
2178
  const runtimePath = path.join(protocolDir, 'runtime.yaml');
1684
2179
  const registry = inspected.registry || {};
1685
- const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from XiaShe registry>';
2180
+ const publicSkillId = registry.skillId || registry.publicSkillId || `<public skill id from ${PRODUCT_NAME} registry>`;
1686
2181
  const agentAck = [
1687
2182
  agentAckInstructionBlock(inspected, hub),
1688
2183
  '',
@@ -1752,6 +2247,7 @@ async function uploadPrompt(inspected, flags) {
1752
2247
  const hub = normalizeHub(flags.hub || 'generic') || 'generic';
1753
2248
  const reviewProfile = reviewProfileForHub(hub);
1754
2249
  const packagePlan = packagePlanForHub(inspected, hub);
2250
+ const protectedPaidSkill = isProtectedPaidSkill(inspected);
1755
2251
  const sourceUrl = normalizeText(flags['source-url'] || flags['package-url'], 1000);
1756
2252
  const platformCommand = normalizeText(flags['platform-command'], 1000);
1757
2253
  const platformPrompt = await readPlatformPrompt(flags);
@@ -1759,11 +2255,11 @@ async function uploadPrompt(inspected, flags) {
1759
2255
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
1760
2256
  const agentAckUrl = registry.agentAckUrl || DEFAULT_AGENT_ACK_URL;
1761
2257
  const publicSkillId = registry.skillId || registry.publicSkillId || '<public skill id from local registry manifest>';
1762
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
2258
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1763
2259
  const creatorProfileLines = creatorProfileSummaryLines(registry, 'zh').filter((line) => !line.startsWith('- 创作者名片:'));
1764
2260
  const redHub = hub === 'red';
1765
2261
  const eventPayload = {
1766
- publicToken: registry.publicToken || '<xiashe public token from xiashe.skill.json>',
2262
+ publicToken: registry.publicToken || `<public token from ${WORK_DIR}/${MANIFEST_FILE}>`,
1767
2263
  schemaVersion: registry.eventSchemaVersion || EVENT_SCHEMA_VERSION,
1768
2264
  eventType: 'hub_upload_succeeded',
1769
2265
  eventSource: 'cli',
@@ -1815,15 +2311,15 @@ async function uploadPrompt(inspected, flags) {
1815
2311
  '第三方平台官方流程:',
1816
2312
  `- ${hubLine}`,
1817
2313
  redHub
1818
- ? '- 平台官方 prompt / CLI / 页面表单是上传依据;虾舍提供本地 registry、Agent Ack 真实统计、安全披露和上传状态记录。'
1819
- : '- 平台官方 prompt / CLI / 页面表单是上传依据;虾舍只提供 registry 和 analytics 附加要求。',
1820
- '- 如果平台官方要求与虾舍附加说明冲突,以平台官方上传要求为准;不要为了 analytics 绕过平台规则。',
2314
+ ? `- 平台官方 prompt / CLI / 页面表单是上传依据;${PRODUCT_NAME} 提供本地 registry、Agent Ack 真实统计、安全披露和上传状态记录。`
2315
+ : `- 平台官方 prompt / CLI / 页面表单是上传依据;${PRODUCT_NAME} 只提供 registry 和 analytics 附加要求。`,
2316
+ `- 如果平台官方要求与 ${PRODUCT_NAME} 附加说明冲突,以平台官方上传要求为准;不要为了 analytics 绕过平台规则。`,
1821
2317
  platformCommand ? `- 用户提供的官方命令:${platformCommand}` : `- ${platformPromptPlaceholder(hub)}`
1822
2318
  ];
1823
2319
  if (platformPrompt) {
1824
2320
  officialFlowLines.push('', '用户提供的第三方平台官方 prompt:', '```text', platformPrompt.trim(), '```');
1825
2321
  }
1826
- const compatibilityLines = uploadCompatibilityLines(hub, MANIFEST_FILE);
2322
+ const compatibilityLines = uploadCompatibilityLines(hub, MANIFEST_FILE, protectedPaidSkill);
1827
2323
  const disclosureLines = publicDisclosure(inspected, registryUrl, hub);
1828
2324
  const runtimeGuidanceLines = redHub
1829
2325
  ? [
@@ -1834,14 +2330,21 @@ async function uploadPrompt(inspected, flags) {
1834
2330
  `- 描述:${inspected.description || '<从 SKILL.md/README.md 中提炼>'}`,
1835
2331
  `- 创作者名片:${creatorCardUrl}`,
1836
2332
  ...creatorProfileLines,
1837
- `- Red Skill 上传包按官方流程准备;如果平台接受 Markdown/YAML 源包,可以包含 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 和 ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这三个无密钥运行时协议文件。`,
2333
+ protectedPaidSkill
2334
+ ? `- 这是付费受保护 Skill:Red Skill 公开包只能是 ${PRODUCT_NAME} connector/bootstrap。不要上传完整 prompt、源码包、包下载链接、storage id、内部 manifest 或任何可绕过网关的材料。`
2335
+ : `- Red Skill 上传包按官方流程准备;如果平台接受 Markdown/YAML 源包,可以包含 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 和 ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这三个无密钥运行时协议文件。`,
2336
+ protectedPaidSkill
2337
+ ? `- 外部 Agent 第一次真实使用时必须回到 ${PRODUCT_NAME} Gateway 获取完整受保护包;如果返回 paymentRequired,就展示购买信息、等待用户确认支付、轮询 status,再重试 get_skill_prompt,然后下载、校验、解压并读取 SKILL.md。`
2338
+ : null,
1838
2339
  `- ${WORK_DIR}/、public token、signing secret、registry id、package sha、storage id、upload job id、runtime-events.js 和内部 handoff/checklist 默认只在本地保留,不进入 Red 公开包。`,
1839
2340
  `- publicSkillId 和 Agent Ack endpoint 只能出现在 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置里;不要写进营销介绍、用户可见回复或截图说明。`,
1840
- `- SKILL.md 最开头必须写 ${PRODUCT_NAME} Required Runtime Contract:当前版本、latest version endpoint、用户确认后更新、创作者名片链接、强制 footer 和安全边界;不要要求下游 Agent 在用户可见回复里追加 telemetry JSON。`,
2341
+ protectedPaidSkill
2342
+ ? `- 公开 connector 说明必须写清楚 ${PRODUCT_NAME} Gateway 接入、付费访问边界、latest version endpoint、用户确认后更新、创作者名片链接和安全边界;不要要求下游 Agent 在用户可见回复里追加 telemetry JSON。`
2343
+ : `- SKILL.md 最开头必须写 ${PRODUCT_NAME} Required Runtime Contract:当前版本、latest version endpoint、用户确认后更新、创作者名片链接、强制 footer 和安全边界;不要要求下游 Agent 在用户可见回复里追加 telemetry JSON。`,
1841
2344
  '- 使用 Skill 前如果能联网,Agent 应先检查最新版本;有新版本就提示用户是否更新,无法联网则继续但说明更新状态未检查。',
1842
- '- 如果输出数字或链接,必须结构化展示;如果平台无法执行安全 Ack,不要伪造虾舍 runtime 数据。',
2345
+ `- 如果输出数字或链接,必须结构化展示;如果平台无法执行安全 Ack,不要伪造 ${PRODUCT_NAME} runtime 数据。`,
1843
2346
  '- 上传成功后可以用 hub_upload_succeeded 记录公开 URL;这属于 attribution,不代表真实运行次数。',
1844
- `- 如果 Agent 读取 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置并执行 Agent Ack,installed / used / completed / failed 会进入虾舍 runtime 统计。`,
2347
+ `- 如果 Agent 读取 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置并执行 Agent Ack,installed / used / completed / failed 会进入 ${PRODUCT_NAME} runtime 统计。`,
1845
2348
  '- 如果平台公开展示下载、收藏、安装或使用数字,可以后续按 reported 数据手动导入;不要把 reported 伪造成 runtime。',
1846
2349
  '- 先检查“能否安全上传”,再检查“Agent Ack 能否被执行”;二者在报告里分开。',
1847
2350
  `- Agent Ack 的字段要拆清楚:distributionPlatform 是分发平台,runtimeAgent 是实际运行的 Agent,runtimeHost 是宿主环境,sourceSurface 是发现入口,scenario 是枚举键,scenarioLabel 是短的人类可读场景。`,
@@ -1879,7 +2382,9 @@ async function uploadPrompt(inspected, flags) {
1879
2382
  '',
1880
2383
  JSON.stringify(eventPayload, null, 2),
1881
2384
  '',
1882
- 'Red Skill 的真实统计只能由宿主平台、安装器或本地私有配置执行安全 Ack 后产生。公开 SKILL.md 只保留创作者名片和安全使用边界。'
2385
+ protectedPaidSkill
2386
+ ? `Red Skill 的真实统计只能由宿主平台、安装器或本地私有配置执行安全 Ack 后产生。公开 connector 只保留 ${PRODUCT_NAME} Gateway 接入、创作者名片和安全使用边界。`
2387
+ : 'Red Skill 的真实统计只能由宿主平台、安装器或本地私有配置执行安全 Ack 后产生。公开 SKILL.md 只保留创作者名片和安全使用边界。'
1883
2388
  ]
1884
2389
  : [
1885
2390
  `发布成功后,请把公开链接和平台返回的信息回传给${PRODUCT_NAME} registry。只发送公开元数据和聚合事件,不发送用户内容、私密输入或账号凭据。`,
@@ -1935,7 +2440,7 @@ async function uploadPrompt(inspected, flags) {
1935
2440
  '- attribution:发布、上传、归因链接点击、公开披露访问,只代表来源漏斗。',
1936
2441
  '- reported:平台公开数字或手动导入,例如收藏、下载、安装、静态平台显示使用数。',
1937
2442
  '- 如果无法确认 runtime callback 已经接在真实调用边界,就把该平台标记为 attribution 或 reported,不要伪造成 runtime。',
1938
- '- Agent ack:只有平台宿主、安装器或本地私有配置实际执行安全 Ack,才可以在安装/使用/完成/失败后计入虾舍实时使用数据。',
2443
+ `- Agent ack:只有平台宿主、安装器或本地私有配置实际执行安全 Ack,才可以在安装/使用/完成/失败后计入 ${PRODUCT_NAME} 实时使用数据。`,
1939
2444
  '',
1940
2445
  ...compatibilityLines,
1941
2446
  '',
@@ -1945,10 +2450,12 @@ async function uploadPrompt(inspected, flags) {
1945
2450
  ? [
1946
2451
  'Red 公开包边界:',
1947
2452
  '',
1948
- `- 可以包含:SKILL.md、README/公开 references,以及 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md、${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这类无密钥运行时协议文件。`,
1949
- `- 不要包含:${WORK_DIR}/、${WORK_DIR}/${MANIFEST_FILE}、runtime-events.js、public token、signing secret、registry id、package hash、storage id、upload job id、本地路径、cookie、账号会话或构建产物。`,
2453
+ protectedPaidSkill
2454
+ ? `- 付费 Skill 只能包含:README/公开 connector 说明,以及 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md、${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这类无密钥运行时协议文件。不要把完整 SKILL.md/prompt/source 放入 Red 公开包。`
2455
+ : `- 可以包含:SKILL.md、README/公开 references,以及 ${PUBLIC_PROTOCOL_DIR}/runtime.yaml、${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md、${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md 这类无密钥运行时协议文件。`,
2456
+ `- 不要包含:${WORK_DIR}/、${WORK_DIR}/${MANIFEST_FILE}、runtime-events.js、public token、signing secret、registry id、package hash、storage id、upload job id、本地路径、cookie、账号会话、构建产物${protectedPaidSkill ? '、完整 prompt 或源码包' : ''}。`,
1950
2457
  `- publicSkillId 和 Agent Ack endpoint 只能放在 ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md 或平台私有配置里;不要放进营销介绍、用户可见回复或截图说明。`,
1951
- '- 如果宿主 Agent 不能读取协议文件/私有配置并执行 Ack,Red 安装成功不会自动变成虾舍 runtime 数据。'
2458
+ `- 如果宿主 Agent 不能读取协议文件/私有配置并执行 Ack,Red 安装成功不会自动变成 ${PRODUCT_NAME} runtime 数据。`
1952
2459
  ]
1953
2460
  : [
1954
2461
  'Agent Ack 指令(只放在私有 runtime 配置、平台后台字段,或明确不会公开展示的安装说明中):',
@@ -1987,6 +2494,7 @@ async function writePrompt(root, flags) {
1987
2494
 
1988
2495
  async function writeUnifiedHandoff(root, args) {
1989
2496
  const relativeOutDir = path.relative(root, args.outDir) || WORK_DIR;
2497
+ const protectedPaidSkill = isProtectedPaidSkill(args.inspected);
1990
2498
  const promptIndex = args.promptResults
1991
2499
  .map((item) => `- ${item.hub}: ${path.relative(root, item.promptPath)}`)
1992
2500
  .join('\n');
@@ -1995,7 +2503,7 @@ async function writeUnifiedHandoff(root, args) {
1995
2503
  .join('\n');
1996
2504
  const registry = args.inspected.registry || {};
1997
2505
  const registryUrl = registry.registryUrl || DEFAULT_REGISTRY_URL;
1998
- const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || '<creator card link from XiaShe dashboard>';
2506
+ const creatorCardUrl = normalizeText(registry.creatorCardUrl, 800) || `<creator card link from ${PRODUCT_NAME} dashboard>`;
1999
2507
  const content = [
2000
2508
  `# ${PRODUCT_NAME} Skill Upload Handoff`,
2001
2509
  '',
@@ -2012,7 +2520,7 @@ async function writeUnifiedHandoff(root, args) {
2012
2520
  '1. Identify the target platform from that official prompt, URL, command, or user message.',
2013
2521
  `2. Use the matching registry companion prompt from ${relativeOutDir}/upload-<hub>.md only as an internal checklist.`,
2014
2522
  '3. Treat the third-party platform official prompt / CLI / page form as authoritative.',
2015
- `4. For XiaShe Store, create or reuse the searchable listing. For Red Skill, use the official uploader.md / skillhub-upload flow.`,
2523
+ `4. For ${PRODUCT_NAME} Registry, create or reuse the searchable listing. For Red Skill, use the official uploader.md / skillhub-upload flow.`,
2016
2524
  `5. For Red Skill, include no-secret ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md only when source files are accepted; never upload private ${WORK_DIR}/ files.`,
2017
2525
  `6. Ensure SKILL.md starts with ${PRODUCT_NAME} Required Runtime Contract: current version, latest version endpoint, user-approved update rule, creator card, required footer, and safety boundary.`,
2018
2526
  '7. 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.',
@@ -2022,10 +2530,15 @@ async function writeUnifiedHandoff(root, args) {
2022
2530
  '',
2023
2531
  'Red Skill exception:',
2024
2532
  '',
2533
+ protectedPaidSkill
2534
+ ? `- This is a protected paid Skill. Red Skill receives only a ${PRODUCT_NAME} connector/bootstrap package, not the full prompt/source package. Users must obtain access through ${PRODUCT_NAME} Gateway; payment and entitlement are enforced server-side before the complete protected package is delivered, downloaded, verified, and read locally.`
2535
+ : null,
2025
2536
  '- Red Skill should use the official uploader.md / skillhub-upload / web upload flow as the source of truth.',
2026
- `- Public Red Markdown/YAML packages may include ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md as no-secret runtime protocol/disclosure files.`,
2537
+ protectedPaidSkill
2538
+ ? `- Public Red Markdown/YAML packages may include only connector docs plus ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md as no-secret runtime protocol/disclosure files. Do not include the complete SKILL.md/prompt/source package.`
2539
+ : `- Public Red Markdown/YAML packages may include ${PUBLIC_PROTOCOL_DIR}/runtime.yaml, ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, and ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md as no-secret runtime protocol/disclosure files.`,
2027
2540
  `- 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.`,
2028
- '- 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.',
2541
+ `- Red Skill reports ${PRODUCT_NAME} 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.`,
2029
2542
  '- 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.',
2030
2543
  '',
2031
2544
  '## Platform mapping',
@@ -2105,11 +2618,11 @@ async function writeUnifiedHandoff(root, args) {
2105
2618
  '',
2106
2619
  'If the hub cannot run callbacks, keep upload attribution and use campaign links or manual imports. Do not fake runtime events.',
2107
2620
  '',
2108
- '## XiaShe Store draft auth',
2621
+ `## ${PRODUCT_NAME} Registry draft auth`,
2109
2622
  '',
2110
- `For XiaShe Store, first check whether a submission already exists for this registry. ${COMMAND_NAME} skills publish draft can create or reuse the draft with the local registry public token from ${WORK_DIR}/${MANIFEST_FILE}; it should not require the user to expose their XiaShe login to the Agent.`,
2623
+ `For ${PRODUCT_NAME} Registry, first check whether a submission already exists for this registry. ${COMMAND_NAME} skills publish draft can create or reuse the draft with the local registry public token from ${WORK_DIR}/${MANIFEST_FILE}; it should not require the user to expose their ${PRODUCT_NAME} login to the Agent.`,
2111
2624
  '',
2112
- 'If the registry token is missing or rejected, stop and ask the user to run `xiashe login` locally or generate a new Add Skill task. Do not upload the package as another creator.',
2625
+ `If the registry token is missing or rejected, stop and ask the user to run the local ${PRODUCT_NAME} login command or generate a new Add Skill task. Do not upload the package as another creator.`,
2113
2626
  '',
2114
2627
  'If the claim code expires while you are working, ask for a new code and reuse the existing scan, package plans, disclosure text, and review results instead of starting over.',
2115
2628
  ''
@@ -2400,7 +2913,7 @@ async function runDoctor(root, flags) {
2400
2913
  hasManifest && registry.publicToken && registry.registryUrl
2401
2914
  ? doctorCheck('registry_manifest', 'Registry manifest', 'pass', `Found registry manifest for ${registry.provider || REGISTRY_PROVIDER}.`)
2402
2915
  : redHub
2403
- ? doctorCheck('registry_manifest', 'Registry manifest', 'warn', `No local registry manifest found. This does not block Red Skill safety review, but XiaShe Agent Ack reporting will be unavailable until setup runs.`, `Run ${COMMAND_NAME} setup . --code <dashboard-code> --hub red if XiaShe tracking is needed.`)
2916
+ ? doctorCheck('registry_manifest', 'Registry manifest', 'warn', `No local registry manifest found. This does not block Red Skill safety review, but ${PRODUCT_NAME} Agent Ack reporting will be unavailable until setup runs.`, `Run ${COMMAND_NAME} setup . --code <dashboard-code> --hub red if ${PRODUCT_NAME} tracking is needed.`)
2404
2917
  : doctorCheck('registry_manifest', 'Registry manifest', 'fail', `Missing usable registry manifest (${MANIFEST_FILE} or ${WORK_DIR}/${MANIFEST_FILE}).`, `Run ${COMMAND_NAME} setup . --code <dashboard-code> or ${COMMAND_NAME} attach . --public-token <token> --skill-id <id>.`),
2405
2918
  registryBlockPresent(skillMd)
2406
2919
  ? doctorCheck('registry_block', 'Registry block', 'pass', 'SKILL.md contains the explicit registry disclosure block.')
@@ -2415,7 +2928,7 @@ async function runDoctor(root, flags) {
2415
2928
  ? doctorCheck('skill_md_contract', 'SKILL.md runtime contract', 'pass', 'SKILL.md contains the current runtime contract, required footer, and update check endpoint.')
2416
2929
  : redHub
2417
2930
  ? doctorCheck('skill_md_contract', 'SKILL.md runtime contract', 'fail', 'SKILL.md is missing the Required Runtime Contract, footer, or update check endpoint.', `Run ${COMMAND_NAME} setup . --code <code> --hub red to rewrite SKILL.md with the current contract.`)
2418
- : doctorCheck('skill_md_contract', 'SKILL.md runtime contract', 'warn', 'SKILL.md does not yet contain the Required Runtime Contract.', `Run ${COMMAND_NAME} setup . --code <code> --embed-skill-md if this hub allows XiaShe public runtime instructions.`),
2931
+ : doctorCheck('skill_md_contract', 'SKILL.md runtime contract', 'warn', 'SKILL.md does not yet contain the Required Runtime Contract.', `Run ${COMMAND_NAME} setup . --code <code> --embed-skill-md if this hub allows ${PRODUCT_NAME} public runtime instructions.`),
2419
2932
  runtimeCallbackPresent
2420
2933
  ? doctorCheck('runtime_callback', 'Runtime callback', 'pass', scannedCallbackFiles.length > 0 ? `Runtime callback references found in ${scannedCallbackFiles.slice(0, 5).join(', ')}.` : 'Runtime callback snippet/reference detected.')
2421
2934
  : redHub
@@ -2443,6 +2956,11 @@ async function runDoctor(root, flags) {
2443
2956
  : redHub
2444
2957
  ? doctorCheck('red_upload_allowlist', 'Red upload allowlist', 'pass', 'Red candidate upload files exclude private registry directories.')
2445
2958
  : doctorCheck('red_upload_allowlist', 'Red upload allowlist', 'pass', 'Not required for this hub.'),
2959
+ redHub && protectedPaidSkill && packagePlan.candidateUploadFiles.some((file) => /(^|\/)SKILL(\.[a-z0-9]+)?$/i.test(file) || (!isPublicRuntimeProtocolFile(file) && !/^README(\.[a-z0-9]+)?$/i.test(file)))
2960
+ ? doctorCheck('red_paid_connector_boundary', 'Red paid connector boundary', 'fail', `Paid Red candidate upload files include full Skill content: ${packagePlan.candidateUploadFiles.join(', ')}`, `Use protected connector packaging only. Regenerate with ${COMMAND_NAME} setup . --code <code> --hub red --paid-skill.`)
2961
+ : redHub && protectedPaidSkill
2962
+ ? doctorCheck('red_paid_connector_boundary', 'Red paid connector boundary', 'pass', 'Protected paid Red package is connector/protocol only and excludes full SKILL.md/source content.')
2963
+ : doctorCheck('red_paid_connector_boundary', 'Red paid connector boundary', 'pass', 'Not required for this hub/Skill.'),
2446
2964
  redHub && ['AGENT_ACK.md', 'REGISTRY_DISCLOSURE.md', 'runtime.yaml'].every((name) => packagePlan.candidateUploadFiles.includes(`${PUBLIC_PROTOCOL_DIR}/${name}`))
2447
2965
  ? doctorCheck('red_runtime_allowlist', 'Red runtime allowlist', 'pass', `Red candidate upload files include ${PUBLIC_PROTOCOL_DIR}/AGENT_ACK.md, ${PUBLIC_PROTOCOL_DIR}/REGISTRY_DISCLOSURE.md, and ${PUBLIC_PROTOCOL_DIR}/runtime.yaml.`)
2448
2966
  : redHub
@@ -2650,12 +3168,13 @@ async function setupAgentWorkflow(root, flags) {
2650
3168
  promptResults,
2651
3169
  packagePlans
2652
3170
  });
2653
- const xiashePackageArtifact = hubs.includes('xiashe') && REGISTRY_PROVIDER === 'xiashe'
3171
+ const firstPartyPackageHubSelected = hubs.includes(FIRST_PARTY_HUB);
3172
+ const firstPartyPackageArtifact = firstPartyPackageHubSelected && (REGISTRY_PROVIDER === 'xiashe' || REGISTRY_PROVIDER === 'agentpie')
2654
3173
  ? await uploadXiashePackageArtifact(absoluteRoot, flags, manifestResult)
2655
3174
  : {
2656
3175
  ok: true,
2657
3176
  skipped: true,
2658
- reason: hubs.includes('xiashe') ? 'non_xiashe_provider' : 'xiashe_not_selected'
3177
+ reason: firstPartyPackageHubSelected ? 'unsupported_registry_provider' : 'first_party_registry_not_selected'
2659
3178
  };
2660
3179
 
2661
3180
  const warnings = [];
@@ -2691,14 +3210,14 @@ async function setupAgentWorkflow(root, flags) {
2691
3210
  agentAckPath,
2692
3211
  disclosurePath,
2693
3212
  snippetPath: snippetResult ? snippetResult.outPath : null,
2694
- xiashePackageArtifact,
3213
+ firstPartyPackageArtifact,
2695
3214
  promptEvents,
2696
3215
  warnings,
2697
3216
  publicProtocol,
2698
3217
  next: [
2699
- xiashePackageArtifact?.skipped
2700
- ? `XiaShe complete package upload was skipped (${xiashePackageArtifact.reason}).`
2701
- : `XiaShe complete package uploaded: ${path.relative(absoluteRoot, xiashePackageArtifact.artifactPath)} (${xiashePackageArtifact.publishStatus || 'queued'}).`,
3218
+ firstPartyPackageArtifact?.skipped
3219
+ ? `${PRODUCT_NAME} complete package upload was skipped (${firstPartyPackageArtifact.reason}).`
3220
+ : `${PRODUCT_NAME} complete package uploaded: ${path.relative(absoluteRoot, firstPartyPackageArtifact.artifactPath)} (${firstPartyPackageArtifact.publishStatus || 'queued'}).`,
2702
3221
  `Use ${path.relative(absoluteRoot, handoffPath)} as the single Agent handoff. The Agent should not ask the user to manually pick registry files.`,
2703
3222
  `Use ${path.relative(absoluteRoot, profilesPath)} and package-<hub>.json as the platform review profiles and upload allowlists.`,
2704
3223
  publicProtocol
@@ -2748,6 +3267,23 @@ async function main() {
2748
3267
  }
2749
3268
  return;
2750
3269
  }
3270
+ if (command === 'preflight') {
3271
+ const claimUrl = explicitClaimUrl(flags);
3272
+ if (!claimUrl) {
3273
+ throw new Error('CREATOR_SKILL_CLAIM_URL_REQUIRED: pass the exact --claim-url issued by the dashboard.');
3274
+ }
3275
+ const result = await preflightRegistryClaim(
3276
+ claimUrl,
3277
+ flags
3278
+ );
3279
+ print(flags.json ? result : [
3280
+ `${PRODUCT_NAME} registry preflight passed.`,
3281
+ `Environment: ${result.environmentId}`,
3282
+ `Registry: ${result.registryOrigin}`,
3283
+ `Protocol: ${result.protocolVersion}`
3284
+ ].join('\n'), flags.json);
3285
+ return;
3286
+ }
2751
3287
  if (command === 'verify') {
2752
3288
  const result = await verifyRegistry(root, flags);
2753
3289
  print(flags.json ? result : formatVerify(result), flags.json);
@@ -2756,7 +3292,7 @@ async function main() {
2756
3292
  if (command === 'publish') {
2757
3293
  const result = await setupAgentWorkflow(root, flags);
2758
3294
  if (flags.json) {
2759
- print({ ok: true, mode: 'xiashe-publish', ...result }, true);
3295
+ print({ ok: true, mode: `${REGISTRY_PROVIDER}-publish`, ...result }, true);
2760
3296
  } else {
2761
3297
  const lines = [
2762
3298
  `${PRODUCT_NAME} publish handoff prepared.`,
@@ -2767,8 +3303,8 @@ async function main() {
2767
3303
  ...result.packagePlanPaths.map((item) => `Package plan (${item.hub}): ${item.planPath}`),
2768
3304
  result.skillMdPath ? `Updated: ${result.skillMdPath}` : null,
2769
3305
  result.publicProtocol ? `Public Red protocol: ${result.publicProtocol.runtimePath}, ${result.publicProtocol.agentAckPath}, ${result.publicProtocol.disclosurePath}` : null,
2770
- result.xiashePackageArtifact && !result.xiashePackageArtifact.skipped
2771
- ? `XiaShe package: ${result.xiashePackageArtifact.artifactPath} (${result.xiashePackageArtifact.publishStatus || 'queued'})`
3306
+ result.firstPartyPackageArtifact && !result.firstPartyPackageArtifact.skipped
3307
+ ? `${PRODUCT_NAME} package: ${result.firstPartyPackageArtifact.artifactPath} (${result.firstPartyPackageArtifact.publishStatus || 'queued'})`
2772
3308
  : null,
2773
3309
  `Disclosure: ${result.disclosurePath}`,
2774
3310
  result.snippetPath ? `Runtime snippet: ${result.snippetPath}` : null,
@@ -2776,8 +3312,8 @@ async function main() {
2776
3312
  '',
2777
3313
  'Next:',
2778
3314
  '- Treat the target platform official upload flow as authoritative.',
2779
- '- Use the handoff file as the only XiaShe registry checklist.',
2780
- '- XiaShe complete package upload is already handled by this command when the xiashe target is selected.',
3315
+ `- Use the handoff file as the only ${PRODUCT_NAME} registry checklist.`,
3316
+ `- ${PRODUCT_NAME} complete package upload is already handled by this command when the first-party registry target is selected.`,
2781
3317
  '- Before uploading to third-party targets, check whether this Skill already has a draft, submitted review, or published listing for the selected target; skip duplicate uploads and verify/update the existing record when possible.',
2782
3318
  '- Check upload readiness and runtime readiness separately before submission.',
2783
3319
  '- Only ask for creator CLI login if a separate store-management command is needed and the registry public token is missing or rejected.',
@@ -2802,8 +3338,8 @@ async function main() {
2802
3338
  result.publicProtocol ? `Wrote ${result.publicProtocol.runtimePath}` : null,
2803
3339
  result.publicProtocol ? `Wrote ${result.publicProtocol.agentAckPath}` : null,
2804
3340
  result.publicProtocol ? `Wrote ${result.publicProtocol.disclosurePath}` : null,
2805
- result.xiashePackageArtifact && !result.xiashePackageArtifact.skipped
2806
- ? `Uploaded XiaShe package ${result.xiashePackageArtifact.artifactPath} (${result.xiashePackageArtifact.publishStatus || 'queued'})`
3341
+ result.firstPartyPackageArtifact && !result.firstPartyPackageArtifact.skipped
3342
+ ? `Uploaded ${PRODUCT_NAME} package ${result.firstPartyPackageArtifact.artifactPath} (${result.firstPartyPackageArtifact.publishStatus || 'queued'})`
2807
3343
  : null,
2808
3344
  `Wrote ${result.disclosurePath}`,
2809
3345
  result.snippetPath ? `Wrote ${result.snippetPath}` : null,