@banbolee/dsh-fish-shell 0.7.0 → 0.8.1
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.
- package/CHANGELOG.md +91 -0
- package/README.md +318 -13
- package/README.zh.md +101 -4
- package/THIRD-PARTY-NOTICES.md +91 -0
- package/cordis.patch.yml +56 -0
- package/node_modules/@deepseek-ai/cosmokit/LICENSE +21 -0
- package/node_modules/@deepseek-ai/cosmokit/README.md +30 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/index.js +418 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/array.d.ts +15 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/array.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/index.d.ts +13 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/index.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/misc.d.ts +41 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/misc.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/string.d.ts +89 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/string.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/time.d.ts +19 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/time.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/types.d.ts +42 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/types.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/volatile.d.ts +40 -0
- package/node_modules/@deepseek-ai/cosmokit/lib/types/volatile.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/cosmokit/package.json +32 -0
- package/node_modules/@deepseek-ai/cosmokit/src/array.ts +42 -0
- package/node_modules/@deepseek-ai/cosmokit/src/index.ts +12 -0
- package/node_modules/@deepseek-ai/cosmokit/src/misc.ts +78 -0
- package/node_modules/@deepseek-ai/cosmokit/src/string.ts +113 -0
- package/node_modules/@deepseek-ai/cosmokit/src/time.ts +92 -0
- package/node_modules/@deepseek-ai/cosmokit/src/types.ts +168 -0
- package/node_modules/@deepseek-ai/cosmokit/src/volatile.ts +86 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/LICENSE +21 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/README.i18n.yaml +100 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/README.md +186 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/README.zh.md +186 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/lib/index.js +646 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/lib/types/background.d.ts +20 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/lib/types/index.d.ts +32 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/lib/types/render.d.ts +81 -0
- package/node_modules/@deepseek-ai/dsh-tool-terminal/package.json +63 -0
- package/node_modules/@deepseek-ai/schemastery/LICENSE +21 -0
- package/node_modules/@deepseek-ai/schemastery/README.md +393 -0
- package/node_modules/@deepseek-ai/schemastery/lib/index.cjs +635 -0
- package/node_modules/@deepseek-ai/schemastery/lib/index.mjs +635 -0
- package/node_modules/@deepseek-ai/schemastery/lib/types/index.d.ts +211 -0
- package/node_modules/@deepseek-ai/schemastery/lib/types/index.d.ts.map +1 -0
- package/node_modules/@deepseek-ai/schemastery/package.json +39 -0
- package/node_modules/@deepseek-ai/schemastery/src/index.ts +962 -0
- package/node_modules/@standard-schema/spec/LICENSE +21 -0
- package/node_modules/@standard-schema/spec/README.md +198 -0
- package/node_modules/@standard-schema/spec/dist/index.cjs +18 -0
- package/node_modules/@standard-schema/spec/dist/index.d.cts +119 -0
- package/node_modules/@standard-schema/spec/dist/index.d.ts +119 -0
- package/node_modules/@standard-schema/spec/dist/index.js +0 -0
- package/node_modules/@standard-schema/spec/package.json +52 -0
- package/package.json +56 -42
- package/terminal-tools.js +259 -0
- package/terminal.js +297 -0
- package/tool.js +1 -1
package/terminal.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L2 interactive PTY backend: the official `@deepseek-ai/dsh-terminal-bash`
|
|
3
|
+
* `BashTerminalBackend` with ONE addition — the harness home contract
|
|
4
|
+
* (`DSH_HOME`, and `DSH_PROFILE` / `DSH_PROFILE_DIR` when the context carries
|
|
5
|
+
* them) in the PTY child environment.
|
|
6
|
+
*
|
|
7
|
+
* Why it exists: an interactive session's child environment is the backend's
|
|
8
|
+
* own overlay (`TERM=dumb`, `PAGER=cat`, `GIT_PAGER=cat`, `DSH_SHELL`,
|
|
9
|
+
* `DSH_SESSION_ID`, `DSH_PTY_SESSION_ID`) layered on `scrubbedParentEnv()`,
|
|
10
|
+
* which drops EVERY inherited `DSH_*` name. The one-shot `fish` tool instead
|
|
11
|
+
* runs with the trusted snapshot of the `ctx.shellEnv` registry, merged after
|
|
12
|
+
* that same scrub. The two surfaces therefore disagreed about the harness
|
|
13
|
+
* home: a `dsh` nested inside an interactive session resolved `~/.dsh` (an
|
|
14
|
+
* older home with older plugin state) while the one-shot tool resolved the
|
|
15
|
+
* real harness home. This module puts both surfaces on the same home facts.
|
|
16
|
+
*
|
|
17
|
+
* How the injection stays on public seams: the base class builds the overlay
|
|
18
|
+
* with its module-private `childEnvironment()` and hands the finished terminal
|
|
19
|
+
* spec to its `spawnTerminal` CONSTRUCTOR parameter. This subclass passes a
|
|
20
|
+
* wrapper for that parameter (the official default is
|
|
21
|
+
* `(spec) => ctx.subprocess.spawnTerminal(spec)`), and the spec's `env` field
|
|
22
|
+
* is documented as "explicit environment layered after the provider's ambient
|
|
23
|
+
* scrub" — exactly where a deliberately forwarded `DSH_*` fact belongs. No
|
|
24
|
+
* official module internal, no private field and no monkey-patch is involved:
|
|
25
|
+
* argv confinement, the sandbox-mode fence, `session.initialize()` readiness,
|
|
26
|
+
* the `-C` fish prompt setup, the session factory and the config surface
|
|
27
|
+
* (`Config` is re-exported verbatim, so `backendType` keeps its `shell`
|
|
28
|
+
* default) are all the base class's own code.
|
|
29
|
+
*
|
|
30
|
+
* Where the values come from: the SAME registry the one-shot shell tools use —
|
|
31
|
+
* `ctx.shellEnv.collect()`, whose snapshot `dsh-bash-local` merges into a
|
|
32
|
+
* one-shot execution — so the two cannot drift. The three home facts are
|
|
33
|
+
* execution-independent by construction: `collect` reads `execution.agent`
|
|
34
|
+
* only to add `DSH_SESSION_ID`, and the profile facts come from the context.
|
|
35
|
+
* The snapshot is therefore taken at spawn time with an empty execution, and
|
|
36
|
+
* the session facts stay the PTY overlay's own official values: this module
|
|
37
|
+
* never rewrites `DSH_SHELL` / `DSH_SESSION_ID` / `DSH_PTY_SESSION_ID`.
|
|
38
|
+
*
|
|
39
|
+
* `DSH_SESSION_ID` / `DSH_PTY_SESSION_ID` are deliberately KEPT on a newly
|
|
40
|
+
* opened shell. Evidence: they are the official PTY facts, produced by
|
|
41
|
+
* `dsh-terminal-bash` (`spec.owner.id` / `spec.sessionId`) and by
|
|
42
|
+
* `dsh-api-terminal-controller` (`agent.id`) for the GUI terminal panel, and
|
|
43
|
+
* grep over the installed harness finds no reader of either name (only those
|
|
44
|
+
* two producers and the registry's reserved-key list). Removing them would be
|
|
45
|
+
* a deviation with no evidence behind it. What would change that: an in-tree
|
|
46
|
+
* consumer that reads the CURRENT session's identity out of these variables
|
|
47
|
+
* (e.g. a plugin resolving its caller from `$DSH_SESSION_ID`), or a nested-boot
|
|
48
|
+
* failure traced to the inherited value.
|
|
49
|
+
*
|
|
50
|
+
* Fallback, and its exact boundary: the overlay always comes from exactly ONE
|
|
51
|
+
* source. When the registry is NOT mounted at all (`ctx.get('shellEnv')` is
|
|
52
|
+
* `undefined` — a custom composition outside `dsh-base`), the three facts are
|
|
53
|
+
* read from their public owners: `resolveDshHome()` and
|
|
54
|
+
* `ctx.get('profileContext')`, which are the registry's own sources, so the
|
|
55
|
+
* values are byte-identical unless a profile configures `dshHome` on its
|
|
56
|
+
* `shell-env` row — that row is visible to the registry path only. When the
|
|
57
|
+
* service IS mounted (even if its snapshot declares fewer than three keys) the
|
|
58
|
+
* registry is the only source: nothing is merged in per key, and a throwing
|
|
59
|
+
* `collect()` PROPAGATES, because the one-shot shell path collects fail-loud
|
|
60
|
+
* too and swallowing that error would silently resolve a DIFFERENT home (the
|
|
61
|
+
* ambient `DSH_HOME`) than the one the registry recorded — the exact failure
|
|
62
|
+
* this module exists to remove.
|
|
63
|
+
*
|
|
64
|
+
* The config surface is the official one in behaviour, not only in shape: the
|
|
65
|
+
* `apply` below runs the official `resolveConfig`/`validateConfig` semantics
|
|
66
|
+
* (replicated as {@link resolveTerminalConfig} / {@link validateTerminalConfig},
|
|
67
|
+
* because the official package does not export them), so an empty
|
|
68
|
+
* `shellPath`/`shellArgs` gets the dialect defaults and an invalid duration is
|
|
69
|
+
* rejected rather than accepted. A parity test drives the official `apply` and
|
|
70
|
+
* this one with the same inputs and compares the effective config and the
|
|
71
|
+
* validation outcome.
|
|
72
|
+
*
|
|
73
|
+
* Upstream note: this subclass is only here because the official row exposes
|
|
74
|
+
* no `env` config field. If `@deepseek-ai/dsh-terminal-bash` ever grows one
|
|
75
|
+
* (or accepts an environment overlay from the registry itself), the patch row
|
|
76
|
+
* can go back to the official package and this module can be deleted — together
|
|
77
|
+
* with the `NESTED_DSH_ENTRY` contract in
|
|
78
|
+
* `tests/docs-shape-fish-shell.spec.ts`, which names this module.
|
|
79
|
+
*
|
|
80
|
+
* @module @banbolee/dsh-fish-shell/terminal
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
import { BashTerminalBackend, Config } from '@deepseek-ai/dsh-terminal-bash'
|
|
84
|
+
import { DSH_HOME_ENV, resolveDshHome } from '@deepseek-ai/dsh-home-paths'
|
|
85
|
+
import { resolvePwshPath } from '@deepseek-ai/dsh-pwsh-local'
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The official backend's config schema, re-exported verbatim so cordis
|
|
89
|
+
* resolves this row with exactly the official defaults (`backendType: 'shell'`
|
|
90
|
+
* included) and the row keeps the official config surface. Also imported
|
|
91
|
+
* above, where `apply` uses it to materialize those defaults for a direct,
|
|
92
|
+
* loader-free call.
|
|
93
|
+
*/
|
|
94
|
+
export { Config }
|
|
95
|
+
|
|
96
|
+
/** Cordis plugin name (the bundle patch decides the row id and mount point). */
|
|
97
|
+
export const name = 'terminal-home'
|
|
98
|
+
/**
|
|
99
|
+
* Required services: verbatim the official backend's list. The inherited
|
|
100
|
+
* `spawn()` needs all four; declaring the same list keeps activation identical
|
|
101
|
+
* to the official row.
|
|
102
|
+
*/
|
|
103
|
+
export const inject = [
|
|
104
|
+
'terminals',
|
|
105
|
+
'sandboxPolicy',
|
|
106
|
+
'sessionProjections',
|
|
107
|
+
'subprocess',
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
/** Environment variable holding the profile facts the registry snapshots. */
|
|
111
|
+
const DSH_PROFILE_KEY = 'DSH_PROFILE'
|
|
112
|
+
/** Environment variable holding the profile directory the registry snapshots. */
|
|
113
|
+
const DSH_PROFILE_DIR_KEY = 'DSH_PROFILE_DIR'
|
|
114
|
+
/** The home contract this module forwards, in the order it is documented. */
|
|
115
|
+
const HOME_CONTRACT_KEYS = /** @type {const} */ ([DSH_HOME_ENV, DSH_PROFILE_KEY, DSH_PROFILE_DIR_KEY])
|
|
116
|
+
|
|
117
|
+
/** Bash dialect default executable — the official `DEFAULT_BASH_SHELL`. */
|
|
118
|
+
const DEFAULT_BASH_SHELL = '/bin/bash'
|
|
119
|
+
/** Bash dialect default arguments (interactive, profile-free) — the official `DEFAULT_BASH_ARGS`. */
|
|
120
|
+
const DEFAULT_BASH_ARGS = ['--noprofile', '--norc', '-i']
|
|
121
|
+
/** Pwsh dialect default arguments — the official `DEFAULT_PWSH_ARGS`. */
|
|
122
|
+
const DEFAULT_PWSH_ARGS = ['-NoLogo', '-NoProfile']
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The official `resolveConfig`, replicated from
|
|
126
|
+
* `@deepseek-ai/dsh-terminal-bash@0.2.0-rc.2` (`lib/index.js:27-35`; the
|
|
127
|
+
* official package does not export it). An unset or EMPTY
|
|
128
|
+
* `shellPath`/`shellArgs` selects the dialect's defaults — schemastery
|
|
129
|
+
* materializes an absent optional array as `[]` — while a non-empty explicit
|
|
130
|
+
* value always wins. This module re-exports the official schema, so both
|
|
131
|
+
* `apply`s must materialize the same effective config; the parity test drives
|
|
132
|
+
* the official `apply` and this one side by side. Keep in sync with the
|
|
133
|
+
* official source: a change there changes this row's config contract.
|
|
134
|
+
* @param {import('@deepseek-ai/dsh-terminal-bash').TerminalLocalConfig} config - the schemastery-resolved plugin configuration.
|
|
135
|
+
* @returns {ConstructorParameters<typeof BashTerminalBackend>[1]} the fully resolved configuration.
|
|
136
|
+
*/
|
|
137
|
+
export function resolveTerminalConfig(config) {
|
|
138
|
+
const shellDialect = config.shellDialect ?? 'bash'
|
|
139
|
+
return /** @type {ConstructorParameters<typeof BashTerminalBackend>[1]} */ ({
|
|
140
|
+
...config,
|
|
141
|
+
shellDialect,
|
|
142
|
+
shellPath: config.shellPath !== undefined && config.shellPath.length > 0
|
|
143
|
+
? config.shellPath
|
|
144
|
+
: shellDialect === 'pwsh' ? resolvePwshPath() : DEFAULT_BASH_SHELL,
|
|
145
|
+
shellArgs: config.shellArgs !== undefined && config.shellArgs.length > 0
|
|
146
|
+
? config.shellArgs
|
|
147
|
+
: shellDialect === 'pwsh' ? DEFAULT_PWSH_ARGS : DEFAULT_BASH_ARGS,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The official `validateConfig`, replicated from
|
|
153
|
+
* `@deepseek-ai/dsh-terminal-bash@0.2.0-rc.2` (`lib/index.js:62-74`; not
|
|
154
|
+
* exported) with the official error text verbatim — including the
|
|
155
|
+
* `terminal-bash:` prefix, which is deliberate: the row's config surface IS the
|
|
156
|
+
* official schema, and parity with the official validator (messages compared
|
|
157
|
+
* byte for byte) is a tested contract. Every numeric field is a positive safe
|
|
158
|
+
* integer except `promptTailGraceMs`, whose zero is the documented "no
|
|
159
|
+
* extension" value and which is therefore checked on its own. Keep in sync with
|
|
160
|
+
* the official source.
|
|
161
|
+
* @param {ConstructorParameters<typeof BashTerminalBackend>[1]} config - the resolved configuration.
|
|
162
|
+
* @returns {void} throws on a value the backend cannot run with.
|
|
163
|
+
*/
|
|
164
|
+
export function validateTerminalConfig(config) {
|
|
165
|
+
if (config.backendType.length === 0) throw new Error('terminal-bash: backendType must be non-empty')
|
|
166
|
+
if (config.shellPath.length === 0) throw new Error('terminal-bash: shellPath must be non-empty')
|
|
167
|
+
for (const [name, value] of Object.entries(config)) {
|
|
168
|
+
if (name === 'promptTailGraceMs') continue
|
|
169
|
+
if (typeof value === 'number' && (!Number.isSafeInteger(value) || value <= 0)) {
|
|
170
|
+
throw new Error(`terminal-bash: ${name} must be a positive safe integer`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (typeof config.promptTailGraceMs === 'number' && (!Number.isSafeInteger(config.promptTailGraceMs) || config.promptTailGraceMs < 0)) {
|
|
174
|
+
throw new Error('terminal-bash: promptTailGraceMs must be a non-negative safe integer')
|
|
175
|
+
}
|
|
176
|
+
if (config.maxReadBytes > config.scrollbackMaxBytes) throw new Error('terminal-bash: maxReadBytes must not exceed scrollbackMaxBytes')
|
|
177
|
+
if (config.handoffGraceMs < config.pollIntervalMs) throw new Error('terminal-bash: handoffGraceMs must be at least pollIntervalMs so one readiness poll runs inside the grace window')
|
|
178
|
+
if (config.promptTailGraceMs !== 0 && config.promptTailGraceMs < config.pollIntervalMs) throw new Error('terminal-bash: promptTailGraceMs must be zero or at least pollIntervalMs so a nonzero tolerance contains one readiness poll')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The public profile facts `ctx.get('profileContext')` carries. Deliberately
|
|
183
|
+
* structural: the service belongs to the launcher, so this module reads the
|
|
184
|
+
* two fields it forwards instead of importing its owner's types.
|
|
185
|
+
* @typedef {{ name?: string, dir?: string } | undefined} ProfileFacts
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The registry's snapshot of the home contract, or `undefined` when the
|
|
190
|
+
* registry is NOT mounted (`ctx.get('shellEnv') === undefined`, i.e. a custom
|
|
191
|
+
* composition outside `dsh-base`). The distinction is load-bearing: a mounted
|
|
192
|
+
* registry is the ONLY source, so a key its snapshot omits stays omitted
|
|
193
|
+
* instead of being filled in from another source.
|
|
194
|
+
*
|
|
195
|
+
* `collect` is called with an empty execution on purpose: of the three keys
|
|
196
|
+
* this module forwards, none depends on the execution (only `DSH_SESSION_ID`
|
|
197
|
+
* does, and that one stays the PTY overlay's own value). A throwing `collect`
|
|
198
|
+
* is NOT swallowed either: once the registry is mounted it is the only source
|
|
199
|
+
* that can see a configured `dshHome`, and quietly falling back would resolve a
|
|
200
|
+
* different home — exactly the drift this module removes. It fails loud here
|
|
201
|
+
* for the same reason the one-shot path (`dsh-tool-bash`) does.
|
|
202
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx - the backend's plugin context.
|
|
203
|
+
* @returns {Record<string, string> | undefined} the home facts the registry declares, or `undefined` when it is not mounted.
|
|
204
|
+
*/
|
|
205
|
+
function registryHomeContract(ctx) {
|
|
206
|
+
/** @type {import('@deepseek-ai/dsh-shell-env').ShellEnvRegistry | undefined} */
|
|
207
|
+
const registry = ctx.get('shellEnv')
|
|
208
|
+
if (registry === undefined) return undefined
|
|
209
|
+
const snapshot = registry.collect(
|
|
210
|
+
/** @type {import('@deepseek-ai/dsh-tools').ToolExecution} */ ({}),
|
|
211
|
+
)
|
|
212
|
+
/** @type {Record<string, string>} */
|
|
213
|
+
const facts = {}
|
|
214
|
+
for (const key of HOME_CONTRACT_KEYS) {
|
|
215
|
+
const value = snapshot[key]
|
|
216
|
+
if (value !== undefined) facts[key] = value
|
|
217
|
+
}
|
|
218
|
+
return facts
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The harness home contract for one PTY child, from exactly ONE source:
|
|
223
|
+
*
|
|
224
|
+
* - the mounted `ctx.shellEnv` registry (the one-shot shell tools' own
|
|
225
|
+
* source) — its declared facts verbatim, including the case where it
|
|
226
|
+
* declares fewer than three (the official registry always declares
|
|
227
|
+
* `DSH_HOME`, and declares the profile pair whenever a `profileContext`
|
|
228
|
+
* exists, so an incomplete snapshot means a foreign implementation: the
|
|
229
|
+
* honest reading is then "this environment does not carry that fact", not
|
|
230
|
+
* "borrow it from somewhere else");
|
|
231
|
+
* - only when that service is NOT mounted, the public owners
|
|
232
|
+
* `dsh-home-paths` / `profileContext`, which are the registry's own sources.
|
|
233
|
+
*
|
|
234
|
+
* A `collect` error propagates rather than switching sources — see the module
|
|
235
|
+
* doc for both rules.
|
|
236
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx - the backend's plugin context.
|
|
237
|
+
* @returns {Record<string, string>} the environment overlay to merge into the child environment.
|
|
238
|
+
*/
|
|
239
|
+
export function harnessHomeEnvironment(ctx) {
|
|
240
|
+
const fromRegistry = registryHomeContract(ctx)
|
|
241
|
+
if (fromRegistry !== undefined) return fromRegistry
|
|
242
|
+
/** @type {ProfileFacts} */
|
|
243
|
+
const profile = ctx.get('profileContext')
|
|
244
|
+
/** @type {Record<string, string>} */
|
|
245
|
+
const environment = { [DSH_HOME_ENV]: resolveDshHome() }
|
|
246
|
+
if (profile?.name !== undefined) environment[DSH_PROFILE_KEY] = profile.name
|
|
247
|
+
if (profile?.dir !== undefined) environment[DSH_PROFILE_DIR_KEY] = profile.dir
|
|
248
|
+
return environment
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* One terminal spawn spec with the home contract merged into its environment:
|
|
253
|
+
* the same request object, one layer further down than the base class's own
|
|
254
|
+
* overlay (the provider applies `spec.env` after its ambient scrub).
|
|
255
|
+
* @param {import('@deepseek-ai/dsh-subprocess').SubprocessTerminalSpawnSpec} spec - the terminal request the base class built.
|
|
256
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx - the backend's plugin context.
|
|
257
|
+
* @returns {import('@deepseek-ai/dsh-subprocess').SubprocessTerminalSpawnSpec} the same request with the home facts added.
|
|
258
|
+
*/
|
|
259
|
+
export function withHarnessHome(spec, ctx) {
|
|
260
|
+
return { ...spec, env: { ...spec.env, ...harnessHomeEnvironment(ctx) } }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The official local PTY backend with the harness home contract in its child
|
|
265
|
+
* environment. `spawn()` is NOT overridden: argv confinement, the sandbox-mode
|
|
266
|
+
* fence, readiness through `session.initialize()` and the `-C` prompt setup
|
|
267
|
+
* are the base class's own.
|
|
268
|
+
*/
|
|
269
|
+
export class HarnessHomeTerminalBackend extends BashTerminalBackend {
|
|
270
|
+
/**
|
|
271
|
+
* Wrap the base class's `spawnTerminal` seam so every terminal request it
|
|
272
|
+
* builds carries the home contract. The seam is deliberately not a
|
|
273
|
+
* constructor parameter: the row must not be able to lose the contract.
|
|
274
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx - the harness context.
|
|
275
|
+
* @param {ConstructorParameters<typeof BashTerminalBackend>[1]} config - the resolved official backend configuration.
|
|
276
|
+
* @param {ConstructorParameters<typeof BashTerminalBackend>[3]} [createSession] - the base class's session-factory seam, for tests; omitted in production, where the base default builds the `LocalPtySession`.
|
|
277
|
+
*/
|
|
278
|
+
constructor(ctx, config, createSession) {
|
|
279
|
+
super(ctx, config, (spec) => ctx.subprocess.spawnTerminal(withHarnessHome(spec, ctx)), createSession)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Register the home-carrying local PTY backend under the configured type
|
|
285
|
+
* (`shell` unless the row overrides it).
|
|
286
|
+
* @param {import('@deepseek-ai/cordis').Context} ctx - the harness context.
|
|
287
|
+
* @param {import('@deepseek-ai/dsh-terminal-bash').TerminalLocalConfig} [config] - optional backend configuration.
|
|
288
|
+
*/
|
|
289
|
+
export function apply(ctx, config = {}) {
|
|
290
|
+
// The exported `Config` is the official schema, so cordis has already
|
|
291
|
+
// resolved this object; resolving again keeps a direct `apply()` call (tests,
|
|
292
|
+
// direct assembly) on the same defaults. The two explicit steps after it are
|
|
293
|
+
// the official `apply`'s own: dialect defaulting, then validation.
|
|
294
|
+
const resolved = resolveTerminalConfig(Config(config))
|
|
295
|
+
validateTerminalConfig(resolved)
|
|
296
|
+
ctx.terminals.registerBackend(new HarnessHomeTerminalBackend(ctx, resolved))
|
|
297
|
+
}
|
package/tool.js
CHANGED
|
@@ -505,7 +505,7 @@ function fishParameters(escalationModes) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
/** Canonical output declaration: a background acknowledgment or a foreground
|
|
508
|
-
* run. The rc.
|
|
508
|
+
* run. The rc.2 ToolRuntime dialect forbids `type`/`additionalProperties`
|
|
509
509
|
* beside a `oneOf` root (exact-one branches), so the union is expressed as a
|
|
510
510
|
* bare `oneOf` with each branch carrying its own object shape.
|
|
511
511
|
* @type {import('@deepseek-ai/dsh-tools').JsonSchemaNode} */
|