fix!: port the settings page and shell seam to dsh 0.1.7-rc.2
Release / release (push) Successful in 6s
Release / release (push) Successful in 6s
0.1.7-rc.2 moved four things this bundle was built on, and every one of them
failed quietly rather than loudly, so the plugin looked installed and did
nothing:
* `ctx.settings.installSection(...)` is gone. The call threw into a
`try/catch` that only logged a warning, so the settings namespace was never
registered and `configure` had nowhere to persist. An entry's exported
Config IS the form now; the namespace is the Loader entry id the bundle's
patch declares, and `ctx.settings.configure({ auto: false })` is what claims
that entry's own page.
* `settingsScope` and the `settings.plugin.item` slot are gone. The card
registered into both, so it never rendered — the tab draws the intersection
of served namespaces and registered cards, and that intersection was empty.
It now registers into `plugins.bundle.config` keyed by the bundle's package
name, over the `configForms` service, and uses the framework's own form
chrome (`SettingsFormModel` / `SettingsForm` / `SettingsValueField` /
`SettingsSecretField`) instead of the hand-rolled equivalent. A save is one
revision-fenced write.
* `ctx.shell.run(spec)` is gone. Every action that runs a command threw
`shell.run is not a function`; the seam is now
`await ctx.shell.execute(ctx.shell.resolve(request)).result()`.
* A job's `owner` is a session id, not the agent object. The wrong owner
registered a job no session could collect or cancel, and the producer's
`done` resolved `output` where the registry reads `result`, so a finished
job handed back nothing.
Two conformance gaps went with them: a Config field that is not marked
`volatile()` is invisible to every settings UI — the form projection keeps only
volatile fields, and an entry with no form is dropped from `settings.describe`
entirely — and `embeddingApiKey` was a plain field, so the key rode every
settings read back to the browser. It is `role('secret')` now: the browser is
told whether a key is stored and never receives the key.
Also declares the `@deepseek-ai/dsh-*` peerDependencies the compatibility gate
reads, so an incompatible runtime refuses this plugin at startup instead of
half-loading it.
Verified against a real 0.1.7-rc.2 boot: `settings.describe` serves the entry
with `autoGenerate: false`, the key redacted and reported only as `set: true`,
and no startup diagnostics. The host mock in the tests now has no `run` and no
`installSection`, so those seams are contract-tested rather than assumed, and
the browser tests read the installed harness's own slot catalog and package
exports — a rename fails a test instead of quietly emptying a page.
This commit is contained in:
1 parent
ffefc42e01
commit
1b2ab3613f
8 files changed
+1549
-689
No files matched your search
@@ -112,6 +112,48 @@ export function commandLine(argv) {
|
||||
return argv.map(shellQuote).join(' ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Run one command through the harness shell seam.
|
||||
*
|
||||
* The seam is `execute(resolve(request))`, whose handle carries the foreground
|
||||
* projection as `result()`. That is deliberately two steps: the handle is the
|
||||
* background-capable object, and awaiting `result()` is what makes a run
|
||||
* foreground. The single-step `run(spec)` this plugin used before
|
||||
* 0.1.7-rc.2 no longer exists on `ShellExecutor`, so calling it threw
|
||||
* `shell.run is not a function` on every action that needed a command.
|
||||
*
|
||||
* `result()` rejects only for infrastructure failures (a spawn that never
|
||||
* produced a process); non-zero exits, timeout kills and abort kills resolve
|
||||
* with a descriptive result. The returned shape is the plugin's own flattened
|
||||
* form, so callers never have to know which generation of the seam they are on.
|
||||
*
|
||||
* @param {{ execute: Function, resolve: Function }} shell the `ctx.shell` service
|
||||
* @param {object} request a `ShellExecRequest`
|
||||
* @returns {Promise<{
|
||||
* exitCode: number | null,
|
||||
* signal: string | null,
|
||||
* timedOut: boolean,
|
||||
* aborted: boolean,
|
||||
* timeoutMs: number,
|
||||
* sandbox: object | undefined,
|
||||
* stdout: { text: string, truncated: boolean },
|
||||
* stderr: { text: string, truncated: boolean },
|
||||
* }>}
|
||||
*/
|
||||
export async function runShell(shell, request) {
|
||||
const result = await (await shell.execute(shell.resolve(request))).result()
|
||||
return {
|
||||
exitCode: result.exitCode,
|
||||
signal: result.signal,
|
||||
timedOut: result.timedOut,
|
||||
aborted: result.aborted,
|
||||
timeoutMs: result.timeoutMs,
|
||||
sandbox: result.sandbox,
|
||||
stdout: { text: result.stdout.text, truncated: result.stdout.truncated },
|
||||
stderr: { text: result.stderr.text, truncated: result.stderr.truncated },
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slopo arguments for one action.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user