Files
deepseek-harness/packages/api/workspace-controller/src/default-directory.ts
T
_Kerman ff1c7d415f fix(workspace): keep the default Workspace path language-neutral
The first-use directory name and its stored title came from the Client's
startup language, so one product decision carried two incompatible
requirements: a path addressed by shell commands, tool arguments, @path
references, and session logs, and a label a reader reads.

The controller now owns the fixed `default-workspace` directory name and
the registry titles the Workspace after that same segment, so
`workspace.initializeDefault` takes no request at all —
WorkspaceInitializeDefaultRequest and its name validation are gone, and
the registry resolver returns a path instead of a path and title. No
language reaches the Host.

Browser consumers label a Workspace through the new
`workspaceDisplayTitle`: a title still equal to the automatic one reads
as the localized default name from the `common` namespace, every other
title reads verbatim. The Workspace browser resolves it once at the store
read, so rows, hover card, search meta, and the rename and delete dialogs
inherit it; the hero picker and the Conversation chip resolve it too.
Confirming a rename stores the text on screen, which is what stops a row
following the language.
2026-09-23 17:46:32 +08:00

79 lines
3.3 KiB
TypeScript

/** Resolve the Host account's Documents directory for first-use Workspace creation. */
import { homedir } from 'node:os'
import { posix, win32 } from 'node:path'
import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command'
import { DEFAULT_WORKSPACE_DIRECTORY } from './default-workspace.ts'
/** Platform observations replaceable in directory-resolution tests. */
interface DocumentsDirectoryInternals {
readonly platform?: NodeJS.Platform
readonly home?: string
readonly run?: NativeCommandRunner
}
/**
* Validate a configured or OS-returned Documents path without resolving it against cwd.
* @param directory - fully qualified directory spelling.
* @param platform - Host platform.
* @returns the normalized directory.
*/
export function validateDocumentsDirectory(directory: string, platform: NodeJS.Platform = process.platform): string {
const paths = platform === 'win32' ? win32 : posix
const root = paths.parse(directory).root
if (!paths.isAbsolute(directory) || (platform === 'win32' && (root === '\\' || root === '/'))) {
throw new Error(`Documents directory must be fully qualified: '${directory}'`)
}
return paths.normalize(directory)
}
/**
* Resolve the first-use directory on the Host without creating files.
* @param documentsDirectory - explicit deployment override for the system Documents directory.
* @param signal - caller lifetime and lookup deadline.
* @param internals - platform facts and native command runner.
* @returns the absolute candidate path.
*/
export async function defaultWorkspaceDirectory(
documentsDirectory: string | undefined,
signal: AbortSignal,
internals: DocumentsDirectoryInternals = {},
): Promise<string> {
const platform = internals.platform ?? process.platform
const paths = platform === 'win32' ? win32 : posix
signal.throwIfAborted()
let directory = documentsDirectory
if (directory === undefined) {
const run = internals.run ?? runNativeCommand
let stdout: string
switch (platform) {
case 'darwin':
({ stdout } = await run('osascript', [
'-e', 'POSIX path of (path to documents folder from user domain without folder creation)',
], signal))
break
case 'win32':
({ stdout } = await run('powershell.exe', [
'-NoLogo', '-NoProfile', '-NonInteractive', '-Command',
'[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); '
+ '[Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments, '
+ '[Environment+SpecialFolderOption]::DoNotVerify)',
], signal))
break
case 'linux':
({ stdout } = await run('xdg-user-dir', ['DOCUMENTS'], signal))
break
default:
throw new Error(`system Documents directory is unavailable on ${platform}`)
}
directory = stdout.replace(/[\r\n]+$/, '')
// XDG reports the home directory when this user directory is disabled.
if (directory === '' || (platform === 'linux' && paths.normalize(directory) === (internals.home ?? homedir()))) {
throw new Error('system Documents directory is unavailable')
}
}
directory = validateDocumentsDirectory(directory, platform)
signal.throwIfAborted()
return paths.join(directory, 'deepseek-harness', DEFAULT_WORKSPACE_DIRECTORY)
}