tablize CLI(node)
agent 面的 CLI —— 表 · 变换 · 批次 · 血缘,全是打 REST。
它解决的是这个:agent 处理长表时,整表进上下文会把注意力摊薄、会话一断就得重来。
tablize 把流程固化在服务端 —— agent 一小批一小批领活(batch pull → 算 →
batch submit),手上只有派给它的那几行,跑断了是 tablize context 续跑而不是重跑。
每一次尝试、每个模型、每次标准改动都落在同一本账上。金标门是可选的加固件:
建线时不给金标,这条线就直通(--gate none,行落成「已入账 · 未经第三方验收」);
给了金标就是 enforce,错一条拒整批,见
ADR 0066。档位不是建线时定死的 ——
transform gate --mode … --reason … 能移动它(理由必填、落到规则层时间线;
与 revise / goldens 不同,它不 bump 配方版本,所以什么都不重排、已做的活还算数)。
npm i -g @tablize/cli
tablize install
tablize --help
macOS / Linux / Windows 同一个产物。
为什么是 node 而 Rust 那份还在
命令面按受众切成两半(ADR 0065):
-
agent 面(这里)—— line / table / transform / batch / lineage / task /
query / pipeline / context / workspace / whoami / login / import /
export / doctor / diagnose / install / update。全部是 HTTP,所以 node 写得动,
且 Windows 第一天就有(原生二进制那条一直只支持 macOS + Linux,Windows 用户从来装不上)。
一个产物、不用交叉编译、不用签名。
line(ADR 0070)
只在这一份里有 —— Rust 那份停在 0.2 的命令面。它是 pipeline init 的继任者:
声明名字 + 位置 + 采集契约,一张表都不建,位置在第一次写入时才物化。
-
admin 面 —— store check / columnar / formula / migrate / openapi dump /
deploy / healthz。要在进程内开磁盘上的 store、接同一个列存服务、从 Rust 类型出
OpenAPI,只能是 Rust。它跑在服务端那台机器上,而那台机器本来就有 Rust 二进制。
装法:npm i -g @tablize/admin → 命令名 tablize-admin(名字不撞)。
两份实现共用的三样东西
过渡期两份同时在世,所以这三处必须逐字相同,不是「最好一致」:
~/.config/tablize/config.json —— 同一个文件、同一批 key(含 userId /
expiresAt)、同一个回退顺序(flag > env > .tablize > 文件)。用户能在两者之间来回
切而不用重新 login。.tablize 项目绑定同理(向上查找),但它只绑 workspace:
task 走 --task / TABLIZE_TASK,因为绑定按目录共享而 task 是会话的属性(ADR 0085)。
文件里遗留的 task 键两边都读得出、都不生效、都由 task 生命周期顺手摘掉。
- skill 树的内容摘要 —— 配方是「按路径排序,每个文件喂 路径字节 ·
0x00 ·
长度的 u64 小端 · 内容」。摘要漂一位,两边的 doctor 就会互相把对方装的 skill 报成
E_SKILL_STALE,而且重装多少次都清不掉。tests/install.test.ts 用一个独立算出来的
期望值钉住配方,另一条钉住「npm 包里的文件集 = git 里的减去共享排除表」。
.tablize-install.json 的字段 —— 两边的 doctor 都要读它。
.gitignore 不随 skill 发 —— npm 无条件把这个名字从 tarball 里剔掉,没有开关。两边的
排除表因此一起加上了它,否则文件集差一个、摘要必然不等。
doctor 第一层问的是「哪一份」,不是「哪个字节」
Rust 版第一层是「装的 = 跑的?」,它自己把二进制拷上 PATH,所以能比字节。这一份没有那个
动作(npm 干完了),而真正会咬人的是另一件事,现在就在咬:
~/.local/bin/tablize(老 curl | sh 装的原生二进制)在很多机器上排在 npm 的全局 bin
前面。以前装过的人 npm i -g @tablize/cli 之后跑的还是那个旧二进制,一句提示都没有,
而它答的是 0.1/0.2 的命令面。
所以第一层改问 PATH 第一名是哪一份实现,判据是读文件头的魔数(Mach-O / ELF / PE →
native;#!…node → node)。发现原生时报 E_BIN_NATIVE_SHADOW 并给出那条 rm。
install 的 binary 那一半同理:不搬文件,只回答「PATH 上跑起来的会是哪一份」。
Rust 版的 --bin-dir / --no-build 在这里不存在 —— 留着就是两个假 flag。
install 不进 postinstall:装个包不该悄悄改用户的 agent 配置。
复刻的代价,写在这里
这份是复刻,不是共享类型。移植过程中被服务端打回两次,另有四处是逐字段核过 Rust 版
之后才发现的偏差 —— 每一条现在都是 tests/wire.test.ts 里的一条断言,而且期望值抄自
Rust 源码 / 服务端 DTO,不是抄自这份实现自己:
- 单元格类型在 header 的
meta(外部标签枚举)里,不在 kind 里 —— kind 是 header
的角色(row / 公式 / lookup)。当类型读会让每列都变成 row,于是每次写入都被拒。
- dedupe 的 body 是
onHeaderIds / projectHeaderIds,不是 on / project。
encode 的三处「宽进」曾被写成「严拒」 —— Rust 把 number 原样转发字符串、认不出的
布尔词落 false、未知 kind 落 text,让服务端说不。抢着判会拒掉服务端本来收的值。
- 日期不能用
Date.parse 读不带时区的 datetime —— 它按本地时区算,Rust 按 UTC。
同一条命令在两台机器上写进去的是两个时刻,不报错、跨机器才显形。
- 配置文件的过期时间键是
expiresAt,不是 tokenExpiresAt。一个字母让会话轮转永远
读不到过期时刻。
- 连接 flag 也要挂在顶层 —— clap 那边是
global = true,tablize --url X table list
必须成立。
Rust 那份不会犯这些错,因为它 use tablize_types —— 类型是同一份。所以这一侧必须有测试
替代编译器。 这是选 node 换来 Windows 与单产物时,实际付出的那笔账。
开发
npm ci
npm run check
npm test
npm run build && node dist/main.js --help
bash scripts/ci-local.sh 是仓库的合并判据,里面有 cli typecheck 与 cli test 两道闸。