@blurname/debrief 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/dist/debrief.js +1207 -0
  4. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 blurname
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # debrief
2
+
3
+ ***Debrief* 你的 Debian 机器——问它到底装了什么,并追踪它的变化。**
4
+
5
+ (名字取自 `deb` + *debrief*「事后汇报」:把整台机器的安装情况汇报上来。)
6
+
7
+ 主线用法:**收集当前机器上装了什么,变成一份清单,然后追踪它的变化。** 软件你(或某个
8
+ agent)照常用 apt 直接装,debrief 不负责装、负责"看和管"——`collect` 把系统快照成
9
+ `debrief.json` + `debrief.lock`,`status` 告诉你系统相对快照又多了/少了/升级了什么。
10
+
11
+ ## 概念映射
12
+
13
+ | npm | debrief |
14
+ | --- | --- |
15
+ | `package.json`(直接依赖) | `debrief.json` — 你**主动装**的包(`apt-mark showmanual`) |
16
+ | `package-lock.json`(完整依赖树) | `debrief.lock` — **全部**已装包 + 确切版本(含依赖) |
17
+ | `node_modules` | 系统 dpkg 状态(通过后端读写) |
18
+ | npm registry | apt sources / Debian 镜像 |
19
+ | `pip freeze` / `dpkg --get-selections` | `debrief collect` — 从系统反向生成清单 |
20
+ | `git status` | `debrief status` — 系统当前状态 vs 快照 |
21
+ | `npm install` | `debrief install` — 把清单里缺的装到另一台机器 |
22
+
23
+ 底层就是 Debian 原生的 `dpkg-query`(装了什么)+ `apt-mark showmanual`(哪些是你主动装的、
24
+ 而非被依赖带进来的)。后者恰好等价于"直接依赖 vs 传递依赖":主动装的进 manifest,依赖只进 lock。
25
+
26
+ ### 也能抓"非 dpkg"装的裸二进制
27
+
28
+ 有些软件根本不走 apt——`curl … | sh`、手动下载、brew/cargo 之类,直接把一个可执行文件丢到
29
+ PATH 上(比如 [herdr](https://herdr.dev/docs/install/) 就是 `curl -fsSL https://herdr.dev/install.sh | sh`)。
30
+ `collect` 会顺带扫 `/usr/local/bin`、`~/.local/bin` 等目录,对每个可执行文件用 `dpkg -S` 反查:
31
+ **dpkg 不认领的,就是这类"裸二进制"**,记进 lock 的 `binaries`(含内容 `sha256`,所以自更新会被
32
+ `status` 认出来;加 `--probe-versions` 还能跑 `--version` 取版本)。
33
+
34
+ ```
35
+ $ debrief status
36
+ non-dpkg binaries (curl|sh / manual / brew / ...):
37
+ + herdr /usr/local/bin/herdr
38
+ ~ bun /usr/local/bin/bun (contents changed)
39
+ ```
40
+
41
+ `--no-binaries` 关掉这个扫描;`--scan-dirs a:b:c` 指定要扫的目录。
42
+
43
+ ## 主线:收集 & 追踪
44
+
45
+ ```bash
46
+ debrief collect # 把当前系统快照进 debrief.json(主动装的)+ debrief.lock(全部)
47
+ debrief collect --pin # manifest 里记确切版本(而非默认的 "*")
48
+ debrief status # 对比:系统相对上次 collect 多了/少了/升级了什么
49
+ debrief list # 清单里的包及其状态、可升级情况
50
+ debrief outdated # 有新版本可升级的包
51
+ ```
52
+
53
+ 典型场景:把 `debrief.json` + `debrief.lock` 提交进你的 dotfiles / 配置仓库。之后 agent 直接
54
+ `apt install` 了什么新东西,`debrief status` 一眼就能看出来;确认无误就 `debrief collect` 重新快照。
55
+
56
+ ```
57
+ $ debrief status
58
+ + ripgrep 14.1.0-1
59
+ + libpcre2-16-0 10.42-1 (dep)
60
+ ~ nginx 1.24.0-2 → 1.26.0-1
61
+ - jq 1.6-2.1
62
+
63
+ +2 added ~1 changed -1 removed (vs snapshot; run `debrief collect` to update it)
64
+ ```
65
+
66
+ ## 设计取向:友好封装(friendly wrapper)
67
+
68
+ 这是刻意选择的最保守语义:
69
+
70
+ - **`install` 只增不改**:只安装 manifest 里*尚未安装*的包。已经装了的,即使版本不满足约束
71
+ (drift),也**只警告、绝不改动**。不做整机对齐(no reconcile),不会去卸载"多余"的包。
72
+ - **`add` / `remove` 是显式动作**:`add` 会安装并写入 manifest;`remove` 会卸载并从 manifest 移除。
73
+ - **lockfile 给你可复现性**:`install` 时若锁定的版本仍然可用且满足约束,就装*锁定的那个*
74
+ (输出里标 `↺`),而不是最新的——这样在另一台机器上 `install` 得到同样的结果。
75
+
76
+ ## 后端(可插拔)
77
+
78
+ 所有逻辑都只依赖一个 `Backend` 接口,因此同一套代码能在有没有 apt 的机器上跑:
79
+
80
+ - **`apt`** — 真实 Debian,底层调用 `dpkg-query` / `apt-cache` / `apt-get`(改动系统的操作会在
81
+ 非 root 时自动加 `sudo`)。
82
+ - **`mock`** — 内存/文件模拟的假仓库,自带一批仿真包版本。在没有 dpkg 的机器(比如 NixOS)上
83
+ 默认启用,用于开发和测试。
84
+
85
+ 选择顺序:`--backend` > `$DEBRIEF_BACKEND` > lockfile 里记录的后端 > 自动探测(有 `apt-get` 就用
86
+ `apt`,否则 `mock`)。
87
+
88
+ ## 反向:把清单装到另一台机器(可选)
89
+
90
+ collect 出来的清单也能反过来用——在一台新机器上把清单里缺的装齐。这条线是刻意保守的
91
+ "友好封装":
92
+
93
+ - **`install` 只增不改**:只安装清单里*尚未安装*的包。已装的即使版本不满足约束(drift),
94
+ 也**只警告、绝不改动**;绝不卸载"多余"的包。
95
+ - **`add` / `remove` 是显式动作**:`add` 会安装并写入 manifest;`remove` 会卸载并移除。
96
+ - **lockfile 给可复现性**:`install` 时若锁定版本仍可用且满足约束,就装*锁定的那个*(标 `↺`)。
97
+
98
+ ```bash
99
+ debrief install # 把 manifest 里缺的装上(优先按 lock 复现)
100
+ debrief add nginx@^1.24 git # 显式安装并记录
101
+ debrief add htop=3.2.1-1 # 钉死一个确切版本
102
+ debrief remove git # 卸载 + 从 manifest 移除
103
+ debrief remove git --save-only # 只从 manifest 移除,保留已安装
104
+ debrief init # 创建一个空的 debrief.json
105
+ debrief --dry-run <cmd> # 预演,不改动任何东西
106
+ ```
107
+
108
+ 安装用 bun:`bun link` 后可直接用 `debrief`;或 `bun run bin/debrief.ts <command>` 直接跑。
109
+
110
+ ## 版本约束(npm 风格,作用在真实 Debian 版本号上)
111
+
112
+ 约束语法借用 npm,但比较用的是 dpkg 自己的版本算法(epoch、`~` 预发布排序等都正确处理):
113
+
114
+ | 写法 | 含义 |
115
+ | --- | --- |
116
+ | `nginx` | 最新候选版;manifest 里写成 `^<装上的版本>` |
117
+ | `nginx@^1.24` | `>=1.24` 且 `<2`(caret:允许非 major 升级) |
118
+ | `nginx@~1.24` | `>=1.24` 且 `<1.25`(tilde:钉住 minor) |
119
+ | `nginx@'>=1.22 <1.26'` | 空格分隔 = AND 组合区间 |
120
+ | `nginx=1.24.0-1` | 精确的 Debian 版本 |
121
+
122
+ ## 开发
123
+
124
+ ```bash
125
+ bun install
126
+ bun test # 单元 + 集成测试(全程用 mock 后端,任何机器都能跑)
127
+ bun run typecheck
128
+ ```
129
+
130
+ ## 分发
131
+
132
+ 代码只用 `node:` 内置模块(通过 `src/sys.ts` 这层薄封装),所以**同一份代码在 Bun 和 Node
133
+ (≥18)上都能跑**。默认分发方式:打成一个几十 KB 的 JS,**要求目标机有 `bun` 或 `node`**——比把整个
134
+ 运行时塞进二进制(~100MB)划算得多。
135
+
136
+ ```bash
137
+ bun run build # → dist/debrief.js(~40KB,单文件,含 node shebang)
138
+ node dist/debrief.js collect # 用 node 跑
139
+ bun dist/debrief.js collect # 用 bun 跑
140
+ ```
141
+
142
+ - **npm**:`npm i -g debrief`(或 `bunx debrief`)—— `bin` 指向 `dist/debrief.js`,`prepack`
143
+ 会自动构建。`npm pack` 出来的包只有 16KB / 3 个文件。
144
+ - **`curl | sh`**:`curl -fsSL <host>/install.sh | sh` —— 拉下 `dist/debrief.js` 和一个 exec
145
+ node/bun 的启动器丢到 `~/.local/bin`(见 `scripts/install.sh`)。注意:这样装的 `debrief`
146
+ 自己就是个"裸二进制",会被它自己的 `collect` 扫描逮到。
147
+
148
+ 已在真实 Debian 上验证:
149
+ - 两种运行时结果一致 —— `bun run test:apt`(Bun)、`bun run test:node`(纯 Node)。
150
+ - **`npm pack` → 在干净的 node 机器上 `npm i -g` → `debrief collect`/`add hello` 对着真 apt 跑通。**
151
+ - `install.sh` 拉取 + 启动器 + 运行,链路验证过。
152
+
153
+ > 真正 `npm publish` / 建 Release / 建 apt 仓库还没做(需要账号和授权)——上面验证的是"发出去就能用"。
154
+
155
+ ### 想要"零运行时依赖"?(体积换取)
156
+
157
+ 如果目标机连 node/bun 都不想装,可以退回自包含二进制 / `.deb`(每个 ~100MB,因为把运行时也打进去了):
158
+
159
+ ```bash
160
+ bun run build:bin # → dist/debrief-linux-{x64,arm64}(自包含,无需运行时)
161
+ bun run build:deb # → dist/debrief_<ver>_amd64.deb(需 dpkg-deb;NixOS 上在容器里打)
162
+ sudo apt install ./dist/debrief_0.1.0_amd64.deb
163
+ ```
164
+
165
+ `.deb` 有个额外的好处:装完 debrief 就是个正经 dpkg 包,会出现在它自己的 `collect` 里(吃自己的狗粮)。
166
+
167
+ ## 已知边界
168
+
169
+ - 管理的是**系统级**的包(不是项目本地隔离);这是 apt 的本性。
170
+ - `apt` 后端已在 **Debian 13 (trixie) 容器**里对着真实镜像端到端验证过(见下);开发主机
171
+ (NixOS)日常用 mock 后端顶替。
172
+ - 依赖解析交给 apt 自己:manifest 只记你主动装的包,依赖只进 lock——正如 npm 只把直接依赖
173
+ 写进 `package.json`。
174
+
175
+ ## 在真实 Debian 上验证
176
+
177
+ 单元测试全程用 mock 后端,任何机器都能跑。`apt` 后端要在真 Debian 上验证,用容器即可:
178
+
179
+ ```bash
180
+ docker run --rm -v "$PWD":/app:ro oven/bun:debian bash /app/test/apt-e2e.sh
181
+ # 受限网络下加 --dns 1.1.1.1
182
+ ```
183
+
184
+ 已验证:`collect`/`status` 正确解析 `dpkg-query` + `apt-mark showmanual`(手动装的包进
185
+ manifest、依赖标 `auto:true` 只进 lock、带 `arch`);`status` 能发现绕过工具、直接用 apt 装/删
186
+ 的包;`add`/`remove` 走真实 `apt-get install/remove`。
187
+
188
+ MIT