chore: snapshot working tree - pty_exited notifications + in-flight inference WIP
feat(booterm): structured pty_exited WS notifications. Plan-validated, impl-validated, code-reviewed green (contracts build clean, contracts test 29/29, booterm + web typecheck clean). wip: in-progress inference/provider refactor (agents.ts, provider.ts, new llama-providers.ts, removed llama-args-validator), plus arena, dispatcher, compaction, schema changes. openspec: pty-exit-notifications complete; x-agent-flags planned (not yet implemented).
This commit is contained in:
55
openspec/changes/boocontrol-ssh-verbmode/design.md
Normal file
55
openspec/changes/boocontrol-ssh-verbmode/design.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Design — BooControl SSH editor verb-mode + model pull
|
||||
|
||||
## Files touched
|
||||
|
||||
- `apps/control/src/services/ssh-config.ts` — add the `RemoteOps` seam + `shellOps`/`wrapperOps`; thread `mode` through `readRemoteConfig`/`applyRemoteConfig`.
|
||||
- `apps/control/src/services/model-pull.ts` (new) — non-blocking pull job runner.
|
||||
- `apps/control/src/routes/ssh-config.ts` — accept `sshMode` in PATCH; pass mode to read/diff/apply; add `POST /api/hosts/:id/pull`.
|
||||
- `apps/control/src/schema.sql` — `ALTER TABLE control_hosts ADD COLUMN IF NOT EXISTS ssh_mode TEXT NOT NULL DEFAULT 'shell'`.
|
||||
- `apps/web/src/components/control/HostConfigEditor.tsx` — SSH-mode selector + Pull-model field.
|
||||
- `apps/control/src/services/__tests__/ssh-config.test.ts` — add wrapper-mode mapping tests (keep existing shell-mode tests).
|
||||
- `apps/control/src/services/__tests__/model-pull.test.ts` (new) — repo-id validation + verb emission.
|
||||
|
||||
## RemoteOps seam
|
||||
|
||||
```ts
|
||||
interface RemoteOps {
|
||||
read(): Promise<string>; // throws on failure
|
||||
backup(now: Date): Promise<string>; // returns backup path
|
||||
write(content: string): Promise<void>; // throws on failure
|
||||
restart(restartCmd: string): Promise<void>;
|
||||
}
|
||||
|
||||
// shell: today's behavior — emits `cat 'p'`, `cp 'p' 'p.bak-ts'`, `cat > 'p'`, restartCmd.
|
||||
function shellOps(target, configPath, exec): RemoteOps
|
||||
// wrapper: emits the verbs `read` / `backup` / `write`(stdin) / `restart`.
|
||||
function wrapperOps(target, exec): RemoteOps
|
||||
```
|
||||
|
||||
`applyRemoteConfig` selects ops from `opts.mode` (default `'shell'`). Shell `backup`
|
||||
computes the name via `backupFilename` then `cp`; wrapper `backup` sends the
|
||||
`backup` verb and reads the returned path from stdout (the wrapper stamps it).
|
||||
Everything else (validate, diff via `computeDiff`, health-wait) is unchanged, so
|
||||
the existing shell-mode tests pass byte-for-byte.
|
||||
|
||||
## Pull job
|
||||
|
||||
`runModelPull({ target, repo, mode }, exec, emitter)`:
|
||||
1. Validate `repo` against `^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$`; reject early.
|
||||
2. `exec(target, 'pull ' + repo)` (wrapper) or `exec(target, 'huggingface-cli download ' + repo + ' --local-dir <modelsDir>/...')` (shell). Wrapper mode is the supported path; shell mode requires a `models_dir` and is best-effort.
|
||||
3. Publish `control_job` frames: `running` at start, `completed`/`failed` at end, `detail.kind = 'pull'`, `detail.repo`, and tail output in `detail.line`.
|
||||
|
||||
Reuses jobType `action` from the existing `ControlJobFrame` (no contracts change).
|
||||
|
||||
## Backward compatibility
|
||||
|
||||
- `ssh_mode` defaults to `shell` -> existing hosts behave exactly as P9.1.
|
||||
- `applyRemoteConfig` `mode` defaults to `shell` -> existing call sites + tests unchanged.
|
||||
- No `control_job` schema change; the web `useControlStream` already accepts `jobType: 'action'`.
|
||||
|
||||
## Validation lenses folded in
|
||||
|
||||
- **V1 (adversarial):** wrapper `backup` must return the path the wrapper chose, not a client-computed one (clock skew between control host and GPU host) -> wrapper `backup` reads stdout.
|
||||
- **V2 (adversarial):** a `wrapper`-mode host without the script must fail loudly -> verbs surface the non-zero exit + stderr per pipeline step; no shell fallback.
|
||||
- **JD1 (junior):** server-side repo validation duplicates the wrapper's -> intentional defense in depth; documented.
|
||||
- **JD2 (junior):** reusing jobType `action` keeps the change additive; a dedicated `pull` type is deferred (would touch contracts + web union) with reopen trigger "if pull needs distinct UI filtering."
|
||||
Reference in New Issue
Block a user