fix: recover legacy Windows session workdirs

This commit is contained in:
Relakkes Yang
2026-05-18 13:13:12 +08:00
parent cf7c4487f0
commit 3c3030fddf
12 changed files with 115 additions and 45 deletions
+1 -9
View File
@@ -5,7 +5,7 @@
*/
import { describe, it, expect } from 'bun:test'
import * as path from 'node:path'
import { isOutsideWorkDir } from '../path-safety.js'
// ---------- helpers extracted from feishu/index.ts for testability ----------
@@ -209,14 +209,6 @@ function summarizeToolCall(toolName: string, input: unknown): ToolCallSummary {
}
}
function isOutsideWorkDir(filePath: string, workDir: string): boolean {
const abs = path.isAbsolute(filePath)
? path.normalize(filePath)
: path.resolve(workDir, filePath)
const normWork = path.normalize(workDir).replace(/\/+$/, '')
return abs !== normWork && !abs.startsWith(normWork + path.sep)
}
function truncateTarget(s: string, maxLen = 160): string {
if (s.length <= maxLen) return s
return s.slice(0, maxLen - 1) + '…'
+1 -10
View File
@@ -37,6 +37,7 @@ import { AttachmentStore } from '../common/attachment/attachment-store.js'
import { checkAttachmentLimit } from '../common/attachment/attachment-limits.js'
import { ImageBlockWatcher } from '../common/attachment/image-block-watcher.js'
import type { PendingUpload } from '../common/attachment/attachment-types.js'
import { isOutsideWorkDir } from './path-safety.js'
// ---------- init ----------
@@ -502,16 +503,6 @@ function summarizeToolCall(toolName: string, input: unknown): ToolCallSummary {
}
}
/** True if `filePath` resolves to a location outside of `workDir`.
* Relative paths are resolved against workDir first. */
function isOutsideWorkDir(filePath: string, workDir: string): boolean {
const abs = path.isAbsolute(filePath)
? path.normalize(filePath)
: path.resolve(workDir, filePath)
const normWork = path.normalize(workDir).replace(/\/+$/, '')
return abs !== normWork && !abs.startsWith(normWork + path.sep)
}
/** Truncate a single-line target preview (e.g. shell command) to maxLen. */
function truncateTarget(s: string, maxLen = 160): string {
if (s.length <= maxLen) return s
+23
View File
@@ -0,0 +1,23 @@
import * as path from 'node:path'
/** True if `filePath` resolves to a location outside of `workDir`.
* Relative paths are resolved against workDir first. */
export function isOutsideWorkDir(filePath: string, workDir: string): boolean {
const pathApi = usesWindowsPath(filePath) || usesWindowsPath(workDir) ? path.win32 : path.posix
const abs = pathApi.isAbsolute(filePath)
? pathApi.normalize(filePath)
: pathApi.resolve(workDir, filePath)
const normWork = stripTrailingSeparators(pathApi.normalize(workDir), pathApi)
const relative = pathApi.relative(normWork, abs)
return relative !== '' && (relative.startsWith('..') || pathApi.isAbsolute(relative))
}
function usesWindowsPath(value: string): boolean {
return /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\')
}
function stripTrailingSeparators(value: string, pathApi: typeof path.posix | typeof path.win32): string {
const root = pathApi.parse(value).root
if (value === root) return value
return value.replace(/[\\/]+$/, '')
}