@dsh-workbench/shared 0.1.17 → 0.1.18

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.
@@ -8,6 +8,7 @@ export interface UnitCardDeclaration {
8
8
  export interface UnitToolDeclaration {
9
9
  readonly name: string;
10
10
  readonly writes: boolean;
11
+ readonly key?: string;
11
12
  }
12
13
  export interface UnitDeclaration {
13
14
  readonly apiVersion: number;
@@ -16,7 +16,8 @@ export const DECLARATION_REQUIRED_KEYS = [
16
16
  'host',
17
17
  ];
18
18
  const CARD_KEYS = ['titleKey', 'order'];
19
- const TOOL_KEYS = ['name', 'writes'];
19
+ const TOOL_KEYS = ['name', 'writes', 'key'];
20
+ const TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
20
21
  const HOST_KEYS = ['inject', 'optional'];
21
22
  function text(value) {
22
23
  return typeof value === 'string' ? value.trim() : '';
@@ -89,6 +90,7 @@ export function declarationProblemsOf(subject, raw) {
89
90
  }
90
91
  else {
91
92
  const seen = new Set();
93
+ const seenKeys = new Set();
92
94
  tools.forEach((tool, index) => {
93
95
  const at = `dsh.workbench.tools[${index}]`;
94
96
  if (!isRecord(tool)) {
@@ -112,6 +114,19 @@ export function declarationProblemsOf(subject, raw) {
112
114
  if (typeof tool.writes !== 'boolean') {
113
115
  problems.push(`${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`);
114
116
  }
117
+ if (tool.key !== undefined) {
118
+ const key = text(tool.key);
119
+ if (!TOOL_KEY_SHAPE.test(key)) {
120
+ problems.push(`${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),` +
121
+ `现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`);
122
+ }
123
+ else if (seenKeys.has(key)) {
124
+ problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
125
+ }
126
+ else {
127
+ seenKeys.add(key);
128
+ }
129
+ }
115
130
  });
116
131
  }
117
132
  }
package/dist/i18n/en.js CHANGED
@@ -65,11 +65,10 @@ export const en = {
65
65
  'panels.market.missing': 'not installed',
66
66
  'panels.market.unchecked': 'unknown',
67
67
  'panels.market.checking': 'checking',
68
- 'panels.market.sectionInstalled': 'Installed',
68
+ 'panels.market.sectionInstalled': 'Installed modules',
69
69
  'panels.market.sectionPending': 'Not installed',
70
70
  'panels.market.sectionShared': 'Shared dependencies',
71
71
  'panels.market.sharedUsedBy': 'Used by {modules}',
72
- 'panels.market.sharedFoot': 'These cannot be uninstalled here: they come with modules, and would be reinstalled anyway. To stop using one, turn off the modules that use it.',
73
72
  'panels.market.listSeparator': ', ',
74
73
  'panels.market.sharedMissing': 'not installed',
75
74
  'panels.market.sharedOutdated': 'update available',
@@ -94,15 +93,13 @@ export const en = {
94
93
  'panels.market.noSource': 'This module declares no install method — install it yourself',
95
94
  'panels.market.badSource': 'The install method is incomplete — install it yourself',
96
95
  'panels.market.empty': 'No modules available',
97
- 'panels.market.installedSection': 'Installed',
98
- 'panels.market.sectionLocal': 'Linked locally',
96
+ 'panels.market.sectionLocal': 'Local modules',
99
97
  'panels.market.sectionUnlisted': 'Not from the market',
100
98
  'panels.market.sectionPlugins': 'Installed opencli plugins',
101
99
  'panels.market.moduleOutdated': 'update available',
102
100
  'panels.market.moduleRefused': 'This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.',
103
101
  'panels.market.moduleRefusedDetail': 'Why it cannot be used: {reason}',
104
102
  'panels.market.moduleRestartHint': 'installed but not active — restart dsh once to bring it up',
105
- 'panels.market.pluginLocalNoUpdate': 'Installed from a local folder, so there is nowhere to check for a newer version — uninstall only',
106
103
  'panels.market.pluginKeptInUse': 'Files kept for now: {names} still uses it.',
107
104
  'panels.market.pluginKeptUnsure': 'Files kept for now: some modules on this machine do not say what they use, so it is unclear who needs it.',
108
105
  'panels.market.pluginKeptHeld': 'Files kept: this one is yours, and a module letting go will not remove it.',
@@ -67,11 +67,10 @@ export declare const dictionaries: {
67
67
  readonly 'panels.market.missing': "未装";
68
68
  readonly 'panels.market.unchecked': "状态未知";
69
69
  readonly 'panels.market.checking': "检测中";
70
- readonly 'panels.market.sectionInstalled': "已安装";
70
+ readonly 'panels.market.sectionInstalled': "已安装模块";
71
71
  readonly 'panels.market.sectionPending': "未安装";
72
72
  readonly 'panels.market.sectionShared': "共用依赖";
73
73
  readonly 'panels.market.sharedUsedBy': "供 {modules} 使用";
74
- readonly 'panels.market.sharedFoot': "这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。";
75
74
  readonly 'panels.market.listSeparator': "、";
76
75
  readonly 'panels.market.sharedMissing': "未装";
77
76
  readonly 'panels.market.sharedOutdated': "有新版";
@@ -96,15 +95,13 @@ export declare const dictionaries: {
96
95
  readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
97
96
  readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
98
97
  readonly 'panels.market.empty': "暂无可用模块";
99
- readonly 'panels.market.installedSection': "已安装";
100
- readonly 'panels.market.sectionLocal': "本地装的";
98
+ readonly 'panels.market.sectionLocal': "本地模块";
101
99
  readonly 'panels.market.sectionUnlisted': "不是从市场装的";
102
100
  readonly 'panels.market.sectionPlugins': "已安装 opencli 插件";
103
101
  readonly 'panels.market.moduleOutdated': "有新版";
104
102
  readonly 'panels.market.moduleRefused': "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。";
105
103
  readonly 'panels.market.moduleRefusedDetail': "装不上的原因:{reason}";
106
104
  readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
107
- readonly 'panels.market.pluginLocalNoUpdate': "本地装的,没地方查新版,所以只给卸载";
108
105
  readonly 'panels.market.pluginKeptInUse': "文件先留着:{names} 还要用它。";
109
106
  readonly 'panels.market.pluginKeptUnsure': "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。";
110
107
  readonly 'panels.market.pluginKeptHeld': "文件留着:你自己装的那一份,模块放手不动它。";
@@ -329,7 +326,7 @@ export declare const dictionaries: {
329
326
  readonly 'panel.conversation': "对话";
330
327
  readonly 'panels.label': "全局面板";
331
328
  };
332
- en: Record<"time.now" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "panels.title" | "panels.reset" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.hintEffect" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.outdated" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.sharedFoot" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.installedSection" | "panels.market.sectionLocal" | "panels.market.sectionUnlisted" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.pluginLocalNoUpdate" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginAlsoKnownAs" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
329
+ en: Record<"time.now" | "time.minutes" | "time.hours" | "time.days" | "blocker.retry" | "blocker.copy" | "blocker.unknown.title" | "blocker.login.go" | "blocker.login.titleGeneric" | "blocker.login.title" | "blocker.login.manual" | "blocker.login.hint" | "blocker.forbidden.open" | "blocker.forbidden.titleGeneric" | "blocker.forbidden.title" | "blocker.forbidden.hint" | "blocker.timeout.hint" | "blocker.exec.hint" | "blocker.fix.install" | "blocker.fix.update" | "blocker.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.missingPlugin" | "env.unit.hint" | "env.unit.missingCommands" | "env.unit.missingCommandsHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.rename.row" | "project.rename.current" | "project.rename.title" | "project.rename.description" | "project.rename.field" | "project.rename.placeholder" | "project.rename.hint" | "project.rename.save" | "project.rename.saving" | "project.rename.cancel" | "project.rename.failed" | "session.new" | "session.new.label" | "session.empty" | "session.noProject" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "panels.title" | "panels.reset" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.hintEffect" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.dep.failed" | "panels.dep.missingPlugin" | "panels.dep.missingCommands" | "panels.dep.unchecked" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.outdated" | "panels.market.hasNew" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.checking" | "panels.market.sectionInstalled" | "panels.market.sectionPending" | "panels.market.sectionShared" | "panels.market.sharedUsedBy" | "panels.market.listSeparator" | "panels.market.sharedMissing" | "panels.market.sharedOutdated" | "panels.market.sharedUnsatisfied" | "panels.market.sharedOk" | "panels.market.versionArrow" | "panels.market.lead" | "panels.market.install" | "panels.market.update" | "panels.market.uninstall" | "panels.market.uninstallLabel" | "panels.market.installing" | "panels.market.updating" | "panels.market.uninstalling" | "panels.market.confirmUninstallTitle" | "panels.market.confirmUninstallDetail" | "panels.market.confirmUninstallAck" | "panels.market.confirmYes" | "panels.market.confirmNo" | "panels.market.failed" | "panels.market.needEnv" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionLocal" | "panels.market.sectionUnlisted" | "panels.market.sectionPlugins" | "panels.market.moduleOutdated" | "panels.market.moduleRefused" | "panels.market.moduleRefusedDetail" | "panels.market.moduleRestartHint" | "panels.market.pluginKeptInUse" | "panels.market.pluginKeptUnsure" | "panels.market.pluginKeptHeld" | "panels.market.pluginKeptUnnamed" | "panels.market.pluginAlsoKnownAs" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.recheck" | "panels.market.checkUpdates" | "panels.market.checkedAt" | "panels.market.justNow" | "panels.market.minutesAgo" | "panels.market.hoursAgo" | "panels.market.add.description" | "panels.market.add.specPlaceholder" | "panels.market.add.guideToggle" | "panels.market.add.guideHide" | "panels.market.add.guideIdTitle" | "panels.market.add.guideIdExample" | "panels.market.add.guideIdHint" | "panels.market.add.guideGitTitle" | "panels.market.add.guideGitExample" | "panels.market.add.guideGitHint" | "panels.market.add.guidePathTitle" | "panels.market.add.guidePathExample" | "panels.market.add.guidePathHint" | "panels.market.add.guideExampleLabel" | "panels.market.add.guideFill" | "panels.market.add.guideSafety" | "panels.market.add.registryLegend" | "panels.market.add.registryOfficial" | "panels.market.add.registryMirror" | "panels.market.add.registryWithHost" | "panels.market.add.registryToggle" | "panels.market.add.installVersion" | "panels.market.add.registryDefault" | "panels.market.add.registryCustom" | "panels.market.add.registryCustomPlaceholder" | "panels.market.add.registryCustomHint" | "panels.market.add.registryCustomInvalid" | "panels.market.add.run" | "panels.market.add.edit" | "panels.market.add.changeRegistry" | "panels.market.add.installingTitle" | "panels.market.add.installedTitle" | "panels.market.add.failedTitle" | "panels.market.add.cancelledTitle" | "panels.market.add.failureNetworkHost" | "panels.market.add.problemInvalid" | "panels.market.add.problemInstalled" | "panels.market.add.problemNotFound" | "panels.market.add.problemNotPackage" | "panels.market.add.problemNotBundle" | "panels.market.add.problemNotWorkbenchModule" | "panels.market.add.problemNotModuleOrPlugin" | "panels.market.add.failureNotModuleOrPlugin" | "panels.market.add.installedPlugin" | "panels.market.add.problemNetwork" | "panels.market.add.problemUnknown" | "panels.market.add.failureNetwork" | "panels.market.add.failureNotFound" | "panels.market.add.failureNoMatchingVersion" | "panels.market.add.failureDiskFull" | "panels.market.add.failurePermission" | "panels.market.add.failureBuildBlocked" | "panels.market.add.failureIntegrity" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureGeneric" | "panels.market.add.approveTitle" | "panels.market.add.approveCaution" | "panels.market.add.approveRun" | "panels.market.add.restart" | "panels.market.add.tookEffect" | "panels.market.add.details" | "panels.market.add.open" | "panels.market.add.title" | "panels.market.add.intro" | "panels.market.add.specLabel" | "panels.market.add.specHint" | "panels.market.add.exampleTitle" | "panels.market.add.exampleFill" | "panels.market.add.registryLabel" | "panels.market.add.registryProfile" | "panels.market.add.registryPnpm" | "panels.market.add.check" | "panels.market.add.checking" | "panels.market.add.classifying" | "panels.market.add.change" | "panels.market.add.previewTitle" | "panels.market.add.fieldName" | "panels.market.add.fieldVersion" | "panels.market.add.fieldDescription" | "panels.market.add.fieldSource" | "panels.market.add.install" | "panels.market.add.installing" | "panels.market.add.cancel" | "panels.market.add.close" | "panels.market.add.retry" | "panels.market.add.changeSource" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.cancelled" | "panels.market.add.failed" | "panels.market.add.removed" | "panels.market.add.supplyPending" | "panels.market.add.supplyFailed" | "panels.market.add.notModule" | "panels.market.add.removedRestart" | "panels.market.add.remove" | "panels.market.add.problem.invalidSpec" | "panels.market.add.problem.alreadyInstalled" | "panels.market.add.problem.notFound" | "panels.market.add.problem.notAPackage" | "panels.market.add.problem.notABundle" | "panels.market.add.problem.network" | "panels.market.add.problem.unknown" | "panels.market.add.reason.invalidSpec" | "panels.market.add.reason.incompatible" | "panels.market.add.reason.notBundle" | "panels.market.add.reason.notRemovable" | "panels.market.add.reason.bundleInUse" | "panels.market.add.reason.stopProfile" | "panels.market.add.reason.staleApproval" | "panels.market.add.reason.operationError" | "panels.market.add.reason.buildBlocked" | "panels.market.add.reason.pnpmMissing" | "panels.market.add.reason.noMatchingVersion" | "panels.market.add.reason.diskFull" | "panels.market.add.reason.permission" | "panels.market.add.reason.integrity" | "panels.market.add.reason.timeout" | "panels.market.add.reason.network" | "panels.market.add.reason.notFound" | "panels.market.add.reason.unknown" | "blocker.fix.running" | "blocker.fix.failed" | "blocker.retry.running" | "panels.reading" | "panels.cardBroken" | "panels.cardRetry" | "panels.empty" | "panels.emptyHint" | "env.title" | "env.checking" | "env.installing" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.daemon" | "env.bridge.extension" | "env.bridge.extensionLink" | "env.bridge.profile" | "env.bridge.stale" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.copyCommand" | "env.copied" | "env.step.opencli" | "env.step.codingPlugin" | "env.step.bridge" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "rightbar.label" | "rightbar.expand" | "rightbar.collapse" | "rightbar.column.label" | "rightbar.column.workbench" | "rightbar.column.dsh" | "rightbar.refresh" | "rightbar.refreshing" | "rightbar.updatedAt" | "rightbar.noProject" | "rightbar.openPlatform" | "coding.notARepo.title" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label", string>;
333
330
  };
334
331
  export type { WorkbenchMessages };
335
332
  export interface Dictionaries {
package/dist/i18n/zh.d.ts CHANGED
@@ -65,11 +65,10 @@ export declare const zh: {
65
65
  readonly 'panels.market.missing': "未装";
66
66
  readonly 'panels.market.unchecked': "状态未知";
67
67
  readonly 'panels.market.checking': "检测中";
68
- readonly 'panels.market.sectionInstalled': "已安装";
68
+ readonly 'panels.market.sectionInstalled': "已安装模块";
69
69
  readonly 'panels.market.sectionPending': "未安装";
70
70
  readonly 'panels.market.sectionShared': "共用依赖";
71
71
  readonly 'panels.market.sharedUsedBy': "供 {modules} 使用";
72
- readonly 'panels.market.sharedFoot': "这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。";
73
72
  readonly 'panels.market.listSeparator': "、";
74
73
  readonly 'panels.market.sharedMissing': "未装";
75
74
  readonly 'panels.market.sharedOutdated': "有新版";
@@ -94,15 +93,13 @@ export declare const zh: {
94
93
  readonly 'panels.market.noSource': "这个模块没声明怎么装,只能自己动手";
95
94
  readonly 'panels.market.badSource': "安装方式没写全,只能自己动手";
96
95
  readonly 'panels.market.empty': "暂无可用模块";
97
- readonly 'panels.market.installedSection': "已安装";
98
- readonly 'panels.market.sectionLocal': "本地装的";
96
+ readonly 'panels.market.sectionLocal': "本地模块";
99
97
  readonly 'panels.market.sectionUnlisted': "不是从市场装的";
100
98
  readonly 'panels.market.sectionPlugins': "已安装 opencli 插件";
101
99
  readonly 'panels.market.moduleOutdated': "有新版";
102
100
  readonly 'panels.market.moduleRefused': "这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。";
103
101
  readonly 'panels.market.moduleRefusedDetail': "装不上的原因:{reason}";
104
102
  readonly 'panels.market.moduleRestartHint': "装好了但还没生效 —— 重启一次 dsh 就会出来";
105
- readonly 'panels.market.pluginLocalNoUpdate': "本地装的,没地方查新版,所以只给卸载";
106
103
  readonly 'panels.market.pluginKeptInUse': "文件先留着:{names} 还要用它。";
107
104
  readonly 'panels.market.pluginKeptUnsure': "文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。";
108
105
  readonly 'panels.market.pluginKeptHeld': "文件留着:你自己装的那一份,模块放手不动它。";
package/dist/i18n/zh.js CHANGED
@@ -65,11 +65,10 @@ export const zh = {
65
65
  'panels.market.missing': '未装',
66
66
  'panels.market.unchecked': '状态未知',
67
67
  'panels.market.checking': '检测中',
68
- 'panels.market.sectionInstalled': '已安装',
68
+ 'panels.market.sectionInstalled': '已安装模块',
69
69
  'panels.market.sectionPending': '未安装',
70
70
  'panels.market.sectionShared': '共用依赖',
71
71
  'panels.market.sharedUsedBy': '供 {modules} 使用',
72
- 'panels.market.sharedFoot': '这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。',
73
72
  'panels.market.listSeparator': '、',
74
73
  'panels.market.sharedMissing': '未装',
75
74
  'panels.market.sharedOutdated': '有新版',
@@ -94,15 +93,13 @@ export const zh = {
94
93
  'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
95
94
  'panels.market.badSource': '安装方式没写全,只能自己动手',
96
95
  'panels.market.empty': '暂无可用模块',
97
- 'panels.market.installedSection': '已安装',
98
- 'panels.market.sectionLocal': '本地装的',
96
+ 'panels.market.sectionLocal': '本地模块',
99
97
  'panels.market.sectionUnlisted': '不是从市场装的',
100
98
  'panels.market.sectionPlugins': '已安装 opencli 插件',
101
99
  'panels.market.moduleOutdated': '有新版',
102
100
  'panels.market.moduleRefused': '这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。',
103
101
  'panels.market.moduleRefusedDetail': '装不上的原因:{reason}',
104
102
  'panels.market.moduleRestartHint': '装好了但还没生效 —— 重启一次 dsh 就会出来',
105
- 'panels.market.pluginLocalNoUpdate': '本地装的,没地方查新版,所以只给卸载',
106
103
  'panels.market.pluginKeptInUse': '文件先留着:{names} 还要用它。',
107
104
  'panels.market.pluginKeptUnsure': '文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。',
108
105
  'panels.market.pluginKeptHeld': '文件留着:你自己装的那一份,模块放手不动它。',
@@ -0,0 +1,2 @@
1
+ import type { UnitManifest } from '../api/unit.js';
2
+ export declare function declarationOfPackage(pkg: string, packageJson: URL | string): UnitManifest;
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { readDeclaration } from '../api/unit-declaration.js';
4
+ export function declarationOfPackage(pkg, packageJson) {
5
+ const at = typeof packageJson === 'string' ? packageJson : fileURLToPath(packageJson);
6
+ let parsed;
7
+ try {
8
+ parsed = JSON.parse(fs.readFileSync(at, 'utf8'));
9
+ }
10
+ catch (error) {
11
+ throw new Error(`${pkg}: 读不动 ${at} —— ${String(error)}`, { cause: error });
12
+ }
13
+ const declared = typeof parsed === 'object' && parsed !== null
14
+ ? parsed.dsh?.workbench
15
+ : undefined;
16
+ const read = readDeclaration(pkg, declared);
17
+ if (read.manifest === undefined) {
18
+ throw new Error(`${pkg} 的 dsh.workbench 读不出来:${read.problems.join(';')}`);
19
+ }
20
+ return read.manifest;
21
+ }
package/dist/testing.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';
package/dist/testing.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/shared",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "工作台与模块共享的纯代码与契约类型:类型、纯逻辑、哑组件、平台解析。宿主侧的东西从 /host 进(不影响浏览器产物)。",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,52 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { declarationProblemsOf, readDeclaration } from '../unit-declaration.js';
3
+
4
+ const declared = (tools: unknown) => ({
5
+ apiVersion: 1,
6
+ unit: 'demo',
7
+ card: null,
8
+ tools,
9
+ host: { inject: [] },
10
+ });
11
+
12
+ describe('清单里的工具声明', () => {
13
+ it('key 是可选的:不给就只是没有生成物可对', () => {
14
+ expect(declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true }]))).toEqual([]);
15
+ });
16
+
17
+ it('给了 key 就得是能当代码键名的 lowerCamelCase', () => {
18
+ expect(
19
+ declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'sync' }])),
20
+ ).toEqual([]);
21
+ expect(
22
+ declarationProblemsOf('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'podLogs' }])),
23
+ ).toEqual([]);
24
+
25
+ for (const bad of ['Sync', 'pod-logs', 'pod_logs', '1sync', '', 'pod logs', '__proto__']) {
26
+ const problems = declarationProblemsOf(
27
+ '@t/demo',
28
+ declared([{ name: 'workbench_demo_sync', writes: true, key: bad }]),
29
+ );
30
+ expect(problems.join('\n'), `key=${JSON.stringify(bad)} 该红`).toMatch(/\.key 要是 lowerCamelCase/);
31
+ }
32
+ });
33
+
34
+ it('★ key 撞了当场红(生成物里的键只能有一个,不然生成出来是静默覆盖)', () => {
35
+ const problems = declarationProblemsOf(
36
+ '@t/demo',
37
+ declared([
38
+ { name: 'workbench_demo_sync', writes: true, key: 'sync' },
39
+ { name: 'workbench_demo_apply', writes: true, key: 'sync' },
40
+ ]),
41
+ );
42
+
43
+ expect(problems.join('\n')).toMatch(/key「sync」与前面那条撞了/);
44
+ });
45
+
46
+ it('key 不进外壳那份清单(外壳不读它,它是给代码用的)', () => {
47
+ const read = readDeclaration('@t/demo', declared([{ name: 'workbench_demo_sync', writes: true, key: 'sync' }]));
48
+
49
+ expect(read.problems).toEqual([]);
50
+ expect(read.manifest?.tools).toEqual([{ name: 'workbench_demo_sync', writes: true }]);
51
+ });
52
+ });
@@ -12,6 +12,7 @@ export interface UnitCardDeclaration {
12
12
  export interface UnitToolDeclaration {
13
13
  readonly name: string;
14
14
  readonly writes: boolean;
15
+ readonly key?: string;
15
16
  }
16
17
 
17
18
  export interface UnitDeclaration {
@@ -41,7 +42,9 @@ export const DECLARATION_REQUIRED_KEYS: readonly string[] = [
41
42
  ];
42
43
 
43
44
  const CARD_KEYS = ['titleKey', 'order'];
44
- const TOOL_KEYS = ['name', 'writes'];
45
+ const TOOL_KEYS = ['name', 'writes', 'key'];
46
+
47
+ const TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
45
48
  const HOST_KEYS = ['inject', 'optional'];
46
49
 
47
50
  function text(value: unknown): string {
@@ -130,6 +133,7 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
130
133
  problems.push(`${subject}: dsh.workbench.tools 要是一串声明,现在是 ${JSON.stringify(tools)}`);
131
134
  } else {
132
135
  const seen = new Set<string>();
136
+ const seenKeys = new Set<string>();
133
137
  tools.forEach((tool, index) => {
134
138
  const at = `dsh.workbench.tools[${index}]`;
135
139
  if (!isRecord(tool)) {
@@ -153,6 +157,19 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
153
157
  `${subject}: ${at}.writes 要是 true / false(它是"这会写"的唯一声明,缺了审批无从认领)`,
154
158
  );
155
159
  }
160
+ if (tool.key !== undefined) {
161
+ const key = text(tool.key);
162
+ if (!TOOL_KEY_SHAPE.test(key)) {
163
+ problems.push(
164
+ `${subject}: ${at}.key 要是 lowerCamelCase 的标识符(如 \`sync\` / \`podLogs\`),` +
165
+ `现在是 ${JSON.stringify(tool.key)} —— 它会变成生成物里的键名,所以不引号、不许带点或横线`,
166
+ );
167
+ } else if (seenKeys.has(key)) {
168
+ problems.push(`${subject}: ${at}.key「${key}」与前面那条撞了 —— 生成物里的键只能有一个`);
169
+ } else {
170
+ seenKeys.add(key);
171
+ }
172
+ }
156
173
  });
157
174
  }
158
175
  }
package/src/i18n/en.ts CHANGED
@@ -70,11 +70,10 @@ export const en: Record<keyof WorkbenchMessages, string> = {
70
70
  'panels.market.missing': 'not installed',
71
71
  'panels.market.unchecked': 'unknown',
72
72
  'panels.market.checking': 'checking',
73
- 'panels.market.sectionInstalled': 'Installed',
73
+ 'panels.market.sectionInstalled': 'Installed modules',
74
74
  'panels.market.sectionPending': 'Not installed',
75
75
  'panels.market.sectionShared': 'Shared dependencies',
76
76
  'panels.market.sharedUsedBy': 'Used by {modules}',
77
- 'panels.market.sharedFoot': 'These cannot be uninstalled here: they come with modules, and would be reinstalled anyway. To stop using one, turn off the modules that use it.',
78
77
  'panels.market.listSeparator': ', ',
79
78
  'panels.market.sharedMissing': 'not installed',
80
79
  'panels.market.sharedOutdated': 'update available',
@@ -101,15 +100,13 @@ export const en: Record<keyof WorkbenchMessages, string> = {
101
100
  'panels.market.noSource': 'This module declares no install method — install it yourself',
102
101
  'panels.market.badSource': 'The install method is incomplete — install it yourself',
103
102
  'panels.market.empty': 'No modules available',
104
- 'panels.market.installedSection': 'Installed',
105
- 'panels.market.sectionLocal': 'Linked locally',
103
+ 'panels.market.sectionLocal': 'Local modules',
106
104
  'panels.market.sectionUnlisted': 'Not from the market',
107
105
  'panels.market.sectionPlugins': 'Installed opencli plugins',
108
106
  'panels.market.moduleOutdated': 'update available',
109
107
  'panels.market.moduleRefused': 'This module was built for an older version of the workbench, so it does not work here. Update it, or uninstall.',
110
108
  'panels.market.moduleRefusedDetail': 'Why it cannot be used: {reason}',
111
109
  'panels.market.moduleRestartHint': 'installed but not active — restart dsh once to bring it up',
112
- 'panels.market.pluginLocalNoUpdate': 'Installed from a local folder, so there is nowhere to check for a newer version — uninstall only',
113
110
  'panels.market.pluginKeptInUse': 'Files kept for now: {names} still uses it.',
114
111
  'panels.market.pluginKeptUnsure': 'Files kept for now: some modules on this machine do not say what they use, so it is unclear who needs it.',
115
112
  'panels.market.pluginKeptHeld': 'Files kept: this one is yours, and a module letting go will not remove it.',
package/src/i18n/zh.ts CHANGED
@@ -68,11 +68,10 @@ export const zh = {
68
68
  'panels.market.missing': '未装',
69
69
  'panels.market.unchecked': '状态未知',
70
70
  'panels.market.checking': '检测中',
71
- 'panels.market.sectionInstalled': '已安装',
71
+ 'panels.market.sectionInstalled': '已安装模块',
72
72
  'panels.market.sectionPending': '未安装',
73
73
  'panels.market.sectionShared': '共用依赖',
74
74
  'panels.market.sharedUsedBy': '供 {modules} 使用',
75
- 'panels.market.sharedFoot': '这一段不给卸载:它们跟着模块来,卸掉也会被装回来。要停用一份,就去关掉正在用它的那些模块。',
76
75
  'panels.market.listSeparator': '、',
77
76
  'panels.market.sharedMissing': '未装',
78
77
  'panels.market.sharedOutdated': '有新版',
@@ -97,15 +96,13 @@ export const zh = {
97
96
  'panels.market.noSource': '这个模块没声明怎么装,只能自己动手',
98
97
  'panels.market.badSource': '安装方式没写全,只能自己动手',
99
98
  'panels.market.empty': '暂无可用模块',
100
- 'panels.market.installedSection': '已安装',
101
- 'panels.market.sectionLocal': '本地装的',
99
+ 'panels.market.sectionLocal': '本地模块',
102
100
  'panels.market.sectionUnlisted': '不是从市场装的',
103
101
  'panels.market.sectionPlugins': '已安装 opencli 插件',
104
102
  'panels.market.moduleOutdated': '有新版',
105
103
  'panels.market.moduleRefused': '这个模块是给旧版本的工作台写的,现在用不了。更新它,或者卸载。',
106
104
  'panels.market.moduleRefusedDetail': '装不上的原因:{reason}',
107
105
  'panels.market.moduleRestartHint': '装好了但还没生效 —— 重启一次 dsh 就会出来',
108
- 'panels.market.pluginLocalNoUpdate': '本地装的,没地方查新版,所以只给卸载',
109
106
  'panels.market.pluginKeptInUse': '文件先留着:{names} 还要用它。',
110
107
  'panels.market.pluginKeptUnsure': '文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。',
111
108
  'panels.market.pluginKeptHeld': '文件留着:你自己装的那一份,模块放手不动它。',
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { declarationOfPackage } from '../declaration.js';
6
+
7
+ const roots: string[] = [];
8
+
9
+ function packageAt(manifest: unknown, text?: string): string {
10
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wb-declaration-'));
11
+ roots.push(root);
12
+ const at = path.join(root, 'package.json');
13
+ fs.writeFileSync(at, text ?? JSON.stringify({ name: '@t/demo', dsh: { workbench: manifest } }), 'utf8');
14
+ return at;
15
+ }
16
+
17
+ afterEach(() => {
18
+ for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
19
+ });
20
+
21
+ const DECLARED = {
22
+ apiVersion: 1,
23
+ unit: 'demo',
24
+ card: { titleKey: 'demo.cardTitle', order: 7 },
25
+ tools: [{ name: 'workbench_demo_list', writes: false }],
26
+ host: { inject: [] },
27
+ };
28
+
29
+ describe('模块作者写用例时读自己那份声明', () => {
30
+ it('读出来的就是外壳会拿到的那份清单', () => {
31
+ const manifest = declarationOfPackage('@t/demo', packageAt(DECLARED));
32
+
33
+ expect(manifest.id).toBe('demo');
34
+ expect(manifest.card).toEqual({ titleKey: 'demo.cardTitle', order: 7 });
35
+ expect(manifest.tools).toEqual([{ name: 'workbench_demo_list', writes: false }]);
36
+ });
37
+
38
+ it('★ 声明有毛病就当场抛,并把 problems 原样带出来(别让用例拿着半个清单继续跑)', () => {
39
+ expect(() => declarationOfPackage('@t/demo', packageAt({ unit: 'demo' }))).toThrow(/apiVersion/);
40
+ expect(() =>
41
+ declarationOfPackage('@t/demo', packageAt({ ...DECLARED, faces: { host: 'src/host.ts' } })),
42
+ ).toThrow(/faces/);
43
+ });
44
+
45
+ it('package.json 本身读不动时也说清是哪个文件(不是一句 JSON 语法错)', () => {
46
+ expect(() => declarationOfPackage('@t/demo', packageAt({}, '{ 坏掉的'))).toThrow(/读不动/);
47
+ });
48
+ });
@@ -0,0 +1,23 @@
1
+ import fs from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import type { UnitManifest } from '../api/unit.js';
4
+ import { readDeclaration } from '../api/unit-declaration.js';
5
+
6
+ export function declarationOfPackage(pkg: string, packageJson: URL | string): UnitManifest {
7
+ const at = typeof packageJson === 'string' ? packageJson : fileURLToPath(packageJson);
8
+ let parsed: unknown;
9
+ try {
10
+ parsed = JSON.parse(fs.readFileSync(at, 'utf8'));
11
+ } catch (error) {
12
+ throw new Error(`${pkg}: 读不动 ${at} —— ${String(error)}`, { cause: error });
13
+ }
14
+ const declared =
15
+ typeof parsed === 'object' && parsed !== null
16
+ ? (parsed as { dsh?: { workbench?: unknown } }).dsh?.workbench
17
+ : undefined;
18
+ const read = readDeclaration(pkg, declared);
19
+ if (read.manifest === undefined) {
20
+ throw new Error(`${pkg} 的 dsh.workbench 读不出来:${read.problems.join(';')}`);
21
+ }
22
+ return read.manifest;
23
+ }
package/src/testing.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/host-fakes.js';
2
+ export * from './testing/declaration.js';