@dsh-workbench/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +55 -0
  2. package/bin/dsh-workbench +200 -0
  3. package/bin/opencli-isolated +61 -0
  4. package/client.js +5334 -0
  5. package/cordis.patch.yml +42 -0
  6. package/dist/client/client.body.js +5323 -0
  7. package/dist/client/index.js +5324 -0
  8. package/dist/host/index.js +5420 -0
  9. package/dist/types/app/client.d.ts +1476 -0
  10. package/dist/types/app/host.d.ts +4 -0
  11. package/dist/types/app/layouts/__tests__/panel-hint.test.d.ts +1 -0
  12. package/dist/types/app/layouts/__tests__/project-selection.test.d.ts +1 -0
  13. package/dist/types/app/layouts/env-gate/__tests__/force-plan.test.d.ts +1 -0
  14. package/dist/types/app/layouts/env-gate/__tests__/gate-hidden.test.d.ts +1 -0
  15. package/dist/types/app/layouts/env-gate/__tests__/gate-mocks.test.d.ts +1 -0
  16. package/dist/types/app/layouts/env-gate/boot-gate.d.ts +16 -0
  17. package/dist/types/app/layouts/env-gate/gate-dom.d.ts +4 -0
  18. package/dist/types/app/layouts/env-gate/gate-mock.d.ts +18 -0
  19. package/dist/types/app/layouts/env-gate/gate-text.d.ts +13 -0
  20. package/dist/types/app/layouts/market/panel.d.ts +4 -0
  21. package/dist/types/app/layouts/panel-hint.d.ts +9 -0
  22. package/dist/types/app/layouts/project-selection.d.ts +3 -0
  23. package/dist/types/app/layouts/rightbar/BoardEmpty.d.ts +6 -0
  24. package/dist/types/app/layouts/rightbar/BoardMockStrip.d.ts +5 -0
  25. package/dist/types/app/layouts/rightbar/Breadcrumbs.d.ts +11 -0
  26. package/dist/types/app/layouts/rightbar/ExpandButton.d.ts +9 -0
  27. package/dist/types/app/layouts/rightbar/MarketFreshLines.d.ts +5 -0
  28. package/dist/types/app/layouts/rightbar/MarketView.d.ts +14 -0
  29. package/dist/types/app/layouts/rightbar/ModulesPage.d.ts +15 -0
  30. package/dist/types/app/layouts/rightbar/PanelModules.d.ts +14 -0
  31. package/dist/types/app/layouts/rightbar/Rightbar.d.ts +24 -0
  32. package/dist/types/app/layouts/rightbar/__tests__/board-mock.test.d.ts +1 -0
  33. package/dist/types/app/layouts/rightbar/__tests__/board-plan.test.d.ts +1 -0
  34. package/dist/types/app/layouts/rightbar/__tests__/board-selection.test.d.ts +1 -0
  35. package/dist/types/app/layouts/rightbar/__tests__/breadcrumbs.test.d.ts +1 -0
  36. package/dist/types/app/layouts/rightbar/__tests__/market-freshness.test.d.ts +1 -0
  37. package/dist/types/app/layouts/rightbar/__tests__/market-mock.test.d.ts +1 -0
  38. package/dist/types/app/layouts/rightbar/__tests__/market-rows-libs.test.d.ts +1 -0
  39. package/dist/types/app/layouts/rightbar/__tests__/market-rows.test.d.ts +1 -0
  40. package/dist/types/app/layouts/rightbar/__tests__/market-shared.test.d.ts +1 -0
  41. package/dist/types/app/layouts/rightbar/__tests__/panel-mock.test.d.ts +1 -0
  42. package/dist/types/app/layouts/rightbar/__tests__/suite-world.test.d.ts +1 -0
  43. package/dist/types/app/layouts/rightbar/__tests__/use-panels.test.d.ts +1 -0
  44. package/dist/types/app/layouts/rightbar/board-mock.d.ts +22 -0
  45. package/dist/types/app/layouts/rightbar/board-plan.d.ts +25 -0
  46. package/dist/types/app/layouts/rightbar/board-selection.d.ts +15 -0
  47. package/dist/types/app/layouts/rightbar/market-freshness.d.ts +13 -0
  48. package/dist/types/app/layouts/rightbar/market-mock.d.ts +29 -0
  49. package/dist/types/app/layouts/rightbar/market-rows.d.ts +45 -0
  50. package/dist/types/app/layouts/rightbar/panel-mock.d.ts +26 -0
  51. package/dist/types/app/layouts/rightbar/registry.d.ts +11 -0
  52. package/dist/types/app/layouts/rightbar/registry.test.d.ts +1 -0
  53. package/dist/types/app/layouts/rightbar/store.d.ts +23 -0
  54. package/dist/types/app/layouts/rightbar/store.test.d.ts +1 -0
  55. package/dist/types/app/layouts/rightbar/suite-world.d.ts +14 -0
  56. package/dist/types/app/layouts/rightbar/use-panels.d.ts +15 -0
  57. package/dist/types/app/layouts/sidebar/PanelNav.d.ts +13 -0
  58. package/dist/types/app/layouts/sidebar/ProjectSwitcher.d.ts +10 -0
  59. package/dist/types/app/layouts/sidebar/SessionList.d.ts +13 -0
  60. package/dist/types/app/layouts/sidebar/Sidebar.d.ts +25 -0
  61. package/dist/types/app/layouts/sidebar/WorkspaceSection.d.ts +7 -0
  62. package/dist/types/app/layouts/sidebar/__tests__/session-pick.test.d.ts +1 -0
  63. package/dist/types/app/layouts/sidebar/session-pick.d.ts +20 -0
  64. package/dist/types/app/units/host.d.ts +4 -0
  65. package/dist/types/app/units/index.d.ts +4 -0
  66. package/dist/types/app/units/registry.d.ts +12 -0
  67. package/dist/types/modules/panels/__tests__/config.test.d.ts +1 -0
  68. package/dist/types/modules/panels/__tests__/face.test.d.ts +1 -0
  69. package/dist/types/modules/panels/__tests__/market-dependency.test.d.ts +1 -0
  70. package/dist/types/modules/panels/__tests__/market-tools.test.d.ts +1 -0
  71. package/dist/types/modules/panels/__tests__/market.test.d.ts +1 -0
  72. package/dist/types/modules/panels/__tests__/prefs.test.d.ts +1 -0
  73. package/dist/types/modules/panels/__tests__/routes.test.d.ts +1 -0
  74. package/dist/types/modules/panels/__tests__/service.test.d.ts +1 -0
  75. package/dist/types/modules/panels/__tests__/tools.test.d.ts +1 -0
  76. package/dist/types/modules/panels/index.d.ts +2 -0
  77. package/dist/types/modules/panels/model/config.d.ts +18 -0
  78. package/dist/types/modules/panels/model/handoff.d.ts +8 -0
  79. package/dist/types/modules/panels/model/handoff.test.d.ts +1 -0
  80. package/dist/types/modules/panels/model/market-dependency.d.ts +41 -0
  81. package/dist/types/modules/panels/model/market-dispatch.d.ts +14 -0
  82. package/dist/types/modules/panels/model/market-tools.d.ts +3 -0
  83. package/dist/types/modules/panels/model/market.d.ts +49 -0
  84. package/dist/types/modules/panels/model/prefs.d.ts +13 -0
  85. package/dist/types/modules/panels/model/routes.d.ts +4 -0
  86. package/dist/types/modules/panels/model/routes.test.d.ts +1 -0
  87. package/dist/types/modules/panels/model/service.d.ts +25 -0
  88. package/dist/types/modules/panels/model/tools.d.ts +4 -0
  89. package/dist/types/modules/panels/unit.d.ts +4 -0
  90. package/dist/types/modules/sessions/index.d.ts +1 -0
  91. package/dist/types/modules/sessions/model/session-node.d.ts +25 -0
  92. package/dist/types/modules/sessions/model/session-node.test.d.ts +1 -0
  93. package/dist/types/shared/__tests__/i18n-keys.test.d.ts +1 -0
  94. package/dist/types/shared/api/__tests__/context-ref.test.d.ts +1 -0
  95. package/dist/types/shared/api/context-chips.d.ts +10 -0
  96. package/dist/types/shared/api/host/__tests__/capability-force.test.d.ts +1 -0
  97. package/dist/types/shared/api/host/__tests__/cli-error.test.d.ts +1 -0
  98. package/dist/types/shared/api/host/__tests__/context-inject.test.d.ts +1 -0
  99. package/dist/types/shared/api/host/__tests__/data-root.test.d.ts +1 -0
  100. package/dist/types/shared/api/host/__tests__/events.test.d.ts +1 -0
  101. package/dist/types/shared/api/host/__tests__/fact-writes.test.d.ts +1 -0
  102. package/dist/types/shared/api/host/__tests__/facts.test.d.ts +1 -0
  103. package/dist/types/shared/api/host/__tests__/long-task.test.d.ts +1 -0
  104. package/dist/types/shared/api/host/__tests__/plugin-source.test.d.ts +1 -0
  105. package/dist/types/shared/api/host/__tests__/preflight-fixtures.d.ts +19 -0
  106. package/dist/types/shared/api/host/__tests__/preflight-warm.test.d.ts +1 -0
  107. package/dist/types/shared/api/host/__tests__/preflight.test.d.ts +1 -0
  108. package/dist/types/shared/api/host/__tests__/probe-cache.test.d.ts +1 -0
  109. package/dist/types/shared/api/host/__tests__/probe-libs.test.d.ts +1 -0
  110. package/dist/types/shared/api/host/__tests__/probe-units-cache.test.d.ts +1 -0
  111. package/dist/types/shared/api/host/__tests__/probe-units-npm-source.test.d.ts +1 -0
  112. package/dist/types/shared/api/host/__tests__/probe-units-release.test.d.ts +1 -0
  113. package/dist/types/shared/api/host/__tests__/probe-units.test.d.ts +1 -0
  114. package/dist/types/shared/api/host/__tests__/probe-verdict.test.d.ts +1 -0
  115. package/dist/types/shared/api/host/__tests__/provision-dependency.test.d.ts +1 -0
  116. package/dist/types/shared/api/host/__tests__/release.test.d.ts +1 -0
  117. package/dist/types/shared/api/host/__tests__/tool-approval.test.d.ts +1 -0
  118. package/dist/types/shared/api/host/__tests__/tool-kit.test.d.ts +1 -0
  119. package/dist/types/shared/api/host/__tests__/tool-scope.test.d.ts +1 -0
  120. package/dist/types/shared/api/host/__tests__/tool-surface.test.d.ts +1 -0
  121. package/dist/types/shared/api/host/__tests__/unit-face.test.d.ts +1 -0
  122. package/dist/types/shared/api/host/__tests__/unit-readiness.test.d.ts +1 -0
  123. package/dist/types/shared/api/host/__tests__/unit-registry.test.d.ts +1 -0
  124. package/dist/types/shared/api/host/__tests__/value-handoff.test.d.ts +1 -0
  125. package/dist/types/shared/api/host/__tests__/version-range.test.d.ts +1 -0
  126. package/dist/types/shared/api/host/__tests__/workspace-store.test.d.ts +1 -0
  127. package/dist/types/shared/api/host/__tests__/write-result.test.d.ts +1 -0
  128. package/dist/types/shared/api/host/context-inject.d.ts +24 -0
  129. package/dist/types/shared/api/host/events.d.ts +9 -0
  130. package/dist/types/shared/api/host/git.test.d.ts +1 -0
  131. package/dist/types/shared/api/host/http.test.d.ts +1 -0
  132. package/dist/types/shared/api/host/mount-units.d.ts +52 -0
  133. package/dist/types/shared/api/host/opencli.test.d.ts +1 -0
  134. package/dist/types/shared/api/host/preflight.d.ts +58 -0
  135. package/dist/types/shared/api/host/probe-cache.d.ts +6 -0
  136. package/dist/types/shared/api/host/probe-libs.d.ts +11 -0
  137. package/dist/types/shared/api/host/probe-units.d.ts +17 -0
  138. package/dist/types/shared/api/host/probe-verdict.d.ts +7 -0
  139. package/dist/types/shared/api/host/release.d.ts +18 -0
  140. package/dist/types/shared/api/host/shell.test.d.ts +1 -0
  141. package/dist/types/shared/api/host/tool-scope.d.ts +32 -0
  142. package/dist/types/shared/api/host/unit-readiness.d.ts +7 -0
  143. package/dist/types/shared/api/host/unit-registry.d.ts +9 -0
  144. package/dist/types/shared/api/host/version-range.d.ts +2 -0
  145. package/dist/types/shared/components/BlockerState.d.ts +11 -0
  146. package/dist/types/shared/hooks/__tests__/mock-mode.test.d.ts +1 -0
  147. package/dist/types/shared/hooks/__tests__/use-api-resource.test.d.ts +1 -0
  148. package/dist/types/shared/hooks/__tests__/use-capabilities.test.d.ts +1 -0
  149. package/dist/types/shared/hooks/__tests__/use-visible-busy.test.d.ts +1 -0
  150. package/dist/types/shared/hooks/fact-transports.d.ts +2 -0
  151. package/dist/types/shared/hooks/mock-mode.d.ts +1 -0
  152. package/dist/types/shared/hooks/use-api-resource.d.ts +18 -0
  153. package/dist/types/shared/hooks/use-blocker-actions.d.ts +11 -0
  154. package/dist/types/shared/hooks/use-capabilities.d.ts +25 -0
  155. package/dist/types/shared/hooks/use-capability-gate.d.ts +20 -0
  156. package/dist/types/shared/hooks/use-card-summary.d.ts +8 -0
  157. package/dist/types/shared/hooks/use-facts.d.ts +2 -0
  158. package/dist/types/shared/hooks/use-fixture-pick.d.ts +20 -0
  159. package/dist/types/shared/hooks/use-host-write.d.ts +17 -0
  160. package/dist/types/shared/hooks/use-long-task.d.ts +14 -0
  161. package/dist/types/shared/hooks/use-unit-board.d.ts +32 -0
  162. package/dist/types/shared/hooks/use-visible-busy.d.ts +8 -0
  163. package/docs/HANDOFF.md +207 -0
  164. package/docs/OPERATIONS.md +109 -0
  165. package/icon.svg +29 -0
  166. package/package.json +97 -0
  167. package/profile/isolation.patch.yml +35 -0
  168. package/profile/sandbox.patch.yml +26 -0
  169. package/scripts/seed-profile.sh +199 -0
@@ -0,0 +1,17 @@
1
+ import type { DshShell } from '@dsh-workbench/shared';
2
+ import type { UnitCapability, UnitCommands } from "@dsh-workbench/shared/host";
3
+ export type { UnitCapability, UnitCommands, UnitLibState, UnitPluginState } from "@dsh-workbench/shared/host";
4
+ export declare function parsePluginList(stdout: string): unknown[] | undefined;
5
+ export declare function probeUnitCommands(options: {
6
+ shell: DshShell;
7
+ command?: string;
8
+ unit: UnitCommands;
9
+ force?: boolean;
10
+ }): Promise<UnitCapability>;
11
+ export declare function probeUnits(options: {
12
+ shell: DshShell;
13
+ command?: string;
14
+ units: readonly UnitCommands[];
15
+ force?: boolean;
16
+ }): Promise<UnitCapability[]>;
17
+ export declare const resetProbeCache: () => void;
@@ -0,0 +1,7 @@
1
+ import type { PluginRelease, UnitPluginNeed } from '@dsh-workbench/shared';
2
+ import type { UnitCapability, UnitCommands, UnitLibState } from "./probe-libs";
3
+ export declare function evaluateUnit(list: readonly unknown[] | undefined, unit: UnitCommands, failure: string | undefined, command?: string, releases?: ReadonlyMap<string, PluginRelease>, libs?: readonly UnitLibState[]): UnitCapability;
4
+ export declare function gapsText(gaps: readonly string[]): string;
5
+ export declare function pluginUpdateCommand(plugin: string | undefined): string | undefined;
6
+ export declare function pluginInstallCommand(plugin: string | undefined): string;
7
+ export declare function upgradeLine(need: UnitPluginNeed, command: string, latest: string): string | undefined;
@@ -0,0 +1,18 @@
1
+ import type { DshShell } from '@dsh-workbench/shared';
2
+ import type { PluginRelease } from '@dsh-workbench/shared';
3
+ export declare function parseLedgerVersion(stdout: string, name: string): string | undefined;
4
+ export declare function compareVersions(a: string, b: string): number | undefined;
5
+ export declare function isNewerVersion(latest: string, installed: string): boolean;
6
+ export declare function latestVersion(options: {
7
+ shell: DshShell;
8
+ name: string;
9
+ registry: string;
10
+ force?: boolean;
11
+ }): Promise<string | undefined>;
12
+ export declare function probeRelease(options: {
13
+ shell: DshShell;
14
+ package: string;
15
+ registry?: string | undefined;
16
+ force?: boolean;
17
+ }): Promise<PluginRelease | undefined>;
18
+ export declare function resetReleaseCache(): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import type { DshPromptAssembly } from '@dsh-workbench/shared';
2
+ export interface ScopedToolRegistry {
3
+ restrict(filter: {
4
+ readonly allow?: readonly string[];
5
+ readonly deny?: readonly string[];
6
+ }): () => void;
7
+ }
8
+ export interface ToolScopeAgent {
9
+ readonly ctx?: {
10
+ readonly tools?: ScopedToolRegistry | undefined;
11
+ get?<T = unknown>(name: string): T | undefined;
12
+ } | undefined;
13
+ readonly session?: {
14
+ readonly header?: {
15
+ readonly cwd?: string;
16
+ } | undefined;
17
+ } | undefined;
18
+ }
19
+ export interface ToolScopeOptions {
20
+ deniedTools(workspace: string | null): readonly string[];
21
+ logger?: {
22
+ info?(message: string): void;
23
+ warn?(message: string): void;
24
+ } | undefined;
25
+ }
26
+ export interface ToolScope {
27
+ apply(agent: unknown): void;
28
+ narrow(assembly: DshPromptAssembly, agent: unknown): DshPromptAssembly;
29
+ reapply(): void;
30
+ disposeAll(): void;
31
+ }
32
+ export declare function createToolScope(options: ToolScopeOptions): ToolScope;
@@ -0,0 +1,7 @@
1
+ import type { UnitManifest } from '@dsh-workbench/shared';
2
+ export interface UnitReadiness {
3
+ readonly canMount: boolean;
4
+ readonly missingHard: readonly string[];
5
+ readonly missingSoft: readonly string[];
6
+ }
7
+ export declare function unitReadiness(manifest: UnitManifest, lookup: (name: string) => unknown): UnitReadiness;
@@ -0,0 +1,9 @@
1
+ import type { UnitManifest } from '@dsh-workbench/shared';
2
+ import type { UnitHostFace } from '@dsh-workbench/shared/host';
3
+ export interface UnitRegistry {
4
+ register(face: UnitHostFace): () => void;
5
+ all(): readonly UnitHostFace[];
6
+ manifests(): readonly UnitManifest[];
7
+ subscribe(listener: () => void): () => void;
8
+ }
9
+ export declare function createUnitRegistry(): UnitRegistry;
@@ -0,0 +1,2 @@
1
+ export type VersionVerdict = 'satisfied' | 'unsatisfied' | 'malformed';
2
+ export declare function checkVersionRange(contract: string | undefined, version: string): VersionVerdict;
@@ -0,0 +1,11 @@
1
+ import type { ModuleBlocker } from '@dsh-workbench/shared';
2
+ import type { Translate } from '@dsh-workbench/shared';
3
+ export interface BlockerStateProps {
4
+ readonly blocker: ModuleBlocker;
5
+ readonly t: Translate;
6
+ readonly onRetry: () => void;
7
+ readonly busy?: boolean;
8
+ readonly flush?: boolean;
9
+ readonly onOpenMarket?: () => void;
10
+ }
11
+ export declare function BlockerState({ blocker, t, onRetry, busy, flush, onOpenMarket, }: BlockerStateProps): JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type FactTransport } from '@dsh-workbench/shared';
2
+ export declare function createAdaptiveTransport(): FactTransport;
@@ -0,0 +1 @@
1
+ export declare function mockModeOn(search: string): boolean;
@@ -0,0 +1,18 @@
1
+ import type { ApiError, ApiResponse, Query } from '@dsh-workbench/shared';
2
+ export interface ResourceState<T> {
3
+ readonly phase: 'idle' | 'loading' | 'ready' | 'error';
4
+ readonly value?: T;
5
+ readonly error?: ApiError;
6
+ readonly at?: number;
7
+ }
8
+ export declare function resourceIdentity(route: string, query: Query, override?: string | undefined): string;
9
+ export declare function readKeyOf(identity: string, refreshToken: number, factRev: number): string;
10
+ export declare function requestResource<T>(route: string, query: Query, key: string): {
11
+ task: Promise<ApiResponse<T>>;
12
+ release: () => void;
13
+ };
14
+ export declare function useApiResource<T>(route: string, query: Query, refreshToken: number, options?: {
15
+ enabled?: boolean;
16
+ key?: string;
17
+ fact?: string;
18
+ }): ResourceState<T>;
@@ -0,0 +1,11 @@
1
+ import type { BlockerAction } from '@dsh-workbench/shared';
2
+ export interface BlockerActions {
3
+ run(action: BlockerAction): void;
4
+ copied: string | null;
5
+ running: string | null;
6
+ failure: {
7
+ message: string;
8
+ detail?: string;
9
+ } | null;
10
+ }
11
+ export declare function useBlockerActions(onRetry: () => void, onOpenMarket?: () => void): BlockerActions;
@@ -0,0 +1,25 @@
1
+ import type { CapabilityPayload } from '@dsh-workbench/shared';
2
+ export declare function shouldForce(refreshToken: number, previousToken: number): boolean;
3
+ export type CapabilityMap = Record<string, CapabilityPayload>;
4
+ export interface CapabilityProbeState {
5
+ readonly checkedAt: number | null;
6
+ readonly checking: boolean;
7
+ }
8
+ export declare function markNextProbeSilent(): void;
9
+ export declare function probeValue(): CapabilityProbeState;
10
+ export declare function subscribeProbe(listener: () => void): () => void;
11
+ export declare const CAPABILITY_FACT = "capability";
12
+ export declare function capabilityValue(): CapabilityMap | null;
13
+ export declare function subscribeCapabilities(listener: () => void): () => void;
14
+ export declare function setCapabilities(next: CapabilityMap | null, rechecked?: boolean): void;
15
+ export declare function beginProbe(): void;
16
+ export declare function endProbe(): void;
17
+ export declare function publishCapability(result: CapabilityMap | null, rechecked?: boolean): boolean;
18
+ export declare function shareCapabilityFetch(force: boolean, load: (force: boolean) => Promise<CapabilityMap | null>): Promise<CapabilityMap | null>;
19
+ export declare function runCapabilityRead(options: {
20
+ readonly force: boolean;
21
+ readonly silent?: boolean;
22
+ readonly load?: (force: boolean) => Promise<CapabilityMap | null>;
23
+ }): Promise<void>;
24
+ export declare function useCapabilities(refreshToken: number, reloadToken?: number): CapabilityMap | null;
25
+ export declare function useCapabilityProbe(): CapabilityProbeState;
@@ -0,0 +1,20 @@
1
+ import type { CapabilityPayload } from '@dsh-workbench/shared';
2
+ import { type GateMode } from '@dsh-workbench/shared';
3
+ import { type CapabilityMap } from './use-capabilities';
4
+ export interface CapabilityGate {
5
+ readonly capability: CapabilityPayload | null;
6
+ readonly gated: boolean;
7
+ forceProbe(): void;
8
+ }
9
+ export declare function useCapabilityGate(unit: string, refreshToken: number, options?: {
10
+ readonly mode?: GateMode | undefined;
11
+ readonly fixture?: CapabilityPayload | undefined;
12
+ }): CapabilityGate;
13
+ export interface CapabilityMapView {
14
+ readonly capabilities: CapabilityMap | null;
15
+ readonly checkedAt: number | null;
16
+ readonly checking: boolean;
17
+ forceProbe(): void;
18
+ reload(): void;
19
+ }
20
+ export declare function useCapabilityMap(refreshToken: number): CapabilityMapView;
@@ -0,0 +1,8 @@
1
+ export interface CardSummary {
2
+ readonly refreshing: boolean;
3
+ readonly at: number | null;
4
+ }
5
+ export declare function useCardSummary(onSummary: (summary: CardSummary) => void, summary: {
6
+ readonly refreshing: boolean;
7
+ readonly at: number | null | undefined;
8
+ }): void;
@@ -0,0 +1,2 @@
1
+ export declare function useFactRevision(fact: string): number;
2
+ export declare function useFactInvalidate(): (fact?: string) => void;
@@ -0,0 +1,20 @@
1
+ export interface PickableFixture {
2
+ readonly id: string;
3
+ readonly label: string;
4
+ }
5
+ export interface FixturePickOptions<T extends PickableFixture> {
6
+ readonly fixtures: readonly T[];
7
+ readonly selected?: string | null | undefined;
8
+ readonly onSelect?: ((id: string | null) => void) | undefined;
9
+ readonly enabled?: boolean | undefined;
10
+ readonly initial?: string | null | undefined;
11
+ }
12
+ export interface FixturePick<T extends PickableFixture> {
13
+ readonly enabled: boolean;
14
+ readonly active: string | null;
15
+ readonly current: T | undefined;
16
+ pick(id: string | null): void;
17
+ readonly nonce: number;
18
+ readonly index: number;
19
+ }
20
+ export declare function useFixturePick<T extends PickableFixture>(options: FixturePickOptions<T>): FixturePick<T>;
@@ -0,0 +1,17 @@
1
+ import { type WriteChannel, type WriteOutcome } from '@dsh-workbench/shared';
2
+ export interface HostWrite<T> {
3
+ readonly busy: boolean;
4
+ readonly failed: string | null;
5
+ readonly detail: string | null;
6
+ send(body: Record<string, unknown>, options?: {
7
+ readonly silent?: boolean;
8
+ }): Promise<WriteOutcome<T>>;
9
+ fail(message: string, detail?: string): void;
10
+ clear(): void;
11
+ }
12
+ export interface HostWriteOptions {
13
+ readonly channel: WriteChannel;
14
+ readonly resource?: string | undefined;
15
+ readonly networkText: string;
16
+ }
17
+ export declare function useHostWrite<T>(options: HostWriteOptions): HostWrite<T>;
@@ -0,0 +1,14 @@
1
+ import { type LongTaskStatus } from '@dsh-workbench/shared';
2
+ export interface LongTaskFeed<T> {
3
+ readonly runs: readonly T[];
4
+ }
5
+ export declare function useLongTask<T extends {
6
+ readonly id: string;
7
+ readonly status: LongTaskStatus;
8
+ }>(options: {
9
+ readonly unit: string;
10
+ readonly workspace: string | null;
11
+ readonly mocked?: boolean | undefined;
12
+ readonly enabled?: boolean | undefined;
13
+ readonly pollMs?: number | undefined;
14
+ }): LongTaskFeed<T>;
@@ -0,0 +1,32 @@
1
+ import type { Translate } from '@dsh-workbench/shared';
2
+ import type { ModuleBlocker } from '@dsh-workbench/shared';
3
+ import { type UnitBlockers } from '@dsh-workbench/shared';
4
+ import type { CapabilityPayload } from '@dsh-workbench/shared';
5
+ import type { ApiError } from '@dsh-workbench/shared';
6
+ import type { ResourceState } from './use-api-resource';
7
+ import type { CapabilityGate } from './use-capability-gate';
8
+ import { type CardSummary } from './use-card-summary';
9
+ export interface CardFeed extends CardSummary {
10
+ readonly state: ResourceState<unknown>;
11
+ readonly settling?: boolean | undefined;
12
+ }
13
+ export interface UnitBoardOptions<TFeed extends CardFeed> {
14
+ readonly unit: string;
15
+ readonly gate: CapabilityGate;
16
+ readonly refreshToken: number;
17
+ readonly onRefresh: () => void;
18
+ readonly onSummary: (summary: CardSummary) => void;
19
+ readonly read: (gated: boolean) => TFeed;
20
+ readonly t: Translate;
21
+ readonly enabled?: boolean | undefined;
22
+ readonly platformBlocker?: ((error: ApiError, t: Translate) => ModuleBlocker | undefined) | undefined;
23
+ }
24
+ export interface UnitBoard<TFeed extends CardFeed> extends UnitBlockers {
25
+ readonly feed: TFeed;
26
+ readonly capability: CapabilityPayload | null;
27
+ readonly gated: boolean;
28
+ readonly refresh: () => void;
29
+ readonly forceProbe: () => void;
30
+ readonly summary: CardSummary;
31
+ }
32
+ export declare function useUnitBoard<TFeed extends CardFeed>(options: UnitBoardOptions<TFeed>): UnitBoard<TFeed>;
@@ -0,0 +1,8 @@
1
+ export type HeldPhase = 'idle' | 'loading' | 'ready' | 'error';
2
+ export interface BusyView {
3
+ readonly busy: boolean;
4
+ readonly settling: boolean;
5
+ }
6
+ export declare function heldPhase(phase: HeldPhase, refreshing: boolean): HeldPhase;
7
+ export declare function busyView(active: boolean, visible: boolean): BusyView;
8
+ export declare function useVisibleBusy(active: boolean, floorMs: number): BusyView;
@@ -0,0 +1,207 @@
1
+ # 把工作台给别人用
2
+
3
+ 工作台是一个 **dsh 插件包**,发在内网源上(地址只写在 `package.json` 的
4
+ `publishConfig.registry` 一处)。别人装的是**独立 profile**(`workbench`)——
5
+ 他自己那份 `dsh web` 一行不改,两边可以同时开着。
6
+
7
+ 一句话:**登录内网源 → `setup` → 起**。
8
+
9
+ ## 一、对方机器上要有的
10
+
11
+ | # | 东西 | 为什么 | 怎么有 |
12
+ | --- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
13
+ | 1 | `dsh` | 本体。本仓库在 `0.1.7-rc.2` 上验过 | `npm i -g @deepseek-ai/dsh`(公网 npm 上有) |
14
+ | 2 | `node` + **`pnpm`** | dsh 的 `plugin` 子命令是**转发给 pnpm** 的;没有 pnpm 会直接报 `dsh: pnpm was not found` | 各自装 |
15
+ | 3 | 跑过一次 `dsh web` | `setup` 拿 `~/.dsh/profiles/web` 当模板(bundle 清单 + 设置快照 + patch 点名的依赖)。没有它会**明确报错**,不会静默建出半个 profile | `dsh web` |
16
+ | 4 | 内网源登录 | 包带 `@pagoda` scope,公网上没有它 | 见第二节 |
17
+ | 5 | Coding 账号 + opencli 登录态 | 面板的数据来自 opencli 的 coding 插件 | 见第四节 |
18
+
19
+ ## 二、两步装
20
+
21
+ ```bash
22
+ # ① 让 pnpm/npx 知道 @pagoda 这个 scope 在哪个源,并登录(各一次)
23
+ npm config set @pagoda:registry "$NPM_REGISTRY"
24
+ npm login --registry=$NPM_REGISTRY
25
+
26
+ # ② 建 profile + 把工作台装进去,然后起(占 :3081,dsh web 继续用 :3080)
27
+ npx --yes @dsh-workbench/core@latest setup
28
+ npx @dsh-workbench/core
29
+ ```
30
+
31
+ **为什么必须有 `@pagoda:registry` 那一行**:不带 scope 的 `dsh-workbench`
32
+ 公网上已经有别人的一个(`lee259/dsh-workbench`),所以我们的包必须带 scope;
33
+ 而带 scope 的包,pnpm / npx 只会去 `.npmrc` 里**给这个 scope 指的那个源**找它 ——
34
+ 不设这一行,症状是 `404 Not Found - GET https://registry.npmjs.org/@pagoda%2fdsh-workbench`。
35
+
36
+ ### 设置从哪来:**两边都要的在 `dsh web` 里配**(单向、实时)
37
+
38
+ 工作台启动时会用 `--patch` 把 `profiles/web/cordis.patch.yml` 叠在自己的差异层之上
39
+ (`bin/dsh-workbench` 里就在做这件事)。所以:
40
+
41
+ - **模型供应商 / API key / 搜索源 / MCP** 这类"机器级、两边都要"的设置 ⇒ **在 `:3080` 的 Settings 里配一次**,工作台自动同源;
42
+ - **只有工作台要的**(面板显示哪些模块、会话隔离)⇒ 在 `:3081` 里配,落在它自己的 profile patch,web 看不到;
43
+ - 被 web 声明过的行,在工作台的 Settings 页里会显示 `overridden by a home patch or command-line overlay` —— 那是"这行由 web 管"的**提示**,不是故障。
44
+
45
+ (这是 `--patch` 单向叠加,不是双向同步:工作台里配的**不会**流回 web。)
46
+
47
+ ### 一条"看起来坏了、其实没坏"的情况
48
+
49
+ Settings → 插件里把 `@dsh-workbench/core` 关掉后,工作台会**退化成原版 dsh web**:
50
+ 左栏回到出厂样式、右侧数据面板消失。这是**设计内的降级**(关掉 = 它那份 bundle patch
51
+ 整份不再应用,连里面的"禁用出厂左栏"也一并失效),**会话 / 面板配置 / 工作区数据都不丢**。
52
+ 恢复:插件页把开关拨回来(或按 `Meta+,` 打开设置)。别把它当成故障去重建 profile。
53
+
54
+ 顺带一条:即使关掉工作台,profile 里剩下那层差异(会话/存储/附件隔离)**仍然生效**
55
+ —— 那是无害且想要的(数据继续分开)。另外启动器每次都会叠一层 `sandbox-policy`,把执行缝的
56
+ 可写根指到 `$DSH_HOME/workbench`(`profile/sandbox.patch.yml`)—— 少了它,工作台自己跑 npm
57
+ (自动装 opencli、市场装插件)会被沙箱拒写。它在 Settings 里显示为
58
+ `overridden by a ... command-line overlay`,**属正常**。
59
+
60
+ ### `setup` 到底做了什么(幂等,可以重复跑)
61
+
62
+ 1. 拿 `~/.dsh/profiles/web` 当模板建 `workbench` profile;
63
+ 2. 继承 web 的 bundle 清单 + 设置快照 + patch 点名的依赖;
64
+ 3. 追加**数据隔离层**(会话 / 存储 / 附件各走 `~/.dsh/workbench/**`);
65
+ 4. `dsh plugin --profile workbench add @dsh-workbench/core` —— 走 **dsh 自己的**
66
+ 安装通路(兼容性预检、profile 写锁、装完自动把 bundle 登记进 profile 清单)。
67
+
68
+ 继承是**一次性快照**,不是订阅:web 之后改设置不会流过来(这正是"互不影响"的实现方式),
69
+ 要重新同步得重跑 `setup`。有意共享的只有两样:**API 凭据**(`~/.dsh/.credentials.yaml`,
70
+ 一机一把 key)和 `$DSH_HOME/cordis.patch.yml`(机器公共层,对所有 profile 实时生效)。
71
+
72
+ ## 三、不想用 npx?仓库副本也可以
73
+
74
+ `bin/dsh-workbench` 认得出自己是不是仓库副本(看有没有 `.git`):
75
+
76
+ ```bash
77
+ git clone <仓库> && cd dsh-workbench && pnpm install
78
+ ./bin/dsh-workbench setup # 仓库副本:以 link 方式装本目录,改源码立刻生效
79
+ ./bin/dsh-workbench
80
+ ```
81
+
82
+ 两种身份装出来的东西**同一个包名**,所以对方以后换成正装也不会打架。
83
+
84
+ ## 四、数据通路(面板要有数据)
85
+
86
+ 1. **opencli 隔离运行时**。第一次打开工作台,启动体检会**自己装**它(`@jackwener/opencli`,
87
+ 公网包),落在 `$DSH_HOME/workbench/opencli/toolchain/`,**与对方本机的 `opencli` 完全隔离**
88
+ —— 插件目录靠改 `HOME` 分开(见 `bin/opencli-isolated` 开头),所以这里装/卸插件
89
+ 不碰他本机的 `~/.opencli`。
90
+
91
+ 装不上时(没网、源不通)失败态里有一条能照抄到终端的命令。**那条得在终端跑**:工作台
92
+ 自己发起的命令走 dsh 的执行缝、带文件沙箱,终端没有 —— 沙箱放行的可写根由启动器声明
93
+ (`profile/sandbox.patch.yml` → `$DSH_HOME/workbench`),少了它"自己装"会死在 EPERM。
94
+
95
+ 2. **交叉依赖**(opencli 生态的 npm 包,例如 cmdb 发布流程用的 `opencli-workflow`)。
96
+ 它**不是 opencli 插件**(不在 `plugin list` 里、没有子命令),所以单独一类:
97
+ 装在**共享**的 `$DSH_HOME/workbench/opencli/libs/`(多模块声明同一份只装一次),
98
+ 版本与源由**用到它的模块**在清单里声明(`env.libs`),工作台据此在体检/市场里说"缺了 / 有新版",
99
+ 并给一条能照抄的命令:
100
+
101
+ ```bash
102
+ npm i --prefix "$HOME/.dsh/workbench/opencli/libs" opencli-workflow@latest \
103
+ --registry "$NPM_REGISTRY" \
104
+ --cache "$HOME/.dsh/workbench/opencli/npm-cache"
105
+ ```
106
+
107
+ 缺它的时候,发布那条流程(`workbench_cmdb_release`)会**明说**"还没装 + 上面这条命令",
108
+ 而不是抛一句 `Cannot find module`。理由与边界见
109
+ [ADR-0015](../../../docs/adr/0015-cross-dependency-vs-opencli-plugin.md)。
110
+
111
+ ⚠️ 清单里那个 `version` 是**版本契约**(`^1.1.23` 这种范围;留空 = 任意版本都满足):
112
+ 共享件只装一份,"装了但不是这个模块声明要的那一支"同样算**没装好**——
113
+ 模块在市场上报「未装」、面板上也不画那张卡(见
114
+ [ADR-0016](../../../docs/adr/0016-installed-means-usable.md))。
115
+
116
+ 3. **登录 Coding**。面板报"未登录"时,卡片上有「去登录 ↗」——在新标签里登完**切回来按
117
+ 卡片上那颗「重试」**(刻意不做自动重试:登录什么时候完成我们不知道)。若仍是未登录,
118
+ 就在终端用**隔离运行时那条**跑一次:
119
+
120
+ ```bash
121
+ $HOME/.dsh/workbench/opencli/bin/opencli coding team-current
122
+ ```
123
+
124
+ ⚠️ 是**这条**,不是 `which opencli` 那条 —— 两条的插件目录不同,登错了面板还是空的。
125
+
126
+ 4. **coding 插件**(私有包,在内网源上)。**不用配任何东西**:那个源由模块自己带着
127
+ (清单里的 `installSource.registryDefault`),市场页那一行直接给「安装」。
128
+
129
+ 只有插件**不在默认那个源**上时,才用环境变量覆盖:
130
+
131
+ ```bash
132
+ export DSH_WORKBENCH_CODING_REGISTRY=<那个源>
133
+ ```
134
+
135
+ 装的时候 `bin/opencli-isolated` 会把 `~/.npmrc` 指回去,就为了拿到源的 token;
136
+ npm 缓存则一律落在那台机器自己的隔离运行时里(**不碰** `~/.npm`)。
137
+
138
+ ## 五、不用管的
139
+
140
+ - **`dsh web` 一行不改**:`./scripts/verify.sh` 是外部证据 —— 比对 web 的 profile 哈希、
141
+ 两个 profile 的组合树差异、数据是否分家,而不是自证。
142
+ - 会话 / 存储 / 附件各自独立;工作台是**另一个端口、另一个进程**,可与 `dsh web` 同开。
143
+ - 改工作台设置只落在 `profiles/workbench/cordis.patch.yml`。
144
+
145
+ ## 六、发版(发起人)
146
+
147
+ ```bash
148
+ cd <仓库根>
149
+ pnpm release:dry # 门禁 + 发布计划 + 打包清单校验,不联网、不真发
150
+ pnpm release # 真发(发前会再跑一次门禁)
151
+ ```
152
+
153
+ 提版本号之后再发:`npm version patch`。脚本把三个坑写成了**硬失败**:
154
+
155
+ - **`dist/` 在 `.gitignore` 里**:npm 打包的兜底就是 `.gitignore`,不在 `files` 里显式列出
156
+ 就会漏掉构建产物 —— 症状不是报错,而是"别人装上后宿主侧起不来";
157
+ - 版本号在源上已被占(pnpm 到最后一步才报,白等一轮门禁);
158
+ - `private: true` 没摘。
159
+
160
+ ## 七、版本与兼容(实话)
161
+
162
+ `package.json` 的 `peerDependencies` 里**没有** `@deepseek-ai/dsh`,这是有意的:
163
+ dsh 目前是 `0.1.7-rc.2`(预发布),声明范围会让版本不一致的人**装不上**;不声明则是
164
+ "谁都能装,坏了当场知道"。
165
+
166
+ 代价要清楚:工作台依赖 dsh 的两个注入面(`@deepseek-ai/dsh-client-ui-slots`、
167
+ `@deepseek-ai/dsh-client-locale`,见 `dsh.client.inject`),这两个面在 dsh 升级时会动。
168
+ 对方的 dsh 版本和上面验过的差得远时,**先在一台上验一次**再铺开。
169
+
170
+ 想改成"装的时候就拦住",在 `package.json` 里声明:
171
+
172
+ ```json
173
+ "peerDependencies": { "@deepseek-ai/dsh": "^0.1.7" }
174
+ ```
175
+
176
+ dsh 会用这条做**安装预检**(`evaluatePluginCompatibility`),不满足就拒绝安装,
177
+ 可用 `dsh plugin --profile workbench allow-version <包@版本> --dsh-version <版本> --accept-risk` 豁免。
178
+
179
+ > `NPM_REGISTRY` 是**你们自己的**内网 npm 源地址(形如 `https://<源主机>/<路径>/`)。
180
+ > 本仓不写它:开源包里不该出现任何内网地址。
181
+
182
+ ## 发布(一个脚本,一条命令)
183
+
184
+ ```bash
185
+ pnpm release # 门禁 → 计划 → 逐包:登录 → 版本没被占 → 打包清单 → 发
186
+ pnpm release:dry # 不联网:门禁 → 计划 → 打包清单校验
187
+ ```
188
+
189
+ `scripts/release.mjs` 是**唯一**的发布入口(曾经的 `publish.sh` + `release-plan.mjs` 已合并进来)。
190
+ 谁要发、发到哪、要发什么,全部从各包自述清单读,不写死名单:
191
+
192
+ - **发不发** = 有 `publishConfig.registry`,或 `publishConfig.access === 'public'`(默认 npmjs);`private: true` 一律不发;
193
+ - **发什么** = 包自己声明的 `exports` / `bin` / `dsh.bundle.patch` 那几份文件必须在(`dist/`、`client.js` 在 `.gitignore` 里,漏了 `files` 的症状是"装上后宿主侧起不来",不是报错);
194
+ - **什么顺序** = 依赖优先(`shared` → `core` → 用到它们的模块);依赖的包这次不发就报错;
195
+ - **公网包**用 `--access public`,内网源不加这个参数。
196
+
197
+ 它替你把四件事变成硬失败:**门禁绿 · 计划成立 · 打包清单真的有产物 · 这个版本号在源上还没人占**。
198
+ 发布用 `pnpm publish`(不是 `npm publish`):只有 pnpm 会把 `workspace:*` 改写成真实版本,
199
+ `npm pack` / `npm publish` 不会 —— 直接发别人装不上。
200
+
201
+ ### 版本同步规则(脚本会替你拦)
202
+
203
+ | 关系 | 规则 |
204
+ | --- | --- |
205
+ | 模块 ↔ 外壳 | 模块 `peerDependencies['@dsh-workbench/core'] = ^0.1.0`。**`^0.x` 锁次版本**:外壳升到 `0.2.0` 时 `^0.1.0` 不再满足,脚本当场报 —— 要把每个模块的范围一起改(或一起升) |
206
+ | `shared` ↔ 外壳/模块 | 依赖写的是 `workspace:*`,发布时会被改写成**当时那个版本号** → `shared` 必须先发;`shared` 升版后,用到它的包要重新发一次才会带上新范围 |
207
+ | `module-tools` ↔ `init` 模板 | 模板里 `@dsh-workbench/core` / `module-tools` 的范围要跟着本仓版本走,否则 `init` 生成的是过期范围(脚本已检查这一条) |