codex-work-receipt 0.8.1 → 0.10.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.
- package/CHANGELOG.md +19 -0
- package/README.en.md +14 -4
- package/README.md +14 -4
- package/docs/cli.en.md +40 -2
- package/docs/cli.md +40 -2
- package/docs/codex-skill.en.md +1 -1
- package/docs/codex-skill.md +1 -1
- package/docs/data-schema.en.md +20 -4
- package/docs/data-schema.md +20 -4
- package/docs/privacy.en.md +8 -3
- package/docs/privacy.md +8 -3
- package/package.json +2 -2
- package/skills/ai-work-receipt/SKILL.md +10 -5
- package/src/auto-runner.mjs +4 -4
- package/src/cli.mjs +64 -5
- package/src/core/args.mjs +59 -3
- package/src/core/fact-buckets.mjs +3 -3
- package/src/core/fact-identity.mjs +8 -4
- package/src/core/generator.mjs +15 -3
- package/src/core/metrics.mjs +143 -15
- package/src/core/presentation.mjs +190 -3
- package/src/core/project-identity.mjs +132 -0
- package/src/core/range.mjs +155 -8
- package/src/core/receipt-record.mjs +36 -1
- package/src/core/selector.mjs +146 -21
- package/src/lib/tool-category.mjs +36 -0
- package/src/parsers/codex.mjs +58 -7
- package/src/renderers/html.mjs +661 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
- 新增 `--custom-range`、`--from` 和 `--to`,支持自定义完整自然日或精确到时分的统计区间。
|
|
6
|
+
- 自定义自然日继续生成 cwr2 规范事实;精确时间区间使用不参与供销社统计的兼容 cwr1 私人摘要。
|
|
7
|
+
- 新增 `--select-session`、`--select-project` 和 `--project <directory>`,支持交互选择会话、项目或直接按项目目录筛选。
|
|
8
|
+
- 项目仓库地址与路径立即转换为本机加盐的匿名身份,不写入 HTML、历史或微信导入文件。
|
|
9
|
+
- HTML“更多小票功能”扩展为 15 条命令,并按时间范围、会话与项目、自动模式、票仔扩展四个可访问 Tab 展示。
|
|
10
|
+
- 命令 Tab 记忆选择状态,支持键盘切换、桌面双列和移动端横向导航,继续排除在长图导出之外。
|
|
11
|
+
- 修复自动模式合并相邻 Stop 触发时丢失原始触发时间、可能导致跨午夜小票写入错误日期的问题。
|
|
12
|
+
|
|
13
|
+
## 0.9.0
|
|
14
|
+
|
|
15
|
+
- 新增缓存命中率、每轮 Token / 输出 Token / 工具调用 / 完成耗时等效率指标。
|
|
16
|
+
- 新增首 Token 与整轮耗时的 P50 / P90 分位数,以及按本地时区生成的 24 小时工作热力图。
|
|
17
|
+
- 新增模型使用轮次和脱敏工具类别结构;解析器不保留原始工具名、调用参数、命令或工具输出。
|
|
18
|
+
- HTML 与本地完整 JSON 展示详细效率洞察;微信 `cwr1` / `cwr2` 紧凑载荷保持不变,继续兼容现有小程序。
|
|
19
|
+
- HTML 赞助商下方新增“更多小票功能”,集中展示生成范围、自动 / 手动模式、Skill 与票仔的 12 条常用命令,并支持一键复制。
|
|
20
|
+
- 功能命令区采用独立的浅色纸张样式,记忆展开状态,适配桌面和移动端,并继续排除在长图导出范围之外。
|
|
21
|
+
|
|
3
22
|
## 0.8.0
|
|
4
23
|
|
|
5
24
|
- 首次交互式运行新增“自动保存 / 仅手动”选择;显式范围命令继续保持非交互兼容。
|
package/README.en.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<img src="docs/images/readme-hero.jpg" alt="Three Codex Work Receipt themes" width="920">
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
|
-
Codex Work Receipt summarizes turns, tool calls, Tokens, duration, and models, then turns
|
|
23
|
+
Codex Work Receipt summarizes turns, tool calls, Tokens, duration, and models. It also reports cache hit rate, per-turn efficiency, P50 / P90 latency, a work-time heatmap, and model/tool structure, then turns the result into playful AI work points, a job title, and a review. Receipts never include prompts, responses, source code, project paths, file names, tool arguments, or tool output.
|
|
24
24
|
|
|
25
25
|
The cwr2 protocol creates stable privacy-safe facts for each session and calendar day, allowing overlapping today, last-seven-days, and this-week receipts to be deduplicated. Every receipt also produces one `.cwr.json` WeChat import file. Scan import appears only when the complete payload fits in one data QR code.
|
|
26
26
|
|
|
@@ -36,7 +36,7 @@ The cwr2 protocol creates stable privacy-safe facts for each session and calenda
|
|
|
36
36
|
|
|
37
37
|
## Quickstart
|
|
38
38
|
|
|
39
|
-
Requires Node.js 20+ and local Codex session records. No clone is required. The first interactive run asks whether to save automatically or use manual-only mode. Manual mode
|
|
39
|
+
Requires Node.js 20+ and local Codex session records. No clone is required. The first interactive run asks whether to save automatically or use manual-only mode. Manual mode can generate by time range, a selected session, or a selected project:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
npx codex-work-receipt@latest --lang en
|
|
@@ -54,9 +54,19 @@ Summarize the last 3 hours:
|
|
|
54
54
|
npx codex-work-receipt@latest --hours 3 --lang en
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
“Last N hours” is a rolling summary for private history only. It does not participate in AI Work Cooperative deduplicated accounting. Use today, this week, the last seven days,
|
|
57
|
+
“Last N hours” is a rolling summary for private history only. It does not participate in AI Work Cooperative deduplicated accounting. Use today, this week, the last seven days, a specific session, or custom whole calendar dates when you want accountable canonical facts.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Interactively choose a session, project, or custom range:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx codex-work-receipt@latest --select-session --lang en
|
|
63
|
+
npx codex-work-receipt@latest --select-project --lang en
|
|
64
|
+
npx codex-work-receipt@latest --custom-range --lang en
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Advanced use can pass `--project <directory>`, `--from <start>`, and `--to <end>` directly. Whole calendar-date ranges produce cwr2 canonical facts; exact date-time ranges are private cwr1 summaries.
|
|
68
|
+
|
|
69
|
+
HTML, structured data, and the WeChat import file are written to `./codex-work-receipt-output/` by default. The page can download its `.cwr.json` file and save a high-resolution PNG containing only the full receipt and WeChat mini-program code. “More receipt features” provides 15 copyable commands organized into tabs for time ranges, sessions and projects, automation, and Ticket Buddy extensions. The web controls stay out of the exported image. See the [CLI guide](docs/cli.en.md).
|
|
60
70
|
|
|
61
71
|
## Automatic saving or manual only
|
|
62
72
|
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<img src="docs/images/readme-hero.jpg" alt="Codex AI 打工小票三种主题效果" width="920">
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
|
-
它会统计 Codex 会话中的轮次、工具调用、Token
|
|
23
|
+
它会统计 Codex 会话中的轮次、工具调用、Token、时长和模型,并提供缓存命中率、每轮效率、P50 / P90 延迟、工作时间热力图、模型与工具结构,生成带有 AI 工分、今日工种和点评的小票。不会把 Prompt、回复正文、代码、项目路径、文件名、工具参数或工具输出写入小票。
|
|
24
24
|
|
|
25
25
|
cwr2 协议按“会话 × 自然日”生成稳定的脱敏事实。今日、近 7 日和本周小票即使范围重叠,接收端也能识别相同工作。每张小票还会生成一个 `.cwr.json` 微信导入文件;只有完整数据能放进一个二维码时,网页才额外提供扫码导入。
|
|
26
26
|
|
|
@@ -36,7 +36,7 @@ cwr2 协议按“会话 × 自然日”生成稳定的脱敏事实。今日、
|
|
|
36
36
|
|
|
37
37
|
## Quickstart
|
|
38
38
|
|
|
39
|
-
需要 Node.js 20 或更高版本,并且本机已经使用过 Codex
|
|
39
|
+
需要 Node.js 20 或更高版本,并且本机已经使用过 Codex。无需克隆仓库。首次交互式运行会先让你选择“自动保存”或“仅手动”;选择手动后,可以按时间、指定会话或指定项目生成小票:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
npx codex-work-receipt@latest
|
|
@@ -54,9 +54,19 @@ npx codex-work-receipt@latest --today
|
|
|
54
54
|
npx codex-work-receipt@latest --hours 3
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
“最近 N 小时”是滚动摘要,只用于查看和保存私人历史,不参与 AI 供销社的去重统计。需要进入供销社时,请生成“今日 / 本周 / 近 7 日 /
|
|
57
|
+
“最近 N 小时”是滚动摘要,只用于查看和保存私人历史,不参与 AI 供销社的去重统计。需要进入供销社时,请生成“今日 / 本周 / 近 7 日 / 指定会话 / 自定义完整自然日”小票。
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
交互选择会话、项目或自定义区间:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx codex-work-receipt@latest --select-session
|
|
63
|
+
npx codex-work-receipt@latest --select-project
|
|
64
|
+
npx codex-work-receipt@latest --custom-range
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
也可以使用 `--project <目录>`、`--from <开始>` 和 `--to <结束>` 直接指定。完整自然日区间生成 cwr2 规范事实;精确到时分的区间属于私人 cwr1 摘要。
|
|
68
|
+
|
|
69
|
+
网页、结构数据和微信导入文件默认保存在 `./codex-work-receipt-output/`。生成的网页支持一键下载 `.cwr.json`,也可以保存只包含完整小票和微信小程序码的高清长图。赞助商下方的“更多小票功能”提供 15 条可复制命令,并按“时间范围 / 会话与项目 / 自动模式 / 票仔扩展”分 Tab 展示。数据二维码和网页功能区不会进入图片。详见 [CLI 使用文档](docs/cli.md)。
|
|
60
70
|
|
|
61
71
|
## 自动保存或仅手动
|
|
62
72
|
|
package/docs/cli.en.md
CHANGED
|
@@ -22,7 +22,7 @@ The first interactive run asks you to choose:
|
|
|
22
22
|
- `Automatic saving`: quietly refresh today's receipt and `.cwr.json` WeChat import file whenever a Codex turn stops
|
|
23
23
|
- `Manual only`: generate only when you run the command or ask Ticket Buddy
|
|
24
24
|
|
|
25
|
-
After choosing manual-only mode, choose
|
|
25
|
+
After choosing manual-only mode, choose today, the last 3 hours, the last 7 calendar days, this week, a custom range, a recent session, or a recent project. Session choices show their time range, turns, tool calls, and model; project choices show a local project name, latest activity, and session count. Explicit range flags such as `--today` and `--latest` never trigger mode setup.
|
|
26
26
|
|
|
27
27
|
## Automatic saving
|
|
28
28
|
|
|
@@ -89,6 +89,38 @@ Monday through now:
|
|
|
89
89
|
npx codex-work-receipt@latest --range this-week --lang en
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## Custom ranges, sessions, and projects
|
|
93
|
+
|
|
94
|
+
Interactively choose whole calendar dates or an exact date-time range:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx codex-work-receipt@latest --custom-range --lang en
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
You can also pass boundaries directly. A date-only end is inclusive; exact date-time input uses the half-open interval `[start, end)`:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx codex-work-receipt@latest --from 2026-07-01 --to 2026-07-15 --lang en
|
|
104
|
+
npx codex-work-receipt@latest --from 2026-07-23T09:00 --to 2026-07-23T18:30 --lang en
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Whole calendar dates produce cwr2 canonical facts and can participate in cooperative deduplication. Exact time ranges can cut through a calendar day, so they produce compatible private cwr1 summaries instead.
|
|
108
|
+
|
|
109
|
+
Interactively choose a recent session or project:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx codex-work-receipt@latest --select-session --lang en
|
|
113
|
+
npx codex-work-receipt@latest --select-project --lang en
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
After selecting a project, choose today, the last 3 hours, the last 7 days, this week, or a custom range. Advanced use can pass a local directory and combine it with any range:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx codex-work-receipt@latest --project ./my-project --today --lang en
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Project names appear only in the terminal selector. Repository URLs and paths are immediately replaced with a locally salted anonymous identity and never enter HTML, history, or WeChat import files.
|
|
123
|
+
|
|
92
124
|
## Language and themes
|
|
93
125
|
|
|
94
126
|
Generate a Chinese receipt:
|
|
@@ -148,11 +180,17 @@ npx codex-work-receipt@latest --latest --lang en --no-open
|
|
|
148
180
|
|
|
149
181
|
| Option | Description |
|
|
150
182
|
| --- | --- |
|
|
151
|
-
| `--range <name>` | `latest`, `last-hours`, `today`, `last-7-days`, or `this-week` |
|
|
183
|
+
| `--range <name>` | `latest`, `last-hours`, `custom-range`, `today`, `last-7-days`, or `this-week` |
|
|
152
184
|
| `--hours <number>` | Summarize the last 1-168 hours |
|
|
185
|
+
| `--custom-range` | Interactively choose custom calendar dates or an exact time range |
|
|
186
|
+
| `--from <value>` | Set the custom start date or date-time |
|
|
187
|
+
| `--to <value>` | Set the custom end date or date-time |
|
|
153
188
|
| `--latest` | Summarize the latest active Codex session; default mode |
|
|
154
189
|
| `--today` | Summarize activity from today in the selected timezone |
|
|
155
190
|
| `--session <id>` | Summarize one specific Codex session |
|
|
191
|
+
| `--select-session` | Interactively choose a recent Codex session |
|
|
192
|
+
| `--project <directory>` | Limit the receipt to one local project directory |
|
|
193
|
+
| `--select-project` | Interactively choose a recent project and range |
|
|
156
194
|
| `--timezone <name>` | Set an IANA timezone such as `Asia/Shanghai` |
|
|
157
195
|
| `--lang <name>` | `zh-CN` (default) or `en` |
|
|
158
196
|
| `--theme <name>` | `classic`, `diner`, or `payroll` |
|
package/docs/cli.md
CHANGED
|
@@ -22,7 +22,7 @@ npx codex-work-receipt@latest
|
|
|
22
22
|
- `自动保存`:Codex 每完成一轮工作,就静默刷新今天的小票和 `.cwr.json` 微信导入文件
|
|
23
23
|
- `仅手动`:只有执行命令或告诉票仔时才生成
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
选择仅手动后,命令行会继续让你选择今天、最近 3 小时、最近 7 个自然日、本周、自定义区间、具体会话或具体项目。选择会话时会展示起止时间、轮次、工具调用和模型;选择项目时会展示本地项目名、最近活动和会话数量。显式使用 `--today`、`--latest` 等范围参数时不会触发模式问答。
|
|
26
26
|
|
|
27
27
|
## 自动保存
|
|
28
28
|
|
|
@@ -89,6 +89,38 @@ npx codex-work-receipt@latest --range last-7-days
|
|
|
89
89
|
npx codex-work-receipt@latest --range this-week
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## 自定义区间、会话与项目
|
|
93
|
+
|
|
94
|
+
交互选择自定义自然日或精确时间区间:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx codex-work-receipt@latest --custom-range
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
也可以直接传入边界。日期格式的结束日包含在统计中;时间格式使用半开区间 `[开始, 结束)`:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npx codex-work-receipt@latest --from 2026-07-01 --to 2026-07-15
|
|
104
|
+
npx codex-work-receipt@latest --from 2026-07-23T09:00 --to 2026-07-23T18:30
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
完整自然日范围生成 cwr2 规范事实,可以参与供销社去重统计。精确到时分的范围可能截断自然日,因此生成兼容的私人 cwr1 摘要,不参与供销社统计。
|
|
108
|
+
|
|
109
|
+
交互选择最近会话或项目:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx codex-work-receipt@latest --select-session
|
|
113
|
+
npx codex-work-receipt@latest --select-project
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
项目选择后可以继续选择今日、最近 3 小时、最近 7 日、本周或自定义区间。高级用法可以通过本地目录直接指定项目,并与任意范围组合:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx codex-work-receipt@latest --project ./my-project --today
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
项目名称只用于终端选择;仓库地址或路径会立即转换为本机加盐的匿名项目身份,不会写入 HTML、历史记录或微信导入文件。
|
|
123
|
+
|
|
92
124
|
## 语言与主题
|
|
93
125
|
|
|
94
126
|
生成英文小票:
|
|
@@ -148,11 +180,17 @@ npx codex-work-receipt@latest --latest --no-open
|
|
|
148
180
|
|
|
149
181
|
| 参数 | 说明 |
|
|
150
182
|
| --- | --- |
|
|
151
|
-
| `--range <name>` | `latest`、`last-hours`、`today`、`last-7-days` 或 `this-week` |
|
|
183
|
+
| `--range <name>` | `latest`、`last-hours`、`custom-range`、`today`、`last-7-days` 或 `this-week` |
|
|
152
184
|
| `--hours <number>` | 统计最近 1~168 小时;等价于 `last-hours` 范围 |
|
|
185
|
+
| `--custom-range` | 交互选择自定义日期或精确时间区间 |
|
|
186
|
+
| `--from <value>` | 自定义开始日期或时间 |
|
|
187
|
+
| `--to <value>` | 自定义结束日期或时间 |
|
|
153
188
|
| `--latest` | 统计最近活跃的 Codex 会话,默认模式 |
|
|
154
189
|
| `--today` | 汇总指定时区今天发生的 Codex 活动 |
|
|
155
190
|
| `--session <id>` | 统计指定的 Codex 会话 |
|
|
191
|
+
| `--select-session` | 交互选择最近的 Codex 会话 |
|
|
192
|
+
| `--project <directory>` | 只统计指定的本地项目目录 |
|
|
193
|
+
| `--select-project` | 交互选择最近项目及统计范围 |
|
|
156
194
|
| `--timezone <name>` | 指定 IANA 时区,例如 `Asia/Shanghai` |
|
|
157
195
|
| `--lang <name>` | `zh-CN`(默认)或 `en` |
|
|
158
196
|
| `--theme <name>` | `classic`、`diner` 或 `payroll` |
|
package/docs/codex-skill.en.md
CHANGED
|
@@ -38,7 +38,7 @@ After installation, ask Codex:
|
|
|
38
38
|
|
|
39
39
|
> Create a Chinese receipt for my latest session.
|
|
40
40
|
|
|
41
|
-
Codex will choose a specific session, the last 1-168 hours, today, the last seven days, or this week, plus the language and theme, then run the CLI and open the generated page. “The last few hours” defaults to three hours.
|
|
41
|
+
Codex will choose a specific session, the current or selected project, a custom date/time range, the last 1-168 hours, today, the last seven days, or this week, plus the language and theme, then run the CLI and open the generated page. “The last few hours” defaults to three hours.
|
|
42
42
|
|
|
43
43
|
If another Codex task is still running and you want the receipt immediately, start a separate local Codex chat. A message sent to the active chat may steer the current run or wait in its queue, depending on Follow-up behavior.
|
|
44
44
|
|
package/docs/codex-skill.md
CHANGED
|
@@ -38,7 +38,7 @@ Skill 会安装到:
|
|
|
38
38
|
|
|
39
39
|
> 用英文生成刚刚这次工作的 AI 打工小票。
|
|
40
40
|
|
|
41
|
-
Codex
|
|
41
|
+
Codex 会根据表达选择单次会话、当前或指定项目、自定义日期/时间、最近 1~168 小时、今天、近七日或本周,以及语言和主题,然后执行命令并打开生成的网页。“最近几个小时”默认统计最近 3 小时。
|
|
42
42
|
|
|
43
43
|
“最近 N 小时”生成的是只用于私人历史的滚动摘要,不进入 AI 供销社;如果用户明确希望参与供销社统计,应改用今日、本周、近七日或指定会话范围。
|
|
44
44
|
|
package/docs/data-schema.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./data-schema.md">中文</a> · <strong>English</strong> · <a href="../README.en.md">Back to README</a></p>
|
|
4
4
|
|
|
5
|
-
The full receipt schema is currently version `2`; rolling-hour summaries remain on compatible version `1`. Each run saves both the full local record and a privacy-safe WeChat import file.
|
|
5
|
+
The full receipt schema is currently version `2`; rolling-hour and custom exact-time summaries remain on compatible version `1`. Custom whole-calendar-date ranges use version `2`. Each run saves both the full local record and a privacy-safe WeChat import file.
|
|
6
6
|
|
|
7
7
|
## Main fields
|
|
8
8
|
|
|
@@ -10,14 +10,28 @@ The full receipt schema is currently version `2`; rolling-hour summaries remain
|
|
|
10
10
|
- `locale`: desktop receipt language, `zh-CN` or `en`
|
|
11
11
|
- `id`: anonymous ID derived from the metrics snapshot
|
|
12
12
|
- `generated_at`: generation time
|
|
13
|
-
- `source`: data source, selected `scope`, and collector version
|
|
13
|
+
- `source`: data source, selected `scope`, `range_kind`, optional `filter_kind`, and collector version
|
|
14
14
|
- `period`: actual activity times, timezone, and `range_start_date` / `range_end_date` calendar boundaries
|
|
15
|
-
- `stats`: turns, messages, tools, Tokens, duration, and
|
|
15
|
+
- `stats`: turns, messages, tools, Tokens, duration, and detailed local efficiency insights
|
|
16
16
|
- `presentation`: default theme, language-neutral `work_profile`, localized role, review, and AI work points
|
|
17
17
|
- `privacy`: explicit declaration of excluded sensitive content
|
|
18
18
|
|
|
19
19
|
The `id` remains stable for the same session, day, or calendar-boundary pair. Regeneration updates the record rather than creating duplicate history. `source.snapshot_hash` detects metric changes.
|
|
20
20
|
|
|
21
|
+
### Detailed local efficiency insights
|
|
22
|
+
|
|
23
|
+
`stats.insights` in the full JSON contains:
|
|
24
|
+
|
|
25
|
+
- `cache_hit_rate`: `cached_input_tokens / input_tokens`, or `0` when input is zero
|
|
26
|
+
- `per_turn`: total Tokens, output Tokens, tool calls, and average completion duration per completed turn
|
|
27
|
+
- `latency_ms.first_token`: sample count plus P50 and P90 first-token latency
|
|
28
|
+
- `latency_ms.turn`: sample count plus P50 and P90 completed-turn duration
|
|
29
|
+
- `activity_by_hour`: 24 hourly buckets in the receipt timezone; each completed or interrupted turn adds one
|
|
30
|
+
- `model_usage`: completed-turn counts attributed to each model
|
|
31
|
+
- `tool_usage`: privacy-safe category counts for terminal, file editing, browser, research, media, multi-agent, planning, integrations, and other tools
|
|
32
|
+
|
|
33
|
+
Percentiles use linear interpolation between adjacent samples and are rounded to milliseconds. Tool classification retains only stable categories, not raw tool names, arguments, commands, or output.
|
|
34
|
+
|
|
21
35
|
## WeChat import file
|
|
22
36
|
|
|
23
37
|
Each run writes a `*.cwr.json` file beside the HTML:
|
|
@@ -37,6 +51,8 @@ Each run writes a `*.cwr.json` file beside the HTML:
|
|
|
37
51
|
|
|
38
52
|
`payload` is exactly the compact structure obtained after decoding a QR payload. `integrity.digest` is a full SHA-256 over canonical payload JSON. It detects corruption but is not a source signature. The mini program must treat every file as untrusted input, enforce a size limit, validate versions, fields, numeric bounds, unique fact IDs, manifests, and content hashes, then persist atomically only after confirmation.
|
|
39
53
|
|
|
54
|
+
To preserve compatibility with existing mini programs, `stats.insights` currently stays in the local full JSON and HTML and is not added to the compact `cwr1` / `cwr2` payload. The compact payload continues to carry the existing accounting metrics.
|
|
55
|
+
|
|
40
56
|
Canonicalization recursively sorts every object's keys using JavaScript's default string order (UTF-16 code units), preserves array order, uses JSON primitive types, and emits no whitespace or trailing newline. Calculate SHA-256 over the resulting UTF-8 bytes. Use the digest in `docs/fixtures/cwr-file-v1.json` to cross-check another implementation.
|
|
41
57
|
|
|
42
58
|
A cross-repository compatibility fixture is available at `docs/fixtures/cwr-file-v1.json`.
|
|
@@ -57,7 +73,7 @@ cwr2p.<transferId>.<partIndex>.<partCount>.<totalChecksum>.<partChecksum>.<chunk
|
|
|
57
73
|
|
|
58
74
|
The mini program validates the prefix and checksum before decompressing and parsing the payload. Future schema versions use the compact `v` field for compatibility.
|
|
59
75
|
|
|
60
|
-
Compact field `o` explicitly carries `latest`, `session`, `last-hours`, `today`, `last-7-days`, or `this-week`. `d[
|
|
76
|
+
Compact field `o` explicitly carries `latest`, `session`, `last-hours`, `custom-range`, `today`, `last-7-days`, or `this-week`. `d[0]` / `d[1]` carry exact boundaries and `d[3]` / `d[4]` carry calendar-date boundaries. Today, last-seven-days, this-week, and custom whole-calendar-date receipts use cwr2 canonical facts. `last-hours` and custom exact-time ranges may cut through a calendar day, so they use compatible cwr1 summaries to avoid colliding with session-day fact identities; they stay in private history and do not participate in AI Work Cooperative accounting. Project filtering keeps the selected time scope and transfers only filtered anonymous facts, never the project name, repository URL, or path. The updated mini program can still import older QR codes.
|
|
61
77
|
|
|
62
78
|
When the same Codex session has multiple append-only log revisions, the generator keeps the more complete revision. Fact IDs in a cwr2 manifest must be unique; an identity collision stops QR generation instead of emitting an invalid payload.
|
|
63
79
|
|
package/docs/data-schema.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><strong>中文</strong> · <a href="./data-schema.en.md">English</a> · <a href="../README.md">返回 README</a></p>
|
|
4
4
|
|
|
5
|
-
当前完整记录结构版本为 `2
|
|
5
|
+
当前完整记录结构版本为 `2`;滚动小时和自定义精确时间摘要为了兼容继续使用版本 `1`。自定义完整自然日范围使用版本 `2`。每次生成都会在本机保存完整结构记录和一份脱敏微信导入文件。
|
|
6
6
|
|
|
7
7
|
## 主要字段
|
|
8
8
|
|
|
@@ -10,14 +10,28 @@
|
|
|
10
10
|
- `locale`:桌面小票语言,支持 `zh-CN` 和 `en`
|
|
11
11
|
- `id`:根据统计快照生成的匿名 ID
|
|
12
12
|
- `generated_at`:生成时间
|
|
13
|
-
- `source`:数据来源、`scope`
|
|
13
|
+
- `source`:数据来源、`scope` 统计范围、`range_kind` 区间精度、可选的 `filter_kind` 和采集器版本
|
|
14
14
|
- `period`:实际活动起止时间、时区,以及 `range_start_date` / `range_end_date` 自然日边界
|
|
15
|
-
- `stats`:轮次、消息、工具、Token
|
|
15
|
+
- `stats`:轮次、消息、工具、Token、时长,以及本地详细效率洞察
|
|
16
16
|
- `presentation`:默认主题、语言无关的 `work_profile`、本地化工种、点评和 AI 工分
|
|
17
17
|
- `privacy`:明确声明不包含的敏感内容
|
|
18
18
|
|
|
19
19
|
同一个会话、同一天或同一组自然日边界的 `id` 保持稳定;重复生成会更新该记录。`source.snapshot_hash` 用于识别统计内容是否发生变化。
|
|
20
20
|
|
|
21
|
+
### 本地详细效率洞察
|
|
22
|
+
|
|
23
|
+
完整 JSON 的 `stats.insights` 包含:
|
|
24
|
+
|
|
25
|
+
- `cache_hit_rate`:`cached_input_tokens / input_tokens`,输入为零时记为 `0`
|
|
26
|
+
- `per_turn`:按已完成轮次计算的总 Token、输出 Token、工具调用和平均完成耗时
|
|
27
|
+
- `latency_ms.first_token`:首 Token 延迟的样本数、P50 和 P90
|
|
28
|
+
- `latency_ms.turn`:已完成整轮耗时的样本数、P50 和 P90
|
|
29
|
+
- `activity_by_hour`:按小票时区统计的 24 个小时槽;每个完成或中断轮次计一次
|
|
30
|
+
- `model_usage`:模型对应的完成轮次数量
|
|
31
|
+
- `tool_usage`:脱敏工具类别及数量,包括终端、文件编辑、浏览器、检索、媒体、多代理、规划、外部集成和其他类别
|
|
32
|
+
|
|
33
|
+
百分位使用相邻样本线性插值后取整。工具分类只保留稳定类别,不保留原始工具名、参数、命令或输出。
|
|
34
|
+
|
|
21
35
|
## 微信导入文件
|
|
22
36
|
|
|
23
37
|
每次生成都会在 HTML 旁边写入一个 `*.cwr.json`:
|
|
@@ -37,6 +51,8 @@
|
|
|
37
51
|
|
|
38
52
|
`payload` 与二维码解压后的精简结构完全相同。`integrity.digest` 是对规范化 payload JSON 计算的完整 SHA-256,用于发现损坏,但不是来源签名。小程序必须把文件视为不可信输入,限制文件大小,校验版本、字段、数值范围、factId 唯一性、manifest 和 content hash,并在用户确认后原子入库。
|
|
39
53
|
|
|
54
|
+
为保持现有小程序兼容,`stats.insights` 当前只存在于本地完整 JSON 和 HTML,不进入 `cwr1` / `cwr2` 精简载荷。精简载荷继续携带原有记账统计。
|
|
55
|
+
|
|
40
56
|
规范化规则是:递归按 JavaScript 默认字符串排序(UTF-16 code unit 顺序)排列每个对象的键,保持数组元素顺序,使用 JSON 原始类型,不添加空白或换行,再对生成的 UTF-8 字节计算 SHA-256。兼容实现可用 `docs/fixtures/cwr-file-v1.json` 的 digest 做交叉验证。
|
|
41
57
|
|
|
42
58
|
兼容测试夹具见 `docs/fixtures/cwr-file-v1.json`。
|
|
@@ -57,7 +73,7 @@ cwr2p.<transferId>.<partIndex>.<partCount>.<totalChecksum>.<partChecksum>.<chunk
|
|
|
57
73
|
|
|
58
74
|
小程序应检查前缀和校验值,再解压并解析数据。未来结构升级通过 `v` 字段兼容。
|
|
59
75
|
|
|
60
|
-
精简字段 `o` 显式携带 `latest`、`session`、`last-hours`、`today`、`last-7-days` 或 `this-week`;`d[3]`
|
|
76
|
+
精简字段 `o` 显式携带 `latest`、`session`、`last-hours`、`custom-range`、`today`、`last-7-days` 或 `this-week`;`d[0]` / `d[1]` 携带精确边界,`d[3]` / `d[4]` 携带自然日边界。今日、近 7 日、本周和自定义完整自然日范围使用 cwr2 canonical facts。`last-hours` 与自定义精确时间区间可能截断自然日,为避免与“会话 × 自然日”事实发生身份冲突,使用兼容的 cwr1 摘要载荷,只进入私人历史、不参与 AI 供销社统计。项目筛选沿用所选时间范围,只传输筛选后的匿名事实,不传输项目名称、仓库地址或路径。旧二维码仍可由新版小程序导入。
|
|
61
77
|
|
|
62
78
|
同一 Codex 会话出现多份 append-only 日志修订时,生成器只采用更完整的修订;cwr2 manifest 中的 factId 必须唯一,检测到身份冲突时不会输出可扫码二维码。
|
|
63
79
|
|
package/docs/privacy.en.md
CHANGED
|
@@ -7,10 +7,13 @@ AI Work Receipt reads Codex sessions, computes metrics, and renders receipts loc
|
|
|
7
7
|
## What it reads
|
|
8
8
|
|
|
9
9
|
- `~/.codex/sessions/**/*.jsonl` by default
|
|
10
|
-
- Event types, timestamps, model names, and numerical metadata
|
|
11
|
-
- Metrics such as turns, tool calls, Tokens, duration, and
|
|
10
|
+
- Event types, timestamps, model names, privacy-safe tool categories, and numerical metadata
|
|
11
|
+
- Metrics such as turns, tool calls, Tokens, duration, interruptions, cache hit rate, latency percentiles, and hourly distribution
|
|
12
|
+
- A session repository URL or working directory only when the user selects or specifies a project for local grouping
|
|
12
13
|
|
|
13
|
-
Session logs are parsed locally in bounded chunks, one JSONL row at a time.
|
|
14
|
+
Session logs are parsed locally in bounded chunks, one JSONL row at a time. Raw tool names are immediately mapped to stable categories. Prompts, responses, images, raw tool names, tool arguments, commands, and tool output are discarded from the in-memory statistics representation.
|
|
15
|
+
|
|
16
|
+
Project filtering groups Git sessions by repository identity and non-Git sessions by working directory. The raw repository URL or path is immediately transformed with a random key that exists only on this computer. A project name appears only in the interactive terminal selector; HTML, full records, history, and WeChat import files do not store the original project name, repository URL, or path.
|
|
14
17
|
|
|
15
18
|
## What receipts exclude
|
|
16
19
|
|
|
@@ -23,6 +26,8 @@ Session logs are parsed locally in bounded chunks, one JSONL row at a time. Afte
|
|
|
23
26
|
|
|
24
27
|
Generated HTML, full JSON, and privacy-safe `.cwr.json` WeChat import files are written to `codex-work-receipt-output/` by default. Deduplicated history is stored under `~/.codex-work-receipt/`. Use `--output` and `--data-dir` to change these locations.
|
|
25
28
|
|
|
29
|
+
The first project-filtered run creates a permission-restricted `project-identity.key` in the local data directory. It is used only to create project identities that cannot be correlated across installations.
|
|
30
|
+
|
|
26
31
|
The runtime does not upload Codex session data to a project server. The initial or updated package download performed by `npx` is a normal package-manager network request and does not contain local session data.
|
|
27
32
|
|
|
28
33
|
If the user actively sends a `.cwr.json` file to a WeChat chat, that file passes through WeChat's file-transfer system. The user can then select it in the companion mini program, preview it, and save it to the anonymous account database for history recovery and canonical deduplication. The file excludes prompts, response text, source code, project paths, file names, and original session IDs. Joining AI Cooperative aggregate statistics remains a separate user choice.
|
package/docs/privacy.md
CHANGED
|
@@ -7,10 +7,13 @@ AI 打工小票在本机读取 Codex 会话,并在本机完成统计和渲染
|
|
|
7
7
|
## 会读取什么
|
|
8
8
|
|
|
9
9
|
- 默认读取 `~/.codex/sessions/**/*.jsonl`
|
|
10
|
-
-
|
|
11
|
-
- 计算轮次、工具调用、Token
|
|
10
|
+
- 提取事件类型、时间、模型名称、脱敏工具类别和数字统计
|
|
11
|
+
- 计算轮次、工具调用、Token、时长、中断次数、缓存命中率、延迟分位与小时分布等指标
|
|
12
|
+
- 仅在用户选择或指定项目时读取会话的仓库地址或工作目录,用于本地归组
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
会话日志会在本机分块逐行解析。每行解析后只保留统计所需字段;工具名会立即映射为稳定类别。Prompt、回复、图片、原始工具名、工具参数、命令和工具输出不会保留在内存统计对象中。
|
|
15
|
+
|
|
16
|
+
项目筛选优先按 Git 仓库身份归组,非 Git 项目按工作目录归组。原始仓库地址或路径会立即通过只存在于本机的随机密钥转换为匿名项目身份;项目名称仅出现在交互式终端列表中。HTML、完整记录、历史和微信导入文件均不保存原始项目名称、仓库地址或路径。
|
|
14
17
|
|
|
15
18
|
## 不会写入小票什么
|
|
16
19
|
|
|
@@ -23,6 +26,8 @@ AI 打工小票在本机读取 Codex 会话,并在本机完成统计和渲染
|
|
|
23
26
|
|
|
24
27
|
生成的 HTML、完整 JSON 和脱敏 `.cwr.json` 微信导入文件默认位于当前目录的 `codex-work-receipt-output/`,去重历史保存在 `~/.codex-work-receipt/`。可以使用 `--output` 和 `--data-dir` 修改位置。
|
|
25
28
|
|
|
29
|
+
首次使用项目筛选时会在本地数据目录创建权限受限的 `project-identity.key`,只用于生成不可跨设备关联的项目匿名身份。
|
|
30
|
+
|
|
26
31
|
运行期间不会把 Codex 会话数据上传到本项目的服务器。通过 `npx` 首次下载或更新 npm 包属于包管理器的正常网络请求,不包含本地会话内容。
|
|
27
32
|
|
|
28
33
|
如果用户主动把 `.cwr.json` 发送到微信聊天,文件会经过微信的文件传输体系。随后用户可在配套小程序中选择该聊天文件、确认内容并保存到匿名账号数据库,用于历史恢复和规范去重。文件不会包含 Prompt、回复正文、代码、项目路径、文件名或原始会话 ID;是否参与 AI 供销社匿名统计仍由用户单独决定。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-work-receipt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Generate a privacy-first Codex work receipt from local session metadata.",
|
|
6
6
|
"author": "a-bai-2026",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"receipt": "node src/cli.mjs",
|
|
29
29
|
"test": "node --test",
|
|
30
|
-
"check": "node --check src/cli.mjs && node --check src/auto-hook.mjs && node --check src/auto-runner.mjs && node --check src/core/auto-mode.mjs && node --check src/core/generator.mjs && node --check src/core/mode-selector.mjs && node --check src/lib/files.mjs && node --check src/core/skill-installer.mjs && node --check src/core/pet-installer.mjs && node --check src/core/range.mjs",
|
|
30
|
+
"check": "node --check src/cli.mjs && node --check src/auto-hook.mjs && node --check src/auto-runner.mjs && node --check src/core/auto-mode.mjs && node --check src/core/generator.mjs && node --check src/core/mode-selector.mjs && node --check src/core/project-identity.mjs && node --check src/core/selector.mjs && node --check src/renderers/html.mjs && node --check src/parsers/codex.mjs && node --check src/lib/files.mjs && node --check src/core/skill-installer.mjs && node --check src/core/pet-installer.mjs && node --check src/core/range.mjs",
|
|
31
31
|
"prepublishOnly": "npm test && npm run check"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ai-work-receipt
|
|
3
|
-
description: Generate and open a privacy-first local AI work receipt from Codex session metadata. Use when the user asks Codex or 票仔 to create, regenerate, open, or locate an “AI 打工小票”, “工票”, or “AI work receipt” for
|
|
3
|
+
description: Generate and open a privacy-first local AI work receipt from Codex session metadata. Use when the user asks Codex or 票仔 to create, regenerate, open, or locate an “AI 打工小票”, “工票”, or “AI work receipt” for a session, project, custom range, recent hours, today, the last seven days, or this week, in Chinese or English. Do not use for real invoices, salary calculations, API billing, or non-Codex activity.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# AI 打工小票
|
|
@@ -13,17 +13,22 @@ Use the published `codex-work-receipt` CLI as the only statistics and rendering
|
|
|
13
13
|
- Use `--hours <N>` for “最近 N 小时”“过去 N 小时” or “近 N 小时”. Accept integers from 1 to 168.
|
|
14
14
|
- Use `--hours 3` for an unspecified “最近几个小时”“过去几个小时” request.
|
|
15
15
|
- Use `--hours 12` for “最近半天”.
|
|
16
|
-
- Treat `--hours` receipts as rolling summaries for private history only. They do not participate in AI Work Cooperative accounting; if the user asks for an accountable cooperative receipt, use today, this week, the last seven days,
|
|
16
|
+
- Treat `--hours` receipts as rolling summaries for private history only. They do not participate in AI Work Cooperative accounting; if the user asks for an accountable cooperative receipt, use today, this week, the last seven days, a specific session, or custom whole calendar dates instead.
|
|
17
17
|
- Use `--today` for “今天”“今日”“今天全部工作” or a daily summary.
|
|
18
18
|
- Use `--range last-7-days` for “最近七天”“近 7 日” or a rolling seven-calendar-day summary.
|
|
19
19
|
- Use `--range this-week` for “本周”“这周” or a Monday-to-now summary.
|
|
20
|
+
- Use `--from YYYY-MM-DD --to YYYY-MM-DD` when the user gives whole start and end dates. The end date is inclusive and the receipt can produce cwr2 canonical facts.
|
|
21
|
+
- Use `--from YYYY-MM-DDTHH:mm --to YYYY-MM-DDTHH:mm` when the user gives exact local times. Explain that the result is a private cwr1 summary and does not participate in cooperative accounting.
|
|
22
|
+
- Use `--custom-range` when the user explicitly wants to enter a custom range interactively.
|
|
23
|
+
- Use `--select-session` when the user wants to choose a recent session interactively. Use `--session <id>` only when an exact session ID is already available from the user or CLI output.
|
|
24
|
+
- Add `--project .` when the user asks for the current project. Use `--select-project` when the user wants to choose among recent projects interactively. Combine a project filter with the requested time range.
|
|
20
25
|
- Use `--theme classic` for “经典白票” or no specified theme.
|
|
21
26
|
- Use `--theme diner` for “复古粉票”“粉色小票” or “diner”.
|
|
22
27
|
- Use `--theme payroll` for “夜班绿票”“绿色小票” or “payroll”.
|
|
23
28
|
- Add `--no-open` only when the user asks not to open the browser.
|
|
24
29
|
- Add `--timezone <IANA name>` only when the user explicitly requests another timezone.
|
|
25
30
|
- Use `--lang en` when the user requests English or asks for the receipt in English. Use `--lang zh-CN` otherwise.
|
|
26
|
-
- If the user asks to choose interactively, run the CLI without a range flag so it can show the local selector.
|
|
31
|
+
- If the user asks to choose a range interactively without specifying session or project selection, run the CLI without a range flag so it can show the complete local selector.
|
|
27
32
|
- If the user asks for an unsupported custom date range, explain the available ranges and do not invent flags.
|
|
28
33
|
- Treat requests addressed to “票仔” the same as direct AI work receipt requests, for example “票仔,开今天的票”.
|
|
29
34
|
|
|
@@ -36,11 +41,11 @@ Use the published `codex-work-receipt` CLI as the only statistics and rendering
|
|
|
36
41
|
npx --yes codex-work-receipt@latest --latest --lang zh-CN --theme classic
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
Replace only the mode and
|
|
44
|
+
Replace only the mode, optional project filter, and presentation flags according to the request.
|
|
40
45
|
|
|
41
46
|
3. Let the CLI open the generated HTML unless the user requested `--no-open`.
|
|
42
47
|
4. On success, report both the HTML path and the `.cwr.json` WeChat import file path. Tell the user to send the import file to WeChat File Transfer, open the official mini program, and choose “Import from chat file”. If the CLI reports that a single data QR is available, mention scanning as an optional shortcut; never instruct the user to scan multipart codes.
|
|
43
|
-
5. When `--hours` was used, explicitly state that the receipt is a private
|
|
48
|
+
5. When `--hours` or an exact date-time custom range was used, explicitly state that the receipt is a private summary and will not enter AI Work Cooperative statistics.
|
|
44
49
|
|
|
45
50
|
## Privacy and failures
|
|
46
51
|
|
package/src/auto-runner.mjs
CHANGED
|
@@ -42,7 +42,7 @@ function crossesConfiguredDay(workReceiptHome, left, right) {
|
|
|
42
42
|
return dateKey(left, timezone) !== dateKey(right, timezone);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function acquireLock(lockPath, pendingPath) {
|
|
45
|
+
function acquireLock(lockPath, pendingPath, pendingAt = new Date()) {
|
|
46
46
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
47
47
|
try {
|
|
48
48
|
const descriptor = fs.openSync(lockPath, "wx");
|
|
@@ -55,12 +55,12 @@ function acquireLock(lockPath, pendingPath) {
|
|
|
55
55
|
const age = Date.now() - fs.statSync(lockPath).mtimeMs;
|
|
56
56
|
if (age > LOCK_STALE_MS) {
|
|
57
57
|
fs.rmSync(lockPath, { force: true });
|
|
58
|
-
return acquireLock(lockPath, pendingPath);
|
|
58
|
+
return acquireLock(lockPath, pendingPath, pendingAt);
|
|
59
59
|
}
|
|
60
60
|
} catch {
|
|
61
61
|
// Another worker may have released the lock between checks.
|
|
62
62
|
}
|
|
63
|
-
fs.writeFileSync(pendingPath, `${
|
|
63
|
+
fs.writeFileSync(pendingPath, `${pendingAt.toISOString()}\n`, "utf8");
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -124,7 +124,7 @@ async function generateOnce({ workReceiptHome, now = new Date() }) {
|
|
|
124
124
|
export async function runAutomaticReceipt({ workReceiptHome, now = new Date(), retryAfterCoalesce = true }) {
|
|
125
125
|
const lockPath = path.join(workReceiptHome, "auto.lock");
|
|
126
126
|
const pendingPath = path.join(workReceiptHome, "auto.pending");
|
|
127
|
-
if (!acquireLock(lockPath, pendingPath)) {
|
|
127
|
+
if (!acquireLock(lockPath, pendingPath, now)) {
|
|
128
128
|
if (!retryAfterCoalesce) return { status: "coalesced" };
|
|
129
129
|
await delay(800);
|
|
130
130
|
if (fs.existsSync(lockPath)) return { status: "coalesced" };
|