CookCLI 0.39.0 配置参数详解
CookCLI 0.39.0 配置面全解:两级配置查找顺序、COOK_CONFIG_DIR 等四个环境变量、aisle.conf/pantry.conf/users.toml 文件格式与 db 数据仓、食谱 frontmatter 约定、cook server 参数、语言与单位设置及生产配置模板,Ubuntu 24.04 实测。
CookCLI 0.39.0 配置参数详解
目标:逐项讲清 CookCLI 0.39.0 的全部配置面——配置查找顺序、四个环境变量、
aisle.conf/pantry.conf/users.toml三种文件格式、食谱 frontmatter 语法约定、cook server命令行参数、语言与单位设置,并给出生产配置模板。全部行为基于 Ubuntu 24.04 服务器上cookcli 0.39.0 - in food we trust实测回显。
一、环境信息
| 项 | 值 |
|---|---|
| 操作系统 | Ubuntu 24.04 LTS(x86_64) |
| 软件 | CookCLI v0.39.0(预编译 musl 二进制裸装,安装路径见本专栏部署篇) |
| 版本回显 | cook --version → cookcli 0.39.0 - in food we trust |
| 食谱库 | /root/my-recipes/(cook seed 生成的官方示例库,12 份 .cook + 2 份 .menu) |
| 全局配置目录 | ~/.config/cook/(Linux 默认;macOS 为 ~/Library/Application Support/cook/,Windows 为 %APPDATA%\cook\config\) |
| 服务器形态 | cook server(默认端口 9080)与 cook build web 静态站点 |
CookCLI 没有 cook.conf 这样的单一主配置文件——它的「配置」分散在五处,本文按此展开:
- 食谱文件自身:
.cook/.menu纯文本 + YAML frontmatter 元数据; - 两份数据配置:
aisle.conf(购物清单分区)、pantry.conf(库存,TOML); - 服务器身份文件:
users.toml及其伴生的auth-secret、revoked-sessions; - 环境变量:
COOK_CONFIG_DIR、COOK_USERS_FILE、COOK_CORS_ORIGIN、COOK_RECIPES_ONLY; - 命令行参数:各子命令 flags,
cook server尤甚。
二、前置条件
- 已安装 CookCLI 0.39.0 并可执行
cook --version(裸装或容器内均可,命令面一致); - 有一个食谱目录用于实验;无则一条命令生成官方示例库:
cook seed /root/my-recipes
ls /root/my-recipes
# 2 Day Plan.menu Breakfast config db lamb-chops.cook Neapolitan Pizza.cook
# README.md reports Risotto.cook Salads Shared Thai Green Curry.cook Weekly Plan.menu
示例库自带 config/aisle.conf、config/pantry.conf、db/、reports/,正好覆盖本文全部配置面。
三、配置查找顺序与修改方式
配置文件按「局部优先、全局回退」两级查找:
./config/——食谱目录内(-b/--base-path指定的根之下),最高优先级;- 全局配置目录——
$COOK_CONFIG_DIR(若设置)否则平台默认(Linux~/.config/cook/)。
实测验证:同一份食谱,在「无局部 config」的目录里购物清单全部落入 [other];把 aisle.conf 放进 COOK_CONFIG_DIR 指向的目录后,同一命令立刻按商店分区归组:
cd /root/nolocal # 该目录下没有 config/ 子目录
cook shopping-list t.cook
# [other]
# parmesan cheese 50 g
# mozzarella cheese 100 g
COOK_CONFIG_DIR=/root/kitchen/cook-config cook shopping-list t.cook
# [milk and dairy]
# mozzarella cheese 100 g
# parmesan cheese 50 g
修改方式三种,优先级从高到低:命令行 flag > 环境变量 > 配置文件。例如临时换一份分区表而不改任何文件:
cook shopping-list t.cook -a /root/kitchen/cook-config/aisle.conf
# [milk and dairy]
# mozzarella cheese 100 g
# parmesan cheese 50 g
cook doctor 是配置面的体检工具(校验解析错误、aisle/pantry 覆盖度),用法见第七章与命令篇。
四、环境变量详解
CookCLI 只认四个环境变量,空值一律按「未设置」处理:
| 变量 | 取值 | 作用 | 覆盖关系 |
|---|---|---|---|
COOK_CONFIG_DIR | 目录路径 | 重定位全局配置目录:aisle.conf/pantry.conf 回退位置、users.toml、auth-secret、revoked-sessions、CookCloud 会话与同步库全部随它走 | 局部 ./config/ 仍优先于它 |
COOK_USERS_FILE | 文件路径 | 指定服务器登录用户文件,取代配置目录内的 users.toml | --users-file flag 优先于它 |
COOK_CORS_ORIGIN | 逗号分隔 origin 列表 | 声明允许跨域读写的浏览器 origin,同时决定服务应答哪些主机名;为容器场景设计(改 flag 意味着重抄整条启动命令) | --cors-origin 优先于它 |
COOK_RECIPES_ONLY | 1 / true / yes | 开启访客只读食谱模式(其他值含空值均不生效) | 等价 --recipes-only |
COOK_CONFIG_DIR 的意义是把 CookCLI 变成「自带配置根」的自包含工具,官方文档示例:
COOK_CONFIG_DIR=~/kitchen/cook-config cook shopping-list dinner.cook
注意:HOME/XDG_CONFIG_HOME 不能替代它——Windows 的配置目录经 Known Folder API 解析,两者都被忽略。
COOK_RECIPES_ONLY 实测(另起一个只读实例的启动日志):
Listening on http://127.0.0.1:9081
Serving recipe files from: "/root/my-recipes"
Sign-in: on, 1 user(s) in /root/.config/cook/users.toml. Guests can browse but not make changes.
Recipes only: guests only browse recipes.
除这四个之外没有其他配置入口:--no-csrf-check 等其余服务器选项只能走命令行。
五、数据配置文件详解
5.1 aisle.conf:购物清单分区表
作用:把食材按「逛商店的动线」归组,cook shopping-list 输出即按分区排序;未登记的食材进 [other]。
格式不是 TOML——这是最易踩的坑:[名称] 行开分区,其下每行一个食材,别名用 | 分隔,注释用 //(可独行可行尾);以 # 或 -- 开头的行会被当作食材名读入:
// In the order you walk the store
[produce]
tomatoes|tomato
basil|basil leaves // fresh, not dried
garlic
onions
[dairy]
milk
cheese
butter
示例库自带的 config/aisle.conf 有 8 个分区、约 130 行,节选实测文件内容:
[fruit and veg]
apples
bananas
...
avocado | avocados
...
[dried herbs and spices]
salt | sea salt | flaky salt
black pepper | pepper
别名机制的效果:食谱里写 @sea salt{pinch} 或 @salt{24.6%g} 都能命中 salt | sea salt | flaky salt 这一行、归入 [dried herbs and spices](实测购物清单回显见第三章)。哪些食材没被分区表覆盖,用体检命令找:
cd /root/my-recipes && cook doctor aisle
# Scanned 26 recipes, found 76 unique ingredients
#
# 11 ingredients not found in aisle configuration:
# - San Marzano tomato sauce (2 recipes)
# - almonds (1 recipe)
# - basil leaves (2 recipes)
# ...
# Consider adding these ingredients to your aisle.conf file.
5.2 pantry.conf:库存表(TOML)
作用:登记家中库存;库存食材自动从购物清单中扣除,并支撑 cook pantry depleted/expiring/recipes/plan 四类分析。
两种条目形态:
[fridge]
butter = { quantity = "250%g", expire = "2026-04-15" }
eggs = { quantity = "12", bought = "2026-03-07" }
"parmesan cheese" = { quantity = "200%g" }
[pantry]
"tinned tomatoes" = { quantity = "0" }
salt = "1%kg" # 简式:值即数量
属性字段:quantity(当前库存,Cooklang 数量语法「数%单位」)、low(低库存阈值,须与 quantity 同单位)、expire(到期日 YYYY-MM-DD)、bought(购入日)。unlim/unlimited 表示「有货但不可计量」(如自家菜园香草)。含空格的键必须加引号("arborio rice" = ...)。
数量解析失败的行为(实测注入 flour = { quantity = "two%kg" }):
cook pantry depleted
# No depleted items found!
# WARN /root/badpantry/config/pantry.conf: "flour" has a quantity that cannot be read back: "two%kg"
即:读不回的数量被 depleted、库存指示与购物清单扣除一并跳过,同时加载时 WARN,手改文件不会静默藏错。无 low 阈值时 depleted 用启发式判断(实测示例库输出):
cook pantry depleted
# Depleted or Low Stock Items:
# ============================
#
# PANTRY:
# • tinned tomatoes (0)
#
# SPICE RACK:
# • fennel seeds (30%g)
# • ground cinnamon (50%g)
写回行为:cook pantry add/update/remove 原地编辑 pantry.conf,注释、空行、键序与不认识的属性原样保留,只动被点名的条目;文件与分区不存在时自动创建(默认落 ./config/pantry.conf)。库存与清单的联动用 cook doctor pantry 检查(实测节选):
cook doctor pantry
# 28 ingredients from recipes are in your pantry:
# ✓ arborio rice (2 recipes)
# ✓ black pepper (12 recipes)
# ...
# These ingredients will be excluded from shopping lists.
5.3 users.toml 与伴生文件:服务器身份配置
cook server 读 users.toml(位置:--users-file > COOK_USERS_FILE > 全局配置目录内 users.toml)。管理员写裸哈希,其他角色写表:
# Who may change recipes on this server.
[users]
admin = "$argon2id$v=19$m=19456,t=2,p=1$..."
demo = { hash = "$argon2id$v=19$m=19456,t=2,p=1$...", role = "reader" }
角色能力阶梯(每级包含上级):
| 角色 | 购物清单与库存 | 食谱/菜单增删改与图片 | CookCloud 同步 |
|---|---|---|---|
reader | |||
shopper | ✅ | ||
editor | ✅ | ✅ | |
admin | ✅ | ✅ | ✅ |
维护命令(实测回显):
cook server user add admin # 交互提示口令;非终端从 stdin 首行读取
# Added admin to /root/.config/cook/users.toml as admin
# Restart cook server to turn sign-in on.
cook server user list
# admin admin
cook server user role kid editor # 即时生效,无需重启、不踢下线
cook server user passwd alice # 改口令并登出所有会话
cook server user remove demo
# Removed demo from /root/.config/cook/users.toml
cook server hash-password # 手改文件时生成 argon2id 哈希
伴生文件(同目录):auth-secret(会话 cookie 的签名密钥,首次启动自动生成,删除=全员登出)、revoked-sessions(登出吊销记录)。关键规则:
- 签名登录只在服务启动时检测 users 文件——添加第一个用户后必须重启服务一次;
- 服务遇到无法使用的 users 文件(不可读、条目畸形、未知角色、
--users-file指向不存在的文件)会拒绝启动,而不是回退成开放服务;运行中被改坏则记错误日志并沿用旧用户表; - users 文件不允许放在食谱目录内——食谱目录的文件会经
/api/static/发布出去; - 空的
[users]表 = 登录开启但无人可登 = 整站只读。
5.4 db/ 数据仓(报表扩展数据)
cook seed 生成的 db/<食材名>/ 目录是报表命令(cook report)的扩展数据仓,纯 YAML:
cat "db/eggs/meta.yml"
# storage:
# shelf life: 30
# fridge life: 60
# density: 1.03
cat "db/eggs/shopping.yml"
# price_per_unit: 0.25 # 1 egg
shopping.yml 的 price_per_unit 供成本类模板(reports/cost.md.jinja)计算,meta.yml 存保质期/密度等元数据。cook report -d <目录> 指定,默认在食谱库根部寻找。
六、食谱文件语法约定(frontmatter 元数据)
.cook 文件 = YAML frontmatter + Cooklang 正文。正文三条核心标注:@食材{数量%单位}、#炊具{}、~{数量%单位} 计时器;@?可选食材{} 默认不进购物清单(--include-optional 才带出并标 (optional));@./路径{} 引用其他食谱/菜单,购物清单会展开其食材。
frontmatter 实测样例(Breakfast/Easy Pancakes.cook 原文头部):
---
servings: 2
tags: breakfast, quick
source: https://cooklang.org
author: CookCLI Team
prep time: 5 min
cook time: 20 min
description: Simple crepes that are perfect for a lazy weekend breakfast.
---
常用键:servings(整数时 Web/缩放按份数步进)、tags、source、author、prep time/cook time/time、description、cuisine、difficulty、image(题图文件名)、date(feed 排序)。值类型有硬约束——示例库两份食谱写了 yield: 6%balls(字符串),cook doctor 即报:
📄 Shared/Pizza Dough.cook
Warning: Unsupported value for key: 'yield'
╰▶ Expected 'number' but got 'string'
份数缩放记法:任何食谱路径后加 :N(cook recipe "Pizza.cook:2"、cook shopping-list "Pasta.cook:0.5");正文里 {=1%tsp} 的「缩放锁」若用于不随份数变的量才有意义,多余时 doctor 报 Unnecessary scaling lock modifier(实测见上引 Risotto.cook 警告)。
七、cook server 参数详解
服务器全部行为参数都是 cook server 的命令行 flag(实测自 cook server --help):
| 参数 | 默认值 | 说明 |
|---|---|---|
[BASE_PATH] | 当前目录 | 食谱库根,递归扫描 .cook/.menu |
--host [<ADDRESS>] | 关(仅 localhost) | 放行外部主机;带值则仅绑定该地址 |
-p, --port <PORT> | 9080 | 监听端口 |
--url-prefix <PREFIX> | 无 | 反代子路径(如 /cook),路由/链接/cookie 全带前缀 |
--proxy-strips-prefix | 关 | 配合上项:反代转发前剥掉前缀(Traefik StripPrefix、nginx proxy_pass .../;、HA ingress) |
--cors-origin <ORIGIN> | 无 | 可重复;允许跨域读写的 origin,且决定服务应答的主机名;* 放开读但跨域写仍 403 |
--cors-allow-credentials | 关 | 跨域携带 cookie,须至少一个显式 origin |
--no-csrf-check | 关 | 关闭同源强化(Host 检查/写请求/编辑器 LSP);旧拼写 --no-cors 仍可用 |
--max-lsp-sessions <N> | 8 | 编辑器语言服务器并发上限,超出回 503;0 彻底关闭 |
--users-file <PATH> | 配置目录 users.toml | 登录用户文件 |
--recipes-only | 关 | 访客只见食谱 |
同源强化(默认开启)是安全面最重要的设计:服务只应答 localhost、IP 或 --cors-origin 声明过的主机名,其他主机名一律 403(防 DNS rebinding);写请求的 Origin 同样受此规则约束;检查读真实 Host 头、忽略可伪造的 X-Forwarded-Host。实测 403 响应体自带解法提示:
{"error":"Only localhost and IP addresses are trusted as this server's own address, since another site could point any other host name at it (DNS rebinding). If you opened the web UI at http://evil.example.com, start the server with --cors-origin http://evil.example.com, or with --no-csrf-check to disable this check."}
典型组合:局域网按 IP 访问 cook server ~/my-recipes --host;按主机名访问再加 --cors-origin http://nas.local:9080;公网反代(TLS 终结)--cors-origin https://cook.example.com,HTTPS 下让反代送 X-Forwarded-Proto: https 使 cookie 带 Secure。
八、语言与单位设置
服务器 UI 语言:无配置项,按请求的 Accept-Language 头逐访客协商,支持 en-US、de-DE、nl-NL、fr-FR、es-ES、eu-ES、sv-SE、it-IT、ja-JP;页面上也可手动选语言(写入 lang cookie)。
静态站点语言:构建期一次性选定,cook build web --lang <BCP-47>(默认取系统 locale,回退 en-US):
cook build web --lang fr-FR
单位体系:没有全局单位配置文件——单位就是 Cooklang 内联语法 {数量%单位} 的一部分,解析器认常用度量(g/kg/ml/l/tsp/tbsp/cup/pinch/knob 等)与任意自定义单位(tins、balls、unlim)。跨配置比较时单位必须同类可换算,否则购物清单扣除库存时报 WARN(实测节选):
WARN Unit mismatch for 'salt': recipe needs 'g', pantry has 'kg'
WARN Unit mismatch for 'milk': recipe needs 'ml', pantry has 'l'
消除方法:把 pantry.conf 的库存单位与食谱常用单位对齐(如食谱多用 g 则库存也记 g),或接受「不换算即不扣除」的语义。
九、生产配置模板
家庭 NAS/小服务器常驻场景的推荐布局(对应本专栏部署篇的 compose 挂载结构,裸装同理):
/opt/cookcli/
├── recipes/ # 食谱库(建议 git 管理)
│ ├── config/
│ │ ├── aisle.conf # 按你家真实购物动线裁剪分区与别名
│ │ └── pantry.conf # 库存:quantity 与食谱单位对齐,生鲜配 expire
│ ├── db/ # (可选)price_per_unit 等扩展数据
│ └── *.cook / *.menu
└── config/ # COOK_CONFIG_DIR 指向这里
├── users.toml # admin + 按家人分角色(reader/shopper/editor)
├── auth-secret # 服务自动生成,纳入备份
└── revoked-sessions # 自动生成
要点清单:
- 服务参数基线:
--host+COOK_CORS_ORIGIN(声明实际访问 origin)+ users.toml;不可信网络加--max-lsp-sessions 0;对外分享但不想暴露购物清单用--recipes-only; pantry.conf用cook pantry add/update维护而非手改(保注释、防格式错);users.toml权限最小化:浏览者reader、管清单的shopper、写菜谱的editor,admin 留一个;- 备份 = 两个目录打包;升级 = 换二进制/镜像 tag,数据零迁移。
十、常见问题
Q1:改了 pantry.conf 后命令直接报错,服务起不来?
TOML 语法错误是硬失败,错误信息精确到行列。实测(值里引号未闭合):
Error: invalid configuration at /root/badpantry/config/pantry.conf: Error parsing input: TOML parse error: TOML parse error at line 2, column 26
|
2 | flour = { quantity = "two
| ^ invalid basic string
含空格键名记得加引号;改完先跑 cook pantry list 验证。
Q2:库存明明有,购物清单却还是列出该食材?
三种原因按序排查:① 单位不匹配不换算(看 WARN Unit mismatch,见第八章);② 数量读不回被跳过(看 WARN cannot be read back,见 5.2);③ 食材名与 pantry.conf 键名不一致——库存扣除按名称匹配,别名机制只属于 aisle.conf。cook doctor pantry 列出哪些库存项会参与扣除。
Q3:加了用户但网页上没有登录入口/写操作仍不拦截?
签名登录在服务启动时检测 users 文件,user add 回显已提示 Restart cook server to turn sign-in on.——重启一次服务即可;后续增删改角色是热生效。
Q4:用 # 写 aisle.conf 注释,分区表莫名多出食材?
aisle.conf 不是 TOML,#/-- 开头的行会被当作食材名;注释只能用 //。cook doctor aisle 会把这类行指出来。
Q5:想给多台机器共用一套配置?
把 aisle.conf/pantry.conf 放 COOK_CONFIG_DIR 指向的目录(或经 Git 同步到该目录),局部 ./config/ 优先规则不变即可。
十一、参考链接
- 官方文档:https://cooklang.org/cli/
- Cooklang 规范:https://cooklang.org/docs/spec
- 仓库与 README:https://github.com/cooklang/cookcli
- 本专栏 《CookCLI 介绍与选型指南》、《CookCLI Docker Compose 部署指南》