@dsh-workbench/core 1.1.14-beta.2 → 1.1.14

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 (40) hide show
  1. package/README.md +1 -1
  2. package/bin/dsh-workbench +6 -52
  3. package/client.js +59 -16
  4. package/cordis.patch.yml +5 -6
  5. package/dist/client/client.body.js +59 -16
  6. package/dist/client/index.js +59 -16
  7. package/dist/host/index.js +1665 -623
  8. package/dist/types/app/layouts/env-gate/gate-text.d.ts +2 -3
  9. package/dist/types/app/units/manifests.d.ts +2 -0
  10. package/dist/types/kernel/host/mount-units.d.ts +2 -1
  11. package/dist/types/modules/panels/model/diy-browser-act.d.ts +3 -3
  12. package/dist/types/modules/panels/model/diy-browser-clip.d.ts +14 -0
  13. package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -14
  14. package/dist/types/modules/panels/model/diy-browser.d.ts +6 -6
  15. package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
  16. package/dist/types/modules/panels/model/diy-record-open.d.ts +2 -2
  17. package/dist/types/modules/panels/model/diy-record.d.ts +12 -11
  18. package/dist/types/modules/panels/model/market-discover.d.ts +2 -2
  19. package/dist/types/modules/panels/model/market-follow-up.d.ts +2 -1
  20. package/dist/types/modules/panels/model/market-route.d.ts +2 -3
  21. package/dist/types/modules/panels/model/market-update-bundle.d.ts +2 -3
  22. package/dist/types/modules/panels/model/market-workbench-update.d.ts +2 -2
  23. package/dist/types/modules/panels/model/market.d.ts +2 -2
  24. package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -3
  25. package/dist/types/modules/panels/model/plugin-section.d.ts +3 -4
  26. package/dist/types/shared/api/host/bundle-routes.d.ts +3 -3
  27. package/dist/types/shared/api/host/diy-artifacts.d.ts +1 -2
  28. package/dist/types/shared/api/host/diy-components.d.ts +2 -2
  29. package/dist/types/shared/api/host/diy-release-staging.d.ts +6 -0
  30. package/dist/types/shared/api/host/diy.d.ts +1 -1
  31. package/dist/types/shared/api/host/git-exec.d.ts +1 -1
  32. package/dist/types/shared/api/host/git-process.d.ts +3 -0
  33. package/dist/types/shared/api/host/shipped-adapter-set.d.ts +4 -0
  34. package/dist/types/shared/api/host/workbench-seam.d.ts +3 -0
  35. package/package.json +3 -6
  36. package/scripts/seed-profile.sh +3 -3
  37. package/scripts/share-data-root.mjs +10 -13
  38. package/skills/workbench-module-diy/BUILD.md +38 -0
  39. package/skills/workbench-module-diy/SKILL.md +1 -0
  40. package/profile/sandbox.patch.yml +0 -28
@@ -6,11 +6,9 @@ export interface GuidanceText {
6
6
  readonly why: string;
7
7
  readonly step: string;
8
8
  }
9
- export declare function guidanceTextOf(guidance: PreflightGuidance | undefined, t: Translate): GuidanceText;
9
+ export declare function guidanceTextOf(guidance: PreflightGuidance | undefined, t: Translate, hasCommand?: boolean): GuidanceText;
10
10
  export declare function stepDetailText(step: PreflightStep, snapshot: PreflightSnapshot, t: Translate): string;
11
11
  export declare function gateHintOf(snapshot: PreflightSnapshot, t: Translate): string;
12
- export declare function showsRawDetails(snapshot: PreflightSnapshot): boolean;
13
- export declare function showsRawAll(snapshot: PreflightSnapshot, panelShown: boolean): boolean;
14
12
  export interface BridgeAdvice {
15
13
  readonly text: string;
16
14
  readonly link?: {
@@ -19,3 +17,4 @@ export interface BridgeAdvice {
19
17
  };
20
18
  }
21
19
  export declare function bridgeAdvice(issue: PreflightBridgeIssue, t: Translate, fixes?: RuntimeProfile['bridge']): BridgeAdvice;
20
+ export declare function bridgeAdviceLines(issues: readonly PreflightBridgeIssue[], t: Translate, fixes?: RuntimeProfile['bridge']): readonly BridgeAdvice[];
@@ -1,5 +1,7 @@
1
1
  import type { ClientUnitRegistry } from '../../kernel/client/registry';
2
2
  export interface UnitManifestLoaderOptions {
3
3
  readonly subscribeFacts?: (fact: string, listener: () => void) => () => void;
4
+ readonly retryDelayMs?: number;
5
+ readonly maxRetries?: number;
4
6
  }
5
7
  export declare function createUnitManifestLoader(options?: UnitManifestLoaderOptions): (registry: ClientUnitRegistry) => void;
@@ -1,7 +1,7 @@
1
1
  import type { DshHostContext, HostSurface } from '@dsh-workbench/shared';
2
2
  import type { UnitManifest } from '@dsh-workbench/shared/host';
3
3
  import { type RouteTable } from '@dsh-workbench/shared/host';
4
- import type { ContextRenderer, LongTaskBase, LongTaskChange, LongTaskRegistry, UnitMountDeps, UnitMountResult, UnitCall } from '@dsh-workbench/shared/host';
4
+ import type { ContextRenderer, LongTaskBase, LongTaskChange, LongTaskRegistry, Seam, UnitMountDeps, UnitMountResult, UnitCall } from '@dsh-workbench/shared/host';
5
5
  import type { DataReader } from '@dsh-workbench/shared/host';
6
6
  import type { HostUnit } from './unit-registry';
7
7
  import { type UnitReadiness } from './unit-readiness';
@@ -69,6 +69,7 @@ export declare function mountUnits(units: readonly HostUnit[], deps: {
69
69
  store?: UnitMountDeps['store'];
70
70
  values?: UnitMountDeps['values'];
71
71
  surface?: HostSurface | undefined;
72
+ seam?: Seam | undefined;
72
73
  longTasks?: {
73
74
  readonly scope: string;
74
75
  readonly bump: (facts: readonly string[]) => void;
@@ -1,10 +1,10 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
1
+ import { type Seam } from '@dsh-workbench/shared/host';
2
2
  import { type LineMaker, type BrowserStep } from './diy-browser';
3
3
  export declare const ACT_TIMEOUT_MS = 15000;
4
4
  export interface ActStep extends BrowserStep {
5
5
  readonly payload: Record<string, unknown> | undefined;
6
6
  }
7
- export declare function actBrowser(shell: DshShell, command: string, session: string, argv: readonly string[], timeoutMs?: number, line?: LineMaker): Promise<ActStep>;
7
+ export declare function actBrowser(seam: Seam, command: string, session: string, argv: readonly string[], timeoutMs?: number, line?: LineMaker): Promise<ActStep>;
8
8
  export declare function actErrorOf(step: ActStep): string;
9
9
  export declare const SWEEP_TIMEOUT_MS = 4000;
10
- export declare function stopNetworkExact(shell: DshShell, file: string, timeoutMs?: number): Promise<boolean>;
10
+ export declare function stopNetworkExact(seam: Seam, file: string, timeoutMs?: number): Promise<boolean>;
@@ -0,0 +1,14 @@
1
+ import type { PngRect } from '../../../shared/api/host/png-crop';
2
+ export interface PngClip {
3
+ readonly x: number;
4
+ readonly y: number;
5
+ readonly width: number;
6
+ readonly height: number;
7
+ }
8
+ export declare function clipOf(value: unknown): PngClip | undefined;
9
+ export declare function deviceClipRect(clip: PngClip, dpr: number): PngRect;
10
+ export declare function clipNote(clip: PngClip, viewport: {
11
+ readonly width: number;
12
+ readonly height: number;
13
+ }): string;
14
+ export declare function defaultShotPath(dir: string, unit: string, stamp: number): string;
@@ -1,13 +1,6 @@
1
1
  import type { DshHostContext, DshToolRegistry } from '@dsh-workbench/shared';
2
- import { type PngRect } from '../../../shared/api/host/png-crop';
3
2
  import { type PanelsToolName } from '../unit';
4
3
  export declare const CARD_TOKEN = "$card";
5
- export interface PngClip {
6
- readonly x: number;
7
- readonly y: number;
8
- readonly width: number;
9
- readonly height: number;
10
- }
11
4
  export type CssTarget = {
12
5
  readonly kind: 'positional';
13
6
  readonly at: number;
@@ -28,12 +21,6 @@ export interface BrowserToolRow {
28
21
  export declare function cardSelectorIn(unit: string): string;
29
22
  export declare function evalWrapped(script: string, card: string): string;
30
23
  export declare function argvOf(row: BrowserToolRow, raw: Record<string, unknown>, unit: string): string[];
31
- export declare function clipOf(value: unknown): PngClip | undefined;
32
- export declare function deviceClipRect(clip: PngClip, dpr: number): PngRect;
33
- export declare function clipNote(clip: PngClip, viewport: {
34
- readonly width: number;
35
- readonly height: number;
36
- }): string;
37
- export declare function defaultShotPath(dir: string, unit: string, stamp: number): string;
38
24
  export declare const BROWSER_TOOLS: readonly BrowserToolRow[];
39
25
  export declare function registerBrowserTools(ctx: DshHostContext, tools: DshToolRegistry): readonly (() => void)[];
26
+ export { clipNote, clipOf, defaultShotPath, deviceClipRect, type PngClip } from './diy-browser-clip';
@@ -1,4 +1,4 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
1
+ import { type Seam, type WriteIntent } from '@dsh-workbench/shared/host';
2
2
  export declare const LOOK_TIMEOUT_MS = 30000;
3
3
  export interface BrowserStep {
4
4
  readonly ok: boolean;
@@ -10,7 +10,7 @@ export interface BrowserStep {
10
10
  }
11
11
  export type LineMaker = (command: string, argv: readonly string[]) => string;
12
12
  export declare function plainLine(command: string, argv: readonly string[]): string;
13
- export declare function runBrowserStep(shell: DshShell, command: string, argv: readonly string[], timeoutMs?: number, line?: LineMaker): Promise<BrowserStep>;
13
+ export declare function runBrowserStep(seam: Seam, command: string, argv: readonly string[], timeoutMs?: number, line?: LineMaker, intent?: WriteIntent): Promise<BrowserStep>;
14
14
  export declare function jsonOf(raw: string): Record<string, unknown> | undefined;
15
15
  export declare function textOf(value: unknown): string;
16
16
  export interface BoundTab {
@@ -19,7 +19,7 @@ export interface BoundTab {
19
19
  readonly title: string;
20
20
  readonly reason: string;
21
21
  }
22
- export declare function bindCurrentTab(shell: DshShell, command: string, session: string, timeoutMs?: number, line?: LineMaker): Promise<BoundTab>;
22
+ export declare function bindCurrentTab(seam: Seam, command: string, session: string, timeoutMs?: number, line?: LineMaker): Promise<BoundTab>;
23
23
  export interface WorkbenchPage {
24
24
  readonly ok: boolean;
25
25
  readonly url: string;
@@ -29,12 +29,12 @@ export interface WorkbenchPage {
29
29
  readonly open: boolean | null;
30
30
  readonly shown: boolean;
31
31
  }
32
- export declare function workbenchPageOf(shell: DshShell, command: string, session: string): Promise<WorkbenchPage>;
33
- export declare function writeShotsDir(shell: DshShell, dir: string): Promise<boolean>;
32
+ export declare function workbenchPageOf(seam: Seam, command: string, session: string): Promise<WorkbenchPage>;
33
+ export declare function writeShotsDir(seam: Seam, dir: string): Promise<boolean>;
34
34
  export interface PageHandshake {
35
35
  readonly ok: boolean;
36
36
  readonly bound: boolean;
37
37
  readonly page: WorkbenchPage;
38
38
  readonly reason: string;
39
39
  }
40
- export declare function readyPageOf(shell: DshShell, command: string, session: string): Promise<PageHandshake>;
40
+ export declare function readyPageOf(seam: Seam, command: string, session: string): Promise<PageHandshake>;
@@ -1,4 +1,5 @@
1
- import type { DshHostContext, DshShell, DshToolRunContext } from '@dsh-workbench/shared';
1
+ import type { DshHostContext, DshToolRunContext } from '@dsh-workbench/shared';
2
+ import { type Seam } from '@dsh-workbench/shared/host';
2
3
  import type { DebugState } from '@dsh-workbench/shared/host';
3
4
  import { type BoardFactState, type BoardProbe, type BoardSituation } from '@dsh-workbench/shared/host';
4
5
  import { type LineMaker, type WorkbenchPage } from './diy-browser';
@@ -8,7 +9,7 @@ export interface PageHandle {
8
9
  readonly artifactsDir: string;
9
10
  readonly unit: string;
10
11
  readonly args: Record<string, unknown>;
11
- readonly shell: DshShell;
12
+ readonly seam: Seam;
12
13
  readonly command: string;
13
14
  readonly session: string;
14
15
  readonly line?: LineMaker;
@@ -1,7 +1,7 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
1
+ import type { Seam } from '@dsh-workbench/shared/host';
2
2
  export declare function samePageOf(here: string, want: string): boolean;
3
3
  export declare function openRecordPage(input: {
4
- readonly shell: DshShell;
4
+ readonly seam: Seam;
5
5
  readonly command: string;
6
6
  readonly session: string;
7
7
  readonly url: string;
@@ -1,5 +1,6 @@
1
- import type { DshHostContext, DshShell } from '@dsh-workbench/shared';
1
+ import type { DshHostContext } from '@dsh-workbench/shared';
2
2
  import type { RecordState, RecordStore } from '@dsh-workbench/shared/host';
3
+ import { type Seam } from '@dsh-workbench/shared/host';
3
4
  import { type LineMaker } from './diy-browser';
4
5
  import type { CaptureEntry } from './diy-record-capture';
5
6
  import type { RecordCall, RecordListRow, RecordPageRead } from './diy-record-text';
@@ -16,10 +17,10 @@ export declare function captureFileOf(artifactsDir: string, unit: string): strin
16
17
  export declare function lastCaptureFileOf(file: string): string;
17
18
  export declare function shotFileOf(captureFile: string, unit: string, n: number): string;
18
19
  export declare function takeShotOf(input: WatchFinishDeps, held: RecordState, n: number): Promise<void>;
19
- export declare function startRecordCapture(shell: DshShell, command: string, session: string, file: string, makeLine?: LineMaker): Promise<boolean>;
20
+ export declare function startRecordCapture(seam: Seam, command: string, session: string, file: string, makeLine?: LineMaker): Promise<boolean>;
20
21
  export interface WatchFinishDeps {
21
22
  readonly ctx: DshHostContext;
22
- readonly shell: DshShell;
23
+ readonly seam: Seam;
23
24
  readonly command: string;
24
25
  readonly sessionId: string;
25
26
  readonly store: RecordStore;
@@ -43,15 +44,15 @@ export interface CaptureRead {
43
44
  readonly truncated: boolean;
44
45
  readonly round: string;
45
46
  }
46
- export declare function captureLinesOf(shell: DshShell, file: string, timeoutMs?: number): Promise<number>;
47
- export declare function captureRoundOf(shell: DshShell, file: string, timeoutMs?: number): Promise<string>;
48
- export declare function captureTextOf(shell: DshShell, file: string, timeoutMs?: number): Promise<{
47
+ export declare function captureLinesOf(seam: Seam, file: string, timeoutMs?: number): Promise<number>;
48
+ export declare function captureRoundOf(seam: Seam, file: string, timeoutMs?: number): Promise<string>;
49
+ export declare function captureTextOf(seam: Seam, file: string, timeoutMs?: number): Promise<{
49
50
  readonly text: string;
50
51
  readonly truncated: boolean;
51
52
  }>;
52
- export declare function captureReadOf(shell: DshShell, file: string, timeoutMs?: number): Promise<CaptureRead>;
53
- export declare function keepCaptureOf(shell: DshShell, file: string, timeoutMs?: number): Promise<boolean>;
54
- export declare function captureLinesWithKeyOf(shell: DshShell, file: string, key: string, timeoutMs?: number): Promise<string>;
53
+ export declare function captureReadOf(seam: Seam, file: string, timeoutMs?: number): Promise<CaptureRead>;
54
+ export declare function keepCaptureOf(seam: Seam, file: string, timeoutMs?: number): Promise<boolean>;
55
+ export declare function captureLinesWithKeyOf(seam: Seam, file: string, key: string, timeoutMs?: number): Promise<string>;
55
56
  export interface SegmentBundle {
56
57
  readonly capture: CaptureRead;
57
58
  readonly entries: readonly CaptureEntry[];
@@ -65,7 +66,7 @@ export declare function hookCallOf(page: RecordPageRead, key: string): RecordCal
65
66
  export declare function detailOfEntry(entry: CaptureEntry, maxBytes: number, ambiguous?: number): Record<string, unknown>;
66
67
  export declare function detailOfCall(call: RecordCall, maxBytes: number): Record<string, unknown>;
67
68
  export declare function releaseRecord(input: {
68
- readonly shell: DshShell;
69
+ readonly seam: Seam;
69
70
  readonly command: string;
70
71
  readonly session: string;
71
72
  readonly url: string;
@@ -75,7 +76,7 @@ export declare function releaseRecord(input: {
75
76
  readonly capture: boolean;
76
77
  }>;
77
78
  export declare function releaseQuietly(input: {
78
- readonly shell: DshShell;
79
+ readonly seam: Seam;
79
80
  readonly command: string;
80
81
  readonly session: string;
81
82
  readonly url: string;
@@ -1,6 +1,6 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
1
  import type { ProfileModule } from '@dsh-workbench/shared/host';
3
2
  import type { DiscoverDoc, DiscoverDocKind, DiscoverEntry, DiscoverFilter, DiscoverPayload } from '@dsh-workbench/shared/contract/protocol/market-index';
3
+ import { type Seam } from '@dsh-workbench/shared/host';
4
4
  import type { MarketIndexReader } from '../../../shared/api/host/market-index-fetch';
5
5
  import type { MarketDocReader } from '../../../shared/api/host/market-doc';
6
6
  import type { MarketSourcesStore } from '../../../shared/api/host/market-sources-store';
@@ -17,7 +17,7 @@ export interface MarketDiscoverOptions {
17
17
  readonly modules: () => readonly ProfileModule[];
18
18
  readonly defaultRegistry: () => Promise<string | undefined>;
19
19
  readonly profilePath?: string | undefined;
20
- readonly shell?: DshShell | undefined;
20
+ readonly seam?: Seam | undefined;
21
21
  readonly shellVersion?: string | undefined;
22
22
  readonly fetchImpl?: ((url: string, init: RequestInit) => Promise<Response>) | undefined;
23
23
  readonly now?: (() => number) | undefined;
@@ -1,9 +1,10 @@
1
- import type { ProfilePackage } from '@dsh-workbench/shared/host';
1
+ import type { ProfilePackage, Seam } from '@dsh-workbench/shared/host';
2
2
  import { type MigrationRunInput, type MigrationRunResult } from '../../../shared/api/host/migrate-workbench';
3
3
  export interface WorkbenchFollowUpDeps {
4
4
  readonly dshHome: (() => string) | undefined;
5
5
  readonly profileName: (() => string | null) | undefined;
6
6
  readonly workbench: ((pkg: string) => ProfilePackage | undefined) | undefined;
7
+ readonly seam?: Seam | undefined;
7
8
  readonly runMigration?: ((input: MigrationRunInput) => Promise<MigrationRunResult>) | undefined;
8
9
  }
9
10
  export interface WorkbenchFollowUpActions {
@@ -1,5 +1,4 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
- import type { PluginSourceCommand } from '@dsh-workbench/shared/host';
1
+ import type { PluginSourceCommand, Seam } from '@dsh-workbench/shared/host';
3
2
  import type { BundleChange } from '../../../kernel/contract/bundle-install';
4
3
  import type { BundleManager } from '@dsh-workbench/shared/host';
5
4
  import type { InstalledModule } from '@dsh-workbench/shared/host';
@@ -11,7 +10,7 @@ export interface BundleInstallDeps {
11
10
  readonly options?: BundleInstallOptions | undefined;
12
11
  readonly registry?: string | undefined;
13
12
  readonly command: string;
14
- readonly shell?: DshShell | undefined;
13
+ readonly seam?: Seam | undefined;
15
14
  readonly supply?: SupplyPort | undefined;
16
15
  readonly manager: BundleManager;
17
16
  readonly installed?: ((pkg: string) => InstalledModule) | undefined;
@@ -1,12 +1,11 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
- import type { BundleManager, SupplyPort } from '@dsh-workbench/shared/host';
1
+ import type { BundleManager, Seam, SupplyPort } from '@dsh-workbench/shared/host';
3
2
  import type { InstalledModule, ProfileModule } from '@dsh-workbench/shared/host';
4
3
  import { type ReplaceBundleDeps } from './market-route';
5
4
  import type { BundleInstallOutcome } from './market-types';
6
5
  export interface BundleUpdateDeps {
7
6
  readonly modules: (() => readonly ProfileModule[]) | undefined;
8
7
  readonly installed: ((pkg: string) => InstalledModule) | undefined;
9
- readonly shell: DshShell | undefined;
8
+ readonly seam: Seam | undefined;
10
9
  readonly manager: BundleManager;
11
10
  readonly routeDeps: ReplaceBundleDeps;
12
11
  readonly sweep: () => Promise<void>;
@@ -1,11 +1,11 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
1
+ import { type Seam } from '@dsh-workbench/shared/host';
2
2
  import type { InstalledModule, ProfilePackage, SupplyPort } from '@dsh-workbench/shared/host';
3
3
  import type { SelfUpdateRequest, SelfUpdateStart } from '@dsh-workbench/shared/host';
4
4
  import type { BundleInstallOutcome } from './market-types';
5
5
  export interface WorkbenchUpdateDeps {
6
6
  readonly asked: string;
7
7
  readonly workbench?: ((pkg: string) => ProfilePackage | undefined) | undefined;
8
- readonly shell?: DshShell | undefined;
8
+ readonly seam?: Seam | undefined;
9
9
  readonly installed?: ((pkg: string) => InstalledModule) | undefined;
10
10
  readonly dshHome?: (() => string) | undefined;
11
11
  readonly profileRegistry?: (() => Promise<string | undefined>) | undefined;
@@ -1,9 +1,9 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
1
  import type { UnitManifest } from '@dsh-workbench/shared';
3
2
  import type { BundleManager } from '@dsh-workbench/shared/host';
4
3
  import type { InstalledModule, ProfileModule, ProfilePackage } from '@dsh-workbench/shared/host';
5
4
  import type { RegistryNotebook } from '@dsh-workbench/shared/host';
6
5
  import { type SupplyPort, type UnitMountDeps } from '@dsh-workbench/shared/host';
6
+ import { type Seam } from '@dsh-workbench/shared/host';
7
7
  import type { SelfUpdateRequest, SelfUpdateStart } from '@dsh-workbench/shared/host';
8
8
  import { type WorkbenchFollowUpDeps } from './market-follow-up';
9
9
  import { type PluginReferences } from './plugin-actions';
@@ -15,7 +15,7 @@ export declare function createMarketService(options: {
15
15
  manifests: readonly UnitManifest[];
16
16
  manifestsNow?: (() => readonly UnitManifest[]) | undefined;
17
17
  probe?: UnitMountDeps['probe'];
18
- shell?: DshShell | undefined;
18
+ seam?: Seam | undefined;
19
19
  command: string;
20
20
  onInstalled?: (unit: string) => void;
21
21
  onUninstalled?: (unit: string) => void;
@@ -1,5 +1,4 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
- import { type PluginSourceCommand, type SupplyPort } from '@dsh-workbench/shared/host';
1
+ import { type PluginSourceCommand, type Seam, type SupplyPort } from '@dsh-workbench/shared/host';
3
2
  import type { PluginLedger } from './plugin-ledger';
4
3
  export type PluginKept = {
5
4
  readonly kind: 'held';
@@ -38,7 +37,7 @@ export interface PluginActions {
38
37
  }
39
38
  export declare function createPluginActions(options: {
40
39
  readonly ledger?: PluginLedger | undefined;
41
- readonly shell?: DshShell | undefined;
40
+ readonly seam?: Seam | undefined;
42
41
  readonly command: string;
43
42
  readonly runAll: (commands: readonly PluginSourceCommand[]) => Promise<void>;
44
43
  readonly claimedBy?: ((plugin: string) => readonly string[]) | undefined;
@@ -1,16 +1,15 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
2
- import { type RouteTable, type SupplyPort } from '@dsh-workbench/shared/host';
1
+ import { type RouteTable, type Seam, type SupplyPort } from '@dsh-workbench/shared/host';
3
2
  import type { InstalledPlugin } from '../../../shared/api/installed-plugins';
4
3
  import type { PluginLedger } from './plugin-ledger';
5
4
  export declare function installedPlugins(options: {
6
5
  readonly ledger: PluginLedger;
7
- readonly shell?: DshShell | undefined;
6
+ readonly seam?: Seam | undefined;
8
7
  readonly force?: boolean;
9
8
  readonly check?: boolean;
10
9
  readonly supply?: SupplyPort | undefined;
11
10
  }): Promise<readonly InstalledPlugin[]>;
12
11
  export declare function pluginRoutes(options: {
13
12
  readonly ledger: PluginLedger;
14
- readonly shell?: DshShell | undefined;
13
+ readonly seam?: Seam | undefined;
15
14
  readonly supply?: SupplyPort | undefined;
16
15
  }): RouteTable;
@@ -1,8 +1,8 @@
1
- import type { DshHostContext, DshShell } from '@dsh-workbench/shared';
2
- import { type RouteHandler } from '@dsh-workbench/shared/host';
1
+ import type { DshHostContext } from '@dsh-workbench/shared';
2
+ import { type RouteHandler, type Seam } from '@dsh-workbench/shared/host';
3
3
  import { type WorkbenchSelfUpdate } from '../../../kernel/contract/bundle-install';
4
4
  export declare function bundleRegistriesHandler(ctx: DshHostContext): RouteHandler;
5
5
  export declare function resetRefusalLog(): void;
6
6
  export declare function resetHoldLog(): void;
7
- export declare function moduleVersionsHandler(ctx: DshHostContext, shell: DshShell | undefined, running?: string | undefined, selfUpdate?: (() => WorkbenchSelfUpdate | undefined) | undefined): RouteHandler;
7
+ export declare function moduleVersionsHandler(ctx: DshHostContext, seam: Seam | undefined, running?: string | undefined, selfUpdate?: (() => WorkbenchSelfUpdate | undefined) | undefined): RouteHandler;
8
8
  export declare function bundleInspectHandler(ctx: DshHostContext): RouteHandler;
@@ -1,3 +1,2 @@
1
- import type { DshHostContext } from '@dsh-workbench/shared';
2
- export declare function artifactsDirOf(ctx: DshHostContext, root: string): string;
1
+ export declare function artifactsDirOf(root: string): string;
3
2
  export declare function ensureDirOf(dir: string): void;
@@ -1,4 +1,4 @@
1
- import type { DshShell } from '@dsh-workbench/shared';
1
+ import { type Seam } from '@dsh-workbench/shared/host';
2
2
  export declare const SURFACE_TIMEOUT_MS = 30000;
3
3
  export declare const EXCERPT_LINES = 60;
4
4
  export declare const SUMMARY_PER_BUCKET = 12;
@@ -20,7 +20,7 @@ export declare function wantsKit(scope: SurfaceScope): boolean;
20
20
  export declare function toolchainBinOf(root: string): string | null;
21
21
  export declare function toolchainBinAmong(root: string, dirs: readonly string[]): string | null;
22
22
  export declare function queryCommandOf(bin: string, only: 'kit' | 'primitives'): string;
23
- export declare function runSurfaceQuery(shell: DshShell, bin: string, only: 'kit' | 'primitives', root: string, fallbackBin?: string): Promise<SurfacePayload>;
23
+ export declare function runSurfaceQuery(seam: Seam, bin: string, only: 'kit' | 'primitives', root: string, fallbackBin?: string): Promise<SurfacePayload>;
24
24
  export declare function filesOf(payload: SurfacePayload | undefined): Record<string, string>;
25
25
  export declare function rootsOf(payload: SurfacePayload | undefined): Record<string, string>;
26
26
  export declare function countsOf(payload: SurfacePayload | undefined): Record<string, number>;
@@ -0,0 +1,6 @@
1
+ export declare function stagingDirOf(unit: string): string;
2
+ export declare function journalFileOf(staging: string, unit: string): string;
3
+ export declare function prepareStaging(unit: string, resume: boolean): string;
4
+ export declare function cleanupStaging(unit: string): void;
5
+ export declare function adoptTarballFile(file: string, unit: string): string | undefined;
6
+ export declare function mirrorJournal(staging: string, unit: string): void;
@@ -1,2 +1,2 @@
1
1
  import type { DshHostContext } from '@dsh-workbench/shared';
2
- export declare function workshopPathOf(ctx: DshHostContext): string | null;
2
+ export declare function workshopPathOf(_ctx: DshHostContext): string | null;
@@ -1,3 +1,3 @@
1
1
  export declare function gitNoPromptEnv(platform?: NodeJS.Platform): Readonly<Record<string, string>>;
2
- export declare function gitNoPromptProcessEnv(parent?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
2
+ export declare function gitNoPromptProcessEnv(parent?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): Record<string, string>;
3
3
  export declare function withoutAskpassNoise(text: string): string;
@@ -1,3 +1,4 @@
1
+ import type { Seam } from '@dsh-workbench/shared/host';
1
2
  export interface GitRunResult {
2
3
  readonly exitCode: number | null;
3
4
  readonly stdout: string;
@@ -10,4 +11,6 @@ export interface GitRunOptions {
10
11
  readonly maxBytes?: number;
11
12
  }
12
13
  export type GitRunner = (args: readonly string[], options?: GitRunOptions) => Promise<GitRunResult>;
14
+ export declare function provideGitProcessSeam(seam: Seam | undefined): void;
15
+ export declare function createGitRunner(seam: Seam): GitRunner;
13
16
  export declare const runGitProcess: GitRunner;
@@ -0,0 +1,4 @@
1
+ import type { DshHostContext } from '@dsh-workbench/shared';
2
+ import type { AdapterSet } from '@dsh-workbench/shared/host';
3
+ export declare function shippedAdapterSetSpec(moduleUrl: string): string;
4
+ export declare function shippedAdapterSet(ctx: DshHostContext): Promise<AdapterSet>;
@@ -0,0 +1,3 @@
1
+ import type { DshHostContext } from '@dsh-workbench/shared';
2
+ import { type Seam } from '@dsh-workbench/shared/host';
3
+ export declare function provideWorkbenchSeam(ctx: DshHostContext): Seam | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "1.1.14-beta.2",
3
+ "version": "1.1.14",
4
4
  "description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -31,7 +31,6 @@
31
31
  "cordis.patch.yml",
32
32
  "icon.svg",
33
33
  "bin",
34
- "profile",
35
34
  "skills",
36
35
  "preset",
37
36
  "scripts/create-workbench.mjs",
@@ -82,7 +81,6 @@
82
81
  "@types/proper-lockfile": "^4.1.4",
83
82
  "@types/react": "^19.3.0",
84
83
  "@types/which": "^3.0.4",
85
- "@types/write-file-atomic": "^4.0.3",
86
84
  "@vitest/coverage-v8": "^5.0.2",
87
85
  "esbuild": "^0.28.2",
88
86
  "react": "^19.3.0",
@@ -95,13 +93,12 @@
95
93
  },
96
94
  "dependencies": {
97
95
  "dayjs": "^1.11.23",
98
- "execa": "^10.2.0",
99
96
  "prompts": "^2.4.2",
100
97
  "proper-lockfile": "^4.1.2",
101
98
  "which": "^7.0.0",
102
99
  "write-file-atomic": "^8.0.0",
103
- "@dsh-workbench/adapters-default": "1.1.14-beta.2",
104
- "@dsh-workbench/shared": "1.1.14-beta.2"
100
+ "@dsh-workbench/adapters-default": "1.1.14",
101
+ "@dsh-workbench/shared": "1.1.14"
105
102
  },
106
103
  "license": "MIT",
107
104
  "repository": {
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # 一次性引导:从 shipped 的 web 模板创建 workbench profile,
4
4
  # 继承 web 的 **bundle 清单**(这台机器上装了哪些 bundle),
5
- # 补上可写根那一行;新 profile 直接跟 web 共用数据根,旧 profile 保持原状,等用户跑 migrate。
5
+ # 清掉旧版留下的可写根那一行(现在由产品代码声明);新 profile 直接跟 web 共用数据根,旧 profile 保持原状,等用户跑 migrate。
6
6
  #
7
7
  # **设置不在这里继承**(这里曾经把 web 的 cordis.patch.yml 快照一份过来):
8
8
  # 工作台自己的 patch 是唯一真相,想同步 web 的哪几行由用户走「设置 → 配置同步」。
@@ -176,7 +176,7 @@ console.log("→ profile 里那条 link: 指的是我这份装出来的副本(
176
176
  '
177
177
  fi
178
178
 
179
- # ── 第 3 步:数据根配置 + 可写根那一行 ─────────────────────────────────
179
+ # ── 第 3 步:数据根配置(顺手清掉旧版塞进来的可写根那一行)─────────────
180
180
  #
181
181
  # 新 profile 跟 `dsh web` / Desktop 一样只分 profile,数据落在 `$DSH_HOME`。
182
182
  # 旧 profile 如果还有没迁的数据,这里只打印 migrate 提示,保持隔离、不动数据;
@@ -203,7 +203,7 @@ fi
203
203
 
204
204
  cat <<EOF
205
205
 
206
- profile '$DST_PROFILE' 的骨架好了(bundle 清单 + 可写根)。
206
+ profile '$DST_PROFILE' 的骨架好了(bundle 清单)。
207
207
  旧实例如果还有数据没迁,启动时会提示;要切共用根并并旧数据,跑 dsh-workbench migrate。
208
208
 
209
209
  设置按创建时的勾选从 $SRC_PROFILE_NAME 继承(只同步改已有插件的行;挂新插件的行不同步);
@@ -13,17 +13,13 @@ import {
13
13
  import { randomUUID } from 'node:crypto';
14
14
  import { homedir } from 'node:os';
15
15
  import { dirname, join, resolve } from 'node:path';
16
- import { fileURLToPath } from 'node:url';
17
- import { appendBlocks, parseBlocks, removeBlocks } from './patch-blocks.mjs';
16
+ import { parseBlocks, removeBlocks } from './patch-blocks.mjs';
18
17
 
19
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
20
- const PKG_ROOT = dirname(SCRIPT_DIR);
21
18
  const HOME = process.env.DSH_HOME?.trim() || join(homedir(), '.dsh');
22
19
  const PROFILE = process.env.WORKBENCH_PROFILE?.trim() || 'workbench';
23
20
  const PROFILE_DIR = join(HOME, 'profiles', PROFILE);
24
21
  const RUNTIME_ROOT = process.env.DSH_WORKBENCH_HOME?.trim() || join(HOME, PROFILE);
25
22
  const PATCH_PATH = join(PROFILE_DIR, 'cordis.patch.yml');
26
- const SANDBOX_PATCH = join(PKG_ROOT, 'profile', 'sandbox.patch.yml');
27
23
  const MARKER = join(RUNTIME_ROOT, '.data-root-shared');
28
24
 
29
25
  const DATA_ROOT_IDS = new Set(['session-persistence-jsonl', 'storage-json', 'attachment-local']);
@@ -300,14 +296,19 @@ function writeMarker() {
300
296
  );
301
297
  }
302
298
 
299
+ const STALE_SANDBOX =
300
+ /workspaceRoot: !!js (?:process\.env\.DSH_WORKBENCH_HOME \?\? )?dshHomePath\(\s*['"]workbench['"]\)/;
301
+
303
302
  function inspectProfile() {
304
303
  const patch = readText(PATCH_PATH);
305
304
  if (patch === null) return null;
306
305
  const legacy = parseBlocks(patch).filter((block) => DATA_ROOT_IDS.has(block.id));
306
+ const sandbox = parseBlocks(patch).filter((block) => block.id === 'sandbox-policy' && STALE_SANDBOX.test(block.body));
307
307
  return {
308
308
  patch,
309
309
  ours: legacy.filter((block) => OWN_ROOT.test(block.body)),
310
310
  custom: legacy.filter((block) => !OWN_ROOT.test(block.body)),
311
+ sandbox,
311
312
  };
312
313
  }
313
314
 
@@ -328,14 +329,10 @@ function applySharedConfig(state) {
328
329
  writeAtomic(PATCH_PATH, patch);
329
330
  say(`数据根改回共用:从 ${PROFILE} 的 profile 里去掉 ${state.ours.map((block) => block.id).join(' / ')}。`);
330
331
  }
331
- const hasSandbox = parseBlocks(patch).some((block) => block.id === 'sandbox-policy');
332
- if (!hasSandbox) {
333
- const sandbox = readText(SANDBOX_PATCH);
334
- const row = sandbox === null ? undefined : parseBlocks(sandbox).find((block) => block.id === 'sandbox-policy');
335
- if (row !== undefined) {
336
- patch = appendBlocks(patch, row.body);
337
- writeAtomic(PATCH_PATH, patch);
338
- }
332
+ if (state.sandbox.length > 0) {
333
+ patch = removeBlocks(patch, (block) => block.id === 'sandbox-policy' && STALE_SANDBOX.test(block.body));
334
+ writeAtomic(PATCH_PATH, patch);
335
+ say(`可写根改由工作台自己声明:从 ${PROFILE} 的 profile 里去掉 sandbox-policy 那一行。`);
339
336
  }
340
337
  repairTarget(join(HOME, 'storages', 'workspace.json'));
341
338
  return true;
@@ -107,3 +107,41 @@ export const apply = plugin.apply;
107
107
 
108
108
  `idle` 是独立态(无工作区或 `enabled: false`),不画成空态。
109
109
  卡片在任意输入下不得抛异常或出现 `undefined` / `NaN`(`check --only cards`)。
110
+
111
+ ## 5 起子进程:走执行缝门面
112
+
113
+ 模块要在宿主侧跑命令(构建、git、打包……)时,用外壳放进 `mount(deps)` 的执行缝门面 `deps.seam`。
114
+ `ctx.get('shell')` / `ctx.get('workbenchShell')` / `ctx.get('seam')`、`runShell` / `shellOf`、裸
115
+ `child_process` 都不给模块用,`check --only seam` 会判红。
116
+
117
+ 先在清单里声明要哪几档写入域(只认两档):
118
+
119
+ ```json
120
+ { "host": { "inject": [], "writes": ["unit"] } }
121
+ ```
122
+
123
+ | 档 | 可写在哪 | 什么时候给 |
124
+ |---|---|---|
125
+ | `unit` | `<运行时根>/units/<unit>/`(模块自己的数据目录;`HOME`、npm/pnpm 缓存与日志也注入到这里) | 声明了就常开、不用审批 |
126
+ | `workspace` | 当前会话的工作区 | 声明了才给;调用时还要带上会话对象 |
127
+
128
+ 不写 `writes`(或写 `[]`)= 不拿门面:`deps.seam` 是 `undefined`,代码里按可选处理。
129
+
130
+ ```ts
131
+ const result = await deps.seam?.run({
132
+ scope: 'unit',
133
+ command: 'git rev-parse --short HEAD',
134
+ timeoutMs: 10_000,
135
+ });
136
+ if (result === undefined || result.exitCode !== 0) {
137
+ return { ok: false, text: '这条命令没跑通' };
138
+ }
139
+ ```
140
+
141
+ - `workspace` 档的 `session` 从工具运行上下文拿(`execute(raw, exec)` 的 `exec.agent?.session`);
142
+ 路由 / 动作这些没有会话的调用点不要请求这一档。
143
+ - 一条命令只声明一个写入域;要写两棵树就在步骤边界拆成两次调用。
144
+ - `retry: 'safe'` 只给幂等 / 只读命令(查询、列清单);写命令用默认的 `'never'`。
145
+ - 请求没声明的档在起进程前就失败(`SeamError` 的 `scope-not-declared` / `workspace-unknown`),别吞掉。
146
+ - 这层门面是**约定 + 门禁 + 归因**,不是进程隔离:模块宿主代码与外壳同进程。真正的安全边界仍是
147
+ 「装不装模块 + 审批」。