@dsh-workbench/core 1.0.0 → 1.1.0

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 (41) hide show
  1. package/README.md +16 -3
  2. package/bin/dsh-workbench +38 -6
  3. package/client.js +669 -305
  4. package/dist/client/client.body.js +669 -305
  5. package/dist/client/index.js +669 -305
  6. package/dist/host/index.js +445 -37
  7. package/dist/types/app/layouts/market/discover-sources-dialog.d.ts +4 -0
  8. package/dist/types/app/layouts/market/discover-states.d.ts +3 -0
  9. package/dist/types/app/layouts/market/use-add-module.d.ts +0 -1
  10. package/dist/types/app/layouts/market/use-discover.d.ts +1 -0
  11. package/dist/types/app/layouts/rightbar/MarketView.d.ts +3 -1
  12. package/dist/types/app/layouts/rightbar/market-names.d.ts +8 -0
  13. package/dist/types/app/layouts/rightbar/market-row-types.d.ts +11 -3
  14. package/dist/types/app/layouts/rightbar/market-rows.d.ts +6 -4
  15. package/dist/types/app/layouts/rightbar/market-view-text.d.ts +4 -3
  16. package/dist/types/app/units/unit-titles.d.ts +3 -0
  17. package/dist/types/kernel/contract/bundle-install.d.ts +1 -1
  18. package/dist/types/kernel/contract/market-index.d.ts +1 -1
  19. package/dist/types/modules/panels/model/diy-tools.d.ts +1 -1
  20. package/dist/types/modules/panels/model/market-dispatch.d.ts +8 -0
  21. package/dist/types/modules/panels/model/market-follow-up.d.ts +16 -0
  22. package/dist/types/modules/panels/model/market-sources-act.d.ts +2 -0
  23. package/dist/types/modules/panels/model/market-types.d.ts +5 -0
  24. package/dist/types/modules/panels/model/market.d.ts +2 -0
  25. package/dist/types/modules/panels/model/publish-preview-routes.d.ts +1 -1
  26. package/dist/types/shared/api/host/diy-publish-preview.d.ts +11 -0
  27. package/dist/types/shared/api/host/follow-up-dismiss.d.ts +2 -0
  28. package/dist/types/shared/api/host/migrate-workbench.d.ts +20 -0
  29. package/dist/types/shared/api/host/migration-follow-up.d.ts +12 -0
  30. package/dist/types/shared/hooks/market-queries.d.ts +3 -2
  31. package/dist/types/shared/hooks/resource-view.d.ts +14 -0
  32. package/dist/types/shared/hooks/use-api-resource.d.ts +3 -7
  33. package/package.json +3 -3
  34. package/scripts/seed-profile.sh +9 -7
  35. package/scripts/share-data-root.mjs +272 -64
  36. package/skills/workbench-module-diy/FAMILY.md +1 -1
  37. package/skills/workbench-module-diy/REVIEW.md +2 -2
  38. package/skills/workbench-module-diy/SKILL.md +1 -0
  39. package/skills/workbench-module-diy/TOOLS.md +1 -1
  40. package/skills/workbench-module-diy/WIRING.md +3 -1
  41. package/skills/workbench-module-diy/WRITES.md +1 -1
@@ -5,8 +5,9 @@ import { createHash, randomUUID } from "node:crypto";
5
5
  import fs, { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
6
  import { createRequire } from "node:module";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
- import { deflateSync, inflateSync } from "node:zlib";
9
8
  import writeFileAtomic from "write-file-atomic";
9
+ import { deflateSync, inflateSync } from "node:zlib";
10
+ import { execa } from "execa";
10
11
  //#region \0rolldown/runtime.js
11
12
  var __create = Object.create;
12
13
  var __defProp = Object.defineProperty;
@@ -86,6 +87,14 @@ function encodeRef(ref) {
86
87
  //#endregion
87
88
  //#region ../shared/dist/contract/unit.js
88
89
  /**
90
+ * 外壳替模块名合成的词表键:模块自己不必写名字键,名字直接从清单里的 title 注册进来。
91
+ * @param unit 单元 id。
92
+ * @returns 该单元名字在词表里的键。
93
+ */
94
+ function unitTitleKey(unit) {
95
+ return `dsh-workbench.unit.${unit}.title`;
96
+ }
97
+ /**
89
98
  * 模块声明的全部环境需要(没写就是空表)。
90
99
  * @param manifest 模块清单。
91
100
  * @returns need 列表。
@@ -382,13 +391,14 @@ function boardSituation(probe, workshop) {
382
391
  /**
383
392
  * 外壳愿意认的协议版本;不在表里的模块当场被拒,不猜语义。
384
393
  */
385
- var SUPPORTED_API_VERSIONS = [2];
394
+ var SUPPORTED_API_VERSIONS = [3];
386
395
  /**
387
396
  * 协议认得的全部键;写了别的键这份清单会被拒(`protocol` 判据与运行时同一套),拼错当场报出来。
388
397
  */
389
398
  var DECLARATION_KEYS = [
390
399
  "apiVersion",
391
400
  "unit",
401
+ "title",
392
402
  "card",
393
403
  "tools",
394
404
  "host",
@@ -400,11 +410,13 @@ var DECLARATION_KEYS = [
400
410
  var DECLARATION_REQUIRED_KEYS = [
401
411
  "apiVersion",
402
412
  "unit",
413
+ "title",
403
414
  "card",
404
415
  "tools",
405
416
  "host"
406
417
  ];
407
- var CARD_KEYS = ["titleKey", "order"];
418
+ var CARD_KEYS = ["order"];
419
+ var TITLE_KEYS = ["zh", "en"];
408
420
  var TOOL_KEYS = [
409
421
  "name",
410
422
  "writes",
@@ -418,6 +430,16 @@ function text$2(value) {
418
430
  function isRecord(value) {
419
431
  return typeof value === "object" && value !== null && !Array.isArray(value);
420
432
  }
433
+ /**
434
+ * 读一份声明里的协议版本;不是对象或 apiVersion 不是整数时给 undefined。
435
+ * @param raw 清单里 dsh.workbench 那段原始值。
436
+ * @returns 声明声明的协议版本,读不出来时 undefined。
437
+ */
438
+ function declarationApiVersionOf(raw) {
439
+ if (!isRecord(raw)) return void 0;
440
+ const version = raw.apiVersion;
441
+ return Number.isInteger(version) ? version : void 0;
442
+ }
421
443
  function outsideKeys(value, allowed) {
422
444
  return Object.keys(value).filter((key) => !allowed.includes(key));
423
445
  }
@@ -437,22 +459,30 @@ function stringArrayProblems(at, value) {
437
459
  function declarationProblemsOf(subject, raw) {
438
460
  if (!isRecord(raw)) return [`${subject}: dsh.workbench 不是一个对象 —— ${JSON.stringify(raw)}`];
439
461
  const version = raw.apiVersion;
440
- if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v2 起,写 \`"apiVersion": 2\`。缺它 = 外壳不认这个模块`];
462
+ if (version === void 0) return [`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v3 起,写 \`"apiVersion": 3\`。缺它 = 外壳不认这个模块`];
441
463
  if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version)) {
442
- const hint = version === 1 ? " —— v2 把 `env` 的三张表(command / plugins / libs)换成了 `needs`:把外部需要逐条写成 `{ kind, name, purpose, ... }`(kind 是 bin / extension / lib / env),再把 apiVersion 改成 2" : "";
464
+ const hint = version === 2 ? " —— v3 把卡片标题挪成了顶层 `title`(`{ \"zh\": \"…\", \"en\": \"…\" }` 字面量):把 `card.titleKey` 删掉、在顶层写 title,再把 apiVersion 改成 3" : "";
443
465
  return [`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ${SUPPORTED_API_VERSIONS.join(" / ")}(要整数)${hint}`];
444
466
  }
445
467
  const problems = [];
446
468
  const unknown = outsideKeys(raw, DECLARATION_KEYS);
447
- if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v2 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(协议外的键会让这份清单被拒:那句声明等于没写)`);
469
+ if (unknown.length > 0) problems.push(`${subject}: dsh.workbench 里有协议 v3 不认识的键 ${listOf(unknown)} —— 只有 ${listOf(DECLARATION_KEYS)}。(协议外的键会让这份清单被拒:那句声明等于没写)`);
448
470
  for (const key of DECLARATION_REQUIRED_KEYS) if (raw[key] === void 0) problems.push(`${subject}: dsh.workbench 缺 ${key}` + (key === "card" ? "(这个模块没有卡片就写 `\"card\": null`)" : ""));
471
+ const title = raw.title;
472
+ if (title !== void 0) {
473
+ if (!isRecord(title)) problems.push(`${subject}: dsh.workbench.title 要是对象(中英各一份),现在是 ${JSON.stringify(title)}`);
474
+ else {
475
+ const extra = outsideKeys(title, TITLE_KEYS);
476
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.title 里有不认的键 ${listOf(extra)} —— title 只有 ${listOf(TITLE_KEYS)}`);
477
+ for (const language of TITLE_KEYS) if (text$2(title[language]) === "") problems.push(`${subject}: dsh.workbench.title.${language} 要是非空字符串(市场和面板里显示的名字)`);
478
+ }
479
+ }
449
480
  const card = raw.card;
450
481
  if (card !== void 0 && card !== null) {
451
482
  if (!isRecord(card)) problems.push(`${subject}: dsh.workbench.card 要么是对象、要么是 null,现在是 ${JSON.stringify(card)}`);
452
483
  else {
453
484
  const extra = outsideKeys(card, CARD_KEYS);
454
- if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
455
- if (text$2(card.titleKey) === "") problems.push(`${subject}: dsh.workbench.card.titleKey 要是非空字符串(面板那一格的文案键)`);
485
+ if (extra.length > 0) problems.push(`${subject}: dsh.workbench.card 里有不认的键 ${listOf(extra)} —— card 只有 ${listOf(CARD_KEYS)}(名字写在顶层的 title 里;卡片默认显不显示由运行期的面板配置决定,清单里没有这一位)`);
456
486
  if (typeof card.order !== "number" || !Number.isFinite(card.order)) problems.push(`${subject}: dsh.workbench.card.order 要是数字,现在是 ${JSON.stringify(card.order)}`);
457
487
  }
458
488
  }
@@ -516,13 +546,18 @@ function readDeclaration(subject, raw) {
516
546
  warnings: needsRead.warnings
517
547
  };
518
548
  const card = declaration.card;
549
+ const id = text$2(declaration.unit);
519
550
  return {
520
551
  declaration,
521
552
  problems: [],
522
553
  manifest: {
523
- id: text$2(declaration.unit),
554
+ id,
555
+ title: {
556
+ zh: text$2(declaration.title.zh),
557
+ en: text$2(declaration.title.en)
558
+ },
524
559
  card: card === null ? null : {
525
- titleKey: text$2(card.titleKey),
560
+ titleKey: unitTitleKey(id),
526
561
  order: card.order
527
562
  },
528
563
  tools: declaration.tools.map((tool) => ({
@@ -1192,7 +1227,9 @@ var PROVISION_ACTIONS = [
1192
1227
  "update-dependency",
1193
1228
  "update-plugin",
1194
1229
  "uninstall-plugin",
1195
- "update-workbench"
1230
+ "update-workbench",
1231
+ "migrate-workbench",
1232
+ "dismiss-followup"
1196
1233
  ];
1197
1234
  var DEPENDENCY_ACTIONS = ["update-dependency"];
1198
1235
  function isDependencyAction(action) {
@@ -1203,7 +1240,8 @@ function provisionTargetOf(action) {
1203
1240
  if (action === "install-bundle" || action === "install-version") return "spec";
1204
1241
  if (action === "remove-bundle") return "name";
1205
1242
  if (action === "update-plugin" || action === "uninstall-plugin") return "name";
1206
- if (action === "update-workbench") return "name";
1243
+ if (action === "update-workbench" || action === "migrate-workbench") return "name";
1244
+ if (action === "dismiss-followup") return "id";
1207
1245
  if (action === "cancel-install") return "requestId";
1208
1246
  return "unit";
1209
1247
  }
@@ -1239,10 +1277,14 @@ function requestOf$1(action, value) {
1239
1277
  action,
1240
1278
  name: value
1241
1279
  };
1242
- if (action === "update-workbench") return {
1280
+ if (action === "update-workbench" || action === "migrate-workbench") return {
1243
1281
  action,
1244
1282
  name: value
1245
1283
  };
1284
+ if (action === "dismiss-followup") return {
1285
+ action,
1286
+ id: value
1287
+ };
1246
1288
  if (action === "cancel-install") return {
1247
1289
  action,
1248
1290
  requestId: value
@@ -3990,6 +4032,31 @@ function refusalReason() {
3990
4032
  return "这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上";
3991
4033
  }
3992
4034
  //#endregion
4035
+ //#region ../shared/dist/host/plugin-source.js
4036
+ function envValue(env, name) {
4037
+ const value = env[name]?.trim();
4038
+ return value === void 0 || value === "" ? { ok: false } : {
4039
+ ok: true,
4040
+ value
4041
+ };
4042
+ }
4043
+ function npmRegistryOf(source, env) {
4044
+ const name = source.registryEnv ?? "";
4045
+ if (name !== "") {
4046
+ const fromEnv = envValue(env, name);
4047
+ if (fromEnv.ok) return fromEnv;
4048
+ }
4049
+ const fallback = source.registryDefault?.trim();
4050
+ if (fallback === void 0 || fallback === "") return {
4051
+ ok: false,
4052
+ env: name
4053
+ };
4054
+ return {
4055
+ ok: true,
4056
+ value: fallback
4057
+ };
4058
+ }
4059
+ //#endregion
3993
4060
  //#region ../shared/dist/host/supply-index.js
3994
4061
  function text$1(value) {
3995
4062
  return value === void 0 ? "" : value.trim();
@@ -4015,24 +4082,28 @@ function packageOf(need) {
4015
4082
  function keyOf(kind, pkg) {
4016
4083
  return `${kind}:${pkg}`;
4017
4084
  }
4018
- function sourceCompatible(left, right) {
4085
+ function registryAddressOf(source, env) {
4086
+ const resolved = npmRegistryOf(source, env);
4087
+ return resolved.ok ? resolved.value : void 0;
4088
+ }
4089
+ function sourceCompatible(left, right, env) {
4019
4090
  if (left.kind !== right.kind) return false;
4020
4091
  if (left.kind === "npm" && right.kind === "npm") {
4021
4092
  if (text$1(left.package) !== "" && text$1(right.package) !== "" && text$1(left.package) !== text$1(right.package)) return false;
4022
- if (text$1(left.registryEnv) !== text$1(right.registryEnv)) return false;
4023
- const l = text$1(left.registryDefault);
4024
- const r = text$1(right.registryDefault);
4025
- return l === "" || r === "" || l === r;
4093
+ const l = registryAddressOf(left, env);
4094
+ const r = registryAddressOf(right, env);
4095
+ if (l !== void 0 && r !== void 0) return l === r;
4096
+ return text$1(left.registryEnv) === text$1(right.registryEnv);
4026
4097
  }
4027
4098
  if (left.kind === "git" && right.kind === "git") return text$1(left.url) === text$1(right.url);
4028
4099
  if (left.kind === "local" && right.kind === "local") return text$1(left.env) === text$1(right.env) && text$1(left.path) === text$1(right.path);
4029
4100
  return false;
4030
4101
  }
4031
- function sourcesCompatible(left, right) {
4102
+ function sourcesCompatible(left, right, env) {
4032
4103
  if (left.length !== right.length) return false;
4033
4104
  return left.every((source, index) => {
4034
4105
  const other = right[index];
4035
- return other !== void 0 && sourceCompatible(source, other);
4106
+ return other !== void 0 && sourceCompatible(source, other, env);
4036
4107
  });
4037
4108
  }
4038
4109
  function mergedSource(left, right) {
@@ -4047,13 +4118,13 @@ function mergedSource(left, right) {
4047
4118
  }
4048
4119
  return left;
4049
4120
  }
4050
- function mergeSources(entry, sources) {
4121
+ function mergeSources(entry, sources, env) {
4051
4122
  if (!entry.declared) {
4052
4123
  entry.declared = true;
4053
4124
  entry.sources = [...sources];
4054
4125
  return true;
4055
4126
  }
4056
- if (!sourcesCompatible(entry.sources, sources)) {
4127
+ if (!sourcesCompatible(entry.sources, sources, env)) {
4057
4128
  entry.conflicted = true;
4058
4129
  return false;
4059
4130
  }
@@ -4072,7 +4143,8 @@ function addDeclarer(entry, unit, commands) {
4072
4143
  if (!entry.declarers.includes(unit)) entry.declarers.push(unit);
4073
4144
  for (const command of commands) if (!entry.commands.includes(command)) entry.commands.push(command);
4074
4145
  }
4075
- function buildSupplyIndex(declarations) {
4146
+ function buildSupplyIndex(declarations, options = {}) {
4147
+ const env = options.env ?? {};
4076
4148
  const entries = /* @__PURE__ */ new Map();
4077
4149
  const byName = /* @__PURE__ */ new Map();
4078
4150
  const conflicts = [];
@@ -4103,7 +4175,7 @@ function buildSupplyIndex(declarations) {
4103
4175
  entry.runtime = need.runtime;
4104
4176
  addDeclarer(entry, declaration.id, need.commands ?? []);
4105
4177
  } else addDeclarer(entry, declaration.id, []);
4106
- if (!mergeSources(entry, need.sources ?? [])) conflicts.push({
4178
+ if (!mergeSources(entry, need.sources ?? [], env)) conflicts.push({
4107
4179
  kind: "source-mismatch",
4108
4180
  key: entry.key,
4109
4181
  names: [...entry.names],
@@ -4477,6 +4549,7 @@ var MARKET_DISCOVER_ROUTE = "panels/market-discover";
4477
4549
  var MARKET_DOC_ROUTE = "panels/market-doc";
4478
4550
  var MARKET_PUBLISH_PREVIEW_ROUTE = "panels/market-publish-preview";
4479
4551
  var MARKET_SOURCES_RESOURCE = "market-sources";
4552
+ var MARKET_SOURCES_ROUTE = "panels/market-sources";
4480
4553
  //#endregion
4481
4554
  //#region ../shared/dist/lib/market-index.js
4482
4555
  var BRANCH = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
@@ -5010,6 +5083,94 @@ var BUNDLE_REGISTRIES_ROUTE = "bundle-registries";
5010
5083
  var BUNDLE_INSPECT_ROUTE = "bundle-inspect";
5011
5084
  var MODULE_VERSIONS_ROUTE = "module-versions";
5012
5085
  //#endregion
5086
+ //#region src/shared/api/host/follow-up-dismiss.ts
5087
+ var DISMISS_FILE = "follow-ups.json";
5088
+ function fileOf$1(runtimeRoot) {
5089
+ return join(runtimeRoot, DISMISS_FILE);
5090
+ }
5091
+ function readDismissed(runtimeRoot) {
5092
+ try {
5093
+ const value = JSON.parse(readFileSync(fileOf$1(runtimeRoot), "utf8"));
5094
+ if (typeof value !== "object" || value === null) return {};
5095
+ const dismissed = value["dismissed"];
5096
+ if (typeof dismissed !== "object" || dismissed === null) return {};
5097
+ return dismissed;
5098
+ } catch {
5099
+ return {};
5100
+ }
5101
+ }
5102
+ function isFollowUpDismissed(runtimeRoot, id) {
5103
+ return Object.prototype.hasOwnProperty.call(readDismissed(runtimeRoot), id);
5104
+ }
5105
+ function dismissFollowUp(runtimeRoot, id) {
5106
+ const file = fileOf$1(runtimeRoot);
5107
+ mkdirSync(dirname(file), { recursive: true });
5108
+ const dismissed = {
5109
+ ...readDismissed(runtimeRoot),
5110
+ [id]: { at: (/* @__PURE__ */ new Date()).toISOString() }
5111
+ };
5112
+ writeFileAtomic.sync(file, `${JSON.stringify({ dismissed }, null, 2)}\n`);
5113
+ }
5114
+ //#endregion
5115
+ //#region src/shared/api/host/migration-follow-up.ts
5116
+ var MIGRATION_FOLLOW_UP_ID = "data-root-migration";
5117
+ function hasEntries(dir) {
5118
+ try {
5119
+ return readdirSync(dir).some((name) => name !== ".DS_Store");
5120
+ } catch {
5121
+ return false;
5122
+ }
5123
+ }
5124
+ function legacyDataIn(root) {
5125
+ return existsSync(join(root, "storages", "workspace.json")) || hasEntries(join(root, "sessions")) || hasEntries(join(root, "attachments"));
5126
+ }
5127
+ function pathsIn(file) {
5128
+ try {
5129
+ const value = JSON.parse(readFileSync(file, "utf8"));
5130
+ if (typeof value !== "object" || value === null) return /* @__PURE__ */ new Set();
5131
+ const tables = value["tables"];
5132
+ if (typeof tables !== "object" || tables === null) return /* @__PURE__ */ new Set();
5133
+ const workspaces = tables["workspaces"];
5134
+ if (typeof workspaces !== "object" || workspaces === null) return /* @__PURE__ */ new Set();
5135
+ const paths = /* @__PURE__ */ new Set();
5136
+ for (const entry of Object.values(workspaces)) {
5137
+ if (typeof entry !== "object" || entry === null) continue;
5138
+ const path = entry["path"];
5139
+ if (typeof path === "string") paths.add(path);
5140
+ }
5141
+ return paths;
5142
+ } catch {
5143
+ return /* @__PURE__ */ new Set();
5144
+ }
5145
+ }
5146
+ function hasMissingPaths(sourceFile, targetFile) {
5147
+ const source = pathsIn(sourceFile);
5148
+ if (source.size === 0) return false;
5149
+ const target = pathsIn(targetFile);
5150
+ for (const path of source) if (!target.has(path)) return true;
5151
+ return false;
5152
+ }
5153
+ function migrationFollowUp(input) {
5154
+ const runtimeRoot = input.runtimeRoot ?? join(input.home, input.profile);
5155
+ if (resolve(runtimeRoot) === resolve(input.home)) return void 0;
5156
+ if (isFollowUpDismissed(runtimeRoot, "data-root-migration")) return void 0;
5157
+ if (!legacyDataIn(runtimeRoot)) return void 0;
5158
+ const marker = join(runtimeRoot, ".data-root-shared");
5159
+ if (!existsSync(marker)) return {
5160
+ id: MIGRATION_FOLLOW_UP_ID,
5161
+ kind: "data-migration",
5162
+ hard: false
5163
+ };
5164
+ const source = join(runtimeRoot, "storages", "workspace.json");
5165
+ const target = join(input.home, "storages", "workspace.json");
5166
+ if (!existsSync(source) || !existsSync(target)) return void 0;
5167
+ return hasMissingPaths(source, target) ? {
5168
+ id: MIGRATION_FOLLOW_UP_ID,
5169
+ kind: "data-migration",
5170
+ hard: false
5171
+ } : void 0;
5172
+ }
5173
+ //#endregion
5013
5174
  //#region src/shared/api/host/bundle-routes.ts
5014
5175
  function registryOf(search) {
5015
5176
  const raw = param(search, "registry");
@@ -5033,6 +5194,25 @@ function bundleRegistriesHandler(ctx) {
5033
5194
  }
5034
5195
  var refusalLog = /* @__PURE__ */ new Map();
5035
5196
  var holdLog = /* @__PURE__ */ new Map();
5197
+ function updateBlockOf(raw, problems) {
5198
+ if (problems.length === 0) return void 0;
5199
+ const declared = declarationApiVersionOf(raw);
5200
+ const newest = Math.max(...SUPPORTED_API_VERSIONS);
5201
+ return declared !== void 0 && declared > newest ? "needs-workbench" : "incompatible";
5202
+ }
5203
+ function titleOf(raw) {
5204
+ if (typeof raw !== "object" || raw === null) return void 0;
5205
+ const title = raw.title;
5206
+ if (typeof title !== "object" || title === null || Array.isArray(title)) return void 0;
5207
+ const record = title;
5208
+ const zh = typeof record.zh === "string" ? record.zh.trim() : "";
5209
+ const en = typeof record.en === "string" ? record.en.trim() : "";
5210
+ if (zh === "" || en === "") return void 0;
5211
+ return {
5212
+ zh,
5213
+ en
5214
+ };
5215
+ }
5036
5216
  async function latestIfReleaseReady(supply, input) {
5037
5217
  const missing = await supply.missingReleaseParts({
5038
5218
  shell: input.shell,
@@ -5075,7 +5255,8 @@ async function workbenchVersion(supply, input) {
5075
5255
  installed: read.version,
5076
5256
  ...latest === void 0 ? {} : { latest },
5077
5257
  ...input.running === void 0 ? {} : { running: input.running },
5078
- ...read.local === true ? { local: true } : {}
5258
+ ...read.local === true ? { local: true } : {},
5259
+ ...input.followUp === void 0 ? {} : { followUp: input.followUp }
5079
5260
  };
5080
5261
  }
5081
5262
  function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
@@ -5101,7 +5282,15 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
5101
5282
  });
5102
5283
  const raw = supply.moduleDeclarationOf(dir, module.pkg);
5103
5284
  const declared = raw === void 0 ? [] : declarationProblemsOf(module.pkg, raw);
5285
+ const title = titleOf(raw);
5104
5286
  const refused = refusals.get(module.unit) ?? (declared.length === 0 ? void 0 : declared);
5287
+ const latestRaw = latest === void 0 || latest === module.version || shell === void 0 || registry === void 0 ? void 0 : await supply.registryDeclarationOf({
5288
+ shell,
5289
+ name: module.pkg,
5290
+ registry,
5291
+ force
5292
+ });
5293
+ const latestBlocked = latestRaw === void 0 ? void 0 : updateBlockOf(latestRaw, declarationProblemsOf(module.pkg, latestRaw));
5105
5294
  if (refused === void 0) refusalLog.delete(module.pkg);
5106
5295
  else {
5107
5296
  const line = refused.join(";");
@@ -5114,18 +5303,28 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
5114
5303
  unit: module.unit,
5115
5304
  pkg: module.pkg,
5116
5305
  installed: module.version,
5306
+ ...title === void 0 ? {} : { title },
5117
5307
  ...latest === void 0 ? {} : { latest },
5308
+ ...latestBlocked === void 0 ? {} : { latestBlocked },
5118
5309
  ...module.local === true ? { local: true } : {},
5119
5310
  ...refused === void 0 ? {} : { refused }
5120
5311
  };
5121
5312
  }));
5122
5313
  const selfUpdateNow = selfUpdate?.();
5314
+ const profile = dir === null ? "" : basename(dir);
5315
+ const home = dir === null ? void 0 : dirname(dirname(dir));
5316
+ const followUp = profile === "" || home === void 0 ? void 0 : migrationFollowUp({
5317
+ home,
5318
+ profile,
5319
+ ...process.env.DSH_WORKBENCH_HOME === void 0 || process.env.DSH_WORKBENCH_HOME === "" ? {} : { runtimeRoot: process.env.DSH_WORKBENCH_HOME }
5320
+ });
5123
5321
  const workbench = await workbenchVersion(supply, {
5124
5322
  dir,
5125
5323
  shell,
5126
5324
  fallback,
5127
5325
  force,
5128
5326
  ...running === void 0 ? {} : { running },
5327
+ ...followUp === void 0 ? {} : { followUp },
5129
5328
  logger: ctx.logger
5130
5329
  });
5131
5330
  return {
@@ -8894,7 +9093,7 @@ function renderList(root, modules, shape = "bare") {
8894
9093
  //#endregion
8895
9094
  //#region src/modules/panels/model/diy-tools.ts
8896
9095
  var PUBLISH_TIMEOUT_MS = 18e5;
8897
- function registerDiyTools(ctx, market, panels, debug, previewOf = () => null) {
9096
+ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null, inferredAuthorOf = async () => void 0) {
8898
9097
  const supply = supplyPortOf(ctx);
8899
9098
  const tools = ctx.get("tools");
8900
9099
  if (tools === void 0) {
@@ -9171,6 +9370,10 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null) {
9171
9370
  type: "string",
9172
9371
  description: "公开发布用的许可证(MIT / Apache-2.0 / 仓库已有 LICENSE)。"
9173
9372
  },
9373
+ author: {
9374
+ type: "string",
9375
+ description: "署谁的名(姓名,或 姓名 <邮箱>);用户点了名才传,不传按模块里写的,没写用推断的署名。"
9376
+ },
9174
9377
  refresh: {
9175
9378
  type: "boolean",
9176
9379
  description: "只刷市集元数据(标题/作者/说明),不升版本、不重发产物。"
@@ -9190,8 +9393,10 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null) {
9190
9393
  const target = asText(asRecord$1(raw)["target"]);
9191
9394
  const version = asText(asRecord$1(raw)["version"]);
9192
9395
  const license = asText(asRecord$1(raw)["license"]);
9396
+ let author = asText(asRecord$1(raw)["author"]);
9193
9397
  const verify = asRecord$1(raw)["verify"] === true;
9194
9398
  const resume = asRecord$1(raw)["resume"] === true;
9399
+ if (!verify && target !== "local" && author === "" && (previewOf(found.unit)?.author ?? "") === "") author = await inferredAuthorOf(found.unit) ?? "";
9195
9400
  const argv = [
9196
9401
  bin,
9197
9402
  "release",
@@ -9205,6 +9410,7 @@ function registerDiyTools(ctx, market, panels, debug, previewOf = () => null) {
9205
9410
  if (target !== "") argv.push("--target", target);
9206
9411
  if (version !== "") argv.push("--version", version);
9207
9412
  if (license !== "") argv.push("--license", license);
9413
+ if (author !== "") argv.push("--author", author);
9208
9414
  if (asRecord$1(raw)["refresh"] === true) argv.push("--refresh");
9209
9415
  const result = await runShell(shell, argv.map((one) => quoteShellArg(one)).join(" "), {
9210
9416
  workdir: root,
@@ -9311,17 +9517,28 @@ function selectedGitSource() {
9311
9517
  return parsed.source.policy === "artifacts-in-repo" ? parsed.source : void 0;
9312
9518
  }
9313
9519
  }
9314
- function publishedVersionsOf(source, name) {
9520
+ function publishedEntryOf(source, name) {
9315
9521
  const file = join(workbenchRoot(), "market-index", marketSourceKey(source), "worktree", source.path ?? "market.json");
9316
9522
  if (!existsSync(file)) return void 0;
9317
9523
  try {
9318
9524
  const parsed = parseMarketIndexJson(readFileSync(file, "utf8"));
9319
9525
  if (!parsed.ok) return void 0;
9320
- return parsed.index.plugins.find((one) => one.name === name)?.versions.map((one) => one.version) ?? [];
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
+ };
9321
9535
  } catch {
9322
9536
  return;
9323
9537
  }
9324
9538
  }
9539
+ function publishedVersionsOf(source, name) {
9540
+ return publishedEntryOf(source, name)?.versions;
9541
+ }
9325
9542
  function bumped(version, kind) {
9326
9543
  const match = SEMVER.exec(version);
9327
9544
  if (match === null) return version;
@@ -9358,6 +9575,71 @@ function marketOf(unit, dir, name, version) {
9358
9575
  ...nextVersion === void 0 ? {} : { nextVersion }
9359
9576
  };
9360
9577
  }
9578
+ function moduleFactOf(root, unit) {
9579
+ if (root === null || root === void 0 || unit === "") return null;
9580
+ const found = modulesIn(root).find((one) => one.unit === unit);
9581
+ if (found === void 0) return null;
9582
+ let manifest;
9583
+ try {
9584
+ manifest = JSON.parse(readFileSync(join(found.dir, "package.json"), "utf8"));
9585
+ } catch {
9586
+ return null;
9587
+ }
9588
+ if (typeof manifest !== "object" || manifest === null) return null;
9589
+ const record = manifest;
9590
+ return {
9591
+ dir: found.dir,
9592
+ name: typeof record["name"] === "string" ? record["name"].trim() : "",
9593
+ author: authorOf(record)
9594
+ };
9595
+ }
9596
+ function siblingAuthorOf(root, unit) {
9597
+ const authors = /* @__PURE__ */ new Set();
9598
+ for (const module of modulesIn(root)) {
9599
+ if (module.unit === unit) continue;
9600
+ try {
9601
+ const manifest = JSON.parse(readFileSync(join(module.dir, "package.json"), "utf8"));
9602
+ if (typeof manifest !== "object" || manifest === null) continue;
9603
+ const author = authorOf(manifest);
9604
+ if (author !== "") authors.add(author);
9605
+ } catch {
9606
+ continue;
9607
+ }
9608
+ }
9609
+ return authors.size === 1 ? [...authors][0] : void 0;
9610
+ }
9611
+ async function localGitIdentityOf(shell, dir) {
9612
+ if (shell === void 0) return void 0;
9613
+ const read = async (argv) => {
9614
+ const result = await runShell(shell, `git -C ${quoteShellArg(dir)} ${argv}`, { workdir: dir });
9615
+ return result.exitCode === 0 ? result.stdout.trim() : "";
9616
+ };
9617
+ const name = await read(`config ${quoteShellArg("user.name")}`);
9618
+ if (name === "") return void 0;
9619
+ const email = await read(`config ${quoteShellArg("user.email")}`);
9620
+ return email === "" ? name : `${name} <${email}>`;
9621
+ }
9622
+ async function lastCommitAuthorOf(shell, dir) {
9623
+ if (shell === void 0) return void 0;
9624
+ const result = await runShell(shell, `git -C ${quoteShellArg(dir)} log -1 --format=${quoteShellArg("%an <%ae>")}`, { workdir: dir });
9625
+ const author = result.exitCode === 0 ? result.stdout.trim() : "";
9626
+ return author === "" ? void 0 : author;
9627
+ }
9628
+ async function inferredAuthorOf(root, unit, shell) {
9629
+ const fact = moduleFactOf(root, unit);
9630
+ if (fact === null) return void 0;
9631
+ if (fact.author !== "") return fact.author;
9632
+ const source = selectedGitSource();
9633
+ if (source !== void 0 && fact.name !== "") {
9634
+ const published = publishedEntryOf(source, fact.name)?.author ?? "";
9635
+ if (published !== "") return published;
9636
+ }
9637
+ const sibling = root === null || root === void 0 ? void 0 : siblingAuthorOf(root, unit);
9638
+ if (sibling !== void 0) return sibling;
9639
+ const local = await localGitIdentityOf(shell, fact.dir);
9640
+ if (local !== void 0) return local;
9641
+ return lastCommitAuthorOf(shell, fact.dir);
9642
+ }
9361
9643
  function publishPreviewOf(root, unit, options = {}) {
9362
9644
  if (root === null || root === void 0 || unit === "") return null;
9363
9645
  const found = modulesIn(root).find((one) => one.unit === unit);
@@ -9377,7 +9659,8 @@ function publishPreviewOf(root, unit, options = {}) {
9377
9659
  const registry = typeof publishConfig === "object" && publishConfig !== null ? publishConfig["registry"] : void 0;
9378
9660
  const market = marketOf(found.unit, found.dir, name, version);
9379
9661
  const target = targetVersionOf(version, (options.version ?? "").trim(), market);
9380
- const author = authorOf(record);
9662
+ const askedAuthor = (options.author ?? "").trim();
9663
+ const author = askedAuthor === "" ? authorOf(record) : askedAuthor;
9381
9664
  return {
9382
9665
  name,
9383
9666
  version,
@@ -11137,7 +11420,7 @@ function findDependency(manifests, id, isReleased) {
11137
11420
  const index = buildSupplyIndex(manifests.map((manifest) => ({
11138
11421
  id: manifest.id,
11139
11422
  needs: needsOf(manifest)
11140
- })));
11423
+ })), { env: process.env });
11141
11424
  const entry = parsed.kind === "extension" || parsed.kind === "lib" ? index.entryOf(parsed.kind, parsed.name) : void 0;
11142
11425
  const conflict = entry?.conflicted === true ? index.conflicts.find((item) => item.names.includes(parsed.name))?.detail : void 0;
11143
11426
  return {
@@ -11469,6 +11752,15 @@ async function updateModuleBundle(unit, deps) {
11469
11752
  registry,
11470
11753
  force: true
11471
11754
  });
11755
+ if (latest !== void 0 && deps.shell !== void 0 && registry !== void 0) {
11756
+ const declared = await supply.registryDeclarationOf({
11757
+ shell: deps.shell,
11758
+ name: pkg,
11759
+ registry,
11760
+ force: true
11761
+ });
11762
+ if (declared !== void 0 && supply.declarationRefusalOf(pkg, declared).length > 0) throw new Error("源上的新版和当前这一版工作台不匹配 —— 先升级工作台,再更新这个模块。");
11763
+ }
11472
11764
  const spec = latest === void 0 ? pkg : `${pkg}@${latest}`;
11473
11765
  const outcome = await replaceDshBundle(deps.routeDeps, spec, { ...registry === void 0 ? {} : { registry } });
11474
11766
  await deps.sweep();
@@ -11578,6 +11870,80 @@ async function updateWorkbenchOnce(deps) {
11578
11870
  };
11579
11871
  }
11580
11872
  //#endregion
11873
+ //#region src/shared/api/host/migrate-workbench.ts
11874
+ async function migrateWorkbench(input) {
11875
+ const script = join(input.home, "profiles", input.profile, "node_modules", input.pkg, "scripts", "share-data-root.mjs");
11876
+ if (!existsSync(script)) throw new Error(`找不到迁移脚本(${script})—— 这一份没装全?`);
11877
+ const runtimeRoot = process.env.DSH_WORKBENCH_HOME ?? join(input.home, input.profile);
11878
+ const result = await input.run({
11879
+ script,
11880
+ env: {
11881
+ DSH_HOME: input.home,
11882
+ WORKBENCH_PROFILE: input.profile,
11883
+ DSH_WORKBENCH_HOME: runtimeRoot
11884
+ }
11885
+ });
11886
+ if (result.exitCode !== 0) {
11887
+ const tail = (result.stderr || result.stdout).trim().split("\n").slice(-6).join("\n");
11888
+ throw new Error(`迁移没成(退出码 ${result.exitCode})${tail === "" ? "" : `:${tail}`}`);
11889
+ }
11890
+ return {
11891
+ added: Number.parseInt(/新增\s+(\d+)\s+个项目/.exec(result.stdout)?.[1] ?? "0", 10),
11892
+ files: Number.parseInt(/补了\s+(\d+)\s+个文件/.exec(result.stdout)?.[1] ?? "0", 10)
11893
+ };
11894
+ }
11895
+ //#endregion
11896
+ //#region src/modules/panels/model/market-follow-up.ts
11897
+ function createWorkbenchFollowUps(deps) {
11898
+ const runMigration = deps.runMigration ?? (async (input) => {
11899
+ const result = await execa(process.execPath, [
11900
+ input.script,
11901
+ "--migrate",
11902
+ "--force"
11903
+ ], {
11904
+ env: {
11905
+ ...process.env,
11906
+ ELECTRON_RUN_AS_NODE: "1",
11907
+ ...input.env
11908
+ },
11909
+ reject: false,
11910
+ timeout: 12e4
11911
+ });
11912
+ return {
11913
+ exitCode: result.exitCode ?? 1,
11914
+ stdout: result.stdout,
11915
+ stderr: result.stderr
11916
+ };
11917
+ });
11918
+ function homeAndProfile() {
11919
+ if (deps.dshHome === void 0 || deps.profileName === void 0) throw new Error("拿不到 DSH_HOME 或 profile 名字 —— 这一步做不了。");
11920
+ const home = deps.dshHome();
11921
+ const profile = deps.profileName();
11922
+ if (profile === null || profile === "") throw new Error("拿不到 profile 名字 —— 这一步做不了。");
11923
+ return {
11924
+ home,
11925
+ profile
11926
+ };
11927
+ }
11928
+ return {
11929
+ async migrateWorkbench() {
11930
+ const { home, profile } = homeAndProfile();
11931
+ const read = deps.workbench?.(PLUGIN_ID);
11932
+ if (read === void 0) throw new Error(`读不到 profile 里装的 ${PLUGIN_ID} —— 迁移做不了。`);
11933
+ return migrateWorkbench({
11934
+ home,
11935
+ profile,
11936
+ pkg: read.pkg,
11937
+ run: runMigration
11938
+ });
11939
+ },
11940
+ async dismissFollowUp(id) {
11941
+ const { home, profile } = homeAndProfile();
11942
+ dismissFollowUp(process.env.DSH_WORKBENCH_HOME ?? join(home, profile), id);
11943
+ }
11944
+ };
11945
+ }
11946
+ //#endregion
11581
11947
  //#region src/modules/panels/model/plugin-actions.ts
11582
11948
  function pluginKeptOf(retention, detail) {
11583
11949
  if (retention === "keep-held") return { kind: "held" };
@@ -11751,7 +12117,7 @@ function createEnvDropper(options) {
11751
12117
  const index = buildSupplyIndex(catalog().filter((item) => options.isReleased?.(item.id) !== true).map((item) => ({
11752
12118
  id: item.id,
11753
12119
  needs: needsOf(item)
11754
- })));
12120
+ })), { env: process.env });
11755
12121
  const extensions = (needs ?? []).filter((need) => need.kind === "extension");
11756
12122
  const libs = (needs ?? []).filter((need) => need.kind === "lib");
11757
12123
  const onMachine = new Set(before.needs.filter((state) => state.kind === "extension" && state.status !== "missing").map((state) => state.name));
@@ -12181,6 +12547,12 @@ function createMarketService(options) {
12181
12547
  await reprobePlugin(pkg, outcome.name);
12182
12548
  return outcome;
12183
12549
  },
12550
+ ...createWorkbenchFollowUps({
12551
+ dshHome,
12552
+ profileName,
12553
+ workbench,
12554
+ ...options.runMigration === void 0 ? {} : { runMigration: options.runMigration }
12555
+ }),
12184
12556
  updateDependency: updateDependencyRow$1
12185
12557
  };
12186
12558
  return service;
@@ -12410,6 +12782,12 @@ function marketDiscoverRoutes(discover) {
12410
12782
  }
12411
12783
  //#endregion
12412
12784
  //#region src/modules/panels/model/market-sources-act.ts
12785
+ function marketSourcesRoutes(store) {
12786
+ return { [MARKET_SOURCES_ROUTE]: async () => ({
12787
+ ok: true,
12788
+ ...store.read()
12789
+ }) };
12790
+ }
12413
12791
  function asRecord(value) {
12414
12792
  return typeof value === "object" && value !== null ? value : void 0;
12415
12793
  }
@@ -12491,13 +12869,29 @@ function marketSourcesAction(store, init) {
12491
12869
  }
12492
12870
  //#endregion
12493
12871
  //#region src/modules/panels/model/publish-preview-routes.ts
12494
- function publishPreviewRoutes(workshop) {
12495
- return { [MARKET_PUBLISH_PREVIEW_ROUTE]: (search) => {
12872
+ function publishPreviewRoutes(workshop, inferredAuthorOf = async () => void 0) {
12873
+ return { [MARKET_PUBLISH_PREVIEW_ROUTE]: async (search) => {
12496
12874
  const unit = (param(search, "unit") ?? "").trim();
12497
12875
  const version = (param(search, "version") ?? "").trim();
12876
+ const author = (param(search, "author") ?? "").trim();
12877
+ if (unit === "") return {
12878
+ ok: true,
12879
+ preview: null
12880
+ };
12881
+ let preview = publishPreviewOf(workshop, unit, {
12882
+ version,
12883
+ author
12884
+ });
12885
+ if (preview !== null && author === "" && (preview.author === void 0 || preview.author === "")) {
12886
+ const inferred = await inferredAuthorOf(unit);
12887
+ if (inferred !== void 0 && inferred !== "") preview = {
12888
+ ...preview,
12889
+ author: inferred
12890
+ };
12891
+ }
12498
12892
  return {
12499
12893
  ok: true,
12500
- preview: unit === "" ? null : publishPreviewOf(workshop, unit, { version })
12894
+ preview
12501
12895
  };
12502
12896
  } };
12503
12897
  }
@@ -12506,6 +12900,7 @@ function publishPreviewRoutes(workshop) {
12506
12900
  var DEFAULT_TTL_MS = 3e5;
12507
12901
  var DEFAULT_TIMEOUT_MS$2 = 3e4;
12508
12902
  var DEFAULT_MAX_BYTES$1 = 4194304;
12903
+ var GIT_NO_PROMPT = `${quoteShellArg("env")} GIT_TERMINAL_PROMPT=0`;
12509
12904
  function messageOf$1(error) {
12510
12905
  return error instanceof Error ? error.message : String(error);
12511
12906
  }
@@ -12671,7 +13066,7 @@ function createMarketIndexReader(options) {
12671
13066
  }
12672
13067
  };
12673
13068
  try {
12674
- const result = await runShell(shell, line, { timeoutMs });
13069
+ const result = await runShell(shell, `${GIT_NO_PROMPT} ${line}`, { timeoutMs });
12675
13070
  if (result.exitCode !== 0) return {
12676
13071
  ok: false,
12677
13072
  problem: classifyGitError(result.stderr, result.timedOut),
@@ -13228,6 +13623,18 @@ async function dispatchMarket(market, request) {
13228
13623
  action: "cancel-install",
13229
13624
  cancellation: await market.cancelInstall(request.requestId ?? "")
13230
13625
  };
13626
+ if (request.action === "migrate-workbench") return {
13627
+ ok: true,
13628
+ action: "migrate-workbench",
13629
+ ...await market.migrateWorkbench()
13630
+ };
13631
+ if (request.action === "dismiss-followup") {
13632
+ await market.dismissFollowUp(request.id ?? "");
13633
+ return {
13634
+ ok: true,
13635
+ action: "dismiss-followup"
13636
+ };
13637
+ }
13231
13638
  if (request.action === "update-workbench") return {
13232
13639
  ok: true,
13233
13640
  action: "update-workbench",
@@ -14382,12 +14789,13 @@ var OWNED = [{
14382
14789
  ...diyDebugRoutes(debug),
14383
14790
  ...diyRecordRoutes(record),
14384
14791
  ...marketDiscoverRoutes(discover),
14385
- ...publishPreviewRoutes(workshopPathOf(ctx))
14792
+ ...marketSourcesRoutes(marketSources),
14793
+ ...publishPreviewRoutes(workshopPathOf(ctx), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell))
14386
14794
  },
14387
14795
  registerTools: () => [
14388
14796
  ...registerPanelTools(ctx, panels, values),
14389
14797
  ...registerMarketTools(ctx, market),
14390
- ...registerDiyTools(ctx, market, panels, debug, (unit) => publishPreviewOf(workshopPathOf(ctx), unit)),
14798
+ ...registerDiyTools(ctx, market, panels, debug, (unit) => publishPreviewOf(workshopPathOf(ctx), unit), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell)),
14391
14799
  ...registerRecordTools(ctx, record),
14392
14800
  ...registerLongTaskTools(ctx, longTaskRuns)
14393
14801
  ],