dsh-win-multi-bash 0.2.0 → 0.3.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.
@@ -13,8 +13,9 @@ import { defineTool, TOOL_ABORTED } from '@deepseek-ai/dsh-tools';
13
13
  import { HarnessError } from '@deepseek-ai/dsh-llm';
14
14
  import { ESCALATION_TARGETS, approveEscalation, canonicalPath, validateEscalationArgs } from '@deepseek-ai/dsh-sandbox';
15
15
  import { DSH_ENV_PREFIX } from '@deepseek-ai/dsh-shell';
16
- import { processOutcome } from "./background.js";
17
- import { parseExitStatus, renderProcessRead, renderResult } from "./render.js";
16
+ import { ownExecutor } from "./backend.js";
17
+ import { processJob, processOutcome, processSources } from "./background.js";
18
+ import { parseExitStatus, renderResult } from "./render.js";
18
19
  const DIALECT_FACTS = {
19
20
  posix: { shell: 'bash', invoke: 'bash -c', paths: 'POSIX paths', env: '$VAR', toolchain: 'the full Unix toolchain' },
20
21
  msys: {
@@ -31,10 +32,10 @@ const DIALECT_FACTS = {
31
32
  env: '$VAR',
32
33
  },
33
34
  };
34
- /** Runtime configuration schema for a shell tool instance. */
35
- export const Config = z.object({
35
+ /** The tool-layer keys every shell tool instance carries, before its backend partition. */
36
+ const TOOL_CONFIG = {
36
37
  enableRunInBackground: z.boolean().default(true),
37
- });
38
+ };
38
39
  /**
39
40
  * The model-facing description of one shell tool instance. The POSIX variant
40
41
  * keeps the legacy wording byte-for-byte (the ACP/headless tool-schema
@@ -197,18 +198,47 @@ const BACKGROUND_OUTPUT_PROPERTIES = {
197
198
  * tool name, the approval subject, the prompt section name (`tool:<toolName>`),
198
199
  * and the `ctx.jobs` kind; the instance module declares its kind in
199
200
  * {@link JobKindMap} via declaration merging.
201
+ *
202
+ * The tool owns its executor outright (see `./backend.js`): dsh 0.1.7 dropped
203
+ * the shell seam's routing field, so a tool can no longer ask a shared seat
204
+ * for a-backend-by-name. `def.Executor` is constructed once per tool load and
205
+ * driven directly, leaving `ctx.shell` to the base bundle's own executor.
200
206
  * @param def - the instance definition: `toolName` (also the job kind),
201
- * `shell` (routed to the selector executor when defined), and `dialect`.
207
+ * `dialect`, `Executor` (the backend class to own), and `configKey` (the
208
+ * Config partition that backend's settings live under).
202
209
  * @returns the complete plugin object (name/inject/Config/apply).
203
210
  */
204
211
  export function defineShellTool(def) {
212
+ const Config = z.object({
213
+ ...TOOL_CONFIG,
214
+ [def.configKey]: def.Executor.Config.default({}),
215
+ });
205
216
  return {
206
217
  name: `tool-${def.toolName}`,
207
- inject: ['tools', 'shell', 'systemPrompt', 'shellEnv'],
218
+ // The last three are the owned executor's own requirements; injecting
219
+ // them here is what lets the backend be built during apply.
220
+ inject: ['tools', 'systemPrompt', 'shellEnv', 'subprocess', 'sandbox', 'sandboxPolicy'],
208
221
  Config,
209
- apply(ctx, config = {}) {
222
+ async apply(ctx, config = {}) {
210
223
  const backgroundEnabled = config.enableRunInBackground ?? true;
211
- const defaultMode = ctx.shell.sandboxMode;
224
+ const { executor } = ownExecutor(ctx, def.Executor, config[def.configKey]);
225
+ // The confinement probe is async on 0.1.7 (the provider's `confine`
226
+ // returns a promise), so the escalation surface settles before the
227
+ // tool registers: a tool never advertises a confinement it cannot
228
+ // reach.
229
+ //
230
+ // A backend whose probe fails *loud* is deliberately swallowed here
231
+ // — that is the documented lazy contract: an explicit
232
+ // `sandbox: bwrap` without bubblewrap must not brick the tool row
233
+ // at load. It advertises no confinement now and its error
234
+ // resurfaces at the first routed command.
235
+ let defaultMode;
236
+ try {
237
+ defaultMode = await executor.resolveSandboxMode();
238
+ }
239
+ catch {
240
+ defaultMode = undefined;
241
+ }
212
242
  const escalationModes = defaultMode === undefined ? [] : ESCALATION_TARGETS;
213
243
  const sandboxPolicy = defaultMode === undefined ? undefined : ctx.get('sandboxPolicy');
214
244
  if (defaultMode !== undefined && sandboxPolicy === undefined) {
@@ -350,7 +380,6 @@ export function defineShellTool(def) {
350
380
  command: args.command,
351
381
  ...workdir !== undefined ? { workdir } : {},
352
382
  ...args.timeoutMs !== undefined ? { timeoutMs: args.timeoutMs } : {},
353
- ...def.shell !== undefined ? { shell: def.shell } : {},
354
383
  dshEnv,
355
384
  ...policy !== undefined ? { sandboxPolicy: policy } : {},
356
385
  };
@@ -369,26 +398,33 @@ export function defineShellTool(def) {
369
398
  error.name = 'AbortError';
370
399
  throw error;
371
400
  }
401
+ // No deadline for a background command, and the process is
402
+ // spawned inside the starter, after the registry admitted it,
403
+ // so the pull sources bind lazily through the `proc` closure.
404
+ const spec = executor.resolve({ ...request, onExpiry: 'none' });
405
+ let proc;
372
406
  // Task preflight finishes before the starter can spawn a process.
373
407
  const id = jobs.start({
374
408
  kind: def.toolName,
375
409
  label: args.command,
376
- ...exec.agent ? { owner: exec.agent } : {},
410
+ ...exec.agent ? { owner: exec.agent.id } : {},
411
+ output: processSources(() => proc),
377
412
  run: () => {
378
- const proc = ctx.shell.start(ctx.shell.resolve(request));
379
- return {
380
- cancel: () => void proc.kill(),
381
- done: proc.done.then(() => processOutcome(proc)),
382
- readOutput: () => renderProcessRead(proc.readOutput(), proc.sandbox, escalationModes),
383
- };
413
+ const hooks = processJob(async (signal) => {
414
+ proc = await executor.execute({ ...spec, signal });
415
+ return proc;
416
+ }, (started) => processOutcome(started));
417
+ return { done: hooks.done, cancel: (reason) => hooks.cancel(reason) };
384
418
  },
385
419
  });
386
420
  return { kind: 'background', jobId: id };
387
421
  }
388
- const result = await ctx.shell.run(ctx.shell.resolve({
422
+ // Foreground is a property of awaiting the handle's result, not
423
+ // of the call: the same `execute` also serves `run_in_background`.
424
+ const result = await (await executor.execute(executor.resolve({
389
425
  ...request,
390
426
  signal: exec.signal,
391
- }));
427
+ }))).result();
392
428
  if (result.aborted) {
393
429
  const error = new HarnessError('tool call aborted', TOOL_ABORTED);
394
430
  error.name = 'AbortError';
@@ -1,13 +1,21 @@
1
1
  /**
2
- * The Git Bash (MSYS) tool instance: routes `request.shell` to the
3
- * 'git-bash' backend so a selector executor can serve it beside pwsh and WSL.
4
- * @module @deepseek-ai/dsh-tool-bash/git-bash
2
+ * The Git Bash (MSYS) tool instance: owns a {@link GitBashExecutor} outright
3
+ * and drives it directly. dsh 0.1.7 removed the shell seam's routing field
4
+ * (`ShellExecRequest.shell`), so there is no shared selector to route through
5
+ * any more — the tool itself is what knows it wants Git Bash.
6
+ * @module dsh-win-multi-bash/tool-bash/git-bash
5
7
  */
8
+ import { GitBashExecutor } from "../../bash-git/index.js";
6
9
  import { defineShellTool } from "./factory.js";
7
- /** The Git Bash tool: routes request.shell to the 'git-bash' backend. */
8
- const tool = defineShellTool({ toolName: 'git_bash', shell: 'git-bash', dialect: 'msys' });
10
+ /** The Git Bash tool: owns the git-bash backend. */
11
+ const tool = defineShellTool({
12
+ toolName: 'git_bash',
13
+ Executor: GitBashExecutor,
14
+ configKey: 'gitBash',
15
+ dialect: 'msys',
16
+ });
9
17
  export const name = tool.name;
10
18
  export const inject = tool.inject;
11
19
  export const Config = tool.Config;
12
20
  export const apply = tool.apply;
13
- //# sourceMappingURL=git-bash.js.map
21
+ //# sourceMappingURL=git-bash.js.map
@@ -57,35 +57,6 @@ export function renderResult(result, escalationModes = []) {
57
57
  body += '\n';
58
58
  return body + markers.join('\n');
59
59
  }
60
- /**
61
- * Shape one background-process read into the `job_output` delta the model
62
- * sees: the incremental delta, plus the lossy-read notice (with full-stream
63
- * spill paths) when in-memory truncation dropped unread bytes. Empty-delta
64
- * rendering (`(no new output)`) is the generic job controller's job.
65
- * @param read - one incremental read from the process handle.
66
- * @param sandbox - settled sandbox facts, when this was a confined process.
67
- * @param escalationModes - escalation targets advertised by this composition.
68
- * @returns the delta text with any loss or sandbox notice appended.
69
- */
70
- export function renderProcessRead(read, sandbox, escalationModes = []) {
71
- const notices = [];
72
- if (read.lossy) {
73
- const paths = [read.stdoutSpillPath, read.stderrSpillPath].filter((path) => path !== undefined);
74
- notices.push(`[some output was dropped from memory; full output: ${paths.length > 0 ? paths.join(', ') : '(unavailable)'}]`);
75
- }
76
- if (sandbox?.runnerFailed) {
77
- notices.push(`[sandbox: the sandbox runner itself failed under ${sandbox.mode} mode — the command did not run; this is a sandbox problem, not a command failure]`);
78
- }
79
- else if (sandbox?.denied) {
80
- notices.push(sandboxDenialMarker(sandbox.mode));
81
- if (escalationModes.length > 0) {
82
- notices.push(escalationHintMarker('command'));
83
- }
84
- }
85
- if (notices.length === 0)
86
- return read.delta;
87
- return `${read.delta}${read.delta.length > 0 && !read.delta.endsWith('\n') ? '\n' : ''}${notices.join('\n')}`;
88
- }
89
60
  /**
90
61
  * The exit-status parse is the shared marker-contract half of the shell-tool
91
62
  * rendering story, owned by `@deepseek-ai/dsh-shell` so `dsh-tool-pwsh` reuses
@@ -1,13 +1,21 @@
1
1
  /**
2
- * The WSL bash tool instance: routes `request.shell` to the 'wsl-bash'
3
- * backend so a selector executor can serve it beside pwsh and Git Bash.
4
- * @module @deepseek-ai/dsh-tool-bash/wsl-bash
2
+ * The WSL bash tool instance: owns a {@link WslBashExecutor} outright and
3
+ * drives it directly. dsh 0.1.7 removed the shell seam's routing field
4
+ * (`ShellExecRequest.shell`), so there is no shared selector to route through
5
+ * any more — the tool itself is what knows it wants WSL.
6
+ * @module dsh-win-multi-bash/tool-bash/wsl-bash
5
7
  */
8
+ import { WslBashExecutor } from "../../bash-wsl/index.js";
6
9
  import { defineShellTool } from "./factory.js";
7
- /** The WSL bash tool: routes request.shell to the 'wsl-bash' backend. */
8
- const tool = defineShellTool({ toolName: 'wsl_bash', shell: 'wsl-bash', dialect: 'wsl' });
10
+ /** The WSL bash tool: owns the wsl-bash backend. */
11
+ const tool = defineShellTool({
12
+ toolName: 'wsl_bash',
13
+ Executor: WslBashExecutor,
14
+ configKey: 'wslBash',
15
+ dialect: 'wsl',
16
+ });
9
17
  export const name = tool.name;
10
18
  export const inject = tool.inject;
11
19
  export const Config = tool.Config;
12
20
  export const apply = tool.apply;
13
- //# sourceMappingURL=wsl-bash.js.map
21
+ //# sourceMappingURL=wsl-bash.js.map
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "dsh-win-multi-bash",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "author": "Dinosaur_MC",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Dinosaur-MC/dsh-win-multi-bash.git"
8
8
  },
9
- "description": "Windows multi-bash plugin for dsh: git_bash / wsl_bash model tools plus a shell-select executor routing the single ctx.shell seat across Git Bash, WSL and pwsh (pwsh stays the default).",
9
+ "description": "Windows multi-bash plugin for dsh: git_bash / wsl_bash model tools, each owning its own Git Bash / WSL executor and leaving the ctx.shell seat to dsh's own pwsh executor.",
10
10
  "keywords": [
11
11
  "dsh-plugin",
12
12
  "windows",
@@ -26,7 +26,6 @@
26
26
  "main": "lib/index.js",
27
27
  "exports": {
28
28
  ".": "./lib/index.js",
29
- "./shell-select": "./lib/shell-select/index.js",
30
29
  "./tool-git-bash": "./lib/tool-bash/types/git-bash.js",
31
30
  "./tool-wsl-bash": "./lib/tool-bash/types/wsl-bash.js",
32
31
  "./package.json": "./package.json"
@@ -50,14 +49,11 @@
50
49
  },
51
50
  "peerDependencies": {
52
51
  "@deepseek-ai/cordis": ">=4.0.1",
53
- "@deepseek-ai/dsh-bash-local": ">=0.1.0-rc.7",
54
- "@deepseek-ai/dsh-llm": ">=0.1.0-rc.7",
55
- "@deepseek-ai/dsh-pwsh-local": ">=0.1.0-rc.7",
56
- "@deepseek-ai/dsh-pwsh-sandbox": ">=0.1.0-rc.7",
57
- "@deepseek-ai/dsh-sandbox": ">=0.1.0-rc.7",
58
- "@deepseek-ai/dsh-settings": ">=0.1.2-alpha.2",
59
- "@deepseek-ai/dsh-shell": ">=0.1.0-rc.7",
60
- "@deepseek-ai/dsh-tools": ">=0.1.0-rc.7",
52
+ "@deepseek-ai/dsh-bash-local": ">=0.1.7-rc.2",
53
+ "@deepseek-ai/dsh-llm": ">=0.1.7-rc.2",
54
+ "@deepseek-ai/dsh-sandbox": ">=0.1.7-rc.2",
55
+ "@deepseek-ai/dsh-shell": ">=0.1.7-rc.2",
56
+ "@deepseek-ai/dsh-tools": ">=0.1.7-rc.2",
61
57
  "@deepseek-ai/schemastery": ">=3.18.0"
62
58
  },
63
59
  "peerDependenciesMeta": {
@@ -70,18 +66,9 @@
70
66
  "@deepseek-ai/dsh-llm": {
71
67
  "optional": true
72
68
  },
73
- "@deepseek-ai/dsh-pwsh-local": {
74
- "optional": true
75
- },
76
- "@deepseek-ai/dsh-pwsh-sandbox": {
77
- "optional": true
78
- },
79
69
  "@deepseek-ai/dsh-sandbox": {
80
70
  "optional": true
81
71
  },
82
- "@deepseek-ai/dsh-settings": {
83
- "optional": true
84
- },
85
72
  "@deepseek-ai/dsh-shell": {
86
73
  "optional": true
87
74
  },
@@ -1,23 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@deepseek-ai/dsh-bash-git`.
4
- * @module @deepseek-ai/dsh-bash-git/invariant
5
- */
6
- const PACKAGE_NAME = "@deepseek-ai/dsh-bash-git";
7
- /** Cordis companion plugin name. */
8
- const name = "bash-git-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * No runtime invariant: this package exposes no independent event sequence or mutable data relation
13
- * beyond contracts enforced at its owning seam.
14
- */
15
- const install = () => {};
16
- /**
17
- * Register this package's invariant companion.
18
- * @param ctx - Cordis context carrying the invariant service.
19
- * @returns the installed registration's disposer after setup succeeds.
20
- */
21
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
- //#endregion
23
- export { apply, inject, name };
@@ -1,23 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@deepseek-ai/dsh-bash-wsl`.
4
- * @module @deepseek-ai/dsh-bash-wsl/invariant
5
- */
6
- const PACKAGE_NAME = "@deepseek-ai/dsh-bash-wsl";
7
- /** Cordis companion plugin name. */
8
- const name = "bash-wsl-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * No runtime invariant: this package exposes no independent event sequence or mutable data relation
13
- * beyond contracts enforced at its owning seam.
14
- */
15
- const install = () => {};
16
- /**
17
- * Register this package's invariant companion.
18
- * @param ctx - Cordis context carrying the invariant service.
19
- * @returns the installed registration's disposer after setup succeeds.
20
- */
21
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
- //#endregion
23
- export { apply, inject, name };
@@ -1,170 +0,0 @@
1
- import z from "@deepseek-ai/schemastery";
2
- import { HarnessError } from "@deepseek-ai/dsh-llm";
3
- import { SHELL_SETTINGS_NAMESPACE, ShellExecutor } from "@deepseek-ai/dsh-shell";
4
- import { GitBashExecutor } from "../bash-git/index.js";
5
- import { WslBashExecutor } from "../bash-wsl/index.js";
6
- import { SandboxPwshExecutor } from "@deepseek-ai/dsh-pwsh-sandbox";
7
- import { assertServiceableBashConfig } from "@deepseek-ai/dsh-bash-local";
8
- import { assertServiceablePwshConfig } from "@deepseek-ai/dsh-pwsh-local";
9
- //#region lib/types/index.js
10
- /**
11
- * Selector Service Provider for the bash capability seam: occupies the single
12
- * `ctx.shell` seat and routes each request to one of several backends held as
13
- * plain instances. The tool name selects the backend (`request.shell`); a
14
- * request without one goes to the configured `default`. Backends are
15
- * constructed on their own child fibers with isolated `shell`/`settings`
16
- * scopes so their inherited Service registration and settings wiring cannot
17
- * collide with this executor's own seat. win32 compositions only; POSIX
18
- * compositions keep a single backend directly on the seat.
19
- * @module @deepseek-ai/dsh-shell-select
20
- */
21
- /** Error code for routing to an unknown or disabled backend. */
22
- const SHELL_BACKEND_UNAVAILABLE = "SHELL_BACKEND_UNAVAILABLE";
23
- const DEFAULT_FACTORIES = {
24
- "git-bash": (ctx, config) => new GitBashExecutor(ctx, GitBashExecutor.Config(config)),
25
- "wsl-bash": (ctx, config) => new WslBashExecutor(ctx, WslBashExecutor.Config(config)),
26
- pwsh: (ctx, config) => new SandboxPwshExecutor(ctx, SandboxPwshExecutor.Config(config))
27
- };
28
- /**
29
- * Selector executor over the bash capability seam. Registers as `ctx.shell`
30
- * (the single seat), routes every request to one enabled backend by name, and
31
- * stamps the chosen name on the resolved spec as `shellBackend`. Enabled
32
- * names without a factory fail loud at the first rebuild; routing to an
33
- * unbuilt name throws `SHELL_BACKEND_UNAVAILABLE`.
34
- */
35
- var ShellSelectExecutor = class ShellSelectExecutor extends ShellExecutor {
36
- static inject = [
37
- "subprocess",
38
- "sandbox",
39
- "sandboxPolicy"
40
- ];
41
- static Config = z.object({
42
- backends: z.array(z.string()).default([
43
- "git-bash",
44
- "wsl-bash",
45
- "pwsh"
46
- ]),
47
- default: z.string().default("pwsh"),
48
- gitBash: GitBashExecutor.Config.default({}),
49
- wslBash: WslBashExecutor.Config.default({}),
50
- pwsh: SandboxPwshExecutor.Config.default({})
51
- });
52
- /** Test hook: replace backend construction wholesale (mirrors the sandbox-local internals pattern). */
53
- internals = {};
54
- /** The currently authoritative config: the settings section, or the composition entry. */
55
- source;
56
- backends = /* @__PURE__ */ new Map();
57
- fibers = [];
58
- constructor(ctx, config) {
59
- super(ctx);
60
- const entry = config;
61
- assertServiceableBashConfig(entry.gitBash);
62
- assertServiceableBashConfig(entry.wslBash);
63
- assertServiceablePwshConfig(entry.pwsh);
64
- this.source = () => entry;
65
- ctx.inject(["settings"], (settingsCtx) => {
66
- settingsCtx.settings.installSection(ctx, SHELL_SETTINGS_NAMESPACE, ShellSelectExecutor.Config, entry, {
67
- validate: (value) => {
68
- const v = value;
69
- assertServiceableBashConfig(v.gitBash);
70
- assertServiceableBashConfig(v.wslBash);
71
- assertServiceablePwshConfig(v.pwsh);
72
- },
73
- setSource: (current) => {
74
- this.source = current;
75
- },
76
- onChange: () => {
77
- this.rebuildBackends(this.config);
78
- }
79
- });
80
- });
81
- }
82
- /** Validated config (schemastery applied the defaults before construction). */
83
- get config() {
84
- return this.source();
85
- }
86
- /**
87
- * The first sandbox mode any enabled backend declares (tool-layer
88
- * advertisement). One backend's probe/verdict failure (e.g. explicit
89
- * `sandbox: bwrap` without bubblewrap installed) must not brick the whole
90
- * selector: that backend advertises no mode here and its error resurfaces
91
- * loudly at its first routed command — the documented lazy contract.
92
- */
93
- get sandboxMode() {
94
- this.ensureBackends();
95
- for (const name of this.config.backends) {
96
- let mode;
97
- try {
98
- mode = this.backends.get(name)?.sandboxMode;
99
- } catch {
100
- continue;
101
- }
102
- if (mode !== void 0) return mode;
103
- }
104
- }
105
- resolve(request) {
106
- this.ensureBackends();
107
- const name = request.shell ?? this.config.default;
108
- return {
109
- ...this.requireBackend(name).resolve(request),
110
- shellBackend: name
111
- };
112
- }
113
- run(spec) {
114
- this.ensureBackends();
115
- return this.requireBackend(spec.shellBackend ?? this.config.default).run(spec);
116
- }
117
- start(spec) {
118
- this.ensureBackends();
119
- return this.requireBackend(spec.shellBackend ?? this.config.default).start(spec);
120
- }
121
- requireBackend(name) {
122
- const backend = this.backends.get(name);
123
- if (backend === void 0) throw new HarnessError(`shell-select: backend "${name}" is not enabled (enabled: ${[...this.backends.keys()].join(", ")})`, SHELL_BACKEND_UNAVAILABLE);
124
- return backend;
125
- }
126
- /** The first rebuild runs at first use, after the internals hook is installed. */
127
- ensureBackends() {
128
- if (this.backends.size > 0) return;
129
- this.rebuildBackends(this.config);
130
- }
131
- /**
132
- * Construct one backend per enabled name on its own child fiber with
133
- * isolated shell/settings scopes (see the class doc), or throw for an
134
- * unknown name. Old fibers are disposed first so rebuilds never leak.
135
- */
136
- rebuildBackends(entry) {
137
- for (const fiber of this.fibers.splice(0)) fiber.dispose();
138
- this.backends.clear();
139
- for (const name of entry.backends) {
140
- const factory = this.internals.factories?.[name] ?? DEFAULT_FACTORIES[name];
141
- if (factory === void 0) throw new Error(`shell-select: unknown backend "${name}" (enabled: ${entry.backends.join(", ")})`);
142
- const fiber = this.ctx.plugin({
143
- inject: [
144
- "subprocess",
145
- "sandbox",
146
- "sandboxPolicy"
147
- ],
148
- apply: () => {}
149
- });
150
- this.fibers.push(fiber);
151
- const backendCtx = fiber.ctx.isolate("shell", Symbol(name)).isolate("settings", Symbol(name));
152
- this.backends.set(name, factory(backendCtx, backendConfig(name, entry)));
153
- }
154
- }
155
- };
156
- /**
157
- * The config partition for one backend name. Names without a dedicated
158
- * partition (test-hook factories) receive an empty config; the owning factory
159
- * decides what it needs.
160
- */
161
- function backendConfig(name, entry) {
162
- switch (name) {
163
- case "git-bash": return entry.gitBash;
164
- case "wsl-bash": return entry.wslBash;
165
- case "pwsh": return entry.pwsh;
166
- default: return {};
167
- }
168
- }
169
- //#endregion
170
- export { SHELL_BACKEND_UNAVAILABLE, ShellSelectExecutor, ShellSelectExecutor as default };
@@ -1,23 +0,0 @@
1
- //#region lib/types/invariant.js
2
- /**
3
- * Package-owned invariant companion for `@deepseek-ai/dsh-shell-select`.
4
- * @module @deepseek-ai/dsh-shell-select/invariant
5
- */
6
- const PACKAGE_NAME = "@deepseek-ai/dsh-shell-select";
7
- /** Cordis companion plugin name. */
8
- const name = "shell-select-invariant";
9
- /** Service required before the companion can reserve package ownership. */
10
- const inject = ["invariants"];
11
- /**
12
- * No runtime invariant: this package exposes no independent event sequence or
13
- * mutable data relation beyond contracts enforced at its owning seams.
14
- */
15
- const install = () => {};
16
- /**
17
- * Register this package's invariant companion.
18
- * @param ctx - Cordis context carrying the invariant service.
19
- * @returns the installed registration's disposer after setup succeeds.
20
- */
21
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
- //#endregion
23
- export { apply, inject, name };