mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
feat(util): add workspace path helpers
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: f255e38fdbc3c5831a625510110bb8a52ea280ad
|
||||
config-catalog.zh.md: f1f6774d957858e452f7120baeb78ef1712c6543
|
||||
config-catalog.md: 2fa4a59d00867c4be168347a4e1dfd827ea443f4
|
||||
config-catalog.zh.md: c2d64b1a5cef1fa20a37d954afc36f29cd3147c9
|
||||
|
||||
@@ -3375,4 +3375,5 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
|
||||
- `@deepseek-ai/dsh-typert-protocol` ([`packages/typert/protocol/src/index.ts`](../packages/typert/protocol/src/index.ts))
|
||||
- `@deepseek-ai/dsh-typert-registry` ([`packages/typert/registry/src/index.ts`](../packages/typert/registry/src/index.ts))
|
||||
- `@deepseek-ai/dsh-util-crypto` ([`packages/util/crypto/src/index.ts`](../packages/util/crypto/src/index.ts))
|
||||
- `@deepseek-ai/dsh-util-workspace-path` ([`packages/util/workspace-path/src/index.ts`](../packages/util/workspace-path/src/index.ts))
|
||||
- `@deepseek-ai/dsh-win32-process` ([`packages/subprocess/win32-process/src/index.ts`](../packages/subprocess/win32-process/src/index.ts))
|
||||
|
||||
@@ -3376,4 +3376,5 @@ export interface Config {
|
||||
- `@deepseek-ai/dsh-typert-protocol`([`packages/typert/protocol/src/index.ts`](../packages/typert/protocol/src/index.ts))
|
||||
- `@deepseek-ai/dsh-typert-registry`([`packages/typert/registry/src/index.ts`](../packages/typert/registry/src/index.ts))
|
||||
- `@deepseek-ai/dsh-util-crypto`([`packages/util/crypto/src/index.ts`](../packages/util/crypto/src/index.ts))
|
||||
- `@deepseek-ai/dsh-util-workspace-path`([`packages/util/workspace-path/src/index.ts`](../packages/util/workspace-path/src/index.ts))
|
||||
- `@deepseek-ai/dsh-win32-process`([`packages/subprocess/win32-process/src/index.ts`](../packages/subprocess/win32-process/src/index.ts))
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/util/README.md
|
||||
README.md: 7867f6c235dfd3cb063214381ac2f40ed0f26ce3
|
||||
README.zh.md: 33ca9912f3670dfd3419b7a2a33f462cb4be95a3
|
||||
README.md: a4bdee07492b268901a7bc03b416e58bde44e21b
|
||||
README.zh.md: 247c60dd9dc61d390b39efb50b2d0804cebbe3b7
|
||||
|
||||
@@ -12,3 +12,4 @@ These zero-dependency packages provide small primitives shared by multiple capab
|
||||
| [`retention/`](output-retention/README.md) | Bounds retained text and item collections |
|
||||
| [`atomic-write/`](atomic-write/README.md) | Replaces files atomically |
|
||||
| [`native-command/`](native-command/README.md) | Runs host-native commands without a shell |
|
||||
| [`workspace-path/`](workspace-path/README.md) | Provides browser-safe Workspace path and display helpers |
|
||||
|
||||
@@ -12,3 +12,4 @@
|
||||
| [`retention/`](output-retention/README.zh.md) | 限制保留文本和项集合的大小 |
|
||||
| [`atomic-write/`](atomic-write/README.zh.md) | 以原子方式替换文件 |
|
||||
| [`native-command/`](native-command/README.zh.md) | 不经 shell 运行宿主原生命令 |
|
||||
| [`workspace-path/`](workspace-path/README.zh.md) | 提供浏览器可用的 Workspace 路径与展示辅助函数 |
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/util/workspace-path/README.md
|
||||
README.md: 2777a3d32387a414aa1b2f406dc3a1f4900e7f43
|
||||
README.zh.md: 708c0e7c367fd8420e3732ab4f356c1200e6bce1
|
||||
@@ -0,0 +1,10 @@
|
||||
# dsh-util-workspace-path
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Browser-safe path helpers shared by Workspace-facing client and controller packages. The package joins Workspace-relative paths, abbreviates POSIX home directories for display, and derives Workspace titles from POSIX or Windows paths. It has no Cordis service or runtime state.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Resolution is lexical** — it recognizes POSIX absolute paths, Windows drive paths, and UNC paths but does not access a filesystem or canonicalize `.` and `..` segments.
|
||||
- **Home abbreviation is POSIX-only** — Windows paths remain unchanged because a portable browser cannot infer Windows home-path equivalence safely.
|
||||
@@ -0,0 +1,10 @@
|
||||
# dsh-util-workspace-path
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
供 Workspace 相关客户端和控制器包共享、可在浏览器使用的路径辅助函数。该包负责拼接 Workspace 相对路径、缩写用于展示的 POSIX 主目录,以及从 POSIX 或 Windows 路径提取 Workspace 标题;它不提供 Cordis service,也不持有运行时状态。
|
||||
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **路径解析仅处理字面值**——它识别 POSIX 绝对路径、Windows 盘符路径和 UNC 路径,但不访问文件系统,也不规范化 `.` 与 `..` 路径段。
|
||||
- **主目录缩写仅支持 POSIX**——Windows 路径保持不变,因为可移植浏览器无法安全推断 Windows 主目录路径等价关系。
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-util-workspace-path",
|
||||
"description": "Browser-safe Workspace path and display helpers",
|
||||
"version": "0.1.1-rc.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
||||
"directory": "packages/util/workspace-path"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./invariant": {
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Browser-safe Workspace path and display helpers.
|
||||
* @module @deepseek-ai/dsh-util-workspace-path
|
||||
*/
|
||||
|
||||
/** Whether a path uses a Windows drive or UNC prefix. */
|
||||
function isWindowsStylePath(value: string): boolean {
|
||||
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith('\\\\')
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
|
||||
* @param cwd - Session Workspace root, when known.
|
||||
* @param path - Absolute or Workspace-relative path.
|
||||
* @returns an absolute path when a Workspace root is available, otherwise the original path.
|
||||
*/
|
||||
export function resolveWorkspacePath(cwd: string | undefined, path: string): string {
|
||||
if (path.startsWith('/') || isWindowsStylePath(path)) return path
|
||||
if (cwd === undefined || cwd === '') return path
|
||||
const base = cwd.replace(/[/\\]+$/, '')
|
||||
const relative = path.replace(/^[/\\]+/, '')
|
||||
return `${base}/${relative}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Abbreviate a POSIX home directory for display.
|
||||
* @param path - Absolute or already-short display path.
|
||||
* @param home - Host account home; absent skips abbreviation.
|
||||
* @returns `~` or `~/…` for the POSIX home and its descendants, otherwise `path`.
|
||||
*/
|
||||
export function abbreviateHomePath(path: string, home?: string): string {
|
||||
if (home === undefined || home === '') return path
|
||||
if (isWindowsStylePath(path) || isWindowsStylePath(home)) return path
|
||||
const root = home.replace(/\/+$/, '')
|
||||
if (root === '' || root === '/') return path
|
||||
if (path.replace(/\/+$/, '') === root) return '~'
|
||||
if (path.startsWith(`${root}/`)) return `~${path.slice(root.length)}`
|
||||
return path
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the final non-empty segment of a Workspace path for display.
|
||||
* @param path - Workspace directory path using POSIX or Windows separators.
|
||||
* @returns the final segment, or an empty string for a separator-only path.
|
||||
*/
|
||||
export function workspaceTitleOf(path: string): string {
|
||||
return path.replace(/[/\\]+$/, '').split(/[/\\]/).pop() ?? ''
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Package-owned invariant companion for `@deepseek-ai/dsh-util-workspace-path`.
|
||||
* @module @deepseek-ai/dsh-util-workspace-path/invariant
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start */
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-util-workspace-path'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'workspace-path-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** No runtime invariant: this utility owns no mutable runtime relationship. */
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
/**
|
||||
* Register this package's invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
/* jscpd:ignore-end */
|
||||
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
abbreviateHomePath, resolveWorkspacePath, workspaceTitleOf,
|
||||
} from '@deepseek-ai/dsh-util-workspace-path'
|
||||
|
||||
describe('Workspace path helpers', () => {
|
||||
it('resolves relative paths without changing absolute paths', () => {
|
||||
expect(resolveWorkspacePath('/w', 'src/a.ts')).toBe('/w/src/a.ts')
|
||||
expect(resolveWorkspacePath('/w/', '/abs/a.ts')).toBe('/abs/a.ts')
|
||||
expect(resolveWorkspacePath(undefined, 'src/a.ts')).toBe('src/a.ts')
|
||||
expect(resolveWorkspacePath('', 'src/a.ts')).toBe('src/a.ts')
|
||||
expect(resolveWorkspacePath('/w', 'C:\\x\\a.ts')).toBe('C:\\x\\a.ts')
|
||||
expect(resolveWorkspacePath('/w', '\\\\server\\share')).toBe('\\\\server\\share')
|
||||
})
|
||||
|
||||
it('abbreviates only descendants of a POSIX home', () => {
|
||||
expect(abbreviateHomePath('/Users/u', '/Users/u')).toBe('~')
|
||||
expect(abbreviateHomePath('/Users/u/', '/Users/u')).toBe('~')
|
||||
expect(abbreviateHomePath('/Users/u/Documents/project', '/Users/u')).toBe('~/Documents/project')
|
||||
expect(abbreviateHomePath('/Users/u2/a.ts', '/Users/u')).toBe('/Users/u2/a.ts')
|
||||
expect(abbreviateHomePath('/Users/u/a.ts')).toBe('/Users/u/a.ts')
|
||||
expect(abbreviateHomePath('/Users/u/a.ts', '')).toBe('/Users/u/a.ts')
|
||||
expect(abbreviateHomePath('/etc/hosts', '/')).toBe('/etc/hosts')
|
||||
expect(abbreviateHomePath('C:\\Users\\u\\project', 'C:\\Users\\u')).toBe('C:\\Users\\u\\project')
|
||||
expect(abbreviateHomePath('\\\\server\\share\\u', '\\\\server\\share\\u'))
|
||||
.toBe('\\\\server\\share\\u')
|
||||
})
|
||||
|
||||
it('reads the final path segment on both path styles', () => {
|
||||
expect(workspaceTitleOf('/work/project/')).toBe('project')
|
||||
expect(workspaceTitleOf('C:\\work\\project\\')).toBe('project')
|
||||
expect(workspaceTitleOf('/')).toBe('')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -34,6 +34,7 @@ const NO_MODEL_EXPERIENCE_SECTION: Readonly<Record<string, string>> = {
|
||||
'packages/util/brand': 'The package is a type-only primitive erased at compile time.',
|
||||
'packages/util/home-paths': 'The package only resolves harness-owned host paths; model-facing consumers own any rendered use.',
|
||||
'packages/util/launch-environment': 'The package only resolves host environment values; model-facing consumers own any rendered use.',
|
||||
'packages/util/workspace-path': 'The package only formats Workspace paths for browser UI; it never constructs model input.',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
{ "path": "./packages/util/home-paths" },
|
||||
{ "path": "./packages/util/timeout" },
|
||||
{ "path": "./packages/util/crypto" },
|
||||
{ "path": "./packages/util/workspace-path" },
|
||||
{ "path": "./packages/util/output-retention" },
|
||||
{ "path": "./packages/util/atomic-write" },
|
||||
{ "path": "./packages/attachment/attachment" },
|
||||
|
||||
Reference in New Issue
Block a user