@dsh-workbench/shared 0.3.9 → 0.3.11

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 (48) hide show
  1. package/dist/api/dsh-client.d.ts +1 -0
  2. package/dist/api/dsh.d.ts +18 -0
  3. package/dist/api/host-write.d.ts +0 -15
  4. package/dist/api/host-write.js +1 -35
  5. package/dist/api/unit-board.d.ts +0 -21
  6. package/dist/api/unit-board.js +1 -45
  7. package/dist/components/icons.d.ts +0 -6
  8. package/dist/components/icons.js +0 -12
  9. package/dist/config/storage.d.ts +0 -4
  10. package/dist/config/storage.js +0 -11
  11. package/dist/host/browser-find.js +1 -1
  12. package/dist/host/browser-read.js +5 -5
  13. package/dist/host/tool-approval.d.ts +12 -0
  14. package/dist/host/tool-approval.js +4 -0
  15. package/dist/i18n/en.js +40 -20
  16. package/dist/i18n/index.d.ts +41 -21
  17. package/dist/i18n/zh.d.ts +40 -20
  18. package/dist/i18n/zh.js +40 -20
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/lib/blocker-action.d.ts +0 -15
  22. package/dist/lib/blocker-action.js +1 -25
  23. package/dist/lib/react-types.d.ts +5 -0
  24. package/dist/lib/react-types.js +1 -0
  25. package/dist/lib/react.d.ts +14 -13
  26. package/dist/lib/react.js +11 -11
  27. package/package.json +1 -1
  28. package/src/api/dsh-client.ts +4 -1
  29. package/src/api/dsh.ts +16 -0
  30. package/src/api/host-write.ts +0 -42
  31. package/src/api/unit-board.ts +0 -45
  32. package/src/components/icons.tsx +0 -16
  33. package/src/config/storage.test.ts +1 -25
  34. package/src/config/storage.ts +0 -12
  35. package/src/host/__tests__/browser-find.test.ts +1 -1
  36. package/src/host/__tests__/browser-read.test.ts +1 -1
  37. package/src/host/browser-find.ts +1 -1
  38. package/src/host/browser-read.ts +5 -5
  39. package/src/host/tool-approval.ts +17 -0
  40. package/src/i18n/en.ts +42 -20
  41. package/src/i18n/zh.ts +42 -20
  42. package/src/index.ts +1 -1
  43. package/src/lib/blocker-action.ts +0 -27
  44. package/src/lib/react-types.ts +5 -0
  45. package/src/lib/react.ts +14 -13
  46. package/src/api/__tests__/host-write.test.ts +0 -75
  47. package/src/api/__tests__/unit-board.test.ts +0 -76
  48. package/src/lib/blocker-action.test.ts +0 -40
package/src/i18n/en.ts CHANGED
@@ -92,7 +92,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
92
92
  'panels.buildModule': 'Build a module',
93
93
  'panels.failed': 'Could not save the change. Try again.',
94
94
  'panels.dragHint': 'Drag to reorder',
95
- 'panels.unknownStep': 'No suite provides this view',
95
+ 'panels.unknownStep': 'No panel for this view yet',
96
96
  'panels.labelWithHint': '{label} ({hint})',
97
97
  'panels.hintJoin': '; ',
98
98
  'panels.dep.failed': '⚠ failed to start',
@@ -121,12 +121,20 @@ export const en: Record<keyof WorkbenchMessages, string> = {
121
121
  'panels.market.sharedMissing': 'not installed',
122
122
  'panels.market.sharedOutdated': 'update available',
123
123
  'panels.market.sharedUnsatisfied': 'wrong version',
124
- 'panels.market.sharedOk': 'installed',
124
+ 'panels.market.sharedOk': 'ready',
125
125
  'panels.market.versionArrow': '{from} → {to}',
126
126
  'panels.market.lead': 'Installed on this machine, shared by every project.',
127
127
  'panels.market.install': 'Install',
128
128
  'panels.market.update': 'Update',
129
129
  'panels.market.swapSlow': 'Not swapped yet — maybe a slow or failed download. Press Update again; if it stays like this, restart dsh.',
130
+ 'panels.market.updateNoChange': 'Not updated: you were on {from}, and after the run the disk still has that version; the newest on the registry is {latest}.',
131
+ 'panels.market.updateUnknownVersion': '(unknown)',
132
+ 'panels.market.updateGone': 'Not updated: after the run the module cannot be read from disk.',
133
+ 'panels.market.updateRestored': 'Not updated: the new version was missing from disk, so {version} was put back.',
134
+ 'panels.market.updateBehind': 'Installed {installed}, but the newest on the registry is {latest} — not the latest; you can update once more.',
135
+ 'panels.market.selfUpdateFailed': 'Self-update failed: {reason}',
136
+ 'panels.market.selfUpdateUnknown': 'the outside process left no reason (see ~/.dsh/workbench/self-update.log)',
137
+ 'panels.market.selfUpdateRolledBack': 'Not swapped; rolled back to {version}.',
130
138
  'panels.market.uninstall': 'Uninstall',
131
139
  'panels.market.uninstallLabel': 'Uninstall {name}',
132
140
  'panels.market.installing': 'Installing…',
@@ -159,7 +167,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
159
167
  '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.',
160
168
  'panels.market.pluginKeptHeld': 'Files kept: this one is yours, and a module letting go will not remove it.',
161
169
  'panels.market.pluginKeptUnnamed': 'Files kept for now: this one cannot be identified as a plugin, so it is unclear who needs it.',
162
- 'panels.market.pluginBroken': 'This install is broken: it cannot be identified as a plugin. Uninstall it, or tell the agent to reinstall it by package name with workbench_modules_install_bundle.',
170
+ 'panels.market.pluginBroken': 'This plugin install is broken: it cannot be identified. Uninstall it first, then ask the agent to reinstall it by package name.',
163
171
  'panels.market.restartNeeded': 'The new version is installed — restart once to pick it up.',
164
172
  'panels.market.foot': 'After installing, enable it in the panel page of that project.',
165
173
  'panels.market.recheck': 'Check for updates',
@@ -204,7 +212,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
204
212
  'panels.market.add.cancelledTitle': 'Cancelled',
205
213
  'panels.market.add.failureNetworkHost': 'Cannot reach {host}. Git URLs and .tgz links bypass the registry, so this machine needs direct access or a proxy; if the module is also on npm, use its package name instead.',
206
214
  'panels.market.add.problemInvalid': 'Cannot read this package name or address: {reason}',
207
- 'panels.market.add.problemInstalled': 'This module is already installed',
215
+ 'panels.market.add.problemInstalled': 'This module is already installed (add @version to switch versions)',
208
216
  'panels.market.add.problemNotFound': 'No such module found',
209
217
  'panels.market.add.problemNotPackage': 'That path does not exist or is not a valid module package',
210
218
  'panels.market.add.problemNotBundle':
@@ -228,7 +236,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
228
236
  'panels.market.add.failureTimeout': 'The install timed out',
229
237
  'panels.market.add.failurePnpmMissing': 'pnpm was not found, cannot install',
230
238
  'panels.market.add.failureInstalled':
231
- 'It is already in this profile: this dsh runs with hot reload, which refuses to install over the same package (remove it first) — and removing it failed too',
239
+ 'It is already installed in this workbench. The same package cannot be installed over itself: uninstall it first, then install again. The uninstall did not succeed this time.',
232
240
  'panels.market.add.failureGeneric': 'Something went wrong during install — see the install details',
233
241
  'panels.market.add.approveTitle': 'Your approval is needed for install scripts',
234
242
  'panels.market.add.approveCaution': 'Only allow packages you trust: install scripts run on this machine with your permissions.',
@@ -266,7 +274,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
266
274
  'panels.market.add.doneApplied': 'Installed and live.',
267
275
  'panels.market.add.doneRestart': 'Installed — restart once to pick it up.',
268
276
  'panels.market.add.restartWhy': 'Why this needs a restart: {reason}',
269
- 'panels.market.add.restartWhy.no-hmr': 'this profile only applies changes at startup (no hot reload)',
277
+ 'panels.market.add.restartWhy.no-hmr': 'this configuration only applies changes at startup (hot reload is off)',
270
278
  'panels.market.add.restartWhy.package-moved':
271
279
  'an installed package now resolves to another directory, or the change replaced something dsh itself provides',
272
280
  'panels.market.add.restartWhy.host-module': 'the change reaches modules dsh itself loaded',
@@ -282,17 +290,17 @@ export const en: Record<keyof WorkbenchMessages, string> = {
282
290
  'panels.market.add.remove': 'Remove it',
283
291
  'panels.market.add.problem.invalidSpec': "That is not a spec we accept: it cannot be empty or start with a dash.",
284
292
  'panels.market.add.problem.alreadyInstalled': 'That package is already in the profile (add @version to switch versions).',
285
- 'panels.market.add.problem.notFound': 'No such package on that source.',
286
- 'panels.market.add.problem.notAPackage': 'There is no package at that location.',
287
- 'panels.market.add.problem.notABundle': 'It is an npm package, but not a dsh plugin (no bundle patch).',
288
- 'panels.market.add.problem.network': 'The source is unreachable.',
289
- 'panels.market.add.problem.unknown': 'The check did not pass (reason unknown).',
293
+ 'panels.market.add.problem.notFound': 'No such package on that source. Try another name or version.',
294
+ 'panels.market.add.problem.notAPackage': 'There is no package at that location. Pick a folder that contains a package.json.',
295
+ 'panels.market.add.problem.notABundle': 'It is an npm package, but not a dsh plugin (it is missing the dsh wiring declaration).',
296
+ 'panels.market.add.problem.network': 'The source is unreachable. Check the network and retry.',
297
+ 'panels.market.add.problem.unknown': 'The check did not pass (reason unknown). Open the install details for the raw output, or retry.',
290
298
  'panels.market.add.reason.invalidSpec': 'the spec is not accepted',
291
299
  'panels.market.add.reason.incompatible': 'it wants a different dsh version than this machine runs',
292
300
  'panels.market.add.reason.notBundle': 'it is not a dsh plugin package',
293
301
  'panels.market.add.reason.notRemovable': 'it ships with the installation and cannot be removed',
294
302
  'panels.market.add.reason.bundleInUse': 'something is still using it',
295
- 'panels.market.add.reason.stopProfile': 'this profile has no hot reload, so stop the process first',
303
+ 'panels.market.add.reason.stopProfile': 'this configuration has no hot reload, so stop the process first',
296
304
  'panels.market.add.reason.staleApproval': 'the scripts to allow changed — start over',
297
305
  'panels.market.add.reason.operationError': 'pnpm failed (see the install output)',
298
306
  'panels.market.add.reason.buildBlocked': 'pnpm blocked install scripts',
@@ -347,11 +355,11 @@ export const en: Record<keyof WorkbenchMessages, string> = {
347
355
  'env.bridge.extensionLink': 'Get the extension in the Chrome Web Store',
348
356
  'env.bridge.profile': 'Several browser profiles are connected but none is the default: list them (command below), then pick one.',
349
357
  'env.bridge.stale': 'The default browser profile is stale (it names an extension instance that no longer exists): pick one again.',
350
- 'env.bridge.unknown': 'Could not tell what went wrong: the raw output below has the steps opencli itself suggests.',
358
+ 'env.bridge.unknown': 'Could not tell what went wrong. Expand the raw output and follow the steps in it.',
351
359
  'env.fix.title': 'How to fix it',
352
360
  'env.fix.why': 'The workbench uses its own opencli (installed separately from the one in your terminal), and it is not set up on this machine yet.',
353
361
  'env.fix.whyPath': 'opencli is installed, but this environment cannot find it \u2014 usually a PATH or shell-session issue.',
354
- 'env.fix.whyShell': 'The workbench has no shell channel, so it cannot fetch data \u2014 nothing the user can install.',
362
+ 'env.fix.whyShell': 'The workbench has no shell channel, so it cannot fetch data \u2014 you cannot install this from here; someone needs to fix the background environment.',
355
363
  'env.fix.step': 'Run this in a terminal, then hit Retry:',
356
364
  'env.fix.stepPath': 'Open a new terminal window (or make sure npm\u2019s global bin directory is on PATH), then hit Retry.',
357
365
  'env.fix.stepShell': 'Restart the workbench (or dsh); if it keeps happening, the shell channel is not wired up.',
@@ -363,9 +371,9 @@ export const en: Record<keyof WorkbenchMessages, string> = {
363
371
  'env.diy.ok': 'Available',
364
372
  'env.diy.short': 'Cannot load',
365
373
  'env.diy.hold': 'DIY mode cannot load \u2014 upgrade dsh, then hit Retry; or enter the workbench first.',
366
- 'env.diy.why': '\u201cWorkbench DIY\u201d is a dsh agent preset, and it is generated from the dsh generation this shell was verified against. The dsh on this machine is older and does not ship some of those rows, so the whole preset fails to mount. dsh lists only the presets that mount, so the mode will simply not appear \u2014 panels and everything else are unaffected.',
367
- 'env.diy.rows': 'This dsh is missing these rows: {rows}',
368
- 'env.diy.noRow': 'The health check did not find \u201cWorkbench DIY\u201d in dsh\u2019s preset roster \u2014 most likely this bundle\u2019s preset patch did not apply.',
374
+ 'env.diy.why': 'The dsh on this machine is older than the one this workbench was verified against, so DIY mode cannot load. Panels and everything else are unaffected. Upgrade dsh and restart.',
375
+ 'env.diy.rows': 'This dsh is missing these items: {rows}',
376
+ 'env.diy.noRow': 'The health check did not find \u201cWorkbench DIY mode\u201d in dsh\u2019s mode roster \u2014 the workbench install most likely did not include the matching mode patch.',
369
377
  'env.diy.step': 'Upgrade dsh, then restart the workbench:',
370
378
  'env.diy.continue': 'Enter the workbench anyway',
371
379
  'env.copyCommand': 'Copy command',
@@ -404,6 +412,20 @@ export const en: Record<keyof WorkbenchMessages, string> = {
404
412
  'panel.conversation': 'Conversation',
405
413
  'panels.label': 'Global panels',
406
414
 
415
+ 'workbenchSettings.nav': 'Workbench settings',
416
+ 'workbenchSettings.lead': 'Settings the workbench itself owns live here.',
417
+
418
+ 'sidebarEdition.nav': 'Session list',
419
+ 'sidebarEdition.lead': 'Which whole left column to use: the official dsh one, or the workbench’s own.',
420
+ 'sidebarEdition.official': 'Official',
421
+ 'sidebarEdition.officialHint': 'Matches dsh exactly and stays stable: search, view options, show more, archive, add workspace — no waiting for us to catch up. Pick it to keep things simple.',
422
+ 'sidebarEdition.custom': 'Custom',
423
+ 'sidebarEdition.customHint': 'Built for multi-project work: one project per dropdown, recency-first sessions, edge fades, marquee, DIY new session. Easier when you juggle projects.',
424
+ 'sidebarEdition.saving': 'Configuring…',
425
+ 'sidebarEdition.reloadRequired': 'Saved. Reload the page to apply — no workbench restart needed.',
426
+ 'sidebarEdition.reload': 'Reload page',
427
+ 'sidebarEdition.failed': 'That choice did not save; try again.',
428
+
407
429
  'configSync.nav': 'Config sync',
408
430
  'configSync.lead': 'Bring over the rows another profile configures explicitly — pick any profile as the source.',
409
431
  'configSync.source': 'Source',
@@ -426,8 +448,8 @@ export const en: Record<keyof WorkbenchMessages, string> = {
426
448
  'configSync.code.expandAria': 'Expand the diff ({count} more lines)',
427
449
  'configSync.code.expand': 'Show {count} more lines',
428
450
  'configSync.missing': '{pkg} is not installed: the row will not take effect — run `dsh plugin --profile {profile} add {pkg}`, or install it from the module market',
429
- 'configSync.apply': 'Sync {count} selected',
430
- 'configSync.applyNone': 'Pick some rows first',
451
+ 'configSync.apply': 'Sync {count} selected items',
452
+ 'configSync.applyNone': 'Choose what to sync',
431
453
  'configSync.applying': 'Writing…',
432
454
  'configSync.applied': 'Written to the workbench config · applied',
433
455
  'configSync.appliedIds': 'Written this time: {ids}',
@@ -435,7 +457,7 @@ export const en: Record<keyof WorkbenchMessages, string> = {
435
457
  'configSync.nothing': 'Nothing to sync',
436
458
  'configSync.unreadable': 'Cannot read {path}: {reason}',
437
459
  'configSync.noSource': 'This machine has no other profile to sync from',
438
- 'configSync.emptyProviders': 'The workbench has no model provider yet — sync the llm- rows from some profile, or configure one under Models.',
460
+ 'configSync.emptyProviders': 'The workbench has no model provider yet — sync the model-provider rows from some profile, or configure one under Models.',
439
461
  'configSync.failed': 'The sync did not go through; try again.',
440
462
 
441
463
  'debug.workspace': 'this workspace',
package/src/i18n/zh.ts CHANGED
@@ -90,7 +90,7 @@ export const zh = {
90
90
  'panels.buildModule': '造一个模块',
91
91
  'panels.failed': '改配置失败了,再试一次。',
92
92
  'panels.dragHint': '拖动调整顺序',
93
- 'panels.unknownStep': '这一层没有套件提供',
93
+ 'panels.unknownStep': '这一块还没有面板',
94
94
  'panels.labelWithHint': '{label}({hint})',
95
95
  'panels.hintJoin': ';',
96
96
  'panels.dep.failed': '⚠ 起不来',
@@ -119,12 +119,20 @@ export const zh = {
119
119
  'panels.market.sharedMissing': '未装',
120
120
  'panels.market.sharedOutdated': '有新版',
121
121
  'panels.market.sharedUnsatisfied': '版本不对',
122
- 'panels.market.sharedOk': '已装',
122
+ 'panels.market.sharedOk': '已就绪',
123
123
  'panels.market.versionArrow': '{from} → {to}',
124
124
  'panels.market.lead': '装在这台机器上,所有项目共用。',
125
125
  'panels.market.install': '安装',
126
126
  'panels.market.update': '更新',
127
127
  'panels.market.swapSlow': '还没换上:可能网慢或没装上。再点一次「更新」;一直这样就把 dsh 重启一遍。',
128
+ 'panels.market.updateNoChange': '没换成:点下去是 {from},跑完盘上还是这一版;源上最新是 {latest}。',
129
+ 'panels.market.updateUnknownVersion': '(问不到)',
130
+ 'panels.market.updateGone': '没换成:更新跑完盘上读不到这个模块。',
131
+ 'panels.market.updateRestored': '没换成:盘上已经读不到新版本,已把 {version} 装回去。',
132
+ 'panels.market.updateBehind': '装上了 {installed},但源上最新是 {latest} —— 没更到最新,可以再点一次。',
133
+ 'panels.market.selfUpdateFailed': '自更新没成:{reason}',
134
+ 'panels.market.selfUpdateUnknown': '外部进程没留下原因(看 ~/.dsh/workbench/self-update.log)',
135
+ 'panels.market.selfUpdateRolledBack': '没换成,已回滚到 {version}。',
128
136
  'panels.market.uninstall': '卸载',
129
137
  'panels.market.uninstallLabel': '卸载 {name}',
130
138
  'panels.market.installing': '正在安装…',
@@ -155,7 +163,7 @@ export const zh = {
155
163
  'panels.market.pluginKeptUnsure': '文件先留着:这台机器上还有模块看不出用了什么,说不准谁在用它。',
156
164
  'panels.market.pluginKeptHeld': '文件留着:你自己装的那一份,模块放手不动它。',
157
165
  'panels.market.pluginKeptUnnamed': '文件先留着:看不出这是哪个插件,说不准谁在用它。',
158
- 'panels.market.pluginBroken': '这份装坏了:看不出它是个插件。可以卸载,或者直接跟 agent 说:用 workbench_modules_install_bundle 按包名再装一次。',
166
+ 'panels.market.pluginBroken': '这份插件装坏了:认不出它是什么。可以先卸载,再让 agent 按包名重装一次。',
159
167
  'panels.market.restartNeeded': '新版装好了,重启一次才生效。',
160
168
  'panels.market.foot': '装好后到那个项目的面板页勾选它,模块才会出现。',
161
169
  'panels.market.recheck': '重新检查更新',
@@ -200,7 +208,7 @@ export const zh = {
200
208
  'panels.market.add.cancelledTitle': '已取消',
201
209
  'panels.market.add.failureNetworkHost': '无法连接 {host}。Git 地址和 .tgz 直链不经过安装源,需要本机能直接访问它或配置代理;如果这个模块也发布到了 npm,请改填包名。',
202
210
  'panels.market.add.problemInvalid': '无法识别这个包名或地址:{reason}',
203
- 'panels.market.add.problemInstalled': '该模块已安装',
211
+ 'panels.market.add.problemInstalled': '这个模块已经装过了(要换版本请带上 @版本)',
204
212
  'panels.market.add.problemNotFound': '未找到相关模块',
205
213
  'panels.market.add.problemNotPackage': '该路径不存在或不是有效的模块包',
206
214
  'panels.market.add.problemNotBundle':
@@ -224,7 +232,7 @@ export const zh = {
224
232
  'panels.market.add.failureTimeout': '安装超时',
225
233
  'panels.market.add.failurePnpmMissing': '没有找到 pnpm,无法安装',
226
234
  'panels.market.add.failureInstalled':
227
- '它已经在这份 profile 里了:这一版 dsh 开着热更新,同一个包不许就地覆盖装(得先卸载再装),而这次也没能把它卸下来',
235
+ '它已经装在这份工作台里了。同一个包不能就地覆盖,得先卸载再装;这次卸载也没成功。',
228
236
  'panels.market.add.failureGeneric': '安装过程中出错,原因见安装详情',
229
237
  'panels.market.add.approveTitle': '需要你允许安装脚本',
230
238
  'panels.market.add.approveCaution': '只允许你信得过的包:安装脚本会以你的权限在这台机器上运行。',
@@ -262,7 +270,7 @@ export const zh = {
262
270
  'panels.market.add.doneApplied': '装好了,已经生效。',
263
271
  'panels.market.add.doneRestart': '装好了,重启一次才生效。',
264
272
  'panels.market.add.restartWhy': '为什么这一版要重启:{reason}',
265
- 'panels.market.add.restartWhy.no-hmr': '这个 profile 只在启动时应用改动(没开热换)',
273
+ 'panels.market.add.restartWhy.no-hmr': '这个配置只在启动时应用改动(没开热更新)',
266
274
  'panels.market.add.restartWhy.package-moved': '某个已经装着的包换到了新的目录,或者这一下换掉了 dsh 自己提供的那份',
267
275
  'panels.market.add.restartWhy.host-module': '这一下动到了 dsh 自己加载的模块',
268
276
  'panels.market.add.restartWhy.unmanaged-plugin': '这一下动到了插件清单之外正在跑的插件',
@@ -277,17 +285,17 @@ export const zh = {
277
285
  'panels.market.add.remove': '移除它',
278
286
  'panels.market.add.problem.invalidSpec': '这个写法不认:不能是空的,也不能以横杠开头。',
279
287
  'panels.market.add.problem.alreadyInstalled': '这个包已经在 profile 里了(要换版本请带上 @版本)。',
280
- 'panels.market.add.problem.notFound': '源上没有这个包。',
281
- 'panels.market.add.problem.notAPackage': '这个位置没有包。',
282
- 'panels.market.add.problem.notABundle': '它是一个 npm 包,但不是 dsh 插件包(没有 bundle patch)。',
283
- 'panels.market.add.problem.network': '源连不上。',
284
- 'panels.market.add.problem.unknown': '预检没通过(原因未知)。',
288
+ 'panels.market.add.problem.notFound': '源上没有这个包。换个包名或版本再试。',
289
+ 'panels.market.add.problem.notAPackage': '这个位置没有包。选一个包含 package.json 的目录。',
290
+ 'panels.market.add.problem.notABundle': '它是一个 npm 包,但不是 dsh 插件包(缺 dsh 的接线声明)。',
291
+ 'panels.market.add.problem.network': '源连不上。检查网络后重试。',
292
+ 'panels.market.add.problem.unknown': '预检没通过(原因未知)。展开安装详情看原始输出,或重试。',
285
293
  'panels.market.add.reason.invalidSpec': '写法不认',
286
294
  'panels.market.add.reason.incompatible': '它要的 dsh 版本和这台机器上的不一致',
287
295
  'panels.market.add.reason.notBundle': '它不是一个 dsh 插件包',
288
296
  'panels.market.add.reason.notRemovable': '它是安装自带的组合包,不能卸',
289
297
  'panels.market.add.reason.bundleInUse': '还有东西在用它,卸不掉',
290
- 'panels.market.add.reason.stopProfile': '这个 profile 没开热更新,得先停掉进程',
298
+ 'panels.market.add.reason.stopProfile': '这个配置没开热更新,得先停掉进程',
291
299
  'panels.market.add.reason.staleApproval': '要允许的安装脚本已经变了,重新来一次',
292
300
  'panels.market.add.reason.operationError': 'pnpm 执行出错(详情见安装输出)',
293
301
  'panels.market.add.reason.buildBlocked': 'pnpm 拦下了安装脚本',
@@ -342,11 +350,11 @@ export const zh = {
342
350
  'env.bridge.extensionLink': '去 Chrome 应用商店装扩展',
343
351
  'env.bridge.profile': '连上了多个浏览器 profile,但没选默认的那个:先看有哪些(下面这条命令),再选一个。',
344
352
  'env.bridge.stale': '默认的浏览器 profile 已经失效(它记的是上一次的扩展实例):重新选一个。',
345
- 'env.bridge.unknown': '没认出是哪种问题:下面原始输出里有 opencli 自己给的步骤。',
353
+ 'env.bridge.unknown': '没认出是哪种问题:展开原始输出,按里面的步骤处理。',
346
354
  'env.fix.title': '怎么解决',
347
355
  'env.fix.why': '工作台用它自己那份 opencli(与你在终端里那套分开装,互不影响),这台机器上还没装好它。',
348
356
  'env.fix.whyPath': 'opencli 装上了,但当前环境还找不到它 —— 通常是 PATH 或终端会话的问题。',
349
- 'env.fix.whyShell': '工作台拿不到执行命令的通道,所以取不了数据 —— 这一步用户自己装不了。',
357
+ 'env.fix.whyShell': '工作台拿不到执行命令的通道,所以取不了数据 —— 这一步你装不了,需要有人处理后台环境。',
350
358
  'env.fix.step': '在终端里运行下面这条命令,装好之后点「重试」:',
351
359
  'env.fix.stepPath': '新开一个终端窗口(或确认 npm 的全局 bin 目录在 PATH 里),然后点「重试」。',
352
360
  'env.fix.stepShell': '重启一次工作台(或 dsh);如果反复出现,说明执行通道没配好。',
@@ -358,9 +366,9 @@ export const zh = {
358
366
  'env.diy.ok': '可用',
359
367
  'env.diy.short': '挂不起来',
360
368
  'env.diy.hold': 'DIY 模式挂不起来 —— 升级 dsh 后点「重试」,也可以先进工作台。',
361
- 'env.diy.why': '「工作台 DIY」是 dsh 的一个 agent preset,而它是照这份外壳验过的那一代 dsh 生成的:这台机器上的 dsh 比它旧,其中几行 dsh 根本没有,于是这个模式整个挂不起来。dsh 的模式选择器只列挂得起来的,进去之后它会整个不出现 —— 面板与其它功能不受影响。',
362
- 'env.diy.rows': '这一代 dsh 缺这几行:{rows}',
363
- 'env.diy.noRow': '体检没在 dsh 的 preset 名单里看到「工作台 DIY」—— 多半是这份 bundle 的 preset 补丁没生效。',
369
+ 'env.diy.why': '这台机器上的 dsh 比工作台验过的旧,DIY 模式挂不起来;面板和其它功能不受影响。升级 dsh 后重启再试。',
370
+ 'env.diy.rows': '这一代 dsh 缺这几项:{rows}',
371
+ 'env.diy.noRow': '体检没在 dsh 的模式名单里看到「工作台 DIY 模式」—— 多半是工作台这份安装没带上对应的模式补丁。',
364
372
  'env.diy.step': '升级 dsh,然后重启一次工作台:',
365
373
  'env.diy.continue': '先进入工作台',
366
374
  'env.copyCommand': '复制命令',
@@ -399,6 +407,20 @@ export const zh = {
399
407
  'panel.conversation': '对话',
400
408
  'panels.label': '全局面板',
401
409
 
410
+ 'workbenchSettings.nav': '工作台设置',
411
+ 'workbenchSettings.lead': '工作台自己这一侧的配置都收在这里。',
412
+
413
+ 'sidebarEdition.nav': '会话列表',
414
+ 'sidebarEdition.lead': '左栏整条用哪一套:官方 dsh 的,还是工作台自己的。',
415
+ 'sidebarEdition.official': '官方',
416
+ 'sidebarEdition.officialHint': '跟 dsh 官方一致、最稳:搜索、视图选项、显示更多、归档、添加工作区都齐,升级也不等我们适配。想省心就选它。',
417
+ 'sidebarEdition.custom': '自定义',
418
+ 'sidebarEdition.customHint': '为多项目工作流做的:项目下拉一屏一个项目,会话按最近更新排,两端淡出、跑马灯、工坊 DIY 新会话。项目多时更顺手。',
419
+ 'sidebarEdition.saving': '配置中…',
420
+ 'sidebarEdition.reloadRequired': '已写入。刷新页面后生效(不用重启工作台)。',
421
+ 'sidebarEdition.reload': '刷新页面',
422
+ 'sidebarEdition.failed': '这一档没写进去,再试一次。',
423
+
402
424
  'configSync.nav': '配置同步',
403
425
  'configSync.lead': '把另一个 profile 里显式配过的行搬过来 —— 来源随你选。',
404
426
  'configSync.source': '来源',
@@ -421,8 +443,8 @@ export const zh = {
421
443
  'configSync.code.expandAria': '展开差异(还有 {count} 行)',
422
444
  'configSync.code.expand': '展开剩余 {count} 行',
423
445
  'configSync.missing': '{pkg} 还没装:这一行搬过来也不会生效 —— 先 `dsh plugin --profile {profile} add {pkg}`,或在模块市场里装',
424
- 'configSync.apply': '同步选中的 {count} 行',
425
- 'configSync.applyNone': '先勾几行',
446
+ 'configSync.apply': '同步选中的 {count} 项',
447
+ 'configSync.applyNone': '请选择同步内容',
426
448
  'configSync.applying': '正在写入…',
427
449
  'configSync.applied': '已写入工作台配置 · 已生效',
428
450
  'configSync.appliedIds': '这次写进去:{ids}',
@@ -430,7 +452,7 @@ export const zh = {
430
452
  'configSync.nothing': '没有需要同步的内容',
431
453
  'configSync.unreadable': '读不到 {path}:{reason}',
432
454
  'configSync.noSource': '这台机器上只有工作台这一个 profile,没有可同步的来源',
433
- 'configSync.emptyProviders': '工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把 llm- 那几行搬过来,或自己去「模型」里配。',
455
+ 'configSync.emptyProviders': '工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把模型供应商那几行搬过来,或自己去「模型」里配。',
434
456
  'configSync.failed': '同步没成功,再试一次。',
435
457
 
436
458
  'debug.workspace': '这个工作区',
package/src/index.ts CHANGED
@@ -50,6 +50,6 @@ export * from './lib/blocker-action.js';
50
50
  export * from './lib/board-fact.js';
51
51
  export * from './lib/busy.js';
52
52
  export * from './lib/form-problem.js';
53
- export * from './lib/react.js';
53
+ export * from './lib/react-types.js';
54
54
  export * from './lib/text.js';
55
55
  export * from './lib/ui.js';
@@ -21,30 +21,3 @@ export interface BlockerLabelState {
21
21
  readonly copied: string | null;
22
22
  }
23
23
 
24
- /**
25
- * 这颗钮此刻该不该按成等待态:这一轮忙还没收,或有一支正在跑。
26
- * @param busy - 这一轮忙是不是还在。
27
- * @param running - 正在跑的那一支的 label;没有就给 `null`。
28
- * @returns 该等待给 `true`,否则 `false`。
29
- */
30
- export function blockerPending(busy: boolean, running: string | null): boolean {
31
- return busy || running !== null;
32
- }
33
-
34
- /**
35
- * 这颗钮此刻该显示什么字:正在重试说「重试中…」,正在跑的那一支说「修复中…」,刚复制过的说「已复制」,其余原样。
36
- * @param action - 这颗钮的形状(行为 + 平时文案)。
37
- * @param state - 此刻的运行态。
38
- * @param t - i18n 取词函数。
39
- * @returns 要显示的文案;三种临时态都不成立时就是 `action.label`。
40
- */
41
- export function blockerActionLabel(
42
- action: BlockerActionShape,
43
- state: BlockerLabelState,
44
- t: BlockerLabelTranslate,
45
- ): string {
46
- if (action.kind === 'retry' && state.retrying) return t('blocker.retry.running');
47
- if (state.running === action.label) return t('blocker.fix.running');
48
- if (action.kind === 'copy' && state.copied === action.text) return t('env.copied');
49
- return action.label;
50
- }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 从 react 借来的三个类型(`ReactNode` / `CSSProperties` / `ChangeEvent`)—— 模块从这里拿,
3
+ * 就不必自己直接依赖 react。只取类型:编译期擦除,不进任何产物。
4
+ */
5
+ export type { ReactNode, CSSProperties, ChangeEvent } from 'react';
package/src/lib/react.ts CHANGED
@@ -8,54 +8,55 @@ const react = require('react') as typeof ReactTypes;
8
8
  export const h = react.createElement;
9
9
 
10
10
  /**
11
- * React 的 `Fragment` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
11
+ * React 的 `Fragment` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
12
12
  */
13
13
  export const Fragment = react.Fragment;
14
14
 
15
15
  /**
16
- * React 的 `Component` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
16
+ * React 的 `Component` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
17
17
  */
18
18
  export const Component = react.Component;
19
19
 
20
20
  /**
21
- * React 的 `createContext` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
21
+ * React 的 `createContext` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
22
22
  */
23
23
  export const createContext = react.createContext;
24
24
  /**
25
- * React 的 `useContext` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
25
+ * React 的 `useContext` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
26
26
  */
27
27
  export const useContext = react.useContext;
28
28
 
29
29
  /**
30
- * React 的 `useState` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
30
+ * React 的 `useState` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
31
31
  */
32
32
  export const useState = react.useState;
33
33
  /**
34
- * React 的 `useEffect` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
34
+ * React 的 `useEffect` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
35
35
  */
36
36
  export const useEffect = react.useEffect;
37
37
  /**
38
- * React 的 `useLayoutEffect` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
38
+ * React 的 `useLayoutEffect` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
39
39
  */
40
40
  export const useLayoutEffect = react.useLayoutEffect;
41
41
  /**
42
- * React 的 `useMemo` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
42
+ * React 的 `useMemo` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
43
43
  */
44
44
  export const useMemo = react.useMemo;
45
45
  /**
46
- * React 的 `useCallback` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
46
+ * React 的 `useCallback` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
47
47
  */
48
48
  export const useCallback = react.useCallback;
49
49
  /**
50
- * React 的 `useRef` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
50
+ * React 的 `useRef` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
51
51
  */
52
52
  export const useRef = react.useRef;
53
53
  /**
54
- * React 的 `useSyncExternalStore` 原样再导出 —— 模块从这里拿,就不必自己直接依赖 react。
54
+ * React 的 `useSyncExternalStore` 原样再导出 —— 共享包的哑组件用它;模块写 JSX 一律直接 import react(这一份不进公开入口)。
55
55
  */
56
56
  export const useSyncExternalStore = react.useSyncExternalStore;
57
57
 
58
58
  /**
59
- * 从 react 借来的三个类型(`ReactNode` / `CSSProperties` / `ChangeEvent`)—— 模块从这里拿,就不必自己直接依赖 react。
59
+ * 借来的三个类型从 `react-types.ts` 转一手:那份是**公开面**(模块 `import type` 从根入口拿),
60
+ * 组件内部照旧从这里引,别在两处各写一遍名字。
60
61
  */
61
- export type { ReactNode, CSSProperties, ChangeEvent } from 'react';
62
+ export type { ReactNode, CSSProperties, ChangeEvent } from './react-types.js';
@@ -1,75 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { writeOutcomeOf, writeRoute } from '../host-write.js';
3
-
4
- describe('writeRoute', () => {
5
- it('三条通道各自拼得出路由(面板上的写只有这三条)', () => {
6
- expect(writeRoute('prefs', 'cmdb')).toBe('prefs/cmdb');
7
- expect(writeRoute('actions', 'cmdb-release')).toBe('actions/cmdb-release');
8
- expect(writeRoute('provision', '')).toBe('provision');
9
- });
10
-
11
- it('provision 忽略资源名(它只发 {action, unit},没有资源段)', () => {
12
- expect(writeRoute('provision', 'issues')).toBe('provision');
13
- });
14
- });
15
-
16
- describe('writeOutcomeOf', () => {
17
- it('成了 → 值原样交出', () => {
18
- const outcome = writeOutcomeOf<{ text: string }>({ ok: true, text: '发出去了' }, '网络不通');
19
-
20
- expect(outcome).toEqual({ ok: true, value: { ok: true, text: '发出去了' } });
21
- });
22
-
23
- it('★ 请求没到(null)→ 用调用方给的那句网络文案', () => {
24
- expect(writeOutcomeOf(null, '网络不通')).toEqual({ ok: false, message: '网络不通' });
25
- });
26
-
27
- it('★ 宿主说失败 → 原样用宿主的话 + detail(不再包一层"操作失败")', () => {
28
- const outcome = writeOutcomeOf(
29
- { ok: false, error: { code: 'cmdb/release-blocked', message: '生产环境要先审批', detail: 'job 1234' } },
30
- '网络不通',
31
- );
32
-
33
- expect(outcome).toEqual({ ok: false, message: '生产环境要先审批', detail: 'job 1234' });
34
- });
35
-
36
- it('★ 登录态失效那条错误带着 helpUrl → 一路带出去(卡片才能给「去登录 ↗」而不是"还没绑定")', () => {
37
- const outcome = writeOutcomeOf(
38
- {
39
- ok: false,
40
- error: {
41
- code: 'cmdb/login-required',
42
- message: 'CMDB 登录态失效了',
43
- helpUrl: 'https://cmdb.example.test/login',
44
- },
45
- },
46
- '网络不通',
47
- );
48
-
49
- expect(outcome).toEqual({
50
- ok: false,
51
- message: 'CMDB 登录态失效了',
52
- helpUrl: 'https://cmdb.example.test/login',
53
- });
54
- });
55
-
56
- it('没有 detail 就不给那个字段(别塞一个 undefined 进类型里)', () => {
57
- const outcome = writeOutcomeOf({ ok: false, error: { code: 'x', message: '不行' } }, '网络不通');
58
-
59
- expect(outcome).toEqual({ ok: false, message: '不行' });
60
- expect(Object.keys(outcome)).not.toContain('detail');
61
- });
62
-
63
- it('★ 领域结果那一档(`{ok:false, text}`,没有 `error`)→ 用生产者自己那句话,**不抛异常**', () => {
64
- const outcome = writeOutcomeOf(
65
- { ok: false, text: 'innettest 没有发布配置(configs 查不到)' } as never,
66
- '网络不通',
67
- );
68
-
69
- expect(outcome).toEqual({ ok: false, message: 'innettest 没有发布配置(configs 查不到)' });
70
- });
71
-
72
- it('★ 连话都没说(`{ok:false}`)→ 用调用方的兜底文案,还是不抛', () => {
73
- expect(writeOutcomeOf({ ok: false } as never, '网络不通')).toEqual({ ok: false, message: '网络不通' });
74
- });
75
- });
@@ -1,76 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import type { ModuleBlocker } from '../blocker.js';
3
- import type { CapabilityPayload } from '../workbench-contract.js';
4
- import { gateOf, probeKeyOf, splitBlocker } from '../unit-board.js';
5
-
6
- function capability(status: CapabilityPayload['status']): CapabilityPayload {
7
- return { unit: 'issues', status, required: [], missing: [], detail: '测试用的结论' };
8
- }
9
-
10
- function blocker(id: string, severity: ModuleBlocker['severity']): ModuleBlocker {
11
- return { id, severity, title: id, actions: [] };
12
- }
13
-
14
- describe('gateOf(能力门)', () => {
15
- it('★ 还没探到(null)→ 照常取数:探测只是个前置检查,不许把卡片按住', () => {
16
- expect(gateOf(null, 'capability')).toEqual({ gated: true });
17
- });
18
-
19
- it('ok → 取数;缺插件/缺命令 → 不取(那次平台读注定失败)', () => {
20
- expect(gateOf(capability('ok'), 'capability')).toEqual({ gated: true });
21
- expect(gateOf(capability('missing-plugin'), 'capability')).toEqual({ gated: false });
22
- expect(gateOf(capability('missing-commands'), 'capability')).toEqual({ gated: false });
23
- });
24
-
25
- it('★ 探不动(unchecked)→ **照常取数**:那是"问不到",不是"没装"(ADR-0016)', () => {
26
- expect(gateOf(capability('unchecked'), 'capability')).toEqual({ gated: true });
27
- });
28
-
29
- it("mode: 'none' → 永远取数(套件自己决定不要这道门)", () => {
30
- expect(gateOf(capability('missing-plugin'), 'none')).toEqual({ gated: true });
31
- expect(gateOf(null, 'none')).toEqual({ gated: true });
32
- });
33
- });
34
-
35
- describe('splitBlocker(一条阻断 + 至多一条提示)', () => {
36
- it('没有候选 → 两条都没有', () => {
37
- expect(splitBlocker([])).toEqual({});
38
- expect(splitBlocker([undefined, undefined])).toEqual({});
39
- });
40
-
41
- it('★ 第一条 block 赢(一张卡上压两条阻断,用户不知道该按哪个)', () => {
42
- const first = blocker('一', 'block');
43
- const second = blocker('二', 'block');
44
-
45
- expect(splitBlocker([first, second])).toEqual({ blocker: first });
46
- });
47
-
48
- it('只有 notice → 就是 notice(不拦人)', () => {
49
- const notice = blocker('降级', 'notice');
50
-
51
- expect(splitBlocker([undefined, notice])).toEqual({ notice });
52
- });
53
-
54
- it('★ 有阻断时丢掉提示:那一刻提示只是噪声', () => {
55
- const block = blocker('缺插件', 'block');
56
- const notice = blocker('降级', 'notice');
57
-
58
- expect(splitBlocker([notice, block])).toEqual({ blocker: block });
59
- });
60
- });
61
-
62
- describe('probeKeyOf(出错搭车强探的防抖键)', () => {
63
- it('没有错误 → null(调用方据此不探)', () => {
64
- expect(probeKeyOf(undefined)).toBeNull();
65
- });
66
-
67
- it('★ 键就是错误码:码没变就不再探(否则"错误 → 强探 → 仍然错误"会转圈)', () => {
68
- expect(probeKeyOf({ code: 'auth-required', message: '未登录' })).toBe('auth-required');
69
- expect(probeKeyOf({ code: 'auth-required', message: '未登录(另一个环境)' })).toBe('auth-required');
70
- expect(probeKeyOf({ code: 'missing-plugin', message: '缺插件' })).toBe('missing-plugin');
71
- });
72
-
73
- it('空码给一个稳定的兜底键(不许变成"每次都算新错误")', () => {
74
- expect(probeKeyOf({ code: '', message: '没说什么' })).toBe('unknown');
75
- });
76
- });