@vetta-org/plugin-cli 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11904,6 +11904,18 @@ var agentExperimentalSettingsUpdateType = Type.Object({
11904
11904
  promptPrediction: Type.Optional(Type.Boolean()),
11905
11905
  agentSkills: Type.Optional(Type.Boolean())
11906
11906
  }, { additionalProperties: false, minProperties: 1 });
11907
+ var imageGenerationSettingsType = Type.Object({
11908
+ textToImageProviderId: Type.Optional(Type.String({ minLength: 1, maxLength: 129 })),
11909
+ textToImageModelId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
11910
+ imageToImageProviderId: Type.Optional(Type.String({ minLength: 1, maxLength: 129 })),
11911
+ imageToImageModelId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 }))
11912
+ }, { additionalProperties: false });
11913
+ var imageGenerationSettingsUpdateType = Type.Object({
11914
+ textToImageProviderId: Type.Optional(Type.Union([Type.String({ minLength: 1, maxLength: 129 }), Type.Null()])),
11915
+ textToImageModelId: Type.Optional(Type.Union([Type.String({ minLength: 1, maxLength: 256 }), Type.Null()])),
11916
+ imageToImageProviderId: Type.Optional(Type.Union([Type.String({ minLength: 1, maxLength: 129 }), Type.Null()])),
11917
+ imageToImageModelId: Type.Optional(Type.Union([Type.String({ minLength: 1, maxLength: 256 }), Type.Null()]))
11918
+ }, { additionalProperties: false, minProperties: 1 });
11907
11919
  var agentSettingsEmptyInputSchema = defineCapabilityInputSchema(agentSettingsEmptyInputType);
11908
11920
  var agentExperimentalSettingsSchema = defineCapabilityOutputSchema(agentExperimentalSettingsType, { clean: true });
11909
11921
  var agentExperimentalSettingsUpdateSchema = defineCapabilityInputSchema(agentExperimentalSettingsUpdateType);
@@ -11923,6 +11935,22 @@ var DOMAIN_AGENT_SETTINGS_CAPABILITIES = {
11923
11935
  version: 1,
11924
11936
  input: agentExperimentalSettingsUpdateSchema,
11925
11937
  output: agentExperimentalSettingsSchema
11938
+ }),
11939
+ GET_IMAGE_GENERATION: defineCapability({
11940
+ id: "cap.domain.vetta.agent-settings.image-generation.get",
11941
+ kind: "query",
11942
+ layer: CAPABILITY_LAYERS.DOMAIN,
11943
+ version: 1,
11944
+ input: agentSettingsEmptyInputSchema,
11945
+ output: defineCapabilityOutputSchema(imageGenerationSettingsType, { clean: true })
11946
+ }),
11947
+ SET_IMAGE_GENERATION: defineCapability({
11948
+ id: "cap.domain.vetta.agent-settings.image-generation.set",
11949
+ kind: "command",
11950
+ layer: CAPABILITY_LAYERS.DOMAIN,
11951
+ version: 1,
11952
+ input: defineCapabilityInputSchema(imageGenerationSettingsUpdateType),
11953
+ output: defineCapabilityOutputSchema(imageGenerationSettingsType, { clean: true })
11926
11954
  })
11927
11955
  };
11928
11956
  var DOMAIN_AGENT_SETTINGS_CAPABILITY_CATALOG = createCapabilityCatalog(Object.values(DOMAIN_AGENT_SETTINGS_CAPABILITIES));
@@ -13028,7 +13056,7 @@ var FOUNDATION_JOB_CAPABILITIES = {
13028
13056
  var FOUNDATION_JOB_CAPABILITY_CATALOG = createCapabilityCatalog(Object.values(FOUNDATION_JOB_CAPABILITIES));
13029
13057
 
13030
13058
  // ../../capability-sdk/dist/domain/media.js
13031
- var MEDIA_PROTOCOL_VERSION = 4;
13059
+ var MEDIA_PROTOCOL_VERSION = 5;
13032
13060
  var MEDIA_OPERATIONS = {
13033
13061
  GENERATE: "generate",
13034
13062
  COMPOSE: "compose",
@@ -13127,6 +13155,16 @@ var mediaGenerationModeCapabilityType = Type.Object({
13127
13155
  aspectRatioPolicy: Type.Optional(Type.Union([Type.Literal("configurable"), Type.Literal("input-derived")])),
13128
13156
  audioGeneration: Type.Optional(Type.Union([Type.Literal("none"), Type.Literal("always"), Type.Literal("optional")]))
13129
13157
  }, { additionalProperties: false });
13158
+ var mediaGenerationModelDescriptorType = Type.Object({
13159
+ id: requiredStringType2,
13160
+ displayName: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),
13161
+ sourceId: Type.Optional(requiredStringType2),
13162
+ sourceDisplayName: Type.Optional(Type.String({ minLength: 1, maxLength: 128 })),
13163
+ modes: Type.Array(mediaGenerationModeType, { minItems: 1 }),
13164
+ aspectRatios: Type.Optional(Type.Array(requiredStringType2)),
13165
+ resolutions: Type.Optional(Type.Array(requiredStringType2)),
13166
+ defaultResolution: Type.Optional(requiredStringType2)
13167
+ }, { additionalProperties: false });
13130
13168
  var mediaGenerateCapabilityType = Type.Object({
13131
13169
  operation: Type.Literal(MEDIA_OPERATIONS.GENERATE),
13132
13170
  kind: mediaGenerationKindType,
@@ -13134,6 +13172,8 @@ var mediaGenerateCapabilityType = Type.Object({
13134
13172
  aspectRatios: Type.Optional(Type.Array(requiredStringType2)),
13135
13173
  resolutions: Type.Optional(Type.Array(requiredStringType2)),
13136
13174
  defaultResolution: Type.Optional(requiredStringType2),
13175
+ models: Type.Optional(Type.Array(mediaGenerationModelDescriptorType, { minItems: 1 })),
13176
+ defaultModelId: Type.Optional(requiredStringType2),
13137
13177
  durationsSeconds: Type.Optional(Type.Array(Type.Number({ exclusiveMinimum: 0 }))),
13138
13178
  modeCapabilities: Type.Optional(Type.Array(mediaGenerationModeCapabilityType))
13139
13179
  }, { additionalProperties: false });
@@ -13317,6 +13357,8 @@ var modelProviderUpsertModelType = Type.Object({
13317
13357
  name: Type.Optional(Type.String()),
13318
13358
  api: Type.Optional(Type.String()),
13319
13359
  reasoning: Type.Optional(Type.Boolean()),
13360
+ reasoningLevels: modelDefinitionDetailType.properties.reasoningLevels,
13361
+ defaultReasoningLevel: modelDefinitionDetailType.properties.defaultReasoningLevel,
13320
13362
  contextWindow: Type.Optional(Type.Number()),
13321
13363
  maxTokens: Type.Optional(Type.Number())
13322
13364
  }, { additionalProperties: false });
@@ -15418,7 +15460,9 @@ var PluginAgentTeamMemberManifestSchema = Type.Object({
15418
15460
  agent: Type.Optional(NonWhitespaceStringSchema),
15419
15461
  role: Type.Optional(PluginAgentRoleSchema),
15420
15462
  responsibility: Type.String({ maxLength: 2000 }),
15421
- optional: Type.Optional(Type.Boolean())
15463
+ optional: Type.Optional(Type.Boolean()),
15464
+ instructions: Type.Optional(Type.String({ maxLength: 64000 })),
15465
+ instructionsPath: Type.Optional(NonWhitespaceStringSchema)
15422
15466
  }, { additionalProperties: false });
15423
15467
  var PluginAgentTeamManifestSchema = Type.Object({
15424
15468
  id: Type.String({ pattern: "^[a-z0-9][a-z0-9-]{0,63}$" }),
@@ -18920,13 +18964,33 @@ async function resolveNpmPluginArchive(packageSpec, pack = runNpmPack) {
18920
18964
  }
18921
18965
 
18922
18966
  // src/agents-template.ts
18923
- var AGENTS_GUIDE_REVISION = 2;
18967
+ var AGENTS_GUIDE_REVISION = 3;
18924
18968
  function readAgentsGuideRevision(content) {
18925
18969
  const match2 = /<!--\s*vetta-guide-revision:\s*(\d+)\s*-->/.exec(content);
18926
18970
  if (!match2)
18927
18971
  return;
18928
18972
  return Number(match2[1]);
18929
18973
  }
18974
+ function renderCommands(pluginId, scripts) {
18975
+ const known = [
18976
+ ["dev", "npm run dev # 开发服务器"],
18977
+ ["build", "npm run build # 产出 dist/"],
18978
+ ["install:vetta", "npm run install:vetta # 打包并装进正在运行的 Vetta(需要 Vetta 已启动)"]
18979
+ ];
18980
+ const lines = known.filter(([name]) => scripts.includes(name)).map(([, line]) => line);
18981
+ if (!scripts.includes("install:vetta")) {
18982
+ lines.push("npx vetta-plugin-cli add . # 打包并装进正在运行的 Vetta");
18983
+ }
18984
+ return [
18985
+ ...lines,
18986
+ "",
18987
+ "npx vetta-plugin-cli watch # 热更新:宿主改从本工程目录加载,改完即生效",
18988
+ `npx vetta-plugin-cli reload ${pluginId} # 装完提示有 pending 版本时用它`,
18989
+ "npx vetta-plugin-cli uninstall # 卸载(省略 id 即本工程对应的插件)",
18990
+ "npx vetta-plugin-cli sync # 在 hub 仓库根上跑:把索引与各能力目录对账"
18991
+ ].join(`
18992
+ `);
18993
+ }
18930
18994
  function renderAgentsGuide(input) {
18931
18995
  return `<!-- vetta-guide-revision: ${AGENTS_GUIDE_REVISION} -->
18932
18996
  # ${input.displayName}
@@ -18966,14 +19030,7 @@ npx vetta-plugin-cli docs --check-latest
18966
19030
  ## 常用命令
18967
19031
 
18968
19032
  \`\`\`bash
18969
- npm run dev # Vite + Module Federation 开发服务器
18970
- npm run build # 产出 dist/
18971
- npm run install:vetta # 打包并装进正在运行的 Vetta(需要 Vetta 已启动)
18972
-
18973
- npx vetta-plugin-cli watch # 热更新:宿主改从本工程目录加载,改完即生效
18974
- npx vetta-plugin-cli reload ${input.pluginId} # 装完提示有 pending 版本时用它
18975
- npx vetta-plugin-cli uninstall # 卸载(省略 id 即本工程对应的插件)
18976
- npx vetta-plugin-cli sync # 在 hub 仓库根上跑:把索引与各能力目录对账
19033
+ ${renderCommands(input.pluginId, input.scripts ?? ["dev", "build", "install:vetta"])}
18977
19034
  \`\`\`
18978
19035
 
18979
19036
  细节都在 \`getting-started.md\`。\`docs\` 打印了 \`Marketplace index:\` 就说明这个目录之上有能力
@@ -19026,16 +19083,25 @@ npx @vetta-org/plugin-cli init --id <slug> --name "<Display Name>" abilities/plu
19026
19083
 
19027
19084
  ## 索引由工具对账,不要手改派生字段
19028
19085
 
19029
- \`.vetta/marketplace.json\` 里能力的 \`version\`、\`config.api_version\`、\`config.permissions\`、
19030
- \`config.commands\` 全都是从能力包推导出来的。改完能力后:
19086
+ 改完能力后:
19031
19087
 
19032
19088
  \`\`\`bash
19033
- npx @vetta-org/plugin-cli sync # 回填派生字段,并推进 marketplaceVersion
19089
+ npx @vetta-org/plugin-cli sync # 对账并回填,看输出
19034
19090
  npx @vetta-org/plugin-cli sync --check # 只报不写,非零退出(CI 用)
19035
19091
  \`\`\`
19036
19092
 
19093
+ \`sync\` 到底做什么,分三档看清楚——它不是万能的:
19094
+
19095
+ | 字段 | \`sync\` 的行为 |
19096
+ | --- | --- |
19097
+ | 条目 \`version\` | **回填**成能力包里的版本 |
19098
+ | \`marketplaceVersion\` | 内容有变化时**尝试**推进。只认 semver(\`1.2.3\`)和纯整数;\`YYYY.MM.DD-NN\` 之类的格式推不动,会报出来要你手改 |
19099
+ | \`config.api_version\` / \`config.permissions\` / \`config.commands\` | **不写**。宿主建目录时用 \`plugin.json\` 整个重算 \`config\`,索引里的副本读都不读。已经存在且与真源不符时 \`sync\` 会提醒你删掉或改对 |
19100
+
19101
+ 所以 \`sync\` 跑完要看输出:它报出来的问题(尤其是推不动的 \`marketplaceVersion\`)没人会替你处理。
19102
+
19037
19103
  要**手写**的只有身份与展示:\`slug\`、\`name\`、\`description\`、\`source.path\`、\`category\`、\`tags\`、
19038
- \`detail\`。新能力上架时手动加一条这样的条目,其余字段交给 \`sync\`。
19104
+ \`detail\`。新能力上架时手动加一条这样的条目。
19039
19105
 
19040
19106
  三条容易踩的约束,\`sync --check\` 会替你守住:
19041
19107
 
@@ -19045,7 +19111,7 @@ npx @vetta-org/plugin-cli sync --check # 只报不写,非零退出(CI 用
19045
19111
  | \`plugin.json\` 的 \`entry\` / \`styles\` 必须在已发布目录里真实存在 | 本地能装,市场上装不了 |
19046
19112
  | 改了任何内容必须换 \`marketplaceVersion\` | 客户端不报错、也不更新,用户永远收不到 |
19047
19113
 
19048
- 第三条最阴险——它不报错。
19114
+ 第三条最阴险——它不报错。\`sync\` 只在版本号是 semver 或整数时才推得动,其余格式要你自己换。
19049
19115
 
19050
19116
  ## 为什么插件目录里要提交 \`dist/\`
19051
19117
 
@@ -19213,26 +19279,80 @@ node_modules/
19213
19279
  }
19214
19280
  return { root, pluginId: input.pluginId, files: Object.keys(files).sort() };
19215
19281
  }
19216
- function refreshAgentsGuide(targetDir) {
19282
+ function refreshAgentsGuide(targetDir, options = {}) {
19217
19283
  const root = resolve2(targetDir);
19284
+ const file = join3(root, "AGENTS.md");
19218
19285
  const manifestPath = join3(root, "plugin.json");
19286
+ const hubManifestPath = join3(root, ".vetta", "marketplace.json");
19287
+ let kind;
19288
+ let content;
19219
19289
  if (existsSync(manifestPath)) {
19220
19290
  const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
19221
19291
  const pluginId = typeof manifest.id === "string" ? manifest.id : undefined;
19222
19292
  if (!pluginId)
19223
19293
  throw new Error(`plugin.json at ${root} has no id`);
19224
- const displayName = typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : pluginId;
19225
- writeFileSync(join3(root, "AGENTS.md"), renderAgentsGuide({ pluginId, displayName }), "utf8");
19226
- return { root, kind: "plugin", file: join3(root, "AGENTS.md") };
19294
+ kind = "plugin";
19295
+ content = renderAgentsGuide({
19296
+ pluginId,
19297
+ displayName: resolveManifestText(manifest.name, root, pluginId),
19298
+ scripts: readPackageScripts(root)
19299
+ });
19300
+ } else if (existsSync(hubManifestPath)) {
19301
+ const manifest = JSON.parse(readFileSync(hubManifestPath, "utf8"));
19302
+ kind = "hub";
19303
+ content = renderHubAgentsGuide({
19304
+ name: typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : "marketplace"
19305
+ });
19306
+ } else {
19307
+ throw new Error(`Not a plugin project or marketplace repository: ${root}`);
19308
+ }
19309
+ if (!options.dryRun)
19310
+ assertSafeToOverwrite(file, options.force === true);
19311
+ if (options.dryRun)
19312
+ return { root, kind, file, content, written: false };
19313
+ writeFileSync(file, content, "utf8");
19314
+ return { root, kind, file, content, written: true };
19315
+ }
19316
+ function assertSafeToOverwrite(file, force) {
19317
+ if (force || !existsSync(file))
19318
+ return;
19319
+ if (readAgentsGuideRevision(readFileSync(file, "utf8")) !== undefined)
19320
+ return;
19321
+ throw new Error(`${file} has no vetta-guide-revision marker, so it looks hand-written rather than scaffolded. ` + "Refusing to overwrite it. Review the new template with `--dry-run`, merge what you want by hand, " + "or pass `--force` to replace the file.");
19322
+ }
19323
+ function resolveManifestText(raw2, root, fallback) {
19324
+ if (typeof raw2 !== "string" || raw2.length === 0)
19325
+ return fallback;
19326
+ const match2 = /^%([^%]+)%$/.exec(raw2);
19327
+ if (!match2)
19328
+ return raw2;
19329
+ const key = match2[1];
19330
+ const pluginManifest = readJsonFile(join3(root, "plugin.json"));
19331
+ const locale = typeof pluginManifest?.defaultLocale === "string" ? pluginManifest.defaultLocale : "zh";
19332
+ for (const candidate of [locale, "zh", "en"]) {
19333
+ const table = readJsonFile(join3(root, "locales", `${candidate}.json`));
19334
+ const value = table?.[key];
19335
+ if (typeof value === "string" && value.length > 0)
19336
+ return value;
19227
19337
  }
19228
- const hubManifest = join3(root, ".vetta", "marketplace.json");
19229
- if (existsSync(hubManifest)) {
19230
- const manifest = JSON.parse(readFileSync(hubManifest, "utf8"));
19231
- const name = typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : "marketplace";
19232
- writeFileSync(join3(root, "AGENTS.md"), renderHubAgentsGuide({ name }), "utf8");
19233
- return { root, kind: "hub", file: join3(root, "AGENTS.md") };
19338
+ return fallback;
19339
+ }
19340
+ function readPackageScripts(root) {
19341
+ const pkg = readJsonFile(join3(root, "package.json"));
19342
+ const scripts = pkg?.scripts;
19343
+ if (typeof scripts !== "object" || scripts === null || Array.isArray(scripts))
19344
+ return [];
19345
+ return Object.keys(scripts);
19346
+ }
19347
+ function readJsonFile(path) {
19348
+ if (!existsSync(path))
19349
+ return;
19350
+ try {
19351
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
19352
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
19353
+ } catch {
19354
+ return;
19234
19355
  }
19235
- throw new Error(`Not a plugin project or marketplace repository: ${root}`);
19236
19356
  }
19237
19357
  var HUB_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
19238
19358
  var APP_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
@@ -19292,7 +19412,7 @@ function detectJsonIndent(source) {
19292
19412
  const match2 = /\n([ \t]+)\S/.exec(source);
19293
19413
  return match2?.[1] ?? "\t";
19294
19414
  }
19295
- function readJsonFile(path) {
19415
+ function readJsonFile2(path) {
19296
19416
  try {
19297
19417
  const parsed = JSON.parse(readFileSync2(path, "utf8"));
19298
19418
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
@@ -19336,7 +19456,7 @@ function nextMarketplaceVersion(current) {
19336
19456
  return;
19337
19457
  }
19338
19458
  function syncMarketplaceIndex(input) {
19339
- const manifest = readJsonFile(input.manifestPath);
19459
+ const manifest = readJsonFile2(input.manifestPath);
19340
19460
  if (!manifest)
19341
19461
  throw new Error(`Malformed marketplace manifest: ${input.manifestPath}`);
19342
19462
  const abilities = Array.isArray(manifest.abilities) ? manifest.abilities : [];
@@ -19354,13 +19474,31 @@ function syncMarketplaceIndex(input) {
19354
19474
  for (const member of Array.isArray(bundleConfig?.members) ? bundleConfig.members : []) {
19355
19475
  if (typeof member !== "object" || member === null || Array.isArray(member))
19356
19476
  continue;
19357
- const memberSource = member.source;
19477
+ const memberEntry = member;
19478
+ const memberSource = memberEntry.source;
19358
19479
  const memberPath = typeof memberSource === "object" && memberSource !== null && !Array.isArray(memberSource) ? memberSource.path : undefined;
19359
19480
  if (typeof memberPath !== "string")
19360
19481
  continue;
19361
19482
  const dir = resolveAbilityDir(input.hubRoot, memberPath);
19362
- if (dir)
19483
+ if (dir) {
19363
19484
  listedDirs.add(dir);
19485
+ if (manifest.schemaVersion === 3 && memberEntry.type === "plugin") {
19486
+ const descriptor2 = readJsonFile2(join4(dir, "ability.json"));
19487
+ const memberChanges = [];
19488
+ reconcilePlugin({
19489
+ entry: { ...memberEntry, version: descriptor2?.version },
19490
+ slug: typeof memberEntry.slug === "string" ? memberEntry.slug : "(unnamed)",
19491
+ abilityDir: dir,
19492
+ schemaVersion: manifest.schemaVersion,
19493
+ minAppVersion: manifest.minAppVersion,
19494
+ changes: memberChanges,
19495
+ problems
19496
+ });
19497
+ for (const change of memberChanges) {
19498
+ problems.push({ slug: change.slug, message: `ability.json version does not match latest release: ${change.to}` });
19499
+ }
19500
+ }
19501
+ }
19364
19502
  }
19365
19503
  continue;
19366
19504
  }
@@ -19377,7 +19515,7 @@ function syncMarketplaceIndex(input) {
19377
19515
  }
19378
19516
  listedDirs.add(abilityDir);
19379
19517
  if (type === "plugin")
19380
- reconcilePlugin({ entry, slug, abilityDir, changes, problems });
19518
+ reconcilePlugin({ entry, slug, abilityDir, schemaVersion: manifest.schemaVersion, minAppVersion: manifest.minAppVersion, changes, problems });
19381
19519
  else if (type === "mcp")
19382
19520
  reconcileIdentityFile({ entry, slug, abilityDir, fileName: "mcp.json", changes, problems });
19383
19521
  }
@@ -19404,8 +19542,78 @@ function syncMarketplaceIndex(input) {
19404
19542
  return { manifestPath: input.manifestPath, changes, problems, unlisted, written };
19405
19543
  }
19406
19544
  function reconcilePlugin(context) {
19407
- const { entry, slug, abilityDir, changes, problems } = context;
19408
- const manifest = readJsonFile(join4(abilityDir, "plugin.json"));
19545
+ const { entry, slug, abilityDir, schemaVersion, minAppVersion, changes, problems } = context;
19546
+ if (schemaVersion === 3 && !("releases" in entry)) {
19547
+ problems.push({ slug, message: "schemaVersion 3 plugin requires versioned releases" });
19548
+ return;
19549
+ }
19550
+ if ("releases" in entry) {
19551
+ if (schemaVersion !== 3) {
19552
+ problems.push({ slug, message: "versioned plugin releases require marketplace schemaVersion 3" });
19553
+ return;
19554
+ }
19555
+ const releases = entry.releases;
19556
+ if (!Array.isArray(releases) || releases.length === 0) {
19557
+ problems.push({ slug, message: "plugin releases must be a nonempty array" });
19558
+ return;
19559
+ }
19560
+ let latest;
19561
+ const seen = new Set;
19562
+ for (const raw2 of releases) {
19563
+ if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2)) {
19564
+ problems.push({ slug, message: "plugin release must be an object" });
19565
+ continue;
19566
+ }
19567
+ const release = raw2;
19568
+ const match2 = typeof release.version === "string" ? /^(\d+)\.(\d+)\.(\d+)$/.exec(release.version) : null;
19569
+ if (!match2 || seen.has(release.version)) {
19570
+ problems.push({ slug, message: `invalid or duplicate plugin release version: ${String(release.version)}` });
19571
+ continue;
19572
+ }
19573
+ seen.add(release.version);
19574
+ const minimum = typeof release.minAppVersion === "string" ? /^(\d+)\.(\d+)\.(\d+)$/.exec(release.minAppVersion) : null;
19575
+ if (!minimum || typeof minAppVersion !== "string" || !/^(\d+)\.(\d+)\.(\d+)$/.test(minAppVersion)) {
19576
+ problems.push({ slug, message: `release ${release.version} has an invalid minAppVersion` });
19577
+ } else {
19578
+ const marketMinimum = /^(\d+)\.(\d+)\.(\d+)$/.exec(minAppVersion);
19579
+ if (marketMinimum && compareVersionParts(minimum.slice(1).map(Number), marketMinimum.slice(1).map(Number)) < 0) {
19580
+ problems.push({ slug, message: `release ${release.version} requires an app older than the marketplace` });
19581
+ }
19582
+ }
19583
+ if (typeof release.pluginApiVersion !== "string" || !/^\^\d+\.\d+\.\d+$/.test(release.pluginApiVersion)) {
19584
+ problems.push({ slug, message: `release ${release.version} has an invalid pluginApiVersion` });
19585
+ }
19586
+ for (const field of ["permissions", "commands"]) {
19587
+ if (release[field] !== undefined && !stringArray(release[field])) {
19588
+ problems.push({ slug, message: `release ${release.version} has invalid ${field}` });
19589
+ }
19590
+ }
19591
+ const parts = [Number(match2[1]), Number(match2[2]), Number(match2[3])];
19592
+ if (!latest || compareVersionParts(parts, latest.parts) > 0) {
19593
+ latest = { version: release.version, parts };
19594
+ }
19595
+ const artifact = release.artifact;
19596
+ if (typeof artifact !== "object" || artifact === null || Array.isArray(artifact)) {
19597
+ problems.push({ slug, message: `release ${release.version} has no artifact` });
19598
+ continue;
19599
+ }
19600
+ const { url, sha256 } = artifact;
19601
+ let validUrl = false;
19602
+ try {
19603
+ const parsed = new URL(String(url));
19604
+ validUrl = parsed.protocol === "https:" && !parsed.username && !parsed.password && !parsed.hash;
19605
+ } catch {}
19606
+ if (!validUrl || typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256)) {
19607
+ problems.push({ slug, message: `release ${release.version} has an invalid HTTPS artifact or SHA-256` });
19608
+ }
19609
+ }
19610
+ if (latest && entry.version !== latest.version) {
19611
+ changes.push({ slug, field: "version", from: entry.version, to: latest.version });
19612
+ entry.version = latest.version;
19613
+ }
19614
+ return;
19615
+ }
19616
+ const manifest = readJsonFile2(join4(abilityDir, "plugin.json"));
19409
19617
  if (!manifest) {
19410
19618
  problems.push({ slug, message: "plugin.json is missing or malformed" });
19411
19619
  return;
@@ -19444,9 +19652,16 @@ function reconcilePlugin(context) {
19444
19652
  }
19445
19653
  }
19446
19654
  }
19655
+ function compareVersionParts(left, right) {
19656
+ for (let index = 0;index < 3; index += 1) {
19657
+ if (left[index] !== right[index])
19658
+ return (left[index] ?? 0) - (right[index] ?? 0);
19659
+ }
19660
+ return 0;
19661
+ }
19447
19662
  function reconcileIdentityFile(context) {
19448
19663
  const { entry, slug, abilityDir, fileName, changes, problems } = context;
19449
- const identity = readJsonFile(join4(abilityDir, fileName));
19664
+ const identity = readJsonFile2(join4(abilityDir, fileName));
19450
19665
  if (!identity) {
19451
19666
  problems.push({ slug, message: `${fileName} is missing or malformed` });
19452
19667
  return;
@@ -19489,7 +19704,7 @@ function findAbilityDirectories(hubRoot) {
19489
19704
  return found.sort();
19490
19705
  }
19491
19706
  function describeIndexDrift(input) {
19492
- const manifest = readJsonFile(input.manifestPath);
19707
+ const manifest = readJsonFile2(input.manifestPath);
19493
19708
  if (!manifest || !Array.isArray(manifest.abilities))
19494
19709
  return;
19495
19710
  const entry = manifest.abilities.find((item) => typeof item === "object" && item !== null && !Array.isArray(item) && item.slug === input.slug);
@@ -19575,11 +19790,11 @@ function readManualSdkVersion(manualDir) {
19575
19790
  var HELP_TEXT = `Vetta plugin manager
19576
19791
 
19577
19792
  Usage:
19578
- vetta-plugin-cli add <npm-package|zip-path|http-url> [--json]
19793
+ vetta-plugin-cli add <npm-package|package-path|http-url> [--json]
19579
19794
  vetta-plugin-cli reload <plugin-id> [--json]
19580
19795
  vetta-plugin-cli docs [--check-latest] [--json]
19581
19796
  vetta-plugin-cli init --id <plugin-id> [--name <display>] [dir] [--json]
19582
- vetta-plugin-cli init --refresh-guide [dir] [--json]
19797
+ vetta-plugin-cli init --refresh-guide [dir] [--dry-run] [--force] [--json]
19583
19798
  vetta-plugin-cli init hub --name <slug> --repository <url> --min-app-version <x.y.z> [dir]
19584
19799
  vetta-plugin-cli watch [dir] [--stop] [--json]
19585
19800
  vetta-plugin-cli uninstall [plugin-id] [--json]
@@ -19589,7 +19804,7 @@ Examples:
19589
19804
  npx @vetta-org/plugin-cli add @example/vetta-plugin-demo
19590
19805
  npx @vetta-org/plugin-cli add @example/vetta-plugin-demo@1.2.0
19591
19806
  npx @vetta-org/plugin-cli add . # 当前插件工程(先 pack)
19592
- npx @vetta-org/plugin-cli add ./release/demo-1.2.0.zip
19807
+ npx @vetta-org/plugin-cli add ./release/demo-1.2.0.vettapkg
19593
19808
  npx @vetta-org/plugin-cli reload demo
19594
19809
  npx @vetta-org/plugin-cli docs
19595
19810
  npx @vetta-org/plugin-cli init --id my-plugin --name "My Plugin"
@@ -19615,7 +19830,7 @@ function parsePluginAddCommand(argv) {
19615
19830
  }
19616
19831
  const [source, unexpected] = parsed.positionals;
19617
19832
  if (!source)
19618
- return { type: "error", message: "Missing <npm-package|zip-path|http-url>" };
19833
+ return { type: "error", message: "Missing <npm-package|package-path|http-url>" };
19619
19834
  if (unexpected)
19620
19835
  return { type: "error", message: `Unexpected argument: ${unexpected}` };
19621
19836
  return { type: "add", source, json: parsed.values.json === true };
@@ -19680,7 +19895,9 @@ function parsePluginInitCommand(argv) {
19680
19895
  id: { type: "string" },
19681
19896
  name: { type: "string" },
19682
19897
  json: { type: "boolean" },
19683
- "refresh-guide": { type: "boolean" }
19898
+ "refresh-guide": { type: "boolean" },
19899
+ force: { type: "boolean" },
19900
+ "dry-run": { type: "boolean" }
19684
19901
  }
19685
19902
  });
19686
19903
  } catch (error) {
@@ -19690,7 +19907,13 @@ function parsePluginInitCommand(argv) {
19690
19907
  const [dir, extra] = parsed.positionals;
19691
19908
  if (extra)
19692
19909
  return { type: "error", message: `Unexpected argument: ${extra}` };
19693
- return { type: "refresh-guide", ...dir ? { targetDir: dir } : {}, json: parsed.values.json === true };
19910
+ return {
19911
+ type: "refresh-guide",
19912
+ ...dir ? { targetDir: dir } : {},
19913
+ json: parsed.values.json === true,
19914
+ force: parsed.values.force === true,
19915
+ dryRun: parsed.values["dry-run"] === true
19916
+ };
19694
19917
  }
19695
19918
  const pluginId = parsed.values.id;
19696
19919
  if (typeof pluginId !== "string" || pluginId.length === 0) {
@@ -19838,8 +20061,9 @@ function isHttpUrl(source) {
19838
20061
  return false;
19839
20062
  }
19840
20063
  }
19841
- function isLocalZip(source) {
19842
- if (source.toLowerCase().endsWith(".zip"))
20064
+ function isLocalPackage(source) {
20065
+ const lower = source.toLowerCase();
20066
+ if (lower.endsWith(".vettapkg") || lower.endsWith(".zip"))
19843
20067
  return true;
19844
20068
  const path = resolve5(source);
19845
20069
  return existsSync4(path) && !statSync2(path).isDirectory();
@@ -19858,7 +20082,7 @@ function resolveProjectArchive(source) {
19858
20082
  }
19859
20083
  throw new Error(`No plugin.json found in ${from} or any parent directory.`);
19860
20084
  }
19861
- const archivePath = join6(project.root, "release", `${project.pluginId}-${project.version}.zip`);
20085
+ const archivePath = join6(project.root, "release", `${project.pluginId}-${project.version}.vettapkg`);
19862
20086
  if (!existsSync4(archivePath)) {
19863
20087
  throw new Error(`Packaged archive not found: ${archivePath}
19864
20088
  Build it first: npm run build && npx vetta-plugin pack`);
@@ -19879,6 +20103,7 @@ function indexDriftHint(project) {
19879
20103
  function npmInstallInput(resolved) {
19880
20104
  return {
19881
20105
  operation: "install-from-path",
20106
+ initiator: "plugin-cli",
19882
20107
  path: resolved.archivePath,
19883
20108
  enable: true,
19884
20109
  source: "npm",
@@ -19972,19 +20197,22 @@ async function runPluginCommand(command, dependencies = defaultDependencies) {
19972
20197
  } else if (isHttpUrl(command.source)) {
19973
20198
  result = await dependencies.runAction("plugins.manage", {
19974
20199
  operation: "install-from-url",
20200
+ initiator: "plugin-cli",
19975
20201
  url: command.source
19976
20202
  });
19977
20203
  } else if (isDirectorySource(command.source)) {
19978
20204
  const { archivePath, project } = resolveProjectArchive(command.source);
19979
20205
  result = await dependencies.runAction("plugins.manage", {
19980
20206
  operation: "install-from-path",
20207
+ initiator: "plugin-cli",
19981
20208
  path: archivePath,
19982
20209
  enable: true
19983
20210
  });
19984
20211
  driftHint = indexDriftHint(project);
19985
- } else if (isLocalZip(command.source)) {
20212
+ } else if (isLocalPackage(command.source)) {
19986
20213
  result = await dependencies.runAction("plugins.manage", {
19987
20214
  operation: "install-from-path",
20215
+ initiator: "plugin-cli",
19988
20216
  path: resolve5(command.source),
19989
20217
  enable: true
19990
20218
  });
@@ -20068,7 +20296,9 @@ async function runDocsCommand(command, dependencies) {
20068
20296
  lines.push(`Manual follows the installed SDK. To refresh it: ${SDK_REFRESH_COMMAND}`);
20069
20297
  }
20070
20298
  if (guide.stale) {
20071
- lines.push(`This brief is stale (AGENTS.md revision ${guide.revision ?? "unstamped"} < ${AGENTS_GUIDE_REVISION}). Refresh it with: ${GUIDE_REFRESH_COMMAND}`);
20299
+ lines.push(`This brief is stale (AGENTS.md revision ${guide.revision} < ${AGENTS_GUIDE_REVISION}). Refresh it with: ${GUIDE_REFRESH_COMMAND}`);
20300
+ } else if (guide.unstamped) {
20301
+ lines.push(`AGENTS.md has no revision marker, so it looks hand-written. Review the current template with \`${GUIDE_REFRESH_COMMAND} --dry-run\` and merge by hand; do not overwrite it blindly.`);
20072
20302
  }
20073
20303
  if (hub) {
20074
20304
  lines.push(`Marketplace index: ${hub.manifestPath}`);
@@ -20084,17 +20314,18 @@ var GUIDE_REFRESH_COMMAND = "npx @vetta-org/plugin-cli init --refresh-guide";
20084
20314
  function inspectAgentsGuide(root) {
20085
20315
  const path = join6(root, "AGENTS.md");
20086
20316
  if (!existsSync4(path))
20087
- return { present: false, stale: false };
20317
+ return { present: false, stale: false, unstamped: false };
20088
20318
  let revision;
20089
20319
  try {
20090
20320
  revision = readAgentsGuideRevision(readFileSync4(path, "utf8"));
20091
20321
  } catch {
20092
- return { present: true, stale: false };
20322
+ return { present: true, stale: false, unstamped: false };
20093
20323
  }
20094
20324
  return {
20095
20325
  present: true,
20096
20326
  ...revision === undefined ? {} : { revision },
20097
- stale: revision === undefined || revision < AGENTS_GUIDE_REVISION
20327
+ stale: revision !== undefined && revision < AGENTS_GUIDE_REVISION,
20328
+ unstamped: revision === undefined
20098
20329
  };
20099
20330
  }
20100
20331
  function compareSemver(left, right) {
@@ -20117,11 +20348,18 @@ function compareSemver(left, right) {
20117
20348
  function runRefreshGuideCommand(command, dependencies) {
20118
20349
  const cwd = dependencies.cwd?.() ?? process.cwd();
20119
20350
  try {
20120
- const result = refreshAgentsGuide(resolve5(cwd, command.targetDir ?? "."));
20121
- dependencies.writeStdout(command.json ? `${JSON.stringify({ ok: true, ...result })}
20122
- ` : `Rewrote ${result.file}
20123
- Next: npx vetta-plugin-cli docs --check-latest
20351
+ const result = refreshAgentsGuide(resolve5(cwd, command.targetDir ?? "."), {
20352
+ force: command.force,
20353
+ dryRun: command.dryRun
20354
+ });
20355
+ if (command.json) {
20356
+ dependencies.writeStdout(`${JSON.stringify({ ok: true, ...result })}
20124
20357
  `);
20358
+ return 0;
20359
+ }
20360
+ dependencies.writeStdout(result.written ? `Rewrote ${result.file}
20361
+ Next: npx vetta-plugin-cli docs --check-latest
20362
+ ` : `${result.content}`);
20125
20363
  return 0;
20126
20364
  } catch (error) {
20127
20365
  const message = error instanceof Error ? error.message : String(error);
package/dist/init.d.ts CHANGED
@@ -14,10 +14,19 @@ export interface InitPluginResult {
14
14
  readonly files: readonly string[];
15
15
  }
16
16
  export declare function initPluginProject(input: InitPluginInput): InitPluginResult;
17
+ export interface RefreshGuideOptions {
18
+ /** 覆盖一份没有版本戳的 `AGENTS.md`。缺省拒绝——没有戳的多半是手写的。 */
19
+ readonly force?: boolean;
20
+ /** 只算出内容,不落盘。 */
21
+ readonly dryRun?: boolean;
22
+ }
17
23
  export interface RefreshGuideResult {
18
24
  readonly root: string;
19
25
  readonly kind: "plugin" | "hub";
20
26
  readonly file: string;
27
+ /** 这次生成的说明书正文。`dryRun` 时用它做人工合并。 */
28
+ readonly content: string;
29
+ readonly written: boolean;
21
30
  }
22
31
  /**
23
32
  * 在已有的工程或能力市场仓库里重写 `AGENTS.md`。
@@ -25,8 +34,12 @@ export interface RefreshGuideResult {
25
34
  * `init` 拒绝覆盖已有工程,所以老目录里那份说明书从落地起就再也没变过——它写于某个版本的
26
35
  * SDK,之后新增的约定一条都没有。这里只重写这一个文件:它是脚手架里唯一「纯派生、没有用户
27
36
  * 内容」的产物,其余文件都可能被改过,不该被一次刷新抹掉。
37
+ *
38
+ * **只有确实由脚手架生成的那份才算纯派生。** 没有版本戳的文件无法与手写内容区分——能力市场
39
+ * 仓库的根 `AGENTS.md` 往往是一整本手写的市场规范——所以一律拒绝覆盖,让调用方拿 `dryRun`
40
+ * 的内容去人工合并,或显式 `force`。宁可少刷新一份,也不能悄悄删掉别人写的东西。
28
41
  */
29
- export declare function refreshAgentsGuide(targetDir: string): RefreshGuideResult;
42
+ export declare function refreshAgentsGuide(targetDir: string, options?: RefreshGuideOptions): RefreshGuideResult;
30
43
  export interface InitHubInput {
31
44
  readonly targetDir: string;
32
45
  /** 市场名(slug)。 */