1panel-toolkit
一个 spec 进,一个经过验证的 1Panel 应用包出。
p1 把 1Panel 应用打包里重复的部分(10 语言 label、README 骨架、.env.sample
闭合、LF/BOM 归一化、strict-store 结构校验)变成代码,把真正需要判断的部分
(上游端口、运行用户、依赖拓扑)留给人或 agent,并且把踩过的坑变成可复用的
前置检查。
它不绑定任何 agent 宿主:CLI 可以独立使用,也可以由 skill 包装后交给 coding agent 调用。
核心路径不需要面板、不需要 SSH、不需要任何密钥:resolve → recipe → gen →
check → depth → fix 全部离线跑,输入是仓库地址(或本地检出),输出是一个包。
deploy / uninstall / remote / panel 是可选的附加能力,用面板 API key
帮你检查部署情况、把失败解释清楚——不配也行,核心不依赖它们。
安装
uv tool install 1panel-toolkit # PyPI 上当前是 0.3.0
p1 --version
p1 selftest # 验收:规则数据、写法库、生成→校验链路是否都正常
从源码:
uv venv .venv
uv pip install -e . --python .venv/Scripts/python.exe
从构建产物:
uv build --out-dir dist
uv venv .venv-dist && uv pip install dist/*.whl --python .venv-dist/Scripts/python.exe
.venv-dist/Scripts/p1.exe selftest
用法
p1 check <app-package-dir>
p1 check <dir> --strict
p1 check <dir> --json
p1 rules lessons
p1 ports 203.0.113.10 10000,15000,8080
p1 patterns list
p1 patterns show compose/panel-db-env
p1 patterns find 数据库
p1 depth --corpus <官方apps目录> # 哪些维度是应用特有的
p1 depth <包目录> --corpus <官方apps目录> # 这个包是适配过的还是套模板
p1 resolve <仓库URL|本地检出目录> # 抽取上游证据,回答 10 个生成决策
p1 resolve halo-dev/halo --out ./out/halo # 同时写出报告/spec草稿/证据
p1 remote check # 目标主机的事实与空闲端口
p1 remote smoke <包目录> # 上传→起容器→探针→抓日志→清理(不传端口就用包自己的默认值)
p1 gen examples/demo-spec.json --out ./out --check # 从 spec 生成包并立即校验
p1 gen examples/demo-db-spec.json --out ./out --check # 含面板数据库依赖与 init.sh 的示例
p1 gen ./out/app/draft-spec.json --allow-unresolved # 明知缺证据也要生成(会记账)
p1 recipe list # 5 条"成套写法":面板 DB / Redis / Mongo / MinIO / 组合
p1 recipe apply panel-postgres ./out/app/draft-spec.json --write # 一键接线
p1 deploy ./out/p1-demo --param PANEL_APP_PORT_HTTP=15000 # 走面板 API 正式安装
p1 deploy ./out/p1-demo --param PANEL_APP_PORT_HTTP=15000 --probe # 装完再敲一下端口
p1 fix <包目录> --check # 把 check 的结果幂等修掉
p1 form audit <包目录> --plan plan.json # 表单里哪些输入框用户真的必须填?
p1 fix <包目录> --plan plan.json --check # 按你改过的计划执行
p1 selftest # 验收:这个安装能不能用
p1 ports 区分三种状态:open(已有服务)、closed(主机可达但无人监听,
说明安全组已放行,可以直接用)、filtered(超时,被上游拦掉)。云服务器上做
冒烟测试前先跑一次,能避免把安全组问题当成打包问题。
它检查什么
规则组 P0xx 管包结构:根/版本 data.yml 的键位层级、必备元数据、
architectures 位置、tag 白名单、logo 尺寸;P030 查根 key 与目录名是否一致
(官方 882/882 一致),P017 提示 v 前缀的版本目录(官方 1617 个里只有 6 个这样),
P031/P032 查根元数据的类型(limit/recommend/memoryRequired 必须是整数,
crossVersionUpdate 等必须是布尔——写错会让面板同步直接失败:
cannot unmarshal !!str \512M` into int),P033` 提示官方语料没用过的键。
规则组 I0xx 管 i18n:description 与 formFields[].label 的语言覆盖、
values[].label 必须是纯字符串、bool label 必须加引号。
规则组 C0xx 管 compose:顶层 version:、image 引号、${VAR} 与表单 envKey
闭合、createdBy 标签、端口 envKey 前缀、外部网络、同名服务 DNS 冲突、
healthcheck 必须是映射(C005,healthcheck: true 会让 Docker 直接解析失败)、
service 是否有 restart 策略(C006,官方 1954/1958 个服务都写了)。
规则组 H0xx 管文件卫生:LF/BOM、README store 风格、.env.sample 闭合、
scripts/*.sh 必须有 shebang(H030,官方 2416/2416 个脚本都有)。
规则组 F0xx 管安装表单本身:字段预算(官方中位数 5 个、上限 20)、必填比例、
密钥字段类型、字段是否真被引用(F007),以及 type/rule 是否落在官方用过的枚举里
(F008/F009——表单就是靠这两个值渲染的)。
规则组 L0xx 是经验检查:静态校验通过但容器启动就崩的那类问题,条目见
rules/data/lessons.yaml。
规则本身是数据,放在 src/p1toolkit/rules/data/*.yaml:store 政策变化是改数据,
不是改代码。
写法库(patterns)
p1 patterns 是一套从官方应用包里抽取的真实写法,不是手写的示例:
当前语料是 882 个官方应用 / 1617 个版本,覆盖 data.yml 声明、各类表单字段、
数据库与 Redis 接入、compose 拓扑、init/upgrade/uninstall 脚本、README 与
.env.sample 约定。
每条 pattern 带四样东西:用途、何时使用、官方采用数(多少个应用真的 这么写,用来区分主流做法和个例)、以及真实代码片段 + 来源包路径。
p1 patterns list # 22 条写法,带采用数
p1 patterns show form-field/port # 展开一条,含完整示例与来源
p1 patterns find 数据库 # 关键词检索
p1 patterns build --corpus <apps目录> # 重新从语料构建(官方 store 更新后跑一次)
p1 patterns export --markdown docs/patterns.md # 生成教程文档
构建产物 patterns/data/patterns.json(约 37 KB)随包分发,所以离线也能查;
教程文档见 docs/patterns.md。
生成深度(depth)
模板化生成会做出高度重复的包——因为每个应用真正不同的那些维度(容器内挂载点 642 种 取值、容器内端口 359 种、应用环境变量名 3197 个)在模板里全被压成了一个默认值。
p1 depth 用 882 个官方应用的实测分布量化这件事:哪些维度可以模板化(重启策略 5 种
取值,always 占 82%),哪些必须逐应用决定。它还给出单包判定——把包的应用特有
特征做成指纹,和官方语料比对,识别"是适配过的"还是"套模板套出来的"。
生成器必须逐项回答的 10 个问题在 rules/data/decisions.yaml,说明见
docs/generation-depth.md。
上游情报(resolve)
gen 的每一笔都必须是能指回上游文件的值,所以先生成证据:
p1 resolve halo-dev/halo --out ./out/halo
它从 GitHub API(或本地检出目录)拉 Dockerfile / compose / .env.example /
README,抽取出端口、挂载点、环境变量、运行用户、额外运行时字段、依赖等信息,
每条都带 文件:行号,然后逐条回答 D01–D10:
raw.githubusercontent.com 在部分网络下不可达,另一些网络下是极慢(实测 4–17 秒/文件)。
resolve 第一次调用超过 3 秒(或直接失败)就改用 GitHub 的 contents API 取文件
(同环境实测 0.7 秒/文件),并在报告里说明原因;只有仍在用 raw 时才受抓取总预算
(默认 60 秒)限制,--timeout 可调,--timeout 0 只取仓库元数据。
本地检出则完全离线:p1 resolve <本地目录>。
- ✅
evidenced—— 上游文件里明写了 - ⚠️
inferred—— 有较弱的合法来源(例如路径来自HALO_WORK_DIR=/root/.halo2这类环境变量默认值,镜像名来自 README 的docker run),需要人工确认 - ❌
missing—— 什么都没有
D03(容器内挂载点)、D05(容器内端口)、D06(镜像)没有证据时,resolve 直接
返回非零退出码并列出阻塞项,不猜、不填默认值。间接证据(inferred)会连同答案一起
落进 draft spec——人工确认过的值必须能到达生成器,而不是停在报告里。产出三份文件:
decision-report.md、draft-spec.json、resolve.json。
多服务 compose 按服务归属:compose 里每个服务在 draft spec 里各占一条,镜像、容器端口、
bind 挂载、环境变量、depends_on、运行时字段都只跟着自己的服务走。Dockerfile 里的变量归
主服务;sidecar(自带的数据库/缓存)的密码不会被并到应用服务上,反过来也一样。多服务时还会给
一条提示:如果面板上已经有对应的应用,考虑改用 dependencies 走面板托管(D10)。
实测:halo-dev/halo 的挂载点在 Dockerfile 里没有 VOLUME,但 HALO_WORK_DIR
默认值是 /root/.halo2;镜像只在 README 里出现——它把两条都标成 inferred
并给出出处,而不是停下来或者瞎猜。
运行时验证(remote)
SSH 冒烟(remote smoke)
静态校验和生成的 YAML 都不能告诉你"容器起不起来、端口通不通、应用找不找得到配置文件"。
p1 remote check # 主机事实(内存/磁盘/已用端口/已有本地应用)
p1 remote smoke <包目录> # 完整一轮冒烟(用包自己的默认端口)
p1 remote smoke <包目录> --ports 10000 # 显式覆盖宿主端口
p1 remote smoke <包目录> --env OV_VLM_API_KEY=sk-… # 用真实密钥覆盖表单默认值
它把某个版本目录上传到 /tmp/p1-smoke/<key>(不是 /opt/1panel/resource/apps/local,
不会碰到你已装的包),按安装表单的默认值渲染 .env,docker compose up -d,
然后轮询到"HTTP 有应答"或"healthcheck 明确 healthy"为止,抓日志、清理。
不传 --ports 时用的是包自己的表单默认端口——冒烟要验的就是这个包,不是
"在这台机器上换个端口能不能跑"。端口被占用时失败信息会点名 L008:
那不是包的缺陷,1Panel 安装时会提示换一个端口,SSH 冒烟用 --ports 指定即可。
判定分三类,退出码不同,便于接 CI:
| 结果 | 退出码 | 含义 |
|---|---|---|
PASS |
0 | 容器 running 且端口有 HTTP 应答(或 healthcheck healthy) |
PENDING |
3 | 日志显示缺少用户密钥/凭据 → 是表单待填,不是包的缺陷 |
FAIL |
1 | 其他失败;自动匹配 lesson 并给出根因 |
两个设计细节值得说明:端口被监听不等于就绪(应用可能先绑端口再花几十秒初始化),
所以主判据是 HTTP 应答,纯 TCP 监听只在 healthcheck 认可时才算通过;失败时保留远端目录
以便 p1 remote logs <dir> 复看,成功则连同容器和数据卷一起清掉。
面板安装(deploy / uninstall)
p1 deploy <包目录> [--param K=V] [--uninstall] [--cleanup-local] [--no-wait]
p1 uninstall <安装名> # 或 --install-id N
和 remote smoke(SSH 直接起容器)不同,deploy 走的是真实用户路径:
- 把包放到面板的本地应用目录
/opt/1panel/resource/apps/local/<key> POST /apps/sync/local让面板重新扫描本地应用POST /apps/search找到应用(本地应用注册成local<key>)GET /apps/detail/{appId}/{version}/{type}取appDetailIdPOST /apps/install提交安装,参数以对象形式放进params- 轮询
POST /apps/installed/search,看面板自己记的status是否Running
Running 只说明容器起来了,不代表服务能应答——端口映射写错、应用启动即崩、
初始化失败都会是 Running。加 --probe 就会在装完后从服务器本机敲一下端口:
不带值用安装参数里的 PANEL_APP_PORT_*(否则用表单默认值),也可以 --probe 15000
指定。判定与 remote smoke 共用:HTTP < 500 算有应答(401/403/404 也算),
否则回落到 TCP 监听判定;探针失败时 stage=probe、退出码 1,--uninstall 与
--cleanup-local 照样执行,不会在服务器上留坏安装或残留包。
这几步的端点与字段都是从运行中的 v2.2.4 上探测出来的(给接口发空 body,让面板的
参数校验器报出必填项),不是猜的。实测一轮:安装 → 容器 status=Running → 卸载,
约 10 秒(镜像已缓存)。
为什么两个都要有:remote smoke 验证"这个 compose 能不能起来",
deploy 验证"面板能不能把它装出来"——表单渲染、依赖注入、防火墙放行这些环节
只有走面板才会被走到。
1Panel v2 的 API 鉴权(踩过的坑)
1Panel v2.2.4 的接口约定和直觉不一样,而且三处错误返回同一条
{"code":401,"message":"API 接口密钥错误"},从响应上完全看不出问题在哪:
| 正确做法 | 直觉上会写成 | |
|---|---|---|
| 路由前缀 | /api/v2/... |
/api/v1/...(v2 里已不存在) |
| 时间戳头 | 1Panel-Timestamp |
1Panel-Time |
| Token 头 | md5("1panel" + api_key + 时间戳) |
明文 api_key |
依据是上游 backend/middleware/session.go:
panelToken == GenerateMD5("1panel" + global.CONF.System.ApiKey + panelTimestamp)。
p1 config set panel.* + p1 panel ping 已按这个实现(保留 v1 回退),
lesson 记作 L007。
修复(fix)
p1 fix <包目录> --dry-run # 先看会改什么
p1 fix <包目录> --check # 改完立刻重新校验
fix 是幂等的:第二次跑会告诉你"没有需要修复的内容"。它做两类事:
机械修复(永远安全):CRLF/BOM 归一化为 LF、image 与端口映射加引号、
移除顶层 version:、补 labels.createdBy、补根 data.yml 的 recommend /
memoryRequired、对齐 title/description/shortDescZh、补齐 .env.sample
缺失的变量、把 logo 等比缩放到 180×180 透明底(不放大,小图居中)。
纪律修复(改变安装表单):把"可选且有可用默认值"的字段从表单里移出去,
同时在 compose 里把 ${VAR} 改成 ${VAR:-默认值},让变量仍然有效。这就是
"三十个输入框、六个必填"那个反模式的解法。
不做的事:需要判断的(哪些字段用户真的必须决定、标签该怎么翻译)会列入
需要人工处理 而不是替你猜。
那部分判断现在有个半自动出口:p1 form audit 把每个字段分成
keep(用户必须决定)/ hide(可收进 compose 默认值)/ review(表单超预算时,
必填带默认值但名字不属于官方约定的字段)/ unused(没人引用)/ must-fill
(必填又没默认值),并写出计划骨架。判定依据是实测:官方 10640 个字段里 73% 就是
"必填 + 有默认值",所以那本身不是问题信号;名字命中官方约定(时区、路径、PUID/PGID、
端口、密钥、库名、引擎/模式、资源参数)的一律保留,判定规则在 rules/data/form-review.yaml,
改判断是改数据。
你只改 hide / keep 两个列表,p1 fix --plan plan.json 负责机械执行:
被隐藏的字段会在 compose 里拿到 ${VAR:-默认值},变量不会失效;必填且没有默认值的字段
会被拒绝隐藏(不会把"用户必须填"偷偷变成空值)。计划是幂等的,同一个 plan 跑两次等价。
实测效果(_recon/measure_fix.py 的原始输出):
| 包 | 修复前 | 修复后 | 改动 |
|---|---|---|---|
| hindsight | 2 fail + 8 warn | 0 fail + 2 warn | 7 处 |
| openviking | 1 fail + 6 warn | 0 fail + 1 warn | 8 处 |
| xiaozhi-esp32-server | 1 fail + 13 warn | 0 fail + 1 warn | 14 处 |
(2026-09-20 在真实包上的实测记录;这些包随后已就地修复,
xiaozhi-esp32-server-full 现在是 0 fail / 0 warn。)
生成(gen)
p1 resolve <仓库> --out ./out/app # 1. 上游证据 → draft-spec.json
$EDITOR ./out/app/draft-spec.json # 2. 人工确认证据、补一句简介
p1 gen ./out/app/draft-spec.json --out ./out --check # 3. 生成 + 校验
p1 remote smoke ./out/app --ports 10000 # 4. 真机验证
核心纪律:安装表单是"决策清单",不是"配置转储"。
一个上游变量只有在两种情况下才出现在表单里:用户必须决定它(端口、对外 URL),
或者用户必须提供它(密钥)。其余全部写进 compose 默认值(${VAR:-default}),
并在 README 的「高级配置」里列出来供需要时修改。
这不是审美偏好,是实测差距:官方 1617 个版本的表单中位数 5 个字段、88% 标为必填;
本项目早期的 hindsight 包有 30 个字段、必填只占 20%,用户反馈"有的要填有的不用填,
非常麻烦"。现在 gen 会把这个纪律强制执行,check 的 F 组规则负责事后把关。
生成物包含:根 data.yml(name 放产品名,title/description/shortDescZh
同一句简介 —— 这是 739/771 个官方应用的写法)、版本 data.yml、docker-compose.yml
(引号、createdBy、1panel-network、上游 runtime 开关原样保留)、.env.sample
(包含所有隐藏变量)、中英 README、logo 占位图、source-evidence.json,
以及 check 会提醒的 recommend / memoryRequired(默认 0,spec 可覆盖)——
生成物本身就该是"不用立刻再 fix 一遍"的。
一个服务可以开多个端口:container_ports: [6099, 3001] 会生成两个
PANEL_APP_PORT_* 表单字段和两条端口映射(WebUI + 协议端口这类应用很常见),
container_port 仍是单端口简写。
生成过程会明确告诉你哪些东西是它替你决定的:哪些变量被收进默认值、哪些翻译是 占位、logo 需要替换。
把"用户得自己做的事"写进 README:官方 882 份 README 里 54% 有参数说明表、30% 提到
用户自备的文件(模型、证书、插件)。spec 的 docs 块让生成器替你写这两节:
"docs": {
"first_run": "启动前先把模型放进 ./models,否则容器会退出。",
"parameters": {
"PANEL_APP_PORT_HTTP": "面板入口端口,默认 8000;被占用时面板会提示换一个",
"OV_VLM_API_KEY": "调用上游服务的密钥;留空则用本地模型"
},
"assets": [
{ "path": "./models/voice.pt", "target": "/app/models/model.pt",
"why": "语音识别模型,容器不会自带", "source": "https://example.com/models" }
]
}
生成的 README 会多出「配置项」(变量|说明|默认值|必填)与「需要你自己准备的文件」
(宿主路径 → 容器内路径、为什么需要、从哪拿),first_run 覆盖默认的「首次启动」那句;
被收进默认值的隐藏变量在「高级配置」里逐条带说明,不再只是一串变量名。
三条校验兜底:变量名拼错、路径没被挂载、只给路径不给理由——都会在生成时报出来。
证据纪律:没有证据就不生成
gen 会先审计 resolve 写下的决策账本,再决定要不要写文件。D03(容器内挂载点)、
D05(容器内端口)、D06(镜像)没有证据时直接拒绝生成,退出码 2,且一个文件都不写:
FAIL D03 D03 数据要挂到容器内哪个路径? 没有上游证据
→ 补证据,或在 spec 里写 "acknowledged": {"D03": "理由"};停止,不要猜挂载点
FAIL D06 app: 没有 image
→ 镜像必须来自上游证据(README 的 docker run / 官方镜像页)
这三条是实测出来的分界线:它们的取值分别有 642 / 359 / 19 种,抄模板抄错时的表现是
"装得上、跑得起来、就是不对"——数据升级即丢、端口不通、镜像来路不明。阻塞范围存在
rules/data/decisions.yaml 的 blocking 字段里,resolve 与 gen 读同一份数据。
其余检查不阻塞,但会点名:
- 漂移:spec 的镜像 / 端口 / 挂载点不在账本证据里,或环境变量名不在 D01/D02 里 (变量名拼错是最难查的一类:容器照常启动,值被静默忽略)。
- 结构性缺口:声明了
mount_host却没有容器内挂载路径的 service 会被拒—— 这种包"有数据目录"但什么都没挂进去。 - 非阻塞决策缺证据:把
decisions.yaml里if_unknown的处理方式原样打印出来。
人工可以显式承担决定,两种方式都会记进包内的 decision-audit.json:
# 1. 写进 spec(推荐:评审时能看到理由)
# "acknowledged": {"D03": "上游 Dockerfile 没有 VOLUME,这个应用不写盘"}
# 2. 一次性放行
p1 gen ./out/app/draft-spec.json --allow-unresolved
decision-audit.json 与包一起交付:每条决策的状态、答案、证据缺口、谁批准了哪条,
评审的人不必再去翻对话记录。p1 gen --json 里也有同一份数据,方便接 CI。
依赖接入(D10)与脚本(D09)
依赖是"接线",不是"多几个输入框"。 官方 106 个版本用依赖选择器,主流形状是两步式
type: apps + child.type: service(72 个版本),一步式 type: service 有 53 个
(Redis 只有一种实例,天然用一步式)。两种形状都从语料量出来,放在
rules/data/dependencies.yaml。
"dependencies": [
{
"kind": "postgresql",
"kinds": ["postgresql", "mysql"],
"env": {
"DATABASE_HOST": "${PANEL_DB_HOST}",
"DATABASE_URL": "postgres://${PANEL_DB_USER}:${PANEL_DB_USER_PASSWORD}@${PANEL_DB_HOST}:${PANEL_DB_PORT}/${PANEL_DB_NAME}"
}
}
]
生成器据此产出选择器字段、把 ${PANEL_DB_*} 映射成应用自己的变量名,并只为真正被
引用到的面板变量生成输入框(PANEL_DB_HOST 由选择器提供,其余没用到的一个都不加)。
应用侧的变量名必须由你给出——那是 D01 证据,不是生成器能发明的东西。
p1 resolve 会先给线索:上游 compose / README 里像数据库接线的变量(DB_HOST、
DATABASE_URL、REDIS_*)会按 host / port / user / password / name / url 归类,
写进 draft-spec.json 的 dependency_hints 和决策报告的「D10 线索」一节。
脚本与首次启动配置(lesson L001:静态全绿、容器一起来就退出)有了两条正解:
"data_owner": "1000:1000",
"config_files": [
{ "path": "data/app.conf", "content": "database_url=${DATABASE_URL}\n" }
]
data_owner 生成 init.sh / upgrade.sh,按挂载目录 mkdir -p + chown -R;
config_files 把模板放进版本目录,并在中英文 README 的「首次启动 / First start」里
提醒用户先编辑。更复杂的逻辑用 scripts: {init, upgrade, uninstall} 原样写入
(自动补 #!/bin/bash、统一 LF)。
这两件事同样受证据约束:声明 user / data_owner 却拿不出 D07 证据、带了依赖而
D10 说"未检测到"、带了脚本而 D09 无证据——都会在证据审计里被点名(lesson L004)。
配方(recipes):把成套写法变成一条命令
p1 resolve <仓库> --out ./out/app # 上游证据 + D10 线索
p1 recipe apply panel-postgres ./out/app/draft-spec.json --write # 一键接线
p1 gen ./out/app/draft-spec.json --check # 生成 + 校验
配方只填空的那部分是结构:面板变量怎么映射到应用变量、选择器什么形状、哪些变量得由
包自己声明。应用侧的变量名一律来自 resolve 的 dependency_hints(或 --bind 显式给出),
配方不发明变量名,也不替人决定要不要接依赖。
四条内置配方:panel-postgres(PostgreSQL / MySQL / MariaDB,两步式选择器)、
panel-redis、panel-mongo、panel-db-and-cache(复用前两条)。p1 recipe show <id> 展开角色表;
p1 recipe apply 默认只预览,加 --write 写回 spec;--bind host=DB_HOST 补 hints 没覆盖的
角色。hints 的 subjects 会区分 postgresql 与 redis 这类依赖主体,所以
REDIS_URL 不会被绑到 Postgres 的连接串模板上。
配方里的形状都有出处:p1 recipe show <id> 会列出这条配方依据的官方写法
(每条带"多少个官方应用采用"、示例来自哪个包、以及一小段真实片段),
例如 form-field/apps-child-service(41 个应用)就是两步式选择器的来源;
p1 patterns show <id> 反过来会告诉你"这条写法被哪些配方用着"。
当前状态
完整流程已经闭环:
p1 resolve <仓库> --out ./out/app # 1. 上游证据 + 10 项生成决策
# 人工确认 draft-spec.json
p1 gen ./out/app/draft-spec.json --check # 2. 生成 + 静态校验
p1 depth ./out/app --corpus <官方apps> # 3. 确认不是模板复制品
p1 remote smoke ./out/app --ports 10000 # 4. SSH 冒烟:起容器、探针、抓日志
p1 deploy ./out/app --param ... --uninstall # 5. 走面板 API 正式安装并验证
修复已有包:p1 fix <包目录> --check(幂等,见上文)。
命令一览(16 个):resolve / recipe / gen / check / form / fix / depth /
patterns / ports / remote / deploy / uninstall / config / panel / rules /
selftest——其中前 8 个(到 patterns)离线可用,后面几个是可选的面板/SSH 能力。
文档
| 文档 | 内容 |
|---|---|
| 本文件 | 用户视角:装什么、怎么用、为什么这么设计 |
docs/architecture.md |
接手先读:模块地图、证据链、扩展点、测试策略 |
docs/status.md |
工具状态:命令清单、规则与数据规模、实测数据、验证记录、已知限制、维护手册 |
docs/generation-depth.md |
为什么模板化生成会做出重复的包(实测分布 + 判定口径) |
docs/patterns.md |
22 条官方真实写法的教程(p1 patterns export 生成) |
CHANGELOG.md |
每个版本改了什么、为什么改 |
设计约束
没有官方 Docker 证据就不猜镜像、端口、卷、UID/GID。高风险运行时权限先保留并 标注风险,不为了扫描器好看而删。云服务器上做运行时验证时只用已确认放行的端口 段,避免把安全组问题误判成打包问题。
它不做什么
这条界线值得写下来,因为它划错时工具会开始骗人:
- 不把在线步骤变成必需。 面板与 SSH 是可选的辅助;核心(进 GitHub 地址、出包)
完全离线可用,已经有一条测试盯着这件事(配置指向不存在的文件时,
gen/check/form audit/depth必须照常通过)。 - 不替你挑宿主端口。 端口是包自己的决定(表单默认值),冲突由 1Panel 在安装表单里
提示、由 Compose 报错。工具不会为了"跑通"而在服务器上另找一个空闲端口来装——
那样测的就不是这个包了。
remote smoke --ports是显式覆盖,不是自动兜底。 - 不为某一台服务器调优。 端口区间、安全组放行情况、镜像缓存都随机器和时间变化;
工具只根据它当场读到的这台主机的事实做事(
p1 remote check),不预置任何 服务器专属常量。 - 不假装知道上游没写的东西。 镜像、容器端口、挂载点没有证据就拒绝生成; 应用自己的变量名要么来自证据、要么由你给。
- 不做需要判断的事。 哪些字段用户真必须填、标签怎么翻译、要不要接面板依赖,
工具列清单、给依据,但不替你决定(
p1 form audit+--plan就是为这条设计的)。
Metadata
Release files for 1panel-toolkit 0.16.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| 1panel_toolkit-0.16.0.tar.gz | 245.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 1panel_toolkit-0.16.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 422.3 kB
Release files / 1panel_toolkit-0.16.0.tar.gz
| Download URL | 1panel_toolkit-0.16.0.tar.gz |
|---|---|
| Size | 245.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
015d46d6bb6e45480268c8410f93366240642b483ee1ba05eb1a35641a9c6b56
|
|
BLAKE2b-256 checksum How to use checksums |
7ceda1d118ec0ca8cf66b17debe21d11050944fd56927b369a0b73202aab11c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / 1panel_toolkit-0.16.0-py3-none-any.whl
| Download URL | 1panel_toolkit-0.16.0-py3-none-any.whl |
|---|---|
| Size | 176.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6045362cc30612bf3a6a3dc2a2ba5ea1d8a3b7048e5e784a0c1bcb3089c5070c
|
|
BLAKE2b-256 checksum How to use checksums |
9c29f9d617eaf5e46f837eeeeede729c3e7d18dbd8296900efa318ab28e801b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|