@dsh-workbench/shared 1.1.13 → 1.1.14-beta.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 (91) hide show
  1. package/README.md +6 -4
  2. package/dist/api/dsh.d.ts +2 -0
  3. package/dist/contract/command.d.ts +2 -0
  4. package/dist/contract/unit-declaration.js +3 -2
  5. package/dist/host/data-root.js +5 -3
  6. package/dist/host/flow/step.d.ts +11 -0
  7. package/dist/host/flow/step.js +21 -0
  8. package/dist/host/host-kit.d.ts +7 -2
  9. package/dist/host/host-kit.js +4 -1
  10. package/dist/host/install-failure.js +1 -1
  11. package/dist/host/lib-install.d.ts +1 -0
  12. package/dist/host/lib-install.js +4 -3
  13. package/dist/host/paths.js +8 -7
  14. package/dist/host/payload.d.ts +12 -0
  15. package/dist/host/payload.js +16 -0
  16. package/dist/host/preflight.d.ts +2 -0
  17. package/dist/host/shell-text.d.ts +11 -1
  18. package/dist/host/shell-text.js +11 -3
  19. package/dist/host/shell.d.ts +1 -0
  20. package/dist/host/shell.js +2 -1
  21. package/dist/host/tool-approval.d.ts +20 -0
  22. package/dist/host/tool-approval.js +20 -0
  23. package/dist/host/unit-face.d.ts +5 -0
  24. package/dist/host/unit-plugin.d.ts +33 -0
  25. package/dist/host/unit-plugin.js +25 -0
  26. package/dist/host/workspace-store.js +12 -6
  27. package/dist/host.d.ts +3 -0
  28. package/dist/host.js +3 -0
  29. package/dist/i18n/en.js +1 -0
  30. package/dist/i18n/index.d.ts +2 -1
  31. package/dist/i18n/zh.d.ts +1 -0
  32. package/dist/i18n/zh.js +1 -0
  33. package/dist/lib/board-fact.d.ts +2 -1
  34. package/dist/lib/board-fact.js +10 -4
  35. package/dist/lib/ui.d.ts +8 -1
  36. package/dist/lib/ui.js +21 -3
  37. package/dist/lib/unit-plugin.d.ts +28 -0
  38. package/dist/lib/unit-plugin.js +23 -0
  39. package/dist/testing/host-fakes.d.ts +2 -0
  40. package/dist/testing/host-fakes.js +1 -1
  41. package/package.json +2 -2
  42. package/src/api/dsh.ts +2 -0
  43. package/src/components/__tests__/long-task-row.test.ts +1 -1
  44. package/src/contract/command.ts +2 -0
  45. package/src/contract/unit-declaration.ts +3 -2
  46. package/src/host/__tests__/data-root.test.ts +47 -0
  47. package/src/host/__tests__/plugin-helpers.test.ts +68 -0
  48. package/src/host/__tests__/runtime-layout.test.ts +5 -4
  49. package/src/host/__tests__/shell-text.test.ts +32 -7
  50. package/src/host/__tests__/shell.test.ts +24 -0
  51. package/src/host/data-root.ts +6 -3
  52. package/src/host/flow/__tests__/step.test.ts +43 -0
  53. package/src/host/flow/step.ts +21 -0
  54. package/src/host/host-kit.ts +11 -3
  55. package/src/host/install-failure.ts +1 -1
  56. package/src/host/lib-install.ts +5 -3
  57. package/src/host/paths.ts +8 -7
  58. package/src/host/payload.ts +17 -0
  59. package/src/host/preflight.ts +2 -0
  60. package/src/host/shell-text.ts +16 -2
  61. package/src/host/shell.ts +8 -2
  62. package/src/host/tool-approval.ts +31 -0
  63. package/src/host/unit-face.ts +5 -0
  64. package/src/host/unit-plugin.ts +49 -0
  65. package/src/host/workspace-store.ts +13 -6
  66. package/src/host.ts +3 -0
  67. package/src/i18n/en.ts +1 -0
  68. package/src/i18n/zh.ts +1 -0
  69. package/src/lib/__tests__/board-fact.test.ts +8 -0
  70. package/src/lib/__tests__/ui.test.ts +26 -0
  71. package/src/lib/board-fact.ts +11 -4
  72. package/src/lib/ui.ts +21 -3
  73. package/src/testing/host-fakes.ts +3 -1
  74. package/dist/components/SelectFieldWheel.d.ts +0 -9
  75. package/dist/components/SelectFieldWheel.js +0 -35
  76. package/dist/host/channel-plugin.d.ts +0 -15
  77. package/dist/host/channel-plugin.js +0 -42
  78. package/dist/host/command-name.d.ts +0 -1
  79. package/dist/host/command-name.js +0 -14
  80. package/dist/host/opencli-runner.d.ts +0 -26
  81. package/dist/host/opencli-runner.js +0 -50
  82. package/dist/host/plugin-source.d.ts +0 -33
  83. package/dist/host/plugin-source.js +0 -91
  84. package/dist/host/runner.d.ts +0 -26
  85. package/dist/host/runner.js +0 -50
  86. package/dist/host/runtime-links.d.ts +0 -7
  87. package/dist/host/runtime-links.js +0 -25
  88. package/dist/lib/version.d.ts +0 -4
  89. package/dist/lib/version.js +0 -21
  90. package/dist/testing/git.d.ts +0 -3
  91. package/dist/testing/git.js +0 -36
@@ -620,6 +620,7 @@ export declare const dictionaries: {
620
620
  readonly 'env.fix.permission': "权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。";
621
621
  readonly 'env.fix.network': "网络不通:检查网络或代理设置后重试。";
622
622
  readonly 'env.fix.details': "详细输出(排障用)";
623
+ readonly 'env.log': "详细输出";
623
624
  readonly 'env.retry': "重试";
624
625
  readonly 'env.wake': "打开浏览器";
625
626
  readonly 'env.wake.opening': "正在打开浏览器…";
@@ -826,7 +827,7 @@ export declare const dictionaries: {
826
827
  readonly 'configSync.emptyProviders': "工作台还没有任何模型供应商 —— 去「配置同步」从某个 profile 把模型供应商那几行同步过来,或自己去「模型」里配。";
827
828
  readonly 'configSync.failed': "同步没成功,再试一次。";
828
829
  };
829
- en: Record<"time.now" | "time.ago" | "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.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.unsupported" | "env.unit.unsupportedHint" | "env.unit.degraded" | "env.unit.degradedHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "profileCreate.nav" | "profileCreate.lead" | "profileCreate.name" | "profileCreate.nameHint" | "profileCreate.source" | "profileCreate.current" | "profileCreate.copy" | "profileCreate.settings" | "profileCreate.create" | "profileCreate.creating" | "profileCreate.created" | "profileCreate.command" | "profileCreate.readFailed" | "profileCreate.retry" | "profileCreate.reading" | "profileCreate.noProfiles" | "profileCreate.failed" | "configSync.nav" | "configSync.lead" | "configSync.source" | "configSync.sourceBroken" | "configSync.sourceRows" | "configSync.sourceDead" | "configSync.refresh" | "configSync.loading" | "configSync.readHint" | "configSync.tag.new" | "configSync.tag.differs" | "configSync.conflictNote" | "configSync.code.lang" | "configSync.code.copy" | "configSync.code.copied" | "configSync.code.wrap" | "configSync.code.unwrap" | "configSync.code.collapse" | "configSync.code.collapseAria" | "configSync.code.expandAria" | "configSync.code.expand" | "configSync.missing" | "configSync.apply" | "configSync.applyNone" | "configSync.applying" | "configSync.applied" | "configSync.appliedUnverified" | "configSync.appliedIds" | "configSync.skippedIds" | "configSync.nothing" | "configSync.unreadable" | "configSync.noSource" | "configSync.emptyProviders" | "configSync.failed" | "env.need.bin.missing" | "env.need.bin.hint" | "env.need.extension.missing" | "env.need.extension.outdated" | "env.need.extension.hint" | "env.need.lib.missing" | "env.need.lib.outdated" | "env.need.lib.unsatisfied" | "env.need.lib.hint" | "env.need.env.missing" | "env.need.env.hint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.add.busy" | "project.add.failed" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.group.workspaces" | "project.group.defaultWorkspace" | "project.group.workshop" | "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.list.label" | "session.copy" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.longTask" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.compact.approval" | "status.compact.planReview" | "status.compact.answer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "project.attention.warning.one" | "project.attention.warning.other" | "project.attention.running.one" | "project.attention.running.other" | "project.attention.longTask.one" | "project.attention.longTask.other" | "project.attention.done.one" | "project.attention.done.other" | "project.attention.state.warning" | "project.attention.state.running" | "project.attention.state.done" | "hover.copied" | "time.short.minutes" | "time.short.hours" | "time.short.days" | "time.short.months" | "time.short.years" | "panels.title" | "panels.groupBoardless" | "panels.groupPlugins" | "runtime.plugin" | "runtime.pluginOf" | "runtime.pluginThing" | "runtime.pluginArea" | "panels.market.defaultHint" | "panels.market.defaultLabel" | "panels.market.defaultLocked" | "panels.followAll" | "panels.followAllConfirmTitle" | "panels.followAllConfirmDetail" | "panels.followAllConfirmAck" | "panels.followAllConfirmYes" | "panels.followAllConfirmNo" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.hintJoin" | "panels.dep.failed" | "panels.dep.unchecked" | "panels.dep.missing" | "panels.dep.outdated" | "panels.dep.degraded" | "panels.dep.unsupported" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.notActive" | "panels.market.notUsable" | "panels.market.broken" | "panels.market.hasNew" | "panels.market.hasProblem" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.uncheckedHint" | "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.swapSlow" | "panels.market.removeSlow" | "panels.market.updateNoChange" | "panels.market.updateUnknownVersion" | "panels.market.updateGone" | "panels.market.updateRestored" | "panels.market.updateBehind" | "panels.market.updateStuck.title" | "panels.market.updateStuck.factNoChange" | "panels.market.updateStuck.copiedHint" | "panels.market.selfUpdateFailed" | "panels.market.selfUpdateUnknown" | "panels.market.selfUpdateRolledBack" | "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.noMarketSource" | "panels.market.failed" | "panels.market.failure.permission" | "panels.market.failure.diskFull" | "panels.market.failure.sandbox" | "panels.market.failure.unknown" | "panels.market.failure.factOwner" | "panels.market.failure.factMode" | "panels.market.failure.factSpace" | "panels.market.failure.factStuck" | "panels.market.failure.nowhere" | "panels.market.failure.copyFix" | "panels.market.failure.copied" | "panels.market.failure.copiedHint" | "panels.market.failure.askAgent" | "panels.market.failure.askAgentText" | "panels.market.failure.askAgentHint" | "panels.market.failure.recheck" | "panels.market.failure.showOutput" | "panels.market.failure.hideOutput" | "panels.market.failure.term.commandLine" | "panels.market.failure.term.signal" | "panels.market.failure.term.exitCode" | "panels.market.failure.term.noExitCode" | "panels.market.failure.term.running" | "panels.market.failure.term.failed" | "panels.market.failure.term.done" | "panels.market.failure.term.copy" | "panels.market.failure.term.copied" | "panels.market.failure.term.noOutput" | "panels.market.failure.term.collapse" | "panels.market.failure.term.expandAria" | "panels.market.failure.term.expand" | "panels.market.needEnv" | "panels.market.needCommands" | "panels.market.needBinary" | "panels.market.needLib" | "panels.market.localMark" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionPlugins" | "panels.market.openDetail" | "panels.market.detailBack" | "panels.market.detailSourceTitle" | "panels.market.detailSource" | "panels.market.detailRegistry" | "panels.market.detailSpec" | "panels.market.detailUnit" | "panels.market.detailVersion" | "panels.market.detailLatest" | "panels.market.detailPluginName" | "panels.market.detailInstalledAt" | "panels.market.detailMarket" | "panels.market.detailRepository" | "panels.market.detailCommit" | "panels.market.detailDirectory" | "panels.market.detailUpdateSource" | "panels.market.detailUpdateVersion" | "panels.market.detailLocalDirectory" | "panels.market.detailUnknown" | "panels.market.moduleOutdated" | "panels.market.updateNeedsWorkbench" | "panels.market.updateIncompatible" | "panels.market.moduleRefused" | "panels.market.moduleRestartHint" | "panels.market.pendingRestart" | "panels.market.pendingFollowUp" | "panels.market.migrationHint" | "panels.market.migrateAction" | "panels.market.migrating" | "panels.market.migrateConfirmTitle" | "panels.market.migrateConfirmDetail" | "panels.market.migrateConfirmAck" | "panels.market.migrateConfirmYes" | "panels.market.migrateDone" | "panels.market.dismissFollowUp" | "panels.market.dismissing" | "panels.market.dismissFollowUpTitle" | "panels.market.dismissFollowUpDetail" | "panels.market.dismissFollowUpAck" | "panels.market.dismissFollowUpYes" | "panels.market.dismissed" | "panels.market.workbenchName" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.checkUpdates" | "panels.market.checkAll" | "panels.market.checkingNow" | "panels.market.checkFailed" | "panels.market.checkedAt" | "panels.market.latestPartial" | "panels.market.latestNoRegistry" | "panels.market.latestProbeFailed" | "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.pluginPreview" | "panels.market.add.pluginAlias" | "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.failureStopProfile" | "panels.market.add.failureCancelled" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureInstalled" | "panels.market.add.failureGeneric" | "panels.market.add.failureCredentials" | "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.tabInstalled" | "panels.market.tabs" | "panels.tab.discover" | "panels.discover.loading" | "panels.discover.slowHint" | "panels.discover.lead" | "panels.discover.addSource" | "panels.discover.addingSource" | "panels.discover.addFirstSource" | "panels.discover.removeSource" | "panels.discover.removeTitle" | "panels.discover.removeBody" | "panels.discover.sourceActions" | "panels.discover.sourceStateWritable" | "panels.discover.sourceStateReadOnly" | "panels.discover.sourceEnable" | "panels.discover.sourceDisable" | "panels.discover.sourceWorking" | "panels.discover.sourceEnabled" | "panels.discover.sourceReadOnly" | "panels.discover.refresh" | "panels.discover.search" | "panels.discover.clearSearch" | "panels.discover.all" | "panels.discover.sourceLabel" | "panels.discover.sourceKind" | "panels.discover.sourceKindHttp" | "panels.discover.sourceKindGit" | "panels.discover.sourceUrl" | "panels.discover.sourcePlaceholder" | "panels.discover.sourceRef" | "panels.discover.sourcePath" | "panels.discover.sourceLocal" | "panels.discover.sourceLocalHint" | "panels.discover.save" | "panels.discover.cancel" | "panels.discover.retry" | "panels.discover.noSource" | "panels.discover.noSourceHint" | "panels.discover.sourcesFailed" | "panels.discover.unreadable" | "panels.discover.sandbox" | "panels.discover.sourceAuth" | "panels.discover.invalid" | "panels.discover.emptyRepo" | "panels.discover.missingIndex" | "panels.discover.initTitle" | "panels.discover.initCta" | "panels.discover.initWhat" | "panels.discover.initBranch" | "panels.discover.initBranchHint" | "panels.discover.initBranchBad" | "panels.discover.initOk" | "panels.discover.initBusy" | "panels.discover.initDone" | "panels.discover.stale" | "panels.discover.empty" | "panels.discover.noMatch" | "panels.discover.notInstalled" | "panels.discover.installed" | "panels.discover.installedVersion" | "panels.discover.update" | "panels.discover.badShell" | "panels.discover.badShellDetail" | "panels.discover.unverified" | "panels.discover.installing" | "panels.discover.installingNamed" | "panels.discover.cancelInstall" | "panels.discover.cancelling" | "panels.discover.installCancelled" | "panels.discover.installedDone" | "panels.discover.failed" | "panels.discover.transportTitle" | "panels.discover.transportSsh" | "panels.discover.transportHttps" | "panels.discover.transportOk" | "panels.discover.transportAuth" | "panels.discover.transportUnreachable" | "panels.discover.transportUnknown" | "panels.discover.transportChecking" | "panels.discover.transportTest" | "panels.discover.transportPlaceholder" | "panels.discover.transportNeedsAuth" | "panels.discover.transportChange" | "panels.discover.transportChangeHide" | "panels.discover.transportNone" | "panels.discover.expand" | "panels.discover.collapse" | "panels.discover.versionCount" | "panels.discover.sourceNpm" | "panels.discover.sourceNpmDefault" | "panels.discover.sourceGit" | "panels.discover.installVersion" | "panels.discover.confirmTitle" | "panels.discover.confirmDetail" | "panels.discover.confirmAck" | "panels.discover.confirmOk" | "panels.discover.confirmCancel" | "panels.discover.marketMenu" | "panels.discover.manageSources" | "panels.discover.sourcesTitle" | "panels.discover.sourcesIntro" | "panels.discover.sourcesLabel" | "panels.discover.sourceTrust" | "panels.discover.sourceDuplicate" | "panels.discover.sourceBadUrl" | "panels.discover.close" | "panels.discover.labelsMenu" | "panels.discover.labelsCount" | "panels.discover.tagsAll" | "panels.discover.sort" | "panels.discover.sortWith" | "panels.discover.sortRelevance" | "panels.discover.sortDefault" | "panels.discover.sortPublished" | "panels.discover.categoriesLabel" | "panels.discover.showMore" | "panels.discover.entryMeta" | "panels.discover.entryMetaAuthor" | "panels.discover.openEntry" | "panels.discover.cardInstall" | "panels.discover.cardInstalled" | "panels.discover.cardUpdate" | "panels.discover.cardBlocked" | "panels.discover.installTitle" | "panels.discover.installVersionTitle" | "panels.discover.openInstalled" | "panels.discover.noCompatibleVersion" | "panels.discover.detailBack" | "panels.discover.publishedAt" | "panels.discover.detailMarket" | "panels.discover.detailIncompatible" | "panels.discover.detailUnknown" | "panels.discover.detailSource" | "panels.discover.authorUnknown" | "panels.discover.tabOverview" | "panels.discover.tabReadme" | "panels.discover.tabChangelog" | "panels.discover.docReadme" | "panels.discover.docChangelog" | "panels.discover.docLoading" | "panels.discover.docMissing" | "panels.discover.docUnavailable" | "panels.discover.docUnknownReason" | "panels.discover.detailTabs" | "panels.discover.factPackage" | "panels.discover.factRegistry" | "panels.discover.factRepository" | "panels.discover.factCommit" | "panels.discover.factDirectory" | "panels.discover.factPublished" | "panels.discover.factAuthor" | "panels.discover.factHomepage" | "panels.discover.factCategory" | "panels.discover.factLabels" | "panels.discover.factMarket" | "panels.discover.detailVersions" | "panels.discover.versionsLabel" | "panels.discover.tagRecommended" | "panels.discover.tagPrerelease" | "panels.discover.tagInstalled" | "panels.discover.tagIncompatible" | "panels.discover.tagUnknown" | "panels.discover.selected" | "panels.discover.done" | "panels.discover.installTrust" | "panels.discover.progress" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.restartWhy" | "panels.market.add.restartWhy.no-hmr" | "panels.market.add.restartWhy.package-moved" | "panels.market.add.restartWhy.host-module" | "panels.market.add.restartWhy.unmanaged-plugin" | "panels.market.add.restartWhy.module-comparison" | "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.update" | "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.installingToolchain" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.stepFailed.host" | "env.toolchain.ok" | "env.toolchain.warn" | "env.runtime.ok" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.hold" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.whyHost" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.stepHost" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.retry" | "env.wake" | "env.wake.opening" | "env.wake.browserOpen" | "env.wake.noBrowser" | "env.wake.failed" | "env.bridge.continue" | "env.diy.ok" | "env.diy.short" | "env.diy.hold" | "env.diy.why" | "env.diy.rows" | "env.diy.noRow" | "env.diy.step" | "env.diy.continue" | "env.copyCommand" | "env.copied" | "env.step.toolchain" | "env.step.adapter" | "env.step.runtime" | "env.step.bridge" | "env.step.diy" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "adapterSet.not-configured" | "adapterSet.no-profile" | "adapterSet.malformed" | "adapterSet.not-resolvable" | "adapterSet.import-failed" | "adapterSet.bad-export" | "adapterSet.factory-failed" | "adapterSet.incompatible-version" | "adapterSet.missing-member" | "env.unit.hint" | "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" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label" | "adapterSet.title" | "adapterSet.lead" | "adapterSet.active" | "adapterSet.defaults" | "adapterSet.failed" | "adapterSet.healthUnreadable" | "workbenchSettings.nav" | "workbenchSettings.lead" | "sidebarEdition.nav" | "sidebarEdition.lead" | "sidebarEdition.official" | "sidebarEdition.officialHint" | "sidebarEdition.custom" | "sidebarEdition.customHint" | "sidebarEdition.saving" | "sidebarEdition.reloadRequired" | "sidebarEdition.reload" | "sidebarEdition.failed" | "debug.workspace" | "debug.status" | "debug.status.trouble" | "debug.stalled" | "debug.act.pause" | "debug.act.resume" | "debug.act.stop" | "debug.act.stop.ask" | "debug.card.here.title" | "debug.card.here.note" | "debug.card.away.note" | "debug.card.paused.note" | "debug.card.hidden.note" | "record.waiting" | "record.waiting.note" | "record.recording" | "record.recording.note" | "record.recording.shots" | "record.finished" | "record.finished.note" | "record.act.cancel" | "record.act.halt" | "approval.record.site" | "approval.record.what" | "approval.record.lands" | "approval.record.noUrl" | "approval.record.oneAction" | "approval.record.noSwitch" | "approval.longTask.what" | "approval.longTask.signal" | "approval.plugin.name" | "approval.plugin.command" | "approval.plugin.note" | "approval.publish.module" | "approval.publish.target" | "approval.publish.version" | "approval.publish.author" | "approval.publish.authorMissing" | "approval.publish.labels" | "approval.publish.labelsMissing" | "approval.publish.owner" | "approval.publish.ownerUnowned" | "approval.publish.ownerTaken" | "approval.publish.unit" | "approval.publish.unitTaken" | "approval.publish.path" | "approval.publish.pathTaken" | "approval.publish.registry" | "approval.publish.repo" | "approval.publish.branch" | "approval.publish.defaultBranch" | "approval.publish.artifacts" | "approval.publish.index" | "approval.publish.commits" | "approval.publish.twoCommits" | "approval.publish.reading" | "approval.publish.changelog" | "approval.publish.changelogPending" | "approval.publish.changelogMissing" | "approval.publish.targetAuto" | "approval.publish.targetTeam" | "approval.publish.targetPublic" | "approval.publish.targetTarball" | "approval.publish.targetGit" | "approval.publish.targetMarket" | "approval.publish.targetLocal" | "approval.market.source" | "approval.market.url" | "approval.market.action" | "approval.market.policy" | "approval.market.enable" | "approval.market.disable" | "approval.market.writable" | "approval.market.readOnly" | "approval.market.reading" | "markdown.copy" | "markdown.copied" | "markdown.footnotes", string>;
830
+ en: Record<"time.now" | "time.ago" | "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.market.open" | "env.unit.failed" | "env.unit.failedHint" | "env.unit.unsupported" | "env.unit.unsupportedHint" | "env.unit.degraded" | "env.unit.degradedHint" | "env.unit.unchecked" | "env.unit.uncheckedHint" | "profileCreate.nav" | "profileCreate.lead" | "profileCreate.name" | "profileCreate.nameHint" | "profileCreate.source" | "profileCreate.current" | "profileCreate.copy" | "profileCreate.settings" | "profileCreate.create" | "profileCreate.creating" | "profileCreate.created" | "profileCreate.command" | "profileCreate.readFailed" | "profileCreate.retry" | "profileCreate.reading" | "profileCreate.noProfiles" | "profileCreate.failed" | "configSync.nav" | "configSync.lead" | "configSync.source" | "configSync.sourceBroken" | "configSync.sourceRows" | "configSync.sourceDead" | "configSync.refresh" | "configSync.loading" | "configSync.readHint" | "configSync.tag.new" | "configSync.tag.differs" | "configSync.conflictNote" | "configSync.code.lang" | "configSync.code.copy" | "configSync.code.copied" | "configSync.code.wrap" | "configSync.code.unwrap" | "configSync.code.collapse" | "configSync.code.collapseAria" | "configSync.code.expandAria" | "configSync.code.expand" | "configSync.missing" | "configSync.apply" | "configSync.applyNone" | "configSync.applying" | "configSync.applied" | "configSync.appliedUnverified" | "configSync.appliedIds" | "configSync.skippedIds" | "configSync.nothing" | "configSync.unreadable" | "configSync.noSource" | "configSync.emptyProviders" | "configSync.failed" | "env.need.bin.missing" | "env.need.bin.hint" | "env.need.extension.missing" | "env.need.extension.outdated" | "env.need.extension.hint" | "env.need.lib.missing" | "env.need.lib.outdated" | "env.need.lib.unsatisfied" | "env.need.lib.hint" | "env.need.env.missing" | "env.need.env.hint" | "brand.name" | "toggle.open" | "toggle.collapse" | "project.label" | "workspace.section" | "project.none" | "project.empty" | "project.add" | "project.add.busy" | "project.add.failed" | "project.search.label" | "project.search.placeholder" | "project.search.empty" | "project.group.workspaces" | "project.group.defaultWorkspace" | "project.group.workshop" | "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.list.label" | "session.copy" | "session.pinned" | "session.actions" | "status.idle" | "status.running" | "status.longTask" | "status.waitingApproval" | "status.planReview" | "status.waitingAnswer" | "status.compact.approval" | "status.compact.planReview" | "status.compact.answer" | "status.completed" | "status.subagentsRunning.one" | "status.subagentsRunning.other" | "project.attention.warning.one" | "project.attention.warning.other" | "project.attention.running.one" | "project.attention.running.other" | "project.attention.longTask.one" | "project.attention.longTask.other" | "project.attention.done.one" | "project.attention.done.other" | "project.attention.state.warning" | "project.attention.state.running" | "project.attention.state.done" | "hover.copied" | "time.short.minutes" | "time.short.hours" | "time.short.days" | "time.short.months" | "time.short.years" | "panels.title" | "panels.groupBoardless" | "panels.groupPlugins" | "runtime.plugin" | "runtime.pluginOf" | "runtime.pluginThing" | "runtime.pluginArea" | "panels.market.defaultHint" | "panels.market.defaultLabel" | "panels.market.defaultLocked" | "panels.followAll" | "panels.followAllConfirmTitle" | "panels.followAllConfirmDetail" | "panels.followAllConfirmAck" | "panels.followAllConfirmYes" | "panels.followAllConfirmNo" | "panels.enabled" | "panels.disabled" | "panels.choose" | "panels.buildModule" | "panels.failed" | "panels.dragHint" | "panels.unknownStep" | "panels.labelWithHint" | "panels.hintJoin" | "panels.dep.failed" | "panels.dep.unchecked" | "panels.dep.missing" | "panels.dep.outdated" | "panels.dep.degraded" | "panels.dep.unsupported" | "panels.tab.modules" | "panels.tab.market" | "panels.browseMarket" | "panels.noneInstalled" | "panels.noneInstalledHint" | "panels.market.installed" | "panels.market.notActive" | "panels.market.notUsable" | "panels.market.broken" | "panels.market.hasNew" | "panels.market.hasProblem" | "panels.market.missing" | "panels.market.unchecked" | "panels.market.uncheckedHint" | "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.swapSlow" | "panels.market.removeSlow" | "panels.market.updateNoChange" | "panels.market.updateUnknownVersion" | "panels.market.updateGone" | "panels.market.updateRestored" | "panels.market.updateBehind" | "panels.market.updateStuck.title" | "panels.market.updateStuck.factNoChange" | "panels.market.updateStuck.copiedHint" | "panels.market.selfUpdateFailed" | "panels.market.selfUpdateUnknown" | "panels.market.selfUpdateRolledBack" | "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.noMarketSource" | "panels.market.failed" | "panels.market.failure.permission" | "panels.market.failure.diskFull" | "panels.market.failure.sandbox" | "panels.market.failure.unknown" | "panels.market.failure.factOwner" | "panels.market.failure.factMode" | "panels.market.failure.factSpace" | "panels.market.failure.factStuck" | "panels.market.failure.nowhere" | "panels.market.failure.copyFix" | "panels.market.failure.copied" | "panels.market.failure.copiedHint" | "panels.market.failure.askAgent" | "panels.market.failure.askAgentText" | "panels.market.failure.askAgentHint" | "panels.market.failure.recheck" | "panels.market.failure.showOutput" | "panels.market.failure.hideOutput" | "panels.market.failure.term.commandLine" | "panels.market.failure.term.signal" | "panels.market.failure.term.exitCode" | "panels.market.failure.term.noExitCode" | "panels.market.failure.term.running" | "panels.market.failure.term.failed" | "panels.market.failure.term.done" | "panels.market.failure.term.copy" | "panels.market.failure.term.copied" | "panels.market.failure.term.noOutput" | "panels.market.failure.term.collapse" | "panels.market.failure.term.expandAria" | "panels.market.failure.term.expand" | "panels.market.needEnv" | "panels.market.needCommands" | "panels.market.needBinary" | "panels.market.needLib" | "panels.market.localMark" | "panels.market.noSource" | "panels.market.badSource" | "panels.market.empty" | "panels.market.sectionPlugins" | "panels.market.openDetail" | "panels.market.detailBack" | "panels.market.detailSourceTitle" | "panels.market.detailSource" | "panels.market.detailRegistry" | "panels.market.detailSpec" | "panels.market.detailUnit" | "panels.market.detailVersion" | "panels.market.detailLatest" | "panels.market.detailPluginName" | "panels.market.detailInstalledAt" | "panels.market.detailMarket" | "panels.market.detailRepository" | "panels.market.detailCommit" | "panels.market.detailDirectory" | "panels.market.detailUpdateSource" | "panels.market.detailUpdateVersion" | "panels.market.detailLocalDirectory" | "panels.market.detailUnknown" | "panels.market.moduleOutdated" | "panels.market.updateNeedsWorkbench" | "panels.market.updateIncompatible" | "panels.market.moduleRefused" | "panels.market.moduleRestartHint" | "panels.market.pendingRestart" | "panels.market.pendingFollowUp" | "panels.market.migrationHint" | "panels.market.migrateAction" | "panels.market.migrating" | "panels.market.migrateConfirmTitle" | "panels.market.migrateConfirmDetail" | "panels.market.migrateConfirmAck" | "panels.market.migrateConfirmYes" | "panels.market.migrateDone" | "panels.market.dismissFollowUp" | "panels.market.dismissing" | "panels.market.dismissFollowUpTitle" | "panels.market.dismissFollowUpDetail" | "panels.market.dismissFollowUpAck" | "panels.market.dismissFollowUpYes" | "panels.market.dismissed" | "panels.market.workbenchName" | "panels.market.pluginBroken" | "panels.market.restartNeeded" | "panels.market.foot" | "panels.market.checkUpdates" | "panels.market.checkAll" | "panels.market.checkingNow" | "panels.market.checkFailed" | "panels.market.checkedAt" | "panels.market.latestPartial" | "panels.market.latestNoRegistry" | "panels.market.latestProbeFailed" | "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.pluginPreview" | "panels.market.add.pluginAlias" | "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.failureStopProfile" | "panels.market.add.failureCancelled" | "panels.market.add.failureTimeout" | "panels.market.add.failurePnpmMissing" | "panels.market.add.failureInstalled" | "panels.market.add.failureGeneric" | "panels.market.add.failureCredentials" | "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.tabInstalled" | "panels.market.tabs" | "panels.tab.discover" | "panels.discover.loading" | "panels.discover.slowHint" | "panels.discover.lead" | "panels.discover.addSource" | "panels.discover.addingSource" | "panels.discover.addFirstSource" | "panels.discover.removeSource" | "panels.discover.removeTitle" | "panels.discover.removeBody" | "panels.discover.sourceActions" | "panels.discover.sourceStateWritable" | "panels.discover.sourceStateReadOnly" | "panels.discover.sourceEnable" | "panels.discover.sourceDisable" | "panels.discover.sourceWorking" | "panels.discover.sourceEnabled" | "panels.discover.sourceReadOnly" | "panels.discover.refresh" | "panels.discover.search" | "panels.discover.clearSearch" | "panels.discover.all" | "panels.discover.sourceLabel" | "panels.discover.sourceKind" | "panels.discover.sourceKindHttp" | "panels.discover.sourceKindGit" | "panels.discover.sourceUrl" | "panels.discover.sourcePlaceholder" | "panels.discover.sourceRef" | "panels.discover.sourcePath" | "panels.discover.sourceLocal" | "panels.discover.sourceLocalHint" | "panels.discover.save" | "panels.discover.cancel" | "panels.discover.retry" | "panels.discover.noSource" | "panels.discover.noSourceHint" | "panels.discover.sourcesFailed" | "panels.discover.unreadable" | "panels.discover.sandbox" | "panels.discover.sourceAuth" | "panels.discover.invalid" | "panels.discover.emptyRepo" | "panels.discover.missingIndex" | "panels.discover.initTitle" | "panels.discover.initCta" | "panels.discover.initWhat" | "panels.discover.initBranch" | "panels.discover.initBranchHint" | "panels.discover.initBranchBad" | "panels.discover.initOk" | "panels.discover.initBusy" | "panels.discover.initDone" | "panels.discover.stale" | "panels.discover.empty" | "panels.discover.noMatch" | "panels.discover.notInstalled" | "panels.discover.installed" | "panels.discover.installedVersion" | "panels.discover.update" | "panels.discover.badShell" | "panels.discover.badShellDetail" | "panels.discover.unverified" | "panels.discover.installing" | "panels.discover.installingNamed" | "panels.discover.cancelInstall" | "panels.discover.cancelling" | "panels.discover.installCancelled" | "panels.discover.installedDone" | "panels.discover.failed" | "panels.discover.transportTitle" | "panels.discover.transportSsh" | "panels.discover.transportHttps" | "panels.discover.transportOk" | "panels.discover.transportAuth" | "panels.discover.transportUnreachable" | "panels.discover.transportUnknown" | "panels.discover.transportChecking" | "panels.discover.transportTest" | "panels.discover.transportPlaceholder" | "panels.discover.transportNeedsAuth" | "panels.discover.transportChange" | "panels.discover.transportChangeHide" | "panels.discover.transportNone" | "panels.discover.expand" | "panels.discover.collapse" | "panels.discover.versionCount" | "panels.discover.sourceNpm" | "panels.discover.sourceNpmDefault" | "panels.discover.sourceGit" | "panels.discover.installVersion" | "panels.discover.confirmTitle" | "panels.discover.confirmDetail" | "panels.discover.confirmAck" | "panels.discover.confirmOk" | "panels.discover.confirmCancel" | "panels.discover.marketMenu" | "panels.discover.manageSources" | "panels.discover.sourcesTitle" | "panels.discover.sourcesIntro" | "panels.discover.sourcesLabel" | "panels.discover.sourceTrust" | "panels.discover.sourceDuplicate" | "panels.discover.sourceBadUrl" | "panels.discover.close" | "panels.discover.labelsMenu" | "panels.discover.labelsCount" | "panels.discover.tagsAll" | "panels.discover.sort" | "panels.discover.sortWith" | "panels.discover.sortRelevance" | "panels.discover.sortDefault" | "panels.discover.sortPublished" | "panels.discover.categoriesLabel" | "panels.discover.showMore" | "panels.discover.entryMeta" | "panels.discover.entryMetaAuthor" | "panels.discover.openEntry" | "panels.discover.cardInstall" | "panels.discover.cardInstalled" | "panels.discover.cardUpdate" | "panels.discover.cardBlocked" | "panels.discover.installTitle" | "panels.discover.installVersionTitle" | "panels.discover.openInstalled" | "panels.discover.noCompatibleVersion" | "panels.discover.detailBack" | "panels.discover.publishedAt" | "panels.discover.detailMarket" | "panels.discover.detailIncompatible" | "panels.discover.detailUnknown" | "panels.discover.detailSource" | "panels.discover.authorUnknown" | "panels.discover.tabOverview" | "panels.discover.tabReadme" | "panels.discover.tabChangelog" | "panels.discover.docReadme" | "panels.discover.docChangelog" | "panels.discover.docLoading" | "panels.discover.docMissing" | "panels.discover.docUnavailable" | "panels.discover.docUnknownReason" | "panels.discover.detailTabs" | "panels.discover.factPackage" | "panels.discover.factRegistry" | "panels.discover.factRepository" | "panels.discover.factCommit" | "panels.discover.factDirectory" | "panels.discover.factPublished" | "panels.discover.factAuthor" | "panels.discover.factHomepage" | "panels.discover.factCategory" | "panels.discover.factLabels" | "panels.discover.factMarket" | "panels.discover.detailVersions" | "panels.discover.versionsLabel" | "panels.discover.tagRecommended" | "panels.discover.tagPrerelease" | "panels.discover.tagInstalled" | "panels.discover.tagIncompatible" | "panels.discover.tagUnknown" | "panels.discover.selected" | "panels.discover.done" | "panels.discover.installTrust" | "panels.discover.progress" | "panels.market.add.approveBuilds" | "panels.market.add.approveHint" | "panels.market.add.doneApplied" | "panels.market.add.doneRestart" | "panels.market.add.restartWhy" | "panels.market.add.restartWhy.no-hmr" | "panels.market.add.restartWhy.package-moved" | "panels.market.add.restartWhy.host-module" | "panels.market.add.restartWhy.unmanaged-plugin" | "panels.market.add.restartWhy.module-comparison" | "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.update" | "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.installingToolchain" | "env.failed" | "env.elapsed" | "env.stepFailed.install" | "env.stepFailed.path" | "env.stepFailed.shell" | "env.stepFailed.host" | "env.toolchain.ok" | "env.toolchain.warn" | "env.runtime.ok" | "env.bridge.ok" | "env.bridge.short" | "env.bridge.why" | "env.bridge.hold" | "env.bridge.unknown" | "env.fix.title" | "env.fix.why" | "env.fix.whyPath" | "env.fix.whyShell" | "env.fix.whyHost" | "env.fix.step" | "env.fix.stepPath" | "env.fix.stepShell" | "env.fix.stepHost" | "env.fix.permission" | "env.fix.network" | "env.fix.details" | "env.log" | "env.retry" | "env.wake" | "env.wake.opening" | "env.wake.browserOpen" | "env.wake.noBrowser" | "env.wake.failed" | "env.bridge.continue" | "env.diy.ok" | "env.diy.short" | "env.diy.hold" | "env.diy.why" | "env.diy.rows" | "env.diy.noRow" | "env.diy.step" | "env.diy.continue" | "env.copyCommand" | "env.copied" | "env.step.toolchain" | "env.step.adapter" | "env.step.runtime" | "env.step.bridge" | "env.step.diy" | "env.step.warm" | "env.warm.done" | "env.warm.short" | "adapterSet.not-configured" | "adapterSet.no-profile" | "adapterSet.malformed" | "adapterSet.not-resolvable" | "adapterSet.import-failed" | "adapterSet.bad-export" | "adapterSet.factory-failed" | "adapterSet.incompatible-version" | "adapterSet.missing-member" | "env.unit.hint" | "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" | "error.unknown" | "board.title" | "panel.conversation" | "panels.label" | "adapterSet.title" | "adapterSet.lead" | "adapterSet.active" | "adapterSet.defaults" | "adapterSet.failed" | "adapterSet.healthUnreadable" | "workbenchSettings.nav" | "workbenchSettings.lead" | "sidebarEdition.nav" | "sidebarEdition.lead" | "sidebarEdition.official" | "sidebarEdition.officialHint" | "sidebarEdition.custom" | "sidebarEdition.customHint" | "sidebarEdition.saving" | "sidebarEdition.reloadRequired" | "sidebarEdition.reload" | "sidebarEdition.failed" | "debug.workspace" | "debug.status" | "debug.status.trouble" | "debug.stalled" | "debug.act.pause" | "debug.act.resume" | "debug.act.stop" | "debug.act.stop.ask" | "debug.card.here.title" | "debug.card.here.note" | "debug.card.away.note" | "debug.card.paused.note" | "debug.card.hidden.note" | "record.waiting" | "record.waiting.note" | "record.recording" | "record.recording.note" | "record.recording.shots" | "record.finished" | "record.finished.note" | "record.act.cancel" | "record.act.halt" | "approval.record.site" | "approval.record.what" | "approval.record.lands" | "approval.record.noUrl" | "approval.record.oneAction" | "approval.record.noSwitch" | "approval.longTask.what" | "approval.longTask.signal" | "approval.plugin.name" | "approval.plugin.command" | "approval.plugin.note" | "approval.publish.module" | "approval.publish.target" | "approval.publish.version" | "approval.publish.author" | "approval.publish.authorMissing" | "approval.publish.labels" | "approval.publish.labelsMissing" | "approval.publish.owner" | "approval.publish.ownerUnowned" | "approval.publish.ownerTaken" | "approval.publish.unit" | "approval.publish.unitTaken" | "approval.publish.path" | "approval.publish.pathTaken" | "approval.publish.registry" | "approval.publish.repo" | "approval.publish.branch" | "approval.publish.defaultBranch" | "approval.publish.artifacts" | "approval.publish.index" | "approval.publish.commits" | "approval.publish.twoCommits" | "approval.publish.reading" | "approval.publish.changelog" | "approval.publish.changelogPending" | "approval.publish.changelogMissing" | "approval.publish.targetAuto" | "approval.publish.targetTeam" | "approval.publish.targetPublic" | "approval.publish.targetTarball" | "approval.publish.targetGit" | "approval.publish.targetMarket" | "approval.publish.targetLocal" | "approval.market.source" | "approval.market.url" | "approval.market.action" | "approval.market.policy" | "approval.market.enable" | "approval.market.disable" | "approval.market.writable" | "approval.market.readOnly" | "approval.market.reading" | "markdown.copy" | "markdown.copied" | "markdown.footnotes", string>;
830
831
  };
831
832
  /** 中文词表的键类型(从 `zh.ts` 转出):`en` 与模块词表都得覆盖同一批键。 */
832
833
  export type { WorkbenchMessages };
package/dist/i18n/zh.d.ts CHANGED
@@ -618,6 +618,7 @@ export declare const zh: {
618
618
  readonly 'env.fix.permission': "权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。";
619
619
  readonly 'env.fix.network': "网络不通:检查网络或代理设置后重试。";
620
620
  readonly 'env.fix.details': "详细输出(排障用)";
621
+ readonly 'env.log': "详细输出";
621
622
  readonly 'env.retry': "重试";
622
623
  readonly 'env.wake': "打开浏览器";
623
624
  readonly 'env.wake.opening': "正在打开浏览器…";
package/dist/i18n/zh.js CHANGED
@@ -610,6 +610,7 @@ export const zh = {
610
610
  'env.fix.permission': '权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。',
611
611
  'env.fix.network': '网络不通:检查网络或代理设置后重试。',
612
612
  'env.fix.details': '详细输出(排障用)',
613
+ 'env.log': '详细输出',
613
614
  'env.retry': '重试',
614
615
  'env.wake': '打开浏览器',
615
616
  'env.wake.opening': '正在打开浏览器…',
@@ -21,7 +21,8 @@ export interface BoardProbe {
21
21
  * 判一个路径落不落在这个工作区里(按路径前缀比,不是字符串包含)。
22
22
  * @param root - 工作区根路径。
23
23
  * @param target - 要判的路径。
24
- * @returns 同一个路径或在其下给 `true`;`root` 末尾带不带斜杠都认(`/a` 不匹配 `/ab`)。
24
+ * @returns 同一个路径或在其下给 `true`;分隔符先归一(Windows 的 `\` 与 `/` 都认),
25
+ * 盘符路径按大小写不敏感比;`root` 末尾带不带斜杠都认(`/a` 不匹配 `/ab`)。
25
26
  */
26
27
  export declare function insideWorkshop(root: string, target: string): boolean;
27
28
  /**
@@ -1,14 +1,20 @@
1
+ function normalizePath(value) {
2
+ const forward = value.replace(/\\/g, '/').replace(/\/+$/g, '');
3
+ return /^[A-Za-z]:\//.test(forward) || forward.startsWith('//') ? forward.toLowerCase() : forward;
4
+ }
1
5
  /**
2
6
  * 判一个路径落不落在这个工作区里(按路径前缀比,不是字符串包含)。
3
7
  * @param root - 工作区根路径。
4
8
  * @param target - 要判的路径。
5
- * @returns 同一个路径或在其下给 `true`;`root` 末尾带不带斜杠都认(`/a` 不匹配 `/ab`)。
9
+ * @returns 同一个路径或在其下给 `true`;分隔符先归一(Windows 的 `\` 与 `/` 都认),
10
+ * 盘符路径按大小写不敏感比;`root` 末尾带不带斜杠都认(`/a` 不匹配 `/ab`)。
6
11
  */
7
12
  export function insideWorkshop(root, target) {
8
- if (target === root)
13
+ const base = normalizePath(root);
14
+ const real = normalizePath(target);
15
+ if (real === base)
9
16
  return true;
10
- const base = root.endsWith('/') ? root : `${root}/`;
11
- return target.startsWith(base);
17
+ return real.startsWith(base === '' ? '/' : `${base}/`);
12
18
  }
13
19
  /**
14
20
  * 把探针读数归成左栏那句人话的档位。
package/dist/lib/ui.d.ts CHANGED
@@ -50,11 +50,18 @@ export declare function relativeTimeAgo(epochMs: number, t: TimeTranslate, now?:
50
50
  */
51
51
  export declare function relativeTime(epochMs: number, t: TimeTranslate): string;
52
52
  /**
53
- * 把 `/Users/<用户名>/…` 缩成 `~/…`,免得会话行里全是一样的长前缀。
53
+ * 把用户主目录(macOS `/Users/<用户名>`、Linux `/home/<用户名>`、Windows `C:\Users\<用户名>`)缩成 `~`,
54
+ * 免得会话行里全是一样的长前缀。
54
55
  * @param path - 任意值。
55
56
  * @returns 缩好的路径;不在用户主目录下原样返回,不是字符串给空串。
56
57
  */
57
58
  export declare function shortenPath(path: unknown): string;
59
+ /**
60
+ * 取路径最后一段,`/` 与 `\` 都认(Windows 路径、混合分隔符都行)。
61
+ * @param path - 任意值。
62
+ * @returns 末段;尾部有分隔符先去掉,没有分隔符就整串,不是字符串给空串。
63
+ */
64
+ export declare function pathBase(path: unknown): string;
58
65
  /**
59
66
  * 解析不带时区的本地时间戳(`YYYY-MM-DD HH:mm:ss`,也接受 `T` 分隔),按本机时区换算。
60
67
  * @param text - 原文;`null` / `undefined` 当空串;首尾空白容忍。
package/dist/lib/ui.js CHANGED
@@ -82,16 +82,34 @@ export function relativeTime(epochMs, t) {
82
82
  return t('time.days', { n: Math.floor(diff / (24 * 60 * MINUTE)) });
83
83
  return dayjs(epochMs).format('YYYY/M/D HH:mm:ss');
84
84
  }
85
+ const HOME_PREFIXES = [/^\/Users\/[^/]+/, /^\/home\/[^/]+/, /^[A-Za-z]:\\Users\\[^\\]+/];
85
86
  /**
86
- * 把 `/Users/<用户名>/…` 缩成 `~/…`,免得会话行里全是一样的长前缀。
87
+ * 把用户主目录(macOS `/Users/<用户名>`、Linux `/home/<用户名>`、Windows `C:\Users\<用户名>`)缩成 `~`,
88
+ * 免得会话行里全是一样的长前缀。
87
89
  * @param path - 任意值。
88
90
  * @returns 缩好的路径;不在用户主目录下原样返回,不是字符串给空串。
89
91
  */
90
92
  export function shortenPath(path) {
91
93
  if (typeof path !== 'string')
92
94
  return '';
93
- const hit = path.match(/^\/Users\/[^/]+/);
94
- return hit === null ? path : path.replace(hit[0], '~');
95
+ for (const pattern of HOME_PREFIXES) {
96
+ const hit = path.match(pattern);
97
+ if (hit !== null)
98
+ return path.replace(hit[0], '~');
99
+ }
100
+ return path;
101
+ }
102
+ /**
103
+ * 取路径最后一段,`/` 与 `\` 都认(Windows 路径、混合分隔符都行)。
104
+ * @param path - 任意值。
105
+ * @returns 末段;尾部有分隔符先去掉,没有分隔符就整串,不是字符串给空串。
106
+ */
107
+ export function pathBase(path) {
108
+ if (typeof path !== 'string')
109
+ return '';
110
+ const trimmed = path.replace(/[\\/]+$/, '');
111
+ const at = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\'));
112
+ return at === -1 ? trimmed : trimmed.slice(at + 1);
95
113
  }
96
114
  const LOCAL_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})$/;
97
115
  /**
@@ -0,0 +1,28 @@
1
+ import type { UnitClientFace } from '../components/board-module.js';
2
+ /**
3
+ * 浏览器面注册需要的那点上下文:`get` / `effect`。
4
+ */
5
+ export interface UnitClientContextLike {
6
+ get<T = unknown>(name: string): T | undefined;
7
+ effect(effect: () => void | (() => void), label?: string): () => void;
8
+ }
9
+ /**
10
+ * 一个浏览器插件导出的两件:`inject` / `apply`。
11
+ */
12
+ export interface UnitClientPlugin {
13
+ readonly inject: readonly string[];
14
+ apply(ctx: UnitClientContextLike): void;
15
+ }
16
+ /**
17
+ * 把「浏览器面注册进外壳」这段在每个模块里都一样的接线收成一份。
18
+ * 把它直接当插件导出用:`export const { inject, apply } = defineUnitClientPlugin({ … })`。
19
+ * @param options.unit 单元 id。
20
+ * @param options.face 这个单元的浏览器面。
21
+ * @param options.label 清理标签里用的名字;默认用 `unit`。
22
+ * @returns `{ inject, apply }`;`apply` 里取 `workbenchUnits`、缺了就 warn 一声。
23
+ */
24
+ export declare function defineUnitClientPlugin(options: {
25
+ readonly unit: string;
26
+ readonly face: UnitClientFace;
27
+ readonly label?: string | undefined;
28
+ }): UnitClientPlugin;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 把「浏览器面注册进外壳」这段在每个模块里都一样的接线收成一份。
3
+ * 把它直接当插件导出用:`export const { inject, apply } = defineUnitClientPlugin({ … })`。
4
+ * @param options.unit 单元 id。
5
+ * @param options.face 这个单元的浏览器面。
6
+ * @param options.label 清理标签里用的名字;默认用 `unit`。
7
+ * @returns `{ inject, apply }`;`apply` 里取 `workbenchUnits`、缺了就 warn 一声。
8
+ */
9
+ export function defineUnitClientPlugin(options) {
10
+ const { unit, face } = options;
11
+ const label = options.label ?? unit;
12
+ return {
13
+ inject: ['workbenchUnits'],
14
+ apply(ctx) {
15
+ const units = ctx.get('workbenchUnits');
16
+ if (units === undefined) {
17
+ console.warn(`[${unit}] 外壳没有提供 workbenchUnits 服务:这张卡片装不进来`);
18
+ return;
19
+ }
20
+ ctx.effect(() => units.registerUnit(face), `${label}: 注册 ${unit}`);
21
+ },
22
+ };
23
+ }
@@ -5,6 +5,7 @@ export interface ShellCall {
5
5
  timeoutMs?: number;
6
6
  onExpiry?: string;
7
7
  stdoutMaxBytes?: number;
8
+ env?: Readonly<Record<string, string>>;
8
9
  }
9
10
  export interface ShellReply {
10
11
  stdout?: string;
@@ -12,6 +13,7 @@ export interface ShellReply {
12
13
  exitCode?: number | null;
13
14
  timedOut?: boolean;
14
15
  aborted?: boolean;
16
+ truncated?: boolean;
15
17
  failWith?: Error;
16
18
  }
17
19
  export declare function createFakeShell(replies?: ShellReply[] | ((call: ShellCall, index: number) => ShellReply)): {
@@ -25,7 +25,7 @@ export function createFakeShell(replies = []) {
25
25
  exitCode: reply.exitCode ?? 0,
26
26
  timedOut: reply.timedOut ?? false,
27
27
  aborted: reply.aborted ?? false,
28
- stdout: { text: reply.stdout ?? '', truncated: false },
28
+ stdout: { text: reply.stdout ?? '', truncated: reply.truncated === true },
29
29
  stderr: { text: reply.stderr ?? '', truncated: false },
30
30
  };
31
31
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/shared",
3
- "version": "1.1.13",
3
+ "version": "1.1.14-beta.0",
4
4
  "description": "工作台与模块共享的纯代码与契约类型:类型、纯逻辑、哑组件、平台解析。宿主侧的东西从 /host 进(不影响浏览器产物)。",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "react": "*"
34
34
  },
35
35
  "devDependencies": {
36
- "@arch-guard/core": "^0.11.2",
36
+ "@arch-guard/core": "^0.11.3",
37
37
  "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.2",
38
38
  "@types/node": "^24.19.0",
39
39
  "@types/react": "^19.0.0",
package/src/api/dsh.ts CHANGED
@@ -149,6 +149,7 @@ export interface DshShellExecution {
149
149
 
150
150
  /**
151
151
  * dsh 的 shell 服务:先 resolve 出执行规格,再把它原样喂给 execute。
152
+ * `env` 是**叠加**在宿主环境上的变量覆盖(不是替换),所以设一个变量不必自己拼 shell 语法。
152
153
  */
153
154
  export interface DshShell {
154
155
  resolve(request: {
@@ -158,6 +159,7 @@ export interface DshShell {
158
159
  onExpiry?: 'kill' | 'none';
159
160
  stdoutMaxBytes?: number;
160
161
  signal?: AbortSignal;
162
+ env?: Readonly<Record<string, string>>;
161
163
  }): unknown;
162
164
  execute(spec: unknown): Promise<DshShellExecution>;
163
165
  }
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
3
 
4
4
  const HERE = new URL('../', import.meta.url);
5
- const read = (name: string): string => readFileSync(new URL(name, HERE), 'utf8');
5
+ const read = (name: string): string => readFileSync(new URL(name, HERE), 'utf8').replaceAll('\r\n', '\n');
6
6
 
7
7
  const ROW = read('LongTaskRow.tsx');
8
8
  const CSS = read('LongTaskRow.css');
@@ -7,6 +7,7 @@ export interface CommandRequest {
7
7
  readonly argv: readonly string[];
8
8
  readonly timeoutMs?: number | undefined;
9
9
  readonly cwd?: string | undefined;
10
+ readonly env?: Readonly<Record<string, string>> | undefined;
10
11
  readonly files?: readonly CommandFile[] | undefined;
11
12
  readonly appendFileArgs?: string | undefined;
12
13
  }
@@ -16,6 +17,7 @@ export interface CommandResult {
16
17
  readonly stderr: string;
17
18
  readonly exitCode: number;
18
19
  readonly timedOut: boolean;
20
+ readonly truncated: boolean;
19
21
  readonly filePaths: readonly string[];
20
22
  readonly error?: string | undefined;
21
23
  }
@@ -120,7 +120,8 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
120
120
  if (version === undefined) {
121
121
  return [
122
122
  `${subject}: dsh.workbench 没有 apiVersion —— 协议从 v${WORKBENCH_API_VERSION} 起,` +
123
- `写 \`"apiVersion": ${WORKBENCH_API_VERSION}\`。缺它 = 外壳不认这个模块`,
123
+ `写 \`"apiVersion": ${WORKBENCH_API_VERSION}\`。缺它 = 外壳不认这个模块` +
124
+ '(`dsh-wb-module migrate` 能自动补这一项)',
124
125
  ];
125
126
  }
126
127
  if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version as number)) {
@@ -128,7 +129,7 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
128
129
  version === 2
129
130
  ? ' —— v3 把卡片标题挪成了顶层 `title`(`{ "zh": "…", "en": "…" }` 字面量):' +
130
131
  '把 `card.titleKey` 删掉、在顶层写 title,再把 apiVersion 改成 3'
131
- : '';
132
+ : ' —— 先跑 `dsh-wb-module migrate` 把版本号改到 3,再按 check 的提示改源码';
132
133
  return [
133
134
  `${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ` +
134
135
  `${SUPPORTED_API_VERSIONS.join(' / ')}(要整数)${hint}`,
@@ -0,0 +1,47 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { afterEach, describe, expect, it, vi } from 'vitest';
4
+ import { dshHome, workbenchRoot, workspacesRoot } from '../data-root.js';
5
+
6
+ afterEach(() => {
7
+ vi.unstubAllEnvs();
8
+ });
9
+
10
+ describe('dshHome', () => {
11
+ it('★ DSH_HOME 优先', () => {
12
+ vi.stubEnv('DSH_HOME', '/tmp/explicit-home');
13
+
14
+ expect(dshHome()).toBe('/tmp/explicit-home');
15
+ });
16
+
17
+ it('★ 没配 DSH_HOME 时交给系统的用户主目录,不自己拼 HOME', () => {
18
+ vi.stubEnv('DSH_HOME', '');
19
+ vi.stubEnv('HOME', '');
20
+
21
+ expect(dshHome()).toBe(join(homedir(), '.dsh'));
22
+ expect(dshHome()).not.toBe('/.dsh');
23
+ });
24
+
25
+ it('★ 无论 HOME 怎么写,数据根都跟着系统的用户主目录走', () => {
26
+ vi.stubEnv('DSH_HOME', '');
27
+ vi.stubEnv('HOME', '/tmp/posix-only-home');
28
+
29
+ expect(dshHome()).toBe(join(homedir(), '.dsh'));
30
+ });
31
+
32
+ it('★ workbenchRoot 与 workspacesRoot 都挂在数据根下面', () => {
33
+ vi.stubEnv('DSH_HOME', '/tmp/root');
34
+ vi.stubEnv('DSH_WORKBENCH_HOME', '');
35
+
36
+ expect(workbenchRoot()).toBe(join('/tmp/root', 'workbench'));
37
+ expect(workspacesRoot()).toBe(join('/tmp/root', 'workbench', 'workspaces'));
38
+ });
39
+
40
+ it('DSH_WORKBENCH_HOME 能单独指定运行时根', () => {
41
+ vi.stubEnv('DSH_HOME', '/tmp/root');
42
+ vi.stubEnv('DSH_WORKBENCH_HOME', '/tmp/runtime');
43
+
44
+ expect(workbenchRoot()).toBe('/tmp/runtime');
45
+ expect(workspacesRoot()).toBe(join('/tmp/runtime', 'workspaces'));
46
+ });
47
+ });
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import { defineUnitHostPlugin, type UnitHostContextLike } from '../unit-plugin.js';
4
+ import { recordOf, textOf } from '../payload.js';
5
+ import { writeApprovalOf } from '../tool-approval.js';
6
+ import type { UnitModuleFace } from '../unit-face.js';
7
+
8
+ const face: UnitModuleFace = {
9
+ unit: 'demo',
10
+ mount: () => ({ routes: {}, registerTools: () => [] }),
11
+ };
12
+
13
+ describe('recordOf / textOf', () => {
14
+ it('非对象回空对象;字符串 trim;别的算空串', () => {
15
+ expect(recordOf(null)).toEqual({});
16
+ expect(recordOf('x')).toEqual({});
17
+ expect(recordOf({ a: 1 })).toEqual({ a: 1 });
18
+ expect(textOf(' hi ')).toBe('hi');
19
+ expect(textOf(3)).toBe('');
20
+ expect(textOf(undefined)).toBe('');
21
+ });
22
+ });
23
+
24
+ describe('writeApprovalOf', () => {
25
+ const approval = writeApprovalOf({
26
+ tools: ['demo_write'],
27
+ reason: '会写一条',
28
+ question: { zh: '写吗?', en: 'Write?' },
29
+ fallback: { zh: '只写模块自己的文件', en: 'Writes the module’s own file' },
30
+ detail: { zh: '写 1 条', en: 'Write 1' },
31
+ });
32
+
33
+ it('点名的写工具:ask + 明细', () => {
34
+ expect(approval.needsApproval({ name: 'demo_write' })).toMatchObject({ kind: 'ask' });
35
+ expect(approval.approvalDetail({ name: 'demo_write' })).toEqual({ zh: '写 1 条', en: 'Write 1' });
36
+ });
37
+
38
+ it('别的工具:read、没有明细', () => {
39
+ expect(approval.needsApproval({ name: 'demo_read' })).toEqual({ kind: 'read' });
40
+ expect(approval.needsApproval({})).toEqual({ kind: 'read' });
41
+ expect(approval.approvalDetail({ name: 'demo_read' })).toBeUndefined();
42
+ });
43
+ });
44
+
45
+ describe('defineUnitHostPlugin', () => {
46
+ it('导出 name / inject;apply 把面注册进 workbenchUnits 并交回清理函数', () => {
47
+ const plugin = defineUnitHostPlugin({ name: '@demo/module', unit: 'demo', face });
48
+ expect(plugin.name).toBe('@demo/module');
49
+ expect(plugin.inject).toEqual(['workbenchUnits']);
50
+ const dispose = vi.fn();
51
+ const register = vi.fn(() => dispose);
52
+ const effect = vi.fn((run: () => void | (() => void)) => {
53
+ run();
54
+ return () => {};
55
+ });
56
+ plugin.apply({ get: () => ({ register }), effect } as unknown as UnitHostContextLike);
57
+ expect(register).toHaveBeenCalledWith(face);
58
+ expect(effect).toHaveBeenCalledWith(expect.any(Function), '@demo/module: 注册 demo');
59
+ });
60
+
61
+ it('外壳没给服务:warn 一声,不抛', () => {
62
+ const plugin = defineUnitHostPlugin({ name: '@demo/module', unit: 'demo', face });
63
+ const warn = vi.fn();
64
+ const ctx = { get: () => undefined, effect: () => () => {}, logger: { warn } } as unknown as UnitHostContextLike;
65
+ expect(() => plugin.apply(ctx)).not.toThrow();
66
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('workbenchUnits'));
67
+ });
68
+ });
@@ -1,3 +1,4 @@
1
+ import { join } from 'node:path';
1
2
  import { describe, expect, it } from 'vitest';
2
3
  import { runtimeLayoutOf, runtimeRootOf } from '../paths.js';
3
4
 
@@ -6,9 +7,9 @@ describe('运行时根的算法(这里不认识任何具体运行时)', () =
6
7
  const layout = runtimeLayoutOf('demo-runtime');
7
8
 
8
9
  expect(layout.root).toBe(runtimeRootOf('demo-runtime'));
9
- expect(layout.root.endsWith('/workbench/demo-runtime')).toBe(true);
10
- expect(layout.vendorPluginsDir).toBe(`${layout.root}/plugins-npm/node_modules`);
11
- expect(layout.libsPrefix).toBe(`${layout.root}/libs`);
12
- expect(layout.npmCacheDir).toBe(`${layout.root}/npm-cache`);
10
+ expect(layout.root.endsWith(join('workbench', 'demo-runtime'))).toBe(true);
11
+ expect(layout.vendorPluginsDir).toBe(join(layout.root, 'plugins-npm', 'node_modules'));
12
+ expect(layout.libsPrefix).toBe(join(layout.root, 'libs'));
13
+ expect(layout.npmCacheDir).toBe(join(layout.root, 'npm-cache'));
13
14
  });
14
15
  });