@dsh-workbench/core 1.1.7 → 1.1.8

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/client.js CHANGED
@@ -3382,7 +3382,7 @@ window.__ModuleLoader__.load({
3382
3382
  "panels.discover.missingIndex": "This repository has no {path} yet.",
3383
3383
  "panels.discover.initTitle": "Initialize this repository as a module market index?",
3384
3384
  "panels.discover.initCta": "Initialize market index",
3385
- "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/<version>/ and their entry under market/<package-name>/<version>.json. The repository stays owned by its owner.",
3385
+ "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/ and their entry in market/<package-name>.json. The repository stays owned by its owner.",
3386
3386
  "panels.discover.initBranch": "Branch",
3387
3387
  "panels.discover.initBranchHint": "The index and build files go to this branch; an empty repository gets it created.",
3388
3388
  "panels.discover.initBranchBad": "Use only letters, digits, dots, underscores, slashes, or dashes in a branch name.",
@@ -3713,6 +3713,8 @@ window.__ModuleLoader__.load({
3713
3713
  "approval.publish.version": "Version",
3714
3714
  "approval.publish.author": "Author",
3715
3715
  "approval.publish.authorMissing": "unsigned",
3716
+ "approval.publish.labels": "Labels",
3717
+ "approval.publish.labelsMissing": "none yet (1–2 picked on publish)",
3716
3718
  "approval.publish.owner": "Owner",
3717
3719
  "approval.publish.ownerUnowned": "originally unattributed",
3718
3720
  "approval.publish.ownerTaken": "Owned by {owner}",
@@ -4128,7 +4130,7 @@ window.__ModuleLoader__.load({
4128
4130
  "panels.discover.missingIndex": "仓库里还没有索引文件 {path}",
4129
4131
  "panels.discover.initTitle": "把这个仓库初始化成模块市场索引?",
4130
4132
  "panels.discover.initCta": "初始化成市场索引",
4131
- "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/<版本>/,条目写到 market/<包名>/<版本>.json。仓库的写权限归它的所有者。",
4133
+ "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/,条目写到 market/<包名>.json。仓库的写权限归它的所有者。",
4132
4134
  "panels.discover.initBranch": "分支",
4133
4135
  "panels.discover.initBranchHint": "索引和打包文件都发到这个分支;空仓库会新建它。",
4134
4136
  "panels.discover.initBranchBad": "分支名只能用字母、数字、点、下划线、斜杠或短横线",
@@ -4459,6 +4461,8 @@ window.__ModuleLoader__.load({
4459
4461
  "approval.publish.version": "版本",
4460
4462
  "approval.publish.author": "作者",
4461
4463
  "approval.publish.authorMissing": "未署名",
4464
+ "approval.publish.labels": "标签",
4465
+ "approval.publish.labelsMissing": "还没有(发布到市场时补 1–2 个)",
4462
4466
  "approval.publish.owner": "认领人",
4463
4467
  "approval.publish.ownerUnowned": "原来没署名",
4464
4468
  "approval.publish.ownerTaken": "已有主人:{owner}",
@@ -13247,6 +13251,11 @@ window.__ModuleLoader__.load({
13247
13251
  const value = args[key];
13248
13252
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
13249
13253
  }
13254
+ function labelsOf(args) {
13255
+ const value = args["labels"];
13256
+ if (!Array.isArray(value)) return [];
13257
+ return [...new Set(value.flatMap((one) => typeof one === "string" && one.trim() !== "" ? [one.trim()] : []))];
13258
+ }
13250
13259
  function recordApprovalRows(args, t) {
13251
13260
  const unit = textOf(args, "unit");
13252
13261
  const doing = textOf(args, "doing");
@@ -13318,6 +13327,14 @@ window.__ModuleLoader__.load({
13318
13327
  value: author !== "" ? author : preview === void 0 ? t("approval.publish.reading") : preview?.author === void 0 || preview.author === "" ? t("approval.publish.authorMissing") : preview.author
13319
13328
  }
13320
13329
  ];
13330
+ if (target === "market" || target === "" && preview?.market !== void 0) {
13331
+ const asked = labelsOf(args);
13332
+ const labels = asked.length > 0 ? asked : preview?.labels ?? [];
13333
+ rows.push({
13334
+ label: t("approval.publish.labels"),
13335
+ value: preview === void 0 && asked.length === 0 ? t("approval.publish.reading") : labels.length === 0 ? t("approval.publish.labelsMissing") : labels.join(" · ")
13336
+ });
13337
+ }
13321
13338
  const claim = preview?.claim;
13322
13339
  if (claim !== void 0 && claim.state !== "new" && claim.state !== "unknown") {
13323
13340
  if (claim.state === "unit-taken") rows.push({
@@ -3375,7 +3375,7 @@ var en = {
3375
3375
  "panels.discover.missingIndex": "This repository has no {path} yet.",
3376
3376
  "panels.discover.initTitle": "Initialize this repository as a module market index?",
3377
3377
  "panels.discover.initCta": "Initialize market index",
3378
- "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/<version>/ and their entry under market/<package-name>/<version>.json. The repository stays owned by its owner.",
3378
+ "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/ and their entry in market/<package-name>.json. The repository stays owned by its owner.",
3379
3379
  "panels.discover.initBranch": "Branch",
3380
3380
  "panels.discover.initBranchHint": "The index and build files go to this branch; an empty repository gets it created.",
3381
3381
  "panels.discover.initBranchBad": "Use only letters, digits, dots, underscores, slashes, or dashes in a branch name.",
@@ -3706,6 +3706,8 @@ var en = {
3706
3706
  "approval.publish.version": "Version",
3707
3707
  "approval.publish.author": "Author",
3708
3708
  "approval.publish.authorMissing": "unsigned",
3709
+ "approval.publish.labels": "Labels",
3710
+ "approval.publish.labelsMissing": "none yet (1–2 picked on publish)",
3709
3711
  "approval.publish.owner": "Owner",
3710
3712
  "approval.publish.ownerUnowned": "originally unattributed",
3711
3713
  "approval.publish.ownerTaken": "Owned by {owner}",
@@ -4121,7 +4123,7 @@ var dictionaries = {
4121
4123
  "panels.discover.missingIndex": "仓库里还没有索引文件 {path}",
4122
4124
  "panels.discover.initTitle": "把这个仓库初始化成模块市场索引?",
4123
4125
  "panels.discover.initCta": "初始化成市场索引",
4124
- "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/<版本>/,条目写到 market/<包名>/<版本>.json。仓库的写权限归它的所有者。",
4126
+ "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/,条目写到 market/<包名>.json。仓库的写权限归它的所有者。",
4125
4127
  "panels.discover.initBranch": "分支",
4126
4128
  "panels.discover.initBranchHint": "索引和打包文件都发到这个分支;空仓库会新建它。",
4127
4129
  "panels.discover.initBranchBad": "分支名只能用字母、数字、点、下划线、斜杠或短横线",
@@ -4452,6 +4454,8 @@ var dictionaries = {
4452
4454
  "approval.publish.version": "版本",
4453
4455
  "approval.publish.author": "作者",
4454
4456
  "approval.publish.authorMissing": "未署名",
4457
+ "approval.publish.labels": "标签",
4458
+ "approval.publish.labelsMissing": "还没有(发布到市场时补 1–2 个)",
4455
4459
  "approval.publish.owner": "认领人",
4456
4460
  "approval.publish.ownerUnowned": "原来没署名",
4457
4461
  "approval.publish.ownerTaken": "已有主人:{owner}",
@@ -13240,6 +13244,11 @@ function textOf(args, key) {
13240
13244
  const value = args[key];
13241
13245
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
13242
13246
  }
13247
+ function labelsOf(args) {
13248
+ const value = args["labels"];
13249
+ if (!Array.isArray(value)) return [];
13250
+ return [...new Set(value.flatMap((one) => typeof one === "string" && one.trim() !== "" ? [one.trim()] : []))];
13251
+ }
13243
13252
  function recordApprovalRows(args, t) {
13244
13253
  const unit = textOf(args, "unit");
13245
13254
  const doing = textOf(args, "doing");
@@ -13311,6 +13320,14 @@ function publishRows(args, preview, t) {
13311
13320
  value: author !== "" ? author : preview === void 0 ? t("approval.publish.reading") : preview?.author === void 0 || preview.author === "" ? t("approval.publish.authorMissing") : preview.author
13312
13321
  }
13313
13322
  ];
13323
+ if (target === "market" || target === "" && preview?.market !== void 0) {
13324
+ const asked = labelsOf(args);
13325
+ const labels = asked.length > 0 ? asked : preview?.labels ?? [];
13326
+ rows.push({
13327
+ label: t("approval.publish.labels"),
13328
+ value: preview === void 0 && asked.length === 0 ? t("approval.publish.reading") : labels.length === 0 ? t("approval.publish.labelsMissing") : labels.join(" · ")
13329
+ });
13330
+ }
13314
13331
  const claim = preview?.claim;
13315
13332
  if (claim !== void 0 && claim.state !== "new" && claim.state !== "unknown") {
13316
13333
  if (claim.state === "unit-taken") rows.push({
@@ -3378,7 +3378,7 @@ var en = {
3378
3378
  "panels.discover.missingIndex": "This repository has no {path} yet.",
3379
3379
  "panels.discover.initTitle": "Initialize this repository as a module market index?",
3380
3380
  "panels.discover.initCta": "Initialize market index",
3381
- "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/<version>/ and their entry under market/<package-name>/<version>.json. The repository stays owned by its owner.",
3381
+ "panels.discover.initWhat": "It will write the index directory market/ and README.md on {branch}, commit once, and push. Modules you publish from this workbench will commit their build files under artifacts/<package-name>/ and their entry in market/<package-name>.json. The repository stays owned by its owner.",
3382
3382
  "panels.discover.initBranch": "Branch",
3383
3383
  "panels.discover.initBranchHint": "The index and build files go to this branch; an empty repository gets it created.",
3384
3384
  "panels.discover.initBranchBad": "Use only letters, digits, dots, underscores, slashes, or dashes in a branch name.",
@@ -3709,6 +3709,8 @@ var en = {
3709
3709
  "approval.publish.version": "Version",
3710
3710
  "approval.publish.author": "Author",
3711
3711
  "approval.publish.authorMissing": "unsigned",
3712
+ "approval.publish.labels": "Labels",
3713
+ "approval.publish.labelsMissing": "none yet (1–2 picked on publish)",
3712
3714
  "approval.publish.owner": "Owner",
3713
3715
  "approval.publish.ownerUnowned": "originally unattributed",
3714
3716
  "approval.publish.ownerTaken": "Owned by {owner}",
@@ -4124,7 +4126,7 @@ var dictionaries = {
4124
4126
  "panels.discover.missingIndex": "仓库里还没有索引文件 {path}",
4125
4127
  "panels.discover.initTitle": "把这个仓库初始化成模块市场索引?",
4126
4128
  "panels.discover.initCta": "初始化成市场索引",
4127
- "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/<版本>/,条目写到 market/<包名>/<版本>.json。仓库的写权限归它的所有者。",
4129
+ "panels.discover.initWhat": "会在 {branch} 分支写索引目录 market/ 与 README.md,提交一次并推上去;之后从这个工作台发布的模块会把打包好的文件提交到 artifacts/<包名>/,条目写到 market/<包名>.json。仓库的写权限归它的所有者。",
4128
4130
  "panels.discover.initBranch": "分支",
4129
4131
  "panels.discover.initBranchHint": "索引和打包文件都发到这个分支;空仓库会新建它。",
4130
4132
  "panels.discover.initBranchBad": "分支名只能用字母、数字、点、下划线、斜杠或短横线",
@@ -4455,6 +4457,8 @@ var dictionaries = {
4455
4457
  "approval.publish.version": "版本",
4456
4458
  "approval.publish.author": "作者",
4457
4459
  "approval.publish.authorMissing": "未署名",
4460
+ "approval.publish.labels": "标签",
4461
+ "approval.publish.labelsMissing": "还没有(发布到市场时补 1–2 个)",
4458
4462
  "approval.publish.owner": "认领人",
4459
4463
  "approval.publish.ownerUnowned": "原来没署名",
4460
4464
  "approval.publish.ownerTaken": "已有主人:{owner}",
@@ -13243,6 +13247,11 @@ function textOf(args, key) {
13243
13247
  const value = args[key];
13244
13248
  return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
13245
13249
  }
13250
+ function labelsOf(args) {
13251
+ const value = args["labels"];
13252
+ if (!Array.isArray(value)) return [];
13253
+ return [...new Set(value.flatMap((one) => typeof one === "string" && one.trim() !== "" ? [one.trim()] : []))];
13254
+ }
13246
13255
  function recordApprovalRows(args, t) {
13247
13256
  const unit = textOf(args, "unit");
13248
13257
  const doing = textOf(args, "doing");
@@ -13314,6 +13323,14 @@ function publishRows(args, preview, t) {
13314
13323
  value: author !== "" ? author : preview === void 0 ? t("approval.publish.reading") : preview?.author === void 0 || preview.author === "" ? t("approval.publish.authorMissing") : preview.author
13315
13324
  }
13316
13325
  ];
13326
+ if (target === "market" || target === "" && preview?.market !== void 0) {
13327
+ const asked = labelsOf(args);
13328
+ const labels = asked.length > 0 ? asked : preview?.labels ?? [];
13329
+ rows.push({
13330
+ label: t("approval.publish.labels"),
13331
+ value: preview === void 0 && asked.length === 0 ? t("approval.publish.reading") : labels.length === 0 ? t("approval.publish.labelsMissing") : labels.join(" · ")
13332
+ });
13333
+ }
13317
13334
  const claim = preview?.claim;
13318
13335
  if (claim !== void 0 && claim.state !== "new" && claim.state !== "unknown") {
13319
13336
  if (claim.state === "unit-taken") rows.push({
@@ -5007,6 +5007,84 @@ var MARKET_PUBLISH_PREVIEW_ROUTE = "panels/market-publish-preview";
5007
5007
  var MARKET_SOURCES_RESOURCE = "market-sources";
5008
5008
  var MARKET_SOURCES_ROUTE = "panels/market-sources";
5009
5009
  //#endregion
5010
+ //#region ../shared/dist/host/market-index-claim.js
5011
+ function foldedName(name) {
5012
+ return name.replace(/\s+/gu, " ").trim().toLowerCase();
5013
+ }
5014
+ function publisherIdentityOf(text) {
5015
+ const raw = (text ?? "").trim();
5016
+ if (raw === "") return void 0;
5017
+ const match = /^(.*?)\s*<([^<>]*)>\s*$/u.exec(raw);
5018
+ const name = (match === null ? raw : match[1]).trim();
5019
+ const email = match === null ? "" : match[2].trim().toLowerCase();
5020
+ if (name === "") return void 0;
5021
+ return email === "" ? { name } : {
5022
+ name,
5023
+ email
5024
+ };
5025
+ }
5026
+ function samePublisher(left, right) {
5027
+ const a = publisherIdentityOf(left);
5028
+ const b = publisherIdentityOf(right);
5029
+ if (a === void 0 || b === void 0) return false;
5030
+ if (a.email !== void 0 && b.email !== void 0) return a.email === b.email;
5031
+ return foldedName(a.name) === foldedName(b.name);
5032
+ }
5033
+ function entryOwnerOf(entry) {
5034
+ const owner = (entry.owner ?? "").trim();
5035
+ if (owner !== "") return owner;
5036
+ const author = (entry.author ?? "").trim();
5037
+ return author === "" ? void 0 : author;
5038
+ }
5039
+ function claimOfEntry(entry, publisher) {
5040
+ if (entry === void 0) return { state: "new" };
5041
+ const owner = entryOwnerOf(entry);
5042
+ if (owner === void 0) return { state: "unowned" };
5043
+ if (publisher === void 0 || publisher.trim() === "") return { state: "unknown" };
5044
+ return samePublisher(owner, publisher) ? {
5045
+ state: "owned",
5046
+ owner
5047
+ } : {
5048
+ state: "conflict",
5049
+ owner
5050
+ };
5051
+ }
5052
+ function marketArtifactRoot(name) {
5053
+ return `${MARKET_ARTIFACTS_DIR}/${name}`;
5054
+ }
5055
+ function entryUnitOf(entry) {
5056
+ const unit = entry.metadata?.unit?.trim();
5057
+ if (unit !== void 0 && unit !== "") return unit;
5058
+ for (const version of entry.versions) {
5059
+ if (version.source.type !== "git") continue;
5060
+ const legacy = /^packages\/([^/]+)$/u.exec(version.source.path ?? "")?.[1];
5061
+ if (legacy !== void 0) return legacy;
5062
+ }
5063
+ }
5064
+ function claimOf(index, input) {
5065
+ const base = claimOfEntry(index.plugins.find((one) => one.name === input.name), input.publisher);
5066
+ if (base.state === "conflict" || base.state === "unknown") return base;
5067
+ const unit = (input.unit ?? "").trim();
5068
+ if (unit !== "") for (const other of index.plugins) {
5069
+ if (other.name === input.name) continue;
5070
+ if (entryUnitOf(other) !== unit) continue;
5071
+ return {
5072
+ state: "unit-taken",
5073
+ entry: other.name
5074
+ };
5075
+ }
5076
+ const path = (input.artifactPath ?? "").trim();
5077
+ if (path !== "") for (const other of index.plugins) {
5078
+ if (other.name === input.name) continue;
5079
+ if (!other.versions.some((one) => one.source.type === "git" && (one.source.path ?? "") === path)) continue;
5080
+ return {
5081
+ state: "path-taken",
5082
+ entry: other.name
5083
+ };
5084
+ }
5085
+ return base;
5086
+ }
5087
+ //#endregion
5010
5088
  //#region ../shared/dist/lib/market-index.js
5011
5089
  var BRANCH = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
5012
5090
  function marketBranchOk(value) {
@@ -5363,115 +5441,49 @@ function parseSourceConfig(raw) {
5363
5441
  }
5364
5442
  };
5365
5443
  }
5366
- function docFieldsOf(record, at) {
5367
- const name = asText$4(record["name"]);
5368
- if (name === "" || name.length > 214 || !NPM_NAME.test(name)) return problem$1("name", `不是合法 npm 包名:${JSON.stringify(name)}`, at);
5369
- const icon = asText$4(record["icon"]);
5370
- if (icon !== "") {
5371
- const badIcon = iconProblem(icon);
5372
- if (badIcon !== void 0) return badIcon;
5373
- }
5374
- const owner = asText$4(record["owner"]);
5375
- if (owner !== "") {
5376
- const badOwner = ownerProblem(owner);
5377
- if (badOwner !== void 0) return badOwner;
5378
- }
5379
- const metadata = metadataOf(record["metadata"]);
5380
- if (metadata?.unit !== void 0 && !UNIT_ID.test(metadata.unit)) return problem$1("unit", `metadata.unit 只能是小写字母开头的 a-z0-9-(现在是 ${JSON.stringify(metadata.unit)})`, at);
5381
- const title = asLocalized(record["title"]);
5382
- const description = asLocalized(record["description"]);
5383
- const author = asText$4(record["author"]);
5384
- const homepage = asText$4(record["homepage"]);
5385
- return {
5386
- name,
5387
- ...title === void 0 ? {} : { title },
5388
- ...description === void 0 ? {} : { description },
5389
- ...author === "" ? {} : { author },
5390
- ...owner === "" ? {} : { owner },
5391
- ...homepage === "" ? {} : { homepage },
5392
- ...icon === "" ? {} : { icon },
5393
- ...metadata === void 0 ? {} : { metadata }
5394
- };
5444
+ function indexPathBase(dir) {
5445
+ return dir.replace(/^\/+|\/+$/g, "") || "market";
5395
5446
  }
5396
- function parseMarketEntryDoc(raw) {
5397
- const record = asRecord$4(raw);
5398
- if (record === void 0) return {
5399
- ok: false,
5400
- problems: [problem$1("entry", "条目文件必须是一个 JSON 对象")]
5401
- };
5402
- const fields = docFieldsOf(record, "entry");
5403
- if ("kind" in fields) return {
5404
- ok: false,
5405
- problems: [fields]
5406
- };
5407
- const version = asText$4(record["version"]);
5408
- const timestamp = record["publishTimestamp"];
5409
- if (timestamp !== void 0 && (!Number.isInteger(timestamp) || timestamp < 0)) return {
5410
- ok: false,
5411
- problems: [problem$1("timestamp", "publishTimestamp 必须是 Unix 秒整数", "entry")]
5412
- };
5413
- if (version === "") {
5414
- if (timestamp === void 0) return {
5415
- ok: false,
5416
- problems: [problem$1("timestamp", "元数据文件要有 publishTimestamp", "entry")]
5417
- };
5418
- const epoch = record["epoch"];
5419
- if (epoch !== void 0 && (!Number.isInteger(epoch) || epoch < 0)) return {
5420
- ok: false,
5421
- problems: [problem$1("timestamp", "epoch 必须是非负整数", "entry")]
5422
- };
5447
+ function marketEntryPath(dir, name) {
5448
+ if (!NPM_NAME.test(name)) throw new Error(`不是合法包名:${JSON.stringify(name)}`);
5449
+ return `${indexPathBase(dir)}/${name}.json`;
5450
+ }
5451
+ function parseMarketEntryJson(text) {
5452
+ let raw;
5453
+ try {
5454
+ raw = JSON.parse(text);
5455
+ } catch (error) {
5423
5456
  return {
5424
- ok: true,
5425
- doc: {
5426
- ...fields,
5427
- publishTimestamp: timestamp,
5428
- ...epoch === void 0 ? {} : { epoch }
5429
- }
5457
+ ok: false,
5458
+ problems: [problem$1("not-json", `JSON 解析失败:${error instanceof Error ? error.message : String(error)}`, "entry")]
5430
5459
  };
5431
5460
  }
5432
- if (!isValidVersion(version)) return {
5461
+ const parsed = entryOf$1(raw, "entry");
5462
+ if ("kind" in parsed) return {
5433
5463
  ok: false,
5434
- problems: [problem$1("version", `版本号不是合法 SemVer:${JSON.stringify(version)}`, "entry")]
5464
+ problems: [parsed]
5435
5465
  };
5436
- const source = sourceOf(record["source"], "entry.source");
5437
- if ("kind" in source) return {
5438
- ok: false,
5439
- problems: [source]
5440
- };
5441
- const engines = asRecord$4(record["engines"]);
5442
- const dsh = engines === void 0 ? "" : asText$4(engines["dsh"]);
5443
5466
  return {
5444
5467
  ok: true,
5445
- doc: {
5446
- ...fields,
5447
- version,
5448
- source,
5449
- ...timestamp === void 0 ? {} : { publishTimestamp: timestamp },
5450
- ...dsh === "" ? {} : { engines: { dsh } }
5451
- }
5468
+ entry: parsed
5452
5469
  };
5453
5470
  }
5454
- function indexPathBase(dir) {
5455
- return dir.replace(/^\/+|\/+$/g, "") || "market";
5456
- }
5457
- function marketEntryPath(dir, name, version) {
5458
- if (!NPM_NAME.test(name)) throw new Error(`不是合法包名:${JSON.stringify(name)}`);
5459
- if (!isValidVersion(version)) throw new Error(`不是合法版本号:${JSON.stringify(version)}`);
5460
- return `${indexPathBase(dir)}/${name}/${version}.json`;
5461
- }
5462
5471
  //#endregion
5463
5472
  //#region ../shared/dist/host/market-index.js
5464
5473
  var MARKET_ENTRIES_README_LINES = [
5465
5474
  "# 索引目录",
5466
5475
  "",
5467
- "一个包一个目录,一个版本一个文件:`market/<包名>/<版本>.json`;产物在 `artifacts/<包名>/<版本>/`。",
5476
+ "一个包一个文件:`market/<包名>.json`,里面是这一个包的元数据与所有版本;",
5477
+ "",
5478
+ "产物在 `artifacts/<包名>/`(只放最新一版,老版本按条目里的 commit 取历史)。",
5468
5479
  "",
5469
- "`meta-<epoch>-<随机后缀>.json` 是只改标题 / 署名 / 说明时追加的元数据快照,读取时按 epoch 取最新的一份。"
5480
+ "发布时只改自己包的文件;包与包之间不共享文件。"
5470
5481
  ];
5471
5482
  var MARKET_INDEX_README_LINES = [
5472
5483
  "# 模块市场索引",
5473
5484
  "",
5474
- "这个仓库给 DeepSeek Harness 工作台当模块市场索引:条目在 `market/<包名>/<版本>.json`,产物在 `artifacts/<包名>/<版本>/`。",
5485
+ "这个仓库给 DeepSeek Harness 工作台当模块市场索引:一个包一个文件 `market/<包名>.json`,",
5486
+ "产物在 `artifacts/<包名>/`;老仓库的 `market.json` 会在第一次发布时自动拆开。",
5475
5487
  "",
5476
5488
  "## 发布模块",
5477
5489
  "",
@@ -5481,8 +5493,7 @@ var MARKET_INDEX_README_LINES = [
5481
5493
  "dsh-wb-module release --index <本仓库地址> --index-policy artifacts-in-repo",
5482
5494
  "```",
5483
5495
  "",
5484
- "产物会提交到 `artifacts/<包名>/<版本>/`,条目提交到 `market/<包名>/<版本>.json`。",
5485
- "发布只新增文件、不改已有文件,两台机器同时发也不会冲突;老条目的 `packages/<包名>/` 路径继续可用。",
5496
+ "发布只会改自己包的那一个文件;包与包之间不会碰同一个文件。",
5486
5497
  "",
5487
5498
  "一个包名归第一次发布的人(条目文件里的 `owner`)。换一个署名发同名会被拒;要换人,先把这一条的 `owner` 改成新的人。",
5488
5499
  "同一个市场里,一个模块 id(`metadata.unit`)只能被一个条目使用;另一个包名想用同一个 id / 产物目录也会被拒。",
@@ -5499,37 +5510,13 @@ function problem(kind, detail, at) {
5499
5510
  ...at === void 0 ? {} : { at }
5500
5511
  };
5501
5512
  }
5502
- function docFieldsFromEntry(entry) {
5503
- return {
5504
- name: entry.name,
5505
- ...entry.title === void 0 ? {} : { title: entry.title },
5506
- ...entry.description === void 0 ? {} : { description: entry.description },
5507
- ...entry.author === void 0 ? {} : { author: entry.author },
5508
- ...entry.owner === void 0 ? {} : { owner: entry.owner },
5509
- ...entry.homepage === void 0 ? {} : { homepage: entry.homepage },
5510
- ...entry.icon === void 0 ? {} : { icon: entry.icon },
5511
- ...entry.metadata === void 0 ? {} : { metadata: entry.metadata }
5512
- };
5513
- }
5514
- function marketVersionDocOf(entry, version) {
5515
- return {
5516
- ...docFieldsFromEntry(entry),
5517
- ...version
5518
- };
5519
- }
5520
5513
  function assembleMarketIndex(files) {
5521
5514
  const problems = [];
5522
- const groups = /* @__PURE__ */ new Map();
5515
+ const entries = /* @__PURE__ */ new Map();
5523
5516
  const ordered = [...files].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
5524
5517
  for (const file of ordered) {
5525
- let raw;
5526
- try {
5527
- raw = JSON.parse(file.text);
5528
- } catch (error) {
5529
- problems.push(problem("not-json", `JSON 解析失败:${error instanceof Error ? error.message : String(error)}`, file.path));
5530
- continue;
5531
- }
5532
- const parsed = parseMarketEntryDoc(raw);
5518
+ if (!file.path.endsWith(".json")) continue;
5519
+ const parsed = parseMarketEntryJson(file.text);
5533
5520
  if (!parsed.ok) {
5534
5521
  for (const one of parsed.problems) problems.push({
5535
5522
  ...one,
@@ -5537,71 +5524,47 @@ function assembleMarketIndex(files) {
5537
5524
  });
5538
5525
  continue;
5539
5526
  }
5540
- const doc = parsed.doc;
5541
- const group = groups.get(doc.name) ?? {
5542
- versions: /* @__PURE__ */ new Map(),
5543
- meta: void 0,
5544
- fallback: void 0
5545
- };
5546
- const at = doc.publishTimestamp ?? 0;
5547
- if ("version" in doc) {
5548
- if (group.versions.has(doc.version)) problems.push(problem("duplicate-version", `版本重复:${doc.version}`, file.path));
5549
- group.versions.set(doc.version, {
5550
- version: doc.version,
5551
- source: doc.source,
5552
- ...doc.publishTimestamp === void 0 ? {} : { publishTimestamp: doc.publishTimestamp },
5553
- ...doc.engines === void 0 ? {} : { engines: doc.engines }
5554
- });
5555
- if (group.fallback === void 0 || at > group.fallback.at || at === group.fallback.at && file.path > group.fallback.path) group.fallback = {
5556
- at,
5557
- path: file.path,
5558
- doc
5559
- };
5560
- } else {
5561
- const epoch = doc.epoch ?? 0;
5562
- if (group.meta === void 0 || epoch > group.meta.epoch || epoch === group.meta.epoch && at > group.meta.at || epoch === group.meta.epoch && at === group.meta.at && file.path > group.meta.path) group.meta = {
5563
- epoch,
5564
- at,
5565
- path: file.path,
5566
- doc
5567
- };
5527
+ const entry = parsed.entry;
5528
+ if (entries.has(entry.name)) {
5529
+ problems.push(problem("duplicate-name", `插件名重复:${entry.name}`, file.path));
5530
+ continue;
5568
5531
  }
5569
- groups.set(doc.name, group);
5532
+ entries.set(entry.name, {
5533
+ ...entry,
5534
+ versions: sortMarketVersions(entry.versions)
5535
+ });
5570
5536
  }
5571
5537
  if (problems.length > 0) return {
5572
5538
  ok: false,
5573
5539
  problems
5574
5540
  };
5575
- const plugins = [...groups.entries()].map(([name, group]) => {
5576
- const meta = group.meta?.doc ?? group.fallback?.doc;
5577
- return {
5578
- name,
5579
- versions: sortMarketVersions([...group.versions.values()]),
5580
- ...meta?.title === void 0 ? {} : { title: meta.title },
5581
- ...meta?.description === void 0 ? {} : { description: meta.description },
5582
- ...meta?.author === void 0 ? {} : { author: meta.author },
5583
- ...meta?.owner === void 0 ? {} : { owner: meta.owner },
5584
- ...meta?.homepage === void 0 ? {} : { homepage: meta.homepage },
5585
- ...meta?.icon === void 0 ? {} : { icon: meta.icon },
5586
- ...meta?.metadata === void 0 ? {} : { metadata: meta.metadata }
5587
- };
5588
- });
5589
- plugins.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
5590
5541
  return {
5591
5542
  ok: true,
5592
5543
  index: {
5593
5544
  format: 1,
5594
- plugins
5545
+ plugins: [...entries.values()].sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
5595
5546
  }
5596
5547
  };
5597
5548
  }
5549
+ function marketEntryFileOf(entry, dir = MARKET_INDEX_DIR) {
5550
+ return {
5551
+ path: marketEntryPath(dir, entry.name),
5552
+ text: `${JSON.stringify(entry, null, 2)}\n`
5553
+ };
5554
+ }
5598
5555
  function marketEntryFilesOf(index, dir = MARKET_INDEX_DIR) {
5599
- const files = [];
5600
- for (const entry of index.plugins) for (const version of entry.versions) files.push({
5601
- path: marketEntryPath(dir, entry.name, version.version),
5602
- text: `${JSON.stringify(marketVersionDocOf(entry, version), null, 2)}\n`
5603
- });
5604
- return files;
5556
+ return index.plugins.map((entry) => marketEntryFileOf(healedUnit(entry), dir));
5557
+ }
5558
+ function healedUnit(entry) {
5559
+ if (entry.metadata?.unit !== void 0) return entry;
5560
+ const unit = entryUnitOf(entry);
5561
+ return unit === void 0 ? entry : {
5562
+ ...entry,
5563
+ metadata: {
5564
+ ...entry.metadata,
5565
+ unit
5566
+ }
5567
+ };
5605
5568
  }
5606
5569
  function collectMarketEntryFiles(dir) {
5607
5570
  if (!existsSync(dir)) return [];
@@ -5629,87 +5592,6 @@ function marketEntryFilesOfLegacy(text, dir = MARKET_INDEX_DIR) {
5629
5592
  return marketEntryFilesOf(parsed.index, dir);
5630
5593
  }
5631
5594
  //#endregion
5632
- //#region ../shared/dist/host/market-index-claim.js
5633
- function foldedName(name) {
5634
- return name.replace(/\s+/gu, " ").trim().toLowerCase();
5635
- }
5636
- function publisherIdentityOf(text) {
5637
- const raw = (text ?? "").trim();
5638
- if (raw === "") return void 0;
5639
- const match = /^(.*?)\s*<([^<>]*)>\s*$/u.exec(raw);
5640
- const name = (match === null ? raw : match[1]).trim();
5641
- const email = match === null ? "" : match[2].trim().toLowerCase();
5642
- if (name === "") return void 0;
5643
- return email === "" ? { name } : {
5644
- name,
5645
- email
5646
- };
5647
- }
5648
- function samePublisher(left, right) {
5649
- const a = publisherIdentityOf(left);
5650
- const b = publisherIdentityOf(right);
5651
- if (a === void 0 || b === void 0) return false;
5652
- if (a.email !== void 0 && b.email !== void 0) return a.email === b.email;
5653
- return foldedName(a.name) === foldedName(b.name);
5654
- }
5655
- function entryOwnerOf(entry) {
5656
- const owner = (entry.owner ?? "").trim();
5657
- if (owner !== "") return owner;
5658
- const author = (entry.author ?? "").trim();
5659
- return author === "" ? void 0 : author;
5660
- }
5661
- function claimOfEntry(entry, publisher) {
5662
- if (entry === void 0) return { state: "new" };
5663
- const owner = entryOwnerOf(entry);
5664
- if (owner === void 0) return { state: "unowned" };
5665
- if (publisher === void 0 || publisher.trim() === "") return { state: "unknown" };
5666
- return samePublisher(owner, publisher) ? {
5667
- state: "owned",
5668
- owner
5669
- } : {
5670
- state: "conflict",
5671
- owner
5672
- };
5673
- }
5674
- function marketArtifactRoot(name) {
5675
- return `${MARKET_ARTIFACTS_DIR}/${name}`;
5676
- }
5677
- function marketArtifactPathFor(name, version) {
5678
- return `${MARKET_ARTIFACTS_DIR}/${name}/${version}`;
5679
- }
5680
- function entryUnitOf(entry) {
5681
- const unit = entry.metadata?.unit?.trim();
5682
- if (unit !== void 0 && unit !== "") return unit;
5683
- for (const version of entry.versions) {
5684
- if (version.source.type !== "git") continue;
5685
- const legacy = /^packages\/([^/]+)$/u.exec(version.source.path ?? "")?.[1];
5686
- if (legacy !== void 0) return legacy;
5687
- }
5688
- }
5689
- function claimOf(index, input) {
5690
- const base = claimOfEntry(index.plugins.find((one) => one.name === input.name), input.publisher);
5691
- if (base.state === "conflict" || base.state === "unknown") return base;
5692
- const unit = (input.unit ?? "").trim();
5693
- if (unit !== "") for (const other of index.plugins) {
5694
- if (other.name === input.name) continue;
5695
- if (entryUnitOf(other) !== unit) continue;
5696
- return {
5697
- state: "unit-taken",
5698
- entry: other.name
5699
- };
5700
- }
5701
- const path = (input.artifactPath ?? "").trim();
5702
- if (path !== "") for (const other of index.plugins) {
5703
- if (other.name === input.name) continue;
5704
- if (!other.versions.some((one) => one.source.type === "git" && (one.source.path ?? "") === path)) continue;
5705
- return {
5706
- state: "path-taken",
5707
- entry: other.name
5708
- };
5709
- }
5710
- return base;
5711
- }
5712
- //#endregion
5713
5595
  //#region ../shared/dist/host/changelog.js
5714
5596
  var CHANGELOG_FILE = "CHANGELOG.md";
5715
5597
  var CHANGELOG_KINDS = [
@@ -6823,6 +6705,36 @@ function publishClaimDenyOf(claim, name) {
6823
6705
  if (claim.state === "conflict") return `市场里 ${name} 已经有主了(${claim.owner})—— 同一个名字不能换人发。换一个带团队 scope 的包名;你就是原来的作者(只是换了机器或署名),用 author 参数带上原来的署名;确实是接手,让索引维护者改 market/<包名>/ 条目文件里的 owner。`;
6824
6706
  }
6825
6707
  //#endregion
6708
+ //#region src/modules/panels/model/approval-questions.ts
6709
+ function copiesOf(label, labelEn) {
6710
+ return {
6711
+ team: {
6712
+ zh: `把${label}发到团队源,只发构建产物,不发源码,允许吗?`,
6713
+ en: `Publish ${labelEn} to the team registry; only built artifacts, never the source. Allow?`
6714
+ },
6715
+ public: {
6716
+ zh: `把${label}公开发到 npm,任何人都能安装,允许吗?`,
6717
+ en: `Publish ${labelEn} publicly to npm; anyone can install it. Allow?`
6718
+ },
6719
+ tarball: {
6720
+ zh: `给${label}打一个安装包,只写本机不发源码,允许吗?`,
6721
+ en: `Pack ${labelEn} into a local tarball; nothing leaves this machine. Allow?`
6722
+ },
6723
+ git: {
6724
+ zh: `把${label}提交并推到 git,源码也会一起推送,允许吗?`,
6725
+ en: `Commit and push ${labelEn} to git; the source is pushed too. Allow?`
6726
+ },
6727
+ market: {
6728
+ zh: `把${label}发到市场仓库,产物与索引一起提交,允许吗?`,
6729
+ en: `Publish ${labelEn} to the market repository; artifacts and the index are committed together. Allow?`
6730
+ },
6731
+ local: {
6732
+ zh: `把${label}留在本机,不发,允许吗?`,
6733
+ en: `Keep ${labelEn} local and publish nothing. Allow?`
6734
+ }
6735
+ };
6736
+ }
6737
+ //#endregion
6826
6738
  //#region src/modules/panels/model/approval.ts
6827
6739
  var SPEC_LIMIT = 80;
6828
6740
  var TARGET_LIMIT = 20;
@@ -6908,6 +6820,11 @@ function clipped$1(text) {
6908
6820
  const chars = [...text];
6909
6821
  return chars.length > TARGET_LIMIT ? `${chars.slice(0, TARGET_LIMIT).join("")}…` : text;
6910
6822
  }
6823
+ function labelsOfArgs(raw) {
6824
+ const value = raw["labels"];
6825
+ if (!Array.isArray(value)) return [];
6826
+ return [...new Set(value.flatMap((one) => typeof one === "string" && one.trim() !== "" ? [one.trim()] : []))];
6827
+ }
6911
6828
  function stopVerdict(call, runs) {
6912
6829
  const raw = argsOf$1(call);
6913
6830
  const unit = textOf$3(raw, "unit");
@@ -6945,35 +6862,19 @@ function publishVerdict(call, preview) {
6945
6862
  kind: "deny",
6946
6863
  reason: `这一版还没有更新日志(${found.changelog.version})—— 带上 changelogNote(一行一条)再调,发布时会一起写进 CHANGELOG.md 并随包发`
6947
6864
  };
6948
- const copies = {
6949
- team: {
6950
- zh: `把${label}发到团队源,只发构建产物,不发源码,允许吗?`,
6951
- en: `Publish ${labelEn} to the team registry; only built artifacts, never the source. Allow?`
6952
- },
6953
- public: {
6954
- zh: `把${label}公开发到 npm,任何人都能安装,允许吗?`,
6955
- en: `Publish ${labelEn} publicly to npm; anyone can install it. Allow?`
6956
- },
6957
- tarball: {
6958
- zh: `给${label}打一个安装包,只写本机不发源码,允许吗?`,
6959
- en: `Pack ${labelEn} into a local tarball; nothing leaves this machine. Allow?`
6960
- },
6961
- git: {
6962
- zh: `把${label}提交并推到 git,源码也会一起推送,允许吗?`,
6963
- en: `Commit and push ${labelEn} to git; the source is pushed too. Allow?`
6964
- },
6965
- market: {
6966
- zh: `把${label}发到市场仓库,产物与索引一起提交,允许吗?`,
6967
- en: `Publish ${labelEn} to the market repository; artifacts and the index are committed together. Allow?`
6968
- },
6969
- local: {
6970
- zh: `把${label}留在本机,不发,允许吗?`,
6971
- en: `Keep ${labelEn} local and publish nothing. Allow?`
6972
- }
6865
+ const labels = labelsOfArgs(raw);
6866
+ if (labels.length > 2) return {
6867
+ kind: "deny",
6868
+ reason: `标签最多两个(收到 ${String(labels.length)} 个)—— 挑最能说明这个模块干什么的 1–2 个`
6973
6869
  };
6870
+ if (target === "market" && labels.length === 0 && (found?.labels ?? []).length === 0) return {
6871
+ kind: "deny",
6872
+ reason: "这个模块还没有标签 —— 发布到市场时给它 1–2 个短词(labels,说它干什么,比如「发布编排」「构建状态」);已经有标签就不用传"
6873
+ };
6874
+ const copies = copiesOf(label, labelEn);
6974
6875
  const copy = copies[target] ?? copies["team"];
6975
6876
  const targetLabel = found === null ? target : target === "market" && found.market !== void 0 ? `${found.name}@${found.version} → market (${found.market.url})` : `${found.name}@${found.version} → ${target}${found.registry === void 0 ? "" : ` (${found.registry})`}`;
6976
- const question = refresh ? pair(`只刷新${label}在市集里的元数据(标题 / 作者 / 说明),不升版本、不重发产物,允许吗?`, `Only refresh ${labelEn}'s market metadata (title, author, description); no version bump, no artifacts. Allow?`) : pair(copy.zh, copy.en);
6877
+ const question = refresh ? pair(`只刷新${label}在市集里的元数据(标题 / 作者 / 说明 / 标签),不升版本、不重发产物,允许吗?`, `Only refresh ${labelEn}'s market metadata (title, author, description, labels); no version bump, no artifacts. Allow?`) : pair(copy.zh, copy.en);
6977
6878
  return askOf(`${DIY_PUBLISH_TOOL} ${targetLabel}${refresh ? "(元数据)" : ""}`, question, question);
6978
6879
  }
6979
6880
  function approvalVerdict(call, marketSource) {
@@ -9589,7 +9490,7 @@ var PUBLISH_TIMEOUT_MS = 18e5;
9589
9490
  function registerPublishTool(ctx, tools, deps) {
9590
9491
  const publishTool = {
9591
9492
  name: DIY_PUBLISH_TOOL,
9592
- description: "用户说“发布”后才用:把这一格发到 target 指定的出口(不填按环境默认)。这一版还没有更新日志时把 changelogNote 一起带上 —— 发布时写进 CHANGELOG.md,随包发。",
9493
+ description: "用户说“发布”后才用:把这一格发到 target 指定的出口(不填按环境默认)。这一版还没有更新日志时把 changelogNote 一起带上 —— 发布时写进 CHANGELOG.md,随包发。这个模块还没有市集标签时把 labels 带上(1–2 个短词)。",
9593
9494
  parameters: {
9594
9495
  type: "object",
9595
9496
  properties: {
@@ -9606,6 +9507,11 @@ function registerPublishTool(ctx, tools, deps) {
9606
9507
  enum: [...CHANGELOG_KINDS],
9607
9508
  description: "分类,不填按 changed。"
9608
9509
  },
9510
+ labels: {
9511
+ type: "array",
9512
+ items: { type: "string" },
9513
+ description: "市集条目的标签,1–2 个短词(说它干什么,比如「发布编排」「构建状态」)。模块还没有标签时必填;已有可省,传了会替换掉原来的。"
9514
+ },
9609
9515
  target: {
9610
9516
  type: "string",
9611
9517
  enum: [
@@ -9644,7 +9550,7 @@ function registerPublishTool(ctx, tools, deps) {
9644
9550
  },
9645
9551
  refresh: {
9646
9552
  type: "boolean",
9647
- description: "只刷市集元数据(标题/作者/说明),不升版本、不重发产物。"
9553
+ description: "只刷市集元数据(标题/作者/说明/标签),不升版本、不重发产物。"
9648
9554
  }
9649
9555
  },
9650
9556
  required: ["unit"],
@@ -9664,6 +9570,7 @@ function registerPublishTool(ctx, tools, deps) {
9664
9570
  const license = asText(asRecord$1(raw)["license"]);
9665
9571
  const changelogNote = asText(asRecord$1(raw)["changelogNote"]);
9666
9572
  const changelogType = asText(asRecord$1(raw)["changelogType"]);
9573
+ const labels = labelsOf(asRecord$1(raw)["labels"]);
9667
9574
  let author = asText(asRecord$1(raw)["author"]);
9668
9575
  const verify = asRecord$1(raw)["verify"] === true;
9669
9576
  const resume = asRecord$1(raw)["resume"] === true;
@@ -9685,6 +9592,7 @@ function registerPublishTool(ctx, tools, deps) {
9685
9592
  if (author !== "") argv.push("--author", author);
9686
9593
  if (changelogNote !== "") argv.push("--changelog-note", changelogNote);
9687
9594
  if (changelogType !== "") argv.push("--changelog-type", changelogType);
9595
+ for (const label of labels) argv.push("--label", label);
9688
9596
  if (asRecord$1(raw)["refresh"] === true) argv.push("--refresh");
9689
9597
  const result = await runShell(shell, argv.map((one) => quoteShellArg(one)).join(" "), {
9690
9598
  workdir: root,
@@ -9744,15 +9652,23 @@ function registerPublishTool(ctx, tools, deps) {
9744
9652
  "包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
9745
9653
  ].join("\n");
9746
9654
  const changelog = asRecord$1(payload["changelog"]);
9747
- const changelogLine = asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "";
9655
+ const tail = [asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "", labels.length === 0 || targetOfPayload !== "market" && payload["refresh"] !== true ? "" : `标签:${labels.join(" · ")}`].filter((one) => one !== "").join("\n");
9748
9656
  return plainJson({
9749
9657
  ...payload,
9750
- text: changelogLine === "" ? text : `${text}\n${changelogLine}`
9658
+ text: tail === "" ? text : `${text}\n${tail}`
9751
9659
  });
9752
9660
  }
9753
9661
  };
9754
9662
  return tools.register(publishTool);
9755
9663
  }
9664
+ function labelsOf(value) {
9665
+ if (!Array.isArray(value) || value.length === 0) return [];
9666
+ const labels = value.flatMap((one) => typeof one === "string" ? [one.trim()] : []);
9667
+ if (labels.length !== value.length || labels.some((one) => one === "" || /[\r\n]/u.test(one))) throw new Error("标签是 1–2 个短词:每个都得是一行非空文字");
9668
+ const unique = [...new Set(labels)];
9669
+ if (unique.length > 2) throw new Error(`标签最多两个(收到 ${String(unique.length)} 个)—— 挑最能说明这个模块干什么的`);
9670
+ return unique;
9671
+ }
9756
9672
  //#endregion
9757
9673
  //#region src/shared/api/host/diy-recon.ts
9758
9674
  var SITE_LIMIT = 6;
@@ -10194,7 +10110,11 @@ function renderStatus(root, lines, options = {}) {
10194
10110
  }
10195
10111
  function publishStatusOf(preview) {
10196
10112
  if (preview === null) return "";
10197
- return [preview.market === void 0 ? preview.registry === void 0 ? "发布出口:安装包(这个环境没配可写市场索引源,模块也没声明 publishConfig.registry)" : `发布出口:团队源 ${preview.registry}(package.json 的 publishConfig.registry)` : `发布出口:市场索引 ${preview.market.url}${preview.market.ref === void 0 ? "" : `(${preview.market.ref})`}—— 不填 target 时默认发这里;这一版会发 ${preview.market.nextVersion ?? preview.version}`, preview.changelog === void 0 ? void 0 : preview.changelog.missing ? `发布前置:CHANGELOG 还差 ${preview.changelog.version} 这一版 —— 发布时带 changelogNote(会先建文件)` : `发布前置:CHANGELOG 里已有 ${preview.changelog.version}`].filter((one) => one !== void 0).join("\n");
10113
+ return [
10114
+ preview.market === void 0 ? preview.registry === void 0 ? "发布出口:安装包(这个环境没配可写市场索引源,模块也没声明 publishConfig.registry)" : `发布出口:团队源 ${preview.registry}(package.json 的 publishConfig.registry)` : `发布出口:市场索引 ${preview.market.url}${preview.market.ref === void 0 ? "" : `(${preview.market.ref})`}—— 不填 target 时默认发这里;这一版会发 ${preview.market.nextVersion ?? preview.version}`,
10115
+ preview.market === void 0 ? void 0 : preview.labels === void 0 || preview.labels.length === 0 ? "标签:还没有 —— 发布到市场时带上 1–2 个(labels)" : `标签:${preview.labels.join(" · ")}`,
10116
+ preview.changelog === void 0 ? void 0 : preview.changelog.missing ? `发布前置:CHANGELOG 还差 ${preview.changelog.version} 这一版 —— 发布时带 changelogNote(会先建文件)` : `发布前置:CHANGELOG 里已有 ${preview.changelog.version}`
10117
+ ].filter((one) => one !== void 0).join("\n");
10198
10118
  }
10199
10119
  function renderList(root, modules, shape = "bare") {
10200
10120
  if (modules.length === 0) return [
@@ -10549,7 +10469,7 @@ function marketOf(dir, name, version) {
10549
10469
  url: source.url,
10550
10470
  ...source.ref === void 0 ? {} : { ref: source.ref },
10551
10471
  indexPath: source.path ?? "market",
10552
- artifactPath: marketArtifactPathFor(name, nextVersion ?? version),
10472
+ artifactPath: marketArtifactRoot(name),
10553
10473
  files: ARTIFACTS.filter((one) => existsSync(join(dir, one))),
10554
10474
  ...nextVersion === void 0 ? {} : { nextVersion }
10555
10475
  },
@@ -10639,6 +10559,9 @@ function publishPreviewOf(root, unit, options = {}) {
10639
10559
  const publishConfig = record["publishConfig"];
10640
10560
  const registry = typeof publishConfig === "object" && publishConfig !== null ? publishConfig["registry"] : void 0;
10641
10561
  const market = marketOf(found.dir, name, version);
10562
+ const marketEntry = market === void 0 ? void 0 : publishedEntryOf(market.index, name);
10563
+ const declaredLabels = labelsOfManifest(record);
10564
+ const labels = declaredLabels.length > 0 || marketEntry === void 0 ? declaredLabels : marketEntryLabels(marketEntry);
10642
10565
  const target = targetVersionOf(version, (options.version ?? "").trim(), market?.market);
10643
10566
  const askedAuthor = (options.author ?? "").trim();
10644
10567
  const author = askedAuthor === "" ? authorOf(record) : askedAuthor;
@@ -10652,12 +10575,21 @@ function publishPreviewOf(root, unit, options = {}) {
10652
10575
  name,
10653
10576
  version,
10654
10577
  ...author === "" ? {} : { author },
10578
+ ...labels.length === 0 ? {} : { labels },
10655
10579
  ...typeof registry === "string" && registry !== "" ? { registry } : {},
10656
10580
  ...market === void 0 ? {} : { market: market.market },
10657
10581
  ...claim === void 0 ? {} : { claim },
10658
10582
  changelog: changelogOf(found.dir, target, (options.changelog ?? "").trim(), kindOf(options.changelogType))
10659
10583
  };
10660
10584
  }
10585
+ function labelsOfManifest(record) {
10586
+ const dsh = record["dsh"];
10587
+ const workbench = dsh !== null && typeof dsh === "object" ? dsh["workbench"] : void 0;
10588
+ const market = workbench !== null && typeof workbench === "object" ? workbench["market"] : void 0;
10589
+ const labels = market !== null && typeof market === "object" ? market["labels"] : void 0;
10590
+ if (!Array.isArray(labels)) return [];
10591
+ return [...new Set(labels.flatMap((one) => typeof one === "string" && one.trim() !== "" ? [one.trim()] : []))];
10592
+ }
10661
10593
  function authorOf(manifest) {
10662
10594
  const author = manifest["author"];
10663
10595
  if (typeof author === "string") return author.trim();
@@ -0,0 +1,6 @@
1
+ interface ApprovalCopy {
2
+ readonly zh: string;
3
+ readonly en: string;
4
+ }
5
+ export declare function copiesOf(label: string, labelEn: string): Record<string, ApprovalCopy>;
6
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -100,8 +100,8 @@
100
100
  "proper-lockfile": "^4.1.2",
101
101
  "which": "^7.0.0",
102
102
  "write-file-atomic": "^8.0.0",
103
- "@dsh-workbench/shared": "1.1.7",
104
- "@dsh-workbench/adapters-default": "1.1.7"
103
+ "@dsh-workbench/shared": "1.1.8",
104
+ "@dsh-workbench/adapters-default": "1.1.8"
105
105
  },
106
106
  "license": "MIT",
107
107
  "repository": {
@@ -45,5 +45,6 @@
45
45
  ## 4 发布
46
46
 
47
47
  只在用户明确说「帮我发布」后调。这一版还没有更新日志(`_status` 会说)时,把改了什么写进 `changelogNote`(一行一条,`changelogType` 选分类,缺省 changed):审批屏会展示将写进 `CHANGELOG.md` 的那一段,用户点了头才落盘,文件不在会先建。这一版已有内容就不用传,带了也不追加。
48
+ 这个模块还没有市集标签(`_status` 会说)时,自己给它想 1–2 个短词放进 `labels`(说它干什么,比如「发布编排」「构建状态」);已有标签就不用传,传了会替换原来的。
48
49
  出口、署名、标题、安装命令由工具与审批屏处理,不用问、不用补;没写作者时审批屏显示推断的署名(市集里上次的 / 同工坊的 / 本机 git 身份),用户觉得不对就拒绝后再说,他点名了才传 `author=…`。
49
50
  需要用户接管时返回 `needsUser: { step, text, command? }`:把 `text` 说给用户,等「好了」再跑;不向用户要 token。
@@ -47,6 +47,7 @@ description: 在工作台里把当前面板调明白,或者给它造一块新
47
47
  审批项标注「要你真用一次」,不打勾。结论只报:已装、在面板哪一格。
48
48
  8. **发布** —— 只在用户说「帮我发布」之后:`workbench_diy_publish`(`unit=…`);这一版还没有更新日志就带上 `changelogNote`(一行一条),
49
49
  发布时一起写进 `CHANGELOG.md` 并随包发,审批屏会展示将写入的那一段。
50
+ 还没有市集标签就自己给 1–2 个短词放进 `labels`(说它干什么,比如「发布编排」「构建状态」);已有标签就不用传。
50
51
  不手改 `package.json`、不补标题/署名、不问出口:工具与审批屏已经处理;他明确点名安装包 / 公开 / git 才传 target。
51
52
  他说要换作者才传 `author="姓名 <邮箱>"`,别自己编一个;没写作者时审批屏显示推断的署名,他拒绝后再说。
52
53
  市场里一个包名归第一次发布的人:换署名发同名会被拒;真是接手,让维护者先改那一条的 `owner`。
@@ -74,6 +74,8 @@ DIY 族仅在选了 DIY 模式的会话出现(按模式,不按工作区)
74
74
  用户真是接手的人,让他先找索引维护者改那一条的 `owner`,别自己改成对方的署名去绕。
75
75
  一个模块 id(`dsh.workbench.unit`)在同一个市场里只能有一个条目:换包名、沿用旧 unit 会被拒 ——
76
76
  换包名时把 unit 也换掉,别为了保留 id 去改别人的条目。
77
+ 这个模块还没有标签时(`workbench_diy_status` 会说),自己给它想 1–2 个短词放进 `labels`(说它干什么,比如「发布编排」「构建状态」),
78
+ 别再问用户;已经有标签就不用传,传了会替换掉原来的(仍是最多两个)。发布时会把模块里的标签并进索引(只碰标签)。
77
79
  2. 工具回 `needsUser` 时照 `text` 转述,不在对话里要 token / OTP;`otp` 交接后 `verify=true` 只核不发。
78
- 3. 只改市集元数据(标题 / 作者 / 说明)不升版本:`refresh=true`(换作者时同时传 `author`,仍要过 `owner` 检查)。
79
- 发布新版本不会改这些元数据(工具沿用远端条目当前的值),别指望发一版顺手把标题改掉。
80
+ 3. 只改市集元数据(标题 / 作者 / 说明 / 标签)不升版本:`refresh=true`(换作者时同时传 `author`,仍要过 `owner` 检查)。
81
+ 发布新版本不会改这些元数据(工具沿用远端条目当前的值),别指望发一版顺手把标题改掉;标签是例外 —— 发布总会照模块里的标签并一次。