@dsh-workbench/core 1.1.0 → 1.1.2

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.
Files changed (33) hide show
  1. package/README.md +14 -5
  2. package/bin/dsh-workbench +7 -1
  3. package/client.js +726 -397
  4. package/dist/client/client.body.js +726 -397
  5. package/dist/client/index.js +587 -258
  6. package/dist/host/index.js +657 -328
  7. package/dist/types/app/layouts/approval-detail.d.ts +2 -1
  8. package/dist/types/app/layouts/market/discover-search.d.ts +8 -0
  9. package/dist/types/app/layouts/market/discover-source-item.d.ts +26 -0
  10. package/dist/types/app/layouts/market/discover-sources-dialog.d.ts +4 -0
  11. package/dist/types/app/layouts/market/discover-view.d.ts +2 -0
  12. package/dist/types/app/layouts/market/use-discover.d.ts +3 -0
  13. package/dist/types/kernel/contract/market-index.d.ts +1 -1
  14. package/dist/types/modules/panels/client.d.ts +1 -1
  15. package/dist/types/modules/panels/model/approval.d.ts +4 -2
  16. package/dist/types/modules/panels/model/diy-publish.d.ts +10 -0
  17. package/dist/types/modules/panels/model/diy-tools.d.ts +0 -1
  18. package/dist/types/modules/panels/model/market-sources-act.d.ts +2 -1
  19. package/dist/types/modules/panels/model/market-sources-tool.d.ts +3 -0
  20. package/dist/types/modules/panels/model/market-sources-tool.test.d.ts +1 -0
  21. package/dist/types/modules/panels/model/market-tools.d.ts +8 -1
  22. package/dist/types/modules/panels/model/publish-claim.d.ts +2 -0
  23. package/dist/types/modules/panels/unit.d.ts +7 -5
  24. package/dist/types/shared/api/host/diy-publish-preview.d.ts +2 -0
  25. package/dist/types/shared/api/host/market-source-init.d.ts +1 -0
  26. package/dist/types/shared/api/host/market-sources-store.d.ts +2 -0
  27. package/dist/types/shared/hooks/market-queries.d.ts +1 -1
  28. package/package.json +3 -3
  29. package/skills/workbench-module-diy/FAMILY.md +3 -4
  30. package/skills/workbench-module-diy/SKILL.md +4 -1
  31. package/skills/workbench-module-diy/WIRING.md +9 -2
  32. package/dist/types/modules/panels/model/diy-changelog.d.ts +0 -2
  33. package/dist/types/shared/api/host/diy-changelog.d.ts +0 -13
@@ -556,10 +556,8 @@ function readDeclaration(subject, raw) {
556
556
  zh: text$2(declaration.title.zh),
557
557
  en: text$2(declaration.title.en)
558
558
  },
559
- card: card === null ? null : {
560
- titleKey: unitTitleKey(id),
561
- order: card.order
562
- },
559
+ titleKey: unitTitleKey(id),
560
+ card: card === null ? null : { order: card.order },
563
561
  tools: declaration.tools.map((tool) => ({
564
562
  name: text$2(tool.name),
565
563
  writes: tool.writes
@@ -4579,7 +4577,10 @@ var MARKET_INDEX_README_LINES = [
4579
4577
  "dsh-wb-module release --index <本仓库地址> --index-policy artifacts-in-repo",
4580
4578
  "```",
4581
4579
  "",
4582
- "产物会提交到 `packages/<unit>/`,并更新 `market.json`。",
4580
+ "产物会提交到 `packages/<包名>/`(带 scope 时是 `packages/@scope/名字/`),并更新 `market.json`。",
4581
+ "",
4582
+ "一个包名归第一次发布的人(`market.json` 里的 `owner`)。换一个署名发同名会被拒;要换人,先把这一条的 `owner` 改成新的人。",
4583
+ "同一个市场里,一个模块 id(`metadata.unit`)只能被一个条目使用;另一个包名想用同一个 id / 产物目录也会被拒。",
4583
4584
  "",
4584
4585
  "## 使用这个索引",
4585
4586
  "",
@@ -4685,15 +4686,25 @@ function iconProblem(icon) {
4685
4686
  if (/^https?:\/\//i.test(icon)) return void 0;
4686
4687
  return problem("icon", "icon 只能是 base64 data URL 或 http(s) URL");
4687
4688
  }
4689
+ var MAX_OWNER_CHARS = 200;
4690
+ var UNIT_ID = /^[a-z][a-z0-9-]*$/;
4691
+ function ownerProblem(owner) {
4692
+ if (/[\r\n]/u.test(owner)) return problem("owner", "owner 只能是一行署名");
4693
+ if (owner.length > MAX_OWNER_CHARS) return problem("owner", "owner 超过 200 字");
4694
+ }
4688
4695
  function metadataOf(raw) {
4689
4696
  const record = asRecord$3(raw);
4690
4697
  if (record === void 0) return void 0;
4698
+ const workbench = record["workbench"] === true;
4699
+ const unit = asText$3(record["unit"]);
4691
4700
  const category = asText$3(record["category"]);
4692
4701
  const labels = asTextArray(record["labels"]);
4693
4702
  const keywords = asTextArray(record["keywords"]);
4694
4703
  const sortKeysRecord = asRecord$3(record["sortKeys"]);
4695
4704
  const sortKeys = sortKeysRecord === void 0 ? void 0 : Object.fromEntries(Object.entries(sortKeysRecord).flatMap(([key, value]) => typeof value === "string" || typeof value === "number" ? [[key, value]] : []));
4696
4705
  return {
4706
+ ...workbench ? { workbench: true } : {},
4707
+ ...unit === "" ? {} : { unit },
4697
4708
  ...category === "" ? {} : { category },
4698
4709
  ...labels.length === 0 ? {} : { labels },
4699
4710
  ...keywords.length === 0 ? {} : { keywords },
@@ -4715,14 +4726,21 @@ function entryOf$1(raw, at) {
4715
4726
  const title = asLocalized(record["title"]);
4716
4727
  const description = asLocalized(record["description"]);
4717
4728
  const author = asText$3(record["author"]);
4729
+ const owner = asText$3(record["owner"]);
4730
+ if (owner !== "") {
4731
+ const badOwner = ownerProblem(owner);
4732
+ if (badOwner !== void 0) return badOwner;
4733
+ }
4718
4734
  const homepage = asText$3(record["homepage"]);
4719
4735
  const metadata = metadataOf(record["metadata"]);
4736
+ if (metadata?.unit !== void 0 && !UNIT_ID.test(metadata.unit)) return problem("unit", `metadata.unit 只能是小写字母开头的 a-z0-9-(现在是 ${JSON.stringify(metadata.unit)})`, at);
4720
4737
  return {
4721
4738
  name,
4722
4739
  versions,
4723
4740
  ...title === void 0 ? {} : { title },
4724
4741
  ...description === void 0 ? {} : { description },
4725
4742
  ...author === "" ? {} : { author },
4743
+ ...owner === "" ? {} : { owner },
4726
4744
  ...homepage === "" ? {} : { homepage },
4727
4745
  ...icon === "" ? {} : { icon },
4728
4746
  ...metadata === void 0 ? {} : { metadata }
@@ -4909,6 +4927,84 @@ function gitUrlOk(url) {
4909
4927
  return true;
4910
4928
  }
4911
4929
  //#endregion
4930
+ //#region ../shared/dist/host/market-index-claim.js
4931
+ function foldedName(name) {
4932
+ return name.replace(/\s+/gu, " ").trim().toLowerCase();
4933
+ }
4934
+ function publisherIdentityOf(text) {
4935
+ const raw = (text ?? "").trim();
4936
+ if (raw === "") return void 0;
4937
+ const match = /^(.*?)\s*<([^<>]*)>\s*$/u.exec(raw);
4938
+ const name = (match === null ? raw : match[1]).trim();
4939
+ const email = match === null ? "" : match[2].trim().toLowerCase();
4940
+ if (name === "") return void 0;
4941
+ return email === "" ? { name } : {
4942
+ name,
4943
+ email
4944
+ };
4945
+ }
4946
+ function samePublisher(left, right) {
4947
+ const a = publisherIdentityOf(left);
4948
+ const b = publisherIdentityOf(right);
4949
+ if (a === void 0 || b === void 0) return false;
4950
+ if (a.email !== void 0 && b.email !== void 0) return a.email === b.email;
4951
+ return foldedName(a.name) === foldedName(b.name);
4952
+ }
4953
+ function entryOwnerOf(entry) {
4954
+ const owner = (entry.owner ?? "").trim();
4955
+ if (owner !== "") return owner;
4956
+ const author = (entry.author ?? "").trim();
4957
+ return author === "" ? void 0 : author;
4958
+ }
4959
+ function claimOfEntry(entry, publisher) {
4960
+ if (entry === void 0) return { state: "new" };
4961
+ const owner = entryOwnerOf(entry);
4962
+ if (owner === void 0) return { state: "unowned" };
4963
+ if (publisher === void 0 || publisher.trim() === "") return { state: "unknown" };
4964
+ return samePublisher(owner, publisher) ? {
4965
+ state: "owned",
4966
+ owner
4967
+ } : {
4968
+ state: "conflict",
4969
+ owner
4970
+ };
4971
+ }
4972
+ function marketArtifactPath(name) {
4973
+ return `packages/${name}`;
4974
+ }
4975
+ function entryUnitOf(entry) {
4976
+ const unit = entry.metadata?.unit?.trim();
4977
+ if (unit !== void 0 && unit !== "") return unit;
4978
+ for (const version of entry.versions) {
4979
+ if (version.source.type !== "git") continue;
4980
+ const legacy = /^packages\/([^/]+)$/u.exec(version.source.path ?? "")?.[1];
4981
+ if (legacy !== void 0) return legacy;
4982
+ }
4983
+ }
4984
+ function claimOf(index, input) {
4985
+ const base = claimOfEntry(index.plugins.find((one) => one.name === input.name), input.publisher);
4986
+ if (base.state === "conflict" || base.state === "unknown") return base;
4987
+ const unit = (input.unit ?? "").trim();
4988
+ if (unit !== "") for (const other of index.plugins) {
4989
+ if (other.name === input.name) continue;
4990
+ if (entryUnitOf(other) !== unit) continue;
4991
+ return {
4992
+ state: "unit-taken",
4993
+ entry: other.name
4994
+ };
4995
+ }
4996
+ const path = (input.artifactPath ?? "").trim();
4997
+ if (path !== "") for (const other of index.plugins) {
4998
+ if (other.name === input.name) continue;
4999
+ if (!other.versions.some((one) => one.source.type === "git" && (one.source.path ?? "") === path)) continue;
5000
+ return {
5001
+ state: "path-taken",
5002
+ entry: other.name
5003
+ };
5004
+ }
5005
+ return base;
5006
+ }
5007
+ //#endregion
4912
5008
  //#region ../shared/dist/host/changelog.js
4913
5009
  var CHANGELOG_FILE = "CHANGELOG.md";
4914
5010
  var CHANGELOG_KINDS = [
@@ -5066,6 +5162,25 @@ function appendChangelogEntry(text, options) {
5066
5162
  out.splice(at, 0, ...bullets);
5067
5163
  return `${normalizeWhitespace(out)}\n`;
5068
5164
  }
5165
+ function planChangelog(text, options) {
5166
+ const source = normalizeChangelogText(text);
5167
+ const version = changelogVersion(options.version);
5168
+ const section = changelogSectionOf(source, version);
5169
+ if (section !== void 0 && changelogHasEntry(source, version)) return {
5170
+ action: "kept",
5171
+ version,
5172
+ text: source,
5173
+ section
5174
+ };
5175
+ if (bulletsOf(options.note).length === 0) return void 0;
5176
+ const next = appendChangelogEntry(source, options);
5177
+ return {
5178
+ action: "written",
5179
+ version,
5180
+ text: next,
5181
+ section: changelogSectionOf(next, version) ?? ""
5182
+ };
5183
+ }
5069
5184
  function normalizeWhitespace(rows) {
5070
5185
  const out = [];
5071
5186
  for (const row of rows) {
@@ -5460,12 +5575,14 @@ var MODULES_UPDATE_DEPENDENCY_TOOL = "workbench_modules_update_dependency";
5460
5575
  var MODULES_UPDATE_PLUGIN_TOOL = "workbench_modules_update_plugin";
5461
5576
  var MODULES_UNINSTALL_PLUGIN_TOOL = "workbench_modules_uninstall_plugin";
5462
5577
  var MODULES_UPDATE_WORKBENCH_TOOL = "workbench_modules_update_workbench";
5578
+ var MARKET_SOURCES_TOOL = "workbench_market_sources";
5579
+ var MARKET_ENABLE_TOOL = "workbench_market_enable";
5580
+ var MARKET_DISABLE_TOOL = "workbench_market_disable";
5463
5581
  var DIY_STATUS_TOOL = "workbench_diy_status";
5464
5582
  var DIY_PANEL_TOOL = "workbench_diy_panel";
5465
5583
  var DIY_COMPONENTS_TOOL = "workbench_diy_components";
5466
5584
  var DIY_RECON_TOOL = "workbench_diy_recon";
5467
5585
  var DIY_SPEC_TOOL = "workbench_diy_spec";
5468
- var DIY_CHANGELOG_TOOL = "workbench_diy_changelog";
5469
5586
  var DIY_INSTALL_TOOL = "workbench_diy_install";
5470
5587
  var DIY_PUBLISH_TOOL = "workbench_diy_publish";
5471
5588
  var DIY_SHOW_TOOL = "workbench_diy_show";
@@ -5499,6 +5616,7 @@ var DIY_DEBUG_STOP_TOOL = "workbench_diy_debug_stop";
5499
5616
  var READ_TOOLS = [
5500
5617
  PANELS_LIST_TOOL,
5501
5618
  MODULES_LIST_TOOL,
5619
+ MARKET_SOURCES_TOOL,
5502
5620
  DIY_STATUS_TOOL,
5503
5621
  DIY_PANEL_TOOL,
5504
5622
  DIY_COMPONENTS_TOOL,
@@ -5540,7 +5658,8 @@ var WRITE_TOOLS = [
5540
5658
  MODULES_UPDATE_PLUGIN_TOOL,
5541
5659
  MODULES_UNINSTALL_PLUGIN_TOOL,
5542
5660
  MODULES_UPDATE_WORKBENCH_TOOL,
5543
- DIY_CHANGELOG_TOOL,
5661
+ MARKET_ENABLE_TOOL,
5662
+ MARKET_DISABLE_TOOL,
5544
5663
  DIY_INSTALL_TOOL,
5545
5664
  DIY_PUBLISH_TOOL,
5546
5665
  DIY_SHOW_TOOL,
@@ -5557,7 +5676,6 @@ var DIY_TOOLS = [
5557
5676
  DIY_COMPONENTS_TOOL,
5558
5677
  DIY_RECON_TOOL,
5559
5678
  DIY_SPEC_TOOL,
5560
- DIY_CHANGELOG_TOOL,
5561
5679
  DIY_INSTALL_TOOL,
5562
5680
  DIY_PUBLISH_TOOL,
5563
5681
  DIY_SHOW_TOOL,
@@ -5601,6 +5719,7 @@ function screenOnlyToolsFor(pageDebug) {
5601
5719
  }
5602
5720
  var panelsUnit = {
5603
5721
  id: "panels",
5722
+ titleKey: "panels.title",
5604
5723
  card: null,
5605
5724
  tools: [...READ_TOOLS.map((name) => ({
5606
5725
  name,
@@ -5833,6 +5952,14 @@ function readDomainsOf(design) {
5833
5952
  return [...found];
5834
5953
  }
5835
5954
  //#endregion
5955
+ //#region src/modules/panels/model/publish-claim.ts
5956
+ function publishClaimDenyOf(claim, name) {
5957
+ if (claim === void 0) return void 0;
5958
+ if (claim.state === "unit-taken") return `市场里 ${claim.entry} 已经用了这个模块 id —— 两个模块不能共用一个,装在一起会撞。给这个模块换一个 unit(dsh.workbench.unit)再发。`;
5959
+ if (claim.state === "path-taken") return `市场仓库里的产物目录已经被 ${claim.entry} 占了 —— 换一个包名再发。`;
5960
+ if (claim.state === "conflict") return `市场里 ${name} 已经有主了(${claim.owner})—— 同一个名字不能换人发。换一个带团队 scope 的包名;你就是原来的作者(只是换了机器或署名),用 author 参数带上原来的署名;确实是接手,让索引维护者改 market.json 里这一条的 owner。`;
5961
+ }
5962
+ //#endregion
5836
5963
  //#region src/modules/panels/model/approval.ts
5837
5964
  var SPEC_LIMIT = 80;
5838
5965
  var TARGET_LIMIT = 20;
@@ -5939,11 +6066,22 @@ function publishVerdict(call, preview) {
5939
6066
  if (raw["verify"] === true || raw["target"] === "local") return { kind: "allow" };
5940
6067
  const refresh = raw["refresh"] === true;
5941
6068
  const unit = textOf$3(raw, "unit");
5942
- const found = unit === "" ? null : preview?.(unit) ?? null;
6069
+ const askedAuthor = textOf$3(raw, "author");
6070
+ const found = unit === "" ? null : preview?.(unit, askedAuthor === "" ? void 0 : askedAuthor) ?? null;
5943
6071
  const asked = textOf$3(raw, "target");
5944
6072
  const target = refresh ? "market" : asked === "" ? found?.market === void 0 ? found?.registry === void 0 ? "tarball" : "team" : "market" : asked;
6073
+ const source = textOf$3(raw, "source");
6074
+ const claimReason = publishClaimDenyOf(found?.claim, found?.name ?? unit);
6075
+ if (source === "" && target !== "tarball" && target !== "local" && claimReason !== void 0) return {
6076
+ kind: "deny",
6077
+ reason: claimReason
6078
+ };
5945
6079
  const label = unit === "" ? "工作台模块" : `工作台模块 [${unit}]`;
5946
6080
  const labelEn = unit === "" ? "the workbench module" : `the workbench module [${unit}]`;
6081
+ if (!refresh && found?.changelog?.missing === true && textOf$3(raw, "changelogNote") === "") return {
6082
+ kind: "deny",
6083
+ reason: `这一版还没有更新日志(${found.changelog.version})—— 带上 changelogNote(一行一条)再调,发布时会一起写进 CHANGELOG.md 并随包发`
6084
+ };
5947
6085
  const copies = {
5948
6086
  team: {
5949
6087
  zh: `把${label}发到团队源,只发构建产物,不发源码,允许吗?`,
@@ -5975,7 +6113,7 @@ function publishVerdict(call, preview) {
5975
6113
  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);
5976
6114
  return askOf(`${DIY_PUBLISH_TOOL} ${targetLabel}${refresh ? "(元数据)" : ""}`, question, question);
5977
6115
  }
5978
- var panelsApproval = (call) => {
6116
+ function approvalVerdict(call, marketSource) {
5979
6117
  if (call.name === "workbench_long_tasks_stop") return stopVerdict(call, void 0);
5980
6118
  if (call.name === "workbench_diy_publish") return publishVerdict(call);
5981
6119
  if (call.name === "workbench_diy_debug_start" || call.name === "workbench_diy_debug_resume") {
@@ -6046,9 +6184,41 @@ var panelsApproval = (call) => {
6046
6184
  return askOf(`${MODULES_UNINSTALL_PLUGIN_TOOL} ${plugin ?? ""} uninstalls an opencli plugin on this machine`, pair(`会卸载${target.zh},允许吗?`, `it uninstalls ${target.en}. Allow?`), pair(`agent 想卸载${target.zh},允许吗?`, `The agent wants to uninstall ${target.en}. Allow?`));
6047
6185
  }
6048
6186
  case MODULES_UPDATE_WORKBENCH_TOOL: return askOf(`${MODULES_UPDATE_WORKBENCH_TOOL} replaces the workbench itself with the new version from the registry`, pair("会把工作台本身更新到源上的新版,允许吗?", "it updates the workbench itself to the new version from the registry. Allow?"), pair("agent 想把工作台本身更新到源上的新版,允许吗?", "The agent wants to update the workbench itself from the registry. Allow?"));
6187
+ case MARKET_ENABLE_TOOL: {
6188
+ const id = text("id");
6189
+ const source = marketSource?.(id);
6190
+ if (marketSource !== void 0 && source === void 0) return {
6191
+ kind: "deny",
6192
+ reason: id === void 0 ? "这台机器上还没有选中的索引源 —— 先在「模块市场 → 发现」里添加并选中一个" : `没有这个索引源:${id}`
6193
+ };
6194
+ if (source !== void 0 && source.policy === "artifacts-in-repo") return { kind: "allow" };
6195
+ if (source !== void 0 && source.kind === "http") return {
6196
+ kind: "deny",
6197
+ reason: "http 索引源只能读,不能当发布目的地"
6198
+ };
6199
+ const where = source === void 0 ? {
6200
+ zh: id === void 0 ? "工作台当前选中的索引源" : `工作台的索引源「${id}」`,
6201
+ en: id === void 0 ? "the selected workbench index source" : `the workbench index source "${id}"`
6202
+ } : {
6203
+ zh: `工作台的索引源「${source.label}」`,
6204
+ en: `the workbench index source "${source.label}"`
6205
+ };
6206
+ return askOf(`${MARKET_ENABLE_TOOL}${source === void 0 ? "" : ` ${source.url}`} marks a market index source as a publish destination on this machine`, pair(`把${where.zh}设为可发布,允许吗?`, `Make ${where.en} a publish destination. Allow?`), pair(`会把${where.zh}设为可发布,只改本机配置,不碰仓库,允许吗?`, `It makes ${where.en} a publish destination on this machine, without touching the repository. Allow?`));
6207
+ }
6208
+ case MARKET_DISABLE_TOOL: {
6209
+ const id = text("id");
6210
+ const source = marketSource?.(id);
6211
+ if (marketSource !== void 0 && source === void 0) return {
6212
+ kind: "deny",
6213
+ reason: id === void 0 ? "这台机器上还没有选中的索引源 —— 先在「模块市场 → 发现」里添加并选中一个" : `没有这个索引源:${id}`
6214
+ };
6215
+ if (source === void 0) return askOf(`${MARKET_DISABLE_TOOL} stops using a market index source as a publish destination on this machine`, pair("把工作台当前选中的索引源改回只读,允许吗?", "Make the selected workbench index source read-only again. Allow?"), pair("会把工作台当前选中的索引源改回只读,只改本机配置,不碰仓库,允许吗?", "It makes the selected workbench index source read-only again on this machine, without touching the repository. Allow?"));
6216
+ if (source.kind !== "git" || source.policy !== "artifacts-in-repo") return { kind: "allow" };
6217
+ return askOf(`${MARKET_DISABLE_TOOL} ${source.url} stops using a market index source as a publish destination on this machine`, pair(`把工作台的索引源「${source.label}」改回只读,允许吗?`, `Make the workbench index source "${source.label}" read-only again. Allow?`), pair(`会把工作台的索引源「${source.label}」改回只读,只改本机配置,不碰仓库,允许吗?`, `It makes the workbench index source "${source.label}" read-only again on this machine, without touching the repository. Allow?`));
6218
+ }
6049
6219
  default: return;
6050
6220
  }
6051
- };
6221
+ }
6052
6222
  function recordHeldVerdict(call, recording) {
6053
6223
  if (call.name !== "workbench_diy_record_prepare" || recording === void 0) return void 0;
6054
6224
  const held = recording();
@@ -6058,11 +6228,16 @@ function recordHeldVerdict(call, recording) {
6058
6228
  reason: recordRefusalText(held.sessionId === call.session ? "already-held" : "held-by-other", held)
6059
6229
  };
6060
6230
  }
6061
- function panelsApprovalFor(longTaskRuns, recording, publishPreview) {
6231
+ function panelsApprovalFor(longTaskRuns, recording, publishPreview, marketSources) {
6232
+ const sourceOf = marketSources === void 0 ? void 0 : (id) => {
6233
+ const payload = marketSources.read();
6234
+ const wanted = id === void 0 || id === "" ? payload.selected : id;
6235
+ return wanted === null ? void 0 : payload.sources.find((one) => one.id === wanted);
6236
+ };
6062
6237
  return (call) => {
6063
6238
  if (call.name === "workbench_long_tasks_stop") return stopVerdict(call, longTaskRuns);
6064
6239
  if (call.name === "workbench_diy_publish") return publishVerdict(call, publishPreview);
6065
- return recordHeldVerdict(call, recording) ?? panelsApproval(call);
6240
+ return recordHeldVerdict(call, recording) ?? approvalVerdict(call, sourceOf);
6066
6241
  };
6067
6242
  }
6068
6243
  //#endregion
@@ -6128,7 +6303,7 @@ function resolvePanels(manifests, boards, owned = /* @__PURE__ */ new Set()) {
6128
6303
  const order = hasCard ? rank.get(manifest.id) ?? ordered.length + declared(manifest) : 0;
6129
6304
  return {
6130
6305
  id: manifest.id,
6131
- titleKey: manifest.card?.titleKey ?? manifest.id,
6306
+ titleKey: manifest.titleKey,
6132
6307
  order,
6133
6308
  shown: on,
6134
6309
  source: overrideOf(boards.workspace, manifest.id) === void 0 ? "default" : "user",
@@ -6979,7 +7154,7 @@ async function sessionWorkspaceOf(ctx, workspace) {
6979
7154
  dir = parent;
6980
7155
  }
6981
7156
  }
6982
- var OUTPUT$2 = {
7157
+ var OUTPUT$3 = {
6983
7158
  schema: {
6984
7159
  type: "object",
6985
7160
  properties: {
@@ -7124,7 +7299,7 @@ function componentsTool(ctx) {
7124
7299
  },
7125
7300
  additionalProperties: false
7126
7301
  },
7127
- output: OUTPUT$2,
7302
+ output: OUTPUT$3,
7128
7303
  async execute(raw, exec) {
7129
7304
  const { root } = await workspaceOf(ctx, exec);
7130
7305
  const args = asRecord$1(raw);
@@ -7222,97 +7397,6 @@ function registerComponentsTool(ctx, tools) {
7222
7397
  return [tools.register(componentsTool(ctx))];
7223
7398
  }
7224
7399
  //#endregion
7225
- //#region src/shared/api/host/diy-changelog.ts
7226
- function moduleVersionOf(dir) {
7227
- try {
7228
- const version = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).version;
7229
- return typeof version === "string" ? version.trim() : "";
7230
- } catch {
7231
- return "";
7232
- }
7233
- }
7234
- function writeChangelogEntry(dir, input) {
7235
- const file = join(dir, CHANGELOG_FILE);
7236
- const text = appendChangelogEntry(existsSync(file) ? readFileSync(file, "utf8") : changelogSkeleton(), {
7237
- version: input.version,
7238
- kind: input.kind,
7239
- note: input.note,
7240
- date: input.date ?? changelogDate()
7241
- });
7242
- writeFileSync(file, text);
7243
- return {
7244
- file,
7245
- section: changelogSectionOf(text, input.version) ?? "",
7246
- text
7247
- };
7248
- }
7249
- //#endregion
7250
- //#region src/modules/panels/model/diy-changelog.ts
7251
- function toolOf(ctx) {
7252
- return {
7253
- name: DIY_CHANGELOG_TOOL,
7254
- description: `发布前把这一版写进 ${CHANGELOG_FILE}(\`## v0.1.0\` + \`### ✨ 新增\` 这种小节):传 note 就记一条,文件不在就先建。没写这一版时发布会被拦下。`,
7255
- parameters: {
7256
- type: "object",
7257
- properties: {
7258
- unit: {
7259
- type: "string",
7260
- description: "模块 id(workbench_diy_status 会给)。"
7261
- },
7262
- version: {
7263
- type: "string",
7264
- description: "要写的版本;不填用 package.json 里的 version。"
7265
- },
7266
- type: {
7267
- type: "string",
7268
- enum: [...CHANGELOG_KINDS],
7269
- description: "分类:added 新增 / changed 改进 / fixed 修复 / removed 移除 / security 安全 / deprecated 弃用。"
7270
- },
7271
- note: {
7272
- type: "string",
7273
- description: "这一版改了什么;多行时一行一条(必填)。"
7274
- }
7275
- },
7276
- required: ["unit", "note"],
7277
- additionalProperties: false
7278
- },
7279
- output: OUTPUT$2,
7280
- async execute(raw, exec) {
7281
- const { root } = await workspaceOf(ctx, exec);
7282
- const args = asRecord$1(raw);
7283
- const unit = asText(args["unit"]);
7284
- const { units } = unitsOf(ctx, root);
7285
- if (unit === "") throw new Error(`要一个 unit(这个工作区里:${units.map((one) => one.unit).join(", ") || "(空)"})`);
7286
- const found = units.find((one) => one.unit === unit && one.dir !== "");
7287
- if (found === void 0) throw new Error(`这个工作区里没有单元 id 为「${unit}」的模块(可用的:${units.map((one) => one.unit).join(", ") || "(空)"})`);
7288
- if (!insideRoot(root, found.dir)) throw new Error(`这个模块不在这个工作区里(${found.dir})`);
7289
- const note = asText(args["note"]).trim();
7290
- if (note === "") throw new Error("要一句 note:这一版改了什么");
7291
- const version = (asText(args["version"]) || moduleVersionOf(found.dir)).replace(/^v/, "");
7292
- if (!isValidVersion(version)) throw new Error(`版本要写成 x.y.z(收到「${version}」)—— 也可以不填,用 package.json 里的`);
7293
- const kind = asText(args["type"]) || "changed";
7294
- if (!CHANGELOG_KINDS.includes(kind)) throw new Error(`type 只认 ${CHANGELOG_KINDS.join(" / ")}(收到「${kind}」)`);
7295
- const written = writeChangelogEntry(found.dir, {
7296
- version,
7297
- kind,
7298
- note
7299
- });
7300
- return plainJson({
7301
- ok: true,
7302
- unit,
7303
- version,
7304
- type: kind,
7305
- path: written.file,
7306
- section: written.section,
7307
- text: `${CHANGELOG_FILE} 已记下 ${version}(${kind})。发布时会带进包和市场索引,审批屏会展示它。`
7308
- });
7309
- }
7310
- };
7311
- }
7312
- function registerChangelogTool(ctx, tools) {
7313
- return [tools.register(toolOf(ctx))];
7314
- }
7315
- //#endregion
7316
7400
  //#region src/shared/api/host/diy-artifacts.ts
7317
7401
  function artifactsDirOf(ctx, root) {
7318
7402
  const homePath = ctx.get("dshHomePath");
@@ -7599,7 +7683,7 @@ function registerDebugTools(ctx, tools, store) {
7599
7683
  required: [],
7600
7684
  additionalProperties: false
7601
7685
  },
7602
- output: OUTPUT$2,
7686
+ output: OUTPUT$3,
7603
7687
  async execute(raw, exec) {
7604
7688
  const { root, title } = await workspaceOf(ctx, exec);
7605
7689
  const { units } = unitsOf(ctx, root);
@@ -7634,7 +7718,7 @@ function registerDebugTools(ctx, tools, store) {
7634
7718
  name: DIY_DEBUG_PAUSE_TOOL,
7635
7719
  description: "暂停调试(不审批):屏工具停用,不再拿屏当目标。要接着看用 debug_resume。",
7636
7720
  parameters: EMPTY,
7637
- output: OUTPUT$2,
7721
+ output: OUTPUT$3,
7638
7722
  async execute(_raw, exec) {
7639
7723
  const { root } = await workspaceOf(ctx, exec);
7640
7724
  const held = store.live(Date.now());
@@ -7652,7 +7736,7 @@ function registerDebugTools(ctx, tools, store) {
7652
7736
  name: DIY_DEBUG_RESUME_TOOL,
7653
7737
  description: "继续调试(要点头一次):请用户回到这一场调试所在的那一屏。",
7654
7738
  parameters: EMPTY,
7655
- output: OUTPUT$2,
7739
+ output: OUTPUT$3,
7656
7740
  async execute(_raw, exec) {
7657
7741
  const { root } = await workspaceOf(ctx, exec);
7658
7742
  const held = store.live(Date.now());
@@ -7676,7 +7760,7 @@ function registerDebugTools(ctx, tools, store) {
7676
7760
  name: DIY_DEBUG_STOP_TOOL,
7677
7761
  description: "结束调试(不审批):清掉\"占用用户那一屏\"这件事。收工用它。",
7678
7762
  parameters: EMPTY,
7679
- output: OUTPUT$2,
7763
+ output: OUTPUT$3,
7680
7764
  async execute(_raw, exec) {
7681
7765
  const { root } = await workspaceOf(ctx, exec);
7682
7766
  const held = store.live(Date.now());
@@ -7866,7 +7950,7 @@ function registerLogTools(ctx, tools) {
7866
7950
  name: DIY_LOGS_WATCH_TOOL,
7867
7951
  description: "把这一页的控制台听上(每页最多 200 条、单条截 400 字,随页面刷新消失)。",
7868
7952
  parameters: schema,
7869
- output: OUTPUT$2,
7953
+ output: OUTPUT$3,
7870
7954
  async execute(raw, exec) {
7871
7955
  const { page, step } = await run(exec, raw, LOGS_ARM_SCRIPT);
7872
7956
  const error = actErrorOf(step);
@@ -7886,7 +7970,7 @@ function registerLogTools(ctx, tools) {
7886
7970
  name: DIY_LOGS_READ_TOOL,
7887
7971
  description: "读刚才听下来的控制台(最近 50 条;没装钩子会说清,不假装是\"没有报错\")。",
7888
7972
  parameters: schema,
7889
- output: OUTPUT$2,
7973
+ output: OUTPUT$3,
7890
7974
  async execute(raw, exec) {
7891
7975
  const { page, step } = await run(exec, raw, LOGS_READ_SCRIPT);
7892
7976
  const error = actErrorOf(step);
@@ -7905,7 +7989,7 @@ function registerLogTools(ctx, tools) {
7905
7989
  name: DIY_LOGS_STOP_TOOL,
7906
7990
  description: "控制台收听收工:卸掉钩子、扔掉攒下的记录。",
7907
7991
  parameters: schema,
7908
- output: OUTPUT$2,
7992
+ output: OUTPUT$3,
7909
7993
  async execute(raw, exec) {
7910
7994
  const { page, step } = await run(exec, raw, LOGS_STOP_SCRIPT);
7911
7995
  const error = actErrorOf(step);
@@ -8571,7 +8655,7 @@ function registerBrowserTools(ctx, tools) {
8571
8655
  name: row.tool,
8572
8656
  description: row.description,
8573
8657
  parameters: parametersOf(row),
8574
- output: OUTPUT$2,
8658
+ output: OUTPUT$3,
8575
8659
  execute: async (raw, exec) => plainJson(await runBrowserTool(row, ctx, raw, exec))
8576
8660
  };
8577
8661
  return tools.register(definition);
@@ -8610,7 +8694,7 @@ function specTool(ctx) {
8610
8694
  required: ["command"],
8611
8695
  additionalProperties: false
8612
8696
  },
8613
- output: OUTPUT$2,
8697
+ output: OUTPUT$3,
8614
8698
  async execute(raw, _exec) {
8615
8699
  const args = asRecord$1(raw);
8616
8700
  const argv = specArgvOf(asText(args["command"]));
@@ -8637,6 +8721,176 @@ function registerSpecTool(ctx, tools) {
8637
8721
  return [tools.register(specTool(ctx))];
8638
8722
  }
8639
8723
  //#endregion
8724
+ //#region src/modules/panels/model/diy-publish.ts
8725
+ var PUBLISH_TIMEOUT_MS = 18e5;
8726
+ function registerPublishTool(ctx, tools, deps) {
8727
+ const publishTool = {
8728
+ name: DIY_PUBLISH_TOOL,
8729
+ description: "用户说“发布”后才用:把这一格发到 target 指定的出口(不填按环境默认)。这一版还没有更新日志时把 changelogNote 一起带上 —— 发布时写进 CHANGELOG.md,随包发。",
8730
+ parameters: {
8731
+ type: "object",
8732
+ properties: {
8733
+ unit: {
8734
+ type: "string",
8735
+ description: "模块 id(workbench_diy_status 会给)。"
8736
+ },
8737
+ changelogNote: {
8738
+ type: "string",
8739
+ description: "这一版改了什么,一行一条;这一版还没有更新日志时必填(文件不在会先建)。"
8740
+ },
8741
+ changelogType: {
8742
+ type: "string",
8743
+ enum: [...CHANGELOG_KINDS],
8744
+ description: "分类,不填按 changed。"
8745
+ },
8746
+ target: {
8747
+ type: "string",
8748
+ enum: [
8749
+ "team",
8750
+ "public",
8751
+ "tarball",
8752
+ "git",
8753
+ "market",
8754
+ "local"
8755
+ ],
8756
+ description: "发到哪:团队源 / 公共 npm / 安装包 / git / 模块市场索引;不填按环境默认(有可写索引走市场,有私源走团队源,否则安装包)。"
8757
+ },
8758
+ version: {
8759
+ type: "string",
8760
+ description: "可选:patch / minor / major / x.y.z;不填首次用当前版本,之后自动 patch。"
8761
+ },
8762
+ source: {
8763
+ type: "string",
8764
+ description: "发布到哪个索引源(workbench_market_sources 会给 id);不填用唯一可发布的源。"
8765
+ },
8766
+ verify: {
8767
+ type: "boolean",
8768
+ description: "只核已发布的包(交接验证码后用),不再发;不用审批。"
8769
+ },
8770
+ resume: {
8771
+ type: "boolean",
8772
+ description: "中断后接着发(用记录里的出口与版本)。"
8773
+ },
8774
+ license: {
8775
+ type: "string",
8776
+ description: "公开发布用的许可证(MIT / Apache-2.0 / 仓库已有 LICENSE)。"
8777
+ },
8778
+ author: {
8779
+ type: "string",
8780
+ description: "署谁的名(姓名,或 姓名 <邮箱>);用户点了名才传,不传按模块里写的,没写用推断的署名。"
8781
+ },
8782
+ refresh: {
8783
+ type: "boolean",
8784
+ description: "只刷市集元数据(标题/作者/说明),不升版本、不重发产物。"
8785
+ }
8786
+ },
8787
+ required: ["unit"],
8788
+ additionalProperties: false
8789
+ },
8790
+ output: OUTPUT$3,
8791
+ async execute(raw, exec) {
8792
+ const { root } = await workspaceOf(ctx, exec);
8793
+ const found = deps.resolve(root, raw);
8794
+ const shell = ctx.get("shell");
8795
+ if (shell === void 0) throw new Error("这台机器上没有 shell 服务,发布跑不起来");
8796
+ const bin = toolchainBinOf(root) ?? toolchainBinOf(found.dir);
8797
+ if (bin === null) throw new Error(`这个工作区里没有 dsh-wb-module(${join(root, "node_modules")} 下没有)—— 先 pnpm install`);
8798
+ const target = asText(asRecord$1(raw)["target"]);
8799
+ const source = asText(asRecord$1(raw)["source"]);
8800
+ const version = asText(asRecord$1(raw)["version"]);
8801
+ const license = asText(asRecord$1(raw)["license"]);
8802
+ const changelogNote = asText(asRecord$1(raw)["changelogNote"]);
8803
+ const changelogType = asText(asRecord$1(raw)["changelogType"]);
8804
+ let author = asText(asRecord$1(raw)["author"]);
8805
+ const verify = asRecord$1(raw)["verify"] === true;
8806
+ const resume = asRecord$1(raw)["resume"] === true;
8807
+ if (!verify && target !== "local" && author === "" && (deps.previewOf(found.unit)?.author ?? "") === "") author = await deps.inferredAuthorOf(found.unit) ?? "";
8808
+ const argv = [
8809
+ bin,
8810
+ "release",
8811
+ "--cwd",
8812
+ found.dir,
8813
+ "--json"
8814
+ ];
8815
+ if (verify) argv.push("--verify");
8816
+ else argv.push("--yes");
8817
+ if (resume) argv.push("--resume");
8818
+ if (target !== "") argv.push("--target", target);
8819
+ if (source !== "") argv.push("--index-source", source);
8820
+ if (version !== "") argv.push("--version", version);
8821
+ if (license !== "") argv.push("--license", license);
8822
+ if (author !== "") argv.push("--author", author);
8823
+ if (changelogNote !== "") argv.push("--changelog-note", changelogNote);
8824
+ if (changelogType !== "") argv.push("--changelog-type", changelogType);
8825
+ if (asRecord$1(raw)["refresh"] === true) argv.push("--refresh");
8826
+ const result = await runShell(shell, argv.map((one) => quoteShellArg(one)).join(" "), {
8827
+ workdir: root,
8828
+ timeoutMs: PUBLISH_TIMEOUT_MS
8829
+ });
8830
+ const payload = jsonOf(result.stdout);
8831
+ if (result.exitCode === 3 && payload !== void 0) {
8832
+ const needs = asRecord$1(payload["needsUser"]);
8833
+ const stepText = asText(needs["text"]);
8834
+ const command = asText(needs["command"]);
8835
+ return plainJson({
8836
+ ...payload,
8837
+ text: [
8838
+ `还差一步:${stepText}`,
8839
+ ...command === "" ? [] : [`命令:${command}`],
8840
+ "做完跟我说「好了」,我接着发。"
8841
+ ].join("\n")
8842
+ });
8843
+ }
8844
+ if (result.exitCode === 4 && payload !== void 0) {
8845
+ const resumeInfo = asRecord$1(payload["resume"]);
8846
+ return plainJson({
8847
+ ...payload,
8848
+ text: [`发布中断在第 ${asText(resumeInfo["phase"])} 步:${asText(payload["text"])}`, `再调一次 workbench_diy_publish(unit=${found.unit}, resume=true)就能接着发。`].join("\n")
8849
+ });
8850
+ }
8851
+ if (result.exitCode !== 0 || payload === void 0) {
8852
+ const tail = `${result.stderr}\n${result.stdout}`.trim().split("\n").slice(-6).join(" | ");
8853
+ throw new Error(`发布没走通(exit ${String(result.exitCode)})${tail === "" ? "" : `:${tail}`}`);
8854
+ }
8855
+ const name = asText(payload["name"]);
8856
+ const released = asText(payload["version"]);
8857
+ const registry = asText(payload["registry"]);
8858
+ const file = asText(payload["file"]);
8859
+ const install = asText(payload["install"]);
8860
+ const git = asRecord$1(payload["git"]);
8861
+ const targetOfPayload = asText(payload["target"]);
8862
+ const text = verify ? [
8863
+ `已核过 ${name}@${released}`,
8864
+ `安装:${install}`,
8865
+ "没有重新发布。"
8866
+ ].join("\n") : payload["refresh"] === true ? [payload["refreshed"] === true ? `已刷新 ${name} 的市集元数据(版本 ${released} 不变)` : `${name} 的市集元数据本来就是这样,没动`, `市场索引:${asText(asRecord$1(payload["market"])["url"])}`].join("\n") : targetOfPayload === "tarball" ? [
8867
+ `安装包在 ${file}`,
8868
+ `本机装:${install}`,
8869
+ "文件可以从对话里带走,发给同事。"
8870
+ ].join("\n") : targetOfPayload === "git" ? [
8871
+ `已推到 ${asText(git["url"])}(tag ${asText(git["tag"])})`,
8872
+ `安装:${install}`,
8873
+ "已核过 tag 里的产物。"
8874
+ ].join("\n") : targetOfPayload === "market" ? [
8875
+ `已发到市场索引 ${asText(asRecord$1(payload["market"])["url"])}`,
8876
+ `安装:${install}`,
8877
+ "已从索引仓库侧核过产物。"
8878
+ ].join("\n") : targetOfPayload === "local" ? "没发,留在本机。" : [
8879
+ `已发布 ${name}@${released} → ${registry}`,
8880
+ `同事安装:${install}`,
8881
+ "包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
8882
+ ].join("\n");
8883
+ const changelog = asRecord$1(payload["changelog"]);
8884
+ const changelogLine = asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "";
8885
+ return plainJson({
8886
+ ...payload,
8887
+ text: changelogLine === "" ? text : `${text}\n${changelogLine}`
8888
+ });
8889
+ }
8890
+ };
8891
+ return tools.register(publishTool);
8892
+ }
8893
+ //#endregion
8640
8894
  //#region src/shared/api/host/diy-recon.ts
8641
8895
  var SITE_LIMIT = 6;
8642
8896
  var COMMAND_LIMIT = 12;
@@ -9077,7 +9331,7 @@ function renderStatus(root, lines, options = {}) {
9077
9331
  }
9078
9332
  function publishStatusOf(preview) {
9079
9333
  if (preview === null) return "";
9080
- 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} 这一版 —— 先 workbench_diy_changelog(unit=…)` : `发布前置:CHANGELOG 里已有 ${preview.changelog.version}`].filter((one) => one !== void 0).join("\n");
9334
+ 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");
9081
9335
  }
9082
9336
  function renderList(root, modules, shape = "bare") {
9083
9337
  if (modules.length === 0) return [
@@ -9092,7 +9346,6 @@ function renderList(root, modules, shape = "bare") {
9092
9346
  }
9093
9347
  //#endregion
9094
9348
  //#region src/modules/panels/model/diy-tools.ts
9095
- var PUBLISH_TIMEOUT_MS = 18e5;
9096
9349
  function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, inferredAuthorOf = async () => void 0) {
9097
9350
  const supply = supplyPortOf(ctx);
9098
9351
  const tools = ctx.get("tools");
@@ -9112,7 +9365,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9112
9365
  required: [],
9113
9366
  additionalProperties: false
9114
9367
  },
9115
- output: OUTPUT$2,
9368
+ output: OUTPUT$3,
9116
9369
  async execute(raw, exec) {
9117
9370
  const { root } = await workspaceOf(ctx, exec);
9118
9371
  const { units } = unitsOf(ctx, root);
@@ -9155,7 +9408,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9155
9408
  } },
9156
9409
  additionalProperties: false
9157
9410
  },
9158
- output: OUTPUT$2,
9411
+ output: OUTPUT$3,
9159
9412
  async execute(raw, exec) {
9160
9413
  const { root } = await workspaceOf(ctx, exec);
9161
9414
  const { installed, units } = unitsOf(ctx, root);
@@ -9221,7 +9474,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9221
9474
  required: ["keyword"],
9222
9475
  additionalProperties: false
9223
9476
  },
9224
- output: OUTPUT$2,
9477
+ output: OUTPUT$3,
9225
9478
  async execute(raw, _exec) {
9226
9479
  const args = asRecord$1(raw);
9227
9480
  const keywords = keywordsOf(asText(args["keyword"]), asText(args["url"]));
@@ -9263,7 +9516,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9263
9516
  required: ["unit"],
9264
9517
  additionalProperties: false
9265
9518
  },
9266
- output: OUTPUT$2,
9519
+ output: OUTPUT$3,
9267
9520
  async execute(raw, exec) {
9268
9521
  const { root } = await workspaceOf(ctx, exec);
9269
9522
  const { units } = unitsOf(ctx, root);
@@ -9292,7 +9545,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9292
9545
  required: ["unit"],
9293
9546
  additionalProperties: false
9294
9547
  },
9295
- output: OUTPUT$2,
9548
+ output: OUTPUT$3,
9296
9549
  async execute(raw, exec) {
9297
9550
  const { root } = await workspaceOf(ctx, exec);
9298
9551
  const found = unitOf(root, raw, "workbench_diy_install");
@@ -9332,162 +9585,22 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, in
9332
9585
  });
9333
9586
  }
9334
9587
  };
9335
- const publishTool = {
9336
- name: DIY_PUBLISH_TOOL,
9337
- description: "用户说“发布”后才用:把这一格发到 target 指定的出口(不填按环境默认)。",
9338
- parameters: {
9339
- type: "object",
9340
- properties: {
9341
- unit: {
9342
- type: "string",
9343
- description: "模块 id(workbench_diy_status 会给)。"
9344
- },
9345
- target: {
9346
- type: "string",
9347
- enum: [
9348
- "team",
9349
- "public",
9350
- "tarball",
9351
- "git",
9352
- "market",
9353
- "local"
9354
- ],
9355
- description: "发到哪:团队源 / 公共 npm / 安装包 / git / 模块市场索引;不填按环境默认(有可写索引走市场,有私源走团队源,否则安装包)。"
9356
- },
9357
- version: {
9358
- type: "string",
9359
- description: "可选:patch / minor / major / x.y.z;不填首次用当前版本,之后自动 patch。"
9360
- },
9361
- verify: {
9362
- type: "boolean",
9363
- description: "只核已发布的包(交接验证码后用),不再发;不用审批。"
9364
- },
9365
- resume: {
9366
- type: "boolean",
9367
- description: "中断后接着发(用记录里的出口与版本)。"
9368
- },
9369
- license: {
9370
- type: "string",
9371
- description: "公开发布用的许可证(MIT / Apache-2.0 / 仓库已有 LICENSE)。"
9372
- },
9373
- author: {
9374
- type: "string",
9375
- description: "署谁的名(姓名,或 姓名 <邮箱>);用户点了名才传,不传按模块里写的,没写用推断的署名。"
9376
- },
9377
- refresh: {
9378
- type: "boolean",
9379
- description: "只刷市集元数据(标题/作者/说明),不升版本、不重发产物。"
9380
- }
9381
- },
9382
- required: ["unit"],
9383
- additionalProperties: false
9384
- },
9385
- output: OUTPUT$2,
9386
- async execute(raw, exec) {
9387
- const { root } = await workspaceOf(ctx, exec);
9388
- const found = unitOf(root, raw, "workbench_diy_publish");
9389
- const shell = ctx.get("shell");
9390
- if (shell === void 0) throw new Error("这台机器上没有 shell 服务,发布跑不起来");
9391
- const bin = toolchainBinOf(root) ?? toolchainBinOf(found.dir);
9392
- if (bin === null) throw new Error(`这个工作区里没有 dsh-wb-module(${join(root, "node_modules")} 下没有)—— 先 pnpm install`);
9393
- const target = asText(asRecord$1(raw)["target"]);
9394
- const version = asText(asRecord$1(raw)["version"]);
9395
- const license = asText(asRecord$1(raw)["license"]);
9396
- let author = asText(asRecord$1(raw)["author"]);
9397
- const verify = asRecord$1(raw)["verify"] === true;
9398
- const resume = asRecord$1(raw)["resume"] === true;
9399
- if (!verify && target !== "local" && author === "" && (previewOf(found.unit)?.author ?? "") === "") author = await inferredAuthorOf(found.unit) ?? "";
9400
- const argv = [
9401
- bin,
9402
- "release",
9403
- "--cwd",
9404
- found.dir,
9405
- "--json"
9406
- ];
9407
- if (verify) argv.push("--verify");
9408
- else argv.push("--yes");
9409
- if (resume) argv.push("--resume");
9410
- if (target !== "") argv.push("--target", target);
9411
- if (version !== "") argv.push("--version", version);
9412
- if (license !== "") argv.push("--license", license);
9413
- if (author !== "") argv.push("--author", author);
9414
- if (asRecord$1(raw)["refresh"] === true) argv.push("--refresh");
9415
- const result = await runShell(shell, argv.map((one) => quoteShellArg(one)).join(" "), {
9416
- workdir: root,
9417
- timeoutMs: PUBLISH_TIMEOUT_MS
9418
- });
9419
- const payload = jsonOf(result.stdout);
9420
- if (result.exitCode === 3 && payload !== void 0) {
9421
- const needs = asRecord$1(payload["needsUser"]);
9422
- const stepText = asText(needs["text"]);
9423
- const command = asText(needs["command"]);
9424
- return plainJson({
9425
- ...payload,
9426
- text: [
9427
- `还差一步:${stepText}`,
9428
- ...command === "" ? [] : [`命令:${command}`],
9429
- "做完跟我说「好了」,我接着发。"
9430
- ].join("\n")
9431
- });
9432
- }
9433
- if (result.exitCode === 4 && payload !== void 0) {
9434
- const resumeInfo = asRecord$1(payload["resume"]);
9435
- return plainJson({
9436
- ...payload,
9437
- text: [`发布中断在第 ${asText(resumeInfo["phase"])} 步:${asText(payload["text"])}`, `再调一次 workbench_diy_publish(unit=${found.unit}, resume=true)就能接着发。`].join("\n")
9438
- });
9439
- }
9440
- if (result.exitCode !== 0 || payload === void 0) {
9441
- const tail = `${result.stderr}\n${result.stdout}`.trim().split("\n").slice(-6).join(" | ");
9442
- throw new Error(`发布没走通(exit ${String(result.exitCode)})${tail === "" ? "" : `:${tail}`}`);
9443
- }
9444
- const name = asText(payload["name"]);
9445
- const released = asText(payload["version"]);
9446
- const registry = asText(payload["registry"]);
9447
- const file = asText(payload["file"]);
9448
- const install = asText(payload["install"]);
9449
- const git = asRecord$1(payload["git"]);
9450
- const targetOfPayload = asText(payload["target"]);
9451
- const text = verify ? [
9452
- `已核过 ${name}@${released}`,
9453
- `安装:${install}`,
9454
- "没有重新发布。"
9455
- ].join("\n") : payload["refresh"] === true ? [payload["refreshed"] === true ? `已刷新 ${name} 的市集元数据(版本 ${released} 不变)` : `${name} 的市集元数据本来就是这样,没动`, `市场索引:${asText(asRecord$1(payload["market"])["url"])}`].join("\n") : targetOfPayload === "tarball" ? [
9456
- `安装包在 ${file}`,
9457
- `本机装:${install}`,
9458
- "文件可以从对话里带走,发给同事。"
9459
- ].join("\n") : targetOfPayload === "git" ? [
9460
- `已推到 ${asText(git["url"])}(tag ${asText(git["tag"])})`,
9461
- `安装:${install}`,
9462
- "已核过 tag 里的产物。"
9463
- ].join("\n") : targetOfPayload === "market" ? [
9464
- `已发到市场索引 ${asText(asRecord$1(payload["market"])["url"])}`,
9465
- `安装:${install}`,
9466
- "已从索引仓库侧核过产物。"
9467
- ].join("\n") : targetOfPayload === "local" ? "没发,留在本机。" : [
9468
- `已发布 ${name}@${released} → ${registry}`,
9469
- `同事安装:${install}`,
9470
- "包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
9471
- ].join("\n");
9472
- return plainJson({
9473
- ...payload,
9474
- text
9475
- });
9476
- }
9477
- };
9478
9588
  return [
9479
9589
  tools.register(statusTool),
9480
9590
  tools.register(panelTool),
9481
9591
  ...registerComponentsTool(ctx, tools),
9482
9592
  tools.register(reconTool),
9483
9593
  ...registerSpecTool(ctx, tools),
9484
- ...registerChangelogTool(ctx, tools),
9485
9594
  ...registerLogTools(ctx, tools),
9486
9595
  ...registerBrowserTools(ctx, tools),
9487
9596
  ...registerDebugTools(ctx, tools, debug),
9488
9597
  tools.register(showTool),
9489
9598
  tools.register(installTool),
9490
- tools.register(publishTool)
9599
+ registerPublishTool(ctx, tools, {
9600
+ resolve: (root, raw) => unitOf(root, raw, "workbench_diy_publish"),
9601
+ previewOf,
9602
+ inferredAuthorOf
9603
+ })
9491
9604
  ];
9492
9605
  }
9493
9606
  //#endregion
@@ -9517,27 +9630,22 @@ function selectedGitSource() {
9517
9630
  return parsed.source.policy === "artifacts-in-repo" ? parsed.source : void 0;
9518
9631
  }
9519
9632
  }
9520
- function publishedEntryOf(source, name) {
9633
+ var EMPTY_INDEX = {
9634
+ format: 1,
9635
+ plugins: []
9636
+ };
9637
+ function publishedIndexOf(source) {
9521
9638
  const file = join(workbenchRoot(), "market-index", marketSourceKey(source), "worktree", source.path ?? "market.json");
9522
9639
  if (!existsSync(file)) return void 0;
9523
9640
  try {
9524
9641
  const parsed = parseMarketIndexJson(readFileSync(file, "utf8"));
9525
- if (!parsed.ok) return void 0;
9526
- const entry = parsed.index.plugins.find((one) => one.name === name);
9527
- if (entry === void 0) return {
9528
- versions: [],
9529
- author: ""
9530
- };
9531
- return {
9532
- versions: entry.versions.map((one) => one.version),
9533
- author: typeof entry.author === "string" ? entry.author.trim() : ""
9534
- };
9642
+ return parsed.ok ? parsed.index : void 0;
9535
9643
  } catch {
9536
9644
  return;
9537
9645
  }
9538
9646
  }
9539
- function publishedVersionsOf(source, name) {
9540
- return publishedEntryOf(source, name)?.versions;
9647
+ function publishedEntryOf(index, name) {
9648
+ return index?.plugins.find((one) => one.name === name);
9541
9649
  }
9542
9650
  function bumped(version, kind) {
9543
9651
  const match = SEMVER.exec(version);
@@ -9562,17 +9670,21 @@ function nextVersionOf(version, published) {
9562
9670
  if (!exists(candidate)) return candidate;
9563
9671
  }
9564
9672
  }
9565
- function marketOf(unit, dir, name, version) {
9673
+ function marketOf(dir, name, version) {
9566
9674
  const source = selectedGitSource();
9567
9675
  if (source === void 0) return void 0;
9568
- const nextVersion = nextVersionOf(version, publishedVersionsOf(source, name));
9676
+ const index = publishedIndexOf(source);
9677
+ const nextVersion = nextVersionOf(version, publishedEntryOf(index, name)?.versions.map((one) => one.version));
9569
9678
  return {
9570
- url: source.url,
9571
- ...source.ref === void 0 ? {} : { ref: source.ref },
9572
- indexPath: source.path ?? "market.json",
9573
- artifactPath: `packages/${unit}`,
9574
- files: ARTIFACTS.filter((one) => existsSync(join(dir, one))),
9575
- ...nextVersion === void 0 ? {} : { nextVersion }
9679
+ market: {
9680
+ url: source.url,
9681
+ ...source.ref === void 0 ? {} : { ref: source.ref },
9682
+ indexPath: source.path ?? "market.json",
9683
+ artifactPath: marketArtifactPath(name),
9684
+ files: ARTIFACTS.filter((one) => existsSync(join(dir, one))),
9685
+ ...nextVersion === void 0 ? {} : { nextVersion }
9686
+ },
9687
+ ...index === void 0 ? {} : { index }
9576
9688
  };
9577
9689
  }
9578
9690
  function moduleFactOf(root, unit) {
@@ -9631,7 +9743,7 @@ async function inferredAuthorOf(root, unit, shell) {
9631
9743
  if (fact.author !== "") return fact.author;
9632
9744
  const source = selectedGitSource();
9633
9745
  if (source !== void 0 && fact.name !== "") {
9634
- const published = publishedEntryOf(source, fact.name)?.author ?? "";
9746
+ const published = publishedEntryOf(publishedIndexOf(source), fact.name)?.author ?? "";
9635
9747
  if (published !== "") return published;
9636
9748
  }
9637
9749
  const sibling = root === null || root === void 0 ? void 0 : siblingAuthorOf(root, unit);
@@ -9657,17 +9769,24 @@ function publishPreviewOf(root, unit, options = {}) {
9657
9769
  if (name === "" || version === "") return null;
9658
9770
  const publishConfig = record["publishConfig"];
9659
9771
  const registry = typeof publishConfig === "object" && publishConfig !== null ? publishConfig["registry"] : void 0;
9660
- const market = marketOf(found.unit, found.dir, name, version);
9661
- const target = targetVersionOf(version, (options.version ?? "").trim(), market);
9772
+ const market = marketOf(found.dir, name, version);
9773
+ const target = targetVersionOf(version, (options.version ?? "").trim(), market?.market);
9662
9774
  const askedAuthor = (options.author ?? "").trim();
9663
9775
  const author = askedAuthor === "" ? authorOf(record) : askedAuthor;
9776
+ const claim = market === void 0 ? void 0 : claimOf(market.index ?? EMPTY_INDEX, {
9777
+ name,
9778
+ publisher: author,
9779
+ unit,
9780
+ artifactPath: marketArtifactPath(name)
9781
+ });
9664
9782
  return {
9665
9783
  name,
9666
9784
  version,
9667
9785
  ...author === "" ? {} : { author },
9668
9786
  ...typeof registry === "string" && registry !== "" ? { registry } : {},
9669
- ...market === void 0 ? {} : { market },
9670
- changelog: changelogOf(found.dir, target)
9787
+ ...market === void 0 ? {} : { market: market.market },
9788
+ ...claim === void 0 ? {} : { claim },
9789
+ changelog: changelogOf(found.dir, target, (options.changelog ?? "").trim(), kindOf(options.changelogType))
9671
9790
  };
9672
9791
  }
9673
9792
  function authorOf(manifest) {
@@ -9685,13 +9804,30 @@ function targetVersionOf(version, asked, market) {
9685
9804
  if (/^(major|minor|patch)$/.test(asked)) return bumped(version, asked) ?? version;
9686
9805
  return asked;
9687
9806
  }
9688
- function changelogOf(dir, version) {
9807
+ function kindOf(given) {
9808
+ const wanted = (given ?? "").trim();
9809
+ return CHANGELOG_KINDS.includes(wanted) ? wanted : "changed";
9810
+ }
9811
+ function changelogOf(dir, version, note, kind) {
9689
9812
  let text;
9690
9813
  try {
9691
9814
  text = readFileSync(join(dir, CHANGELOG_FILE), "utf8");
9692
9815
  } catch {
9693
9816
  text = void 0;
9694
9817
  }
9818
+ if (note !== "") {
9819
+ const plan = planChangelog(text ?? "", {
9820
+ version,
9821
+ kind,
9822
+ note,
9823
+ date: changelogDate()
9824
+ });
9825
+ if (plan !== void 0) return {
9826
+ version: plan.version,
9827
+ text: plan.section,
9828
+ ...plan.action === "written" ? { pending: true } : {}
9829
+ };
9830
+ }
9695
9831
  if (text === void 0 || !changelogHasEntry(text, version)) return {
9696
9832
  version,
9697
9833
  missing: true
@@ -11014,7 +11150,7 @@ function registerRecordTools(ctx, store) {
11014
11150
  required: ["url"],
11015
11151
  additionalProperties: false
11016
11152
  },
11017
- output: OUTPUT$2,
11153
+ output: OUTPUT$3,
11018
11154
  async execute(raw, exec) {
11019
11155
  const { root } = await workspaceOf(ctx, exec);
11020
11156
  const artifactsDir = artifactsDirOf(ctx, root);
@@ -11114,7 +11250,7 @@ function registerRecordTools(ctx, store) {
11114
11250
  } },
11115
11251
  additionalProperties: false
11116
11252
  },
11117
- output: OUTPUT$2,
11253
+ output: OUTPUT$3,
11118
11254
  async execute(raw, exec) {
11119
11255
  const shell = ctx.get("shell");
11120
11256
  if (shell === void 0) throw new Error("这台机器上没有 shell 服务,读不了录制");
@@ -11244,7 +11380,7 @@ function registerRecordTools(ctx, store) {
11244
11380
  },
11245
11381
  additionalProperties: false
11246
11382
  },
11247
- output: OUTPUT$2,
11383
+ output: OUTPUT$3,
11248
11384
  async execute(raw, exec) {
11249
11385
  const { root } = await workspaceOf(ctx, exec);
11250
11386
  const artifactsDir = artifactsDirOf(ctx, root);
@@ -11326,7 +11462,7 @@ function registerRecordTools(ctx, store) {
11326
11462
  properties: {},
11327
11463
  additionalProperties: false
11328
11464
  },
11329
- output: OUTPUT$2,
11465
+ output: OUTPUT$3,
11330
11466
  async execute(_raw, exec) {
11331
11467
  const shell = ctx.get("shell");
11332
11468
  if (shell === void 0) throw new Error("这台机器上没有 shell 服务,收不了工");
@@ -12236,7 +12372,7 @@ function createMarketService(options) {
12236
12372
  const manifest = requireEntry(unit);
12237
12373
  return {
12238
12374
  unit,
12239
- titleKey: manifest.card?.titleKey ?? unit,
12375
+ titleKey: manifest.titleKey,
12240
12376
  needs: needsOf(manifest),
12241
12377
  capability: await capabilityOf(unit, force)
12242
12378
  };
@@ -12567,6 +12703,7 @@ function normalizedRepo(value) {
12567
12703
  return value.trim().replace(/^git\+/, "").replace(/\.git$/, "").toLowerCase();
12568
12704
  }
12569
12705
  function isWorkbenchEntry(entry) {
12706
+ if (entry.metadata?.workbench === true) return true;
12570
12707
  const labels = marketEntryLabels(entry);
12571
12708
  if (labels.includes("workbench")) return true;
12572
12709
  return entry.metadata?.category === "workbench" && labels.length === 0;
@@ -12824,37 +12961,66 @@ function parseMarketSourcesAction(payload) {
12824
12961
  id: id.trim()
12825
12962
  };
12826
12963
  }
12964
+ if (action === "read-only") {
12965
+ const id = record["id"];
12966
+ if (typeof id !== "string" || id.trim() === "") throw new Error("read-only 需要 id");
12967
+ return {
12968
+ action,
12969
+ id: id.trim()
12970
+ };
12971
+ }
12827
12972
  if (action === "init") {
12828
12973
  const id = record["id"];
12829
12974
  if (typeof id !== "string" || id.trim() === "") throw new Error("init 需要 id");
12830
12975
  const ref = typeof record["ref"] === "string" ? record["ref"].trim() : "";
12831
12976
  if (ref !== "" && !marketBranchOk(ref)) throw new Error(`分支名不合格:${ref}`);
12977
+ const stampOnly = record["stampOnly"] === true;
12832
12978
  return {
12833
12979
  action,
12834
12980
  id: id.trim(),
12835
- ...ref === "" ? {} : { ref }
12981
+ ...ref === "" ? {} : { ref },
12982
+ ...stampOnly ? { stampOnly } : {}
12836
12983
  };
12837
12984
  }
12838
12985
  throw new Error(`不认识的索引源动作:${String(action)}`);
12839
12986
  }
12987
+ async function markSourceReadOnly(store, id) {
12988
+ const source = store.read().sources.find((one) => one.id === id);
12989
+ if (source === void 0) throw new Error("这个索引源已经不在了 —— 刷新后再试");
12990
+ if (source.kind !== "git") throw new Error("http 索引源本来就是只读");
12991
+ const next = {
12992
+ ...source,
12993
+ policy: "catalog-only"
12994
+ };
12995
+ if (source.policy !== "catalog-only") await store.apply({
12996
+ action: "save",
12997
+ source: next
12998
+ });
12999
+ return next;
13000
+ }
12840
13001
  function marketSourcesAction(store, init) {
12841
13002
  return {
12842
13003
  resource: MARKET_SOURCES_RESOURCE,
12843
13004
  async handle(payload) {
12844
13005
  const action = parseMarketSourcesAction(payload);
13006
+ if (action.action === "read-only") {
13007
+ const source = await markSourceReadOnly(store, action.id);
13008
+ return {
13009
+ ok: true,
13010
+ ...store.read(),
13011
+ source
13012
+ };
13013
+ }
12845
13014
  if (action.action === "init") {
12846
13015
  if (init === void 0) throw new Error("这台机器上没有 shell 服务,初始化不了索引仓库");
12847
13016
  const result = await init({
12848
13017
  id: action.id,
12849
- ...action.ref === void 0 ? {} : { ref: action.ref }
13018
+ ...action.ref === void 0 ? {} : { ref: action.ref },
13019
+ ...action.stampOnly === true ? { stampOnly: true } : {}
12850
13020
  });
12851
13021
  return {
12852
13022
  ok: true,
12853
- ...await store.apply({
12854
- action: "save",
12855
- source: result.source,
12856
- select: true
12857
- }),
13023
+ ...store.read(),
12858
13024
  branch: result.branch,
12859
13025
  commit: result.commit,
12860
13026
  created: result.created
@@ -12874,20 +13040,29 @@ function publishPreviewRoutes(workshop, inferredAuthorOf = async () => void 0) {
12874
13040
  const unit = (param(search, "unit") ?? "").trim();
12875
13041
  const version = (param(search, "version") ?? "").trim();
12876
13042
  const author = (param(search, "author") ?? "").trim();
13043
+ const changelog = param(search, "changelog") ?? "";
13044
+ const changelogType = (param(search, "changelogType") ?? "").trim();
12877
13045
  if (unit === "") return {
12878
13046
  ok: true,
12879
13047
  preview: null
12880
13048
  };
12881
13049
  let preview = publishPreviewOf(workshop, unit, {
12882
13050
  version,
12883
- author
13051
+ author,
13052
+ changelog,
13053
+ changelogType
12884
13054
  });
12885
13055
  if (preview !== null && author === "" && (preview.author === void 0 || preview.author === "")) {
12886
13056
  const inferred = await inferredAuthorOf(unit);
12887
- if (inferred !== void 0 && inferred !== "") preview = {
12888
- ...preview,
12889
- author: inferred
12890
- };
13057
+ if (inferred !== void 0 && inferred !== "") {
13058
+ const again = publishPreviewOf(workshop, unit, {
13059
+ version,
13060
+ author: inferred,
13061
+ changelog,
13062
+ changelogType
13063
+ });
13064
+ if (again !== null) preview = again;
13065
+ }
12891
13066
  }
12892
13067
  return {
12893
13068
  ok: true,
@@ -13514,17 +13689,21 @@ function createMarketSourceInitializer(options) {
13514
13689
  if (created.exitCode !== 0) throw new Error(`建分支 ${branch} 没过:${tailLines(created.stderr, 4)}`);
13515
13690
  }
13516
13691
  const indexPath = (source.path ?? "market.json").replace(/^\/+/, "") || "market.json";
13517
- const written = [];
13518
13692
  const indexFile = join(workdir, indexPath);
13519
- if (!existsSync(indexFile)) {
13520
- mkdirSync(dirname(indexFile), { recursive: true });
13521
- writeFileSync(indexFile, MARKET_INDEX_SEED);
13522
- written.push(indexPath);
13523
- }
13524
- const readmeFile = join(workdir, "README.md");
13525
- if (!existsSync(readmeFile)) {
13526
- writeFileSync(readmeFile, MARKET_INDEX_README_LINES.join("\n"));
13527
- written.push("README.md");
13693
+ const stampOnly = request.stampOnly === true;
13694
+ if (stampOnly && !existsSync(indexFile)) throw new Error(`仓库里还没有 ${indexPath} —— 先选中这个源,按发现页的提示把它初始化成索引`);
13695
+ const written = [];
13696
+ if (!stampOnly) {
13697
+ if (!existsSync(indexFile)) {
13698
+ mkdirSync(dirname(indexFile), { recursive: true });
13699
+ writeFileSync(indexFile, MARKET_INDEX_SEED);
13700
+ written.push(indexPath);
13701
+ }
13702
+ const readmeFile = join(workdir, "README.md");
13703
+ if (!existsSync(readmeFile)) {
13704
+ writeFileSync(readmeFile, MARKET_INDEX_README_LINES.join("\n"));
13705
+ written.push("README.md");
13706
+ }
13528
13707
  }
13529
13708
  if (written.length > 0) {
13530
13709
  const added = await git(["add", ...written], workdir);
@@ -13551,8 +13730,7 @@ function createMarketSourceInitializer(options) {
13551
13730
  };
13552
13731
  await options.store.apply({
13553
13732
  action: "save",
13554
- source: next,
13555
- select: true
13733
+ source: next
13556
13734
  });
13557
13735
  forget(source, next);
13558
13736
  return {
@@ -13679,6 +13857,73 @@ async function dispatchMarket(market, request) {
13679
13857
  };
13680
13858
  }
13681
13859
  //#endregion
13860
+ //#region src/modules/panels/model/market-sources-tool.ts
13861
+ var OUTPUT$2 = {
13862
+ schema: {
13863
+ type: "object",
13864
+ properties: {
13865
+ text: { type: "string" },
13866
+ sources: {
13867
+ type: "array",
13868
+ items: {
13869
+ type: "object",
13870
+ additionalProperties: true
13871
+ }
13872
+ }
13873
+ },
13874
+ additionalProperties: true
13875
+ },
13876
+ render: (_args, value) => {
13877
+ const payload = value;
13878
+ return [{
13879
+ type: "text",
13880
+ text: typeof payload?.text === "string" ? payload.text : JSON.stringify(value)
13881
+ }];
13882
+ }
13883
+ };
13884
+ function marketSourcesTool(read) {
13885
+ return {
13886
+ name: MARKET_SOURCES_TOOL,
13887
+ description: "看本机的索引源:id、地址、是否可发布、当前用的是哪个。",
13888
+ parameters: {
13889
+ type: "object",
13890
+ properties: {},
13891
+ additionalProperties: false
13892
+ },
13893
+ output: OUTPUT$2,
13894
+ async execute() {
13895
+ if (read === void 0) throw new Error("这台机器上没有索引源服务,看不了");
13896
+ const payload = read();
13897
+ const rows = payload.sources.map((source) => ({
13898
+ id: source.id,
13899
+ label: source.label,
13900
+ kind: source.kind,
13901
+ url: source.url,
13902
+ policy: source.policy ?? "catalog-only",
13903
+ ref: source.ref ?? null,
13904
+ path: source.path ?? null,
13905
+ selected: source.id === payload.selected
13906
+ }));
13907
+ if (rows.length === 0) return {
13908
+ sources: rows,
13909
+ selected: payload.selected,
13910
+ text: "这台机器还没有索引源 —— 在「模块市场 → 发现 → 索引源」里加一个。"
13911
+ };
13912
+ const current = rows.find((one) => one.selected);
13913
+ const head = current === void 0 ? "当前没有选中索引源" : `当前:${current.label}(${current.url})`;
13914
+ const lines = rows.map((one) => {
13915
+ const state = one.policy === "artifacts-in-repo" ? "可发布" : "仅读取模块列表";
13916
+ return `${one.selected ? "●" : "○"} ${one.id} · ${one.label} · ${one.kind} · ${state} · ${one.url}`;
13917
+ });
13918
+ return {
13919
+ sources: rows,
13920
+ selected: payload.selected,
13921
+ text: [head, ...lines].join("\n")
13922
+ };
13923
+ }
13924
+ };
13925
+ }
13926
+ //#endregion
13682
13927
  //#region src/modules/panels/model/market-tools.ts
13683
13928
  function supplyNamesOf(entry) {
13684
13929
  return entry.needs.filter((need) => need.kind === "extension" || need.kind === "lib").map((need) => need.name);
@@ -13774,7 +14019,7 @@ function usersOf(shared, names) {
13774
14019
  if (hit === void 0 || hit.usedBy.length === 0) return "";
13775
14020
  return `它被 ${hit.usedBy.join("、")} 用着(动的是它们的供给,它们的状态跟着重探了一遍)`;
13776
14021
  }
13777
- function registerMarketTools(ctx, market) {
14022
+ function registerMarketTools(ctx, market, sources) {
13778
14023
  const tools = ctx.get("tools");
13779
14024
  if (tools === void 0) {
13780
14025
  ctx.logger?.info?.("[dsh-workbench] 没有 tools 服务,跳过模块市场工具注册");
@@ -13973,6 +14218,82 @@ function registerMarketTools(ctx, market) {
13973
14218
  };
13974
14219
  }
13975
14220
  };
14221
+ const enableTool = {
14222
+ name: MARKET_ENABLE_TOOL,
14223
+ description: "把索引源设为可发布(只改本机配置,不碰仓库);要用户审批。",
14224
+ parameters: {
14225
+ type: "object",
14226
+ properties: { id: {
14227
+ type: "string",
14228
+ description: "索引源 id;不填=当前选中的那个。"
14229
+ } },
14230
+ additionalProperties: false
14231
+ },
14232
+ output: OUTPUT$1,
14233
+ async execute(raw) {
14234
+ if (sources === void 0) throw new Error("这台机器上没有索引源服务,设不了可发布");
14235
+ const payload = sources.read();
14236
+ const asked = textArg(raw, "id");
14237
+ const id = asked === "" ? payload.selected : asked;
14238
+ const source = id === null ? void 0 : payload.sources.find((one) => one.id === id);
14239
+ if (source === void 0) {
14240
+ const known = payload.sources.map((one) => one.id).join("、") || "(空)";
14241
+ throw new Error(`没有这个索引源${asked === "" ? "" : `:${asked}`}。现有的:${known};先在「模块市场 → 发现 → 索引源」里加一个。`);
14242
+ }
14243
+ if (source.kind !== "git") throw new Error(`索引源 ${source.id} 是 http 地址,只能读,不能当发布目的地`);
14244
+ if (source.policy === "artifacts-in-repo") return {
14245
+ source,
14246
+ changed: false,
14247
+ text: `已经是可发布:${source.label}(${source.url})`
14248
+ };
14249
+ const result = await sources.init({
14250
+ id: source.id,
14251
+ stampOnly: true
14252
+ });
14253
+ return {
14254
+ source: result.source,
14255
+ branch: result.branch,
14256
+ commit: result.commit,
14257
+ changed: true,
14258
+ text: `已把「${source.label}」设为可发布(${source.url},分支 ${result.branch})—— 仓库本身没动。之后发布可以落到它的 ${source.path ?? "market.json"}。`
14259
+ };
14260
+ }
14261
+ };
14262
+ const readOnlyTool = {
14263
+ name: MARKET_DISABLE_TOOL,
14264
+ description: "把索引源改回只读(不再作为发布目的地;只改本机配置)。",
14265
+ parameters: {
14266
+ type: "object",
14267
+ properties: { id: {
14268
+ type: "string",
14269
+ description: "索引源 id;不填=当前选中的那个。"
14270
+ } },
14271
+ additionalProperties: false
14272
+ },
14273
+ output: OUTPUT$1,
14274
+ async execute(raw) {
14275
+ if (sources === void 0) throw new Error("这台机器上没有索引源服务,改不了");
14276
+ const payload = sources.read();
14277
+ const asked = textArg(raw, "id");
14278
+ const id = asked === "" ? payload.selected : asked;
14279
+ const source = id === null ? void 0 : payload.sources.find((one) => one.id === id);
14280
+ if (source === void 0) {
14281
+ const known = payload.sources.map((one) => one.id).join("、") || "(空)";
14282
+ throw new Error(`没有这个索引源${asked === "" ? "" : `:${asked}`}。现有的:${known};先在「模块市场 → 发现 → 索引源」里加一个。`);
14283
+ }
14284
+ if (source.kind !== "git") throw new Error(`索引源 ${source.id} 是 http 地址,本来就是只读`);
14285
+ if (source.policy === "catalog-only") return {
14286
+ source,
14287
+ changed: false,
14288
+ text: `已经是只读:${source.label}(${source.url})`
14289
+ };
14290
+ return {
14291
+ source: await sources.readOnly(source.id),
14292
+ changed: true,
14293
+ text: `已把「${source.label}」设为只读(${source.url})—— 发布不会再落到它。`
14294
+ };
14295
+ }
14296
+ };
13976
14297
  return [
13977
14298
  tools.register(listTool),
13978
14299
  tools.register(installTool),
@@ -13982,7 +14303,10 @@ function registerMarketTools(ctx, market) {
13982
14303
  tools.register(updateDependencyTool),
13983
14304
  tools.register(updatePluginTool),
13984
14305
  tools.register(uninstallPluginTool),
13985
- tools.register(updateWorkbenchTool)
14306
+ tools.register(updateWorkbenchTool),
14307
+ tools.register(marketSourcesTool(sources === void 0 ? void 0 : () => sources.read())),
14308
+ tools.register(enableTool),
14309
+ tools.register(readOnlyTool)
13986
14310
  ];
13987
14311
  }
13988
14312
  //#endregion
@@ -14100,6 +14424,7 @@ function listedRunUnits(result) {
14100
14424
  }
14101
14425
  function panelsFactsOf(call, manifests) {
14102
14426
  if (call.source === "workbench_panels_list") return [PANELS_FACT];
14427
+ if (call.source === "market-sources") return [PANELS_FACT];
14103
14428
  if (call.source === "workbench_long_tasks_list") return listedRunUnits(call.result).map(runsRouteOf);
14104
14429
  if (call.source === "workbench_modules_list") return [PANELS_FACT, CAPABILITY_FACT];
14105
14430
  if (call.source === "workbench_modules_update_workbench") return [PANELS_FACT, CAPABILITY_FACT];
@@ -14794,13 +15119,17 @@ var OWNED = [{
14794
15119
  },
14795
15120
  registerTools: () => [
14796
15121
  ...registerPanelTools(ctx, panels, values),
14797
- ...registerMarketTools(ctx, market),
15122
+ ...registerMarketTools(ctx, market, {
15123
+ read: () => marketSources.read(),
15124
+ init: marketInit,
15125
+ readOnly: (id) => markSourceReadOnly(marketSources, id)
15126
+ }),
14798
15127
  ...registerDiyTools(ctx, market, panels, debug, (unit) => publishPreviewOf(workshopPathOf(ctx), unit), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell)),
14799
15128
  ...registerRecordTools(ctx, record),
14800
15129
  ...registerLongTaskTools(ctx, longTaskRuns)
14801
15130
  ],
14802
15131
  factsOf: (call) => panelsFactsOf(call, manifests()),
14803
- needsApproval: panelsApprovalFor(longTaskRuns, () => record.live(Date.now()), (unit) => publishPreviewOf(workshopPathOf(ctx), unit)),
15132
+ needsApproval: panelsApprovalFor(longTaskRuns, () => record.live(Date.now()), (unit, author) => publishPreviewOf(workshopPathOf(ctx), unit, author === void 0 ? {} : { author }), marketSources),
14804
15133
  gate: {
14805
15134
  deniedTools: (context) => {
14806
15135
  const boards = panels.boards(context.workspace);