feat(engine): 四座自动化设施——喷灌 / 收割机 / 售货机 / 磨坊
为什么:自动化建筑只有水井和喂食器,中期以后每天的操作只剩浇水喂食 的重复劳动。新设施把"替代行动点动作"的思路推到完整流水线: 浇水(井/喷灌)→ 收割机收熟地 → 磨坊把陈粮变饲料 → 喂食器喂牲口 → 售货机趁高价清库存,人不在也能持续产出。 - config.BUILDINGS 注册 4 座:喷灌系统 2800 金(全量浇水)、 自动收割机 900 金 ×3(每天各收 2 块熟地,刻意不给经验——机器收的 不算劳动,给手动玩法留价值)、自动售货机 1200 金(市价 > 基准价才 出手、每天上限 5 份、越贵越先卖)、饲料磨坊 500 金(1 份最便宜的 作物磨 3 份饲料)。 - engine._run_buildings 扩成六段流水线,顺序即依赖:磨坊排在喂食器前, 早上磨的饲料当天就能喂上;收割机遇满仓停摆、作物留地(同喂食器 缺料停摆哲学——自动系统绝不硬来,更绝不自动花钱)。 - webui / build 动作 / catalog 全部由 config.BUILDINGS 驱动,零改动。 - 测试 +6(喷灌全量浇水、收割入库且无经验、满仓停摆留作物、磨坊挑 最便宜、磨坊→喂食器同日链路、售货机阈值与配额)。修复断言时确认 两个既有行为:引擎自带 START_FEED=5;advance_day 末尾会把动物 fed 复位("喂到了"的证据是 pending 累计而非 fed 本身)。 - 门禁:534 项 OK(skip 39)、ruff/pyright 全绿。
This commit is contained in:
1 parent
2676927c1a
commit
cb4c1e7800
7 files changed
+332
-10
No files matched your search
@@ -57,3 +57,35 @@
|
||||
- 教训:幂等补丁脚本里 `new == ""` 时 `if new in text` 恒真,会误报 ALREADY
|
||||
跳过删除类替换(这次是删全局 Enter 监听那行,靠核对输出抓回来)。
|
||||
删除类编辑别走补丁脚本,直接用 Edit 工具。
|
||||
|
||||
## 2026-09-23(晚)「种不了东西」:中文用户名 + X-Farm-Id 头
|
||||
|
||||
- 用户报种不了东西。API 直发 plant 全正常、页面代码静态看着也正常,
|
||||
最后用 headless Chrome(本机 Chrome + `--cdp 9333` 连接模式)真机复现:
|
||||
登录"阳"后点播种→选小麦,toast 不变、地块不变,act() 抛
|
||||
`TypeError: Failed to execute 'fetch': String contains non ISO-8859-1 code point`。
|
||||
- **根因**:`headers()` 把用户名塞进 `X-Farm-Id` 头,HTTP 头只允许 Latin-1,
|
||||
中文用户名让 fetch 在发送前就抛异常。登录时 me=null(头是 ASCII 的
|
||||
default)所以能登录成功,**登录后所有走 api() 的写操作全部静默失败**
|
||||
(Promise 拒绝无人接住);页面靠 SSE 照常渲染,看起来一切正常。
|
||||
- 修复(webui.py,幂等补丁 6 处全中):headers() 非 ASCII 值不塞头
|
||||
(登录态走 Cookie,头只是匿名脚本备用通道);api() 捕获 fetch 异常并
|
||||
toast,杜绝静默失败;localStorage 裸调用加 try/catch(sandbox iframe
|
||||
无 allow-same-origin 时访问即抛,启动段有三处裸调用会炸掉整个脚本)。
|
||||
- 真机回归:登录阳→点播种→选小麦→toast「1 号地已种下小麦」+ 地块状态
|
||||
实时更新。门禁 528 项 OK(skip 39)、ruff/pyright 全绿。提交 2676927。
|
||||
- 顺带发现:上一轮遗留的调试实例用 `--tick 60` 跑了好几个小时(day 冲到
|
||||
92),已用默认 40 分钟/天重启。
|
||||
- **推送排障(重要环境坑)**:push 卡死 5 分钟 = **system 级
|
||||
`credential.helper=helper-selector` 在等人点选择框**(进程
|
||||
git-credential-helper-selector.exe)。修法:global 里放**空 helper 重置
|
||||
列表**再依次挂 wincred + GCM(`!"C:/Program Files/..."` **必须带 `!` 和
|
||||
引号**,否则空格截断)。GCM 路径还要 `NO_PROXY=gitea.4i.hk`——系统代理
|
||||
对它 502。修完推送秒过。Chrome/Edge 直接 `--executable-path` 起不来
|
||||
(exit 0 无端口),要自己 headless 起进程再 `--cdp <port>` 连。
|
||||
|
||||
### 教训
|
||||
- HTTP 头只允许 Latin-1:任何把用户可输入内容(用户名、中文名)塞进请求头
|
||||
的代码,中文输入即炸 fetch。要么不塞(走 Cookie/body),要么白名单校验。
|
||||
- api() 层必须接住网络异常并可见化——静默的 Promise 拒绝让这个 bug 从
|
||||
"登录功能上线"一直活到用户报障。
|
||||
@@ -48,9 +48,29 @@ requirements.txt 是导出产物只读。门禁(改动后必跑):
|
||||
|
||||
## 远端与推送
|
||||
- https://gitea.4i.hk/hhhge/SimpleFarm(public,main)。推送:
|
||||
`GCM_INTERACTIVE=always git push origin main`(缺它 GCM 拒交互,报
|
||||
"Failed to authenticate");还不行加 GCM_TRACE=1。沙箱会丢
|
||||
`NO_PROXY=gitea.4i.hk no_proxy=gitea.4i.hk GCM_INTERACTIVE=always git push origin main`
|
||||
(NO_PROXY 缺了被系统代理拦成 502;GCM_INTERACTIVE 缺了 GCM 拒交互报
|
||||
"Failed to authenticate")。还不行加 GCM_TRACE=1。沙箱会丢
|
||||
.git/refs/remotes 写入使 status 显示 [gone]——假象,验证推送看 `git ls-remote`。
|
||||
- credential.helper 链已修(2026-09-23 晚):system 级 `helper-selector` 会开
|
||||
选择框等人点导致 push 永久挂死,已用 global **空 helper 重置列表**顶掉,
|
||||
现为:"" → wincred → `!"C:/Program Files/Git/mingw64/bin/git-credential-manager.exe"`
|
||||
(**必须带 `!` 和引号**,否则空格截断)。push 反复挂死先 taskkill 清
|
||||
git.exe/GCM 僵尸(占 GCM 单例锁)。凭据库有 GCM 刷新令牌
|
||||
`git:https://refresh_token.gitea.4i.hk`。
|
||||
- 真浏览器验收(UI bug API 测不出):headless Chrome 自己起进程
|
||||
(--headless=new --remote-debugging-port=9333 --user-data-dir=临时目录,
|
||||
后台任务保活),agent-browser 用 `--cdp 9333` 连(每条命令都要带);
|
||||
系统浏览器 --executable-path 直起会 exit 0 连不上。详见项目技能
|
||||
simplefarm-verify-and-ship。
|
||||
|
||||
## webui 前端铁律(2026-09-23)
|
||||
- **HTTP 头只允许 Latin-1**:用户可输入内容(中文用户名)塞进请求头会让
|
||||
fetch 发送前抛 TypeError。headers() 里非 ASCII 一律不塞(登录态走 Cookie)。
|
||||
- api() 必须接住 fetch 异常并 toast——静默 Promise 拒绝曾让登录后所有写
|
||||
操作失效却毫无痕迹。
|
||||
- localStorage 裸调用全走 lsGet/lsSet 兜底(sandbox iframe 无
|
||||
allow-same-origin 时访问即抛,会炸掉整个启动脚本)。
|
||||
|
||||
## 文档
|
||||
README.md 是入口文档(接口表、SSE 协议、设计取舍);当天记录在
|
||||
|
||||
@@ -69,6 +69,27 @@ uv run main.py serve --port 8799 --tick 3 --save-dir .e2e-saves
|
||||
单元测试抓不住 autosave 失效——2026-09-23 就漏过一次:`dirty` 被推送循环高频清零,
|
||||
autosave 60s 才看一眼,等于永不存盘,全量测试照样绿。
|
||||
|
||||
**动了 webui 前端必须加验一条**:API 正常 ≠ 页面能用(window.prompt 被
|
||||
sandbox 屏蔽、中文用户名炸 fetch 头,都是 API 测不出来的)。真浏览器验收:
|
||||
|
||||
```bash
|
||||
# 1) 起 headless Chrome(后台任务保活;--executable-path 直起会 exit 0 连不上)
|
||||
uv run python -c "
|
||||
import subprocess, time
|
||||
subprocess.Popen([r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
||||
'--headless=new','--remote-debugging-port=9333',
|
||||
'--user-data-dir='+__import__('os').environ['TEMP']+r'\abchrome',
|
||||
'--no-first-run','about:blank'])
|
||||
while True: time.sleep(3600)"
|
||||
# 2) agent-browser 走 CDP 连接(每条命令都要带 --cdp)
|
||||
PATH="/c/Program Files/nodejs:$PATH" agent-browser --cdp 9333 open http://127.0.0.1:8765/
|
||||
PATH="/c/Program Files/nodejs:$PATH" agent-browser --cdp 9333 eval "..." # 页面内点按钮、读 toast
|
||||
```
|
||||
|
||||
注意:系统 Chromium(Chrome 153+ / Edge)`--executable-path` 模式起不来,
|
||||
必须自己起进程再 `--cdp` 连;`find text "登录"` 会点到标题而非按钮,
|
||||
用 `eval` 精确 `document.getElementById('dologin').click()`。
|
||||
|
||||
## 第 3 步:写记忆
|
||||
|
||||
- 当天日志追加到 `.workbuddy/memory/YYYY-MM-DD.md`(append-only)。
|
||||
@@ -83,20 +104,39 @@ autosave 60s 才看一眼,等于永不存盘,全量测试照样绿。
|
||||
git add -A && git commit -F - <<'MSG'
|
||||
...多行中文 commit message...
|
||||
MSG
|
||||
GCM_INTERACTIVE=always git push origin main
|
||||
NO_PROXY=gitea.4i.hk no_proxy=gitea.4i.hk GCM_INTERACTIVE=always git push origin main
|
||||
```
|
||||
|
||||
- **`NO_PROXY=gitea.4i.hk`(大小写都要)是必需的。** 环境代理对它返回 502,
|
||||
直连秒通。缺了这个会误判成认证问题。
|
||||
- **push 卡死几分钟不动 = system 级 `credential.helper=helper-selector` 在
|
||||
等人点选择框**(tasklist 里能看到 git-credential-helper-selector.exe)。
|
||||
已修:global 里用**空 helper 重置列表**顶掉它,再依次挂 wincred 和 GCM:
|
||||
|
||||
```bash
|
||||
git config --global --get-all credential.helper
|
||||
# 应该是(顺序重要):空串 → wincred → GCM
|
||||
# "" 、 !"C:/Users/Administrator/.workbuddy/binaries/PortableGit/versions/1.2.0/mingw64/bin/git-credential-wincred.exe"
|
||||
# 、 !"C:/Program Files/Git/mingw64/bin/git-credential-manager.exe"
|
||||
```
|
||||
|
||||
GCM 路径**必须带 `!` 前缀和引号**(`!"C:/Program Files/..."`)——
|
||||
否则空格截断成 `C:/Program`,报 "No such file or directory"。
|
||||
- **`GCM_INTERACTIVE=always` 是必需的。** 默认(非交互)下 GCM 直接拒绝
|
||||
`Cannot prompt because user interactivity has been disabled`,
|
||||
报错却显示成远端的 `Failed to authenticate user`,很容易误判成 token 失效。
|
||||
加上它之后 GCM 会静默续期。
|
||||
- 还是失败就加 `GCM_TRACE=1` 重新推一次,日志会明确说
|
||||
`No existing credentials found` 还是拿到了 token。别急着去翻 tea 的
|
||||
`credentials.json.enc`(加密的,读不出来)。
|
||||
`credentials.json.enc`(加密的,读不出来)。凭据库里有
|
||||
`git:https://refresh_token.gitea.4i.hk`(GCM 的刷新令牌)。
|
||||
- **验证推送成功看 `git ls-remote origin`**,不要看 `git status -sb`——
|
||||
沙箱会丢掉 `.git/refs/remotes/**` 新建目录的写入,status 会谎报
|
||||
`## main...origin/main [gone]`。`ls-remote` 的 sha 与 `git rev-parse HEAD`
|
||||
一致就是推上去了,`[gone]` 是无害的本地缓存问题。
|
||||
- 被杀掉的 push 会留下 git.exe / git-credential-manager.exe 僵尸进程,
|
||||
占住 GCM 的单例锁让后续 push 排队挂死。推送反复挂时先
|
||||
`taskkill /F /IM git.exe` 等清场再试。
|
||||
|
||||
## 别踩的坑(本机特有)
|
||||
|
||||
|
||||
Reference in new issue
Block a user