Skip to content

UniPty v0.2.0:命令文本到 argv 的翻译层

jixoai v0.2.0 unipty

UniPty v0.2.0 新增两个可选的官方解析器包:@unipty/shell-parser 把 shell 命令文本分类并翻成结构化 argv,@unipty/powershell-parser 只认官方 Parser.ParseInput。全程不执行一个字节,8 个公共包第一次以完整形态同时出现在 npm 上。

$ npm view @unipty/shell-parser version
0.2.0

UniPty v0.2.0 发布了(GitHub Release 于 2026-08-20 切出)。这一版新增两个可选的官方解析器包,把命令文本翻译成可直接启动的结构化数据,全程不执行一个字节。8 个公共包第一次以完整形态同时出现在 npm 上。

背景:一份契约,三条运行时

Node、Bun 与 Deno 各自暴露互不兼容的 PTY 底层:安装模型、I/O 表示、生命周期语义、原生部署约束全都不同(这是 v1 架构提案开篇的原始动机)。UniPty 把这些差异吸收到一份公共契约后面:Core 拥有一切可观察行为,开发者显式选择 Backend,支持声明由证据门控。三条官方路线分别是 Node 走第三方 node-pty、Bun 走运行时原生的 Bun.Terminal、Deno 走自包含的 @sigma/pty-ffi

一个 runtime/platform 组合只有在公共 conformance 套件对已安装的包产物(pack 并装入隔离消费者,只经公共导出驱动)全量通过时才标记 verified;其余一律是 declared-unverifiednot-targeted。失败留在 CI 诊断里,不固化成永久性的 unsupported 声明(见 conformance 文档)。

@unipty/shell-parser:读命令文本,不执行它

从字符串命令工具迁移过来的项目,不用再自己写「文本 → argv」的翻译。@unipty/shell-parser 读一段命令文本,告诉你它属于哪一类、能不能直接启动:

import { parse } from "@unipty/shell-parser";

parse("git status --force");
// → { kind: "argv", argv: ["git", "status", "--force"] }

parse("ls *.txt | wc -l");
// → { kind: "script", language: "bash", source: "ls *.txt | wc -l" }

parse("echo 'unterminated");
// → { kind: "incomplete", diagnostics: [...] }

Core 只收结构化 argv,没有字符串命令重载。隐式 shell 正是它要避免的那类事故,所以这个包交出的是分类结果,不是执行器。

五分类 argv | script | incomplete | unsupported | invalid 是唯一的稳定边界。argv 意味着这段文本在词法上恰好是一条简单命令(引号与空参数保留),可以直接传给 unipty.spawn(argv)script 意味着它带 shell 语义,要由你显式接受指定的 shell 策略后才谈得上启动。可执行文件怎么解析(PATH、内建、函数、别名)始终是你的决定,解析器不会为一个命令名主张进程启动等价性。

分类政策刻意保守:转义元字符(echo \*)与未加引号的括号字符([ -f x ])同样归入 script。walker 从原始词文本证明「字面性」,有含糊就停在 shell 请求一侧,不猜。

这个包是 unbash 之上的同步薄包装,唯一的公共面是分类结果,unbash 的 AST 不外泄。见包文档

@unipty/powershell-parser:只认官方解析器

PowerShell 的命令文本也能翻成结构化 argv。它不猜语法,调用的是 PowerShell 官方的 Parser.ParseInput,由你指定的 host(默认 pwsh)执行:

import { parsePowershell } from "@unipty/powershell-parser";

await parsePowershell('dotnet build -c "My Config"');
// → { kind: "argv", argv: ["dotnet", "build", "-c", "My Config"] }

适配脚本以 -EncodedCommand 传输;你的文本以 base64 编码的 UTF-8 走 stdin,不经命令行插值,也不受 Windows 控制台代码页歧义影响。

失败是类型化的三段,都不静默:host 缺席 → capability-unavailable,绝不回落到 Bash 式解析;host 启动但异常退出或返回未知结果 → host-failure,绝不降级成 script;超出预算(默认 15 秒,timeoutMs 可配)→ host-timeout

host 的选择是显式的:isPowershellHostAvailable() 先探,options.host 可以指定其它可执行文件(例如 Windows PowerShell 5.1 的 powershell,它同样提供 Parser.ParseInput)。官方 ParseError 记录序列化为 { message, errorId, incomplete, range },带 UTF-16 偏移;标记为 IncompleteInput 的错误映射为 incomplete,其余归 invalid。见包文档

示例:先探 host,再显式选择
import { isPowershellHostAvailable, PowershellParseError } from "@unipty/powershell-parser";

await isPowershellHostAvailable(); // → false when no pwsh exists

await parsePowershell("x", { host: "pwsh-preview" }); // explicit host choice

其余变更

  • 一次发布 8 个包,npm 上不会留下发了一半的版本:release workflow 先发两个 parser 包,再发核心与 Backend。parser 是最新的包名、最可能没配好 Trusted Publisher,鉴权失败要发生在核心包已发布之前。npm 时间戳可以核对:@unipty/shell-parser@0.2.0 于 11:00:13 上架,unipty@0.2.0 于 11:00:26 跟进,GitHub Release v0.2.0 于 11:01 切出。

  • shell-parser 把 locale 字符串($"...")与含 NUL 的 ANSI-C 词挡在 argv 之外(5bcd7d2

  • powershell-parser 文本传输改走 stdin 并强制严格 host 失败(7f121ff

  • 文档站消费真实的 release-catalog schema,并要求 aggregator(聚合校验)套件的通过戳(586e24ce489776

完整清单见 compare v0.1.1...v0.2.0

升级

无破坏性变更。parser 生态是纯新增包,Core / Backend / acquisition / helper / conformance 代码零改动(变更提案的 Impact 原文)。升级即:

npm i unipty@0.2.0
# 可选:按需加装解析器
npm i @unipty/shell-parser@0.2.0 @unipty/powershell-parser@0.2.0

致谢

@unipty/shell-parser 基于 unbash 构建;三条 Backend 路线分别感谢 node-pty(经 @lydell/node-pty 预构建分发)、Bun.Terminal@sigma/pty-ffi

链接