fix(host,client): gate the picker affordance on the advertised kind; reject non-absolute browse paths
ds-review-bot round 2. The workspace UI never consulted the advertised directoryPicker kind: under a browse (or merge-added) backend it still rendered 'Open local folder…' and called pickDirectory(), which the host answers with directory-picker-unavailable. The create flow now reads directoryPickerKind() per menu open and renders the dialog affordance only under 'dialog' — browse (until its in-app browser UI lands) and unknown kinds hide the entry, realizing the seam's documented default; a keyless workspace-flow snapshot pins the hidden entry over the browse fixture. The browse backend also resolved wire paths, silently rebasing '' or relative parents under the host process cwd; both primitives now reject non-absolute explicit paths with their business codes, and the seam JSDoc carries the contract.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
|
||||
import { mkdir, readdir, stat } from 'node:fs/promises'
|
||||
import { homedir } from 'node:os'
|
||||
import { basename, dirname, join, resolve } from 'node:path'
|
||||
import { basename, dirname, isAbsolute, join, resolve } from 'node:path'
|
||||
import {
|
||||
DirectoryPicker, DirectoryPickerError,
|
||||
} from '@deepseek-ai/dsh-host-directory-picker'
|
||||
@@ -81,6 +81,11 @@ export default class BrowseDirectoryPicker extends DirectoryPicker {
|
||||
|
||||
private async list(path?: string): Promise<DirectoryListing> {
|
||||
const home = homedir()
|
||||
// The seam contract takes absolute paths only; resolve() would silently
|
||||
// rebase a relative or empty wire value under the host process cwd.
|
||||
if (path !== undefined && !isAbsolute(path)) {
|
||||
throw new DirectoryPickerError('directory-unreadable', path, `cannot list "${path}": not an absolute path`)
|
||||
}
|
||||
const target = resolve(path ?? home)
|
||||
let names: { name: string; isDirectory: boolean; isSymbolicLink: boolean }[]
|
||||
try {
|
||||
@@ -100,6 +105,10 @@ export default class BrowseDirectoryPicker extends DirectoryPicker {
|
||||
}
|
||||
|
||||
private async createDirectory(path: string, name: string): Promise<string> {
|
||||
// Same absolute-path fence as list: never rebase a parent under the cwd.
|
||||
if (!isAbsolute(path)) {
|
||||
throw new DirectoryPickerError('directory-create-failed', path, `cannot create under "${path}": not an absolute parent path`)
|
||||
}
|
||||
const parent = resolve(path)
|
||||
// The backend owns segment validation (the wire schema also refuses these,
|
||||
// but direct service consumers must hit the same fence).
|
||||
|
||||
Reference in New Issue
Block a user