定位:
ssh://拼写的完整规范——语法、规范化、显示改写与file:URI。 读者:使用者与二次开发者。
ssh://<主机别名>/<远端绝对路径>
例如 ssh://myserver/home/user/project
- 别名是 DNS 式标签、不能含
/,所以别名与路径的切分没有歧义; - 远端路径必须绝对(以
/开头)。 - 别名另有校验规则(写入主机存储时):非空、最长 64 字符、不以
-开头、 不含空白、斜杠、引号或通配符。 parseRemotePath()对「以ssh://开头但别名为空或路径非绝对」的拼写 抛错(这是调用方错误,不该静默);对不是ssh://的拼写返回undefined(表示与本插件无关,交给本地后端)。
normalizeRemotePath() 纯词法地折叠 // 与 .、弹掉 ..,保留开头的
/——不请求服务器 realpath。目标身份(targetKey)用的就是
ssh://别名 + 规范化路径,所以 ssh://a/x/../y 与 ssh://a/y 是同一条目标。
isRemotePathUnder(parent, child) 在规范化后做词法前缀比较(根 / 特判)。
远端写入(writeText / editText / mkdir / 原子替换)只允许落在已挂载的
远端工作区之内,越界返回 FS_SANDBOX_DENIED;读取不受限。
这道围栏之外还有一道独立的围栏:被停用(detach)的工作区,其下读写都拒,
同样返回 FS_SANDBOX_DENIED,文案带 the remote workspace is detached。
两者判定时机不同——写入围栏问「这个路径在不在某个已挂载根之下」,
停用围栏问「这个路径在不在某个被主动停用的根之下」,后者先于前者作答,
且对 ssh_workspace_exec / 持久 shell / glob / grep / git / 传输一并生效
(判据是 engine.isDisabledTarget)。详见 unmount-modes.md。
界面显示时,别名后插入一个 @:
实际拼写(复制与一切功能使用) ssh://myserver/home/user/project
显示拼写(仅界面展示) ssh://myserver@/home/user/project
出现位置:侧栏工作区行的悬停卡片、右侧栏「文件」面板头部、文档预览头部。 改写是纯显示层的行为——复制按钮与所有消费者拿到的仍是实际拼写。
formatRemoteFileUrl() 返回远端执行世界(POSIX)的规范 file: URI:
分隔符保持字面量,其余保留字符与非 ASCII 逐段百分号编码。
不能在本机(例如 Windows)用 pathToFileURL 得到盘符拼写——宿主只用它
换算相对路径(new URL(url).pathname),POSIX 拼写才能得到 / 连接的答案。
会话 header 要求 cwd 通过 path.isAbsolute 校验,而任何平台解析器都不把
裸 ssh:// 拼写当绝对路径(win32 视作缺冒号的两字母伪盘符,posix 视作普通
相对段)。插件把 node:path.isAbsolute 对 ssh:// 拼写改为返回 true
(见 architecture.md 的「内建桥接(node:fs / node:path)」),
realpath 也返回词法规范拼写——会话 header 与工作区登记两处的拼写因此
永远一致,并可以在远端工作区里直接新建会话。