--- url: /docs/agentbuff-stack.md --- # AgentBuff Stack AgentBuff Stack 是用于快速发布海外英文工具站的通用底座,先自用,再考虑出售。模板与具体任务分开:示例 JSON 格式化和文本清理只用于验证输入、预览、下载和账号历史流程,不代表已验证的新词机会。 当前版本 **0.3**。公开页面、工作台和本地 API 可运行;第三方集成需要独立服务凭据及验收;尚未开发的能力有明确边界。完整状态见 [模块总览](./modules.md)。 ## 技术栈 | 层 | 当前选择 | 职责与配置入口 | | --- | --- | --- | | 公开网站 | Astro + React islands | 静态 HTML、工具页、博客;`apps/web/` | | 用户工作台 | React + TanStack Router / Query + Jotai | 登录、历史、积分、团队与设置;`apps/app/` | | 设计系统 | Astryx + StyleX,保留 Radix 控件 | 统一主题与组件;`packages/ui/design/` | | API | Hono + tRPC + Zod | 认证边界、输入校验、类型化接口;`apps/api/` | | 身份 | Better Auth | 密码、OTP、Passkey、Google / GitHub OAuth、组织 | | 数据 | PostgreSQL + Drizzle | 用户、组织、订阅、结果与积分账本;`db/` | | 生产数据路径 | Neon + Cloudflare Hyperdrive | 未部署验收;缓存连接不用来读权限或支付状态 | | 邮件 | React Email + Resend | 验证、密码重置、OTP;本地预览捕获邮件 | | 支付 | Better Auth Stripe plugin | 订阅与账单;未做真实沙箱验收 | | 公开页统计 | 可选 Cloudflare Web Analytics | 默认关闭,不收集工具输入或私有页面 | | 文档 | VitePress | 独立静态文档、搜索、模块说明;`docs/` | | 本地与测试 | Bun、TypeScript、Vitest、PGlite、Oxlint、Oxfmt | Bun 仅用于本地;生产运行时是 Workers | | 部署 | Cloudflare Workers + Wrangler;Terraform 管资源 | 三个 Worker 内部服务绑定;未上线验收 | 版本以 `package.json` 和 `bun.lock` 为准,不把文档里的版本号当作自动升级指令。当前选择继续保持 Cloudflare + Neon 方向,不为对齐竞品而改成 Next.js。 ## 三类配置 1. **产品选项**:`packages/core/website.ts`;只放公开、可打包的数据。 2. **服务凭据**:本地 `.env.local`,部署时 Cloudflare Secrets;通过 API 环境契约校验。 3. **资源与路由**:各应用的 `wrangler.jsonc` 和 `infra/`;Worker 名、域名、Hyperdrive、未来的 R2 / Queue 绑定。 修改产品选项后重新构建全部应用,修改后端环境后重启 API。`packages/core/site.ts` 是兼容读取视图,品牌的唯一编辑入口是 `website.ts`。 ## 文档路径 [快速开始](./quick-start.md) → [网站配置](./website.md) → [环境变量](./env.md) → [模块总览](./modules.md) → [部署与验收](./deployment.md)。 侧边栏后半部分保留 Kriasoft 的架构与配方参考。遇到上游示例与本站配置不一致,以 AgentBuff Stack 本节和当前源码为准;保留原 MIT 版权及第三方许可。 ## 公开参考 核对日期:2026-10-09。参考 [MkSaaS 模板介绍](https://mksaas.com/zh/docs)、[网站配置](https://mksaas.com/zh/docs/config/website)、[环境配置](https://mksaas.com/zh/docs/env) 的模块组织方式;未使用其商业源码。我们的选择与实现状态独立记录,不宣称已拥有相同的全部能力。 模板官网与产品底座已经分开。见[官网、案例与演示](./website-showcase)以及[版本与升级](./template-upgrades)。 文件上传的频率保护、可选 Turnstile、服务故障处理和真实验收边界见[执行限制与验证](./execution-security)。 异步任务的内部记录、并发、输入保留与未完成的队列 / 界面验收见[任务生命周期](./task-lifecycle)。 --- --- url: /docs/agentbuff-stack/quick-start.md --- # 快速开始 ## 本地准备 安装 Bun 1.4.2+、Python 3 和本地 PostgreSQL,使用隔离数据库。新站从模板复制,不能复用已有站的密钥或数据库。 ```sh bun install --frozen-lockfile cp .env.example .env.local bun -e 'console.log(crypto.randomUUID()+crypto.randomUUID())' ``` 把生成的字符串填入 `.env.local` 的 `BETTER_AUTH_SECRET`。设置独立本地 `DATABASE_URL`,使 `APP_ORIGIN` 和 `PUBLIC_SITE_URL` 同为 `http://localhost:4410`。`preview:start` 和 `bun dev` 都使用本地 Workers 运行器,将邮件捕获到 `.local/outbox.jsonl`,不会调用真实 Resend。 ```sh bun run config:check # 只对刚准备好的隔离数据库执行: bun db:migrate bun preview:build bun run preview:start bun run preview:status ``` 产品演示、工具及工作台:。停止:`bun run preview:stop`。启动器不安装 PostgreSQL,不建立生产数据库。 ## 看配置文档 ```sh bun run docs:dev --host 127.0.0.1 --port 4406 ``` 文档地址:。文档与客户工具站分开构建;`bun run docs:build` 后,`preview:start` 会同时启动 4406 文档预览。文档目录是维护者入口,产品页面不展示技术配置。 ## 改完后检查 ```sh bun run config:check bun typecheck bun web:check bun run test -- --run bun lint bun format:check bun preview:build bun run docs:build ``` `bun typecheck` 会先生成 Astro 内容集合类型,再检查项目引用图,首次安装后可直接运行;无需先启动预览或构建官网。生成产品只同步自己的公开站。`.astro` 为生成目录,不提交;这个步骤不代替 `bun web:check` 的页面模板检查,也不会迁移数据库或调用外部服务。 预览使用构建产物:前端修改后重建并刷新,API 修改后停止再启动。`bun dev` 可用于热更新;本地数据库连接由 loopback `DATABASE_URL` 统一派生,详见[环境变量](./env.md)。 ## 本机演示与新站的区别 本机已有独立 `launchkit` 数据库,端口 54329,及演示账号。新复制的站没有这些账号或历史数据,不应把演示密码作为上线默认账号。 ## 官网与独立演示 本仓库 `localhost:4400` 为模板官网,完整产品演示位于 `localhost:4410`。端口由 `preview.config.json` 管理,`preview:build` 与 `preview:start` 自动使用同一配置。文档仍在 `localhost:4406`。详见[官网、案例与演示](./website-showcase)。 --- --- url: /docs/agentbuff-stack/website.md --- # 网站配置 **唯一入口:`packages/core/website.ts`**,类型为 `WebsiteConfig`。这里没有服务密钥,不读取 `process.env`,可安全用于 Astro、React 和 API。兼容的 `site` 对象由它派生。 ## 配置结构 ```ts export const websiteConfig: WebsiteConfig = { metadata: {/* 品牌、文案、语言、联系邮箱、OG 图片、社交链接 */}, brand: { mark: "✳", logo: { light: null, dark: null }, favicon: "/brand/favicon.svg", touchIcon: null, emailLogo: null, }, i18n: { locales: ["en", "zh"], translations: { zh: {/* 完整中文产品文案 */}, es: null }, }, ui: { defaultMode: "system", enableSwitch: true }, auth: { password: true, emailOtp: true, passkey: true }, blog: { enabled: true, homePosts: 2, relatedPosts: 3 }, notifications: { taskEmail: false, preferenceVersion: "2026-10-11", retentionDays: 30, operator: { enabled: false, since: null }, }, analytics: { provider: "none", toolEvents: { enabled: false, consentVersion: "2026-10-10", retentionDays: 30, }, serverEvents: { enabled: false, consentVersion: "2026-10-10.2", retentionDays: 30, }, }, marketing: { newsletter: false, waitlist: false, contactsSync: false, consentVersion: "2026-10-10", confirmationHours: 24, resendMinutes: 15, }, payment: { provider: "stripe", enabled: true, trialDays: 14 }, support: { provider: "crisp", enabled: false, websiteId: null }, affiliates: { provider: "rewardful", enabled: false, campaignId: null, stripeAccountId: null, tracking: { enabled: false, publicKey: null }, }, credits: { signupCredits: 20 }, history: { maxSavedRuns: 100 }, storage: { enabled: true, maxFileBytes: 5 * 1024 * 1024, maxFiles: 25, maxTotalBytes: 50 * 1024 * 1024, retentionDays: 7, uploadsPerMinute: 10, readsPerMinute: 120, }, plans: [/* free、starter、pro */], }; ``` 上面的注释只说明结构,完整默认值以源码为准。账号、私有文件、任务与支付已有本地验收;营销订阅、联系人同步以及公开工具 / 工作台观测默认关闭。工作台观测包含实际任务、附件下载、积分包与个人订阅账单确认。团队付款观测已有独立选择、管理权限保护和去重恢复;AI 扩展、云资源和真实外部环境仍按计划验收,见[模块总览](./modules)。 ## 品牌与元数据 | 字段 | 作用 | | --- | --- | | `metadata.id` | 小写站点 ID;认证 cookie 前缀。上线后修改会使原 cookie 不再被读取 | | `metadata.name` | 网站标题、页头、邮件品牌、Passkey 名称;同步修改 Wrangler `APP_NAME` | | `metadata.tagline` / `hero` / `description` | 默认标题、首页两行主标题和描述 | | `metadata.language` | 默认语言保持 `en`;第二语言通过 `i18n` 启用,不直接更改此字段 | | `metadata.supportEmail` | Contact 页联系方式;上线前填真实地址 | | `support` | 默认 `{ provider: "crisp", enabled: false, websiteId: null }`;开启需公开 Website UUID 与真实支持邮箱,只在联系页主动加载,失败保留邮件回退,见[教程](./support-chat) | | `metadata.ogImage` | `null` 或 `/brand/` 内 PNG / JPEG / WebP;静态分享元数据使用它,两个产品构建的 public 目录均须有文件 | | `metadata.social` | `{ label, href }[]`;生成页脚公开链接,空数组不显示 | 公开域名放在 `PUBLIC_SITE_URL`,认证同源域名放在 `APP_ORIGIN`。本站三 Worker 以一个公共域名工作,两者应相同。新站工厂还会更换安装清单名称和 Worker 名称,并重置品牌素材、OG、联系方式与社交链接。`brand` 字段、图片目录、浅深主题和邮件限制见[品牌素材](./brand-assets)。 ## 产品语言 `i18n.locales` 默认 `["en"]`;可开启 `["en", "es"]`,需要完整的 `i18n.translations.es` 元数据与计划说明。首页、功能与价格共用页面结构,真实译文放在产品字典;登录 / 注册 / 找回与重置、工作台首页、任务列表 / 详情、文件库、历史、积分 / 购买记录、成员 / 邀请 / 设置与管理后台已支持语言路径与认证回跳;公开工具列表、JSON / 文本完整交互与说明已支持双语,博客目录、两篇全文译文与分语言 RSS 已交付,账号验证 / 重置 / 验证码 / 邀请邮件已本地验收,任务邮件持久化语言已本地验收,营销确认邮件语言已本地验收,托管支付回站语言已本地验收。路径、语言切换、缺失译文与 SEO 规则见[产品语言](./languages)。 ## 主题与登录 | 字段 | 默认 | 实际效果 | | --- | --- | --- | | `ui.defaultMode` | `system` | 没有已保存偏好时的默认主题;公共页与工作台预绘制脚本都读取它 | | `ui.enableSwitch` | `true` | 显示 / 隐藏公共页、工作台和设置里的主题切换;不清除已有用户偏好 | | `auth.password` | `true` | 同时控制密码登录 / 注册表单和服务端密码认证 | | `auth.emailOtp` | `true` | 同时控制 OTP 入口和 Better Auth OTP 插件 | | `auth.passkey` | `true` | 同时控制 Passkey 登录入口和服务端插件;需要已注册 Passkey 的账号 | Google / GitHub 通过各自完整的服务端凭据启用。前端查询实际 provider 名称,不复制一份会漂移的 OAuth 开关。至少保留一种让新用户进入的方式;Passkey 本身不能替代首次注册。 ## 内容与运营 | 字段 | 默认 | 实际效果 | | --- | --- | --- | | `blog.enabled` | `true` | 控制导航、页脚、首页文章、文章构建路径、RSS 内容和 sitemap;关闭后 `/blog` 索引页仍存在但 noindex 且无文章 | | `blog.homePosts` | `2` | 首页展示文章数量;0 表示不展示文章卡片 | | `blog.relatedPosts` | `3` | 文章页其他文章的数量;当前按日期排序,不做智能推荐 | | `analytics.provider` | `none` | `cloudflare` 时公共页面插入 beacon;同时需要 `PUBLIC_CF_ANALYTICS_TOKEN` | | `notifications` | 个人任务邮件、站主渠道分别默认关闭,保留 30 天 | 个人邮件需 0022 和验证邮箱选择;站主渠道需 0021、明确起点及服务端秘密地址。去重、有限尝试和失败列表,见[任务邮件](./task-notifications)与[站主通知](./operator-notifications) | | `analytics.serverEvents` | 默认关闭,保留 30 天 | 个人选择授权任务 / 附件读取 / 积分包及个人账单,团队 owner / admin 单独选择授权团队账单;先升级至 0025 并重建 API / 工作台,见[教程](./server-activity-events) | | `analytics.toolEvents` | 默认关闭,保留 30 天 | 公开工具明确选择后发送浏览器使用报告;与访问统计、账号和支付独立;先升级至 0017 并重建公共页 / API,见[教程](./tool-usage-events) | | `affiliates` | Rewardful,默认关闭,计划 / 商户 ID 为空 | 签名回调、佣金 / 结算复核及登录后的个人 / 团队推荐绑定后端;需升级至 0033、两项私密凭据与原 Stripe 完整组。浏览器采集、明确选择与新购买首次结账冻结已受控本地验收;具体订单 / 佣金联查与管理员证据与带原因 / 审计的安全人工复核已受控本地验收;有界漏回调补查与进度 / 审计已受控本地验收;真实购买归因与付款待验,见[教程](./affiliates) | 公开导航集中在 `packages/core/navigation.ts`,公开页头、页脚与工作台共用;首页入口是 `Home`,博客入口是 `Blog`。英文博客来自 `apps/web/content/blog/*.md`,西班牙语同名译文在 `apps/web/content/blog/es/`,工具目录及处理器来自 `packages/core/tools.ts`,工具完整译文集中在 `packages/core/tool-locales.ts`,供公开页和工作台共用。主题变量来自 `packages/ui/design/tokens.css`,字体变更仍需同步资源和许可。 ## 计划、积分和历史 `payment.enabled: false` 同时停用 Stripe 插件和计费可用性接口,已有账单记录不会被删除。为 true 但没有 Stripe 凭据时计费仍关闭;部分凭据会报配置错误。 `payment.trialDays` 控制 Pro 试用天数,0 不提供试用。当前计划固定为 `free`、`starter`、`pro`,对应现有账单与权限映射;增加新计划需要同步服务端映射和支付配置,不只增加卡片。 `plans` 中 `price` 是月度展示价格,`members` 是计划团队限额,`name` 和 `description` 是展示文案。Stripe 实际价格由服务端 Price ID 决定,离线检查不能核实报价是否一致。 Pro 年付选项由服务端 `STRIPE_PRO_ANNUAL_PRICE_ID` 启用,不从这里的月度展示价乘以 12 或推算折扣。设置页分别发送月付 / 年付选择,实际收费项在创建时核对,最终金额在 Checkout 确认;官网当前价格卡片继续展示上述月度价格。 `credits.signupCredits` 是积分账户首次初始化时的赠送额(整数 0—1,000,000),一人一次;已有账户不会因修改配置重领。`history.maxSavedRuns` 控制每用户保存上限,降低上限不删除现有数据,超过上限后需先删除再保存。 `creditPacks` 是一次性积分包目录,默认 `{ enabled: false, packs: [] }`。配置 SKU、名称、版本、整数金额、币种、积分和真实 Stripe Price ID;客户端目录不暴露 Price ID。必须配完整 Stripe 凭据和独立积分回调,并完整迁移至 0024、重建工作台 / API 后才能启用;退款 / 争议对账已本地验收,真实沙箱仍待验收,字段及流程见[支付教程](./billing-tasks)。新站生成器清空目录,需为新站重新配置。 ## 用户密钥与外部接口 `apiKeys` 默认关闭:每人最多 10 把有效密钥,新密钥每分钟 120 次、最长 365 天。启用前升级 0013 并重建前后端;设置页提供一次性展示、改名 / 撤销,`/api/v1` 只开放个人文件与任务。密钥限额叠加原有账号限额、文件配额和任务收费,不授予站点或团队管理员身份。字段范围、幂等、版本重试与本地验收见[用户密钥教程](./api-keys)。本任务主预览保持关闭,0013 尚未应用到主数据库。 ## 邮件订阅与候补名单 `marketing.newsletter` 与 `marketing.waitlist` 默认关闭。匿名申请必须单独勾选用途,收到确认邮件后再主动确认;退订不影响账号邮件。`consentVersion` 记录文案版本,确认有效期为 1—24 小时,再次申请间隔为 1—60 分钟。当前版本启用前升级包含 0016 的完整迁移,并重建公共页与 API;主预览保留 0012。`contactsSync` 独立启用两个用途的供应商同步和签名回调;缺配置拒绝启动,两个主题必须默认退订。公开工具使用报告已单独接入,见[使用事件](./tool-usage-events);工作台使用与积分包确认见[服务端事件](./server-activity-events);个人订阅付款也已本地验收;团队独立选择及付款观测也已本地验收,真实外部环境仍在 I11 后续范围,见[邮件订阅教程](./marketing-subscriptions)。 ## 修改与检查 `storage` 的范围、配额占用、原始二进制上传、会话授权下载、批次清理和本地验收方式,见[私有文件存储](./files-storage)。文件工作台已接入主预览,实际 320 / 390 像素布局已检查;两个生成站删除按钮的完整浏览器流程仍待验证,不把开关开启当成完整模块交付。 `tasks` 控制个人异步任务:`enabled` 停止新提交 / 重试但保留历史 / 取消;`maxActive` 默认五个活跃名额;`maxAttempts` 默认三次,创建时保存到任务,配置变更不增加已有预算;`writesPerMinute` 默认十次,`readsPerMinute` 默认一百二十次,服务端逐过程原子计数。`creditCost` 默认 0,整数 0—1,000,000;`priceVersion` 是报价版本,改价需更新,旧任务保存原价。报价确认与返还规则见[支付、积分与任务](./billing-tasks)。开启任务必须开启 `storage` 并配置 API 的 `STORAGE` / `TASK_QUEUE`,前后端一起重建。处理器、结果、工作台与验收范围见[任务生命周期](./task-lifecycle)。 ```sh bun run config:check bun typecheck bun build ``` 前后端必须一起重建并发布,避免页面和 API 的开关不一致。环境变更后重启 API。检查命令验证关键范围、凭据组合、示例文件覆盖和域名一致性;不替代真实服务验收。 ## 官网与产品配置分开 本页的 `packages/core/website.ts` 控制生成产品,包括品牌、认证、积分和产品套餐。模板官网配置在 `apps/website/config/site.ts`,不读取产品套餐。官网、案例及独立演示的维护方式见[对应教程](./website-showcase)。 ## 上传验证开关 `security.turnstile.enabled` 默认为 `false`,开启后保护文件上传、任务提交、手动重试、积分购买与营销申请,前后端一起重建。实际凭据保留在 API 环境中,不填写到网站配置。收费与外部 API 已复用业务限制和验证,见[执行限制与验证](./execution-security)。 --- --- url: /docs/agentbuff-stack/env.md --- # 环境变量 可选[客服聊天](./support-chat)没有新的环境密钥:`support.websiteId` 是公开站点标识,放在产品配置;真实支持邮箱用 `metadata.supportEmail`。默认关闭,未知供应商、非法 ID 和开启后缺少邮件回退会被配置 / 构建检查拒绝。不要把 Crisp REST API 凭据放入前端配置。 ## 文件与安全边界 根目录 `.env` 是仓库已有的共享默认值和占位符;**真实凭据只放被忽略的 `.env.local`**。从 `.env.example` 复制,不能把生产值提交回 `.env`。新站工厂不复制任何本地凭据、数据库或 Git 历史。 API 契约:`apps/api/lib/env.ts`。`local.ts`、`dev.ts` 和 `worker.ts` 都通过 `parseEnv` 检查基础字段及 OAuth / Stripe 组合;报错仅包含变量名和问题类别。Bun 本地加载环境文件,部署后的 Worker 从 Secrets 与绑定读取数据,不会读取电脑上的 `.env.local`。 ## 核心与构建 | 变量 | 可见性 / 使用位置 | 要求 | | --- | --- | --- | | `ENVIRONMENT` | 服务端 | `development` / `staging` / `production` | | `APP_NAME` | 非秘密品牌;Wrangler / API / 构建兼容 | 与 `websiteConfig.metadata.name` 同步;工作台实际品牌取配置 | | `APP_ORIGIN` | 非秘密;API、OAuth、邮件链接 | 裸 origin;非开发环境必须 HTTPS | | `PUBLIC_SITE_URL` | 公开;Astro 构建 | canonical、OG、RSS、sitemap;线上与 `APP_ORIGIN` 相同,本地可预览线上 canonical | | `API_ORIGIN` | 本地开发 | Vite 代理目标;完整预览默认 `http://127.0.0.1:4600` | | `PORT` | 本地开发 | API 端口,完整预览为 4600;更改需同步网关 | | `DOCS_SITE_URL` | 公开;文档构建 | 可选文档域名;缺省文档 noindex 且不生成 sitemap | `PUBLIC_*` 会进入公开网页。`VITE_APP_NAME`、`VITE_DEFAULT_MODE`、`VITE_SITE_LANGUAGE` 由工作台构建从网站配置派生,不需要手工配置。不要把私钥或账号级 Cloudflare token 放进这些变量。 ## 数据库与绑定 | 变量 / 绑定 | 使用位置 | 要求 | | --- | --- | --- | | `DATABASE_URL` | 本地预览、Drizzle CLI | 含凭据,服务端保密;本地完整预览限制 loopback 数据库 | | `STORAGE` | API Worker 资源绑定 | 私有 R2 对象;本地文件 API 与定时清理已验收,预览数据库需具备迁移 0002 / 0003;测试 / 正式环境已分别声明,资源创建与云端验收待做 | | `TASK_QUEUE` | API Worker 资源绑定 | 队列对象,名称在 Wrangler 各环境分别声明;任务消费者与实际本地验收已接入,数据库需迁移至 0006 | | `TASK_DEAD_QUEUE_NAME` | API Worker 服务端变量 | 必须与死信消费者队列名一致,用于区分业务 / 死信批次;开发运行时从 Wrangler 派生,生成器同步重命名;云端资源仍待验收 | | `DISCORD_NOTIFICATION_WEBHOOK_URL` | API 秘密 | 可选站主频道地址,启用需明确起点与完整迁移 0021;仅允许标准 Discord 服务地址 | | `LOCAL_OPERATIONS` | 仅本地运行时注入 | 站主消息捕获,开发缺失不回退外网;非开发环境不得携带 | | `LOCAL_MAIL` | 仅本地运行时注入 | 邮件捕获绑定,不作为云端变量或公开配置 | | `LOCAL_MARKETING` | 仅受控本地验收注入 | Contacts 测试绑定;开发环境缺少时明确失败,禁止回退到外部请求 | | `HYPERDRIVE_CACHED` / `HYPERDRIVE_UNCACHED` | 生产 Worker 资源绑定 | 配置 ID 在 API Wrangler 中;数据库原始凭据保存在 Hyperdrive 服务 | | `APP_SERVICE` / `API_SERVICE` / `ASSETS` | Web Worker 资源绑定 | 服务绑定与静态资产,不是环境变量字符串 | 可选[任务邮件](./task-notifications)复用现有邮件变量,不引入新密钥或新队列绑定;启用前完整升级 0022,重建 API / 工作台 / 公共页,并核对定时维护。`APP_NAME`、`APP_ORIGIN`、发信地址或密钥变化会停止旧通知重试,接收地址和内容不能由客户端指定。正常预览仍关闭,两张通知表尚未应用到主库。 可选[站主通知](./operator-notifications)使用独立服务端频道地址和 `notifications.operator` 起点 / 开关;不复用个人邮件选择。启用前完整升级 0021 并重建。开发环境始终捕获;主库未升级,默认关闭。 迁移 CLI 与生产 API 的连接路径不同。运行 `db:migrate` 前核实 CLI 的数据库目标;不因配置检查通过就自动执行生产迁移。 线上应用使用独立业务角色,迁移连接使用实际所有者。更换数据库密码时更新该环境两个 Hyperdrive 的源凭据,并核对已有连接池;改本机 `DATABASE_URL` 不会更新云端。授权脚本与隔离复验见[数据库角色与凭据](./database-roles)。 `bun dev` 与完整预览均从 `DATABASE_URL` 派生两个本地 Hyperdrive 绑定,不再使用旧的 `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_*` 覆盖。支持 loopback PostgreSQL 的 trust 认证;模拟器要求非空密码,因此空密码仅在内存中补本地占位值,不改环境文件或数据库用户。 资源对象类型集中在 `apps/api/lib/bindings.ts`,与 `parseEnv` 的普通字段分开。本地 R2 / 队列持久化目录为本项目 `.local/workerd/`,重启保留,生成站不复制或共享该目录。开发、测试、正式环境已分别声明存储、任务 / 死信队列与定时维护;云端资源尚未创建或验收。`bun deploy:check --target staging` 检查资源关系与占位配置,不能验证资源存在,见[部署与验收](./deployment)。 `bun dev` 监听 API、数据库模型、公共产品配置及已构建邮件模板,重新打包并重载 Worker。修改本地运行器本身、启动变量或邮件模板源码后,重启开发命令以重新加载运行器和构建邮件模板。完整预览使用构建产物,修改后按快速开始重建并重启。 ## 认证与邮件 | 变量 | 规则 | | --- | --- | | `BETTER_AUTH_SECRET` | 至少 32 字符,每站每环境独立;非开发环境拒绝已知占位符 | | `BETTER_AUTH_SECRETS` | 可选、按顺序排列的 `版本:密钥`;未设置时明确使用版本 0。切换当前密钥需重新登录,基础密钥仍影响邮件选择与营销链接,见[认证密钥轮换](./auth-key-rotation) | | `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | 两个一起设置或一起留空;仅服务端读取 | | `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | 两个一起设置或一起留空;仅服务端读取 | | `RESEND_API_KEY` | 线上邮件必须有值;`bun dev` / 完整本地预览由捕获器提供临时测试密钥,不会真实发送 | | `RESEND_EMAIL_FROM` | 合法邮箱,线上使用已验证发信域;本地可用 `preview@example.test` | 本地完整预览会把邮件写到 `.local/outbox.jsonl`,里面有 OTP / 验证链接,应只留在本机。Google / GitHub 的 Client ID 不是前端直接用的登录密钥,本站仍统一由服务端管理。 ## 营销联系人 `marketing.contactsSync` 默认关闭,独立于两个申请页面开关。开启时复用具备 Full access 的 `RESEND_API_KEY`,以下三项一起设置: | 变量 | 规则 | | --- | --- | | `RESEND_MARKETING_WEBHOOK_SECRET` | 营销回调独立签名 secret,`whsec_` 后为供应商提供的 Base64 内容 | | `RESEND_NEWSLETTER_TOPIC_ID` | 邮件订阅主题 UUID | | `RESEND_WAITLIST_TOPIC_ID` | 候补名单主题 UUID;必须与邮件订阅不同 | 当前名单 API 需完整迁移至 0016,同步时两个供应商主题的默认订阅必须为 `opt_out`。配置检查不联网验证权限或默认订阅。主预览保持 0012 与关闭状态;`--contacts` 使用独立数据库、完整迁移和测试绑定。完整说明见[邮件订阅与候补名单](./marketing-subscriptions)。 ## 支付 以下四项**全部设置或全部留空**。即使网站配置停用了支付,也不接受半组凭据,以便暴露部署错误。 | 变量 | 格式 / 职责 | | --- | --- | | `STRIPE_SECRET_KEY` | `sk_` 前缀,服务端调用 Stripe | | `STRIPE_WEBHOOK_SECRET` | `whsec_` 前缀,验证 Better Auth 订阅 webhook | | `STRIPE_CREDITS_WEBHOOK_SECRET` | 可选 `whsec_` 前缀,积分包独立回调;必须与订阅 endpoint secret 不同,启用积分包时必填 | | `STRIPE_STARTER_PRICE_ID` | `price_` 前缀,对应 Starter 月度价格 | | `STRIPE_PRO_PRICE_ID` | `price_` 前缀,对应 Pro 月度价格 | | `STRIPE_PRO_ANNUAL_PRICE_ID` | 可选 `price_` 前缀,需前四项完整;独立的 Pro 年度价格,启用设置页年付选项 | 测试和生产使用各自的 Stripe 密钥、价格及 webhook secret,不混用。一次性积分包还需公开目录 `creditPacks`、独立回调 secret,并完整迁移至 0024、重建工作台 / API;当前仍要求前四项订阅凭据完整,不能仅配两个积分包变量。退款扣回、争议恢复和八连接并发已本地验收,真实沙箱待验收,见[支付教程](./billing-tasks)。当前没有 Creem、Waffo 运行时变量。 三个订阅 Price ID 必须不同,配置校验只报告字段要求,不输出实际 ID。月付商品需要一月一个周期,年付 Pro 需要一年一个周期;目前仅支持已启用、同测试 / 正式模式、固定正整数金额、按单位计价的 licensed 订阅商品,不支持多年月期、计量、阶梯价或一次性商品冒充订阅。创建 Checkout 前通过 SDK 核对实际收费项,配置存在不代表供应商商品已核实。 不配置年付 ID 时,设置页只提供月付;`config:check` 的 `StripeAnnual` 显示是否已配置。离线检查不连接 Stripe,实际收费金额和周期须在 Checkout 核对,真实付款仍需沙箱验收。 ## 联盟营销 Rewardful 后台核查默认关闭。`REWARDFUL_API_SECRET` 与 `REWARDFUL_WEBHOOK_SECRET` 一起设置或一起留空,分别用于只读 REST API 与原始字节签名验证。开启还需 `sk_test_` / `sk_live_` 的 Stripe 私钥,以及已有 Stripe 四项完整凭据组;推广计划 UUID 和商户账户 ID 放在 `websiteConfig.affiliates`。这些服务密钥不放入前端配置。先完整升级至 0033 并重建 API,正常主预览不自动迁移。 浏览器追踪默认关闭;仅公开推荐页明确同意后加载 SDK,工作台仅读取本地候选并分别确认个人 / 团队绑定。`tracking.publicKey` 只能填写公开浏览器 key,不能填写服务密钥;200 回调只表示持久接收,供应商 `paid` 不证明银行到账。配置、有限恢复、退款 / 结算复核与受控本地验收见[联盟营销教程](./affiliates)。 ## 统计 `PUBLIC_CF_ANALYTICS_TOKEN` 是 Web Analytics 的公开 beacon token。只有 `websiteConfig.analytics.provider === "cloudflare"` 才构建统计脚本;缺失 token 会使检查和 Astro 构建失败。它不是 Cloudflare API / 账号权限 token。私有工作台没有植入该脚本。 ## 检查方式 ```sh bun run config:check bun --env-file .env.staging.local scripts/config-check.ts --target staging bun --env-file .env.production.local scripts/config-check.ts --target production bun run config:check --json ``` 命令读取当前本地准备的环境,`--target` 不会把开发变量自动转换成生产变量。会检查匹配的 `ENVIRONMENT`、必需字段、凭据组、网站范围、域名和 `.env.example` 字段覆盖。只输出变量名与模块状态,不输出值;错误返回非零退出码。 该命令不联网,不验证账户权限、价格真实性、邮件可送达性、Hyperdrive / R2 绑定或 Worker 部署。生产 Worker 请求入口也做环境校验,但它不能代替部署前的沙箱验收。 ## 模板官网与预览端口 `WEBSITE_ORIGIN`、`WEBSITE_DOCS_URL`、`WEBSITE_DEMO_URL` 只作用于模板官网构建,均为公开地址,不是服务凭据。生成产品不需要这些变量。 官网域名已确认为 `stack.agentbuff.dev`,专用示例为 `.env.website.example`;当前只配置、暂不发布。公开教程和模板演示地址尚未确定,示例留空,正式构建会拒绝未补齐的地址。它不设置产品 `APP_ORIGIN`,也不要求先接入真实模型或产品数据库。 `preview.config.json` 是构建产物预览的端口来源。`bun preview:build` 和 `bun preview:start` 派生产品 origin 与内部端口;生成器的 `--port` 设置新项目入口。生产构建仍以实际公开域名配置为准。 ## 可选上传验证 `PUBLIC_TURNSTILE_SITE_KEY` 是 widget 的公开键,由文件配置 API 提供给工作台;`TURNSTILE_SECRET_KEY` 只供 API 调用 Siteverify。两个值成对配置。`security.turnstile.enabled` 默认关闭;开启后缺配置会拒绝启动。已知官方测试键只允许 development。详细操作与验收范围见[执行限制与验证](./execution-security)。 营销确认邮件复用以上邮件变量;启用名单前完整迁移至 0023,重建公共页与 API。语言来自每代主动同意的记录,不能用环境变量或请求头改写重试语言,详见[邮件订阅](./marketing-subscriptions)。 --- --- url: /docs/agentbuff-stack/modules.md --- # 模块总览 模块全面指每项能力有位置、依赖、边界和验收要求。下面区分当前已经交付的模块与后续模块;没有给待开发服务添加会让人误以为可用的开关或密钥。 状态:**本地可用**=已运行 / 测试;**接口已接入**=代码存在,第三方实测仍待做;**基础配置**=仅基础资源或源码;**待开发**=当前不能启用。 与完整计划逐项对照的当前缺口见[验收核对](./completion-audit-2026-10-11)。下面的本地状态不能代替真实模型、外部服务、云端或两个真实产品验收。 ## 当前模块 | 模块 | 状态 | 技术 / 入口 | 启用与验收 | | --- | --- | --- | --- | | 页面与工具目录 | 本地可用 | Astro;`apps/web/pages/` | 修改品牌、任务文案;检查静态 HTML 和直接 URL | | 工具交付 | 本地可用 | React 交互组件 + `core/tools.ts` | 本地 JSON / 文本处理;预览、复制、下载、错误输入 | | 品牌素材 | 本地可用 | `brand` + `/brand/`;[中文教程](./brand-assets) | 浅深图标、浏览器图标、安装清单、OG 与实际邮件;预检及新站重置已验收,真实邮箱显示待验 | | 产品语言 | I13 公开页与账号 / 任务阶段本地可用,默认仅英文 | 共享 `i18n` 配置 + Astro 静态页面;[中文教程](./languages) | 开启西班牙语后发布首页 / 功能 / 价格,以及登录 / 注册 / 找回与重置、工作台首页、任务列表 / 详情、文件库、历史、积分 / 购买记录、成员 / 邀请 / 设置与管理后台;语言导航、认证回跳及公开页 SEO 已验,工具列表与 JSON / 文本完整交互已验,博客目录 / 两篇全文与分语言 RSS 已验,即时账号与邀请邮件已验,任务邮件持久化语言已验,营销确认邮件持久化语言已验,托管支付回站语言已本地验收 | | 共享界面 | 本地可用 | Astryx / StyleX / Radix | `ui` 配置、主题变量;桌面 / 手机、浅深主题 | | 博客与 SEO | 本地可用 | Markdown、结构化数据、RSS、sitemap | `blog` 配置;无草稿、canonical、关闭行为 | | 密码、OTP、找回密码 | 本地可用 | Better Auth + Resend | `auth` + mail 变量;本地捕获 / 线上真实邮箱流程 | | Google / GitHub | 接口已接入 | Better Auth OAuth | 完整凭据组;回调、私有邮箱、取消授权 | | 通行密钥 | 本地管理与协议可用 / 真实设备待验 | Better Auth Passkey | 添加入口、列表、改名 / 删除、近期登录与最后登录方式保护已本地验收;真实系统弹窗及设备注册 / 登录待验 | | 组织与成员 | 本地邀请可用 / 团队管理进行中 | Better Auth 组织插件 + 授权搜索接口 | 创建、个人 / 团队切换、成员搜索 / 分页;邀请、重发、取消、接受 / 拒绝与邮件捕获已验收;套餐人数限制、角色修改 / 移除 / 退出已本地验收;原子所有者转移已本地验收;设置页会话列表、单个 / 批量撤销和重新登录已本地验收;通行密钥管理 / 删除保护已本地验收;真实邮件 / OAuth / 通行密钥设备仍待验 | | 数据与隔离 | 本地可用 | PostgreSQL + Drizzle | 真实表与 PGlite 测试;跨用户 / 跨组织访问检查 | | 结果历史 | 本地可用 | `api/routers/workspace.ts` | 用户隔离、幂等、保存上限、删除、导出 | | 积分账本 | 本地可用 | `api/lib/credits.ts` | 欢迎积分、原子增减 / 退款;免费例子不扣分 | | 一次性积分包 | 接口 / 页面已接入,默认关闭 | Stripe SDK + `api/lib/payments/` | 签名、退款扣回 / 差额、争议恢复和八连接并发本地已验;真实沙箱待验,见[教程](./billing-tasks) | | 订阅与账单 | 接口、月付 / Pro 年付入口及恢复已接入 | Better Auth Stripe | 完整凭据组 + `payment`;年付另需独立年度 Price。价格 / 当前状态与恢复已本地验,真实支付 / 门户沙箱待验收 | | 邮件模板 | 本地可用 / 发送待验收 | React Email + Resend | 纯文本 / HTML;即时账号与邀请邮件支持英 / 西班牙语;任务邮件支持持久化语言与旧载荷兼容;营销确认邮件支持持久化语言与旧载荷兼容;真实投递仍待 | | 公开页统计 | 接口已接入 | Cloudflare Web Analytics | 默认关闭;provider + beacon token;真实数据待验收 | | 工作台使用与购买事件 | I11 本地可用,默认关闭 | 真实任务 / 文件 / 积分包结算 + PostgreSQL | 账号独立选择、版本保护、去重和故障补查;0025 主库未应用;个人订阅账单付款、续费去重与漏回调恢复已接入,团队独立选择、权限保护、账单去重和续费恢复已本地验收,真实供应商待验,见[教程](./server-activity-events) | | 公开工具使用事件 | I11 浏览器报告,默认关闭 | 第一方 API + PostgreSQL | 明确选择、一次处理一个 UUID、累计成功 / 下载、不记录输入;服务端口径另有独立模块,见[教程](./tool-usage-events) | | 客服聊天 | I14 本地已验收,默认关闭 | Crisp + 联系页邮件回退 | 主动加载、打开事件确认、失败 / 超时、迟到回调、英 / 西译文与新站重置已验;真实工作区待验,见[教程](./support-chat) | | 联盟后台核查 | I14 核查与绑定后端本地已验收,默认关闭 | Rewardful REST / 签名回调 + Stripe 当前 Charge | 0033、两项独立密钥与完整 Stripe 组;回调复核、个人 / 团队推荐绑定、有限恢复及新站重置已验。浏览器采集、明确选择与新购买首次结账冻结已受控本地验收;具体订单 / 佣金联查与管理证据与带原因 / 审计的安全人工复核已受控本地验收;有界漏回调补查与进度 / 审计已受控本地验收;真实购买归因与付款仍待,见[教程](./affiliates) | | 开发者文档 | 本地可用 | VitePress | 自有文档首页、网站配置、环境、模块、部署;独立构建 | | 新站复制 | 本地可用 | `scripts/create-site.ts` | 排除凭据 / 数据 / 产物;替换品牌、域名、Worker,重置品牌素材与联系链接、清空积分包目录 | | 配置诊断 | 本地可用 | `scripts/config-check.ts` | 离线、字段级错误、API 三入口运行时检查 | | 本地 Workers 资源 | 运行时、文件 API 与清理、任务消费者已本地验收 | workerd + Miniflare;`api/local/runtime.ts` | 实际本地 Queue / R2、接口 / 页面、死信恢复、私有结果与重启通过;云端待验 | | 私有文件 | API、清理与工作台及 I2 本地验收已有证据 | R2 + `api/lib/files/` | 四种类型、所有权、配额、补偿与批次清理;320 / 390 像素浅深主题、键盘和两个生成站隔离已验,真实云端 / 真机仍待,见[教程](./files-storage) | | Cloudflare / Neon | 基础配置 | Workers / Wrangler / Hyperdrive | 当前未部署;独立账号资源、绑定、真实生产验收 | | 站点管理员与审计 | I9 前五阶段本地已验收 | Better Auth Admin + 原子审计 | 明确初始化、账号搜索、封禁 / 解封、角色、财务只读、积分调整、任务恢复与主动对账 / 审计;生产模式初始化 / 恢复命令已本地验收,真实远程环境待验,见[教程](./admin-audit) | | 用户 API 密钥 | I10 本地可用,默认关闭 | Better Auth API Key + `/api/v1` | 个人文件 / 任务作用域、一次性展示、过期 / 撤销 / 封禁、原业务限额与账本、版本重试;0013 主库未应用,真实云端待验,见[教程](./api-keys) | | 邮件订阅与候补名单 | I11 表单与同步本地可用,默认关闭 | 本站状态 + Resend 联系人 / 主题 + 确认邮件 | 明确同意、主动确认 / 退订、用途隔离、并发和失败重试;0014—0016 主库未应用,同步 / 签名回调 / 失败恢复和业务观测已有本地证据,真实投递待验,见[教程](./marketing-subscriptions) | ## 必须补全的产品模块 I12 第一阶段已交付默认关闭的[任务邮件通知](./task-notifications):已验证账号选择、终态扫描、去重、有限重试、本人投递记录及本地故障恢复。第二阶段加入默认关闭的[站主运营通知](./operator-notifications),任务、已发放积分包、核实个人 / 团队账单与管理员列表;正常预览主库未应用 0020 / 0021,真实投递仍待验收。 下表是模板演进的交付要求,不是已经可启用的服务。按完整工具站所需能力组织,具体 AI 模型与收费形式由第一个验证过的任务决定。 | 模块 | 首选方向 | 最小交付与验收要求 | | --- | --- | --- | | 文件存储完整交付 | Cloudflare R2 | 私有 API、定时清理、会话授权工作台、手机主题组合与两个生成站已有本地验收;真实云端 / 真机仍待 | | 异步任务 | Cloudflare Queues + 数据库状态 | I4 本地服务 / 页面、私有结果、重启、死信恢复与预算耗尽、两份干净生成站闭环已验;云端待验,见[任务生命周期](./task-lifecycle) | | 付费任务与积分包 | Stripe 一次性 Checkout | 验签 webhook、重复事件只发一次、原子预扣、失败退款、退款争议对账 | | AI 调用 | 按真实任务选择的模型服务 | 真实处理器未接入;候选图片转矢量的公开接口、费用、不确定调用与代码差异见[接入核对](./image-to-svg-contract),待任务选择和真实小批次验收 | | CAPTCHA 与业务限流 | Turnstile + 业务限流存储 | 前端 challenge + 服务端验证、action / hostname、超时与重放;Auth 限流不等于工具限流 | | 管理员后续运营 | 既有账本 / 任务 / 支付服务 + 审计 | 任务恢复、主动支付对账、旧结账人工关联与精确审计检索已有本地证据;真实环境权限 / 对账与本站保留安排待验 | | 邮件订阅与候补名单后续 | Resend 联系人 / 主题 + 本站订阅状态 | 接续已验收的确认 / 退订、最新状态同步与供应商回调,补真实 Resend 环境与邮箱验收 | | 通知 | 用户任务邮件 / 站主频道分开,默认关闭 | 任务邮件本地已验;站主消息、付款核实、恢复与列表见[教程](./operator-notifications),真实频道 / 邮箱与云端维护仍待验,失败不撤回业务结果 | | 多语言完整路径 | Astro 路径 + React 字典 | 公开九组页面、账号入口、工作台首页、任务 / 文件 / 历史 / 积分与购买 / 成员 / 邀请 / 设置 / 管理后台路径已验;工具列表与 JSON / 文本完整交互已验;博客目录 / 两篇全文与分语言 RSS 已验;即时账号与邀请邮件已验;任务邮件持久化语言已验;营销确认邮件持久化语言已验;托管支付回站语言已本地验收;真实 Stripe、人工审校和外部抓取仍待验 | | 真实客服环境 | 已选 Crisp | 真实 Website ID、消息往返、Cookie 设置、浏览器限制与供应商异常;本地契约不能代替真实投递 | | 联盟营销后续 | 已选 Rewardful | 原结账参数首次冻结、绑定与购买串联、带审计人工恢复已本地实现;后续补漏回调对账,最后核对真实归因、退款与银行结算;后台观察不等于自动计佣 / 打款 | | 转化事件 | 单一事件契约 | 工具开始 / 完成 / 下载 / 支付,不传内容;与账单事件去重和实际使用证据区分 | ## 完整模块的标准 一个模块交付时必须同时具备:类型化配置、服务端变量 / 资源绑定、真实处理器、必要的用户界面、失败行为、权限或数据边界、最小验证和模块文档。完成后再加入 `websiteConfig` 和 `.env.example`,并更新本表状态。 下一阶段优先形成“文件 → 任务 → 预览 → 导出 → 积分 / 支付”闭环,再补增长模块。轻量客户端任务仍可保持免费与无登录;低频任务不强行套订阅。 对照 MkSaaS 后的技术选择、API 密钥等额外缺口和分批验收标准,见[功能扩展路线](./feature-roadmap)。 I3 的文件执行保护与可选 Turnstile 已接入,真实 widget 和未来任务 / 收费入口仍待验收,见[执行限制与验证](./execution-security)。 --- --- url: /docs/agentbuff-stack/feature-roadmap.md --- # 功能扩展路线:对照 MkSaaS 核对日期:2026-10-09(北京时间)。基线为 AgentBuff Stack `324d1ff1` 的代码和当前公开文档。本页是后续实施范围与验收要求,不表示未开发模块已交付;当前状态以[模块总览](./modules)和[能力清单](./capabilities)为准。 具体代码位置、数据变更、依赖、异常处理和逐轮验收,见[开发迭代计划](./development-plan)。 ## 1. 技术选择与目标 目标是让模板覆盖常用独立站功能,复制后按需启用。通用文件、任务、支付与运营能力可以先建设,不要求先选定一个具体关键词产品;具体模型、任务参数和定价仍由产品决定。 MkSaaS 的公开[技术栈](https://mksaas.com/zh/docs)包含 Next.js、Drizzle、Better Auth、Resend、Stripe、AI SDK、国际化和组件库。我们的对应选择如下;这不是更换框架的计划。 | 职责 | AgentBuff Stack 的选择 | 实施边界 | | --- | --- | --- | | 公开内容与 SEO | Astro 静态页面 | 官网与产品分别构建,正文可直接阅读 | | 登录后的工作台 | React + TanStack Router / Query | 保留既有会话守卫与数据缓存 | | 业务 API | Hono + tRPC + Zod | 输入校验、权限和业务限制在服务端 | | 数据 | PostgreSQL / Neon + Drizzle | 保留 Hyperdrive 的新鲜读写与缓存读边界 | | 身份认证 | Better Auth | 扩展已有认证,不另建平行账号系统 | | 视觉系统 | Astryx + StyleX + 共享变量 | 官网与产品复用通用组件,不增加第二套主题系统 | | 邮件 | React Email + Resend | 事务邮件与营销订阅分开 | | 支付 | 先完善已有 Stripe 集成 | 第二个供应商有实际需求后再扩展 | | 文件与任务 | R2 + Queues + 数据库任务状态 | 这是我们的实施选择,不是从 MkSaaS 文档推断其具备完整任务系统 | | 维护教程 | VitePress,中文 | 与面向最终用户的产品帮助内容分开 | ## 2. 功能差异与完整交付要求 | 能力 | 当前代码边界 | 下一步交付 | | --- | --- | --- | | 身份认证 | 密码、OTP、恢复本地可用;OAuth / Passkey 有集成 | 第三方验收;Passkey 添加、列表、撤销及会话管理 | | 团队 | 创建、切换、成员搜索与分页可用 | 邀请投递与接受、角色编辑、成员移除;区分站点管理员与团队管理员 | | 用户文件 | 只有可选 R2 基础设施配置,没有文件 API 与工作台 | 上传、列表、查看、下载、删除、配额与保留期;头像公开资产与私有任务文件分别定义访问规则 | | 异步任务 | 未实现 | 提交、状态、结果、重试、超时、取消与失败记录 | | 积分 | 有原子账本和内部增减方法 | 预扣、成功结算、失败返还;重复提交、重复消费均不会重复扣费 | | 支付 | Stripe 订阅插件已有;真实沙箱尚未验收 | 一次性积分包、购买状态、签名回调、重复事件处理、退款对账;再补齐订阅完整流程 | | AI | 无业务模型接入 | 服务端处理器、超时、版本与用量记录;输出由具体产品校验,不公开模型密钥 | | 验证码与限制 | 业务 Turnstile 与限流未实现 | 前后端验证闭环、用户 / 访客限制、超限提示;上传和付费调用分别设限 | | 用户管理 | 没有站点管理员角色或管理路由 | 用户查询与封禁、任务排查、积分审计;所有写操作有服务端授权与审计记录 | | API 密钥 | 没有用户 API Key 表与接口 | 创建、一次性展示、列表、撤销、有效期、作用域、调用限制;不能复用浏览器会话保护外部 API | | 统计与转化 | Cloudflare 公开页统计已接;公开工具浏览器使用报告已本地验收,均默认关闭 | 公开工具独立同意、并发去重、开始 / 成功 / 下载发起见[教程](./tool-usage-events);工作台实际任务 / 附件下载、已验证积分包确认与确认页去重已本地验收,需账号独立同意,见[服务端教程](./server-activity-events);个人订阅账单已接当前付款核查、续费去重与恢复;团队独立选择、权限保护、账单去重与恢复已本地验;真实外部环境仍待验 | | 邮件订阅与候补名单 | I11 表单与同步已本地验收,默认关闭 | 已交付独立同意、确认 / 退订、联系人 / 主题最新状态同步、签名退订回调及失败重试;继续转化事件与真实外部验收 | | 通知 | I12 个人邮件与站主频道分别默认关闭 | 邮件选择 / 投递列表,站主任务 / 已核实付款与管理员回执、有限重试;真实外部与云端待验,见[个人邮件](./task-notifications)及[站主通知](./operator-notifications) | | 多语言 | 默认产品英文,教程中文;可选西班牙语公开六页、账号入口、工作台首页及任务 / 文件 / 历史 / 积分与购买记录 / 成员 / 邀请 / 设置 / 管理后台已本地验;真实字典 / 切换 / 认证回跳、公开页 SEO 与明确英文回退已交付 | 工具列表与 JSON / 文本完整交互已交付;博客、实际邮件语言及人工审校;[中文教程](./languages) | | 客服与联盟营销 | Crisp 客服及 Rewardful 核查 / 推荐绑定后端本地已验,默认关闭 | 联系页与真实工作区见[客服教程](./support-chat);联盟验签、当前资源 / 退款、个人 / 团队绑定及有限恢复见[联盟教程](./affiliates),公开采集与设置已本地验收;完整支付归因和真实结算继续验收 | | 页面与内容 | 官网、产品博客、静态 SEO、配置化品牌素材已本地验收;见[品牌教程](./brand-assets) | 真实条款与品牌素材外部显示验收;按真实用途增加任务页,不建设万能页面生成器 | 以上是代码和文档对照,不是本轮重新执行所有模块的验收。参考页面:[存储](https://mksaas.com/zh/docs/storage)、[支付](https://mksaas.com/zh/docs/payment)、[AI](https://mksaas.com/zh/docs/ai)、[用户管理](https://mksaas.com/zh/docs/usermanagement)、[API 密钥](https://mksaas.com/zh/docs/apikeys)。 尤其需要区分:MkSaaS 的 API 密钥模块是用户调用产品 API 的凭据管理;我们现有的 `.env` 校验处理的是站主服务配置,两者不能互相替代。MkSaaS 的通知文档介绍站主渠道通知;我们的用户任务通知是额外需求。 ## 3. 实施批次 ### 第一批:文件能力 在 API 增加文件路由与 R2 绑定,数据库保存对象归属、类型、大小、状态和过期时间,工作台增加文件列表与上传入口。先支持明确列出的输入格式,限制参数可由产品调整。 用户文件默认私有。通过文件记录授权读取和删除,不接受客户端指定他人对象 key。若采用直传,上传授权、上传完成确认和实际对象校验必须一起实现。上传、删除及过期清理的中断状态可恢复,不能仅因请求失败就遗失对象记录。 验收:本地存储模拟可用;跨账号访问拒绝;超限与类型不符拒绝;上传、查看、下载、删除形成真实闭环。资源配置与本地演示分别记录;没有真实 R2 验收时标为本地可用。 ### 第二批:任务与执行保护 引入 Queues 和数据库任务记录。状态由服务端维护,工作台展示实际进度阶段与结果。先用确定性文件处理任务验收队列、结果文件与重试,不用假 AI 成功结果冒充模型接入。 提交幂等键与任务关联,队列消息只携带必要标识。处理器明确哪些错误可重试,设置次数上限与失败恢复;取消请求不承诺撤销已经执行的第三方操作。同步任务继续使用同步路径。 同批增加任务提交限流和可选 Turnstile 验证。收费业务不能等待增长模块完成后才保护资源。 验收:重复投递不会重复执行有效交付;重试耗尽进入明确失败;失联任务可检测;状态查询按归属授权;浏览器刷新后可继续查看真实状态。 ### 第三批:收费与积分闭环 复用 `apps/api/lib/credits.ts`,将账本事件关联任务和支付事件;预扣、结算和失败返还必须与终态规则一致。先交付一次性积分包,再验收已有订阅的购买、续费、取消与权益变化。 支付凭据不进入公共配置。价格配置分别表达展示金额、币种、周期、积分数量和真实商品 ID;金额单位保持一致,核对第三方实际价格。权益由验签后的支付事件写入,不根据成功页参数发放。 验收:重复回调只发放一次;无效签名拒绝;余额不足拒绝;任务失败只返还一次;退款与争议具有明确对账策略。真实支付仍需单独沙箱验收。 ### 第四批:账号与运营完整性 完成组织邀请与角色操作、Passkey 凭据管理;新增站点管理员授权、用户管理、任务排查及账本审计。API Key 单独作为可选模块,为明确对外开放的接口增加作用域和限额,避免为了后台页面创建不需要的外部 API。 验收:普通用户无法调用管理员接口;组织权限不自动提升为站点管理员;封禁与撤销凭据的实际访问行为可验证;敏感修改具有审计记录。 ### 第五批:增长与本地化 先补转化事件、候补名单和营销订阅,再加入任务邮件、站主通知、多语言、客服与联盟营销。第三方增长脚本按页面用途和启用状态加载,私有工作台不默认承载营销追踪。 多语言保持单语言站可以正常构建;新增语言需要真实译文。候补名单无需强制创建产品账号。营销订阅与账号注册各自记录同意和状态。 参考:[统计](https://mksaas.com/zh/docs/analytics)、[邮件订阅](https://mksaas.com/zh/docs/newsletter)、[通知](https://mksaas.com/zh/docs/notification)、[验证码](https://mksaas.com/zh/docs/captcha)、[国际化](https://mksaas.com/zh/docs/i18n)、[客服](https://mksaas.com/zh/docs/chatbox)、[联盟营销](https://mksaas.com/zh/docs/affiliates)。 ## 4. 配置与代码约束 公共能力配置继续从 `packages/core/website.ts` 进入;服务凭据在 API 环境校验,资源在 Worker 绑定中定义。官网的模块状态只表达已经核实的交付程度,不决定产品运行权限。 新增模块交付完整行为后再加入可启用配置。一个模块的关闭、缺配置和服务故障是不同状态:关闭时页面和 API 明确不可用;启用但缺必要配置应报告具体字段;运行故障给用户明确结果并保留可排查记录。 服务端逻辑首先放在实际使用的 API 路由与服务文件。UI 只复用展示组件,不能导入数据库、计费或模型实现。任务状态等确实跨端使用的契约进入 core;出现第二个真实消费者后再提取共享能力。 不预建全能插件注册器、抽象支付网关或泛化模型引擎。首先实现一个真实提供商,随后按第二个使用场景确定最小接口。保留现有 Better Auth 订阅与账本的单一数据来源。 每批交付代码、配置诊断、必要行为测试、中文教程与验收记录。数据库迁移文件随代码提交;执行迁移、部署、真实发信和真实收费另行按环境安排。 --- --- url: /docs/agentbuff-stack/development-plan.md --- # AgentBuff Stack 开发迭代计划 编写日期:2026-10-09(北京时间)。代码基线:`5e711305`,模板版本 `0.3.0`。本计划细化[功能扩展路线](./feature-roadmap),各轮状态均为待开发;之前的官网、案例、博客与生成器改造已经完成本地验收,不重复施工。 执行更新(2026-10-11):I4 / I5 本地闭环已验收,I6 购买、验签、退款 / 争议对账与八连接并发已本地验收;订阅正常生命周期 / 到期权限 / 更新失败重投、原结账重试 / 八连接门禁 / 回跳归属已本地验,订阅旧通知当前状态同步与创建 / Checkout 保存故障已本地验;回跳完整保存校验及补丁生成 / 安装已本地验;带原请求标记的未知 Checkout 查找、提前过期 / 完成恢复及页面已本地验;年付首购页面、原年付请求重试及创建前价格核对已本地验;未标记旧未知请求的人工核查 / 关联、审计回滚及原用户恢复已本地验;真实沙箱仍待验;完整 I0—I16 范围与外部验收门槛保持。事实记录见[迭代记录](./iteration-progress)。下文基线与待开发描述保留为原计划。 执行更新(2026-10-11):I12 用户任务邮件与站主运营通知已完成本地验收,真实外部投递仍待验;I13 品牌素材、实际邮件品牌与新站复制隔离,以及英文 / 西班牙语的首页、功能、价格三组公开页和语言 SEO 已本地验收。登录 / 注册 / 找回与重置、工作台首页、任务列表 / 详情、文件库、历史、积分 / 购买记录、成员 / 邀请 / 设置与管理后台的语言路径与认证回跳已本地验收;博客目录、两篇全文译文和分语言 RSS 已本地验收,账号验证 / 重置 / 验证码 / 邀请邮件已本地验收,任务邮件持久化语言已本地验收,营销确认邮件语言已本地验收,托管支付回站语言已本地验收,公开工具列表与 JSON / 文本完整交互已本地验收;I13 尚未整体完成。主预览仍使用已授权的 0012,后续可选结构只在一次性本地数据库验收。完整范围和外部验收要求不变,详见[迭代记录](./iteration-progress)。 ## 1. 目标与交付方式 执行更新(2026-10-11,当前范围):按用户要求继续模板与官网的本地迭代,暂不接真实产品、暂不发布云端。默认启用英文与简体中文;官网、产品公开页、认证 / 工作台 / 管理界面、相关邮件、20 篇核心使用指南及教程界面已完成双语本地验收,历史记录保留中文。中文持久化语言约束采用新的 0035 增量迁移,只在隔离测试验证;主预览库保持 0012。独立复制与关闭中文后的重新构建已验证;详细证据见[最新迭代记录](./iteration-progress)。原计划外部验收条件保留,当前双语交付不代表 I13 或完整 I0—I16 已完成。 建设可复制、可配置、可独立运行的通用独立站模板。先服务自己的英文产品,经过真实独立站验证后再出售。通用能力开发与产品选题可以并行;具体业务模型、参数、输出检查与定价由产品拥有。 继续使用 Astro、React、TanStack、Hono、tRPC、Better Auth、Drizzle、Cloudflare、Neon、Astryx / StyleX 和中文 VitePress 教程。官网、产品、工作台、API 与邮件保留已有职责。默认保留产品的三个 Worker,队列与定时入口先归 API Worker;确有独立资源或运行隔离需求再拆消费者。 每轮交付一条可以操作和检查的完整流程。代码、数据约束、配置诊断、界面、失败行为、行为测试和中文教程同时完成;每轮独立提交和验收。不以开关数量、页面数量或目录数量作为完成标准。 本次任务仅编写计划,不启用下表模块,不执行数据库迁移、云资源变更、部署或真实业务调用。 ## 2. 已有能力与工程前置条件 | 基线 | 保留与复用 | 本轮计划需要处理的缺口 | | --- | --- | --- | | 官网与产品应用 | `apps/website`、`apps/web`、`apps/app` | 新模块在产品中运行,官网只展示核实后的状态 | | 本地预览 | `preview.config.json`、`scripts/preview.py` | 当前 `local.ts` 是 Bun HTTP 入口,需要真实 Workers 本地资源与事件入口 | | API 上下文 | `apps/api/lib/context.ts`、三个 API 入口 | 当前只有普通环境与数据库 / 认证上下文,新增资源绑定不能被环境校验覆盖掉 | | 数据与权限 | Drizzle、PGlite、新鲜读写连接 | 新表导出、ID、索引、约束、跨账号与组织权限核查 | | 积分账本 | `creditAccount`、`creditLedger`、`lib/credits.ts` | 当前方法自行开事务,需要支持任务创建与扣费在同一事务完成 | | 账单 | Better Auth Stripe 插件及 `routers/billing.ts` | 保留订阅所有权,新增一次性积分购买,避免重复维护同一订阅状态 | | 路由与导航 | `packages/core/routing.ts` | 新工作台路径同时更新边缘路由、导航与直接刷新验收 | | 生成器 | `scripts/lib/create-site.ts`、`template.json` | 新增 bucket / queue 等名称和绑定不能残留源项目的资源指向 | | 迁移规范 | 现有迁移、数据库说明 | 上游文档称单次压平,但当前已有 `0000` / `0001`;I0 必须确定本项目的增量迁移规范 | Cloudflare 支持通过 Wrangler 本地运行 R2 和 Queues;队列本地运行也有并发能力限制。因此 I0 的运行链路必须先实测,不能仅给 Bun 入口加一个绑定类型就视为完成。[R2 官方说明](https://developers.cloudflare.com/r2/api/workers/workers-api-usage/)、[Queues 本地开发](https://developers.cloudflare.com/queues/configuration/local-development/)。 ## 3. 迭代顺序与里程碑 实施记录与当前未完成项见[持续迭代记录](./iteration-progress)。计划仍覆盖 I0— I16,局部验收不代表完整模板已交付。 版本号是拟定的发布节点,只有对应验收通过后才更新 `template.json`。云端验收缺失时保留明确的本地状态,不发布“生产可用”结论。 | 里程碑 | 迭代 | 可检查的交付结果 | 发布条件 | | --- | --- | --- | --- | | 0.4 文件基础 | I0— I2 | 真实本地资源、上传 → 查看 → 下载 → 删除 | 隔离、清理及生成站回归通过 | | 0.5 任务基础 | I3— I5 | 限流、队列执行、结果交付、积分预扣与返还 | 故障与重复处理验收通过 | | 0.6 商业与模型 | I6— I7 | 积分购买、订阅验收、一个真实模型处理器 | 沙箱支付与真实模型证据齐全,否则标记接入待验收 | | 0.7 账号与运营 | I8— I10 | 账号与团队完整流程、管理员、可选 API Key | 权限、封禁、撤销与审计回归通过 | | 0.8 增长与语言 | I11— I14 | 转化、订阅、通知、多语言、可选客服与联盟营销 | 关闭 / 启用及外部服务验收清楚区分 | | 发布候选版本 | I15 | 独立站部署、备份恢复、费用与运行记录 | 云端完整流程和升级验证通过 | | 1.0 模板交付 | I16 | 第二个独立产品复用与对外分发准备 | 两站边界验证与交付文档通过;销售安排独立确定 | 主依赖链:I0 → I1 → I2 → I3 → I4 → I5 → I6。I7 依赖任务及收费规则;I8 可在文件闭环后独立推进;I9 依赖任务、账本和账号;I10 依赖执行保护与账号;增长迭代消费已经稳定的事件。当前首个开发交付包为 **I0— I2**。 不预排未经验证的日历截止日期。每轮开始前把工作拆成可合并提交,记录预计投入、外部依赖和风险;结束记录实际投入及返工原因。I0— I2 完成后依据实际速度估计剩余阶段。等待凭据、外部验收和产品选题分别记录,不计作代码开发完成。 ## 4. 每轮详细任务 ### I0:本地资源、配置与迁移规则 **目标:** 文件和后台任务使用真实本地运行链路,已有认证、邮件捕获及预览隔离继续可用。 * 在当前版本的 Wrangler / Workers 模拟环境中验证 R2 读写、队列事件和定时处理入口;先做最小运行验证再确定预览启动方式,新增内部端口统一进入 `preview.config.json`。 * 统一资源绑定类型与运行时注入;`worker.ts`、`dev.ts`、`local.ts` 的环境校验与资源处理有明确分工,普通字段和 `R2Bucket` / `Queue` 等对象分别处理。 * API Worker 组合 `fetch`、`queue`、`scheduled` 入口,保留 Hono 的请求处理、权限与错误行为;事件消费者独立调用业务服务,不通过伪造登录 HTTP 请求执行任务。 * 保留本地邮件捕获;新运行模式不允许把本地邮件静默转为外部发送。本地资源默认独立持久化,重启后可恢复,生成站不共享状态目录。 * 将本项目后续迁移确定为增量历史,更新与现状冲突的贡献说明;保留已发布迁移,生成站不修改已应用迁移。迁移生成与执行区分。 * 统一模块三种运行状态:关闭、启用但缺配置、已配置但服务故障。新增模块完成后才加入公开配置契约,不提前批量放入不可运行的选项。 **代码位置:** `apps/api/{worker,dev,local}.ts`、`lib/{context,env}.ts`、`scripts/preview.py`、预览配置、相关 Wrangler 配置、数据库规范和生成器。 **验收:** 两个新生成站的本地 R2 互不相见;队列发送触发消费者;定时处理可手动触发;认证与捕获邮件回归通过;关闭资源模块仍可启动;没有连接远程资源。只有本地并发模拟通过不能宣称线上并发验证完成。 ### I1:私有文件 API 与元数据 **依赖:** I0。 * 新增文件表,建议字段为 `id`、`userId`、`objectKey`、原始名称、检测类型、字节数、状态、用途、校验摘要、过期时间及创建 / 更新时间。对象 key 唯一;用户、状态和过期时间支持必要查询索引。 * 首版采用登录用户个人文件,与现有个人结果历史一致。工作台说明归属;切换组织不会改变文件所有权。团队共享文件后续另设实际权限契约,不预埋未使用的多态所有者系统。 * 元数据采用 tRPC;二进制上传、预览和下载采用 Hono HTTP 路由。先做经服务端校验的受限上传与读取,优先 R2 原生绑定;暂不为了直传引入 S3 凭据和签名体系。 * 首版演示允许 PNG、JPEG、UTF-8 文本和 JSON,小文件上限建议为 5 MiB,这是产品初始策略而非平台额度。各限制从完成的配置读取;文件名、后缀与客户端 MIME 都不能单独决定实际类型。 * 状态包含 `uploading`、`ready`、`deleting`、`failed`;数据库与对象存储没有共同事务,明确写对象前后的失败补偿与恢复路径。 * 每次读写从会话解析所有者并查新鲜数据,不接受客户端传来的 userId 或任意对象 key 作为授权。私有下载设置适当缓存策略;未知与可执行内容默认作为附件下载。 * 文件操作具有大小 / 数量限制和请求频率保护;先实现必要保护,再在 I3 统一到任务侧策略。 **接口草案:** `files.list`、`files.get`、`files.remove`;`POST /api/files`、`GET /api/files/:id/content`。最终命名在实现前固定,避免同一动作维护两套写入口。 **验收:** 合法文件上传后字节与摘要一致;其他用户不能列出、读取或删除;无会话、超限、伪装类型和非法文本拒绝;R2 写入失败不形成可下载记录;对象缺失呈现明确状态。 ### I2:文件工作台、清理与资源复制 **2026-10-11 补充验收:** 已完成 320 / 390 像素与浅深主题四种组合、实际浏览器上传 / 预览 / 原件下载 / 永久删除、任务占用拒绝及解除占用后的原位键盘重试。修复预览 / 取消 / 失败 / 删除成功时的焦点恢复,列表刷新较慢的情况也有回归证据。两个生成站已有文件与隔离验收;真实云端、真机与完整 I0—I16 目标继续保留,见[文件教程](./files-storage)。 **依赖:** I1。 * 在 `apps/app/routes/(app)/files.tsx` 提供上传进度、文件列表、大小、状态、查看、下载、删除、过期提示和空状态。上传百分比只代表传输,不能代表后台处理百分比。 * 更新工作台导航、`APP_PATHS` 和边缘路由验收;进入文件页、刷新与登录回跳都正确。 * 定时清理过期对象、超时上传与失败删除;按批次处理,重复执行不重复扣减配额。为清理保留可排查记录。 * 并发上传的额度检查需要数据库原子预留与释放,不能通过“先查总量再写文件”绕过上限。文件被任务引用后的保留与删除规则在 I4 扩展。 * 更新生成器的 R2 名称、绑定占位与本地资源路径;不复制源项目资源 ID,生成后需要重新配置生产资源。 * 交付 `文件与存储` 中文教程:类型 / 大小策略、本地预览、私有访问、清理与故障处理。 **验收:** 浏览器走完上传 → 查看 → 下载 → 删除;并发上传不超额;清理失败后可恢复;320 / 390 像素和浅深主题可用;生成两个站运行同一流程且资源隔离。 ### I3:执行限制与 Turnstile **2026-10-11 补充:** 已修复当前 workerd 下 Siteverify 的重定向模式兼容性,实际文件入口验证缺失 / 超长 token、动作 / 域名、重放、503 / 非法结构、外站重定向拒绝和十秒超时,无效验证不写文件、配额或 R2。共享函数五种动作测试、关闭模块的原上传 / 私有读取 / 删除 / 清理已回归;真实 widget 和云端仍待,见[中文教程](./execution-security)。 **依赖:** I0;文件保护在 I1 / I2 已存在,本轮扩展到任务和未来付费接口。 * 定义各操作限制:上传、提交任务、查询、下载与外部 API,登录用户、访客采用各自策略。收费任务首版只接受登录用户。 * 使用可核对的原子计数或预留策略。起步按现有 PostgreSQL 条件更新 / 唯一约束实现和测负载;有真实性能证据后再迁到专用存储,不先叠加多种限流后端。 * Turnstile 前端交互与服务端验证一起交付,检查预期 action / hostname;缺凭据、失效 token、服务异常有明确结果。关闭时不加载脚本;登录认证已有的限制继续保留。 * 返回稳定错误码和重试提示,客户端避免连续自动提交;跨项目配置不共享计数。 **验收:** 并发绕过按钮仍受服务端限制;批量请求逐项受约束;无效验证不能触发任务 / 扣费;验证服务故障不会静默放行已要求验证的操作;本地测试与真实验证码验收分开记录。 ### I4:任务生命周期与可靠投递 **依赖:** I2、I3。 * 新增任务表和必要执行记录,保存所有者、处理器 ID / 版本、输入文件引用、参数、请求幂等键、状态、尝试次数、领取租约、结果引用、错误分类与时间。`userId + requestKey` 唯一。 * 使用明确状态:`pending`、`queued`、`running`、`retry_wait`、`reconciling`、`succeeded`、`failed`、`canceled`。把任务阶段与进度百分比分开,首版不伪造连续百分比。 * 创建数据库任务后发送队列;投递失败保留待发送记录,定时处理补投。处理“消息先消费、生产者还没更新投递状态”的竞争,不能把迟到的投递标记覆盖任务终态。 * 消费者原子领取租约并使用领取版本保护写回;重复消息、旧消费者和已完成任务不能再次发布结果。重启和租约过期后按规则恢复。 * 仅可重试错误使用有限重试;死信、超时和处理中失联均可核查。手动重试创建明确的新尝试,遵守次数与权限限制。 * 外部服务可能已成功但响应丢失时进入 `reconciling`。支持供应商查询 / 幂等键时据此对账,无法确认时暂停自动重试;不承诺所有外部操作都能严格只执行一次。 * 取消尚未执行任务立即终止;已发起的外部调用先记录取消请求,依据真实供应商能力停止或继续对账。取消不是立即退款的同义词。 * 文件引用有保留规则:输入被运行任务占用时拒绝删除或延后删除;输出文件只在成功完成后成为用户可下载结果。 * 使用确定性文本文件规范化处理器验收实际输入、队列、输出文件和下载;同步 JSON 工具继续保留同步执行。 Cloudflare Queues 默认至少投递一次,幂等与结果去重必须由应用完成。[官方投递保证](https://developers.cloudflare.com/queues/reference/delivery-guarantees/)。 **界面与接口:** `tasks.submit/list/get/cancel/retry`、任务列表 / 详情,真实状态轮询、断线恢复、输入与结果文件链接。 **验收:** 创建成功而消息发送失败可补投;重复与并发消费只留下一个有效结果;旧租约不能覆盖新结果;超时、重试耗尽和死信可恢复;刷新浏览器不丢任务;跨账号访问拒绝。 ### I5:任务收费与账本事务 **依赖:** I4。现有免费客户端工具保持免费。 * 明确积分语义:`balance` 表示可用积分,预扣即减少可用余额;每个收费任务对应固定报价快照与预扣记录,成功后标为结算,失败按规则一次性返还。 * 改造现有积分方法的事务边界:任务创建、余额锁定、预扣账本与待投递状态在同一个事务完成。外部队列和模型调用在事务提交后执行。 * 保留现有账户与账本唯一事件约束,增加必要任务关联 / 结算记录;不另建第二套积分账户。稳定事件键关联预扣与返还。 * 终态更新与返还一致提交;成功 / 失败 / 取消并发时只有一个最终结算决定。重试沿用原预扣,不每次重试再扣用户。 * 前端先展示服务端报价与余额,用户确认后提交。请求重复返回原任务;同一幂等键携带不同参数时报冲突,不覆盖原任务。 * 供应商结果仍不确定时保持预扣与对账状态,运营人员有明确处理路径,不能一面退款一面继续产生有效收费结果。 **验收:** 并发请求不会负余额;任务插入失败不扣积分;余额不足不创建可执行任务;重试不再次扣费;重复终态不重复退款;管理员尚未完成时保留可核对的只读记录与本地诊断路径。 ### I6:积分包与订阅完整流程 **依赖:** I5;需要 Stripe 沙箱与测试商品才能完成外部验收。 * 在产品价格配置增加类型明确的积分包,包含 SKU、整数最小货币单位金额、币种、积分数量与真实 Price ID。由同一配置生成购买卡片和服务端商品映射。 * 从服务端创建 Checkout,只信任服务端商品与会话用户;支付完成页查询购买状态,不依据 URL 成功标记发放积分。 * 新增购买记录与支付事件去重记录;验签、校验模式 / 商品 / 金额 / 币种 / 归属后,购买终态和积分发放在同一个事务完成。延迟支付只在实际成功后发放。 * 事件写入与业务处理可安全重试;不同事件指向同一支付对象时仍不能重复发放。退款与争议事件可关联原购买,不能按到达顺序覆盖新状态。 * 退款积分已被使用时,不强行将当前非负余额改成负数。先进入人工核查状态并限制进一步收费使用,显示原因,待明确业务规则后处理差额;部分退款有独立数量与账目规则。 * 保留 Better Auth 对已有订阅的状态维护,完善购买、试用结束、续费失败、取消、到期与门户回跳验收。积分包先属于个人;组织订阅不自动变成组织积分池或给个人重复发额度。 * 锁定当前已安装 Stripe / Better Auth 插件的接口与事件职责,避免一次事件同时由两个处理器维护同一订阅或发放同一权益。 Stripe 事件可能重复且不保证顺序,因此回调去重与状态核对是必要验收项。[Stripe Webhook 文档](https://docs.stripe.com/webhooks)。 **验收:** 错误签名拒绝;重复 / 乱序 / 处理失败再投递无重复积分;延迟成功与失败正确;退款和争议可核对;个人 / 组织账单权限正确。凭据未提供时记录“沙箱待验收”,不把本地签名测试当真实支付。 ### I7:一个真实 AI 处理器与成本记录 **依赖:** I4;收费接入依赖 I5 / I6。 * 先根据真实产品的输入与输出选择一个模型服务。SDK 和模型版本在接入时核对官方资料,不预先铺设多个供应商、模型路由器和全能 AI Playground。 * 请求只在服务端发起;模型密钥、预算和供应商回调配置不进入 core 或页面。公开的处理器目录只描述产品能力与输入约束。 * 每次执行记录供应商请求 ID、模型版本、用量和可核实成本;未知成本留空,不写成零。输入内容的保存目的、时长与结果清理有配置。 * 对结果做格式、尺寸或结构检查,必要时使用真实输入人工验收质量;质量失败按确定规则进入失败 / 退款路径,不能只检查 HTTP 200。 * 处理超时、限流、余额不足、拒绝生成、坏结果和供应商回调乱序;无法确认的调用沿用 `reconciling`。 * 如果当前没有可用凭据或产品尚未选定,文件与任务能力可发布本地版本,AI 保持待接入;演示处理器不会标成真实模型。 **验收:** 一组固定真实输入记录输出、耗时、失败率与费用来源;完成预览和下载;密钥不出现在客户端产物或日志;不确定结果不会自动重复造成成本。 ### I8:账号与团队操作完整性 **当前状态:进行中。** 第一阶段已补齐团队邀请、捕获邮件、重发 / 取消、接受 / 拒绝、过期与再次邀请。邀请和成员变更共用组织行锁,八连接竞争与工作区切换期间的原团队绑定已验收。第二阶段已落实套餐人数的实际写入限制,并完成角色编辑、移除及退出界面;8 个独立连接的邀请接受和可信插件新增成员竞争均只允许最后一个名额被使用一次。第三阶段已完成原子所有者转移的独立确认,八连接竞争、原生 Worker 第二步故障回滚和转移后退出已验收。第四阶段已完成会话列表、单个 / 批量撤销、当前设备保留与近期登录后的设置页回跳;真实 Worker 与浏览器已验收。第五阶段已接入通行密钥添加入口、列表、改名 / 删除确认,签名协议、八连接删除和最后登录方式回滚已本地验收;真实邮件、OAuth / 通行密钥设备环境尚待验收。具体证据见[持续迭代记录](./iteration-progress)。 **依赖:** I0;无需等待真实模型。 * 补齐组织邀请创建、邮件、接受 / 拒绝、取消、过期与再次邀请;处理现有 `invitation(organizationId, email)` 唯一约束对再次邀请的限制。 * 角色编辑、移除、退出与所有者转移分别授权;最后一名 owner 不能被移除或降级。已失去成员资格的旧会话不能继续访问组织数据。 * 补齐 Passkey 添加、命名、列表和撤销,以及会话列表与撤销。删除最后一种可用登录方式前,确保用户仍有可验证的恢复路径。 * 邀请、恢复与安全邮件复用现有发送入口,提供中文维护说明和英文用户文案;本地捕获与真实邮箱验收区分。 * OAuth / Passkey 在真实浏览器与第三方环境验收,不把登录按钮出现当流程成功。 **验收:** 过期、已使用、被取消和错误身份的邀请拒绝;角色变更立即影响接口权限;Owner 边界正确;撤销会话 / Passkey 后实际访问或登录失败;现有注册与恢复仍正常。 ### I9:站点管理员与操作审计 **当前进度:** 前五阶段角色、明确初始化、封禁 / 解封、用户搜索、任务 / 财务记录、带原因积分调整、任务恢复、主动支付对账与审计已本地验收;旧结账人工关联、按对象 / 执行者 / 原请求及完成记录检索已本地验收,默认保留历史;生产模式初始化 / 恢复命令已本地验收;真实远程环境及本站保留 / 归档安排待验。详见[管理员教程](./admin-audit)和[迭代记录](./iteration-progress)。I9 不标记完成。 **依赖:** I5、I8;支付排查界面依赖 I6。 * 站点角色与组织角色分开。按已安装 Better Auth 的能力确定管理员集成和数据映射;新增插件表或字段时更新 ID 映射及迁移。 * 首个管理员通过受控初始化步骤明确指定,不能把第一个注册用户或任意组织 owner 自动提升为站点管理员。 * 增加服务端管理员过程与 `/admin` 路由,提供用户搜索、封禁 / 解封、任务与支付状态、积分账本和失败原因查看;默认不展示用户原始输入。 * 积分调整必须有原因并通过同一账本服务执行;重试 / 对账操作复用既有任务与结算规则,管理员也不能绕过扣费或退款幂等。 * 新增操作审计记录,保存执行者、动作、对象、必要变更和时间;审计失败时涉及权益变更的操作不能部分完成。 * 封禁应影响现有会话与后续 API Key 调用,同时定义已经运行任务的处理方式。后台前端菜单隐藏只负责呈现。 **验收:** 普通用户、团队 admin 无法调用站点管理员 API;权限撤销及时生效;封禁用户不能继续新建收费任务;积分调整和恢复操作有可核对审计;列表分页不会泄漏全量私有内容。 ### I10:可选用户 API Key **2026-10-10 状态:** 创建 / 一次性展示 / 列表 / 改名 / 有效期 / 撤销与实际个人文件、任务 API 已本地验收,默认关闭。签发 UUID、封禁、双层限额、原账本和任务版本重试保留统一边界。0013 仅在独立数据库验收,主库未应用;真实云端 / 第三方调用待验,见[教程](./api-keys)。不将本地结果冒充完整 I0—I16 交付。 **依赖:** I3、I8,封禁与管理流程依赖 I9。 * 接入 Better Auth API Key 插件前核对当前安装版本、包、生成 schema、ID 和客户端接口;不与站主环境密钥混用。 * 增加创建、一次性展示、名称 / 前缀列表、过期和撤销界面;不自行保存可恢复明文密钥。作用域先覆盖真正公开的文件 / 任务接口,拒绝管理、支付和身份配置操作。 * 外部 API 明确版本、鉴权、请求幂等和错误码,不把全部 tRPC 管理接口直接暴露为可用能力。Key 解析出的用户仍需执行业务限制与所有权检查。 * 基于 Key 和账户限制请求与收费使用;撤销、过期、封禁及权限变化对后续请求生效。 * 默认关闭。没有对外 API 需求的产品仍只用浏览器会话,关闭后 API Key 路由和设置页不可用。 **验收:** 缺失、撤销、过期或错误作用域拒绝;普通 Key 不能调用管理员操作;调用任务的积分与会话请求一致;明文只展示一次且不进入日志。 ### I11:转化事件、候补名单与营销订阅 **2026-10-11 状态:进行中。** 第一阶段已完成公开匿名表单、独立用途同意、确认邮件、主动确认 / 退订、并发去重及定时失败恢复;真实本地 Worker / PostgreSQL 和浏览器已验收。第二阶段的联系人 / 主题同步、SDK 签名回调、全局阻止与后台失败恢复也已在实际本地 Worker / PostgreSQL 验收。公开工具的明确同意、浏览器尝试 / 成功 / 下载报告、并发去重、保留清理与故障隔离也已本地验收,见[工具使用事件](./tool-usage-events)。个人账号独立选择、实际任务开始 / 成功、附件下载与已验证积分包确认,以及确认页去重和定时恢复已本地验收,见[服务端事件](./server-activity-events)。名单、同步与使用报告默认关闭;0014—0025 的可选增量未应用主库。个人订阅账单的已签名回调、完整付款分配核查、续费 / 确认页去重与漏回调恢复已本地验收;团队独立 owner / admin 选择、成员只读、角色变化保护、共享账单去重与续费恢复也已在实际本地 Worker / PostgreSQL 和浏览器验收;真实 Stripe / Resend 环境和真实投递仍需推进,见[订阅教程](./marketing-subscriptions)。 **依赖:** 文件 / 任务事件依赖 I2 / I4,收入事件依赖 I6;订阅表单可独立开发。 * 定义真实发生的事件:开始、成功、下载发起、购买确认。浏览器触发下载不证明用户最终保存;页面访问不代表使用或收入。 * 服务端购买事件与前端确认页去重,默认不记录输入、文件内容、邮箱和完整带参 URL。统计启用与同意策略有明确配置,默认不在私有工作台加载营销脚本。 * 候补名单与营销订阅区分账号;新增订阅状态、确认 token、同意时间 / 来源及退订时间,确认与退订链接可过期或幂等重用。 * 默认采用已有 Resend 邮件能力;当前 API 契约在实现时核对,不照搬旧 Audience 字段。注册和营销同意分别记录,恢复邮件不受营销退订影响。 * 处理重复邮箱、确认失败、退订、提供商异常和退订状态同步;联系人同步故障不会阻塞账号或任务。 **验收:** 重复提交不会重复创建有效订阅;未确认状态不作为订阅成功;退订实际生效;匿名候补名单不强制登录;捕获邮件可检查,真实投递状态单独验收。 ### I12:任务通知与站主通知 **2026-10-11 状态:进行中。** 第一阶段个人任务邮件已本地验;第二阶段加入独立 Discord 站主渠道、任务 / 已发放积分包 / 核实个人与团队账单、漏回调恢复及管理员投递列表,实际本地 Worker / Queue / R2、受控付款和捕获故障已验,桌面及手机浅深主题已检查。0020 / 0021 尚未应用正常主库,两个渠道默认关闭。真实供应商、云端维护及既有外部门槛仍按完整范围推进,见[个人邮件](./task-notifications)与[站主通知](./operator-notifications)。 **依赖:** I4、I6、I11。 * 用户通知首版提供站内任务状态与可选完成 / 失败邮件;站主通知首版提供一种明确配置的运营 Webhook,二者的接收者与内容策略分开。 * 从已提交的任务 / 支付结果生成通知投递记录,稳定事件键去重,有限重试与失败列表;通知失败不撤回已经成功的结果或支付权益。 * 通知只发送必要事件与内部详情链接,不附原始文件、提示词或外部服务密钥。目标地址来自站主服务配置,不能由普通用户传任意 Webhook URL。 * 优先沿用已有 Queues 与邮件入口,不创建第二套通知任务引擎。第三方不支持幂等且发送结果不确定时保留可排查状态,不承诺绝对无重复邮件。 **验收:** 重复业务事件不创建重复通知记录;投递失败可重试和查看;没有配置时明确关闭;恶意接收地址不进入任意网络请求路径;业务成功不依赖通知成功。 ### I13:多语言与品牌素材 **依赖:** I8— I12 的主要用户文案稳定后。 * 默认继续英文单语言。以一个真实第二语言验证完整路径,维护教程仍中文;先记录译文负责人或译文来源,不生成无内容的语言目录。 * 建立共享语言配置与实际译文,Astro 公共页、React 工作台、邮件按各自入口消费;使用与当前框架适配的机制,不为采用 next-intl 迁移到 Next.js。 * 默认语言无前缀,其他语言采用固定前缀;工作台语言前缀同步处理边缘路由、登录回跳、API origin、静态资产及直接刷新。语言不改变租户和权限。 * canonical、hreflang、sitemap 与真实翻译内容对应,缺翻译采用明确回退或不发布策略;不发布空语言页。 * 品牌 Logo、favicon、OG 图片、社交链接和任务文案有清楚配置入口,保留哑光浅深主题、焦点状态和减少动画偏好。 **验收:** 英文单语言构建和多语言构建都通过;无错域名、死链接与重复 canonical;登录 / 任务 / 邮件语言可预测;两种视口和主题验收;生成器不残留原品牌素材。 ### I14:可选客服与联盟营销 **2026-10-11 状态:进行中。** 客服首版选用 Crisp,默认关闭;联系页主动加载、SDK 窗口打开事件确认、脚本失败 / 超时回退、迟到会话保护及新站重置已本地验收。实际浏览器使用受控的本地 SDK 协议,未访问真实供应商;工具页和工作台只提供联系入口,英 / 西双语共用页面。配置、Cookie / 会话边界及真实工作区待验项目见[客服教程](./support-chat)。Rewardful 签名回调、佣金 / 退款与结算复核、登录后的个人 / 团队推荐绑定后端已本地验收,默认关闭。0027—0033 只在一次性验收库应用,主预览保持 0012;真实 Worker 重启恢复和 PostgreSQL 权限锁已验。公开采集与个人 / 团队设置已受控本地验收;新购买首次结账冻结、共享客户创建与实际重启恢复已本地验收;具体订单 / 佣金联查、管理员证据、带原因 / 审计的安全人工复核及有界漏回调补查已受控本地验收,真实结算继续验证,不能把后端元数据绑定当作完整计佣,见[联盟教程](./affiliates)。 联盟后台首版选用 Rewardful,默认关闭:原始字节验签、事件去重、当前佣金 / 结算与 Stripe Charge / 退款核查、有限重试及实际 Worker 重启恢复已本地验收。0026 只在独立验收库应用,正常主库仍为 0012。结算的供应商 `paid` 保留为历史观察,退款或核查失败标记待复核,不宣称银行到账,见[中文教程](./affiliates)。公开采集、明确选择与 Stripe Customer `referral` 元数据绑定已受控本地验收。新积分包和个人 / 团队订阅已接原支付幂等、首次参数冻结与共享客户创建,保留 `client_reference_id` 的既有归属;具体订单 / 佣金联查及退款复核已接独立证据队列和只读管理页面;人工复核和有界漏回调补查已接原维护入口,真实供应商验收仍待做。 **依赖:** I6、I11、I13。 * 客服选一个真实供应商,提供启用配置、必要 ID、按需加载与联系页回退;关闭时不加载第三方请求,任务输入不自动进入聊天上下文。 * 联盟营销选一个真实供应商,明确推荐归因、支付元数据、过期、重复事件、退款撤销与结算核查。前端放脚本只能标记接入,完整计佣需真实流程验收。 * 默认关闭;切换供应商发生实际需求后再确定公共接口。没有账号或测试能力时保持“外部验收待做”,不放虚假的佣金面板。 **验收:** 开关与实际网络加载一致;缺配置报告字段;推荐支付、重复事件、退款有可追踪记录;提供商异常不阻塞工具交付。 ### I15:生产验收、运行成本与恢复 **2026-10-11 升级补充:** 已发布的 0012 原 Worker 在独立库完成实际账号 / 私有文件 / 收费任务,再应用完整增量历史;末尾故障整批回滚、重复迁移、新 Worker 使用原会话补投在途任务及切回旧 Worker 已验收。旧字段摘要、原迁移和业务数据保持;默认关闭可选模块与本地固定样本的证据不能代替云端升级、其他历史版本或产品自定义处理器,见[升级教程](./template-upgrades)。 **2026-10-11 状态:进行中。** 测试 / 正式环境的存储、队列、死信和十五分钟维护已分别声明;可执行资源清单、环境隔离检查及目标域名产物检查已接发布脚本 / 持续集成,生成站存储名与 Terraform 对齐。当前仅离线验证,不创建云资源、不迁移远程数据库、不上传 Worker;完整数据库归档、同一快照数据核对和独立空库恢复已在实际本地 PostgreSQL 验证;冻结文件对象检查点、独立空桶恢复和原私有结果下载已受控本地验证;本机归档的保留预览、人工清理与保留归档实际恢复已验;认证版本密钥、实际重启重新登录和基础密钥邮件用途已本地验收;数据库受限应用角色、SCRAM 认证、环境连接隔离、授权事务回滚、密码变更及新凭据恢复原任务已本地验收,见[角色教程](./database-roles);真实云端流程、用量、对象与完整恢复仍待验收,见[部署教程](./deployment)及[恢复教程](./backup-restore)。 **依赖:** 要发布的模块已完成本地验收;需要明确的测试 / 生产环境安排。 * 编写可执行的资源与绑定清单:Workers、Hyperdrive、R2、队列、定时入口、Neon、邮箱、Stripe、模型;资源创建与代码部署的职责保持清楚。 * 先做独立测试环境,核对域名、Cookie、CORS、私有文件、邮件链接和支付模式。使用真实服务验证队列并发、失败恢复及资源限制,本地模拟不能替代。 * 按选定计划核算存储、请求、数据库、队列和模型费用;记录日期、来源及实际用量,免费额度不作为成本永远为零的保证。 * 做数据库备份与恢复、对象保留 / 删除验证、任务中断恢复与支付对账。升级采用兼容数据变更,版本回退不依赖删除用户数据或重写已应用迁移。 * 密钥轮换与操作权限安排写入维护步骤,不在模板生成时分发真实密钥;生产账号、资源、付费行为与发布由明确环境请求执行。 **验收:** 在测试环境完成注册 → 上传 → 收费任务 → 预览 → 下载及失败返还;恢复后能核对文件、任务、账本与购买;记录负载与费用,保留具体限制。 ### I16:第二个独立产品与模板分发 **2026-10-11 分发准备:** 新增开发源码归档、来源提交与文件摘要清单、工作区直接依赖声明和中文安装教程,复用原新站生成器。实际打包 / 解压、逐文件核对、私有文件排除与再次生成已验收;这不是完整依赖许可审计或正式销售发布。真实两站、服务成本与生产验收仍保持原依赖,见[交付教程](./distribution)。 **2026-10-11 依赖补充:** 安装后可导出 Bun 实际依赖版本 / 许可标签,分别记录全部与生产范围,并关联锁文件摘要、工具和平台。机器路径等字段移除,已有报告保留;未知标签需读随包原文核查。此资料用于分发核对,不等同完整授权审计或构建产物清单。 **依赖:** 第一真实产品完成可交付与云端验收。 * 依据本项目新词研究规则选择真实任务,保存社区 / 查询 / 竞品证据;需求与订阅持续性分别验证,不把模板能力演示当市场需求。 * 第一个和第二个产品使用独立仓库、数据库、资源、域名与模板 revision。优先选择不同交付形态验证复用边界,例如同步本地任务与异步文件任务;这只是验证结构,不是具体产品推荐。 * 记录每站复用模块、自定义代码、迁移步骤、构建与运行成本。只有出现真实两处消费者的能力再回收到共享代码,保持产品业务可直接定位。 * 新站生成、从生成站再次生成、已有站升级各走一遍;检查密钥、缓存、官网案例和源站资源 ID 均未混入。 * 完善分发包、依赖与许可证说明、配置教程、升级说明、故障排查和真实案例。模板价格、授权方案、购买流程和支持范围另行确定,不复用演示站订阅充当模板销售。 **验收:** 两站独立运行;可指出共用与自定义代码;生成和升级证据完整;官网只展示真实上线站点;销售状态与实际交付一致。 ## 5. 代码结构与数据演进 建议模块靠近实际消费者,目录在实现时按职责增长: ```text packages/core/ 公开模块配置、处理器描述、跨端任务契约 packages/ui/ 通用展示组件、设计变量、可访问交互 apps/api/routers/ files、tasks、purchases、admin 等元数据过程 apps/api/lib/ storage、tasks、credits、payment 等服务 apps/api/worker.ts HTTP、队列、定时入口组合 apps/app/routes/ 文件、任务、购买、设置、管理界面 apps/email/ 事务、安全、订阅与任务邮件 db/schema/ 文件、任务、购买、通知与必要授权表 scripts/ 配置诊断、资源预览、独立产品生成 docs/agentbuff-stack/ 中文模块教程与验收记录 ``` 目录不代表强制创建对应包。服务没有第二处真实调用时可以留在业务文件;有真实跨入口事务或运行需求后再拆。资源绑定可放一个类型模块,但不得把所有业务访问包装成抽象平台。 数据采用本项目既有 snake\_case 映射、前缀 ID、带时区时间、schema 导出和数据库驱动无关类型。文件、任务与账本采用真实唯一约束保障幂等;授权、余额、终态与支付状态读取新鲜连接。 文件删除、账号删除和财务审计的保留目的不同。到 I6 / I9 新增购买与审计表时明确数据保留和删除策略;既有数据库约定若与必要账目保留冲突,先记录具体理由并同步规范,不能无声地级联删掉对账证据。 配置先增加当前轮真正实现的字段。公共限制是展示与共享契约,服务端仍强制执行;密钥与私有资源在 API 环境 / 绑定中。模块关闭时隐藏导航只是呈现,直接 API 访问也必须有明确拒绝行为。 ## 6. 提交、验证与发布标准 每轮可按三个可构建提交组织:数据与服务 → 实际界面和配置 → 验收与教程。测试随相关行为代码加入,不能推迟到最后提交;同一功能没有完成用户流程之前不更新官网为已交付。 | 修改类型 | 必做验证 | 证据 | | --- | --- | --- | | 数据与权限 | PGlite 跑实际迁移;约束、跨用户 / 组织、并发与事务 | 命令、结果、故障案例 | | Worker 与事件 | Workers 本地运行;绑定、队列、定时和重启恢复 | 本地资源与状态记录 | | 页面与交互 | 浏览器主流程、浅深主题、320 / 390 像素、键盘、直接 URL 刷新 | 截图、下载结果和实际状态 | | 生成器 | 两个干净新站;安装、配置、构建、隔离、重复生成 | 模板 revision 与验收记录 | | 支付 / 邮件 / AI | 本地边界测试加真实测试环境流程 | 模式、日期、请求 / 事件标识、结果与成本来源 | | 文档和配置 | 构建、死链接、字段文档与缺配置诊断 | 构建日志和诊断结果 | 按变更运行最小覆盖检查;涉及认证 / 权限 / 账本 / 支付时运行对应完整回归,里程碑运行全仓测试。常用命令: ```sh bun typecheck bun web:check bun website:check bun run test -- --run bun lint bun format:check bun preview:build bun docs:build ``` 每份迭代验收记录包含:代码 revision、计划状态、实现范围、数据与配置变化、测试命令、浏览器证据、已知限制、外部待验收、下一项关键验证。所有“待开发 / 本地可用 / 接口已接入 / 外部已验收”状态以对应证据更新。 ## 7. 开发任务单与当前待办 每轮采用下列任务单,便于接续开发与避免范围漂移: ```text 迭代编号 / 负责人 / 开始日期: 目标与用户可操作流程: 依赖与进入条件: 需要修改的文件 / 数据 / 配置: 按顺序执行的子任务: 异常、权限、幂等与恢复规则: 本地验收: 外部验收及所需环境: 生成站与升级影响: 完成证据 / revision / 已知限制: 下一轮进入条件: ``` I0— I2 的独立生成站和文件流程已完成本地验收。当前推进 I14:已知佣金 / 结算的带原因人工复核与请求 / 终态审计已接原维护入口,有界漏回调补查与进度 / 审计已受控本地验收,下一项补真实供应商环境验证。I15 云端运行与 I16 第二独立产品仍按原依赖推进;完整状态和证据见[迭代记录](./iteration-progress),不以本地协议验收替代真实外部交付。 --- --- url: /docs/agentbuff-stack/iteration-progress.md --- # 持续迭代记录 范围以[开发迭代计划](./development-plan)的 I0— I16 为准。只记录实际实现与验证,不把已配置的资源当成产品功能,不把本地测试当成云端验收。 ## 2026-10-09:I0 运行基础 **状态:进行中。** 本地资源与认证运行基础已实现;API 的清理事件与任务消费者分别随 I2、I4 接入实际业务,两个新生成站的完整文件闭环仍待验收。 这轮将本地 API 从 Bun HTTP 替换为 workerd:Wrangler 只做本地 dry-run 打包,Miniflare 启动与生产相同的 Worker bundle;开发监听与完整预览复用入口。保留 Hono 安全头、请求标识、错误处理、新鲜数据库读写及认证初始化,资源类型集中在 `lib/bindings.ts`。 本地邮件通过专用绑定捕获,捕获失败不外发,服务日志不打印验证码。数据库只允许 loopback PostgreSQL,本地连接由 `DATABASE_URL` 统一派生。R2 与队列状态使用项目独立的 `.local/workerd/`;生成器会改写 bucket 和 queue 名,排除本地数据。已发布迁移保留,贡献说明改为增量历史。 | 证据 | 验证口径 | | --- | --- | | `api/local/runtime.test.ts` | 实际 workerd 读写 R2、发送后触发测试消费者、手动触发测试定时事件;重启恢复 R2;不同持久化目录隔离;无资源绑定仍可启动 | | `api/lib/email.test.ts` | 捕获内容、失败不回退、云端拒绝本地绑定 | | `scripts/lib/create-site.test.ts` | 连续两次生成会改写 Worker、bucket、queue;排除凭据与本地数据 | | 真实 API + 本地 PostgreSQL | OTP 捕获 → 登录 → 读取会话 → 退出 → 会话清除,均返回成功;未调用真实 Resend | | 开发自动重载 | 修改 API 源文件后重新打包并重载 workerd,健康接口仍返回 200;正式预览六个服务恢复正常 | 全量 Vitest 为 21 个文件、138 项测试通过;TypeScript、Oxlint、Oxfmt 和文档构建通过。开发自动重载另做实际运行验证。新增运行基础没有变更数据库结构或执行云端操作。 运行验证使用测试入口的消费者与定时处理器,生产代码未增加公开探针。尚未实现任务执行或文件清理,不宣称这些业务已经完成。Cloudflare 本地队列并发存在限制,线上并发、配额与故障仍待云端验收。 后续由 I1 交付私有文件元数据与 API,I2 交付工作台、配额、清理及两个生成站验收。I3— I16 保持原范围;支付、模型、云端部署和两个真实产品的验收证据仍需后续补齐。 实际投入约 20 分钟。运行适配期间发现当前 Miniflare 5 alpha 与旧版持久化选项不同,验收曾捕获重启丢失;已改用当前版本的持久化字段并实测恢复。依赖固定版本,避免未经验证的升级改变运行行为。 ## 2026-10-09:I1 私有文件 API **状态:I1 本地 API 验收通过;完整文件模块仍待 I2。** 本次数据与服务提交接续 `03d31a4c` 的运行基础。没有工作台界面,不更新官网为已交付文件能力。 新增 Hono 原始二进制上传与会话授权下载,tRPC 列表、详情及删除。四种允许类型为 PNG、JPEG、UTF-8 文本和 JSON;字节检查、文件名处理、5 MiB 默认大小、SHA-256、私有下载头和个人所有权均由服务端执行。组织切换不改变所有权,匿名会话被拒绝。 新增迁移 `0002_private_files.sql` 与三个数据表,保留既有历史。上传在同一事务中原子预留数量、字节及文件记录,然后写 R2;失败补偿确认删除后才释放配额。补偿或删除失败保留占用与故障记录。重复释放使用条件更新防止重复扣减。操作频率按数据库原子窗口限制,包含失败请求。 | 证据 | 验证口径 | | --- | --- | | `bun run test -- --run` | 23 个文件、155 项测试通过;包含实际迁移、并发数量 / 字节配额、失败补偿、删除重试、缺失对象、分页和限流 | | `bun files:validate` | 实际 Worker bundle + 临时 PostgreSQL + 独立 R2;两个账号,四种格式,原始下载字节、摘要、响应头、越权拒绝、过期拒绝及删除通过;测试资源成功销毁 | | `bun typecheck` / `bun lint` / `bun format:check` | 通过 | | `bun db:check` / `bun api:build` / `bun docs:build` | 通过;Workers bundle 另由真实验收命令打包并运行 | | `bun config:check` | 通过,明确报告仍需实际绑定与迁移,不把离线检查当资源验收 | 真实验收仅迁移自动创建的临时本地数据库,未迁移正常预览或共享数据库;未调用真实支付、OAuth、邮件或云端部署。当前主预览仍使用先前构建,文件 API 的新代码不等于该预览已经更新。 中文[私有文件教程](./files-storage)与配置、环境、模块状态已同步。PNG / JPEG 做必要结构检查,未声称完整解码或内容扫描。自动过期物理清理、超时上传恢复与孤立对象处理留在 I2;当前过期文件不能下载,但未自动删除。 下一轮继续 I2:接入定时清理与故障记录、文件工作台、导航和边缘路由、生成器资源重置,再验收两个干净生成站及 320 / 390 像素浅深主题。I0 的实际任务消费者待 I4,I3— I16 范围继续保留。 ## 2026-10-09:I2 清理与生成资源基础 **状态:I2 进行中。** 数据与服务部分通过本地验收;文件工作台、导航 / 路由和两个干净生成站的完整浏览器流程仍待做。本次接续 I1 的 `48c75bcf`,保持可构建提交。 Worker 实际入口组合 `fetch` 与 `scheduled`,定时处理直接调用文件业务服务,不伪造登录请求。新增迁移 `0003_file_cleanup.sql`,保存扫描游标、两分钟租约与 30 天批次记录。每次至多处理 20 条文件和扫描 100 个对象,失败记录保留占用并可重试;孤立扫描按本站前缀与一小时宽限处理迟到写入 / 账号删除的遗留。 旧执行器不能覆盖新租约的游标,已清理的超时上传不能迟到标记成功。扫描持久化翻页,不只反复检查第一批对象;删除失败保留该扫描页,配额释放复用同一条件更新。批次错误有稳定代码、计数和日志 ID;尚未提供管理员界面。 工厂改用 JSONC 结构编辑重置所有当前支持的环境资源,保留未删除字段的配置注释。R2 / 预览桶、队列 / 死信队列按绑定生成新名字,Hyperdrive 与已设置的 Cloudflare 账号 ID 替换为占位,远程本地绑定被关闭。已配置的未知资源类型会在创建目标目录前明确拒绝。移除生成的旧 Hyperdrive 连接覆盖变量,统一来源为 loopback `DATABASE_URL`。 | 证据 | 验证口径 | | --- | --- | | `bun run test -- --run` | 25 个文件、164 项测试通过 | | `cleanup.test.ts` | 实际迁移与 R2:20 条批次、失败恢复、迟到上传、跨页扫描、宽限、租约接管、账号删除遗留;时间宽限只在测试返回值中调整 | | `bun files:validate` | 真正 Worker 定时入口两次调用,文件实际删除、配额零占用、两条已完成批次记录;没有重复释放;临时资源已销毁 | | `site-resources.test.ts` / `create-site.test.ts` | 非品牌前缀的资源和真实来源 ID 被重置;生产 / 开发 / staging 分开;两次连续生成的队列消费引用与品牌对应;无源站凭据 / 状态 | | TypeScript、Oxlint、Oxfmt、迁移检查、文档构建 | 均通过;Worker bundle 已由真实本地验收构建运行 | 普通预览六项服务仍运行,但 API 是先前 bundle,未迁移其数据库。当前本地运行器不会自动按 Cron 时间触发清理,真实验收显式调用定时入口;线上 Cron 和资源需 I15 独立验收,不把配置中的频率当成已部署调度。 下一项关键验证是文件工作台上传 → 查看 → 下载 → 删除,再核实直接路由、登录回跳、320 / 390 像素与浅深主题。之后生成两个干净站完成安装、构建、文件与资源隔离验收。I0 的任务消费者仍由 I4 完成,I3— I16 的外部与产品验收范围保持不变。 ## 2026-10-10:I2 文件工作台与本地网关 **状态:I2 进行中。** 本轮接续 `11fd161a`,完成文件工作台、导航、边缘路由与搜索索引排除。主预览已更新,增量迁移仅应用于本任务创建的 loopback `127.0.0.1:54329/launchkit` 本地数据库,未迁移远程或共享环境。 界面复用语义设计变量、既有按钮和 Radix 对话框;传输、文件条目、预览和工作台控制分开,未新增通用插件框架。上传发送进度与存储成功确认分开显示,网络结果不确定时提醒先刷新,避免自动重传。查询按账号分隔,文本预览限制 64 KiB,原件下载保留完整字节;删除失败保留确认框与错误。 浏览器验收发现本地网关的 Bun fetch 默认解压响应,但转发仍保留 gzip 头,导致登录请求双重解码失败。改为转发原始压缩体,回归测试启动实际 Bun 上游与网关,验证压缩字节、浏览器式解码和 Cookie 转发。 | 证据 | 验证口径 | | --- | --- | | 主预览浏览器 `/files` | 文本、JSON 与 PNG 实际上传、预览;文本 / JSON 原件下载 SHA-256 与输入完全一致;PNG 实际解码尺寸正确;切换团队保留个人文件 | | 异常与交互 | 伪 PNG 被服务端拒绝且不占用配额;超 5 MiB 被选择校验阻止;长 JSON 明示 64 KiB 预览限制;删除确认与 Escape 取消 | | 浅深主题 | 实际桌面页面检查;手机尺寸接口未改变渲染宽度,320 / 390 像素仍待验收 | | `files.test.ts` | 四项:100% 传输不等于存储成功、401 认证错误、超时不自动重传、非 ready 响应拒绝 | | `local-gateway.test.ts` | 实际 gzip 上游与网关响应、原始字节 / 解码 / Cookie | | 全量 Vitest | 27 个文件、169 项测试通过;TypeScript、Oxlint、Oxfmt、前端及文档构建通过 | 普通预览的本地邮件捕获、Workers 与文件数据库现已使用当前代码。生产部署、远程迁移、真实邮件与支付未执行。实际永久删除、越权与清理由既有真实 Worker 验收命令覆盖,本轮浏览器仅验证删除确认和取消,不宣称确认按钮后的浏览器流程已通过。 下一项继续生成两个干净新站,验证独立安装、构建、完整文件操作与数据库 / R2 隔离,并补实际手机尺寸与到期状态。完成 I2 的验收门槛后再推进 I3;I0 的业务队列消费者仍归 I4,I3— I16 保持原计划范围。 ## 2026-10-10:I2 两个干净生成站与删除恢复 **状态:I2 进行中。** 文件工作台提交 `7a2f00b1` 已推送;两个生成站均记录该完整 revision 和 `sourceHadUncommittedChanges: false`,未沿用旧复制站作为新证据。Alpha 入口为 6710,Beta 为 7710;两站分别安装锁定依赖、离线检查、迁移新建临时数据库、构建和启动。没有复制源站凭据、依赖目录或本地 R2。 浏览器分别验证无会话直接 `/files` 回跳登录,登录后恢复原路径,空配额开始,实际上传、预览和下载。Alpha 使用 PNG,实际解码为 240 × 160;Beta 使用 UTF-8 文本;两份浏览器下载 SHA-256 与输入一致。Alpha 上传后 Beta 仍为零配额,主预览始终保留自己的三份验收文件。 两个新站各使用两个测试账号:第二账号无列表 / 配额,不能读取或删除第一账号文件;交叉站点文件 ID 在两个方向均返回 404。数据库对象 key 具有各自站点前缀,资源名字与持久化目录分别属于生成站。Beta 由真实 API 删除测试文件,配额归零,重复请求被拒绝;浏览器刷新后列表为空。Alpha 仅调整它自己的测试文件有效期,界面显示到期并禁用查看 / 下载,真实读取返回 410。 停止各站所属预览后,以各站的同一持久化目录启动真正 Worker bundle,分别两次调用实际 scheduled 入口。Alpha 的 R2 对象从 1 变为 0,文件删除与配额归零,总删除计数只有 1;Beta 的 R2 在清理前后均为 0。两站各产生两条无故障的已完成清理批次,再恢复预览。该流程同时验证重启保留与跨项目 R2 隔离;本地调度仍为显式触发,不宣称自动 Cron。 新增工作台组件回归测试覆盖取消不调用删除、等待期间禁止再次提交 / 取消、删除失败保留文件与占用、错误提示、用户主动重试成功后才清空列表和释放界面配额。接口由测试夹具控制,React / TanStack Query / Radix 使用实际组件;这是组件验证,不标成完整浏览器删除流程。 仍待补齐:320 / 390 像素真实渲染、永久删除确认按钮的完整浏览器流程。手机视口工具没有生效,实际仍为 1280 像素,相关截图仅作为桌面证据。I3 可按其 I0 依赖继续准备执行保护,I2 保留待验收状态;云端服务与 I4— I16 的范围均未缩减。 本轮最终检查为 28 个文件、170 项测试通过,TypeScript、Oxlint、Oxfmt 与中文文档构建通过。期间的返工来自网关压缩响应缺陷、视口接口未生效,以及组件测试需识别 Radix 弹窗期间隐藏底层页面的语义。核心验收日志覆盖本地时间约 00:08—00:28;源码改造另见上一节。 为最后的浏览器永久删除验收,已在 Alpha / Beta 各重新上传一份 79 字节、自建的 `agentbuff-file-acceptance.txt` 并停在确认框;源码文件保留于主仓库 `.local/evidence/files/`。该确认项已向用户单独请求,原因是浏览器工具对永久删除要求当次确认。未获答复前不点击最终按钮;其余检查继续。临时站和测试数据库保留供该项验收,主预览数据没有被删除。 ## 2026-10-10:I3 文件执行保护与可选 Turnstile **状态:I3 进行中。** 本轮接续 `d2994cd0`,文件入口已有真实调用,先在这里交付可选验证;任务 / 收费和用户 API 密钥入口尚未实现,不把它们标成已受保护。I2 的手机尺寸与最终浏览器删除仍保留待验收,删除确认未获答复。 操作限制脱离文件错误类型,使用共享 `ExecutionError` 与稳定错误码,HTTP / tRPC 分别保留 429 和重试提示。新增默认关闭的 `security.turnstile.enabled` 与成对的 API 环境字段;开关开启但无完整配置拒绝初始化,已知官方测试键在非 development 环境被拒绝。公开配置仅包含 site key 和动作,不包含 secret。 上传入口先检查当前会话、本站来源、操作次数,再核对 Siteverify 成功、动作和域名,之后才读取文件、预留配额与写 R2。网络 / 服务故障、非法响应和范围不匹配均拒绝操作;服务调用超时为 10 秒,不自动重试,不记录 token、密钥、provider 响应或输入。 前端仅在 API 明确要求验证时加载 SDK,采用显式 widget、英文提示、浅深主题和紧凑尺寸。过期 / 失败 / 卸载清空 token,迟到回调失效;脚本失败可主动重试。每次上传结束重建验证,原始文件仍为 XHR 请求体,token 单独放在请求头。任务执行保护将在 I4 接入,资金预留 / 退款由 I5 实现。 | 证据 | 验证口径 | | --- | --- | | 全量 Vitest | 30 个文件、187 项测试通过 | | Siteverify 边界测试 | 成功、缺配置、超长 / 缺失 token、动作 / 域名不匹配、重复 / 过期、服务异常 / 非法结构、网络失败与固定 endpoint;provider 响应受控 | | 真实 Hono / Better Auth / 迁移 PGlite / 本地 R2 | 缺 token、服务故障、错误域名不会创建文件或占配额;有效响应只存一份;公开列表不泄露 secret | | 实际 tRPC 批量请求 | 三项读取、窗口上限二:两项成功,一项拒绝并带稳定错误 / 重试提示;数据库计数为二 | | 实际 React / SDK 回调夹具 | 加载失败重试、过期清空、widget 卸载 / 重新验证和旧回调失效;未执行第三方脚本 / 真实验证码 | | 上传与关闭状态 | token 仅传头部、文件字节不变;验证关闭时不插入 SDK 脚本 | 上述验证不等于真实 Cloudflare widget、生产调度、线上负载或成本验收。实际凭据、允许域名与真实验证需要后续环境接入;保持默认关闭,不替用户创建 widget 或部署资源。中文[执行限制与验证](./execution-security)写明策略、开启方式和缺口。当前实现没有迁移数据结构。 实际 `bun files:validate` 已对更新后的 Worker bundle 完成完整文件与定时清理回归,临时数据库 / R2 成功销毁。主预览六项服务已重建并恢复;浏览器确认三份原验收文件仍在,验证关闭时 SDK 脚本数为零。TypeScript、Oxlint、Oxfmt、离线配置检查、产品构建与中文文档构建通过。返工包括 SDK 加载失败后 Promise 需清除,及测试环境需阻止第三方脚本执行;未放宽服务端校验来让夹具通过。下一步继续 I4 的可靠任务生命周期,同时保留 I2 与 I3 的实际验收缺口。 ## 2026-10-10:I4 任务记录、租约与输入保留 **状态:I4 进行中,第一批数据与服务。** 本轮接续 `ad638206`;尚未开放任务 API、界面、实际业务消费者、成功结果发布或死信处理。没有新模块开关,没有更新模板版本 / 官网为已交付,也没有执行模型、收费、云端部署。 迁移 `0004_task_lifecycle.sql` 增加任务、执行记录与输入引用。请求所有者与 UUID 请求键唯一,同键同参数返回原任务,换参数报冲突;账号行锁保护五个活跃名额,使用 `FOR NO KEY UPDATE` 避免阻碍同账号的外键插入。创建与输入保留同一事务,历史任务保存输入快照,原文件删除后不会丢掉历史。 投递、执行与用户请求按实际职责分开。30 秒投递租约、失败补投、两分钟未领取重投均以数据库状态驱动;发送方迟到确认不能覆盖已领取或终态。消费者领取原子增加次数 / 版本并插入唯一尝试;旧版本和过期租约无法失败写回。已分类的存储失败有限等待重试,单任务三次总预算;手动重试不重置次数,重新核对名额、输入摘要 / 有效期。 任务提交、用户删除与过期清理共享输入文件行锁,检查锁后的引用。活跃输入不能删除,清理跳过它;等待阶段取消立即释放,运行阶段先记录取消请求,执行器结算或租约恢复后释放。未知处理器失联进入 `reconciling` 且停止自动投递,不把它当成已实现供应商对账。 | 证据 | 实际验证口径 | | --- | --- | | 全量 Vitest | 31 个文件、203 项测试通过;任务新增 16 项,实际迁移 PGlite + R2;包含幂等冲突、个人隔离、活跃上限、并发投递 / 领取、保留、重试、旧版本、取消和未知处理器 | | `bun tasks:validate-store` | 临时实际 PostgreSQL,确认八个独立服务端会话;并发相同请求只留一个任务、名额只准五个、领取只产生一次执行;20 轮提交 / 删除竞争全部由删除先赢且提交拒绝 | | 强制锁竞争 | 实际 `pg_stat_activity` 确认提交和删除均等待文件行锁;安排提交先领取后,删除检查已提交的引用并拒绝。不是靠定时猜测并发 | | 旧版本与迟到发送端 | 实际 PostgreSQL 过期 / 接管后旧版本失败写回被拒绝;消费先到失败终态,发送端随后返回仍保留终态 | | `bun files:validate` | 真正 Worker bundle + 临时 PostgreSQL + R2;HTTP 删除活跃引用文件返回 409 / `FILE_IN_USE`,内部取消后删除 200;文件、原字节与定时清理回归通过 | | 运行限制 | 新增保护检查触发真实第十次窗口上限,下一次返回 429 / `RATE_LIMITED` / 重试 60 秒;仅推进临时用户计数时间继续验收,未提高限制 | 任务发送方法在本批验收中由受控夹具提供,不是实际 Cloudflare Queue;任务服务运行在测试 / 本地 Bun,实际 workerd 只验证已经接入的文件入口。输入竞争脚本使用实际 R2,但它的测试 Worker 不包含业务任务消费者。两类临时数据库 / R2 均成功清理,正常预览与生成站数据库没有执行本轮迁移;新代码启用前需迁移。 TypeScript、Oxlint、Oxfmt、迁移检查与中文教程构建通过。返工包括手动重试不应把自己重复计入活跃名额、发送失败后的旧 token 需在主动重试时失效,以及新增 HTTP 检查超过已有写入窗口需要明确验收 429。没有以绕过限流或放宽数据约束使夹具通过。 下一项是接入真正 Worker 消费者与维护入口:确定性文本输入 → 执行 → 私有结果对象 → 同事务发布文件 / 成功状态,补重复消费和过期执行器结果丢弃、失败补偿、死信与重启恢复。之后接 `tasks.submit/list/get/cancel/retry`、执行保护与可操作工作台,并补浏览器 / 生成站验收。I2 手机尺寸 / 最终浏览器删除和 I3 真实验证码待验收项继续保留,I5— I16 原范围不变。 ## 2026-10-10:I4 实际队列消费者与私有结果 **状态:I4 进行中,第二批执行与结果。** 本轮接续 `1f99e84e`。生产 Worker 入口接入业务 Queue 消费与定时任务维护,确定性 UTF-8 文本规范化能够产生实际私有结果;用户任务接口、工作台、死信消费者 / 人工恢复仍待实现。官网与模板版本没有标记异步任务为已交付;本轮没有部署云端、远程迁移、调用真实模型或收费。 结果复用文件模块的配额预留与下载协议,没有另一套存储账本。当前执行版本先在事务中预留未发布文件并记录尝试引用,R2 写入后,在同一事务将文件置为可下载并将任务置为成功;重复 / 旧版本不能发布。取消会结算取消并清理自己的输出,失败补偿不删除已发布结果。允许空白文本产生零字节结果,但上传入口仍拒绝空文件。增量迁移 `0005_task_results.sql` 保留既有历史。 读取输入对象、读取字节与写入结果各有 15 秒等待上限;超时后迟到对象不能发布,但可能暂留为孤立对象,沿用一小时宽限清理。删除失败保留配额,后续定时维护重试。空间不足进入明确失败,用户腾出空间后可在原三次总预算内手动重试。未知基础设施故障交由消息重投,提交成功却丢失数据库响应时也不会误删已发布结果。 | 证据 | 实际验证口径 | | --- | --- | | 结果生命周期测试 | 13 项,实际迁移 PGlite + 本地 R2:并发 / 重复只发布一次、字节与摘要、零结果、私有列表与下载隔离、旧版本拒绝、运行中取消、失败补偿 / 配额、输入篡改、到期保留、空间不足重试、消息结算、提交响应丢失 | | 超时夹具 | 只缩短第三次存储操作的等待计时,不替换真实 R2;挂起写入进入等待重试,补偿后迟到对象仍不产生可下载结果。没有实等 15 秒或声称已实测云端超时 | | `bun tasks:validate` | 真正生产 Worker bundle + 临时 PostgreSQL + 实际本地 Queue / R2;定时 outbox 补投、消费者执行与私有 HTTP 下载通过,核对精确字节 / SHA-256 / 响应头 / 越权 404 | | 重复与取消消息 | 经实际 Queue 投递,同一任务只保留一次尝试,已取消任务没有尝试;同批后续新任务成功确认消费者运行 | | 失联与重启 | 定时维护恢复过期任务并清理旧尝试结果;旧版本无法发布。停止该验收 Worker 后复用持久目录重启,原 Cookie / 结果下载仍有效,新任务继续执行 | | 并发与文件回归 | `tasks:validate-store` 八个实际 PostgreSQL 会话、输入 / 删除竞争和迟到发送端通过;`files:validate` 当前 Worker 文件 / 私有权限 / 配额 / 429 / 定时维护通过 | | 全量检查 | 32 个测试文件、216 项测试;TypeScript、Oxlint、Oxfmt、迁移检查与中文文档构建通过 | 业务受理仍由验收脚本调用内部服务,不能称为公开任务接口验收;PGlite 也不能代替真实多连接锁竞争。上述三个脚本都成功销毁自己的临时数据库和资源,正常预览与两个生成站的数据库没有迁移。新代码启用前需要 `0004` 和 `0005`,本轮没有重启正常预览 API。GitHub 部署开关查询仍为空,不触发真实部署。 开发队列已配置基础设施重投上限与死信目标,尚未接死信消费与人工恢复,不宣称完成死信闭环。下一批接个人任务 API、稳定错误与分页,复用执行保护,再接可操作任务工作台;继续补死信恢复、浏览器和两个生成站任务闭环。I2 / I3 待验收项与 I5— I16 原计划范围继续保留。 ## 2026-10-10:I4 个人任务接口与实际工作台 **状态:I4 进行中,第三批接口与页面。** 本轮接续 `8f5c2a3c`,交付 `tasks.submit/list/get/cancel/retry`、个人任务列表 / 详情、真实状态轮询、断线恢复和输入 / 结果预览与下载。仍未完成死信消费 / 人工恢复、重试耗尽的实际队列验收及两个干净生成站的任务闭环;官网没有标记整个 I4 为已交付。 任务使用个人账号、新鲜数据库和逐次所有权检查,活动组织字段不扩大权限;变更请求要求本站来源。列表按时间 / ID 双键分页,每页二十条,相同时间不会漏页。统一返回稳定任务 / 文件 / 执行错误,内部版本、输入摘要、投递 token 与请求指纹不会出现在用户响应。 配置新增 `tasks.enabled/maxActive/maxAttempts/writesPerMinute/readsPerMinute`,离线检查核对范围与文件依赖。关闭新增任务仍保留历史 / 取消,缺 R2 / Queue 时新执行明确失败。任务次数在创建时保存,配置变更不重置旧任务;写入 / 查询使用已有数据库原子计数,真实 tRPC 批量过程逐项受限。可选 Turnstile 复用服务与组件,提交 / 重试分别匹配 `task_submit` / `task_retry`,token 不存入任务。取消不要求验证码;真实 widget 默认关闭且仍待验收。 页面沿用哑光黑白、StyleX 与公共变量。提交响应丢失时保留同一请求键,手动重试沿用;变更输入 / 参数才换键。成功后导航到详情,输出仅在成功状态展示;运行中取消记录请求,显示等待结算,不伪造已停止。活跃列表五秒、详情三秒检查;查询错误暂停轮询并禁止按旧状态操作,恢复联网 / 页面焦点重新核对。复用原文件预览与私有下载,没有新增一套前端文件处理器。 | 证据 | 实际验证口径 | | --- | --- | | 任务 API 测试 | 十项,实际迁移 PGlite / R2:匿名 / 来源拒绝、个人隔离、幂等与投递失败保留、公开字段、禁用 / 缺绑定、同时间二十三条分页、并发操作限制、验证码动作 | | 真正 HTTP 批量过程 | tRPC fetch adapter + 实际数据库,三项读取、上限二:两项成功,一项稳定 `RATE_LIMITED` / 重试六十秒;数据库计数二 | | 页面组件测试 | 三项:丢失响应沿用键 / 改参数换键、断线禁止旧状态操作 / 重连显示结果、运行中取消保持等待;接口为受控夹具,不冒充实际 Worker | | `bun tasks:validate` | 当前生产 Worker bundle + 临时 PostgreSQL + 实际本地 Queue / R2;真实登录 HTTP 提交 / 同键重试 / 查询 / 列表 / 取消 / 重试 / 越权和私有下载通过;定时补投 / 恢复、重复消息、重启保持结果通过 | | 主预览浏览器 | 实际选择原 79 字节文本,提交后 Queued → Completed、一次尝试;结果预览 / 浏览器下载成功,下载字节数与 SHA-256 匹配数据库;直接详情刷新保持任务 | | 真实手机宽度 | 浏览器实际 `innerWidth` 为 320 / 390;任务与文件布局检查无横向溢出;文件页 main 的 client / scroll 宽度分别 305 / 305、375 / 375;390 像素长文本对话框 client / scroll 为 356 / 356,64 KiB 提示保留 | | 全量检查 | 34 个测试文件、229 项测试;TypeScript、Oxlint、Oxfmt、迁移检查、离线配置、产品预览构建和中文教程构建通过 | 更新主预览时仅迁移已授权的任务自有 loopback PostgreSQL(`launchkit` / 54329),原三份文件在迁移前后数量相同;新任务另新增一份结果,现四份文件。六项主预览服务已恢复,两个旧生成站与所有远程数据库未迁移,真实模型 / 邮件 / 收费 / 部署未执行。自动验收脚本继续只创建 / 销毁独立临时数据库。 浏览器检查发现 StyleX 没有输出 `border` 简写,明确拆成宽度 / 样式 / 颜色,连同文件工作台同类边框修复;实际详情计算样式确认一像素边框。另修正查询错误类型推断、服务器异常响应同时遮蔽消息与调试堆栈,以及提交成功后的请求键不应提前轮换,避免导航异常被当作新提交而重复创建。没有以扩大配额、绕过限流 / 验证或修改已有迁移来通过验收。 主预览手机布局的旧缺口现有实际证据,两个生成站删除按钮的最终浏览器操作仍待验收;真实验证码手机体验亦未验证。下一批接死信查看 / 恢复与执行耗尽,再用两份干净新站验收任务复制能力,随后进入 I5 原子收费事务。完整 I0— I16 目标继续保留。 ## 2026-10-10:I4 第四批死信恢复与生成站验收 **I4 本地任务闭环已验收,下一阶段为 I5 原子预扣与结算。** 本轮补齐独立死信消费者、投递版本保护、持久收据、本人查看故障时间和受限恢复。任务状态仍为原八种,没有另建队列 / 文件 / 配额体系;保留已发布迁移,新增 `0006_task_dead_letters.sql`。 每次补投、结束旧租约和手动恢复推进投递版本,消费及死信处理均核对当前版本。旧投递不能领取新尝试,旧死信不能终止新任务。死信事务以队列 / 消息标识唯一保存记录,成功提交才确认;提交响应丢失重投仍只有一份记录。等待任务明确失败并停止补投;运行任务保留租约,允许真实成功,失败或失联后再停止自动恢复,未知处理器继续核查。本人重试沿用所有权、来源、频率、名额、原文件 / 到期 / 摘要、验证码及保存的次数限制,不清零预算。 | 验收 | 实际证据 | | --- | --- | | 原生死信 | 当前生产 Worker bundle、临时 PostgreSQL、实际 Queue / R2;数据库拒绝领取夹具触发首次投递及三次十秒重投,死信消费者保存一份 `applied` 收据;自动维护不再补投;移除故障后真实 HTTP 重试成功并下载 | | 处理预算耗尽 | 数据库仅在临时验收环境拒绝结果预留,让实际消费者领取后失联;显式推进租约时间、执行实际维护 / 队列三轮,三次尝试后失败、释放输入并拒绝重试(409);没有清零次数或改变实际队列配置 | | 新站复制 | 两份干净目录分别独立安装、迁移至 0006、离线配置检查及产品构建;8410 / 9410 使用不同数据库、认证命名空间、R2 和业务 / 死信队列,目标、消费者及路由变量匹配 | | 新站接口隔离 | 网关实际登录 / 上传 / 提交 / 同键重复 / 查询 / 私有下载;各一次结果;直接详情刷新返回应用;混合 Cookie 选择本站会话;外站任务查询 / 取消 / 结果下载为 404,只有外站 Cookie 为 401 | | 新站真实页面 | 两站登录、选择原文本、开始任务,Queued → Completed;本人预览 / 浏览器下载及直接刷新通过。A 下载 17 字节,B 下载 16 字节,内容分别为本站标记;与数据库字节数和 SHA-256 一致 | | 新站死信恢复页面 | A 的受控数据库领取故障产生原生死信,详情显示 Failed、故障时间、0 / 3 和 Retry task;移除故障后点击恢复,完成 1 / 3,保留历史故障时间及唯一收据;生产代码没有故障开关 | | 新站实际重启 | 停止 / 重启两站自身四项服务;A 的三个、B 的两个任务和所有结果可下载、摘要匹配、每项一次尝试;原认证会话与 A 的故障历史保持;浏览器详情再次刷新保持完成 | | 主预览回归 | 仅迁移已授权的任务自有 `launchkit` / 54329;前后均四份文件,一个原任务及结果保留;六项服务恢复,原会话、活动组织与任务详情仍可用 | | 全量检查 | 34 个测试文件、238 项测试;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置、产品预览与中文教程构建通过;实际任务 / 文件 / 八会话验收也通过 | 第一次复制配置检查发现新增死信变量未列入环境示例;修正模板后重新生成两份干净目录并重新安装 / 配置 / 构建,旧的本轮无用户数据夹具数据库和目录已清理。旧 6710 / 7710 文件站未改动。两份最新站生成时源仓库包含本轮未提交改动,`template.json` 保留 `sourceHadUncommittedChanges: true`,不能声称其来源为已发布的新版本。 本轮只新增结构化、稳定错误与历史时间;用户响应不含队列名称、消息编号、内部版本、原始输入或供应商诊断。死信消费者自身为十次有限重投,持续数据库故障时不能承诺平台无限保存消息;云端资源、告警与保留 / 容量策略归 I15。真实验证码、真实模型 / 邮件 / 收费及部署没有执行;两个旧生成站永久删除按钮仍待对应确认,不阻塞独立的 I5 事务工作。完整 I0— I16 目标继续保留。 ## 2026-10-10:I5 报价、事务预扣与结算 **I5 本地闭环已验收,下一阶段为 I6 积分包与支付事件。** 接续 `85525f63`,复用个人积分账户、账本、任务 / 文件与队列体系,新增 `0007_task_charges.sql`,不修改已发布迁移。文本队列任务仍默认 0 积分,浏览器工具继续免费。可配置正数价格只在独立本地夹具启用;没有真实 Stripe 收费或模型调用。 服务端报价返回整数积分、版本和本人可用余额;收费提交必须确认当前报价,价格或版本不符明确拒绝。任务插入、输入保留、欢迎初始化、预扣和待投递在一个事务提交。可用余额行锁与既有增减服务共用,外部队列在提交之后执行。每个任务保存固定价格,成功发布与结算同事务;取消、不可重试失败或预算耗尽与一次性返还同事务。重复请求 / 重放不能再扣或再退。 可手动重试失败且还有预算时保留预扣,原价重试;本人可以放弃并返还,输入失效后定时维护返还。自动等待重试继续保留输入 / 积分,已受理输入到期不直接终止正在执行的任务。外部结果不确定时继续核查且保留预扣,不因取消请求提前返还。已退款任务不可领取或重试。预扣记录异常或事务失败全部回滚,不显示虚假的完成 / 退款。 页面新增价格、余额、明确确认、失败后的返还操作和收费状态。丢失响应保存完整原请求 / 原报价,余额减少或当前报价变化仍可重放;收到明确报价 / 余额拒绝后重新查看并确认,不能静默换价。个人账本可直接打开关联任务。英文产品界面、中文教程和现有哑光主题保持。 | 验收 | 实际证据 | | --- | --- | | 收费生命周期 | 14 项,实际迁移 PGlite / 本地 R2:免费兼容、并发幂等、余额不足、错误报价、整事务回滚、结果结算、原价重试、放弃失败、预算耗尽、运行中取消、未知结果保留、失败输入失效维护、账本异常回滚、死信预扣与放弃返还 | | 报价 API 与页面 | API 核对匿名拒绝、个人余额、旧报价 / 余额不足无任务、一次预扣 / 返还;七项页面测试包含确认、余额不足、响应丢失原报价重放、明确改价后重新确认、断线与退款展示。页面接口是受控夹具 | | 八连接 PostgreSQL | `tasks:validate-charges` 确认八个独立服务端会话;八个不同请求余额只够一个时七个拒绝;同键只扣一次;并发成功 / 失败 / 取消只能发布或返还,不能两者兼得;其他积分消费者共用余额锁 | | 真正收费 Worker | 独立生成站配置每任务 3 积分;`tasks:validate-paid` 使用生产 Worker bundle、临时 PostgreSQL、实际 Queue / R2,HTTP 报价 / 旧报价拒绝、重复提交、成功结算、取消一次返还、重试不多扣、scheduled 输入失效返还及重启余额 / 下载通过 | | 真实浏览器 | 10410 独立收费夹具:选择原文本,未确认禁用按钮;确认后 Queued → Completed,3 积分 Settled,余额 20 → 17。实际下载字节与数据库 SHA-256 相符;另由内部夹具建立 `STORAGE_FULL` 失败,在页面放弃后显示 Returned,账本一笔 +3,余额回到 17 | | 手机报价界面 | 实际 390 / 320 像素:文档宽度分别 390 / 320;确认标签 client / scroll 宽度分别 293 / 293、223 / 223,无横向溢出;检查后恢复默认尺寸 | | 免费与文件回归 | 当前生产 Worker 的 `tasks:validate` 继续通过原生死信、预算耗尽、私有下载与重启;`files:validate` 上传 / 配额 / 引用保护 / 权限 / 清理回归通过,全部临时资源清理 | | 主预览升级 | 仅任务自有 loopback `launchkit` / 54329 应用 0007;前后四份文件、一个原任务;六项服务恢复,原会话 / 组织 / 结果保留。浏览器原任务显示免费,一次尝试,79 字节下载摘要保持 | | 全量检查 | 35 个文件、257 项测试;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置、主产品 / 官网预览与中文教程构建通过 | 收费复制站使用独立数据库、认证命名空间、R2 / Queue 和 10410 / 10414 / 10510 / 10610 端口,生成来源如实记录未提交修改;不能称其源自已发布版本。8410 / 9410 两个旧任务生成站仍运行 I4,没有自动迁移或声称已升级收费。临时自动验收数据库 / 资源已清理,供查看的独立收费站保留。 初次生成站 Worker 验收未先构建邮件包,明确失败后补构建;另一个到期夹具误用了自动等待重试状态,改为真实可手动重试失败 `STORAGE_FULL` 后才验证维护返还,未扩大退款条件或放宽业务规则。浏览器发现账本文案仍写付费处理未启用,修正并加关联任务入口;取消后的页面不再提示继续重试。 I6 继续实现积分包、验签事件、原子发放、重复 / 乱序 / 退款与争议流程;真实 Stripe 沙箱需对应测试商品与凭据,未提供前不得把本地事件测试当实际支付。真实验证码、模型 / 邮件、云部署及两个旧站永久删除按钮待验收项保留。完整 I0—I16 目标继续,不因 I5 完成缩减范围。 ## 2026-10-10:I6 第一批购买与验签 **状态:I6 进行中。** 已接入个人积分包目录、购买记录与详情、Checkout、签名事件和原子发放;默认售卖关闭。自动退款扣回、争议恢复、真实 PostgreSQL 支付并发与 Stripe 沙箱 / 订阅完整生命周期仍待验收,不能把本批描述为 I6 完成。 新增迁移 `0008_credit_purchases.sql`,保留 0000—0007。购买保存不可变商品 / 金额 / 版本、原请求键、模式和操作租约;事件记录不保存原始支付内容。每笔购买的稳定发放事件、购买终态、账本和余额同事务提交。使用当前已安装 Stripe SDK 22.6.2 与 Better Auth Stripe 1.7.4,积分 endpoint 和 secret 与订阅分开,订阅职责保持在 Better Auth。 服务端 Checkout 核对实际 Price,未知结果重试使用原购买与 provider 幂等键。签名回调读取当前 Checkout / PaymentIntent / Charge 后核对归属、商品、数量、金额、币种及模式;延迟未成功不发积分,不按旧事件载荷把已发放购买降级。退款、争议或信息不符进入保守核查,阻止新的积分包购买、收费任务和收费手动重试;免费任务、取消返还仍可用。**目前不自动扣回或解除核查,包括退款失败 / 争议关闭也仍保留核查。** 页面显示公开目录、本人分页历史和购买详情,回跳只查询服务端结果;网络故障不提供过时支付链接。购买与积分保持个人所有权,不随组织切换。新站复制清空积分包目录,不能带走源站 Stripe 商品;使用 Babel 解析配置并按语法节点替换,避免执行配置或用正则截断嵌套对象。首次尝试误用了 TypeScript 7 已移除的旧入口,生成测试明确失败后改用固定 Babel parser 8.0.7 并通过,未降级编译器。 | 证据 | 验证口径 | | --- | --- | | 支付服务测试 | 17 项:真实 SDK + 受控 HTTP 响应 + 实际 PGlite 迁移;报价变化、响应丢失、重复 / 乱序 / 延迟事件、事务回滚再投递、信息不符、退款与争议核查、归属 / 来源限制;未向 Stripe 发请求 | | 购买页面测试 | 3 项:响应丢失保留原请求、回跳等待服务端确认、核查时禁止新购买;接口是受控夹具 | | 原生验签 | `payments:validate`:生产 workerd bundle + 一次性 PostgreSQL,真实 SDK / SubtleCrypto 错误及过期签名、错误模式、超大原始请求拒绝;合法订阅发票被忽略,不调用 provider,不验收 Checkout 发放;资源已清理 | | 全量检查 | 37 个文件、278 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、配置诊断通过 | | 构建 | API bundle、主产品 / 官网预览、中文教程构建通过 | | 主预览升级 | 仅任务自有 loopback 54329 / launchkit 应用 0008;六项服务恢复,四份文件和一个任务保留;浏览器会话 / Preview Studio 保留,余额 20、购买关闭,原免费任务完成一次;实际重新下载 79 字节与数据库 SHA-256 相符 | `tasks:validate` 免费任务生产 Worker 回归已通过实际 Queue / R2、原生死信、预算耗尽、私有下载、维护与重启持久性,临时资源已清理。10410 收费夹具仍运行 I5,8410 / 9410 仍运行 I4,未声称它们已升级本批购买能力。未迁移云数据库、部署云资源或调用真实支付 / 邮件 / 模型。 下一批实现累计退款扣回、已消费差额、争议恢复与可审计解除,并以八个真实 PostgreSQL 连接验证购买 / 发放 / 核查并发。真实 Stripe 测试商品与凭据仍是外部验收门槛。完整 I0—I16 目标继续保持。 ## 2026-10-10:I6 第二批退款、争议与差额 **状态:I6 本地财务闭环继续完善,外部验收仍未完成。** 新增成功退款的累计按比例扣回、争议扣回 / 恢复、非负余额下的差额、可审计服务端核查和详情页记录。没有调用实际 Stripe API 或完成真实支付;订阅完整生命周期与真实沙箱继续保留。 迁移 `0009_credit_purchase_settlement.sql` 增加退款 / 争议金额、应扣回 / 已扣回 / 差额、调整版本和核查时间;支付事件保存该次财务快照、余额变化、原因及来源。保留 0000—0008。外部查询仍在事务外,购买租约限制并发;购买状态、调整账本、余额和事件同事务提交。账户余额锁串行处理本人不同购买,解除标记前检查全部购买,避免另一笔争议被并发解除。 当前规则使用成功退款累计额与尚未恢复争议额,合计最多按原购买金额计算,统一累计后向下取整。余额不足只扣可用额并记录差额;争议恢复只返实际扣过的积分,不将差额当成已消费的可返积分。待退款 / 开放争议继续暂停收费;失败退款、胜诉 / 询问关闭 / 被阻止等终态从当前 provider 状态核对。付款元信息不匹配保持人工核查,普通重查不能解除。分页完整性、最多 1,000 条记录、未知状态和不一致快照均有保守核查边界,见[教程](./billing-tasks)。 新增 `purchases.reconcile`,仅允许本人、同站来源和原有业务限流,客户端只能传购买 ID;关闭新售卖后仍可处理既有购买。`purchases.events` 只返回本人最近 20 条精简快照。详情展示差额、实际扣回和恢复记录;另一笔购买造成账户暂停时不提供本站 Checkout 链接或重试入口。历史已在外部打开的 Stripe 页面不会因此失效,仍需事件核对。 | 证据 | 验证口径 | | --- | --- | | 支付服务测试 | 28 项:真实 SDK + 受控 HTTP + 实际 PGlite 迁移;累计取整、先退款后确认、余额不足、待 / 失败退款、胜 / 败诉、退款与争议重叠、超过一页的历史、截断拒绝、回滚再投递、跨人 / 来源拒绝、关闭售卖后可信核查 | | 页面测试 | 5 项,接口为受控夹具;原请求保留、支付确认、账户暂停、差额展示及只恢复实际扣回额、另一笔核查时隐藏已有 Checkout | | 实际八连接 PostgreSQL | `payments:validate-settlement`:确认八个独立 backend pid;并发创建只建一笔 Checkout,八个不同签名事件发一次积分,退款与收费任务竞争保持资金恒等式,八笔同账户争议交错恢复不会清掉其他核查,实际事务回滚后再投递成功;SDK HTTP 是受控响应,不是实际支付 | | 生产 workerd 验签 | `payments:validate` 使用新 bundle / 迁移,错误 / 过期签名、大小及模式边界通过;合法订阅发票被忽略,无 provider 调用,不支持推断实际 Checkout 发放已验 | | 独立生成站浏览器 | 11410 / 11414 / 11510 / 11610、独立认证与数据库;真实 API 读界面,财务状态由真实业务函数 + SDK 受控响应建立。初始应扣 100、实扣 10、差额 90、余额 0;受控胜诉后差额 0,账本 +10、余额 10。没有实际支付或点击 provider 核查入口,后者由接口 / 页面受控测试验证 | | 响应式 | 390 浅色和 320 深色,文档宽度与 viewport 均分别 390 / 320,无横向溢出;随后恢复浅色与默认 1280×720 | | 全量检查 | 37 个文件、291 项测试通过;TypeScript、Oxlint、Oxfmt、迁移与离线配置通过,API / 主产品 / 官网 / 中文教程构建通过 | | 主预览升级 | 仅任务自有 loopback 54329 / launchkit 应用 0009;六项服务运行,原会话 / Preview Studio / 20 积分、四文件和一免费任务保留;既有 79 字节实际下载与数据库摘要仍一致 | 全量测试两次在文件清理多页案例超过原 5 秒时限;单项真实 R2 案例通过,保留 105 对象、跨页与隔离断言,仅给该超过 200 次 Worker 代理操作的案例 15 秒,再跑全量通过。没有改文件清理业务来绕过检查。初次更新独立夹具时复制命令工作目录错误,确认失败后使用源目录绝对路径复制并重建;最终浏览器显示新版本的确认文案。 支付对账临时数据库已销毁;供查看的独立 UI 夹具保留,来源如实记录未提交修改,并对最终两份 UI 文案作同步,不能声称来自已发布版本。10410 收费夹具与 8410 / 9410 旧任务站未升级;没有迁移云数据库、部署或发出真实付款 / 邮件 / 模型请求。 后续继续 I6 的订阅集成验证与故障行为;真实 Stripe 沙箱、生产绑定、管理员人工核查和永久财务保留仍按原计划与 I9 验收。完整 I0—I16 目标继续保持,未因本地财务闭环缩减。 ## 2026-10-10:I6 第三批订阅状态与更新故障 **状态:I6 继续进行,尚未完成真实沙箱或订阅乱序 / 并发完整验收。** 接续 `d1e0066a`,保留 Better Auth 的订阅状态写入职责;本批完善账单读模型、页面恢复入口、模式拒绝和更新 / 删除保存校验,没有另写订阅状态处理器或发放组织订阅积分。 当前有效方案与账单方案分开:active 必须有仍有效的确认周期,trialing 必须有仍有效的确认试用截止;周期、提前取消 / 结束的最早截止限制访问,缺失或过期不延长,当前没有宽限期。续费失败、未付、暂停、未确认首付保留原账单状态和可用门户,不显示新的 Checkout。仅没有订阅、取消或首付过期允许有管理权的人新购。个人 / 组织客户严格按活动 reference 判断,多个 active / trialing 不任意选一笔。成员上限读值不代表邀请入口自动执行,实施仍按 I8。 账单 UI 提取为独立组件,页面接收服务端可用动作,查询失败隐藏旧动作;刷新、返回窗口和每 30 秒检查状态,缓存增加 userId / organizationId,避免个人账单跨账号复用。产品文案英文,维护教程中文。 实际已安装 Better Auth Stripe 1.7.4 的 updated / deleted 处理器会捕获部分数据库错误并继续返回成功。SDK 客户端先真实验签,在插件写入前拒绝模式混用;`onEvent` 再通过新鲜数据库校验状态、方案、周期与取消时间,未保存就返回非 2xx 供重投,已配置价格对应的缺失记录也不静默确认。另发现该库测试环境默认跳过来源检查;明确 `disableOriginCheck: false` 使集成测试执行和生产一样的回跳 / CSRF 检查,没有降低生产保护。 | 证据 | 验证口径 | | --- | --- | | 订阅插件集成 | 六项:真实 Better Auth 路由、实际迁移 PGlite、真实 Stripe SDK + 受控 Fetch;Checkout 创建 / 回跳、试用 / 续费失败 / 恢复 / 取消、门户个人 / 组织权限与不可信回跳、错误模式、实际数据库触发器拒绝 / 修复、缺失管理记录与其他 Stripe 产品隔离;没有访问 Stripe | | 账单接口 / 缓存 / 页面 | 27 项接口测试含周期 / 试用到期、提前取消、无周期、多笔 active、逾期客户隔离;七项查询键测试包括不同用户;五项页面测试覆盖付款异常、确认刷新、取消新购、成员操作隐藏、查询 / 门户失败。页面接口为受控夹具 | | 原生 workerd | `payments:validate` 扩展:实际 PostgreSQL 和生产 Worker 内的 Better Auth 更新 trialing → active → past\_due → unpaid → paused → active、预约取消及删除;错误模式不写入。真实触发器拒绝导致非 2xx,修复后同一个事件 ID / 载荷成功;没有积分发放;临时资源已删除。原积分签名 / 模式 / 大小边界仍通过 | | 浏览器 | 11410 独立测试站沿用既有用户 / 数据库;虚构测试密钥只供本地签名,不是真实 Stripe 凭据。由真实本地 webhook 更新状态,浏览器只查询 / 刷新:逾期显示原 Pro / 付款异常和门户、不显示升级;试用显示截止;预约取消保留到期前访问;确认周期过期显示暂停。没有打开门户、完成支付或调用 provider | | 响应式 | 320 像素下文档宽度 320,关闭侧栏后正文 client / scroll 宽度均 305;随后恢复默认 1280×720 和浅色 | | 全量检查 | 39 个文件、311 项测试通过;TypeScript、Oxlint、Oxfmt 通过;API / 主产品 / 官网及中文教程构建通过 | | 主预览 | 六项服务更新,浏览器原会话 / Preview Studio 仍在,主站账单未启用;未应用新迁移。四文件和一份成功免费任务仍保留,既有 79 字节下载摘要与数据库相符 | 首次集成测试因邮件基础配置缺失与 Bun Fetch 类型额外要求而失败,补齐本地配置与准确函数签名后修复。门户拒绝的插件实际状态为 401,先核对权限中间件再修正断言;不可信回跳最初在测试环境被自动绕过,明确来源检查后才验证 403。编译发现数组下标推断与实际空结果不符,使用 `at(0)` 保留空结果类型;没有用断言伪装成始终存在订阅。全量检查随后通过。 **仍需推进:** 锁定插件对 updated 的载荷写入仍有旧事件覆盖新状态的风险;本批保存校验不是事件排序器,创建 / Checkout 吞错、回跳归属、未知 Checkout 结果恢复、插件直接 API 的重复新购门禁、并发、年付及真实沙箱仍按原 I6 验收。正常流程通过不能证明这些门槛已完成。无真实支付 / 邮件 / 模型调用、云部署或共享数据库迁移;旧 I4 / I5 生成站没有升级。完整 I0—I16 目标继续保持。 ## 2026-10-10:I6 第四批结账请求、并发门禁与回跳归属 **状态:I6 继续进行,订阅事件乱序和真实沙箱仍未完成。** 接续 `6f6772d9`,本批把设置页动作限制落实到服务端 HTTP 入口,保持 Better Auth 插件对订阅状态的所有权。新增 `subscription_operation` 保存当前结账请求和两分钟操作租约;插件单行创建 / 更新在短事务内校验令牌与当前组织角色,外部 HTTP 不进入事务,没有另建一套订阅状态。 同一主体只允许一个操作,新购先核对本地未结束订阅。Checkout 和客户创建使用当前请求的稳定 SDK 幂等键,固定一小时 Checkout 期限;请求指纹、实际 SDK 参数摘要和插件行 ID 先保存再发送。响应未知允许原请求重试,改方案 / 数量 / 元信息 / 回跳或价格配置拒绝。创建及恢复已有 Checkout 会分页核对当前 provider 全部订阅(最多 1,000);欠费不能藏在取消历史后。已知 expired 会话经当前 provider 确认后允许新请求、复用插件 incomplete 行;过期未知结果保持核查,自动恢复和管理员解除还未实现。 升级 / 门户等写操作要求同站来源和新鲜管理权限,个人客户不能指定组织 reference;结账回跳先核对登录、主体、provider 客户 / 会话 / 元信息与模式,插件再次读取时也检查归属。每人每分钟写操作 10 次、回跳 30 次。订阅回调保留原始字节并限制 256 KiB,忙返回 503;旧操作不能覆盖新令牌或释放新租约。未分配 scope 的内部插件写入被拒绝。新的状态查询优先未结束记录,多笔未解决记录进入错误;仅当前有管理权的人能读取原请求并显示“Resume original checkout”。 | 证据 | 验证口径 | | --- | --- | | 插件集成 | 24 项真实 Better Auth handler + 实际迁移 PGlite + 真实 SDK / 受控 Fetch;包含原请求复用、Checkout / 客户响应丢失后相同键及参数、改请求 / 改价格、六种未结束状态门禁、分页欠费、已知 / 未知到期、跨用户回跳、组织客户模式、大小与独立限流,保留之前生命周期 / 故障案例;没有访问 Stripe | | 账单接口 / 页面 | 29 项接口、七项查询键、六项页面测试;旧取消历史不遮待支付、跨用户原请求隔离,页面重试原方案 / 年付及回跳地址,接口为受控夹具 | | 八连接 PostgreSQL | `subscriptions:validate` 确认八个不同 backend pid;一笔 SDK 请求暂停时其余七个 upgrade 为 409,接受后响应丢失的第二次请求保留相同 SDK key / 参数,只有一个插件行;旧持有者更新和释放被拒绝、不同主体独立、组织撤权后事务拒绝。实际 Better Auth HTTP handler 在 Bun 执行,SDK HTTP 为受控响应,不等于 Worker Checkout 或真实付款 | | 生产 workerd | `payments:validate` 使用新 bundle / 0010,在实际临时 PostgreSQL 核对原订阅生命周期、模式、触发器故障后相同事件再投递,新增订阅 raw 大小 413;原积分验签边界保留。没有 provider API、Checkout / 门户调用或积分发放;临时资源删除 | | 独立浏览器 | 11410 / 11414 / 11510 / 11610 独立生成测试站升级本批文件、应用 0010;待支付行和原请求是明确插入的本地 UI 夹具,浏览器实际读取 API 显示继续原请求、隐藏新购,刷新后保持;没有点击恢复 / 门户或访问 provider。截图后删除新建夹具行并恢复旧订阅,由签名测试事件再次确认 past\_due;原账户及积分保留 | | 响应式 | 320 像素下文档宽度 320、main client / scroll 均 305,恢复入口可见;恢复默认 1280×720、浅色,保留桌面 / 手机截图 | | 全量检查 | 39 个文件、332 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置检查通过;API、主预览 / 官网及中文教程构建通过 | | 主预览 | 仅任务自有 loopback 54329 / launchkit 应用 0010,六项服务运行,原登录 / Preview Studio 保留,主账单仍关闭;四文件和一个成功免费任务保留,既有 79 字节下载与数据库摘要一致(没有称为本批重新下载) | 新增的 0010 在尚未应用时随字段完善重新生成,之前 0000—0009 没有修改。实现过程中类型检查发现适配器的泛型结果推断丢失,明确传递 SDK 适配器声明的泛型参数修复,没有通过 `any` 或不安全类型断言隐藏问题。主体管理权限拒绝现在由入口返回 403,区别于之前插件返回 401,断言与教程按最终行为更新。 **仍需推进:** 本批租约不解决旧事件快照覆盖;当前 updated / deleted 保存校验未覆盖创建 / Checkout 全部吞错,过期未知 Checkout 自动查找、永久审计和管理员流程仍待验;年付首购界面、真实沙箱与生产绑定继续保留。下一批先完善签名事件的当前 provider 状态核对及这些故障路径,再进入原计划后续模块。没有云部署、共享数据库迁移、真实付款 / 邮件 / 模型调用;旧 I4 / I5 站未升级。完整 I0—I16 目标继续保持。 ## 2026-10-10:I6 第五批当前订阅状态与失败确认 **状态:I6 继续进行,当前状态同步与创建 / Checkout 保存故障已本地验,外部门槛仍保留。** 接续 `cf724f07`,保持订阅表由 Better Auth 插件写入。没有新增迁移,保留 0000—0010。历史条目中的“乱序快照尚未处理”是当时状态;本批通过当前 SDK 资源核对处理这些旧通知,完整 I0—I16 范围保持。 真实原始请求验签并取得主体租约后,先读取当前 Stripe 订阅;Checkout 通知同时读取当前完成会话。当前对象的主体、客户、订阅 ID、模式、配置商品、周期与试用范围校验通过后,按已有行更新或已知唯一客户创建交给插件;不直接写一份平行订阅状态。旧 created / updated / deleted 与旧 Checkout 不再直接恢复旧快照。插件写入事务内检查实际保存的状态、方案、周期、席位、试用、取消 / 结束与日程,不一致回滚,提交后继续核对,内部吞错不能确认成功;当前读取失败 / 列表不完整或租约忙返回 503。锁定插件创建遗漏的取消 / 结束 / 日程字段,在同一次插件创建中补齐。实际触发器静默改变创建或更新状态时整次写入回滚,防止失败时暴露部分新权限,修复后同事件可以重投。 发现 Stripe SDK 将同一静态回调对象分配给不同客户端,原包装方式会累积验签闭包,导致请求串用模式 / 租约检查。现在先复制客户端回调对象再包装,真实 SDK 的测试 / 正式与普通客户端隔离测试通过。未修改 SDK 的签名算法。 | 证据 | 实际口径 | | --- | --- | | 订阅插件集成 | 40 项真实 Better Auth 路由 + 实际迁移 PGlite + 真实 SDK / 受控 Fetch;新增旧通知 / Checkout、读取失败重投、创建与 Checkout 触发器保存故障、年付当前状态、归属 / 模式不符、截断列表、首次取消字段 / 创建及更新静默异常回滚与客户端隔离;所有 SDK HTTP 受控 | | 八连接 PostgreSQL | `subscriptions:validate` 的八个独立连接;结账请求重试保持原键 / 参数、七个并发新购被拒绝;一条签名通知读取暂停时其余七条返回 503,随后均保存当前 active 而非原 past\_due;旧令牌与撤权保护继续通过,临时数据库删除 | | 生产 workerd | `payments:validate` 在新 bundle 和一次性 PostgreSQL 验证真实 SDK / SubtleCrypto、大载荷 / 模式 / 签名、正常生命周期、旧 created / deleted、当前读取失败 503 与实际触发器故障后相同事件成功;当前订阅 GET 经 Miniflare 出站拦截提供受控对象,其他外部目标拒绝;无原生 Checkout / 门户 HTTP 或积分发放,临时资源删除 | | 全量检查 | 39 个文件、348 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性与离线配置通过;API、主预览 / 官网和中文教程构建通过 | 主预览六个服务重启后均返回 200,新中文教程已实际读取。已有账户仍为 20 积分,文件数仍为四个;既有成功任务保持一次执行,其 79 字节浏览器下载文件与数据库结果 SHA-256 一致。本批没有重新下载文件,不将该比对称为新浏览器下载验收。 本轮没有浏览器样式或页面变更。保留的 11410 独立 UI 夹具仍在前一批版本,不将其截图算作本批新事件处理验收;主站计费仍关闭,不向实际 Stripe 发请求。年付当前资源同步已经验证,年付首次购买和真实 Stripe Sandbox 尚未验证。 内部插件事件类型用于当前状态创建 / 更新,不保证原通知的完成 / 删除钩子触发。尚未新增这些钩子对应的邮件或积分,持久副作用去重和通知发件箱按 I12 实施。读取后供应商的新变化仍需后续通知收敛,不能称为跨系统原子快照。 **下一步:** 完善 GET 回跳完整保存校验与未知过期 Checkout 查找,保留管理员处理 / 永久财务留存、真实沙箱、年付首购和生产绑定验收。后续继续原计划账号、运营与增长模块,不因本批状态同步通过将 I6 或完整目标标记完成。没有云资源变更、共享数据库迁移或真实付款 / 邮件 / 模型调用。 ## 2026-10-10:I6 第六批结账回跳的当前状态与补丁交付 **状态:I6 继续进行,GET 回跳完整保存校验已本地验,未知过期结账恢复与外部门槛保留。** 接续 `c998cdc2`,不增加数据库迁移,保留 0000—0010。复用原始通知与登录回跳共同的当前资源核对、主体租约和插件写入事务;完整 I0—I16 目标没有缩减。 回跳只接受本人 / 有管理权组织的已完成、已付款或无须付款的 Checkout,核对当前客户、模式与绑定订阅,再读取当前订阅。插件在本次请求中复用已核实的 SDK 响应,仍承担订阅表写入;事务内检查并回滚异常,提交后检查保存结果。读取失败 503,未完成会话 409,保存失败非 2xx;失败不会返回成功跳转,也不会提前将原请求记为已解决。重新访问同一回跳即可重试,不重新创建付款或发放个人积分。已取消 / 欠费的当前状态可以同步后返回设置页,跳转本身不证明付费有效。 核对安装包发现原 Better Auth Stripe 1.7.4 在本地 active / trialing 时提前跳过更新,且回跳更新遗漏 endedAt 和 stripeScheduleId。本批通过 Bun 版本补丁删除提前返回并补齐两字段;API 精确锁定版本,补丁声明和锁文件一同维护,保留第三方 MIT 许可证。生成器复制 patches 目录,二次生成也保留。没有复制整个插件源码或新增另一套状态写入器,回跳不伪造已签名支付事件。 | 证据 | 实际口径 | | --- | --- | | 订阅集成 / 生成器 | 49 项真实路由 / SDK / 迁移 PGlite 订阅测试,三项生成器测试;新增已 active 后取消及日程更新、单次 SDK 读取、回跳读取失败 / 保存异常 / 静默状态改动回滚与原请求重试、open / expired / unpaid 及错误会话 ID 拒绝、无需付款的年付试用状态;所有 SDK HTTP 受控,年付首次购买界面仍未验 | | 原生 Worker | `payments:validate` 用生产 bundle、一次性 PostgreSQL 与实际注册会话验证 GET 更新、原 active 到欠费 / 取消、读取失败 503、实际触发器保存失败无跳转和修复重试;客户创建、Checkout / 订阅读取由本地出站拦截返回,其他外部目标拒绝;原签名 / 模式 / 大小 / 乱序故障回归通过,临时资源删除 | | 八连接回归 | `subscriptions:validate` 保持七条并发升级拒绝、同键重试、七条并发通知 503 后原事件重投、旧令牌与撤权保护;实际 PostgreSQL + Bun 中 Better Auth handler,SDK HTTP 受控;不是原生 Checkout 创建 / 门户 HTTP 验收 | | 新站安装 | 实际生成新产品,在空目录离线安装 / frozen lockfile 成功,核对真实安装包的回跳补丁已应用;没有启动该站运行时或数据库,临时安装目录删除;源代码生成与二次生成另由行为测试核对补丁文件 | | 全量回归 | 39 个文件、357 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性和离线配置检查通过;API、主预览 / 官网、中文教程构建通过 | 主预览六个服务刷新后均返回 200,实际读取到新的中文回跳教程。原账户仍为 20 积分,四个文件与既有成功任务保留;此前 79 字节下载文件与结果记录 SHA-256 一致。本批只比对既有文件,不宣称完成新浏览器下载。 本轮未改页面样式,不复用旧 UI 截图作为新回跳证据。主预览保持计费关闭;11410 独立 UI 夹具仍在旧版,未加载新 SDK 读取或补丁。没有实际 Stripe、Resend、模型调用、云部署或共享数据库迁移。 **下一步:** 继续过期未知 Checkout 的安全查找与恢复,保留管理员处理和永久财务留存;真实 Stripe Sandbox、年付首购界面、生产绑定与副作用持久去重仍需按原计划验收,后续继续账号、运营与增长模块。I6 与整体目标仍进行中。 ## 2026-10-10:I6 第七批原结账请求查找与页面恢复 **状态:I6 继续进行,带请求标记的未知会话恢复已本地验,未标记旧未知请求与外部门槛保留。** 接续 `b06d26b4`,保留增量迁移 0000—0010,没有新增表。恢复复用已有主体租约、当前资源核对、插件回跳与写入保存校验,不增加第二套订阅状态写入器;完整 I0—I16 范围保持。 新 Checkout 在实际 SDK 参数中增加 `subscriptionAttemptId`。旧请求的 SDK 摘要若对应升级前未标记参数,重试仍使用相同正文 / 幂等键,不能追加元信息触发 Stripe 参数冲突。恢复入口只能核查服务端原请求:未知会话按原客户和固定一小时窗口分页查找,最多 1,000 条,要求唯一标记匹配,再读取当前会话核对主体、客户、模式、本地行和原期限。已知 ID 的旧会话可核查;未知且没有标记的旧请求仍留给后续人工流程,查不到不等于可以另付一次。 open 保存原会话但不自动跳转;complete / paid 或无需付款通过当前资源及实际插件回跳同步,保存成功后才解决原请求;expired 无关联订阅、客户无未结束订阅且本地行仍 incomplete 时释放原请求,供应商提前过期也适用。找不到、多个匹配、分页未完成 / 超限、未付款、信息不符和供应商失败均保留核查,恢复本身不创建付款。旧租约失效或组织撤权不能写回;POST 要求同站来源、当前会话和账单管理权,与回跳共用每人每分钟 30 次独立额度。 设置页增加“Check previous checkout”,与继续原请求、门户共同管理忙碌和错误状态。结果只刷新请求原本的用户 / 组织查询,界面核对当前主体后显示反馈;确认过期后允许新购,刷新仍保持。主账单配置不因此启用。 | 证据 | 实际口径 | | --- | --- | | 插件 / 数据库测试 | 66 项真实 Better Auth handler、SDK 与迁移 PGlite;本批包含未知 open / complete / 提前 expired、后续分页、缺失 / 歧义 / 未标记旧请求、绑定不符、保存失败修复、欠费门禁、跨站 / 跨主体、SDK 旧参数兼容、列表失败 / 截断 / 超限、处理中租约替换和未付完成;SDK HTTP 全部受控 | | 账单 / 页面 | 30 项账单接口、八项页面测试;原请求确认过期且没有供应商订阅才开放新购,核查失败不触发升级;组件接口是受控夹具 | | 八连接 PostgreSQL | 原结账 / 签名通知并发、旧令牌和撤权回归;一笔已接受但响应丢失的 SDK 请求,首个恢复查询暂停时其余七个为 409,放行后全部核查同一会话,没有第二个创建请求。真实 PostgreSQL 与 Bun 插件 handler,SDK HTTP 受控;临时数据库删除 | | 生产 workerd | 新 bundle、一次性 PostgreSQL、真实注册 Cookie 和 SDK Checkout POST;本地出站服务先接受会话再返回故障,核查找回 open、提前 expired 后新请求,再将完成会话通过插件同步。全部 SDK HTTP 拦截,其他目的地拒绝;旧签名 / 模式 / 大小 / 乱序与保存失败回归保留,没有真实 provider API / 门户 / 付款;临时资源删除 | | 实际浏览器 | 12410 一次性独立数据库 / 受控 Worker 与新页面构建;原请求由真实本地 HTTP 升级创建并丢失响应,浏览器登录后点击核查先收到受控 503,修复后核查确认提前 expired,隐藏原请求并显示新购,刷新仍保持;没有点击新购 / 门户。全部操作只有一份受控创建;保留失败、成功与手机截图,临时数据库 / Worker 删除 | | 响应式 | 320 像素文档宽度 320,关闭侧栏后 main client / scroll 均 305;恢复默认视口,关闭临时验收页 | | 全量检查 | 39 个文件、377 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性与离线配置通过;API、主预览 / 官网和中文教程构建通过 | 新增接口测试最初把已解决请求插成缺少原请求字段的记录,实际数据库约束正确拒绝;改为完整合法原请求夹具后通过。浏览器临时代理初次保留了已解压响应的压缩头,先修正本地透传并清理该次临时资源,再完成上述页面验收。该代理只属于忽略的验收脚本,不进入产品代码。 主预览六项服务刷新后均返回 200,实际读取到新的恢复教程;主账单仍关闭,没有应用新迁移。原账户保持 20 积分、四文件和一份成功免费任务;此前 79 字节下载与结果摘要一致,本批没有重新下载。12410 使用源站 bundle 的同名 localhost Cookie 替换了浏览器主会话,验收后已通过本地捕获验证码重新登录原 Local Preview / Preview Studio,未修改原账号凭据或数据;后续同源 bundle 夹具应使用不同回环主机名隔离 Cookie。 **后续仍需推进:** 未标记旧未知会话的人工核查、管理员处理及永久财务留存按 I9;真实 Stripe Sandbox、年付首购界面、生产绑定与持久副作用去重仍按 I6 / I12 验收。无实际 Stripe / Resend / 模型请求、云部署或共享数据库迁移;旧 11410 独立 UI 与 I4 / I5 生成站未升级。I6 和整体目标保持进行中。 ## 2026-10-10:I6 第八批年付首购与收费商品核对 **状态:I6 继续进行,年付首购与原请求恢复已本地验,真实沙箱仍待验收。** 接续 `cdf1b27a`,保留 0000—0010,没有新增迁移或依赖。继续由 Better Auth 写订阅状态,完整 I0—I16 范围保持;第七批中的年付首购待验为当时状态,本批完成本地页面流程,真实年费支付并未验收。 账单查询返回服务器配置的年付可用性及保存的周期。仅完整计费启用、独立年度 Price 配置且可新购的管理员看到 Pro 年付入口;Starter 和默认 Pro 仍是月付,已有订阅改周期走门户。新请求明确发送方案与周期;原请求继续保留原周期和回跳。个人请求显式绑定原用户,失败后也只刷新请求起始时的用户 / 组织查询,切换工作空间不会将结果归到新主体。页面显示原请求周期和当前服务器周期;发起下一项操作清除旧错误,忙碌期间禁用刷新,核查成功不再显示已解决的升级失败提示。 核对安装包发现插件价格读取会吞错,并回退配置 ID。实际 Checkout 创建前因此另读取收费项:必须是当前选中方案 / 周期的配置 Price ID,恰好一个收费项、正整数数量、同模式、已启用的 recurring / per\_unit / licensed 商品,周期为单月或单年、金额为正整数。供应商读取失败、归属 / 类型 / 模式 / 周期不符或商品停用不发送 Checkout POST。该路径比插件多一次当前 Price 读取,外部调用仍在事务之外,不新增供应商抽象或订阅状态表。三个配置 Price ID 必须不同,诊断不输出实际值。 设置页不从月度展示价计算年度金额或折扣,最终收费在 Checkout 确认。`StripeAnnual` 离线诊断和环境示例补齐启用说明;中文网站配置与支付教程说明支持类型、价格核对、原请求恢复及外部门槛。 | 证据 | 实际口径 | | --- | --- | | SDK / 插件 | 82 项真实 Better Auth 路由、SDK、迁移 PGlite;年付首购使用配置年度 Price、响应丢失后原键 / 正文重试、改月付拒绝、缺配置 / Starter 年付拒绝;月价冒充年价、错误模式、停用、计量 / 阶梯 / 多年 / 一次性 / 零值 / 小数 / 错误 ID、读取失败与插件读取后价格停用均不发 Checkout POST;SDK HTTP 全部受控 | | 账单 / 界面 / 配置 | 31 项账单、13 项页面、11 项环境测试;年度可用性来自服务器,保存周期可核对;原年付请求恢复、已解决错误清除、未配置不显示、普通成员无购买动作、切换工作空间后只刷新原查询;组件接口为受控夹具 | | 八连接 PostgreSQL | 结账门禁 / 原键重试、当前通知重投、旧持有者与撤权、原请求查找竞争均回归通过;插件 HTTP 在 Bun,SDK 受控,临时数据库删除 | | 原生 workerd | 当前生产 bundle、一次性 PostgreSQL 与真实注册 Cookie;先核查已失去响应的月付原请求 / 提前过期,再首次发送年付请求;实际 SDK POST 收费项明确为配置年度 Price,完成后通过原插件保存 yearly / trialing,核查不再次创建。原签名、模式、大小、旧通知、保存故障与回跳回归保留;SDK HTTP 受控,临时资源删除 | | 浏览器 | 最新 bundle 在独立 `127.0.0.1:13410` 和一次性数据库;浏览器从零 Checkout 选择年付,受控供应商接受后模拟响应丢失,页面自动查询并显示原 Pro 年付;点击核查同步 yearly / trialing、清除旧错误,刷新仍保持。一次验收只有一个 SDK Checkout POST,无替代请求、外部跳转或实际付款;当前构建重验后临时资源删除 | | 响应式 | 320 像素年付选项可见,关闭侧栏后文档 320、main client / scroll 均 305;恢复默认视口,保留手机选项、原请求和确认状态截图;主预览 Cookie 由不同回环主机名隔离 | | 全量检查 | 39 个文件、401 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性和离线配置通过;API / 主产品 / 官网及中文教程构建通过 | 测试过程中补齐真实 Price 夹具字段,并让旧 SDK 正文兼容测试显式提供服务器商品映射,旧请求正文及幂等键仍保持。测试工具与浏览器工具的角色查找参数不同,类型检查拒绝后使用正确接口,未通过类型断言绕过。没有修改已应用迁移或插件版本补丁。 主预览六项服务更新后均返回 200,实际读取到新中文教程;Stripe / StripeAnnual 在离线配置中均关闭,不向实际供应商发请求。浏览器原 Local Preview 会话及 Preview Studio 保留,原账户仍为 20 积分、四文件和一份成功免费任务;此前 79 字节下载与数据库结果摘要一致,本批没有重新下载或迁移主数据库。 **仍需推进:** 真实 Stripe Sandbox 下的月付 / 年付收费与门户、未标记旧未知会话的人工核查、管理员处理 / 永久财务留存、生产绑定及持久副作用去重;外部 AI 处理器与后续账号、运营、增长模块继续按完整计划迭代。没有实际 Stripe / Resend / 模型请求、云部署或共享数据库迁移,旧 I4 / I5 生成站与 11410 UI 未升级;I6 和整体目标保持进行中。 ## 2026-10-10:I8 第一阶段 · 团队邀请闭环 **状态:I8 进行中。** 本批接续 `5bd9b9a3`。完成团队邀请的本地接口、邮件和工作台流程;角色管理界面、套餐人数写入限制、退出 / 所有者转移及会话 / Passkey 仍待开发,不把整个账号模块标记为完成。 `Members` 中已验证邮箱的 owner / admin 可邀请 member / admin,查看每页 20 条历史、重新发送 pending 邀请或取消。接收页 `/invitations` 支持保留邮件中的邀请 ID,经登录后仅展示当前已验证邮箱的有效邀请;接受会由 Better Auth 建立成员关系并更新会话中的工作区,拒绝不会创建成员。个人文件、任务、结果、积分的归属保持既有规则。 新增增量迁移 `0011_simple_inhumans.sql`,以 `(organization_id, lower(email)) WHERE status = 'pending'` 索引替换终身唯一约束。终态历史可与新邀请共存;过期 pending 在重新邀请的锁内退役为 canceled,旧 ID 继续失效。0000—0010 及已发布历史未改写,额外 `acceptedAt` / `rejectedAt` 仍未自动填充。 所有邀请决定与成员移除、降级、退出、组织删除在组织行锁内执行认证插件的实际写入,错误响应会使事务回滚。没有另建成员写入接口。未显式指定团队时,在请求入口选定团队并绑定给插件,防止等待锁期间切换工作区改变操作目标。创建 / 重发还会在锁内读取最新的发起者邮箱验证状态。邀请发送复用现有 HTML + text 邮件入口,并在数据库提交后执行;发送失败返回可恢复的 503,保留 pending 并刷新界面供 Resend。持久化邮件任务仍属 I12,进程在提交后发送前终止时需要重新发送。 | 证据 | 实际口径 | | --- | --- | | 迁移 / 插件测试 | 真实 PGlite 迁移、实际 Better Auth HTTP 路由:错误 / 未验证邮箱、已接受、已取消、过期、拒绝后再邀、pending 重发、重复请求、邮件失败恢复与发起者验证;索引允许终态历史、拒绝大小写变体的第二条 pending | | 读取隔离 | 历史仅 owner / admin 可读;降级或移除后的旧会话立即失去历史权限;接收页仅返回该已验证邮箱的 live pending ID 和团队名 | | `bun organizations:validate` | 临时本地 PostgreSQL 的 8 个独立连接:重复邀请只有一条、接受 / 拒绝 / 取消只成功一个、两位 owner 同时自降级始终剩一位、降级者不能邀请、最后 owner 不能退出;真实锁等待期间切换团队,未指定 org 的邀请仍写入原团队 | | 原生 Worker | 同一 Worker bundle 完成注册、捕获含接收页 URL 的邮件、验证邮箱夹具接受、接受后的旧取消拒绝;外部请求全部拒绝,最终销毁临时数据库及 Worker 资源 | | 浏览器 | 一次性数据库与 `127.0.0.1:14310`:发送 / 重发、接收者实际邮箱 OTP 登录、打开邮件链接、接受并切换到团队、普通成员无邀请表单、取消后状态更新;与主预览 Cookie 隔离 | | 响应式 | 320 浅色、390 深色下完成邀请 / 取消;文档 scrollWidth 等于视口宽,无横向溢出;保存浅色邀请、接受及手机截图,临时视口恢复 | | 全量检查 | 40 个文件、412 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置、API / 主预览 / 中文教程构建全部通过 | 认证边界的改动另回归 `subscriptions:validate` 与 `payments:validate`:八连接原 Checkout 竞争 / 恢复、实际 Worker 的订阅状态、签名与保存故障 / 原请求恢复均通过,供应商 HTTP 全部受控。 仅任务所属主预览 `127.0.0.1:54329/launchkit` 应用 0011;迁移前后确认原免费任务 succeeded、1 次尝试、20 积分及四文件完全一致。六项预览服务更新后均返回 200,主浏览器原 Local Preview / Preview Studio 会话保留,新邀请卡片可见。此前 79 字节下载仍与结果摘要一致,本批没有重新下载。离线诊断 Stripe / StripeAnnual 均关闭,邮件只本地捕获。 **下一阶段:** 先将套餐人数上限落实到成员新增的数据库写入门禁,并验证并发接受不同邀请不能超额;再交付角色编辑、移除、退出和独立的所有者转移流程。接续会话 / Passkey 管理、恢复路径与真实外部环境验收。I6 的真实 Stripe、I7 的实际 AI 处理器、I9— I16 保持原计划范围。主预览仍不启用支付;本批未请求真实邮件、支付或模型服务,也未部署云端或迁移共享数据库。 ## 2026-10-10:I8 第二阶段 · 套餐人数与成员管理 **状态:I8 继续进行。** 接续 `66aa4c1c`,新增成员名额检查、角色编辑、成员移除与退出。没有新增依赖或迁移;保留 0000—0011。所有者转移、会话与 Passkey 管理、真实邮件及 OAuth 环境仍待推进,整体 I0—I16 目标保持。 人数使用同一配置中的 Free 1 / Starter 5 / Pro 50,所有者计入。账单和成员写入共享当前订阅访问判断:团队只使用自己的有效订阅,个人 Pro 不提升团队;支付关闭或试用 / 周期到期回到 Free,原成员保留。邀请不预留席位,接受满额邀请返回 403 且 pending 保留。组件的预检查提供提示;真正的门禁在插件 adapter 新增 member 前锁组织行并读取当前人数,认证插件继续负责写入。 检查实际认证组件代码发现,其默认 adapter 事务回调会传入未装饰的底层实例。因此让回调保留带有成员 / 订阅门禁的 adapter;组织 HTTP 边界仍拥有整体事务,可信 `addMember` 自身的新增检查使用短事务。明确拒绝开启另一个 Drizzle adapter 独立事务配置,避免绕过外层边界。本批同时回归已有订阅 / 支付写入门禁,没有另建成员写入服务或订阅状态表。 成员行可保存 admin / member 角色,admin 不能管理 owner;移除与退出使用二次确认。最后 owner 禁止退出、移除与降级。修改完成或失败都重新读取会话并刷新原团队数据,操作期间切换工作区不会改变目标;退出后实际会话返回个人范围。界面不提供 owner 提升,独立的所有者转移留待下一阶段。 | 证据 | 实际口径 | | --- | --- | | 真实组件 / 数据库测试 | PGlite 全迁移、实际 Better Auth 邀请 / 角色 / 移除 / 退出 HTTP 与可信 `addMember`;Free、Starter 最后名额、释放名额、个人订阅隔离、到期保留成员、adapter 事务回调不能绕过;前端验证拒绝后刷新、原团队绑定和退出期间保持等待 | | 八连接 PostgreSQL | 8 个已验证用户接受不同邀请争抢 Starter 最后名额,1 成功、7 个 403、成员为 5 且 7 个 pending 保留;另用 8 个独立连接直接调用可信插件 `addMember`,不经过 HTTP 组织锁,仍只有 1 个新增成功;原邀请 / 终态 / owner 竞争和切换工作区回归通过 | | 原生 workerd | 临时数据库、真实注册 Cookie、捕获邮件与接受;角色升降、移除后的原 Cookie 被团队及账单读取拒绝,用户记录保留;退出后实际会话 activeOrganizationId 为 null。付费资格是数据库夹具,客户 SDK HTTP 由本地白名单拦截并校验客户 ID 已持久化,其他出站拒绝;不是实际 Stripe / Resend 验收 | | 浏览器 | 当前 app + Worker bundle、一次性数据库、独立 127.0.0.1:14310;owner 修改角色、刷新仍保留、降级、二次确认移除后人数减少;普通成员看不到管理操作,确认退出后转个人,刷新仍保持。最后 owner 退出按钮禁用;满额页面显示 5 / 5 与邀请不占名额说明;保留截图,临时服务 / 数据库销毁 | | 手机布局 | 320 像素深 / 浅主题及移除确认:document scrollWidth 305、main client / scroll 均 290,视口 320,无横向溢出;恢复默认视口 | | 支付回归 | `subscriptions:validate` 的八连接竞争 / 恢复与 `payments:validate` 的真实 Worker 通知 / 保存故障 / 年付原请求恢复全部通过,所有供应商 HTTP 受控;临时资源删除 | | 全量检查 | 41 个文件、424 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性和离线配置通过;API / 主预览 / 官网与中文教程构建通过 | 主预览六项服务已更新,原 Local Preview / Preview Studio 会话保留;主支付关闭,按 Free 显示 1 / 1,最后 owner 不能退出。原账户保持 20 积分、四文件、成功免费任务和一次尝试;此前 79 字节下载仍匹配数据库结果摘要,本批没有重新下载或迁移主数据库。 **下一阶段:** 独立的所有者转移、会话列表 / 撤销及 Passkey 管理。真实邮件、OAuth / Passkey 与 I6 Stripe 沙箱、I7 实际 AI 处理器仍按各自验收推进,后续运营 / 增长模块保持完整计划范围。本批未访问实际支付、邮件或模型供应商,未云部署或迁移共享数据库。 ## 2026-10-10:I8 第三阶段 · 原子所有者转移 **状态:I8 继续进行。** 接续 `0ffd6442`。完成独立的所有者转移流程;会话与 Passkey 管理、真实邮件 / OAuth 环境以及后续 I9—I16 仍保持原计划范围。没有新依赖、迁移或成员写入器。 组织边界增加显式 `organizationId` / `memberId` 的同源认证操作,在已有组织行锁内调用 `ownership-transfer.ts`。验证当前发起者为已验证非匿名 owner,目标是同一团队的另一位已验证非匿名成员;调用同一个事务绑定的完整 Better Auth handler,先提升目标为 owner,再降级发起者为 admin。任一步错误响应或数据库异常使整体回滚。已有 owner 可接收,其他 owner 保持不变;它不是把所有 owner 替换为一个人。转移不会新增成员,也不受降级后已有成员超额的影响。 普通角色编辑仍只提供 admin / member,公开普通角色接口也拒绝 string / array / 逗号组合中的 owner 提升。只有 owner 能看到转移入口,成员搜索 / 分页返回服务器判断的候选资格;自选、未验证、匿名、已移除和跨团队成员拒绝。页面确认说明权限变化;转移通过认证客户端发起单个操作,不在浏览器拆成两次角色请求、不自动重试。成功或失败后读取真实会话和原团队列表,响应丢失也能核对当前角色。转移不更改个人资产或支付账户;进一步近期登录策略、审计与安全通知仍按账号 / I9 / I12 推进。 | 证据 | 实际口径 | | --- | --- | | 实际组件 / PGlite | 8 项迁移数据库与完整 Better Auth 路由测试:两步角色变化、旧 owner 失权 / 可退出、新 owner 不能直接退出、成员 / admin 发起拒绝、双方邮箱 / 匿名状态读取、自选 / 外团队 / 移除 / 无效请求拒绝、真实 SQL 触发器拒绝第二步时提升回滚、已有 owner 接收 | | 查询与前端 | owner 候选跨搜索页、未验证 / 匿名目标排除、admin 不返回可转移资格;前端单个 auth `$fetch` 操作,失败 / 响应丢失后仍刷新原团队角色,不调用两次客户端角色变更 | | 八连接 PostgreSQL | 同一 owner 同时向不同成员提交 8 项转移:1 个成功、7 个 403,原 owner 为 admin,仍有一个 owner。原邀请 / 名额 / 终态 / owner 竞争及工作区切换回归通过 | | 原生 Worker | 真实数据库触发器拒绝实际第二步降级,API 返回 500 且目标提升回滚;解除故障后重试 200,实际两步角色均保存,原 owner 的旧 Cookie 再转移 403。客户 SDK HTTP 全部受控、邮件仅捕获;临时数据库 / Worker 删除 | | 浏览器 | 最新 bundle、一次性数据库与独立 127.0.0.1:14310;确认转移后目标成为 owner、发起者成为 admin,刷新仍保留且转移入口消失;发起者随后成功确认退出并回到个人工作区。保留确认、成功和退出截图,临时资源销毁 | | 响应式 | 320 浅 / 深主题确认说明及按钮可用,document scrollWidth 305、main client / scroll 均 290,视口 320,无横向溢出;恢复默认视口 | | 全量检查 | 42 个文件、434 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置及 API / 主预览 / 官网 / 中文教程构建通过 | 主预览六项服务更新后均返回 200,新中文所有者转移教程可读。Local Preview / Preview Studio 保留,主团队只有一位成员,不显示转移目标;未修改主团队角色。原账户仍为 20 积分、四文件、成功免费任务和一次尝试;此前 79 字节下载与结果摘要一致,本批没有重新下载或迁移主数据库。支付继续关闭,没有实际 Stripe / Resend / 模型请求、云部署或共享数据库迁移。 **接续:** 会话列表 / 撤销、Passkey 添加 / 命名 / 列表 / 撤销及恢复路径。I6 实际支付沙箱、I7 实际 AI 处理器和后续完整运营 / 增长模块继续按计划,I8 和整体目标不标记完成。 ## 2026-10-10:I8 第四阶段 · 登录设备与会话撤销 **状态:I8 继续进行。** 接续 `39014964`,设置页新增登录设备列表、当前标记、单个会话撤销、其他设备批量撤销和近期登录恢复入口。没有新依赖、迁移、会话写入器或服务端认证策略替换;保留 0000—0011。Passkey、真实邮件 / OAuth、I6 真实支付、I7 实际模型与 I9—I16 继续按完整计划推进。 读取和撤销直接使用现有 Better Auth 客户端。查询按用户 ID 隔离,不随工作空间转移;列表只展示有效会话,当前会话排首位,其他按创建时间排序。日期为浏览器本地时区,User-Agent 只作设备辨认提示,IP 缺失不编造地点。撤销 token 只在内存中按 SDK 契约使用,不进入 DOM、链接或持久存储;变更缓存只保存目标 ID,不包含 token。 单个 / 批量都要求确认,当前会话没有远端撤销按钮,继续使用原侧栏退出。写入失败也重新读取真实列表,因为认证组件批量删除可能部分完成;不自动重试、不把报错标成成功。设备列表保留 SDK 默认近期登录门槛,过期或会话已失效时可重新登录;只有实际 sign-out 成功才清理本地会话并跳转安全的 settings 回跳,失败仍保留当前登录。没有关闭服务端 freshness 或 Cookie / CSRF 检查。 | 证据 | 实际口径 | | --- | --- | | 真实组件 / PGlite | 6 项完整 Better Auth 与迁移数据库测试:有效会话列表 / UA / 用户隔离、单个撤销与旧 Cookie 失效、外用户及猜测 token 不影响外用户、批量保留当前 / 外用户、25 小时会话拒绝列表而新登录恢复、无 Cookie / 撤销者 / 错误 Origin 被拒绝 | | 页面 / 缓存 | 5 项页面测试:当前标记、只有远端操作、DOM 不含 token、确认后才发撤销与刷新、部分批量失败不报成功并读取真实余下会话、重登录 sign-out 失败保留缓存 / 成功后携带安全回跳;测试夹具等待列表完成加载,非活跃 auth 查询保留到测试结束 | | 原生 Worker | 真实注册 / 多次密码登录、实际 Cookie、一次性 PostgreSQL;单个撤销后的原 Cookie 的私有 tRPC 立即 401;8 个并发批量撤销全部 200,只余当前设备,外用户仍有效。25 小时会话 list 403 / get-session 仍有效,实际退出和登录后恢复列表。外部请求全部拒绝,临时资源删除 | | 实际浏览器 | 最新 app / Worker、独立 127.0.0.1:15310 与一次性数据库;单个确认撤销后消失,手机批量确认后只余 This device,刷新保留;夹具将会话改为 25 小时,刷新显示 Sign in again,实际退出、密码登录自动回 settings 并恢复当前设备列表。不同设备提示由受控 UA 登录夹具创建,并非真实物理远端设备;保留截图,临时资源销毁 | | 响应式 | 320 浅 / 深主题确认按钮与设备日期可用,document scrollWidth 320、main client / scroll 均 305,无横向溢出;恢复默认视口 | | 全量检查 | 44 个文件、445 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置及 API / 主预览 / 官网 / 中文教程构建通过 | 主预览六项服务更新,新中文会话教程已读取。Local Preview / Preview Studio 会话及原账号保持,设置页显示实际当前设备;主支付仍关闭,没有撤销主账号其他会话或迁移主数据库。原账户为 20 积分、四文件、成功免费任务和一次尝试;此前 79 字节下载仍与结果摘要一致,本批没有重新下载。未访问真实 Stripe / Resend / 模型、云部署或迁移共享数据库。 **下一阶段:** Passkey 添加、命名、列表 / 删除与最后可用登录方式的恢复检查,再接续管理员与审计等完整计划模块。会话撤销阻止后续认证请求,不承诺取消已经排队的任务或推送清空其他设备当前页面;I8 与整体目标继续进行。 ## 2026-10-10:I8 第五阶段 · 通行密钥与恢复底线 **状态:I8 继续进行。** 接续 `a23a994e`。设置页提供通行密钥添加入口、列表、改名、删除确认及近期登录回跳;服务端保护最后登录方式与密码凭据解绑。没有新依赖、迁移或凭据写入器,保留 0000—0011。真实设备 / OAuth / 邮件、I6 真实支付、I7 实际模型和 I9—I16 保持原范围。 客户端使用实际 Better Auth Passkey API,按用户隔离缓存;名称 1—80 字符,取消 / 失败不显示成功,不自动重试设备注册。变更结束重新读列表,处理响应丢失后的真实状态。名称及创建时间可见,公钥与 credential ID 不进入页面;不使用尚未写入的 `lastUsedAt` 或设备字段冒充真实记录。添加检查安全上下文和 WebAuthn 支持,管理入口关闭跟随通用配置。 列表、注册 / 验证、改名、删除复用组件原生近期登录 middleware,匿名账号拒绝。删除与公开解绑账号使用同一账号行锁,完整插件在同一数据库事务执行实际变更;若最后通行密钥或启用中的密码被移除后,不再有已验证邮箱的验证码 / 真实密码登录路径,则回滚。另一个通行密钥可继续保留登录,单有外部账号绑定不认作可靠的邮箱恢复底线。前端二次确认说明删的是站点凭据、既有登录会话与设备上的副本保留。不是用按钮禁用代替服务端保护。 | 证据 | 实际口径 | | --- | --- | | 真实认证组件 / PGlite | 12 项迁移数据库与完整 handler 测试:真实 ES256 注册 / 签名登录、持久改名、删除后拒绝登录、最后凭据回滚 / 仍可登录、另一个通行密钥保留、验证状态与配置 / 密码为空检查、用户 / Origin 隔离、近期窗口、挑战重放 / 错误 origin / 伪造签名拒绝、匿名 / 撤销会话拒绝、端点替代写法不绕过、密码解绑回滚及验证码恢复路径 | | 页面 / 缓存 | 6 项测试:添加调用官方设备客户端 / 刷新,改名保存,确认后才删除、服务器拒绝时保留凭据和错误,注册取消不重复调用或假成功,近期登录恢复,浏览器不支持时解释限制;DOM 不含公钥 / credential ID。设备注册调用在页面测试中为受控模拟,不称为真实系统弹窗 | | 原生 Worker / PostgreSQL | 软件认证器生成真实注册和签名消息,实际生产 Worker 验证身份、重放、改名、跨用户和 Origin;8 个并发删除只成功一个,余下凭据仍可登录,删除者不能登录。25 小时会话管理拒绝,新密码登录恢复;验证邮箱后可删除最后凭据,密码登录仍有效。另实际解绑密码,捕获验证码并通过组件验证码登录同一账号;OAuth 行只是受控数据库夹具,未实际授权,未外发邮件 | | 浏览器 | 独立 `passkeys.localhost:16310`、最新 app / Worker 和一次性数据库;两个通过协议注册的凭据列出,改名 / 删除成功、刷新保留;最后一个未验证邮箱时拒绝并仍在列表;25 小时会话后实际退出 / 密码登录自动回设置;夹具邮箱验证条件满足后删除,刷新为空。结束后脚本用两份实际签名确认页面删除的凭据均不能登录、密码仍可登录;临时资源销毁 | | 响应式 | 删除确认在 320 像素浅 / 深主题可用;修复确认按钮超宽,document scrollWidth 320、main client / scroll 均 305。恢复默认视口;保存改名、删除确认、最后凭据保护、重登录和删除结果截图 | | 全量检查 | 46 个文件、463 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置和 API / 主预览 / 官网 / 中文教程构建通过。新增中文通行密钥教程读取 200 | 主预览六项服务返回 200,Local Preview / Preview Studio 会话保留,设置页显示实际当前设备与通行密钥管理。没有为主账号添加、移除或解绑凭据,主支付关闭。原账号保持 20 积分、四文件、成功免费任务与一次尝试;此前 79 字节下载与结果摘要一致,本批没有重新下载、迁移主数据库或云部署。没有真实 Stripe / Resend / 模型请求。 **外部验收边界:** 软件认证器验证服务端签名协议;系统屏幕锁、手机、Windows Hello 和实体安全密钥的真实注册 / 登录弹窗仍需人工设备验收,不把添加按钮或模拟 API 调用算成完成。真实验证码邮件送达和外部账号恢复仍按独立环境验收。下一阶段接续 I9 管理员与审计;I8 和整个目标不标记完成。 ## 2026-10-10:I9 第一阶段 · 站点授权、封禁与审计 **状态:I9 进行中。** 接续 `45d8f267`。实现站点角色、明确账号初始化、`/admin`、用户搜索 / 分页、角色变更、永久封禁 / 解封、任务元数据和审计查看;积分账本管理、支付排查、带原因积分调整、任务恢复 / 对账、生产初始化与 I10—I16 继续按完整计划。新增[中文教程](./admin-audit),不把第一阶段标成完整运营后台。 接入已安装 Better Auth Admin,不增加第二套身份或会话写入器。新增增量迁移 0012、用户角色 / 封禁字段、兼容会话字段与 `admin_audit`。普通注册保持 `user`;组织 owner / admin 不提供全站权限;注册 / 资料更新不能注入角色。每次读取 / 变更都检查新鲜用户状态,授权不使用缓存连接。审计保留历史 ID,无账号级联外键;同步数据库规范说明,不删改旧迁移。 受控本地命令要求现有、已验证、非匿名、未封禁的明确 `usr_` ID 和原因,拒绝远程数据库 / 自动选首个账号;真实组件用户写入与审计同一事务。重复初始化不恢复已撤销角色,其他候选拒绝。生产初始化 / 灾难恢复仍待实现。 公开管理写入只有三种同源 POST。原因、16 KiB 请求上限、严格字段及 UUID;先锁管理变更 / 目标,再调用完整组件并保存前后状态。封禁实际删除会话,审计故障使两者一起回滚。全局事务级咨询锁保护跨账号并发;至少留一位有效管理员,禁止自封禁或提升未验证 / 封禁账号。未审计的插件模拟登录、用户创建 / 删除、密码设置等接口拒绝,替代路径不绕过钩子。 执行者与请求 ID 唯一约束去重;重复 ID 的变更请求必须保持动作、对象、角色和原因一致。解封后重放旧封禁不再次封禁。页面明确确认,失败保持原请求 ID 供手动重试;不自动重试,成功 / 失败都重新读状态及审计。权限检查失败或失权时隐藏缓存的管理数据;近期会话过期提供实际退出 / 登录回跳。任务只读页不返回原始输入、名称、参数、摘要或私有下载。 封禁拒绝后续登录及新任务请求;已接受任务继续原扣费、退款和执行规则,不额外退款 / 取消,不承诺中断所有在途请求。用户 API Key 尚未实现,其新鲜封禁检查随 I10 完成。 | 证据 | 实际口径 | | --- | --- | | 完整认证组件 / PGlite | 8 项:明确初始化 / 非自动授权 / 字段注入拒绝、团队分权、真实封禁 / 会话删除 / 登录拒绝 / 解封、SQL 触发器让审计失败后的全事务回滚、重放与冲突、最后管理员及旧会话失权、Origin / 原因 / freshness / 未审计接口、账号删除后审计保留 | | 管理读取 / PGlite | 3 项:故意空的独立缓存库不影响权限、旧上下文失权 / 封禁、26 用户分页与字面通配符搜索、任务 / 审计筛选和私有字段缺席 | | 页面 / 缓存 | 5 项:确认前不写入与取消、响应丢失后同一 ID 手动重试 / 刷新、失权与权限检查故障隐藏旧私有数据、近期登录恢复入口 | | 原生 Worker / PostgreSQL | 明确 CLI 初始化;实际 Cookie、团队 API 403;八个并发相同请求全 200 / 一条审计;真实封禁旧 Cookie 失效、登录 403、新任务 401;审计 SQL 故障回滚封禁和会话;解封后重放无变化;八个跨账号并发降权只发生一次有效角色下降且保留一位管理员,旧 Cookie 管理 API 403;未审计模拟登录 403 | | 浏览器 | 独立 `admin.localhost:17310`、最新 app / Worker 与一次性数据库。搜索、取消确认、封禁 / 解封、状态和对应原因审计、刷新保留;脚本核实两条浏览器变更审计实际落库。正式权限变更只发生在一次性夹具;没有修改主预览角色 | | 响应式 | 320 浅 / 深主题发现并修复确认按钮宽度;document scrollWidth 320,main client / scroll 均 305。390 下 document 390,main 均 375;恢复默认视口、关闭测试标签,保留桌面与手机截图 | | 全量检查 | 49 个文件、479 项测试通过;TypeScript、Oxlint、Oxfmt、迁移一致性、离线配置、官网 / 主预览 / 中文教程构建通过;生产 Worker bundle 由原生验收实际构建运行 | 一次性数据库、Worker、资源目录销毁,实际查询没有遗留 `admin_acceptance_%` 数据库;禁止的外部请求为零。主预览六项服务 200、新中文教程可读;只重启本任务文档服务,主 API 保留此前 0011 bundle。实际设置页仍显示 Local Preview / Preview Studio、当前设备、无通行密钥与支付关闭。主账号邮箱已验证、20 积分、四文件、成功免费任务及一次尝试保留;本批不重新下载、修改主权限、迁移主数据库、部署云端或请求实际支付 / 邮件 / 模型。 主数据库尚未应用 0012,不能直接启动新 API。下一阶段先在独立验收环境补积分 / 支付只读排查与同一账本中的管理调整,再补恢复、生产初始化和 API Key。主预览升级需明确确认本地增量迁移范围;全计划目标保持进行中。 ## 2026-10-10:I9 第二阶段 · 财务排查与审计积分调整 **状态:I9 继续进行。** 接续 `3e6dd9df`,交付已有账本、购买 / 支付事件、个人 / 团队订阅查看,以及带原因、先复核再确认的积分调整。任务恢复、主动支付对账、生产初始化和 I10—I16 保持完整计划范围。没有新依赖或 SQL 迁移;JSON 审计类型和 text 动作扩展兼容 0012,生成检查确认无需迁移,既有 SQL / 快照 / journal 未改写。 财务查看按站点权限、账号 / 购买 / 账单归属筛选,每页 20 条并使用稳定时间 / ID 排序。读取不初始化钱包;实际零余额与未初始化区分,事件零快照与未记录区分。全站视图包含团队订阅,选择用户只显示其个人订阅。仅展示数据库已有记录与排查 ID,不访问供应商、不返回 Checkout URL、内部租约 / 请求键或原始负载,也不把保存的订阅状态代替实时权益规则。 调整只接受本站 Origin、正式非匿名目标、非零整数 ±1,000,000 和 5—500 字符原因。执行者权限及实际会话在事务锁内复核,与 Better Auth 共用 24 小时近期登录窗口;撤销会话拒绝,过期页面提供退出 / 登录回跳。封禁目标可修正账目,但不解除封禁、购买差额或支付审核限制。调用同一余额锁 / 账本服务,独立 `adjustment` 不带任务 / 购买关联;必要欢迎初始化与调整、审计同事务,审计 SQL 故障全部回滚。 审计保存实际前后余额 / 审核状态、调整量、账本 ID、执行者、目标、原因和时间。管理请求共用执行者 / UUID 命名空间,相同请求只处理一次,改变内容重用拒绝;消费后重放不恢复历史余额;孤立账本事件缺少审计时拒绝继续。页面取消不写入,响应不确定保留同一 ID 手动重试,不自动重试;结束后重读余额 / 账本 / 审计,权限或读取错误隐藏缓存数据。用户账本只展示通用调整说明,维护原因留在管理审计。 本轮原生并发测试发现人工调整持有用户 `UPDATE` 锁、另一个任务 / 支付写入持有钱包锁后检查用户外键时形成死锁。修复为用户 `NO KEY UPDATE`,允许账本 `KEY SHARE` 外键检查,并按执行者用户 → 会话 → 目标 → 钱包顺序锁定。重新运行独立连接的真实任务预扣、支付冲正和人工调整,所有操作完成;后续实际退款后余额与全账本合计一致,不通过跳过断言或自动吞掉数据库错误解决。 | 证据 | 实际口径 | | --- | --- | | 积分 / PGlite | 8 项:余额增减、审核 / 封禁保留、独立类型与关联限制、重放后消费保留、跨动作 UUID 冲突、已有 / 首次钱包审计故障回滚、负余额 / 溢出 / 非法字段、孤立账本拒绝、降权 / 封禁 / 25 小时 / 撤销会话拒绝 | | 管理读取 / PGlite | 累计 6 项:故意空的缓存库不用于权限;普通 / 团队 owner 与旧上下文不能读取财务;23 条账本 / 购买 / 事件 / 个人订阅分页无重复,团队归属独立;只读不发欢迎积分;私有 Checkout / 租约缺席;事件 null / 0 保留;错误 / 缺失 Origin 不能写,正确同源可写 | | 页面 / 缓存 | 新增 6 项:取消不写,失败同 ID 手动重试 / 读实际余额与账本,typed 近期登录错误调用恢复,未初始化只读 / 非整数与零禁用,购买事件 / 订阅筛选,读取失败隐藏旧账本;原账号管理 5 项保留 | | 原生 Worker / PostgreSQL | 8 个相同调整请求一笔账本 / 审计;8 个唯一扣减只有余额允许的 3 个成功 / 5 个 412,重放不改消费;账号动作与调整 UUID 冲突;SQL 审计故障回滚新欢迎 / 钱包及既有余额;真实 25 小时 Cookie 收到 typed 403、新密码登录恢复;独立连接同时人工调整 / 实际任务预扣 / 支付冲正,后续实际任务退款、全账本合计一致;审核标志未清除;原账号封禁 / 最后管理员验收仍通过 | | 浏览器 | 一次性 `admin.localhost:17310`、最新 app / 生产 Worker:筛选账号、复核 / 取消、确认 +7 和 -4,余额实际 102 → 109 → 105;对应账本和原因审计刷新保存;购买 / 事件显示缺失快照与 0,个人 / 全站订阅显示正确归属。购买 / 订阅是明确合成的数据库夹具,没有真实 Stripe 交易 | | 响应式 | 320 浅 / 深主题的调整确认及财务记录 document scrollWidth 320、main client / scroll 均 305;390 的全站订阅 document 390、main 均 375,无横向溢出。恢复视口、关闭测试标签,保存桌面和手机截图;主账号登录页 / 主题未被测试账号影响 | | 回归与构建 | 51 个文件、496 项测试通过;类型、Oxlint、Oxfmt、迁移一致性、离线配置及主预览 / 中文教程构建通过。`tasks:validate-charges` 的 8 独立连接原扣费 / 退款规则通过;`payments:validate` 实际 SDK / Worker 的订阅生命周期、签名 / 当前状态、故障回滚与原 Checkout 恢复通过,供应商 HTTP 由受控夹具响应 | 用户明确选择升级本地预览后,只对本任务 `127.0.0.1:54329/launchkit` 应用已验收 0012,确认原 12 条迁移历史及全部 13 条 SQL 摘要匹配;现有账号仍为未封禁普通用户,没有初始化主管理员。新版 API 和六项预览服务已重启并返回 200。实际浏览器原 Local Preview / Preview Studio 会话保留,当前设备、无通行密钥和支付关闭显示正常;原账号保持已验证邮箱、20 积分、四文件、成功免费任务与一次尝试。本批没有重新下载原 79 字节结果。 一次性数据库、Worker 和资源目录实际销毁,查询没有遗留 `admin_acceptance_%`。没有实际 Stripe / Resend / 模型请求、云部署或共享数据库迁移。**下一阶段:** 带原因的任务恢复与主动对账流程,保留原扣费 / 退款 / 支付校验和可核对审计,再接续生产初始化及 API Key。I9 与整体目标继续进行。 ## 2026-10-10:I9 第三阶段 · 带原因任务恢复 **状态:I9 继续进行。** 接续 `014175e0`,在已有管理页增加按选中账号分页、带原因复核 / 确认的重试与取消,任务操作和审计原子提交。主动支付对账、生产初始化、真实外部环境及 I10—I16 仍保留完整范围。没有新依赖或 SQL 迁移;生成检查确认 text 动作与 JSON 审计类型扩展不改变数据库结构,保留全部 0000—0012 SQL / 快照 / journal。 将原任务服务拆出事务内入口,普通用户仍使用原事务包装;管理员与任务执行共用尝试预算、输入可用性、容量、处理器版本、收费快照和扣费 / 退款规则。管理员不能追加尝试、重新预扣、复活已退款任务或绕过审核 / 封禁。未运行取消结束并按原账本返还一次;运行 / 核对中的任务仅记录请求,执行器持有实际租约后结束并结算。前端区别取消请求与执行完成,已取消记录不再提示可能仍在运行。 实际角色、近期 24 小时登录及未撤销会话在事务锁等待后重查;同源严格输入,每管理员任务写入每分钟最多 10 请求,重放计入。共享管理咨询锁、账号 `NO KEY UPDATE` 和任务锁保留已有并发顺序。审计失败时任务状态、输入保留与退款全部回滚。审计仅存任务 ID 和必要运行状态,没有文件名、摘要、输入、参数、私有下载或会话 token。 请求 UUID 与账号 / 积分操作共用执行者命名空间;同 UUID 的同内容重放不派发、不复活后来完成 / 取消 / 退款的任务,改变动作 / 任务 / 原因会拒绝。页面取消确认不写入,响应丢失沿用冻结 ID 手动重试;没有自动重试。每次结束重新读任务、账本与审计,读取出错隐藏旧任务数据。任务恢复区单独组件,账号 / 财务 / 任务操作共享忙碌状态,避免请求中切换对象。 任务与审计提交后才调用 Queue;派发失败 / 进程中断由原数据库待派发记录及维护流程继续,原执行版本与租约防止重复发布。审计证明管理请求已提交,不冒充实际处理完成或退款完成。 | 证据 | 实际口径 | | --- | --- | | 事务 / PGlite | 新增 8 项:原尝试 / 收费 / 输入保留及有限审计、后续取消后旧重试不复活 / 跨动作 UUID 冲突、两种审计故障回滚、尝试耗尽 / 版本 / 已退款 / 故障 / 输入 / 容量 / 审核门禁、运行取消延后结算、实际角色 / 25 小时 / 撤销会话、封禁不能重试但可取消清账 | | HTTP / PGlite | 新增 3 项,累计管理路由 9 项:普通账号拒绝、同一取消请求前 10 次成功 / 第 11 次 429 且一次审计 / 退款、未配置执行资源 / 缺 Origin / 实际近期会话拒绝、最小任务元数据与可操作提示 | | 页面 / 缓存 | 新增 5 项:复核 / 取消不写入与账号筛选、丢响应同 ID 手动重试并读取实际退款状态、运行取消不声称已完成 / 退款、typed 登录恢复、读失败隐藏缓存任务;最后取消提示修订后单独重跑 5 项通过 | | 原生 PostgreSQL / Worker | `admin:validate-tasks` 新建一次性数据库并运行生产 Worker。8 相同请求只有一审计及一次实际 Queue 消费,保留 3 积分并结算,原尝试元数据 1 → 实际尝试 2;真实 R2 结果经会话下载匹配文本。8 个不同重试 / 取消请求竞争,结果结算与退款互斥;实际审计触发器分别阻断重试 / 取消,任务 / 输入保留 / 账本回滚;25 小时 Cookie、封禁与 10 次速率门禁通过。不同场景仅重置一次性夹具限流计数,生产不绕过 | | 浏览器 | 隔离 `admintasks.localhost:18310`:原确认取消后任务仍失败 / 保留,重新确认实际重试并刷新为 succeeded / settled;原因审计持久。受控长运行租约取消后仍 running / reserved 并明确请求中;结束浏览器验收后实际原执行器 `failTask` 转 canceled / refunded,脚本核实两条原因审计各一次、账本余额一致。既有失败尝试与长租约为明确控制的夹具,没有实际远程处理器 | | 响应式 | 320 浅 / 深主题的取消确认及任务审计 document scrollWidth 320、main 305;1280 桌面 document 1280。保存任务、实际审计截图,恢复临时视口 | | 回归 | 53 个文件、512 项测试通过;类型、Oxlint、Oxfmt、迁移一致性与离线配置通过。`tasks:validate-charges` 的八独立连接扣费 / 退款互斥及实际 R2 通过;原 `admin:validate` 账号 / 财务审计、并发账本、最近登录 / 降权 / 最后管理员再次通过;没有改支付结算实现 | 一次性数据库、Worker 和资源目录均实际销毁,外部供应商请求为零。新版主预览与中文教程重建 / 重启;原账号仍已验证、未封禁普通用户,20 积分、四文件、成功免费任务与一次尝试保留,六项服务返回 200。本轮没有新增迁移、提升主账号、真实支付 / 邮件 / 模型请求或云部署。 **下一阶段:** 主动支付对账,复用真实供应商校验和原账本结算,并为请求及结果保留可核对审计;接续生产初始化与 API Key。I9 及整个目标保持进行中。 ## 2026-10-10:I9 第四阶段 · 主动支付对账与完成审计 **状态:I9 继续进行。** 接续 `7d1425f3`,购买事件区新增带原因复核 / 确认的当前支付检查;复用原 Stripe SDK 读取与结算服务,增加请求和完成审计。生产初始化、真实外部验收和 I10—I16 仍保留完整范围。没有新依赖或 SQL 迁移;审计 text 动作与 JSON 类型扩展兼容 0012,生成检查确认无结构变化。 请求只接受购买 ID、UUID 和 5—500 字符原因。管理员实际角色、同源、近期 24 小时会话及对应测试 / 正式模式检查;每分钟最多 10 次,重放计入。服务器列表提示当前模式 / 引用是否可检查,读取不访问供应商;可以清理已有购买,不能在检查中创建 Stripe 收费、退款或订阅变更。新销售开关无需为历史对账开启。 先提交 `purchase.reconcile_requested`,随后原 SDK 读取当前 Checkout / PaymentIntent / Charge / 退款 / 争议。网络期间不持有管理与账本事务锁;原购买租约限制并发。结算前再次锁定并检查实际角色和会话;期间撤销会话不能提交财务写入。原校验决定金额 / 模式 / 归属 / Price 一致性及实际到账,不能注入“已支付”或手动清除审核。 复用原结算规则发积分、冲正 / 恢复、计算短缺与保留其他购买的审核限制。完成审计、购买、钱包 / 欢迎积分、账本和支付事件同事务提交,完成审计失败全部财务写入回滚;先前请求记录保留。审计只保存购买 / 对账事件 ID、已应用 / 需审核结果及实际前后状态、发放标志、退款 / 争议、扣回 / 差额,不含 Checkout URL、供应商原始负载、会话 token 或密钥。 稳定事件 ID 和执行者 / UUID 复用原幂等协议;同内容恢复缺失完成时可以继续,已完成重放不查询供应商、不恢复历史余额。不同内容或跨账号 / 积分 / 任务动作 UUID 冲突。购买已有事件却缺少完成审计时拒绝伪造完成。历史请求审计显示“请求已记录”,不误标为当前仍在等待;需结合完成审计和当前购买判断。新的后续退款检查需重新复核并生成新 UUID。 前端不自动重试;失败保留冻结确认供手动恢复,结束后重读购买、事件、余额、账本和审计。复核取消不提交;成功提示要求查看真实记录,`review` 不冒充支付正常。对账确认独立组件,与账号 / 积分 / 任务共享忙碌状态;请求中不能切换对象。读取失败隐藏旧购买及操作入口。 | 证据 | 实际口径 | | --- | --- | | 服务 / PGlite | 新增 7 项,使用实际 Stripe SDK 与受控 HTTP:请求先落库 / 完成与结算原子、消费后重放无供应商读取 / 不恢复余额、供应商失败保留请求并同 ID 恢复、完成审计 SQL 故障回滚欢迎 / 钱包 / 发放 / 事件、真实会话在查询期间撤销、金额不符保留审核、实际部分 / 全额退款和余额短缺;另含模式、引用、注入字段、跨动作 UUID 与近期登录门禁 | | 路由 / PGlite | 新增 2 项,累计 11:角色 / Origin / 严格输入 / 不存在购买拒绝,不创建账目;未配置模式列表不可操作、前十个请求 503 / 第十一个 429,没有伪造请求审计 / 事件 | | 页面 | 新增 4 项:原因 / 复核 / 取消不写入,响应不确定同 ID 手动恢复且无自动重试,未配置清晰不可操作,typed 最近登录恢复;原账号 / 财务 11 项保留,最后审计呈现修改后 15 项再次通过 | | 原生 PostgreSQL / Worker | `admin:validate-payments` 一次性数据库与生产 Worker,实际 SDK HTTP 只由受控响应处理;8 并发相同请求一请求 / 一完成 / 一支付事件 / 一发放,竞争租约返回忙碌,完成重放不查询;真实供应商模拟 500、真实完成审计触发器故障、同 ID 修复恢复;查询中实际删除会话 401、25 小时 Cookie typed 403、金额不符未发放、部分 / 全额退款与 shortfall、每分钟 10 次门禁通过。仅独立场景重置一次性限流计数 | | 浏览器 | 隔离 `adminpayments.localhost:19310`,取消复核后重新确认一次。原 pending 购买变 paid,当前受控供应商退款 450 美分、实际扣回 50 积分,事件与账本真实刷新;两条原因审计保存,完成审计显示 grant 未记录 → 已记录、扣回 0 → 50。浏览器结束后脚本核实实际两条审计、退款 / 扣回快照及钱包 / 全账本合计;没有真实 Stripe 交易 | | 响应式 | 320 浅 / 深主题确认与结果 document scrollWidth 320、main 305;1280 桌面审计可读。保存截图并恢复临时视口 | | 回归 | 55 个文件、525 项测试通过,类型 / Oxlint / Oxfmt / 迁移一致性 / 离线配置与预览 / API / 中文教程构建通过。`payments:validate-settlement` 八独立真实连接验事件发放去重、退款 / 任务消费守恒、审核 / 恢复及回滚;`payments:validate` 生产 Worker 验签 / 当前订阅 / Checkout 恢复与真实 SQL 故障重发;`admin:validate` 原账号 / 财务 / 近期登录 / 最后管理员回归通过,供应商均受控 | 一次性数据库、Worker、资源目录实际销毁;没有真实供应商请求、云部署、共享迁移或主管理员提权。主预览与中文教程重建 / 重启,六项服务 200;原账号验证、未封禁普通角色、20 积分、四文件、成功免费任务与一次尝试保留。I6 真实 Stripe 沙箱验收、I7 实际模型、I8 真实邮件 / OAuth / 通行密钥设备仍未标记完成。 **下一阶段:** 正式环境的受控首个管理员初始化与恢复边界,再接续可选 API Key 及完整计划。I9 和整体目标继续进行。 ## 2026-10-10:I9 第五阶段 · 维护计划、明确初始化与灾难恢复 **状态:I9 继续进行。** 接续 `f57f8232`,新增数据库维护者的 `admin:operator plan / apply`:先只读生成可复核计划,再匹配摘要执行角色授权。支持 development / staging / production 显式环境,本次只在自有一次性本地 PostgreSQL 中以 production 模式验收,没有实际远程初始化、邮箱送达、云部署或主账号提权。I0—I16 及全部外部门槛保持,不因维护命令完成而标记完整模板交付。 命令只读取指定私有配置文件,不继承 shell 的 DATABASE\_URL、环境或服务凭据;显式环境和站点必须与文件完全一致。development 只接受 loopback;远程连接要求 TLS、非 pooler origin。检查数据库 owner 权限及 checkout 的全部增量 SQL 摘要 / 时间 / 条数,不执行迁移;配置、权限、历史或输入不符即拒绝。计划文件以 0600 创建,拒绝覆盖 / 符号链接;打印可复核数据库身份、环境、站点、精确账号、操作者、原因、UUID、状态 / 数据库指纹及 30 分钟有效期,不输出连接 URL、密码或服务密钥。 初始化只接受既有已验证、非匿名、未封禁且角色已知的账号;不会选注册第一人、创建人员、补验证、解封、改密码或提升团队所有者。站点已有任何管理员角色或初始化 / 恢复历史时 initialize 拒绝,即使账号后来删除。恢复要求有历史管理,但当前没有任何已验证、非匿名、未封禁的 admin;仍有有效管理员的“忘记密码”应走已有登录恢复或角色管理。恢复明确指定另一账号,不清理旧角色、封禁、会话或审计。 计划与执行使用站内授权的全局事务锁;实际目标与管理员行采用 NO KEY UPDATE,与已有账本外键锁兼容。执行在锁等待后复查摘要、环境 / 站点 / 数据库、有效期和实际授权状态指纹;发生变化必须重新复核,不允许 apply 替换指令。通过 Better Auth 实际适配器写角色并原子保存 user.initialize\_admin / user.recover\_admin;审计失败全部回滚。外部执行者记作 operator:<维护者标识>,保存环境、站点、数据库指纹与真实前后角色,页面展示维护者 / 环境 / 站点;不冒充站内会话身份。维护者名称是其自报标识,实际人员身份仍需凭据管理和外部维护记录核查;审批 SHA256 也不是签名或抗数据库 owner 篡改保证。 UUID 按维护者命名空间去重;同内容重放只返回已处理,不恢复后来的降权、封禁或删除。不同动作 / 目标 / 原因 / 环境复用拒绝。响应丢失可在有效期内沿用原计划手动重试;过期可用同原始指令和 UUID 重新生成计划核对,新的恢复使用新 UUID。旧开发 bootstrap 不能绕过新的维护历史。没有公开 HTTP / 自动启动 / 默认管理员入口,也没有新增环境白名单、依赖或 SQL 迁移;已有 0012 保持不改写。 | 证据 | 实际口径 | | --- | --- | | 服务 / PGlite | 新增 6 项:计划不写入、明确选择与实际适配器 / 外部审计、降权 / 删除后旧重放不恢复;已初始化 / 删除历史与有效管理员恢复门禁;不存在 / 未验证 / 匿名 / 封禁 / 未知角色 / 注入字段拒绝;摘要 / 环境 / 站点 / 数据库 / 有效期 / 状态变化拒绝;真实审计触发器回滚角色并同计划恢复;跨指令 UUID 冲突及旧 bootstrap 历史边界 | | 配置 | 新增 2 项:显式文件缺数据库不能继承 shell、指纹不含凭据且轮换密码不改变身份、不同数据库改变指纹;环境 / 站点、remote development、pooler / 缺 TLS 在连接前拒绝。配置测试没有实际远程连接 | | 实际命令 / PostgreSQL | `admin:validate-operator` 在一次性数据库调用真正 CLI,传入错误 shell 数据库仍使用显式 production 文件;只读计划 / 0600、审批 / 环境 / 数据库 / 迁移记录 / 非 owner 门禁通过。8 并发原计划均成功但一次实际授权 / 审计;删除原管理员后两个恢复候选竞争只提交一个。真实审计 SQL 故障回滚角色,修复后原计划继续;实际适配器读到新角色、后续降权旧重放保持 user | | 实际 Worker / 浏览器 | 独立 adminoperator.localhost:20310,实际注册和 fixture 邮箱验证后,普通会话的 access.allowed=false / 审计 403;实际 CLI recovery 提权后同 Cookie allowed=true / 审计 200。浏览器正常密码登录进入真实管理页,看到 user.recover\_admin、外部维护者、production / 指定站点与历史初始化审计。fixture 中的生产站点字符串不代表该域名部署或远程执行;隔离代理保持原 gzip 响应避免重复解压 | | 页面 / 响应式 | 新增 1 项外部维护审计呈现,原页面流程保留;1280 桌面实际审计截图,320 浅 / 深主题 document 320、main 305,无横向溢出;保存截图并恢复视口 | | 回归 | 57 个文件、534 项测试通过;最后配置 / 命令调整后针对服务 / 配置 / 管理页面的 14 项再次通过。类型、Oxlint、Oxfmt、迁移一致性、离线配置与预览 / API / 中文教程构建通过;db:generate 确认没有结构变化。原 admin:validate 再验账号 / 财务审计、真实并发账本、最近登录 / 降权 / 最后管理员,无外部供应商请求 | 所有一次性数据库、临时数据库角色、Worker、私有配置 / 计划目录实际销毁。主预览重建 / 重启,六项服务 200;原账号仍已验证、未封禁普通用户,20 积分、四文件、成功免费任务与一次尝试保留。中文维护教程覆盖精确配置、首次初始化、真实有效管理员判断、恢复、失败重试与证据边界。 **下一阶段:** 可选用户 API Key 的插件契约、一次性展示 / 命名 / 有效期 / 撤销、文件 / 任务实际作用域与封禁 / 限流门禁。正式数据库维护、真实支付 / 模型 / 邮件 / OAuth / 通行密钥及 I10—I16 继续按完整计划推进。 ## 2026-10-10:I10 · 个人用户密钥、版本接口与原账本 **状态:I10 本地验收通过,默认关闭。** 接续 `44d17d8f`,接入匹配 Better Auth 1.7.4 的独立 API Key 插件、个人设置卡片与 `/api/v1`。完整 I0—I16 目标继续,真实云端 / 第三方调用、既有支付 / AI / 邮件 / OAuth / 通行密钥等外部门槛仍未完成。新增[中文教程](./api-keys),官网与产品的配置 / 内容仍分开。 签发、验证、改名与撤销使用真实插件及原数据库适配器,没有另造可恢复密钥存储。明文仅创建成功时返回,在组件内存中一次性展示;不进入 QueryClient 查询 / 变更缓存、本地存储、管理列表或邮件。完整密钥哈希保存在 `apikey`;Worker 请求日志额外遮蔽误放在 URL 中的 `abk_` 明文,私有插件关闭错误日志,外层仅打印错误类别。不能据此宣称浏览器开发工具或已复制凭据被擦除。 密钥管理只接受实际已验证、非匿名、未封禁账号;变更需同源且实际存在、未过期、创建不足 24 小时的会话。每账号每分钟 30 次管理请求;关闭时返回 404。只开放列表、创建、改名和软撤销,插件其他 HTTP 管理路径关闭;没有权限增补、续期、重新启用或 API Key 模拟会话。创建复核冻结请求 UUID,服务端按所有者先锁账号,实际插件写入与签发记录同事务;失败全部回滚。同 UUID / 参数只签发一次,重放返回 ID 与空明文;改参数重用拒绝。插件清理过期密钥后,独立签发记录继续阻止旧请求重新签发。 外部接口只使用 Bearer,开放个人 files:read / write 和 tasks:read / write;身份来自 Key,不接受客户端所有者或团队选项。每次从新鲜连接检查所有者、封禁 / 验证 / 匿名、过期 / 撤销及真实插件权限;先锁所有者再锁密钥,与签发 / 撤销 / 删除顺序一致。密钥插件限制叠加原每账号文件 / 任务额度、配额和余额;多把 Key 不能扩大业务限额。原有 Turnstile 开启后上传 / 提交 / 重试仍需实际验证。 文件返回原所有权检查后的元数据与真实私有 R2 字节,上传继续检查二进制、类型 / 大小 / 配额,删除保留原任务输入保护。上传尚无请求 UUID 去重,响应丢失先查询列表,不能宣称所有写请求均自动幂等。任务提交使用原 requestKey、输入摘要、报价确认、单笔事务预扣、执行 / 结果 / 取消返还;API 与浏览器同账号重放共用任务 / 账本。单条任务返回 revision,外部重试必须带 If-Match,在原任务锁内比较派发版本;响应丢失后旧重试不能在下一次失败时再授权一次尝试。重试保留原尝试预算和扣费,派发在提交后进行;不把 201 或取消请求当作执行 / 退款完成。 新增迁移 `0013_big_thunderbolts.sql` 只创建密钥与签发记录表、索引和账号外键,全部 0000—0012 SQL / 快照保留。主数据库未应用 0013;主预览保持默认关闭,原有账号与功能不读取新表。当前 checkout 的维护命令仍要求目标数据库完整迁移历史,不能绕过缺失 0013 初始化管理员。没有新增服务凭据、共享迁移、云资源或真实外发。 | 证据 | 实际口径 | | --- | --- | | 服务 / PGlite | 新增 6 项:真实插件哈希 / 一次签发、权限 / 过期 / 撤销 / 封禁 / 验证 / 匿名、关闭 / 未开放端点 / 非会话、严格输入 / UUID 冲突 / 容量 / 实际近期登录、SQL 签发触发器故障原子回滚和过期清理后 UUID 保留、真实插件限流 / 所有权 / Origin | | 路由 / PGlite | 新增 6 项:缺少 Bearer、跨账号文件 / 任务拒绝、实际上传与多个密钥共享账户限额、三积分报价 / API 与浏览器重放一任务 / 一预扣 / 取消返还一次、管理及身份接口不获授权、Turnstile 缺配置不上传 / 扣费、封禁 / 撤销 / 关闭、原任务版本重试及旧请求不重复执行。自己的文件内容下载使用原生 Worker 证据,避免把 Miniflare 与 Bun 跨运行时流当作生产故障 | | 页面 | 新增 5 项:先复核、明文一次性展示并未缓存、响应不确定同 UUID 手动恢复且无自动重试、确认撤销并刷新实际元数据、近期登录退出失败保留会话、关闭时无卡片 / 请求 | | 原生 Worker / PostgreSQL | 真实生产入口、独立完整迁移、真实 Queue / R2:8 并发创建只有一个 ID / 一把明文 / 一条签发记录;文件原字节下载、实际消费者的标准化结果、三积分报价 / 一预扣 / API 与浏览器重放、取消返还一次、版本重试 / 旧请求 409、四并发密钥限额恰好二个成功 / 二个 429、错误作用域 / 过期 / 封禁 / 撤销;URL 误带密钥实际日志已遮蔽,日志扫描无完整明文。按独立限流场景仅重置一次性密钥计数 | | 浏览器 | 隔离 `apikey.localhost:21310` 登录实际测试账号,取消复核不写入,创建后关闭明文,确认撤销,列表实际显示 Revoked;脚本核对该 Browser acceptance 密钥已持久化为关闭。主账号 / 权限不改动 | | 响应式 | 320 浅 / 深主题 document scrollWidth 320、main 305;关闭手机侧栏后列表可读,保存浅 / 深截图与 1280 桌面撤销结果,临时视口已恢复 | | 回归 | 全量 60 个文件、551 项测试通过;最后语义标签 / 过期呈现修改后对应 17 项再次通过,类型 / lint / 格式 / 迁移一致性 / 无新增结构 / 离线配置检查通过。真实管理员维护命令在完整 0013 数据库回归;八独立 PostgreSQL 会话的收费竞争、退款去重和账本守恒回归通过 | 验收数据库、Worker、前端构建及独立资源实际销毁。浏览器暂停首次结束时发现未发送邮件就没有收件箱文件,修复验收夹具的空收件箱初始化后,原生全链路重新通过并完成清理;没有把测试收件箱当作真实发信证据。主预览与中文文档已重建 / 重启,六项服务返回 200;原账号已验证、未封禁且角色仍为普通用户,20 积分、4 文件、成功免费任务和一次尝试保持。浏览器刷新主设置页仍为 Local Preview,密钥卡片保持关闭。 **下一阶段:** I11 真实事件、候补名单与主动营销订阅,区分账号、同意、确认和退订;继续保留全部 I0—I16 与既有外部验收要求。整体目标保持进行中。 ## 2026-10-10:I11 第一阶段 · 明确同意、邮件确认与退订 邮件订阅和候补名单新增独立公开入口,默认关闭。两个用途共用本站状态服务,但同一邮箱分别记录同意文案 / 版本 / 时间 / 来源。匿名申请不要求登录,不创建账号,也不把已有注册当作营销同意。公开成功响应仅表示申请已收到,已有地址、已确认地址和发信失败使用相同响应。 确认 / 退订链接绑定站点、名单、操作、代次和有效期,签名不含邮箱并放在 URL 片段。页面只读取状态,必须主动按按钮才修改;确认和退订幂等,退订后旧确认不可恢复。待确认或退订后重新申请需经过冷却并重新确认。旧的有效退订链接仍可撤回重发后的该名单。偏好页面禁止索引和来源传递,不加载统计脚本,不把链接写入持久化浏览器缓存;操作成功后清除当前地址栏片段,不宣称清除全部浏览器历史。 发送通过已有 React Email / Resend 单一入口。数据库原子领取租约,在事务外发信,按原代次和租约回写;失败保留通用错误码和同一个供应商幂等 key,15 分钟后由现有定时入口尝试,总计最多三次,且限于确认有效期。发送接口接受请求不代表真正送达,也不代表订阅已确认。关闭名单停止新申请 / 确认 / 发信,历史退订保留;营销维护故障不会阻断文件清理和任务恢复。共享 Turnstile 组件从工作台抽到设计包,两个真实用途共用生命周期,工作台仍由自己的主题状态提供外观。 | 证据 | 实际口径 | | --- | --- | | 服务 / 数据库 | 新增 14 项:明确同意和严格输入;记录与账号隔离;重复 / 已确认 / 失败统一响应;名单隔离;只读打开与主动操作;错误目的 / 签名 / 站点 / 过期 / 代次;退订后的新同意;关闭与历史退订;并发发送领取;有限重试 / 过期;传输中退订后的回写;摘要限流、窗口与清理;无效链接在新增表查询前拒绝;请求体与跨源门禁 | | 页面 | 新增 6 项:未勾选不提交、明确发送;失败不假报订阅成功且不自动重试;打开仅检查、按钮确认后清除片段;退订失败可手动再试;无链接不请求 / 已撤回不能确认;同标签页新链接重置已完成状态 | | 现有邮件 SDK | 新增 2 项:通过当前安装的 Resend SDK 向受控 HTTP 响应传递幂等 header 和纯文本 / HTML;供应商拒绝不假报接受。没有真正联系 Resend | | 实际 Worker / PostgreSQL | 独立数据库完整迁移,8 并发申请一条记录 / 一封捕获邮件,8 并发确认幂等;确认与再次申请竞争不降级;两名单隔离、退订后旧确认拒绝;失败发信及两次定时调用只补一次已接受记录;发生人为营销维护故障的定时调用仍先完成文件清理与任务恢复;URL 中误放的能力链接被日志脱敏 | | 浏览器 | 最终构建在 1280 × 920 和 320 × 860 下检查;黑白主题、未勾选禁用、实际申请 / 确认 / 退订、同标签页链接切换、跳过导航不覆盖片段通过。手机实际内容宽度 305,与滚动宽度相同,没有横向溢出 | | 完整回归 | 62 文件、573 项测试通过;类型与 lint 通过。受控供应商测试、本地邮件捕获不替代真实投递验收 | | 中断恢复 | 上一轮中断留下的独立数据库经实际只读核查,浏览器记录已退订、账号 / 账本均为空,再清理其资源;最终验证器正常完成并自行清理。本任务主数据库不参与此类验证 | 0014 只创建营销状态和短期摘要限流表,既有迁移不重写。主库仍为 0012,0013、0014 尚未应用;邮件订阅 / 候补入口与用户密钥保持关闭,主账号没有被授权为管理员。 此阶段提供确认邮件和本站状态,**尚未实现 Resend 联系人 / 主题同步、供应商退订回调、营销广播和工具 / 收入转化事件**。也没有真实外发、云部署或远程迁移。I11 与完整 I0—I16 目标继续推进,维护说明见[邮件订阅与候补名单](./marketing-subscriptions)。 ## 2026-10-10:I11 第二阶段 · 联系人同步与签名退订回调 代码 revision:`b4395ad3`,基线 `e094a6b2`。第一阶段确认 / 退订流程继续保留;本阶段增加可独立关闭的 `marketing.contactsSync`,复用 Resend 邮件密钥,新增独立营销回调 secret 与两个不同主题 UUID。默认关闭,启用时配置组必须完整;远端两个主题默认值必须为 `opt_out`。产品不新增假报供应商成功的提示。 确认、撤回和待同步版本在同一事务提交,两个名单按同一邮箱串行。联系人 HTTP 在事务外运行,60 秒租约保护同步回写,每轮最多三个联系人。失败有有限频率的后台重试,成功后每日复核;原有文件 / 任务维护先执行,可选营销故障分别隔离。创建联系人时先默认退订两个主题,再应用明确确认的用途;未知创建响应先查询现有联系人,不直接重复创建。 全局退订与联系人删除保留持续阻止状态,本站的新确认和供应商加入通知不能自动解除;确认邮件领取也遵守该阻止。单主题退订只影响对应名单。回调先用实际 Resend SDK 在原始正文上验签,再访问营销表;事件 ID 去重,既不保存原始负载 / 签名,也不以供应商加入通知替代本站同意。初始化退订回声通过供应商当前主题状态复核,首次同步未完成时可重投;迟到通知不能覆盖较新的明确确认。 0015 新增同步记录与回调去重表。0016 新增每个用途已成功同步的确认代次,并只为已完成版本回填;避免以两次本地时间的大小判断新同意,另一个名单变化也不覆盖原名单的供应商退订。0014 和既有迁移不重写,两项新迁移仅应用独立测试数据库。 | 证据 | 实际口径 | | --- | --- | | 新增行为测试 | 17 项联系人 / 回调流程:未确认不创建、两个主题隔离、8 并发领取、未知创建响应恢复、旧同步回写保护、配置错误、签名 / 大小 / 关闭门禁、全局阻止、真实主题退订与初始化回声、加入通知不激活 / 不循环、每日复核、旧通知、开发环境阻断外部请求、旧记录接续与关闭申请页后的退订、首次回调重投、租约过期、时钟差异 / 双名单交互和实际增量回填 SQL | | 供应商契约 | 2 项受控 HTTP 测试与已安装 Resend SDK 对比 Contacts / Topics 的方法、路径、负载及授权;请求有中止信号,错误不泄露响应正文;没有真实网络请求 | | 配置 | 新增 1 项:完整变量组、不同主题、启用时缺字段拒绝;默认关闭仍可启动 | | 实际 Worker / PostgreSQL | `validate-marketing.ts --contacts` 通过:8 个并发定时入口只创建一个联系人;两个主题加入 / 退出;写入失败保留待同步并由后续定时恢复;实际 SDK 原始验签、主题退订、重放去重、全局阻止;没有账号创建或真实外发 | | 原有流程回归 | 同一验证器保留 8 并发匿名申请、确认 / 再申请竞争、名单隔离、确认邮件恢复、营销故障隔离与日志能力链接脱敏;独立资源正常清理 | | 完整回归 | 64 文件、593 项测试通过;最终迁移读取路径调整后 17 项联系人测试再次通过;类型、lint、格式、配置诊断、数据库元数据检查与生成一致性通过 | | 本地预览 | 六项服务重建 / 重启并返回 200;主库仍为 0012,账号正常、20 积分、4 个文件和原成功任务保留;无遗留测试数据库 | 本轮没有真实 Resend 账户资源、实际邮箱投递、云部署或远程迁移。联系人处于受控本地验收状态,不能宣称供应商实际广播已经停止。生成站升级和真实外部环境仍按原计划验收,完整 I0—I16 目标保持进行中。下一阶段继续工具开始 / 成功、下载发起与购买确认事件的真实语义、隐私和去重,I11 尚未完成。 ## 2026-10-10:I11 第三阶段 · 公开工具的浏览器使用事件 **状态:I11 继续进行。** 接续 `d238e6c0`,代码提交 `74145b06`。交付默认关闭、需独立明确选择的公开工具使用报告,包含实际处理尝试、返回成功和下载发起。来源始终为浏览器上报,不混入服务端任务、购买、独立用户或收入指标;浏览器点击下载也不宣称文件已经保存。 只在公开 `ToolWorkbench` 的处理器调用和下载操作后发出累计状态;页面浏览、示例、编辑、复制、保存账号及恢复输入不造事件。每次处理有内存中的随机 UUID,重复成功 / 下载和八个并发重放只收敛成一行,晚到请求不降级,不改写第一次状态接收时间。合法累计下载可补齐第一次报告丢失的状态;不同工具 / 同意版本复用同一 UUID、已有记录超过 24 小时更新均拒绝。 `analytics.toolEvents` 独立于原 Cloudflare 公共页 beacon、营销名单和账号,提供启用、同意版本及 1—90 天保留配置。同意初始未勾选,浏览器只保存版本化 yes / no;撤回中止在途请求并丢弃排队请求,跨标签页撤回同步,重新同意不回填旧结果。请求省略账号 Cookie 与来源页面 URL;只接受固定工具与小于等于 1 KiB 的严格字段,拒绝输入、结果、文件名、邮箱、任意 URL 和购买金额。每日轮换的 keyed IP 摘要仅用于独立每分钟限流,不与观测关联,也不称为完全没有元数据。 队列最多四个请求,每个最多等待四秒,无自动重试或待发数据持久化。报告失败、队列满或 UUID 生成不可用不阻塞工具,并显示简短提示。新增 `0017_striped_korath.sql` 仅创建观测 / 限流表,全部旧迁移保留。定时清理每批各最多 500 行;统计表故障在文件 / 任务维护之后隔离,默认关闭不读取新表。关闭模块也会停止自动清理,已收到的匿名事件无法按账号删除,教程如实说明保留、漏报、接收时间和去重窗口。 | 验收 | 结果 | | --- | --- | | 新增定向测试 | API / 真实迁移数据库 8 项 + 产品交互 10 项通过;覆盖关闭且表缺失、来源 / 同意 / 私有字段、并发与累计状态、限流、清理、旧同意、恢复输入、跨标签页撤回、队列上限及故障不阻塞 | | 全套测试 | 66 个文件、611 项通过;未调用真实供应商 | | 类型 / 静态检查 | 类型检查、lint、格式、离线配置诊断通过;再次生成数据库结构无新变化 | | 原生本地环境 | 实际 workerd / PostgreSQL 接收八个并发重试只计一次;累计成功 / 下载、过期清理和统计表故障隔离通过;既有营销确认 / 退订 / 捕获邮件 / 失败恢复仍通过 | | 浏览器 | 未同意的实际处理不落库;同意后处理成功 / 下载发起落在同一次观测;重复下载和撤回不增加处理次数;最终版本有说明链接,截图保存于本地验收证据目录 | | 构建与预览 | 邮件 / 产品公开页 / 工作台 / 官方站预览构建、API 与中文教程构建通过;六项服务重启后 200,新增中文教程可访问 | | 主数据保护 | 主库仍为 0012,两张观测表不存在;默认报告端点返回 404,工具正常可用;普通账号、20 积分、4 文件与原成功任务保留;一次性数据库及 Worker 已清理 | 本轮没有收入归因、统计管理面板、真实邮件投递、共享远程迁移或云部署。完整 I0—I16 目标保持进行中。下一阶段接服务端任务开始 / 成功 / 下载,以及购买确认与前端确认页的统一去重;购买只依据已有服务端结算事实,不能由浏览器自报。配置、口径和操作见[工具使用事件](./tool-usage-events)。 ## 2026-10-10:I11 第四阶段 · 工作台真实事件与积分包确认 **状态:I11 继续进行。** 接续 `88268034`,代码提交 `ba8a0e5d`。新增默认关闭的 `analytics.serverEvents`,个人账号在 Settings 独立勾选并保存后才记录当前同意起点之后的事实;公开工具选择、注册与营销订阅不会授权此模块。选择带版本和 revision,旧标签页不能覆盖刚撤回的选择,重复相同选择不延长起点。失败提示未确认,重新读取可恢复实际状态,不自动重试写入。 实际首次任务租约提交后记录开始,当前结果发布提交后记录成功;入队、取消、失败和重试不增加同一任务的开始 / 成功数量。文件读取成功且进入显式下载路径才记录,普通预览不计,外部 API 附件响应计入;每个文件在保留记录期间只计一次,不证明完整传输或用户已保存。 积分包确认要求原购买已验证发放且匹配原积分账本。付款回调、后台补查与本人购买详情页共用资源 / 事件唯一键,未付款的结账页不能造收入。金额为原始最小货币单位,保留币种和测试 / 真实状态;退款后的净收入、订阅账单支付和归因不在此阶段。前端不提供自报购买金额的接口。 内部记录包含账号 / 资源 ID、固定类型、同意版本与时间,任务含处理器 ID,购买含金额 / 币种 / 测试状态;这是可关联账号的第一方记录,不称匿名,不发送至分析供应商,也不保存输入、结果、文件名、邮箱、URL 或支付原始负载。账号删除级联清理选择和事件,资源删除保留其历史事件至保留清理。 记录在业务提交后尝试,统计表故障不撤销真实任务、结算或附件响应。定时入口在已有维护之后每类最多补查 100 个缺失任务 / 购买事实,只处理当前有效同意区间和保留期内的已有业务记录。撤回、版本变更或重新同意前未落库的旧区间可能漏报,下载没有持久化基础事实不能补造。清理每轮最多 500 条,关闭模块也停止清理,期限不是准时删除保证。 0018 只新增最新选择与事件表、索引和账号外键,旧迁移保留。新表只应用 PGlite 和验证器自己创建的独立 PostgreSQL;主库继续保留 0012。验证器通过本地运行时的配置别名启用独立环境,不修改主配置;供应商 HTTP 受控、其他外部请求拒绝、邮件捕获。 | 验收 | 结果 | | --- | --- | | 服务端新增行为 | 8 项:关闭且表缺失仍完成任务;不回填同意前事实;实际领取 / 发布与重试去重;撤回 / 版本变化;并发选择版本保护和账号 / Origin 边界;文件所有权与八并发去重;统计故障不撤销任务、定时补齐;过期基础事实不反复创建与账号级联删除 | | 支付行为 | 现有支付测试新增 1 项:已安装 SDK 签名回调,统计故障仍发放,恢复后一条确认;重复 / 同源另一事件与详情页读取仍为一条记录 / 一次积分发放,待付状态不记录 | | 工作台页面 | 新增 3 项:勾选不自动提交、显式保存 / 撤回、失败不假报确认且重新加载可恢复未保存状态、默认关闭不请求;最终恢复交互调整后这 3 项再次通过 | | 全套回归 | 68 个文件、623 项通过;类型、lint、格式与离线配置诊断通过;迁移元数据检查通过,再次生成没有结构变化 | | 原生 Worker / PostgreSQL | 真实 Queue / R2 执行与结果发布、普通预览不造下载、八并发实际附件请求一条事件;人为观测表故障后任务仍成功,恢复后真实定时入口补齐;实际 SDK 签名付款回调、详情刷新和撤回通过。两个任务四条事件、一个文件下载事件、一个积分包确认,共六条;原钱包只发放一次 | | 浏览器 | 独立测试账号实际登录,在 Settings 保存同意再保存撤回;验证器复核最终 allowed=false、revision=4,页面浏览没有新增事件。保存桌面与手机证据,320 像素下 document scrollWidth=320、main=305,临时视口已恢复 | | 构建与主预览 | 邮件 / 产品公开页 / 工作台 / 官方站预览构建、API 与中文教程构建通过;六项服务重启后 200,中文新教程实际可访问;浏览器原任务仍 Completed、20 积分,主设置无新增统计卡片 | | 主数据与清理 | 主库 13 条迁移、普通已验证账号、20 积分、4 文件、原成功任务与一次尝试保留;两张服务端新表和浏览器观测表均不存在;独立数据库 / Worker / Queue / R2 / 前端资源已销毁,无遗留验证数据库 | 本阶段没有真实供应商交易、真实邮件投递、远程迁移或云部署。配置、隐私、故障口径和验证方法见[工作台使用与购买事件](./server-activity-events)。下一阶段继续订阅账单真实支付事件与 I11 外部验收;完整 I0—I16 目标保持进行中。 ## 2026-10-11:I11 第五阶段 · 个人订阅账单付款与漏回调恢复 **状态:I11 与完整 I0—I16 目标继续进行。** 接续 `81566d08`,代码提交 `e2f7f7eb`。已有订阅状态管理保持由原插件维护;本轮在同一原始正文验签边界增加 `invoice.paid` 的可选观测。签名成功只提供核查线索,不直接信任回调中的金额,也不把订阅激活 / 试用当作购买。 服务端复查当前已付账单、正数金额、币种、模式、余额与付款时间,再分页核查完整已付分配记录、实际成功 PaymentIntent 或已捕获 Charge。账号、订阅和客户必须相符,记录仍需当前有效个人同意。同一付款来源的分配合计不能超过其收款,分配总额须等于账单实际付款额;零元、余额抵扣和线下 PaymentRecord 不造新收款。团队账单不归到某个成员个人账号,团队付款观测仍保留为后续范围。 唯一键为供应商账单 ID 加固定类型 `subscription_payment_confirmed`,同一订阅的另一张续费账单可新增一条。回调与后台恢复共用唯一键;前端确认 / 状态页只读原订阅状态,不扫描供应商历史、不发送购买上报,刷新不增加事件。记录只含账号 / 账单 ID、类型、同意版本、实际付款时间、最小货币单位金额、币种和测试 / 真实状态;不保留发票正文、邮箱、描述、付款密钥或原回调。这是原始付款确认,不代表退款后的净收入、MRR 或留存。 漏回调恢复复用现有定时入口,每轮最多三个个人账号、每账号两次尝试至少相隔 15 分钟、每次最多读取 1,000 张已付账单和每张最多 100 个付款分配。按实际 paid\_at 与当前同意起点 / 保留期筛选,创建很早但近期付款的账单仍可恢复。失败也会让出后续账号的检查位置;分页重复、超限和部分数据不假报完整恢复。已有部分真实记录保留并去重。 恢复检查原子领取统计自己的检查时间,不占用用户的账单操作租约,也没有完成后回写旧检查时间的路径。供应商历史请求暂停时,真实账单管理仍可领取自己的操作租约;普通工作台读取不等待统计供应商。过程中撤回或版本变化,写入再次检查最新选择。有效签名在可选观测故障时仍应答成功,后续定时恢复补查,不撤销已付款或订阅权益。 新增用途同步更新工作台文案与隐私草稿,默认同意版本升级为 `2026-10-10.2`,旧同意不会自动涵盖个人订阅付款。0019 只给活动选择新增 `invoice_checked_at` 和索引,已应用的旧迁移不改写;当前启用前需完整升级至 0019 并重建 API / 工作台。主库仍为 0012,默认统计关闭。 | 验收 | 结果 | | --- | --- | | 新增行为测试 | 11 项真实迁移数据库 / SDK 路由测试:八并发与重复事件;关闭 / 缺表、原始验签;零元 / 待付 / 抵扣 / 线下记录;当前资源所有权、币种 / 模式 / 实际收款;重复分配与不完整分页;旧式 Charge 与续费;传输中撤回 / 同意前事实 / 团队隔离;很早创建的迟付恢复;统计故障与间隔;失败账号轮换;暂停扫描不占用用户账单操作租约 | | 读取边界 | 1 项工作台账单路由测试:已开启统计、已有同意及订阅时,普通状态读取不访问供应商,也不生成付款记录 | | 回归 | 最终 69 文件、635 项通过;类型、lint、格式、离线配置诊断通过;迁移检查和重新生成无结构差异 | | 原生 Worker / PostgreSQL | 扩展已有验证器 `--invoices`,实际 SDK 签名、零元排除、八并发回调、真实前端所用状态读取、两页付款分配、观测表故障后成功应答及真实定时续费恢复通过;最后版本再次完整运行并清理。两个任务四条、文件下载一条、积分包一条、两张已付订阅账单两条,共八条;订阅仍 trialing、钱包 120,没有借统计改写权益或加积分 | | 浏览器 | 独立真实账号登录,在更新后的卡片保存同意再保存撤回,验证器核对 allowed=false / revision=4,事件仍八条。保存桌面 / 手机截图,320 像素文档宽度 320、main 305;临时视口恢复、验收页关闭 | | 构建与主预览 | 邮件 / 产品 / 工作台 / 官网预览、API 和中文教程构建通过;六项服务重启后 200,中文服务端事件教程可访问。浏览器主任务仍 Completed / 一次尝试、20 积分 | | 主数据与清理 | 主库仍 13 条迁移至 0012、已验证普通账号、20 积分、4 文件与原任务保留;新增观测表不存在;本轮独立 PostgreSQL、Worker、Queue、R2 与前端资源实际销毁 | 本轮没有真实 Stripe 交易、真实邮件投递、共享 / 远程迁移或云部署。维护教程见[工作台使用与购买事件](./server-activity-events)。I11 的团队付款观测与外部验收继续保留;下一阶段按完整计划推进 I12 用户任务通知与站主运营通知,不把这些本地证据当作完整模板已交付。 ## 2026-10-11:I12 任务完成与失败邮件 **状态:I12 进行中,第一阶段任务邮件已本地验收。** 接续 `3bca663a`,功能提交 `3e380133`。新增默认关闭的任务邮件配置、已验证账号选择、任务终态投递表、定时补偿与本人投递列表。站主运营 Webhook、支付事件通知及真实外部发信仍按完整计划推进,不将本阶段标为整个 I12 完成。 复用原任务队列、终态、每 15 分钟维护及 `sendEmail` SDK 边界,不创建第二个通知任务引擎。最多扫描 20 个未记录终态,任务 ID / 执行租约版本 / 结果的数据库唯一约束去重;取消、中间重试、选择前结果和过期结果不入队。发送前重新检查最新选择、已验证邮箱、封禁、执行版本和配置。邮箱与接收配置以认证密钥派生摘要绑定,客户端不能提供接收人、所有者或外发 URL。 选择带版本与 revision,旧标签页不能覆盖较新的撤回。重复有效选择不延长起点;更换邮箱需要新选择,旧通知不改投,再次启用不补发旧结果。已经开始外发的请求不能因撤回而收回。状态与失败代码可在本人设置页查看,不存供应商原始错误、邮箱或邮件正文;查询和变更限额返回明确限频响应。 发送最多三次,每次两分钟租约,十秒请求超时,固定幂等键及相同 HTML / 纯文本内容。仅在首次尝试后 23 小时内重试,超过范围或次数保留接收未确认状态;迟到响应不能覆盖新租约。有效供应商回执才记为接收成功,不把捕获回执或供应商接收视为收件箱送达。邮件只给出任务 ID、状态和本站登录后的任务详情 / 设置链接,不能绕过原文件所有权或延长结果保留期。 新增增量迁移 0020,两张表已在 PGlite 和独立 loopback PostgreSQL 验收;应用后的旧迁移未修改。投递按事件时间进入清理资格,每轮最多删 100 条,列表隐藏超过保留期的记录,旧结果不能在清理后重新入队。账号删除级联清除选择与投递,任务删除清除其投递;财务和管理审计的既有规则保持独立。 | 证据 | 实际范围 | | --- | --- | | 新增行为测试 | 37 项:关闭且缺表、验证邮箱、版本 / revision 冲突、重放 / 并发、终态与时间范围、撤回 / 重开、邮箱 / 封禁 / 密钥 / 配置变化、任务重试替换、有限重试与相同内容、23 小时边界、过期租约、迟到回执、所有权 / Origin / 接收人拒绝、扫描公平性、保留 / 级联、实际处理器结果、SDK 超时信号 / 幂等请求、无有效回执、限频,以及前端保存 / 恢复 / 账号切换 | | 全量测试 | 71 个文件、672 项通过 | | 原生本地验收 | 实际 Worker / Queue / R2 产出成功与失败任务;定时处理捕获两个最小邮件,三轮重放不增加;真实捕获文件故障不撤回任务成功,恢复后同键第二次尝试接收;撤回 / 再开不补发第四个结果,原结果仍可私有下载;临时数据库与全部资源成功清理 | | 浏览器 | 独立专用工作台已显示成功 / 失败及重试后的投递记录,实际撤回保存至 revision 4;桌面与 320 像素浅 / 深主题检查,本地截图 `task-notifications-light.jpg` / `task-notifications-dark.jpg` / `task-notifications-mobile-light.jpg` / `task-notifications-mobile-dark.jpg`;浏览器操作未新增邮件 | | 检查与构建 | 类型、lint、格式、配置诊断、迁移一致性、完整预览构建、API / 邮件 / 中文文档构建通过;公共页与官网 Astro 检查均零错误 / 警告 / 提示 | | 正常预览 | 六个本地服务重启后均 200;主库仍 0012 / 13 条迁移,两张通知表不存在,任务邮件、统计及其他可选模块保持关闭;普通账号仍 20 积分、4 个文件、原任务成功且一次执行,没有残留通知验收数据库 | 本轮只捕获合成邮件和受控 SDK HTTP,没有真实供应商发信、支付、模型调用、远程迁移或云部署。[中文任务通知教程](./task-notifications)、配置、环境、隐私草稿和模块状态已同步。主库新增迁移与可选功能启用仍需相应授权,不将临时库证据当作正常预览已经开通邮件。 下一阶段继续 I12 站主运营渠道、任务 / 支付事实的独立通知及失败查看,接收目标仅由服务端配置;I11 团队付款观测与外部验收、I13—I16 的既有范围继续保留。 ## 2026-10-11:I12 站主运营频道与付款通知 **状态:I12 第二阶段已本地验收,完整 I0—I16 目标继续进行。** 接续 `ff8f0ff5`,功能提交 `a10794b6`。新增默认关闭的 Discord 站主渠道、明确事件起点、任务 / 积分包 / 订阅账单投递、有限尝试和管理员只读列表。真实频道、真实邮箱、云端绑定与维护仍保留外部验收,不将捕获回执当成真实送达。 站主消息与个人任务邮件分别配置,不借用营销、任务邮件或活动统计的个人选择。目标仅来自服务端秘密变量,限定标准 Discord 地址,禁用自动提及,不跟随跳转。消息仅含固定类型、资源编号、付款最小货币单位 / 币种 / 模式和管理员入口;没有姓名、邮箱、团队名、输入、输出、文件名、附件、秘密地址或供应商原始错误。目标、起点、环境、认证密钥与 Origin 绑定为摘要,配置变化后旧请求跳过,不改投新频道。 任务从真实已提交终态扫描;积分包必须同时存在原已付购买和对应实际发放流水。订阅账单从原始正文 SDK 验签入口重新核查当前账单、本站客户 / 订阅、实际成功收款及完整分配,个人与团队都可形成运营消息;团队付款不归到成员个人统计。将已有付款核对提取为共享逻辑,统计与运营分别保留授权、启用和时间边界,没有新建一份订阅状态或发放路径。 定时恢复复用原维护入口:每轮最多 20 个任务、20 个积分购买,投递最多 10 条。订阅客户另有原子检查时间,每轮最多三个有效客户,至少间隔 15 分钟,失败仍参与轮换;最多 1,000 张历史账单 / 每张 100 个付款分配,按实际付款时间筛选,早创建晚付款仍可恢复。检查不占用账单操作租约或个人统计检查时间。回调遇到可选通知存储故障仍确认支付,后续恢复使用同一事件键。 发送租约两分钟、请求十秒超时、最多三次。只有明确限频才自动重试,等待至少 15 分钟并尊重供应商更长要求;超过一天的要求停止,不提前截短重试。没有请求幂等键时,断网、超时、服务错误、无有效消息编号和过期租约保存接收未确认并停止,迟到回执不能覆盖;页面没有强制重发入口。管理员列表每页 20 条并检查最新角色 / 验证 / 封禁,撤权后的旧会话被拒绝,读取限频返回明确响应。 0021 新增最小事件投递与客户轮换检查表;未改写 0000—0020 SQL 或快照。投递按事件时间、检查记录按最后检查时间分别清理,每轮最多 100 条,列表隐藏范围外事件;关闭与积压可能延迟物理删除。这些运营记录不存个人账号编号,按自身保留期清理,不替代财务或管理员审计。主库没有应用 0020 / 0021,两个通知模块默认关闭。 | 证据 | 实际范围 | | --- | --- | | 新增行为测试 | 34 项实际迁移数据库 / SDK 受控请求:关闭且缺表、明确起点 / 目标、任务与已发放购买、时间 / 终态范围、重复 / 并发、地址与配置变化、执行版本替换、限频等待 / 停止、断网 / 服务错误 / 无回执 / 跳转、租约过期与迟到回写、并发发送、开发捕获及非开发绑定边界、管理员撤权 / 限频 / 严格输入、清理、个人 / 团队账单、零元排除、晚付恢复、模式不符、扫描故障与过期检查清理 | | 工作台测试 | 新增 5 项:默认关闭不请求、金额单位 / 测试模式及不确定状态、失败读取刷新恢复、翻页与管理员切换不留旧数据、回执与限频状态;既有实际运行器测试扩展站主捕获、超时信号与无重定向 | | 全套回归 | 73 个文件、711 项通过;类型、lint、格式、离线配置诊断、迁移元数据检查通过,重新生成没有结构变化 | | 原生站主验收 | 独立 PostgreSQL、实际 Worker / Queue / R2;两个终态、已提交积分包、两张付款账单共五条捕获,重放与八并发签名账单不增加;零元排除,两页真实 SDK 付款分配受控核查;通知表故障仍应答支付,定时恢复早创建晚付款续费;第六条捕获故障保留未确认一次且不重发,原成功文件仍私有下载;撤回临时管理员后旧 Cookie 读取为 403 | | 付款统计原生回归 | 原有 `validate-server-activity.ts --invoices` 再次通过实际任务 / 下载 / 积分包 / 账单签名、分页、观测故障和定时恢复;仍八条统计,订阅 trialing、钱包 120,独立临时资源删除 | | 浏览器 | 独立合成管理员登录、刷新与直接页面重载,状态和最小货币单位可见,读页面未增加消息;桌面与 320 像素浅 / 深主题,文档和主内容宽度均 320,无横向溢出;保存 `operator-notifications-light.jpg` / `operator-notifications-dark.jpg` / 手机两主题截图,临时视口恢复、验收页关闭 | | 原生兼容修复 | 实际 workerd 拒绝 `redirect:error`,改为手动响应检查,继续禁止跟随跳转;原生运行器测试覆盖该路径,最终独立 Worker 验收通过 | | 构建 | 邮件 / 产品公开页 / 工作台 / 官网预览、API 与中文教程构建通过;两个 Astro 检查均零错误 / 警告 / 提示。构建保留既有空案例集合与体积提示,未把空集合称为已上线独立案例 | | 正常预览与清理 | 六项服务均运行且 200;主库仍 0012 / 13 条迁移、已验证普通账号、20 积分、4 文件与原成功任务 / 一次执行;新增个人和站主通知表均不存在,所有本轮临时数据库 / Worker / Queue / R2 / 专用工作台已删除。浏览器原任务仍显示完成和结果文件 | [中文站主运营教程](./operator-notifications)、网站配置、环境变量、模块总览、路线和条件隐私草稿已同步。只使用受控 SDK HTTP 与本地捕获,没有真实 Discord / 邮件 / 支付 / 模型请求、远程迁移或云部署。I11 的团队活动统计与外部门槛保持;后续按完整计划继续 I13 多语言与品牌素材,并保留 I14—I16 和所有外部验收范围。 ## 2026-10-11:I13 品牌素材与复制隔离 **状态:I13 第一阶段已本地验收,完整 I0—I16 目标继续进行。** 接续 `83850ab1`,功能提交 `fba4bd97`。本阶段统一产品品牌配置及实际消费者,尚未交付第二语言译文、语言路由和多语言 SEO;不把修改 HTML `lang` 算作多语言支持。 新增 `brand` 的短标记、浅深主题图片、浏览器图标、触屏图标和邮件图片。网页页头 / 页脚、工作台导航、登录表单、安装清单与六种实际邮件使用同一配置。公开页 OG 图片也纳入素材预检;所有素材限定在两个产品 public 目录的 `/brand/`,检查路径、文件存在和格式签名。安装清单由配置生成,保留现有主题切换、焦点和默认配色;官网继续读取独立品牌配置。 工厂按源码节点的实际范围更新身份配置,清空新复制目录的保留品牌目录,生成新站自己的 SVG 首字母图标,重置图片、OG、联系邮箱和社交链接;源站不修改。积分包目录仍清空,资源和 Cookie 隔离规则保持。实际新站验收暴露配置检查提前加载未构建邮件的问题,已把通知策略抽出到真实运行与离线检查共同使用的模块,让 `bun install` 后能先检查配置再构建。 邮件移除上游内联图标和不存在的通用退订地址。营销确认与任务通知保留各自的撤回 / 设置入口;可选邮件图片由当前产品 origin 生成绝对地址,默认使用文字品牌。任务邮件内容版本改为 `task-mail-v2`,品牌或邮件图片改变会使未投递记录以 `CONFIG_CHANGED` 停止;已有接受记录不会重发。本阶段没有新增迁移。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 74 文件、725 项通过;新覆盖非法素材路径、实际文件格式、派生清单、两次生成、六种邮件品牌及未发任务邮件内容变更 | | 类型、静态诊断、格式 | TypeScript、Oxlint、Oxfmt、产品 Astro 与独立官网诊断、离线配置检查通过;产品 / 官网 / 邮件构建通过 | | 实际任务邮件 Worker | `bun --cwd apps/api validate:notifications` 通过;真实本地 Worker、队列、R2、临时 PostgreSQL 和邮件捕获,退出与重新同意不回放旧结果,保留结果可私下下载 | | 两个实际生成站 | Paper Studio 与 Quiet Workshop 分别安装依赖、检查配置、构建并启动独立 Worker / 网页 / App / 入口;使用各自一次性本地数据库和认证 secret;本轮工作区含未提交改动,未当作发布产品 | | 品牌素材与邮件 | 第一站实际配置浅深 SVG、触屏 PNG、OG PNG 和邮件 PNG;两个清单一致、实际图标可读取、公开分享 origin 正确;两个站注册后分别发 OTP 并核对捕获 HTML / text 的当前品牌,未真实发信 | | 浏览器 | 桌面 1366、手机 390 / 320 像素的公共页及登录页;浅深图片加载与切换正确,未横向溢出;登录链接键盘焦点可见、直接刷新可用;第二站没有旧站联系方式、社交链接或图片 | 截图保存在本任务 `.local/evidence/i13-brand-*` 与 `i13-generated-second-*`;生成、构建、配置与捕获断言日志在 `.local/i13-*`。全仓一次复跑曾出现 Miniflare 同步 R2 代理断言失败;单独复查相关文件与生成器共 21 项通过,停止临时站点后的完整复跑 725 项通过。没有据此修改产品清理逻辑,也未确认该异常的根因。 两个生成站的四个服务均已停止,一次性数据库、复制源码及私密本地配置已移除,仅保留本任务验收日志和截图。主预览恢复六个服务后核对:普通预览账号仍为普通角色、20 积分、4 个文件,原成功任务仍为一次尝试;主数据库仍为 0012 / 13 条迁移,可选营销 / 观测 / 邮件 / 运营模块保持关闭,没有将验收用 0021 结构应用到主数据库。 [中文品牌素材教程](./brand-assets)、网站配置、模块状态、能力清单与复制新站说明已同步。真实邮箱客户端显示、生产素材缓存与分享抓取仍需外部验收;素材预检不是完整图片解码或 SVG 安全审计。下一阶段继续共享语言配置、真实第二语言内容、Astro / 工作台路径、认证回跳、邮件语言、canonical / hreflang / sitemap;I11 团队观测、I14—I16 与全部外部门槛保持原范围。 ## 2026-10-11:I13 公开页语言与静态发布 **状态:I13 第二阶段已本地验收,完整 I0—I16 目标继续进行。** 接续品牌记录 `77a95948`,功能提交 `a602bf31`。当前新增英文 / 西班牙语的首页、功能页和价格页三组公开页面;工具交互、博客全文、账号 / 工作台、认证回跳、任务及邮件语言仍待后续阶段,未标记整个 I13 完成。 语言列表及产品首页元数据、计划说明集中在 `websiteConfig.i18n`,默认仍为 `["en"]`。英文无前缀,西班牙语固定 `/es`;开启后需完整实际文案,配置诊断与 Astro 构建共用校验,重复语言、默认语言变化、空字段或错误列表均拒绝。正文和界面字典独立维护,两种语言共用三份页面组件;品牌、价格、团队人数、实际功能与工具目的地继续读取原配置。译文由 Codex 根据原英文内容生成,尚未经过母语编辑或产品负责人人工审校。 仅登记已交付的三类页面:静态西班牙语入口由该登记生成,语言菜单返回当前页的其他实际版本。未翻译目标保留真实英文路径,卡片与页脚标注英文,顶部使用提示及辅助标签;西班牙语页说明账号、工具和博客当前使用英文,文章摘录明确 `lang="en"`。没有 `/es/login`、`/es/tools`、空文章或 `/es/api` 回退,也没有改动会话 / 团队 / 授权规则。工作台与 API 路由继续保持原门禁。 已启用的成对页面具有各自 canonical、双向 `en` / `es` 与英文 `x-default`,sitemap 使用相同语言版本集合和实际 public origin。实际构建曾发现 Astro 的尾斜杠 canonical 与语言链接不一致,已统一正常公开路径;英文首页 `/` 与西班牙语首页 `/es/` 保留首页形式。缺译文、未发布和私有路径不生成语言替代链接,未创建四国重复页面或浏览器自动语言跳转。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 最终 76 个文件、737 项通过;新增 12 项覆盖启用 / 缺内容、配置与路由错误、发布版本集合、英文回退及实际功能 / 工具摘要;最终窄范围语言 / 生成器 / 边缘路由共 20 项通过 | | 配置及静态检查 | TypeScript、Oxlint、Oxfmt、产品 Astro 检查通过;独立官网 Astro 检查通过;正常离线诊断通过,错误语言列表实际运行诊断仍返回字段错误,不中断错误报告 | | 实际生成站 | Idioma Studio 从当前源码生成,安装依赖后先离线诊断,再分别构建英文单语言和英 / 西双语言;独立本地数据库、认证 secret、实际 Worker / 网页 / App / 入口,未当作已发布案例 | | 构建与链接 | 英文构建无西班牙语页 / 语言链接 / 菜单;双语言三组 HTML 与 sitemap 的 canonical / 全地址语言版本一致,23 个实际内部目标为 200;未译 `/es/tools` / `/es/blog` / `/es/login` / `/es/api/auth/get-session` 为 404,原根路径会话 API 为 200 | | 浏览器 | 西班牙语功能页桌面浅深主题、同页双向切换、刷新、键盘打开 / Escape 关闭及焦点;390 / 320 像素功能页、320 像素首页 / 价格页、手机菜单切换均无横向溢出;英文账号回退进入根 `/login`,页面 `lang=en`,刷新正常 | | 构建产物 | 邮件 / 产品公开页 / 工作台 / 独立官网预览及中文教程构建通过;保留既有空案例集合和构建体积提示,没有将演示站标为已上线案例 | 验收截图位于本任务 `.local/evidence/i13-language-*`;单 / 双语言构建、HTML、sitemap、内部请求断言与诊断日志位于 `.local/i13-language-*` / `i13-languages-*`。独立生成站服务已停止,一次性数据库、复制源码与私密配置已删除,保留验收证据。 主预览六个服务更新后均运行且为 200;公开页默认英文、canonical 指向当前入口,未发布西班牙语页或显示关闭的语言菜单,新中文教程可访问。主库仍为 0012 / 13 条迁移,可选新增表不存在,普通账号保持已验证、20 积分、4 文件,原成功任务仍一次执行;浏览器直接刷新原任务后显示 Completed、1 / 3 和原私有结果文件。 [中文产品语言教程](./languages)、网站配置、能力清单、模块总览与复制新站说明已同步。接下来补完整账号 / 工作台与邮件语言路径;人工审校、外部抓取、I11 团队观测、I14—I16 和全部外部验收要求保持。当前阶段没有数据库迁移、供应商请求、真实发信或云部署。 ## 2026-10-11:I13 账号入口与任务语言路径 **状态:I13 第三阶段已本地验收,完整 I0—I16 目标继续进行。** 接续公开页记录 `04eb64ba`,功能提交 `db5b27fd`。新增登录、注册、找回 / 重置密码、工作台首页和任务列表 / 详情的英文与西班牙语界面;其余工作台、工具 / 博客和实际邮件正文仍待后续阶段,不标记整个 I13 完成。 工作台保留一份生成路由树和业务组件,用 TanStack Router 双向 URL 改写处理实际已翻译路径。公开页面登记移入共享核心,工作台翻译路径由边缘 Worker、本地网关、公开账号入口和工作台共同消费;已关闭语言及未知前缀不发布。API、私有文件内容、静态资产继续使用根路径,不改变会话、活动团队、所有权和授权规则,也不增加迁移、语言 Cookie 或偏好表。 真实字典覆盖认证、工作台外壳、首页、任务状态 / 费用确认、错误恢复和文件预览。通用认证布局、Turnstile 和对话框只接收文案参数,不依赖产品配置;既有主题同步组件同时更新文档语言。错误根据稳定代码翻译,文件内容请求保留 REST 错误代码,未知错误用当前语言提示;用户 / 团队 / 文件名、输入结果、任务 ID、报价与请求标识保持业务原值。 直接访问西班牙语任务会进入同语言登录,登录或已登录回跳保留安全的原任务路径。验证、社交登录回调和重置入口保留语言,切换语言保留查询参数 / 片段并同步 `returnTo`。未翻译的文件库、历史、积分、成员、设置等明确标记英文并重新加载英文文档;其页面不显示指向同一英文 URL 的虚假西班牙语选择器。实际账号邮件仍是英文,西班牙语认证页明确告知;私有页维持 `noindex`,不进入公开 sitemap / hreflang 集合。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 79 文件、747 项通过;新增 10 项覆盖真实生成路由树的未登录 / 已登录回跳、实际 Hono Worker 分流、未知 / 关闭路径、错误代码、任务费用确认与丢失响应的同请求重试;最后窄范围 4 文件、17 项通过 | | 类型、静态诊断、格式 | TypeScript、Oxlint、Oxfmt、产品 Astro 81 文件、官网 Astro 49 文件均通过;离线配置检查通过,产品 / 官网 / 邮件及中文教程构建通过 | | 实际新站 | Idioma Workspace 从当前工作区生成,安装后分别构建 / 启动英文单语言及英 / 西双语言;独立认证 secret、一次性 loopback PostgreSQL、实际 Worker / R2 / Queue;未当作已发布独立案例 | | 实际入口与资源 | 单语言的西班牙语公开 / 账号 / 工作台入口均 404;双语言六组工作台入口及根 `/_app/` 资产可读取,公开页实际链接到 `/es/login` 与 `/es/dashboard`;`/es/api`、`/es/_app`、未翻译文件库 / 设置和非完整路径段均未冒充可用版本 | | 原生业务 | 三个合成账号,注册 / 验证码 / 验证与重置邮件捕获 / 实际重置 / 密码登录;回调保留西班牙语。原生提交同请求两次返回同任务,实际只执行一次,私有结果为正确字节;其他账号 404、匿名 401,读取和切换语言不改变会话及活动团队 | | 浏览器 | 保护任务 → 西班牙语验证码登录 → 同任务;英 / 西双向切换、直接刷新、活动团队、私有预览、工作台和英文回退通过;另外通过西班牙语表单提交第二个免费任务,完成且只执行一次。已验证账号密码登录、退出、缺重置令牌恢复、键盘焦点、桌面浅深主题及 390 / 320 像素无横向溢出通过 | 首次浏览器密码登录使用了经验证码确认的未验证邮箱账号,认证库既有 `revokeUnprovenAccountAccess` 行为已撤销其未证明的原密码凭据;核对后使用验证码验收原任务,另以已经邮件验证并完成原生重置的合成账号验收密码登录,没有改变认证安全规则。真实外部 OAuth、Turnstile、邮件投递与人工译文审校仍待原外部验收,不把受控本地捕获称为真实发送。 截图位于本任务 `.local/evidence/i13-app-language-*`;单 / 双语言构建、实际 Worker、邮件、浏览器、清理和主预览检查证据位于 `.local/i13-app-language-*`。两个成功任务分别来自原生请求和浏览器操作,一次性站点四个服务、数据库、复制源码及私密配置均已移除,保留本任务验收证据。 主预览继续默认英文和已授权的 0012 / 13 条迁移;可选新模块不启用。原普通账号、20 积分、4 文件与原成功任务 / 一次尝试保持;新西班牙语路径默认不开放。[中文产品语言教程](./languages)、网站配置、模块、能力清单与开发计划已同步。后续继续文件库 / 历史 / 积分 / 团队 / 设置 / 管理后台、工具 / 博客及邮件正文的语言路径;I11 团队观测、I14—I16 和全部外部验收范围保持。 ## 2026-10-11:I13 文件、历史与个人购买语言 **状态:I13 第四阶段已本地验收,完整 I0—I16 目标继续进行。** 接续账号与任务记录 `ccf81a0e`,功能提交 `5a4f5640`。新增文件库、保存历史、积分账本、购买列表与详情的英文 / 西班牙语界面;成员 / 邀请、设置 / 管理后台、公开工具 / 博客、实际邮件正文和托管支付回站语言仍在后续范围,未标记整个 I13 完成。 继续使用同一份业务页面、生成路由树和类型对齐的字典,扩展共享已翻译路径表。文件、历史及积分的站内链接不再绕回英文文档;直接加载、刷新和双向语言切换保留文件 / 购买 ID、账号与活动团队。API、私有文件内容和静态资源使用根路径,未知、关闭或未翻译的西班牙语路径仍为 404。 文件库覆盖实际上传、进度、存储确认、配额、状态、预览 / 下载、删除确认与异常。响应丢失增加稳定的客户端错误代码,提示先刷新核对;传输进度不等于服务端保存,删除失败保留确认框和占用。历史预览与导出保留原始结果。积分账本按稳定业务类型显示译文,原始备注可展开查看;文件名、工具 / 积分包配置名称、输入、内容、金额、币种、数量、报价版本及请求标识不翻译,日期和显示金额按当前语言格式化。 购买的待确认、退款 / 争议、余额缺口、原请求重试及稳定错误代码均已有实际译文;页面返回或刷新不发放积分,仍由服务端决定状态和暂停。购买金额格式化移到实际 UI 消费层,查询模块保持数据职责。托管支付供应商及返回地址仍使用既有英文路径,本轮未接通真实支付、未调用供应商。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 79 文件、750 项通过;新增 3 项验证西班牙语删除失败、丢失购买响应后的原报价 / 原请求重试及待确认到退款审查状态。最后窄范围 7 文件、25 项通过 | | 实际边缘路由 | 旧测试依赖导入语句写法,首次全仓失败 2 项;将测试移到实际 Worker 所属工作区并改为调用 Hono Worker,验证全部生成路由、完整路径段和公开页面分流。类型与最终全仓复验通过,没有改变生产分流行为 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 82 文件、独立官网 49 文件均零错误 / 警告 / 提示;产品公开页、工作台、邮件、官网和中文教程构建通过。保留既有空案例集合及产物体积提示 | | 实际生成站 | Library Workspace 从当前工作区生成,安装依赖后分别检查 / 构建 / 启动英文单语言及英 / 西双语言;独立认证 secret、一次性 loopback PostgreSQL、实际 Worker / R2 / Queue。单语言西班牙语入口 404,双语言新增私有页面可直接加载且保持 noindex | | 原生业务 | 两个合成账号,真实邮件验证捕获与密码登录、活动团队;真实 UTF-8 / JSON 上传和逐字节下载,错误图片不占配额;他人 404 / 匿名 401;保存历史同请求返回同一条记录。积分仍 20,语言读取不改变会话和活动团队 | | 购买详情边界 | 仅在独立数据库构造一条待确认测试记录,实际所有者可读取、他人 / 匿名拒绝;没有 checkout URL、发放时间或积分增量,目录支付关闭。此项是私有状态展示证据,不是实际下单或支付证据 | | 浏览器 | 西班牙语保护入口 → 密码登录 → 文件库;上传 / 确认、预览、原件下载、永久删除提示取消、历史结果导出、积分与原始备注、待确认购买语言切换 / 刷新通过。桌面与手机 390 / 320 像素浅深主题、键盘焦点及四类页面无页面横向溢出 | 浏览器额外上传本任务合成文本,下载核对原字节;随后仅通过原生 API 删除该合成文件,验收站恢复两份原生文件、单条历史、20 积分和原团队。永久删除的实际浏览器操作只验收取消,失败保持由受控组件测试覆盖,未将取消称为删除成功。 证据位于本任务 `.local/i13-library-*` 和 `.local/evidence/i13-library-*`。一次性站点四项服务已停止,专用数据库、复制源码及私密配置已删除,仅保留本任务验收日志与截图。主预览默认英文,双语验收站未当作已发布独立案例。 六项主预览更新后核对原普通账号、已验证邮箱、20 积分、4 文件与原成功任务 / 一次尝试;主库继续 0012 / 13 条迁移,可选营销、观测、个人 / 站主通知及 API 密钥保持关闭、新表不存在。中文语言教程及迭代记录同步可访问。没有新增主库迁移、远程操作、真实发信、付款或云部署。 [中文产品语言教程](./languages)已同步实际交付路径与剩余范围。后续继续成员 / 邀请、设置 / 管理后台、工具 / 博客和邮件语言;人工译文审校、真实 OAuth / Turnstile / 供应商 / 云环境验证、I11 团队观测及 I14—I16 保持原计划范围。 ## 2026-10-11:I13 成员、邀请与设置语言 **状态:I13 第五阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `1493dd03`,功能提交 `b694a974`。成员、收到的邀请及设置页新增英文 / 西班牙语;设置覆盖个人资料、会话、通行密钥、API 密钥、账户活动、任务邮件与团队账单七张卡片。管理后台、公开工具 / 博客、实际邮件正文和托管支付回站语言仍在后续范围,未标记整个 I13 完成。 继续使用同一份业务页面、路由树、类型对齐字典和共享已翻译路径表,新增 `/es/members`、`/es/invitations`、`/es/settings`。直接加载、刷新、语言切换与邀请登录回跳保留邀请 ID、活动团队和原角色。团队名、账号名、邮箱、角色值、权限范围 ID、一次性密钥、同意版本 / 修订号与原 checkout URL 保持业务原值;日期和可见标签按界面语言显示。 认证与组织查询保留原错误对象及稳定代码,消费页面选择译文。最后所有者限制、新鲜会话要求、最后登录方式保护、密钥丢失响应的原请求重放、同意冲突刷新与支付权限不改变。实际账号邮件仍是英文,邀请与通知界面明确说明;没有调用真实邮件、支付或其他外部供应商。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 80 文件、758 项通过;新增实际成员页角色值回归、移除取消 / 权限拒绝、最后所有者、会话重认证失败、通行密钥最后登录方式拒绝、密钥丢失响应重放、同意冲突及账单原年付请求的西班牙语验证 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 82 文件、独立官网 49 文件均零错误 / 警告 / 提示;产品、工作台、邮件、官网与中文教程构建通过。保留既有空案例集合及产物体积提示 | | 实际生成站 | Account Workspace 从当前工作区生成,安装后分别检查 / 构建 / 启动英文单语言及英 / 西双语言;独立认证 secret、一次性 loopback 数据库和实际 Worker。仅验收站启用密钥 / 账户活动 / 任务邮件读取,并将免费计划设为三席位;主源码免费计划仍一席、可选模块关闭。该站不是已发布独立案例 | | 原生业务 | 两个成功流程账号真实注册 / 邮件验证捕获 / 密码登录、团队创建、邀请捕获与接受;服务端设置活动团队。错误账号收到的邀请为空,加入前成员读取 403、匿名 401,普通成员管理邀请 403。搜索使用原邮箱;密钥与通行密钥列表为空、同意关闭、账单关闭、积分 20 | | 实际成员页回归 | 浏览器发现角色译文误传给角色控件,导致可见选择与服务端角色不一致;恢复传递原 `member` 值,并通过实际成员页面测试验证标签为“Miembro”、控件值为 `member`、提交值为 `admin`。原生及最终数据库核对角色未改变 | | 浏览器 | 成员搜索 / 清除、所有权转移确认取消、最后所有者禁用、普通成员无管理控件、英 / 西切换与刷新通过。未登录邀请 → 西班牙语密码登录 → 同邀请;语言切换保留 ID、原团队和待接受状态。邀请接受仅由原生 API 验收,浏览器没有转移权限、接受邀请或移除成员 | | 设置与手机 | 实际个人资料保存后恢复原名;会话关闭与密钥签发只检查确认取消,没有新凭据。键盘焦点、浅深主题、成员 / 设置 / 邀请的 390 / 320 像素布局通过;修复设置卡片最小宽度及长按钮、邀请长邮箱标题溢出与操作挤压,并重新构建浏览器复验 | 首次原生邀请捕获断言依赖正文大小写,捕获模板将标题转为大写,流程停在接受前;改为核对原主题与邀请链接后成功。独立数据库总计四个已验证合成账号,最终核对成功流程的原团队仍两名成员、owner / member 角色不变、原邀请已接受、另外两份邀请待接受、无密钥 / 通行密钥、同意仍关闭、积分仍 20。没有以失败断言推断真实邮件发送问题。 检查预览脚本参数时曾误停六项主预览,已立即恢复并通过原账号、积分、文件、成功任务和六项入口检查;没有执行主库迁移。最终更新后再次核对主预览,主库保持已授权的 0012 / 13 条迁移,原普通账号、20 积分、4 文件与原成功任务 / 一次尝试保持,可选新结构与模块仍关闭。 本任务 `.local/i13-account-*` 与 `.local/evidence/i13-account-*` 保存构建、原生业务、浏览器和清理证据。验收站四项服务、专用数据库、复制源码及私密配置已移除,保留日志与截图。中文语言教程、网站配置、模块、能力清单和计划已同步;默认英文主预览不发布西班牙语路径。 后续继续管理后台、工具 / 博客、实际邮件正文和托管支付回站语言;人工语言审校、真实 OAuth / Turnstile / 邮件 / 支付 / 云环境验证、I11 团队观测和 I14—I16 保持原范围。 ## 2026-10-11:I13 管理后台语言 **状态:I13 第六阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `9aff7d03`,功能提交 `e015f370`。站点管理后台新增英文 / 西班牙语,覆盖账号权限、积分与账本、购买 / 订阅记录、支付核查、任务恢复、站主通知和审计分页。公开工具 / 博客、实际邮件正文与托管支付回站语言继续推进,未标记整个 I13 完成。 继续使用同一组业务组件、路由树、类型对齐字典与共享路径表,新增 `/es/admin`。站点权限与团队角色分别校验,语言开启不授予管理员身份。原始账号 / 任务 / 请求 ID、角色值、审计状态代码、维护环境、原因、账本原备注、金额及币种保持原值;可见标签和日期按所选语言显示。西班牙语账本可展开原备注,未知业务代码继续展示原值。 写操作保持原权限、新鲜会话、原因、明确确认和当前页面内的原请求手动重试,不自动重试。语言切换会重新加载页面,未提交表单与确认状态不保留;刷新后先核对服务端记录与审计,再决定后续操作。管理员任务列表不显示他人原输入、文件名或下载入口,站点角色仍不能下载他人私有文件。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 80 文件、764 项通过;新增实际后台西班牙语权限值、积分丢失响应原请求重放 / 支付限制保持、任务恢复、支付核查不自动重试、无记录与实际零值区分、站主通知不确定状态,以及实际边缘路由正反例 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 82 文件、独立官网 49 文件均零错误 / 警告 / 提示;邮件、产品公开页、工作台、官网及中文教程构建通过。保留既有空案例集合与产物体积提示 | | 实际生成站 | Admin Workspace 从当前源码生成,安装后分别检查 / 构建 / 启动英文单语言及英 / 西双语言;独立认证 secret、一次性 loopback 数据库和实际 Worker。仅验收站开启站主通知读取,主源码仍关闭;单语言 `/es/admin` 为 404,双语入口保持 noindex,API / 资源不增加语言前缀,`/es/admin-extra` 为 404 | | 原生业务与权限 | 两个实际验证邮箱的合成账号、密码登录、原团队;原生审计初始化验收站管理员。普通团队所有者管理 API 为 403,匿名为 401;临时停用 / 恢复与积分增减的原请求重放通过。管理员自我停用被拒绝;实际 24 小时新鲜会话门槛拒绝 25 小时旧会话。实际文本处理成功一次,管理员元数据不泄露私有文件,下载他人结果为 404 | | 支付 / 通知边界 | 仅在一次性数据库构造一条待确认购买、一条不确定通知与支付复核限制。无 checkout URL、积分发放或供应商查询资格;界面不提供核查 / 强制重发动作。不将这些状态展示称为实际付款、外部渠道送达或供应商验收 | | 浏览器 | 账号搜索、权限与积分确认取消、购买事件、原备注展开、英 / 西切换与刷新、键盘焦点通过。退出后保护入口回西班牙语登录,普通团队所有者登录后与直接访问后台均回工作台,原团队保持且无后台导航。320 / 390 像素浅深主题无页面横向溢出;修复购买金额 / 积分标签间空格并重建复验 | 原生验收最初将自我停用预期状态写为 403,实际正确拒绝为 400;修正断言后续验。会话验收曾错误使用两小时旧时间,服务端按现有 24 小时规则接受测试 +1;通过原生审计调整 -1 恢复原余额,并改为 25 小时旧会话核实拒绝。保留两次调整的原始审计原因,未修改生产会话策略,也未将错误验收时间描述为产品权限漏洞。 最终一次性数据库核对两个账号分别保持 admin / user、已验证且未停用;原团队 owner 角色、20 积分、支付复核限制、单任务 / 一次尝试、7 条审计与 5 条账本保持。浏览器只确认取消,没有额外权限、积分或支付写入。专用四项服务已停止,数据库、复制源码及私密配置已移除,日志和截图保存在本任务 `.local/i13-admin-*` 与 `.local/evidence/i13-admin-*`;验收站未发布为独立案例。 六项主预览更新后核对原普通账号、已验证邮箱、20 积分、4 文件及原成功任务 / 一次尝试。主库继续已授权的 0012 / 13 条迁移,可选密钥、营销、观测、个人 / 站主通知保持关闭,新结构不存在。中文语言教程同步实际路径与页面重载限制;默认主预览仍英文单语言,没有新增主库迁移、远程操作、真实发信、付款或云部署。 后续继续公开工具 / 博客、实际邮件正文与托管支付回站语言;人工译文审校、真实供应商 / 云环境验证、I11 团队观测和 I14—I16 保持原计划范围。 ## 2026-10-11:I13 公开工具语言 **状态:I13 第七阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `48f530d1`,功能提交 `5ea9ef50`。工具目录、JSON 格式化与文本清理完整说明 / 常见问题 / 操作界面新增英文与西班牙语;工具保存登录回跳、工作台卡片和历史标题同步。博客、实际邮件正文与托管支付回站语言仍在后续范围,未标记整个 I13 完成。 完整工具译文集中在 `packages/core/tool-locales.ts`,Astro 公开页和 React 工作台共用;两种语言继续使用同一工具处理器和交互组件。语言配置默认仍只有英文,公开翻译路径只登记实际工具 ID。新增 `/es/tools`、`/es/tools/json-formatter` 与 `/es/tools/text-cleaner`,共六组实际公开页面;canonical、双向语言链接、sitemap 和工具结构化数据对应当前语言与本站域名。未知工具、博客及 API / 资源的虚假语言入口不发布。 工具名称、说明、标签、错误、保存 / 导出和可选使用报告选择按界面语言显示;工具 ID、示例数据、输入 / 结果、MIME、扩展名与下载文件名保持原值。处理器增加稳定错误代码,同时保留原英文消息与处理上限,服务端保存行为不变。普通预览 / 文件导入在浏览器完成;保存才发送当前输入到根路径 API,历史仍只属于账号且不扣积分。 未登录保存时,当前标签页暂存输入,经同一语言登录回同一个工具后恢复预览;存储不可用则留在原页,提示在另一标签页登录。响应丢失不自动重试,同一页面的手动保存继续原 UUID。语言切换会重新加载,未保存的编辑输入不自动保留,界面和中文教程明确提醒先复制;恢复预览不会冒充新的处理成功或自动保存。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 81 文件、772 项通过。新增实际西班牙语 JSON 原值 / 失败清空、文本清理、保存丢失响应原请求重放、匿名同语言回跳、标签页存储拒绝、恢复预览不产生请求、文件大小限制及可选使用报告隐私验证;既有英文观测与原生私有历史测试继续通过 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 86 文件零错误 / 警告 / 提示。邮件、公开页、工作台、独立官网与中文教程构建通过;保留既有空案例集合及产物体积提示 | | 实际生成站 | Tools Workspace 从当前源码生成,安装后分别检查 / 构建 / 启动英文单语言及英 / 西双语言;独立认证 secret、一次性 loopback 数据库与实际 Worker。单语言西班牙语工具入口 404;双语逐页读取六组真实 HTML、语言正文、canonical / 三项 hreflang、sitemap 唯一地址、工具 schema 与根资源,未知工具 / 前缀 API 为 404 | | 原生业务 | 两个合成账号真实注册、邮件验证捕获、密码登录与原活动团队;两种工具原内容保存 / 原请求重放返回同一记录,改输入沿用旧 UUID 为 409、错误 JSON 为 400、匿名保存为 401。他人历史为空、他人删除为 404;读取页面前后账号 / 会话 / 团队不变,积分 20 | | 浏览器 | 错误 JSON 无结果可下载;实际粘贴 → 复制 / JSON 下载、匿名保存 → 西班牙语登录 → 恢复同输入 → 保存 → 同语言工作台 / 私有历史通过。实际文本文件本地导入、处理、下载与保存通过,两份下载内容逐字节核对;JSON 示例与原用户结果不翻译。手机菜单、英 / 西切换与刷新、键盘焦点通过 | | 手机与主题 | 两种工具的 320 / 390 像素浅深主题通过。西班牙语处理标签曾撑宽 320 像素页面,增加工具顶部区域换行与标签宽度限制后重建复验,页面无横向溢出;工具列表手机入口与工作台链接均指向真实语言页 | 首次生成站的工作台构建暴露共享模块的省略扩展名引用无法被配置加载器解析;改用明确包导出入口,并使用可直接加载的错误类声明后,单 / 双语言与最终源码构建通过。存储不可用测试改为模拟实际标签页存储实例,使拒绝分支得到验证;未将测试环境方法绑定差异描述为产品保存故障。 一次性站最终核对两个已验证普通用户、原团队 owner、20 积分,以及两条原生和两条浏览器保存记录;四条记录均属于原账号,处理器 ID 与原输出保持。本地文件导入没有上传到私有文件服务,没有创建后台任务或使用报告。四项专用服务已停止,数据库、复制源码及私密配置已移除,日志、输入 / 下载核对和截图保留在 `.local/i13-tools-*` 与 `.local/evidence/i13-tools-*`;不把验收站称为已发布独立案例。 六项主预览更新后核对原普通账号、已验证邮箱、20 积分、4 文件及原成功任务 / 一次尝试。主库继续已授权的 0012 / 13 条迁移,可选密钥、营销、观测、个人 / 站主通知仍关闭,新结构不存在。默认英文主预览不发布西班牙语路径;没有新增主库迁移、远程操作、真实发信、付款或云部署。 中文语言教程、配置说明、模块与能力清单同步实际工具路径和输入保留限制。后续继续博客、实际邮件正文及托管支付回站语言;人工审校、真实供应商 / 云环境验证、I11 团队观测和 I14—I16 保持原范围。 ## 2026-10-11:I13 博客语言与分语言订阅 **状态:I13 第八阶段已本地验收,完整 I0—I16 目标继续进行。** 接续 `2bc3810b`,功能提交 `ca8fb924`。博客目录、两篇完整示例文章、搜索 / 分类、相关文章、目录锚点、首页摘录和 RSS 支持英文 / 西班牙语。默认仍是英文单语言;实际邮件正文与托管支付回站语言继续推进,不将本阶段称为整个 I13 完成。 英文原稿与西班牙语译文按同名 Markdown 文件配对,不维护第二份文章映射。共享目录决定详情、首页、相关卡片、RSS、sitemap 与实际语言替代链接。缺译文明确标注英文原文,草稿原稿同时抑制译文;启用语言的孤立译文、空正文和无效元数据拒绝构建。没有实际发布文章的目录设置 noindex 并从 sitemap 排除。博客关闭时英文索引保留提示、英文订阅无条目,西班牙语博客 / 订阅与所有文章不生成。 公开搜索元数据读取放在构建模块,不进入工具交互的字典模块。首次全仓检查发现两个工具界面测试因字典引入 Markdown 而不能加载;拆分后全仓检查和真实生成站重建通过,工具交互保留原入口。通用文章卡片只接收展示属性,产品卡片适配正文语言、阅读估算、日期和英文回退;独立官网继续使用原英文默认属性。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 83 文件、781 项通过。覆盖真实发布 / 回退规则、来源草稿、重复 ID、无来源译文、日期与空正文、关闭模块、重音搜索、分类组合、空结果、重置焦点和安全 URL 文本;既有工具、认证、权限与账本回归继续通过 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 96 文件零错误 / 警告 / 提示。邮件、产品公开页、工作台、独立官网与中文教程构建通过;保留既有空案例集合与产物体积提示 | | 实际生成站 | Blog Workspace 从当前源码生成、安装并依次检查 / 构建 / 启动英文单语言和英 / 西双语言,独立认证 secret、一次性 loopback 数据库和实际 Worker。单语言西班牙语博客 / RSS 为 404;双语核对三组博客 HTML、完整正文、JSON 示例原值、目录锚点、工具入口、canonical / 三项 hreflang、结构化数据、两份 RSS 的语言 / GUID / UTC 日期及 sitemap 唯一地址 | | 发布边界 | 在实际生成站将一篇译文置为草稿:目录和相关文章回退英文,英文文章无虚假语言菜单,西班牙语详情 404、对应 RSS / sitemap 地址排除。两篇原稿草稿时详情均 404、目录无文章且 noindex;博客关闭排除全部博客输出,西班牙语 RSS 404。孤立译文与空正文分别实测拒绝构建,恢复真实内容后重新通过 | | 浏览器 | 搜索 JSON 的单数计数、分类组合、空结果、清除并回到搜索焦点、带重音的查询参数初始化、键盘焦点、文章锚点、英 / 西切换与直接刷新、同语言工具入口、手机菜单和英文回退页面通过。目录与两篇文章在 320 / 390 像素浅深主题均无页面横向溢出,保留真实截图 | 验收脚本的重置步骤曾把生成站品牌配置覆盖为模板配置;改为保存由生成器重新产出的同名站配置,重跑缺译文、草稿、关闭与恢复验收,最终 HTML 的发布者和站点名称仍为 Blog Workspace。作者署名属于显式文章内容,生成器保留原作者;中文教程明确要求替换示例时同步核对作者、日期与正文,不冒充新站作者或真实需求研究。 一次性数据库最终没有账号、私有文件、任务或工具历史;公开博客验收没有创建私有业务记录、发送邮件或请求外部服务。四项专用服务已停止,数据库、复制源码与私密配置已移除;日志、原生输出、边界证据和截图保留在 `.local/i13-blog-*` 与 `.local/evidence/i13-blog-*`。验收站没有发布为独立案例。 六项主预览更新后仍使用已授权的 0012 / 13 条迁移,原普通账号、已验证邮箱、20 积分、4 文件和原成功任务 / 一次尝试保持。可选密钥、营销、观测、个人 / 站主通知继续关闭,新结构不存在;默认主预览不发布西班牙语路径。中文教程同步文章配对、订阅、作者责任和发布边界。语言切换会重新加载,当前目录筛选及文章锚点不保留;两篇译文由 Codex 提供,人工审校仍待。 后续继续实际邮件正文及托管支付回站语言;真实供应商 / 云环境验收、人工审校、I11 团队观测与 I14—I16 保持完整计划范围。 ## 2026-10-11:I13 认证与邀请邮件语言 **状态:I13 第九阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `528334d7`,功能提交 `7027ab54`。账号验证、密码重置、验证码和组织邀请邮件支持英文 / 西班牙语主题、预览、HTML、纯文本、按钮和到期说明。默认仍是英文单语言;后台任务 / 营销邮件的持久化语言与托管支付回站语言继续推进,未标记整个 I13 完成。 邮件字典位于独立的 `apps/email/locales.ts`,模板包不引入产品配置。认证客户端每次请求读取当前页面路径,使用 `x-agentbuff-locale` 传递文案语言;API 只接受实际启用语言,未知、缺省或关闭的语言回退英文。请求头不参与身份、团队或权限判断,也不保存长期偏好。邀请按发起者当前页面语言发送 / 重发,不能推断收件人的母语;原邀请 ID、角色值、UTC 截止时间、签名链接与验证码保持原值。 验证 / 重置的签名入口继续使用根路径 `/api/auth`,仅回跳页面保留语言。账号验证令牌实测有效 3600 秒,原邮件的 24 小时描述更正为 1 小时,未延长安全期限;重置有效 1 小时,验证码默认 5 分钟,邀请 48 小时。隐私原页仍为英文,西班牙语邮件明确标注。译文由 Codex 根据原英文文案编写,人工审校仍待。 后台任务和营销确认邮件继续保留英文正文及原 HTML / 纯文本字节,没有给已有排队投递增加临时页面语言。用上一提交的实际公共底座逐字节比较,并加入固定载荷摘要回归,避免相同供应商幂等键重试时改变内容;设置页保留后台任务邮件英文说明。后续须先持久化收件人的语言,再处理投递与重试的正文迁移。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 85 文件、786 项通过。新增已安装认证 SDK 的逐请求语言选择、真实认证插件验证 / 重置 / 验证码回调、单语言回退、签名期限、四种验证码文案、邀请原角色 / ID、用户文本转义和既有后台邮件载荷兼容验证 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、离线配置检查通过;产品 Astro 96 文件、独立官网 49 文件均零错误 / 警告 / 提示。邮件、产品公开页、工作台、官网与中文教程构建通过;保留既有空案例集合与产物体积提示 | | 实际生成站 | Mail Workspace 从当前工作区生成、安装并检查 / 构建 / 启动英文单语言及英 / 西双语言。独立认证 secret、一次性 loopback PostgreSQL 和实际 Worker;单语言西班牙语账号页 404,带西班牙语请求头仍发英文。双语验收站仅将免费计划设为三席位,主源码仍一席、可选模块关闭 | | 原生账号 | 两个合成账号真实注册、验证邮件捕获、签名期限核对、验证与密码登录。西班牙语重置邮件回跳正确,真实重置成功,旧令牌再次使用 400;登录 / 邮箱验证 / 找回三种实际验证码邮件保持原六位代码与 5 分钟说明。更换邮箱文案由模板测试验收,未冒充已启用的实际更换流程 | | 原生邀请 | 原团队 owner 发起 member 邀请,提交后捕获西班牙语邮件,链接 / 原 ID / 角色 / 截止时间一致;匿名邀请和接受均 401,目标已验证账号实际接受成功。最终仍是原团队 owner / member 两人。最终源码另捕获一份布局邀请后取消,没有新增成员 | | 浏览器 | 既有账号的西班牙语验证码与密码登录均进入原团队,工作台保持 20 积分。西班牙语找回提交捕获西班牙语重置邮件,切换英文后捕获英文邮件;验证码不在日志输出,展示截图使用替代码。找回页面浅深主题的 320 / 390 像素布局和键盘焦点通过 | | 实际邮件 HTML | 使用本地捕获的验证、重置、验证码和邀请邮件预览,320 / 390 像素无横向溢出。邀请长链接最初撑宽 320 像素,增加链接段落断行后重新构建、原生捕获并浏览器复验;验证码字体与间距调整后六位完整显示。邮件是固定浅色 HTML,不把此预览称为邮箱客户端或深色邮件验收 | 一次性站最终核对两个已验证普通账号、原接受邀请和角色、20 积分;没有私有文件、后台任务或工具历史。专用四项服务和脱敏邮件预览服务已停止,数据库、复制源码及私密配置已移除。构建、原生流程、兼容性、浏览器及清理证据保留在 `.local/i13-mail-*` 与 `.local/evidence/i13-mail-*`;截图与验证 / 重置链接已脱敏。验收站没有发布为独立案例,没有请求真实 Resend、支付或其他外部供应商。 六项主预览已更新;原普通账号、已验证邮箱、20 积分、4 文件及原成功任务 / 一次尝试保持,浏览器确认原结果预览与下载入口仍在。主库继续已授权的 0012 / 13 条迁移,可选密钥、营销、观测、个人 / 站主通知仍关闭,新结构不存在。默认英文主预览不发布西班牙语路径。中文账号 / 语言教程、配置说明、模块、能力清单与计划已同步。 后续继续后台任务 / 营销邮件的持久化语言和投递重试,以及托管支付回站语言;人工审校、真实邮箱显示 / 投递、供应商 / 云环境验证、I11 团队观测和 I14—I16 保持完整范围。 ## 2026-10-11:I13 持久化任务邮件语言 **状态:I13 第十阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `7faec3db`,功能提交 `9d2e5038`。设置页新增独立的任务邮件语言选择,偏好与投递记录分别持久化;成功 / 失败邮件的主题、HTML / 纯文本、任务和设置链接支持英文 / 西班牙语。默认仍是英文单语言且任务邮件关闭,营销确认邮件与托管支付回站语言继续推进,未标记整个 I13 完成。 界面语言切换不自动修改邮件偏好,当前账号必须显式选择并保存。每次保存沿用原 revision 冲突检查、本人已验证邮箱与 Origin 校验;客户端不能指定接收人。显式保存不同语言增加 revision 并重设选择起点,旧未领取邮件失效,同一终态不重复入队;正在发送的请求可能仍按原语言到达。关闭西班牙语后如实保留已保存值,可以改成英文或撤回,相应排队请求停止,不能同键改写为英文。 增量迁移 `0022_task_email_language.sql` 给旧偏好填入英文,但旧投递的语言保留空值。空值继续选择原英文模板、原 `task-mail-v2` 摘要和原幂等键;新投递保存明确语言,使用包含记录语言的 `task-mail-v3` 摘要。旧 HTML / 纯文本逐字节回归继续通过,两种渲染分支在旧待处理记录消退前保留,不将空字段直接改成新版英文。营销邮件暂保留原英文载荷,后续接入独立匿名同意语言。 | 验证 | 结果与口径 | | --- | --- | | 全仓 Vitest | 85 文件、797 项通过。新增实际 RPC 的语言保存 / 缺省保留、成功与失败西班牙语、未知 / 关闭语言拒绝、原 revision 冲突、选择变更后的跳过与去重、同键重试、旧摘要交付、增量迁移保留旧记录和数据库语言约束;实际设置组件验证显式保存、失败后的重新读取与不可用语言撤回 | | 静态检查与构建 | TypeScript、Oxlint、Oxfmt、Drizzle 增量历史检查和离线配置检查通过;邮件、产品公开页、工作台、独立官网与中文教程构建通过。没有修改已应用的迁移、旧快照或历史条目;保留既有空案例集合及产物体积提示 | | 实际英文链路 | 独立 loopback PostgreSQL 完整应用至 0022,实际 Worker / Queue / R2 执行成功与失败任务,定时入口捕获英文邮件;多次重放没有增加投递。实际捕获故障保留业务结果,恢复后沿用同一键接受;撤回 / 再次开启不补发旧终态,原结果仍可下载 | | 实际西班牙语链路 | 另一个独立数据库和配置开启英 / 西双语,保存西班牙语偏好;实际队列成功 / 失败、定时扫描和捕获邮件通过。捕获故障后销毁并重新启动实际 Worker,在同一数据库和本地资源目录恢复偏好与原投递;第二次尝试仍是西班牙语和同一记录键,最终共三份任务邮件 | | 浏览器 | 已有合成账号登录英文设置页,原保存值仍为西班牙语;显式保存英文后切换西班牙语页面,偏好仍为英文。显式保存西班牙语并刷新后恢复同值,再撤回成功;最终 revision 6、语言 es、allowed false。页面检查未产生额外邮件。投递列表继续显示原记录语言 | | 手机与链接 | 设置页的 320 / 390 像素浅深主题与键盘焦点通过;实际成功 / 失败邮件的两种宽度无横向溢出。邮件按钮打开原西班牙语任务,原任务成功一次,私有结果下载仍使用根路径 API。没有在邮件中加入输入、文件名、结果附件或签名下载地址 | 增量迁移验收起初将多条 SQL 合并为单个预备语句,测试驱动明确拒绝;改为按原迁移断点逐条执行后通过。模板验收始终先重新构建邮件包,避免读取旧产物;既有英文载荷摘要与逐字节比较最终均保持。没有将测试驱动或旧构建产物的差异描述为生产迁移故障。 两次原生验收各自销毁专用数据库、Worker、Queue、R2 目录与私密配置;浏览器专用工作台和本地邮件预览入口均已停止。证据保留在 `.local/i13-task-language-*` 与 `.local/evidence/i13-task-language-*`,截图与 HTML 只包含合成任务邮件,没有认证令牌。该一次性工作台不是已发布独立案例,没有访问真实 Resend、支付或其他外部供应商。 六项主预览已更新;原普通账号、已验证邮箱、20 积分、4 文件及原成功任务 / 一次尝试保持。主库继续已授权的 0012 / 13 条迁移,未应用 0022 或其他可选迁移,任务 / 站主通知、密钥、营销及观测继续关闭,新结构不存在。中文任务通知、账号 / 语言教程、环境与网站配置、模块、能力清单和计划同步真实语言与兼容规则。 后续继续营销确认邮件的同意语言与重试,以及托管支付回站语言;人工审校、真实邮箱显示 / 投递、供应商 / 云环境验证、I11 团队观测和 I14—I16 保持完整范围。 ## 2026-10-11:I13 持久化营销确认邮件语言 **状态:I13 第十一阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `83927ab3`,功能提交 `881f3060`。匿名邮件订阅 / 候补名单保存主动同意的语言与对应原文,确认邮件主题、HTML / 纯文本、按钮和确认 / 退订路径支持英文 / 西班牙语;公共页默认英文且两个名单继续关闭。托管支付回站语言、I11 团队付款观测、I14—I16 与各外部服务验收仍待,不将整个 I13 或完整目标标为完成。 语言在每代主动同意时写入,重试与 Worker 重启读取原记录;不采用页面请求头、账号语言或浏览器猜测。冷却期内的匿名重复申请保持原待确认代次,已确认记录不能被另一个匿名语言提交改写。退订并满足冷却期限后,新申请可记录新的语言,旧确认链接失效;原退订能力继续用于该名单,不会确认新代次。名单分别保存同意,不创建或验证账号。 新增 `0023_marketing_confirmation_language.sql` 和对应快照 / 追加日志,只增加可为空的语言及 `en` / `es` 约束。旧空语言保留原英文模板、链接、主题与幂等 key,不自动升级渲染载荷。单元验证先重建迁移前结构,再执行实际增量 SQL,核对 nonce、同意原文、尝试数不变,并比较原英文 HTML / 纯文本;既有黄金摘要仍通过。关闭语言停止领取对应确认投递,不改发另一语言,也不阻止根路径的历史退订;SQL 批量查询同时过滤关闭语言,避免无效记录占满十项处理窗口。已经领取并开始发送的请求仍可能到达。 订阅、候补名单、确认与退订共用既有页面结构和字典。开启第二语言才发布西班牙语页面,关闭名单不发布相应申请页;页脚指向当前已翻译入口。偏好页保持 `noindex`、`no-referrer`,不进入 sitemap、不加载营销统计,只打开邮件链接不修改订阅。成功操作清除 URL 片段。隐私政策仍明确标注英文;译文人工审校及真实邮箱客户端显示仍待。手机检查发现西班牙语申请按钮过长,已缩短并重建,保留明确用途及邮件语言说明。 | 验收 | 结果与边界 | | --- | --- | | Vitest | 85 个测试文件、804 项通过;覆盖同意语言与原文、匿名重复提交保护、同键同载荷重试、关闭语言后的跳过 / 退订、批次不被关闭语言阻塞、新同意代次与旧链接、实际迁移约束和旧载荷兼容 | | TypeScript / Oxlint / Oxfmt | 完整类型检查、严格静态检查和格式检查通过;提交检查通过 | | Astro / 构建 | 98 个产品页面诊断无错误、警告或提示;邮件、正常单语言预览、模板官网、工作台、产品公共页和中文文档构建通过;独立双语言构建与页面产物检查通过 | | 配置 / 迁移检查 | 默认英文、名单关闭,配置检查与 Drizzle 检查通过;预检明确名单需完整迁移至 0023,任务邮件需 0022 | | 实际本地 Worker | 英文与西班牙语分别运行实际 workerd / PostgreSQL;八并发申请一封本地捕获、主动确认 / 退订、名单隔离、确认与申请竞争、失败投递和实际定时恢复通过;西班牙语运行真正销毁并重启 Worker,核对存储语言与重试请求正文 / 原链接 / 幂等 key 完全相同 | | 实际浏览器 | 合成邮箱在西班牙语页面主动申请,实际捕获邮件链接打开后仍待确认;点击确认才成功,原退订链接展示已确认状态,点击退订后才退出;两次成功均清除片段。320 / 390 宽度、浅深主题、提交按钮键盘实线焦点已检查,保留实际截图 | | 故障与隐私 | 实际营销维护表故障不阻断文件 / 任务维护;原签名链接日志脱敏检查继续通过;没有创建账号、真实发送、真实 CAPTCHA 或供应商配置变化 | | 清理 / 正常预览 | 两次一次性数据库与 Worker / 邮件捕获 / 静态资源已清理,端口 21311 已停止;六个主预览运行,主库仍为已授权 0012(13 项迁移),新增营销 / 通知 / 观测结构未应用。原普通账号 20 积分、4 个文件、原任务成功且一次尝试保持完整 | 本地验收程序新增 `--languages --ui`,复用既有邮件捕获和隔离配置,临时固定邮件预览入口只存在于验收程序。完整浏览器过程结束后,程序核对已退订、西班牙语同意和无账号,再清理环境。截图与最小邮件 HTML 留在忽略目录作为本地证据;没有公开成独立案例,没有向 Resend 或其他远程服务发送。本阶段不会把接口接受状态称为真实邮箱收到或付费订阅。 初次旧载荷测试为没有邮件 Logo 的配置错误拼接了 `null` 地址,已修正测试按真实品牌入口的可选 Logo 规则构建预期;产品旧模板与历史迁移没有因此修改。最后整理西班牙语静态路由生成代码,分开列出实际公开页、工具、名单和文章,避免继续扩成长行条件;未新增第二套路由或通知引擎。 **下一项:** 按原计划核对积分包 / 订阅托管支付与门户的发起语言、成功 / 取消 / 返回路径及恢复边界,使用独立本地供应商契约与实际 Worker 验收。真实 Stripe 沙箱、真实 Resend、OAuth / 通行密钥设备、Cloudflare 云资源及人工语言审校继续列为外部待验收;主库后续可选迁移不自动执行。 ## 2026-10-11:I13 托管支付回站语言 **状态:I13 第十二阶段已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `e079f735`,功能提交 `26ec0aa0`。积分包的新结账保存发起语言,订阅的新结账使用同语言设置页,账单门户传递当前界面语言并返回固定设置页。公开默认仍为英文,主预览没有 Stripe 凭据,积分包继续关闭。真实托管页面与沙箱交易、人工审校、I11 团队付款事件以及 I14—I16 保持待验收。 积分包语言与原商品请求一起确定,丢失响应后从保存记录恢复;同一请求改商品或显式语言均拒绝。新增 `0024_credit_checkout_language.sql`、对应快照与追加日志,使用购买 ID 为主键的一对一语言表,主键同时支持购买外键查询;没有修改原购买列或历史迁移。旧购买没有语言快照时继续英文地址和不含 `locale` 的原 SDK 载荷。正常主库停留在 0012,未读取或创建该可选表;启用积分包前需完整迁移至 0024 并重建工作台 / API。 订阅语言从保存的成功 / 取消地址确定,两个地址必须同语言;重试不会根据当前页面重建。原 SDK 参数摘要匹配时,保留已有语言、请求标记及无标记旧版本,不给同一幂等键补新字段。门户清除页面中无关的查询参数和片段,保持原个人 / 团队主体、新鲜会话及当前管理权限。订阅成功返回仍经过归属、当前资源和实际保存核查;未付款拒绝,确认后才返回保存的语言设置页,不因此发积分。 | 验收 | 结果与边界 | | --- | --- | | Vitest | 85 个文件、811 项通过;新增积分包语言快照 / 冲突 / 关闭语言 / SQL 约束 / 旧请求兼容、实际订阅 SDK 同键重试与旧参数摘要、门户语言与客户端个人 / 团队请求范围;完整测试同时覆盖既有付款核查和恢复 | | 类型 / 静态检查 | 完整 TypeScript、严格 Oxlint、Oxfmt、Drizzle 和默认配置检查通过,提交检查通过 | | 构建 | 98 个产品 Astro 页面诊断无错误、警告或提示;邮件、产品公共页、工作台、模板官网、API 与中文文档构建通过;独立双语言工作台也实际构建 | | 实际 Worker | 受控 Stripe HTTP、一次性 PostgreSQL 与生产 workerd;西班牙语积分包 / 订阅分别模拟接受请求后失去响应,真正销毁并重启 Worker 两次,原请求正文 / 幂等键一致,改英文请求拒绝;未付款成功返回 409,受控当前资源确认后 302 返回西班牙语设置页,门户语言 / 返回地址正确,余额保持 20 | | 实际浏览器 | 西班牙语购买详情仍显示待确认,设置页按实际本地订阅显示有效;320 / 390 像素与浅深主题已检查,购买详情长按钮允许换行,最终 320 像素无横向溢出。托管链接未被访问,没有真实付款 | | 正常预览 | 六个服务更新并返回 200;主库仍为 0012 / 13 项迁移,可选语言表不存在。原已验证普通账号、20 积分、4 文件和原任务成功 / 一次尝试保持;实际登录、原任务、积分与未配置账单页面已检查,原购买列表在旧结构正常读取 | | 清理 / 文档 | 专用数据库、Worker、文件资源、临时工作台及 21315 入口已停止并清理;中文支付 / 语言、环境 / 网站配置、模块、能力与计划已同步,实际文档响应逐字节等于最新构建产物 | 本地验收命令 `bun payments:validate-language` 复用已有 Worker 运行基础;可加 `--ui` 查看专用双语言工作台。专用入口使用 `127.0.0.1`,与 `localhost` 主预览隔离登录 Cookie;最终专用账号登录后原预览仍保持登录。受控供应商补齐插件注册时的客户读取,未预期目的地或 SDK 路径会令验收失败,不能被插件内部捕获后当作通过。验收日志与语言 / 主题截图位于忽略的 `.local/i13-payment-language-*` 与 `.local/evidence/`,没有对外发布合成付款案例。 停用语言前需先处理该语言的未解决结账,保留路由到完成或到期核查;直接关闭可能导致历史返回地址 404。没有自动改写已接受付款请求,也不能把本站参数验收等同于供应商页面翻译验收。首次完整测试的一项门户断言仍使用旧的任意页面地址,已改为固定语言设置页后重跑全部测试通过。 **下一项:** 继续 I11 的团队订阅付款事件,核对团队主体、实际已确认收入、独立同意与个人 / 团队去重边界;随后继续原计划其余模块。真实 Stripe / Resend、OAuth / 通行密钥设备、Cloudflare 云资源和人工审校仍需外部验收,主库后续可选迁移不自动应用。 ## 2026-10-11:I11 团队订阅付款观测 **状态:团队付款观测已本地验收,完整 I0—I16 目标继续进行。** 接续记录 `63cd760f`,功能提交 `653cc3c8`。Settings 增加当前团队的独立选择,owner / admin 可以保存或撤回,普通成员只读;个人同意不授权团队账单。两个选择都显示最后确认状态,勾选但尚未保存时不改变该状态。默认仍关闭,未应用主库可选迁移。 个人与团队共用已安装 Stripe SDK 的签名门禁、当前账单与实际收款核查、全局账单 ID / 事件类型唯一键及有界恢复流程。团队付款保存团队 ID,不保存某个管理员为付款所有者;双重身份或客户绑定不清楚时跳过。最终写入在事务内检查当前团队客户、订阅和最新同意,角色变更与选择保存共用团队锁;供应商查询期间撤回不会继续插入。统计不发积分、不修改订阅权限,普通工作台读取不扫描供应商历史。 新增 `0025_team_payment_activity.sql`、对应快照与追加日志。团队选择保存最后决策者 ID 和恢复检查时间;事件表增加团队归属,数据库约束保证恰好一个所有者,团队只能写订阅付款。旧个人选择和事件保持原字段、时间、金额与唯一键。团队删除清除团队选择和事件;个人账号删除不撤销其曾参与决定的团队选择,决策者 ID 保留为历史标识。没有另建通用事件框架或第二套支付核查。 | 验收 | 结果与边界 | | --- | --- | | 全量 Vitest | 86 个文件、826 项通过;新增独立选择、成员只读 / 移除、实际会话团队变化、同源 / revision、默认关闭、事件所有者约束、账单改绑去重、查询中撤回 / 新同意起点、观察故障恢复及西班牙语失败确认 | | 增量迁移 | 在测试数据库重建升级前结构,保存旧个人事件和同意后执行实际 0025 SQL;旧记录逐字段保持、团队选择初始为空,同账单改绑团队仍被原唯一键拒绝。仅变更新迁移和追加条目,已发布 0000—0024 保持 | | 类型 / 构建 | TypeScript、严格 Oxlint、Oxfmt、Drizzle 和配置检查通过;98 个产品 Astro 页面诊断无错误 / 警告 / 提示;邮件、产品公开页、工作台、独立官网、API 与中文教程构建通过 | | 实际原生 Worker | 一次性 loopback PostgreSQL 完整迁移至 0025,生产 workerd / Queue / R2 和受控 Stripe HTTP;真实创建 / 选择团队,个人已同意仍不计团队付款;保存等待实际团队锁期间降级为成员,HTTP 返回 403,普通成员只读;恢复测试 owner 后主动保存,旧 revision 返回 409 | | 付款与故障恢复 | 八个实际 SDK 签名账单回调只写一条团队记录,采用核查后的 2300 最小单位金额而非载荷中的 999999;详情读取不重复。实际统计表故障时有效账单仍返回 200,恢复表后实际定时入口补回续费;撤回后下一账单不记录,余额不因团队付款增加 | | 实际浏览器 | 在 `activity.localhost:21312` 登录专用合成账号,个人与团队分别保存 / 撤回并刷新读取,最终两者 allowed false、revision 4,共 10 条已核实活动保持;390 像素浅深主题与 320 像素浅主题无横向溢出,保留页面截图 | | 清理 / 主预览 | 专用数据库、Worker、Queue、R2 和工作台全部清理,21312 已停止;六个正常预览更新并返回 200。主库仍为授权 0012 / 13 条迁移,团队选择 / 事件表不存在;原已验证普通账号、20 积分、4 文件和成功任务 / 一次尝试保持,实际浏览器刷新后原结果继续可用 | | 中文教程 | 服务端事件、网站配置、模块、能力清单与计划同步独立选择、0025 启用要求、删除归属和恢复上限。正常文档服务重新启动后,实际响应逐字节等于最新构建产物 | 原生验证器新增 `--teams`,包含已有个人账单路径;`--teams --ui` 在专用域名验证两个选择,避免覆盖主预览 Cookie。最后收紧订阅归属读取的事务锁后,重新运行无 UI 原生验收及全量检查。证据保留在忽略目录 `.local/i11-team-*` 与 `.local/evidence/i11-team-payment-*`;测试记录没有发布成独立案例,未访问真实 Stripe / Resend 或云资源。 **下一项:** 继续 I14 的可选客服与联盟营销,先核实一个供应商的实际接口和独立配置,再实现默认关闭、按需加载、联系页回退与生成站重置。完整计佣需要支付、重复事件、退款及结算闭环,不能把前端脚本当成已实现佣金。I11— I13 的真实供应商验收、I15 云端运行与 I16 第二产品仍保留完整范围。 ## 2026-10-11:I14 可选客服与联系页回退 **状态:客服首版已本地验收,I14 和完整 I0—I16 目标继续进行。** 接续记录 `c8030b97`,功能提交 `b9fa0e7e`。选用 Crisp 的官方浏览器命令 / 事件协议,增加显式 `support` 配置和中文客服教程。默认关闭、Website ID 为空、支持邮箱为空;工作台新增支持链接,产品联系页共用英文 / 西班牙语页面。真实工作区和消息往返尚未验收,联盟的归因、付款 / 退款及结算仍待。 聊天只在联系页主动点击后加载固定供应商脚本,没有添加依赖包、数据库迁移或第二套客服框架。首屏渲染和工具 / 私有工作台不加载 SDK;同一文档并发点击复用一次加载,关闭 / 重开复用原实例。脚本下载完成不能显示成功:等待会话就绪,再发起窗口打开,由 SDK 打开事件确认。15 秒期限、脚本错误和 SDK 命令异常均显示失败并保留邮件入口;失败不自动重试,迟到会话事件不能重新打开。 联系页和脚本采用 `no-referrer`,点击前清除当前 URL 查询与片段,联系入口不携带工作台来源。联系页不加载已有 Cloudflare 统计脚本。没有自动设置账号身份、邮箱、团队、付款、任务输入或文件,也没有预填 / 发送消息。真实供应商仍有自己的网络、Cookie 和访客会话,关闭聊天窗口不等于删除远程记录或停止网络;未提供账号身份校验或跨设备连续性,边界写入教程和界面说明。 | 验收 | 结果与边界 | | --- | --- | | 全量 Vitest | 89 个文件、836 项通过;覆盖按需加载 / 同文档去重、脚本加载与窗口确认区分、超时 / 迟到回调、已有实例保护、SDK 命令故障、真实 React 状态、配置拒绝及连续两次生成站。没有向真实供应商请求脚本 | | 配置 / 静态检查 | TypeScript、严格 Oxlint、Oxfmt、默认配置和 Drizzle 历史检查通过;104 个产品 Astro 文件诊断无错误 / 警告 / 提示。新配置要求公开 UUID 和邮件回退,生成站主动重置为关闭 / 空 ID / 空邮箱,来源配置保持 | | 实际静态构建 | 独立开启 / 关闭且英 / 西双语构建通过,核对邮件入口、联系页孤岛、初始 HTML 无供应商脚本、语言 sitemap 与空联系页不索引。额外在受控配置开启 Cloudflare 统计,核对工具页实际含统计脚本而联系页不含;没有连接统计服务 | | 实际浏览器 | 专用 `support.localhost:21316` 使用本地 SDK 协议与仅允许本地资源的 CSP;初次主动打开、关闭 / 重开共用一份脚本,正常导航到工具页后没有 SDK;切换西班牙语再用键盘主动打开,脚本 503 后保留邮件入口 | | 实际超时 / 可用性 | 第三份脚本已下载,但会话事件在实际 18 秒后返回;页面经过 15 秒期限显示失败,迟到事件没有打开窗口。故障后文本工具仍实际输出并预览 `Hello world`。验证器核对三次脚本请求、两次打开事件、无 Referrer 及无额外命令;没有消息输入框或发送方法 | | 手机 / 主题 | 390 与 320 像素浅深主题无横向溢出;长译文与邮箱换行,失败按钮有禁用样式。保留打开预览、桌面超时及手机回退截图,截图只使用合成邮箱和受控窗口 | | 正常预览 / 清理 | 六项主预览重新构建并更新,均返回 200;实际网页和中文教程响应逐字节等于最新构建。专用目录 / 21316 入口已清理。主库仍为授权 0012 / 13 项迁移;原已验证普通账号、20 积分、4 文件及原任务成功 / 一次尝试保持,浏览器实际支持链接进入默认关闭的联系页后返回原任务 | 初次本地测试的 Happy DOM 设置层级不符合当前 Vitest,已按实际运行器修正并禁用远程脚本文件加载;最初单独构建使用了错误的 Astro 执行文件路径,也已纠正。文档逐字节校验的初次断言只匹配大写供应商名,网站配置页采用小写配置值,改为大小写兼容后通过。上述验收程序问题没有修改业务数据或迁移。SDK 命令故障保护补齐后,重新通过全量回归、类型 / 静态检查和无 UI 原生构建验收;最后的统计抑制验收仅修改隔离验证器,再次通过类型 / 静态检查、隔离及正常构建。 主预览没有填写真实客服邮箱或 Website ID,联系页显示暂不可用并保留 `noindex`,不进入 sitemap。使用独立配置展示受控聊天,不把测试站发布为独立案例。证据保留于忽略目录 `.local/i14-support-*` 与 `.local/evidence/i14-support-*`;没有真实 Crisp、Stripe、Resend 或云环境操作。 **下一项:** 继续 I14 联盟营销,选定一个供应商后先核对推荐标识、支付元数据、重复 / 过期归因、退款撤销和结算接口,再实现默认关闭及完整本地付款闭环。真实客服工作区、真实支付 / 邮件 / 身份服务、I15 云端运行与 I16 第二产品继续保留在完整目标中,主库后续可选迁移不会自动应用。 ## 2026-10-11:I14 联盟营销后台核查 **状态:联盟后台首阶段已本地验收,I14 与完整 I0—I16 目标继续进行。** 接续记录 `e9d3f0b7`,功能提交 `acb9637d`。采用 Rewardful 的签名信封、当前佣金 / 结算 API 和 Stripe 当前账户 / Charge 核查;显式配置默认关闭,计划 / 商户 ID 为空,两项秘密凭据成组校验。访客追踪、客户推荐绑定、完整购买归因和真实资金结算仍待,没有通过脚本或合成佣金界面冒充完整交付。 事件先持久化再核查,不信回调载荷金额;`event.id` 去重,不使用会随重投变化的 `request.id` 或整份载荷摘要作为身份。当前资源核对推广计划、商户、客户、币种、模式、捕获 / 付款、争议和退款;较早事件仍读取当前资源,旧版本与同版本矛盾不能覆盖已核查记录。结算最多读取 20 个不重复佣金并核对成员和总额,供应商 `paid` 仅作为观察状态,不表示银行到账。 退款同步滞后留在原事件有限重试;90 秒租约、最多八次、每批三个,复用已有定时维护。最后租约过期转为待复核。佣金新核查、删除、失败或财务变化会把相关已观察结算标为待复核;保留原金额、已付历史及成员关系,不能假装已经追回资金。自己的资源重新完成核查后才能清除标记;没有管理员重放或漏回调扫描接口。 新增 `0026_affiliate_provider_checks.sql`、对应快照和追加日志,三个表分别保存最新供应商快照、当次已核查金额 / 状态与有限恢复、最新已核查结算成员。没有保存原始载荷、客户 / 推荐者邮箱,没有修改产品余额、订阅权限或执行付款。已发布 0000—0025 SQL、快照与日志条目逐字节保持;0026 只在独立测试库执行,主库没有自动升级。 | 验收 | 结果与边界 | | --- | --- | | 完整 Vitest | 90 个文件、855 项通过;新增 19 项联盟测试,覆盖默认关闭、验签、并发重复、当前资源归属、退款滞后 / 全退作废、结算总额 / 成员、旧版本 / 同版本矛盾、删除后的历史 / 关联复核、重定向拒绝、最后租约与重试上限;连续生成站沿用三项实际复制测试 | | 增量迁移 | 测试库退回 0025 结构,保存已验证普通账号、Stripe 客户 ID 和 20 积分后执行实际 0026 SQL;原账号 / 余额逐字段相同,三个新表初始为空,签名回调随后能写入新表 | | 配置 / 静态检查 | 完整 TypeScript、严格 Oxlint、Oxfmt、Drizzle 和默认配置检查通过,提交检查通过;支持未知供应商 / 不完整 ID / 半组凭据拒绝,生成站关闭并清空推广计划 / 商户 ID | | 构建 | 104 个产品 Astro 文件诊断无错误、警告或提示;邮件、产品公开页、工作台、独立官网、API 与中文教程构建通过,正常本地配置再实际构建。文档仍有既有体积提示,没有因此改动其他模块 | | 实际生产 Worker | 一次性 loopback PostgreSQL 与实际 workerd;非法签名 401、超限 413、八个同时发送的同事件只保存一份记录,取核查后金额 300 而非载荷的 999999,同事件改身份 409 | | 退款与重启 | Stripe 先出现部分退款时,原记录保留、待核查标记生效;真正销毁 / 重启 Worker,供应商追上后通过实际定时入口恢复原事件,尝试数为 2、金额 150 / 退款 500。已付结算成员关系、全退作废、旧通知、删除复核均通过 | | 请求与恢复边界 | 受控供应商 302 带外站 Location,实际 Worker 不跟随、不向外站发送凭据;恢复正常响应后原事件完成。503 连续八次转为待复核,健康入口仍 200,原文件 / 任务维护继续执行。全部供应商请求只能 GET,未预期地址会令验证失败 | | 正常预览 | 六项服务更新并返回 200;主库仍为授权 0012 / 13 项迁移,三个联盟表不存在,主 API 与网关回调均 404,公开页没有推荐追踪 SDK。原已验证普通账号、20 积分、4 个文件及成功任务 / 一次尝试保持;实际浏览器刷新原任务继续可用 | | 中文文档 / 清理 | 联盟教程、配置 / 环境、模块、能力、生成站与计划已同步;实际文档响应逐字节等于最新构建。一次性数据库、Worker 和专用文件均已清理,正常配置字节未变;日志留在忽略的 `.local/i14-affiliate-*` | 初次实际 Worker 请求使用 `redirect: "error"`,在当前运行器中于请求发送前抛出 TypeError;改为 `manual` 并明确拒绝非 200 后,实际正常响应、302 与恢复均通过。增量迁移测试第一次使用全局 URL,受到项目 Node / Workers URL 类型差异影响,改为既有绝对路径读取方式后类型检查通过;完整回归及原生验收随后重新通过。相同重定向模式仍存在于既有 Turnstile 请求,后续需单独使用实际 Worker 验证兼容性,当前不将联盟验收当作 CAPTCHA 验收。 没有使用真实 Rewardful 账户、浏览器 SDK、推荐链接、Stripe 沙箱交易 / 退款或付款 API,未创建真实用户、发积分、支付佣金或操作云资源。依据官方文档选定下一阶段 Stripe Customer metadata 的 `referral` 方案,保留现有 `client_reference_id` 的用户 / 团队与支付归属;当前尚未写入元数据,也没有自动将外部客户绑定到本站订单。 **下一项:** 继续 I14 访客推荐、主动选择 / 过期规则、重复 / 已转化保护、客户元数据绑定和原支付幂等,再补人工恢复 / 漏事件对账与真实供应商验收。先复核现有 Turnstile 请求的 Worker 兼容性。真实客服、支付 / 邮件 / 身份服务、I15 云端运行与 I16 第二独立产品继续保留完整范围,主库后续可选迁移不自动执行。 ## 2026-10-11:I3 校验服务 Worker 兼容性 **状态:已修复并完成本地原生验收,完整 I0—I16 目标继续进行。** 接续记录 `dd67a6b6`,功能提交 `5aa09233`。上轮联盟请求发现的重定向模式兼容性问题也存在于共享 Turnstile 校验函数;先以实际文件入口复现,再只将请求模式改为 `manual`,保留固定目的地、POST 字段、十秒期限、响应结构和动作 / 域名检查。3xx 仍拒绝,不跟随或转发 secret / token,校验失败不能写入文件。 复用 `files:validate` 增加 `--turnstile`,没有创建第二个 Worker / 数据库运行框架。开启选项只修改一次性配置、设置合成公开 / 私密标识,Siteverify 由受控出站接口接管;正常网站配置字节保持。旧代码在受控有效目的地尚未被调用时返回 503,程序观测到本应返回 400 的无效 token 请求却为 503、供应商调用数为零。修复后相同入口能调用校验服务,正确区分失败与成功。 | 验收 | 结果与边界 | | --- | --- | | 完整 Vitest | 90 个文件、862 项通过;共享函数 21 项检查通过,新增 302 / 307 / 308 拒绝与五种成功动作;文件、任务、购买、营销及密钥原测试一起回归 | | 真实 Worker 拒绝边界 | 一次性 loopback PostgreSQL、生产 workerd、本地 R2:缺失 / 超长 token 不调用供应商;错误主机 / 动作、重放、非法响应、503、带外站 Location 的 302 均拒绝;每次初始拒绝后文件 / 配额表及 R2 都为空 | | 真实期限与成功 | 受控服务真实延迟 11 秒,Worker 约 10 秒返回 503,实际计时介于 9.5—15 秒,没有自动重试;正确动作 / 主机响应允许一个文件,重放后仍只有一个文件 | | 开启 / 关闭回归 | 开启后仍完成原上传、摘要、流式私有读取、跨账号隔离、文件类型验证、任务输入锁定、删除与两次清理;关闭选项另运行整条原流程通过。额外边界探测只调整一次性账号的限流窗口,不提高产品限额 | | 类型 / 构建 | TypeScript、严格 Oxlint、Oxfmt 与默认配置检查通过;API 与中文文档构建通过,提交检查通过。前端源码和静态产物未变,文档保留既有体积提示 | | 正常预览 | 仅重启本任务管理的 API / 文档,六项预览均 200;主库仍为授权 0012 / 13 条迁移、可选模块关闭,原普通已验证账号、20 积分、4 文件及成功任务 / 一次尝试保持;实际浏览器刷新原任务正常显示 | | 清理与文档 | 一次性数据库、Worker、R2 文件和专用目录已清理;捕获日志没有合成私钥或验证请求头。中文验证码教程、计划和能力清单更新,实际 HTTP 文档逐字节等于最新构建产物 | 第一版验收程序把文件入口的嵌套错误对象误读成字符串,修正为已有 `error.code` 后才复现传输问题;没有为此改变产品响应格式。实际期限断言和五种动作测试补齐后,再运行开启 / 关闭的原生流程与完整测试。所有验证码供应商请求均被截获,没有下载真实 widget、完成验证码或连接 Cloudflare;本次文件入口证据不扩大为真实任务 / 支付 / 营销 widget 验收。证据留在忽略的 `.local/turnstile-worker-*`。 继续核对 I14 客户归因的两个现有边界:积分包 Checkout 当前没有 `customer` 参数,不能仅更新订阅客户便宣称覆盖积分包;已尝试结账可能失去响应,不能在重试时增加参数、改变原幂等正文。官方公开推荐 API 只有已核实的列表 / 客户过滤,未核实按 UUID 获取单条的接口;后续不能以猜测地址验证。上述要求已补入[联盟教程](./affiliates),尚未实现访客或客户绑定。 **下一项:** 继续 I14 的真实推荐 UUID 核查、主动选择 / 过期、个人 / 团队客户绑定和首次结账参数保存,再补安全恢复与漏事件对账。真实 CAPTCHA、客服、支付 / 邮件 / 身份服务、I15 云端运行与 I16 第二产品仍保留完整范围;主库后续可选迁移不自动应用。 ## 2026-10-11:I14 登录后的个人 / 团队推荐绑定后端 **状态:绑定后端已本地验收,I14 与完整 I0—I16 目标继续进行。** 接续记录 `aeded66b`,功能提交 `7997e474`。官方客户元数据方案落到受保护的 API:用户明确选择个人或当前团队、提交 `accepted: true` 与推荐 UUID,经官方推荐列表核实后,只向本站已授权账单客户写入 `metadata.referral`。浏览器采集和设置界面尚未接入;没有改变原积分包 / 订阅 Checkout、`client_reference_id` 或旧请求正文,不将这个阶段称为完整购买归因。 当前公开接口是推荐列表,没有使用猜测的单条 UUID 地址。列表最多三页、每页 100 项,核对计划 / 推荐者、商户 / 客户、转化、停用和有效期;超出范围表示无法核查,不推断为不存在。首次有效选择固定范围、推荐与客户;缺客户时等待既有账单流程,不由联盟接口创建客户。数据库唯一约束限制跨范围复用,客户多归属、客户被替换、已有不同元数据、旧付款或未解决结账均拒绝。 团队必须是会话当前选择,并以新鲜成员记录确认 owner / admin;个人选择不能夹带团队 ID。外部读取后再次锁定归属 / 成员,元数据写入期间保留锁至事务结束。原操作者只作历史证据,不代替后续授权。对外只返回状态 / 次数 / 核对时间,数据库或供应商错误不暴露查询、客户或密钥。 绑定有 `awaiting_customer / pending / attached / review` 状态,最多八次、90 秒租约、每用户每分钟五次写入;固定绑定 ID 产生写入幂等键。外部接受但响应丢失后,重读固定客户的相同元数据即可恢复,不重复写入;此前接受的结果不会因原访客随后过期而丢失,尚未写入的过期推荐仍拒绝。当前由原账户显式重试,没有绑定定时扫描或人工重放入口。`attached` 只记录曾核实的元数据结果,不表示实时状态、佣金产生或到账。 | 验收 | 结果与边界 | | --- | --- | | 全量 Vitest | 91 个文件、889 项通过;新增绑定 27 项覆盖默认关闭、无效 / 过期 / 跨计划推荐、分页范围、明确团队 / 权限、旧付款 / 未解决或异步付款、异常历史、客户多归属 / 替换、丢响应恢复、等待客户、次数上限和错误脱敏 | | 迁移历史 | 0000—0026 SQL、快照与原 journal 条目逐字节保持,只追加 0027;Drizzle 检查通过。实际一次性 PostgreSQL 先迁移至 0026、建立普通账号 / 积分余额 / 联盟事件,再升级至 0027,原数据逐字段保持,新绑定表初始为空 | | 实际生产 Worker | `bun affiliates:validate-bindings` 使用真实 workerd、一次性本地 PostgreSQL 和受控供应商协议;关闭时旧结构没有绑定表仍能查询 / 拒绝写入。启用后 Origin 不符 403、未明确选择 400;八个并发请求只产生一条绑定和一次元数据写入,其余由租约 / 每分钟限额处理 | | 恢复 / 拒绝 | 在受控供应商已接受元数据之后主动丢失响应,实际销毁 / 重启 Worker;原访客过期后重试仍确认原结果,尝试数为 2、写入数保持 1、其他元数据保留。缺客户等待、客户出现后绑定;已付款、旧结账或其他推荐元数据均转复核,不发生元数据 POST | | 团队与 PostgreSQL 锁 | 供应商读取后、写入前真实降为 member,返回 403 且无 POST。另一场景在固定客户 POST 内暂停,独立 PostgreSQL 连接修改组织成员,实际观察 `pg_stat_activity.wait_event_type = Lock`;释放供应商后绑定提交、降权完成,旧会话再查询 / 写入均拒绝。推荐存于团队,原管理员个人不承接 | | 既有联盟回归 | 再运行 `bun affiliates:validate`,签名 / 并发去重、当前资源 / 退款、Worker 重启、结算成员 / 全退 / 旧通知、重定向拒绝与八次上限通过;这条回调验证仍只允许受控 GET | | 检查与构建 | 完整 TypeScript、严格 Oxlint、Oxfmt、默认配置与 Drizzle 检查通过;邮件、产品公开页、工作台、独立官网、API 与中文文档构建通过,再按正常本地配置构建预览。保留既有文档体积提示和空案例集合提示,没有虚构独立案例 | | 正常预览与清理 | 六项服务更新并返回 200;主库仍为授权 0012 / 13 项迁移,四个联盟表都不存在,模块关闭、回调 404、公开页无推荐追踪 SDK。原普通已验证账号、20 积分、4 文件、成功任务 / 一次尝试保持,实际浏览器刷新后继续显示 Completed 和原结果文件。中文教程响应逐字节等于构建,一次性数据库 / Worker / 文件已清理,正常配置未改 | 初版原生验证器误用了本站没有启用的 tRPC JSON 外层信封,按既有接口直接提交 input 后验收通过;验证器的积分主键和类型诊断也在运行前修正。业务代码另外补齐异步 / 不明金额历史拒绝、等待客户的归属冲突检查及对外数据库错误脱敏,随后重新通过最终全量测试和原生绑定验收。没有为通过验收提高产品限额或改变正常账号数据。 所有 Stripe / Rewardful 请求由受控协议截获。绑定验证只允许固定客户、单个 `metadata[referral]` 字段及固定幂等键的 POST;没有真实供应商访问、客户创建、Checkout 创建、积分发放、佣金生成、付款或云部署。捕获日志不含合成服务私钥、签名 secret、元数据正文或邮箱;证据留在忽略的 `.local/i14-binding-*`。首次选择之后的外部控制台并发修改、真实 Cookie / SDK、推荐生命周期和实际佣金仍待真实集成验收。 **下一项:** 接浏览器推荐采集和明确选择界面;将新积分包 / 订阅的归因客户参数在首次请求持久冻结,旧尝试与丢响应重试继续原正文,然后串联具体订单 / 佣金、人工恢复 / 漏事件对账和真实供应商验证。真实 CAPTCHA、客服、支付 / 邮件 / 身份服务、I15 云端运行与 I16 第二独立产品继续保留,主库后续可选迁移不自动执行。 ## 2026-10-11:I14 公开推荐采集与个人 / 团队设置 **状态:进行中。** 实现 revision:`cc141871`。继已验收的推荐绑定后端,本轮加入公开采集页、设置界面及无浏览器 UUID 的保存后重试。中文教程和配置说明同步更新;完整 I0—I16 目标不标记完成。 `affiliates.tracking` 保留显式默认关闭和空公开 key。公开采集共用英文 / 西语页面与同一组文案定义,SDK 仅在推荐页由访客点击同意后加载。清除其他 URL 查询与 fragment,等待官方 `ready` 而非脚本 load;有效 UUID 才保存至当前标签页,按推广计划 / 商户 / 公开 key 隔离,24 小时上限不替代供应商有效期。没有 email convert、来源改写或工作台 SDK。清除候选不取消后台选择,也不承诺删除供应商 Cookie。 设置页分别展示个人和当前团队,首次选择需各自勾选确认;切换团队重置确认,旧请求只更新原范围。保存后的 `awaiting_customer / pending` 使用 `affiliates.retry`,只提交范围,后台读取不可变选择并重新核对 Origin、会话及数据库权限;浏览器丢失 UUID 不影响合法重试。显示核对时间的 `attached` 仍仅表示曾确认客户元数据,不宣称已计佣。 | 验收 | 结果与边界 | | --- | --- | | 全量回归 | 94 个文件、910 项通过。新增 21 项包含 SDK / 页面 / 设置及私钥误填保护;绑定共 29 项。覆盖未勾选、个人 / 团队隔离、过时工作区结果、权限错误、清除候选后重试、默认关闭、脚本故障、迟到事件、存储阻止和跨计划 / 时间边界 | | 实际浏览器与原生服务 | `bun affiliates:validate-ui` 在一次性 PostgreSQL 和真实生产 workerd 中运行真实公开页 / 工作台构建。访问页面不加载 SDK;同意后只排队 ready,URL 不保留邮箱查询 / fragment,脚本无 Referrer。固定客户写入响应主动丢失后,个人 pending;清除本地候选再重试,确认 attached / 两次尝试 / 一次写入,团队仍无选择 | | 故障与窄屏 | 西语按键触发受控 503;另一次真实延迟 ready 在超时后发生,页面仍失败且无候选。供应商事件报告证明迟到回调确已执行。320 / 390 像素无横向溢出,浅深主题与工具出口已检查;推荐故障后文本工具仍处理成功。工作台 DOM 无供应商脚本 | | 后台回归 | 原生 UI 命令先完整重跑 0026→0027 保存原账号 / 余额 / 联盟事件、明确范围、并发、Worker 重启恢复和实际 PostgreSQL 成员锁验收。另运行 `bun affiliates:validate`,回调 / 当前资源 / 退款 / 结算 / 重启与上限验收通过,受控回调核查仍只用 GET | | 检查与构建 | 完整类型、严格 lint、格式、配置与 Drizzle 检查通过;Astro 113 文件无错误 / 警告 / 提示。邮件、官网、产品公开页、工作台、API 与中文文档构建通过,并按正常本地配置重建预览。生成站默认关闭采集并清空原公开 key | | 主预览与清理 | 六项正常服务重启并可用,中文教程响应逐字节等于构建。主库仍为 0012 / 13 条迁移,无四个联盟表;普通账号、20 积分、4 文件和原成功任务 / 一次尝试保持,浏览器刷新后仍显示 Completed 及原结果文件。自己的临时 Worker / 数据库 / 文件均清理,无供应商或云端访问 | 初次浏览器验证发现验收代理保留了 Bun 已解压响应的 gzip 头,已修正;SDK 地址替换改为覆盖全部出现位置。一次延迟验收过早离开页面,未得到回调报告,不计作通过;最终完整重跑,等待实际事件报告后确认超时界面仍未成功。首次全量回归中既有任务并发测试超时并连带失败,未放宽限额或超时;最终完整回归 910 项通过。界面截图留在忽略的 `.local/evidence/i14-referral/`,日志与构建证据在 `.local/i14-referral-*`。 **下一项:** 将新积分包 / 订阅的归因客户参数在首次创建时持久冻结;旧尝试和外部接受但丢响应的重试沿用原正文。随后串联购买订单 / 佣金、人工恢复、漏回调对账及真实 Rewardful / Stripe 环境验收。实际 Cookie / SDK、真实归因、付款 / 退款和银行结算仍未外部验收;主库可选迁移不自动执行,I15 云端与 I16 第二独立产品继续保留。 ## 2026-10-11:I14 首次结账冻结与共享支付客户 **状态:进行中。** 实现 revision:`f775b08a`。新积分包与个人 / 团队订阅已共用首次选择快照和账单客户创建记录,真实供应商、订单 / 佣金联查和人工恢复仍待实现与验收;完整 I0—I16 范围不标记完成。主预览仍保留已批准的 0012。 只追加 0028 的 `affiliate_checkout` 与 `affiliate_customer_request`,历史 0000—0027 SQL、快照和原 journal 条目保持。新尝试在首次准备时固定账单范围、推荐选择、推广计划、商户 / 模式和已有客户。新选择不会补写旧尝试;启用前的积分购买继续原来没有客户参数的正文。完整 Checkout 参数与摘要在 POST 前保存,个人归属、团队归属和 `client_reference_id` 保持原有含义。 开启联盟时注册不提前创建 Stripe 客户;首次购买按账单账户保存唯一创建请求,积分和订阅共用。客户创建响应丢失后,即使名称 / 邮箱改变也沿用原请求;成功提交客户后擦除创建正文中的名称 / 邮箱。团队只发送团队名称和组织归属,不发送管理员邮箱。未知结果超过 24 小时或八次尝试上限后等待复核,不换新键创建客户。本站客户被替换、当前推荐元数据不匹配、模式 / 商户变化、请求损坏、成员降权或账户封禁时拒绝继续 POST。 已明确保存的推荐在新结账前绑定固定客户。结账发送前重读 Stripe 客户及本站唯一归属,数据库客户和成员锁覆盖有超时限制的 provider POST。既有 `attached` 不能代替当前元数据核对。原请求正文已固定的场景,关联盟模块后仍能恢复原 Checkout,不再做推荐绑定;尚未准备好的客户 / 绑定需恢复配置后继续。购买接口超出每分钟限额现在明确返回 429。 | 验收 | 结果与边界 | | --- | --- | | 全量回归 | 95 个文件、927 项通过;新增 17 项首次结账测试覆盖积分 / 个人与团队订阅、已选 / 后选推荐、资料变更、同键正文、丢响应、关模块恢复、旧请求不补写、24 小时未知结果拒绝、商户变化、客户替换、记录损坏、账户封禁、团队移除及替换管理员仍沿用原操作者元数据 | | 真实迁移与 Worker | `bun affiliates:validate-checkouts` 使用独立实际 PostgreSQL 先迁移至 0027,保存普通账号、初始积分与旧购买,再升级 0028;逐字段保持。生产 workerd 真实注册不创建客户,明确选择后首次积分创建 / 绑定 / Checkout 路径通过 | | 实际重启恢复 | 供应商接受客户创建后主动丢响应,修改账号名称并销毁 / 重启 Worker,继续同创建正文与键;再次接受 Checkout 后丢响应,关模块并重启,继续同 Checkout 正文与键。个人订阅共用已创建客户,实际 Better Auth 首购接受后丢响应,重启后恢复原结账 | | 并发与权限锁 | 八个实际认证购买请求,只为新账单账户创建一个客户,其他由租约 / 限额处理。团队结账 POST 内暂停,独立 PostgreSQL 连接修改成员角色,实际观察 Lock 等待;结账提交后降权完成,原会话再写返回 403,无额外 provider POST | | 原有联盟回归 | `bun affiliates:validate-bindings` 和 `bun affiliates:validate` 通过,覆盖明确绑定、原响应恢复、真实成员锁、签名 / 回调去重、当前佣金 / 退款及结算成员核查、Worker 重启和八次上限 | | 检查与构建 | 完整类型、严格 lint、格式、默认配置、Drizzle 迁移检查通过;邮件、独立官网、产品公开页、工作台、API、正常预览与中文文档构建通过。原案例集合为空和文档体积提示保留,没有填充虚假案例 | | 主预览与清理 | 六项正常服务重启并返回 200,中文联盟教程响应与构建一致。主库仍为 0012 / 13 条迁移,新结账和客户创建表不存在;普通已验证账号、20 积分、4 文件、成功任务及一次尝试保持。临时数据库 / Worker / 文件均已清理,配置字节不变,捕获日志不含合成密钥或客户正文 | 初版验收器把 Better Auth 捕获的 Checkout 故障状态误按 500 判断,修正为既有 400,并确认供应商确已接受请求后才计入恢复验收。普通账号的初始免费积分与购买积分分开核对,修正验收基线;并发验收发现购买限流原先会落入 500,已修正为 429,没有提高限额。生成迁移的新增快照 / journal 已按项目格式整理,旧迁移逐字节核对保持。 所有 Stripe / Rewardful 请求都由受控本地协议截获,仅允许固定客户创建、明确推荐元数据和受控 Checkout。没有访问真实供应商、真实付款、购买积分发放、佣金生成、转账或云部署。原生证据保存在忽略的 `.local/i14-checkout-*`。 **下一项:** 串联已核查佣金、Stripe Charge 与具体积分购买 / 订阅订单,展示可追溯的归属和退款复核;继续补人工恢复、漏回调对账与真实 Rewardful / Stripe 验收。I15 云端与 I16 第二独立产品、真实身份 / 邮件 / CAPTCHA / 客服仍保留,不自动升级主库的后续可选结构。 ## 2026-10-11:I14 具体订单 / 佣金联查与只读管理证据 **状态:本阶段受控本地验收完成,I14 与完整 I0—I16 目标继续进行。** 接续记录 `43492caa`。供应商财务快照与本站订单关联分开保存;财务回调仅排队,不追加整条订单核查链路。既有维护入口最多处理三个到期记录,能从已保存财务资源补回漏写的排队记录。新管理页面仅供站点管理员读取,不转账、不自行计算佣金,也不以历史供应商 `paid` 声称银行到账。 积分包沿实际 Charge / PaymentIntent / 购买元数据 / 原冻结 Checkout 核对;订阅沿当前 InvoicePayment 的 PaymentIntent 过滤结果找具体已付发票、实际 Stripe 订阅及唯一原结账。最多 100 个分配 / 结账记录,不接受下一页、重复分配、部分金额、多发票、歧义结账或客户 / 元数据不符。原推荐需早于原结账;没有本站原选择或结账证据则保持无匹配,不补绑历史购买。 关联提交前重新锁定财务版本、操作租约、绑定、结账及本地订单;实际订阅编号和正文摘要都要与外部读取一致。已保存的订单归属不可被后来回调覆盖。退款等待本地结算,旧关联进入等待 / 复核仍保留历史编号并明确标注。成功后重置连续失败次数;每轮八次、90 秒租约和每批三个限制,最后未知结果转复核。 | 验收 | 结果与边界 | | --- | --- | | 完整回归 | 97 个文件、959 项通过;新增订单证据 26 项、管理面板 5 项和后台权限 1 项。覆盖个人积分 / 个人与团队订阅、付款与客户不符、发票 / 结账范围及歧义、退款同步、来源 / 租约变化、外部读取后的本地变更、历史归属保护、漏排队补建、连续失败和关闭模块不读可选表 | | 增量数据库 | 只追加 0029—0031,0000—0028 SQL、快照及原 journal 条目保持。0029 保存独立关联;0030 加入实际订阅编号 / 正文摘要,旧已关联记录重新排队但保留历史事实;0031 明确拒绝部分空值,避免 SQL CHECK 的未知值放过不完整证据 | | 实际 PostgreSQL / workerd | `bun affiliates:validate-orders` 实际从 0028 经 0029、0030 至 0031,普通账号、20 初始积分、历史已付购买、绑定及结账逐字段保持。预存 0029 已关联记录在 0030 转为待核查;八个并发签名通知仅一条事件 / 关联,实际积分与团队订阅链路、发票过滤参数通过 | | 恢复与权限 | 本地退款滞后时保留原订单、显示等待;实际销毁并重启 Worker,本地结算后完成原关联。普通账号 403、负页码 400,当前管理员降权 / 封禁后同一旧会话 403;八次受控供应商故障转复核,新的财务版本开启新一轮 | | 实际管理页面 | `--ui` 构建真实工作台并代理到自己的 Worker / 数据库;实际浏览器确认四种状态、历史订单标注、刷新和个人 / 团队 / 发票 / 测试金额。英文、西班牙语与哑光黑白主题已检查。浏览器只能访问本地;截图在忽略的 `.local/i14-orders-admin-*.jpg` | | 原结账回归 | 另运行 `bun affiliates:validate-checkouts`,原普通账号 / 积分 / 旧购买保持;真实 Better Auth 结账、共享客户、响应丢失 / Worker 重启 / 关闭模块恢复、八请求单客户与 PostgreSQL 成员锁及降权拒绝仍通过;只读证据改造不改变付款正文 | | 检查与构建 | 全类型、严格 lint、格式、离线配置和 Drizzle 检查通过;邮件、官网、公开产品、工作台、API、正常预览与中文文档构建通过。文档保留既有体积提示与空案例提示,没有填充虚构案例 | | 正常预览与清理 | 六项正常服务已更新并可用;主库仍为授权 0012 / 13 项迁移,联盟可选表不存在,模块保持关闭。普通主账号、20 积分、4 文件、成功任务和一次尝试保持;临时 Worker、数据库与专用目录均清理,正常配置未改 | 初版历史列表多传了一个关联表参数,实际数据库测试立即检出并修正;既有联盟迁移测试也补齐新表恢复。全量并行回归发现已有订阅同意测试在准备跨秒时,预先构造的付款时间可能早于同意;改为在同意准备完成后生成测试付款时间,业务判断未放宽。证据空值约束在独立增量中加固,不改写已应用的历史迁移;随后最终全量与原生迁移验收通过。 所有供应商 HTTP 被截获,订单验收只允许 GET。该验收使用事先保存的历史付款事实,没有在此流程完成真实支付、购买积分发放或计佣;初始余额保持 20,账本无新增。原结账回归允许受控客户 / 元数据 / Checkout POST,仍没有真实付款、佣金生成、资金转移或云部署。真实 Rewardful / Stripe、来源 Cookie、实际推荐规则 / 延迟、外部退款与银行到账继续待验。证据保存在忽略的 `.local/i14-orders-*`,不写入仓库。 **下一项:** 补站点管理员的安全复核 / 恢复入口及审计,处理连续失败和待复核记录,再做漏回调对账与真实 Rewardful / Stripe 验收。I15 云端运行与 I16 第二独立产品、真实身份 / 邮件 / CAPTCHA / 客服仍保留;主库后续可选迁移不自动应用。 ## 2026-10-11:I14 管理员复核、恢复与审计 **状态:进行中。** 本轮基于 `9e7ed0cf` 增量实现。已知佣金和结算可由站点管理员填写原因、检查确认内容、排队重新核查;提交与终态分开审计。完整 I0—I16 目标仍进行中,漏回调对账及真实供应商环境继续待验。 只追加 0032,原 0000—0031 SQL、快照及 journal 条目保持。原供应商事件默认保持 `rewardful` 来源,管理员请求记录独立 `administrator` 来源及历史操作者 / 会话 / 请求编号。队列使用原财务核查与订单证据维护入口;没有新增支付状态修改器、付款接口或并行执行框架。请求固定资源版本并保存原因,相同意图重试只返回原受理结果,不复位已完成工作;进行中的订单租约与未完成请求不能被新的意图覆盖。 排队、财务和订单核查的领取 / 提交都重新确认原管理员权限和实际近期会话;外部读取不占用权限锁,提交后仍需确认当前角色、封禁、验证和会话,降权 / 撤销 / 超过 24 小时转复核。佣金财务未核查时不消耗订单尝试次数;历史订单归属继续不可覆盖。每轮八次、90 秒租约、每批三个及原退避保持,维护补齐最后租约过期或中断后的终态审计。已完成任务的后续自动维护不再依赖旧管理员登录。 | 验收 | 结果与边界 | | --- | --- | | 回归与权限 | 全仓 98 文件、983 项通过。新增人工复核测试覆盖当前角色 / 封禁 / 验证 / 匿名 / 会话时效、领取后撤销、供应商读取后降权、原结账读取后撤销、并发幂等、版本与租约冲突、同版本恢复八次失败、无匹配和历史归属保护、结算独立复核、最终租约恢复及完成后正常维护。API 拒绝外站 Origin、普通用户、强制状态和超额请求 | | 实际增量与 Worker | `bun affiliates:validate-orders --ui` 在一次性实际 PostgreSQL 经 0028—0032 升级,已有普通用户、20 初始积分、购买、选择和结账保持;0031 的历史供应商事件升级后来源保持。真实管理员接口并发重复意图只排队一次,销毁并重启生产 workerd 后完成原团队订单;结算独立核查留下请求 / 完成审计 | | 实际浏览器 | 真实管理页面首次成功提交后主动丢响应,再次提交严格比较同一正文,后台只保留一次意图与终态。排队时显示等待、保留此前订单;执行原维护入口后显示完成及原订单。取消确认无写入,结算因相关佣金复核重新标为待核查;黑白主题、英 / 西文案、320 / 390 像素检查无横向溢出。截图在忽略的 `.local/i14-recovery-admin-*.jpg` | | 检查与构建 | 类型、严格 lint、格式、配置、Drizzle 检查以及邮件、官网、公开页、API、工作台、正常预览和中文文档构建通过。原空案例和文档体积提示保留,没有编造真实站点 | | 主预览与清理 | 正常六项服务更新后均可访问。主库保持 0012 / 13 项迁移,模块关闭、可选表不存在;普通用户、20 积分、4 文件、原成功任务及一次尝试保持。临时数据库 / Worker / 专用目录清理,正常配置未改;日志无合成密钥及客户正文 | 第一轮浏览器发现原后台验收已用满同一管理员每分钟五次请求,界面正确限流;最终浏览器验收改用自己的独立临时操作者,没有提高业务限额。响应主动损坏时最初显示解析错误,改为本地化的提交结果未确认提示,复验同意图重试和终态审计。一次第二轮验收启动早于上一轮退出,端口被占用;清理上一轮后完整重跑并通过,不把失败运行算作完成证据。 所有供应商 HTTP 截获,仅允许固定只读 GET;历史付款事实预先保存,没有真实支付、购买积分发放、计佣或转账,也没有云部署。主库未自动应用 0032。证据在忽略的 `.local/i14-recovery-*`,中文配置教程同步至 0032 的启用要求。 **下一项:** 完善有界漏回调对账和历史资源核查,再进入真实 Rewardful / Stripe 环境验证;I15 云端运行和 I16 第二独立产品继续保留。此前已授权的本地主库 0012 升级已完成,这一可选联盟增量继续在隔离环境验收。 ## 2026-10-11:I14 有界漏回调补查与范围进度 **状态:本阶段受控本地验收完成,I14 与完整 I0—I16 目标继续进行。** 接续 `99aa5127`。管理员可选择佣金 / 结算、起始页、最多三页及原因,先检查确认内容再排队;进度、剩余范围和财务结果分别展示。复用原财务 / 订单维护入口,只增加发现进度和审计,没有新增支付状态修改器或并行财务执行框架。 只追加 0033,原 0000—0032 SQL、快照和 journal 条目逐字节保持。旧供应商 / 人工事件来源、历史操作者与结果保留;发现事件使用独立 `reconciliation` 来源,关联 `affiliate_scan`,不伪造签名通知。同一推广计划 / 商户 / 类型只允许一个进行中的范围;原意图重试返回已受理任务,不复位页码或完成结果。补查和人工复核共用五次 / 分钟限额。 每页最多 20 条,每次维护只读一个列表页,单次最多三页;页码、租约与重试持久保存。列表只发现编号,佣金跳过其他计划,结算仍逐个核对当前成员与商户;财务和精确订单关联由原队列分别完成。列表页成功重置连续失败次数,八次上限、90 秒租约和退避保持;最后租约过期可转复核。领取和提交均重查原管理员、近期会话和冻结的计划 / 商户 / 测试模式;读取期间撤销权限不能确认新财务资源。 官方接口按创建时间倒序分页,未核实到更新时间过滤或稳定快照游标;新记录可能使页码偏移。界面明确记录范围、发现数、财务核查 / 等待 / 复核数与未覆盖部分,不称为全量对账,也不把财务检查完成称为所有订单已核实。范围停止时保留此前等待计数,请求与终态分别审计。来源、核对日期与后续范围边界见[联盟教程](./affiliates)。 | 验收 | 结果与边界 | | --- | --- | | 全量回归 | 100 文件、1006 项通过。新增 18 项数据库补查、4 项管理面板和 1 项后台接口测试;覆盖漏回调找回原订单、列表 / 财务分阶段、重复提交、三页上限及跨页去重、其他计划、结算成员、读取前后权限 / 会话撤销、配置变化、坏分页、租约替换、八次失败、最后租约恢复、部分范围停止的等待计数、来源约束及默认关闭 | | 实际数据库与运行时 | `bun affiliates:validate-orders` 实际升级 0028—0033;单独保存 0032 人工任务,0033 后旧字段保持、新关联为空。普通账号、20 初始积分、历史购买、选择和冻结结账保持;实际 same-origin 接口拒绝普通 / 外站 / 强制状态,重复意图只排队一次。没有回调和本地财务记录时从列表发现资源;实际销毁 / 重启 workerd 后继续第二页,财务与原团队订单分别完成,积分账户及账本逐字段保持 | | 实际浏览器 | `--scan-ui` 构建真实后台并代理至自己的实际 Worker / 数据库。取消不写入;成功提交后主动丢响应,再次提交严格比较同一正文,只保留一个任务 / 请求审计。原维护入口执行后显示所选范围结束与真实财务计数,终态审计通过;英 / 西文案、哑光黑白和 320 / 390 像素无横向溢出。截图在忽略的 `.local/i14-scan-admin-*.jpg` | | 检查与构建 | 完整类型、严格 lint、格式、默认配置与 Drizzle 检查通过;邮件、官网、公开页、API、工作台、正常预览和中文文档构建通过。原空案例及文档体积提示保留,没有编造真实产品案例 | | 主预览与清理 | 六项正常服务更新后可访问;主库仍为授权 0012 / 13 条迁移,模块关闭、新联盟结构未应用。普通用户、20 积分、4 文件、原成功任务及一次尝试保持;一次性数据库 / Worker / 专用目录清理,正常配置字节保持 | 初次迁移验收比较包含新列的整行,检出预期新增空关联;改为比较所有旧字段并明确验证新关联为空,没有跳过来源与历史操作者验证。既有增量迁移测试恢复时漏删新进度表,已补齐原测试的清理,再完成最终全量回归。新审计变更没有账户前后状态,原账户审计消费者增加明确类型判断,不加入虚构状态字段。初次类型 / lint 检出测试输入声明与未使用导入,修正后全部检查通过。 所有供应商 HTTP 截获,只允许固定只读 GET。历史付款事实预先保存,没有真实支付、购买积分发放、计佣、转账或云部署;主库未自动升级 0033。证据在忽略的 `.local/i14-scan-*`,中文教程已同步启用结构及分页边界。此前授权的本地主库 0012 升级已完成,不重复迁移。 **下一项:** 准备真实 Rewardful / Stripe 的配置和验收清单,核对真实 SDK、Cookie、购买 / 退款、计佣及供应商延迟;继续 I15 云端运行准备与 I16 第二独立产品。真实环境未验前保持可选模块关闭,不以受控数据宣称真实盈利或银行到账。 ## 2026-10-11:I15 资源清单与发布前离线检查 **状态:离线准备阶段已验证,I15 云端运行与完整 I0—I16 目标继续进行。** 接续 `5a289e24`。原配置仅在开发环境声明文件存储、任务 / 死信消费者和定时维护,测试 / 正式环境仍缺失;现按环境补齐声明,不创建实际云资源。Terraform 继续只拥有 Hyperdrive 与可选 R2,Wrangler 负责 Worker / 域名 / 绑定 / 维护入口,队列创建由操作者单独执行。 新增 `bun deploy:check --target staging|production`,输出非秘密资源清单与字段问题。检查 Worker / 服务目标、资源缺失与环境串用、Hyperdrive 占位编号、存储桶命名、主队列 / 死信对应、维护入口、公开域名和发信地址。`--artifacts` 要求三个非空入口,并检查首页唯一规范链接与目标域名相同;不宣称已验证所有 SEO 页面或真实账号 / 供应商。当前模板的示例配置必须失败,正常本地预览继续可用。 发布脚本先检查资源,再以目标域名构建、检查产物,最后执行原 API → App → Web 顺序;仍不迁移数据库。持续集成仅在原已开启的发布任务选择实际目标域名,构建与部署记录共用该地址;恢复产物后的资源检查位于原远程迁移之前。纯检查任务保持可在占位配置下执行,没有自动开启发布。生成器云端 `STORAGE` 名改为 `{站点编号}-{环境}-uploads`,对齐 Terraform,保留开发命名与碰撞拒绝。 | 验收 | 结果与边界 | | --- | --- | | 回归与静态检查 | 全仓 101 文件、1028 项通过;类型、严格 lint、格式、默认配置和 Drizzle 检查通过。资源 / 产物测试包含缺绑定、环境串用、非继承变量 / 秘密名、示例值、私密输入不进入诊断、错误 / 重复规范链接及二次生成资源隔离 | | 实际入口检查 | 对当前示例配置执行测试 / 正式资源命令和原发布脚本,均在构建 / 上传前退出;没有调用上传或数据库迁移。工作流 YAML 可解析,检查步骤先于迁移,两个部署地址均来自已校验构建目标 | | 实际构建与离线打包 | 实际公开页使用测试 HTTPS 地址构建,目标产物检查通过、另一环境失败;随后完整重建本地预览。三个 Worker 的测试 / 正式环境共六次真实 Wrangler 离线打包通过,仅临时配置使用合成 Hyperdrive 编号,未上传或创建云资源 | | 中文文档与预览 | 部署、环境变量、生成站教程与计划状态同步;中文文档构建通过。主库与六项预览检查见本轮本地记录,迁移没有变化 | 初次离线验收脚本的依赖路径指向根目录,修正为脚本工作区依赖后执行。第一次同时验证旧本地产物和构建目标产物,目标构建先完成,旧产物断言因此失败;改为先验证目标通过 / 另一环境拒绝,再恢复本地构建,最终顺序验收通过。没有把失败运行计入完成证据。 所有离线打包使用临时配置、隔离工作目录和不含业务 / 云端凭据的进程变量,临时目录清理。证据在忽略的 `.local/i15-*`。未执行 Terraform 计划 / 应用、云资源创建、远程迁移、真实供应商调用或 Worker 上传;不以配置中的资源名证明资源存在。规则与后续真实验收清单见[部署教程](./deployment)。 **下一项:** 准备明确的真实测试环境与供应商配置,完成实际数据库 / 存储 / 队列 / 登录 / 支付流程、备份恢复及成本记录;I14 真实供应商和 I16 第二独立产品继续保留。此前本地主库 0012 升级已经完成,不重复应用新可选迁移。 ## 2026-10-11:I15 完整数据库归档与独立空库恢复 **状态:数据库部分已实际本地验收,I15 云端 / 对象恢复与完整 I0—I16 目标继续进行。** 接续 `8a07dfd2`。原导出默认只有结构,新增 `db:backup`、命名环境备份、`db:restore` 和实际 `db:validate-backups`。没有把原导出默认行为改成写入恢复,也不自动创建 / 清空目标或进入发布流程。 备份通过只读可重复读事务导出快照,表数据摘要与真实 `pg_dump` 读取同一快照,包含完整数据、结构及迁移记录。归档 SHA-256、大小、来源指纹、版本、时间和每表行数 / 数据摘要保存在私有清单,不保存连接、密码或客户正文。逐表排序并流式计算摘要,需要额外扫描;没有宣称零成本或零负载。临时归档仅在成功后发布为唯一目录,文件 600、目录 700。 恢复先校验文件 / 清单,再拒绝源数据库和非空目标;实际 `pg_restore` 不使用清空 / 建库选项,单事务执行,之后核对全表清单、行数与数据摘要。恢复只读取根目录 `.env.restore.local` 的独立目标,不沿用来源或 shell 地址。客户端连接通过明确环境字段传递,密码不进入参数;原始数据库 / 客户端错误不输出。对象所有者和 ACL 不照搬,后续需检查自己的应用角色与绑定。 新站生成器同时排除默认备份目录、归档文件、快照及临时目录,二次生成实际不复制源站备份;忽略规则也覆盖归档与恢复环境文件。没有修改历史迁移。R2 对象、队列消息、Worker Secrets、集群角色和恢复时间点后的供应商事实仍需另行处理,不能以数据库 `ready` 元数据证明文件存在。 | 验收 | 结果与边界 | | --- | --- | | 回归与检查 | 全仓 102 文件、1035 项通过;类型、严格 lint、格式、默认配置与 Drizzle 检查通过。7 项归档单测涵盖来源指纹、连接字段、诊断不含私密输入、损坏文件、源库拒绝及无效清单;生成器实际二次复制不带入源归档 | | 实际 PostgreSQL 与工具 | 创建三个自有回环临时库;源库完整应用 0000—0033 并保存账号、文件、配额、成功任务 / 尝试、积分账本与购买。最终备份期间有 58 次已提交并发更新,恢复后的账户余额与账本一致;全表摘要、34 条迁移、序列、约束与外键均通过核对 | | 拒绝与故障清理 | 原库、已有数据目标、空自定义模式 / 函数目标均拒绝;损坏归档在目标写入前拒绝。缺客户端时清理本次部分归档,没有发布完成目录。恢复不覆盖或删除已有数据 | | 实际命令环境 | 隔离目录执行真实备份 / 恢复命令,测试来源文件覆盖错误的 shell 地址,独立恢复文件覆盖错误的 shell 目标;恢复文件缺失明确失败。归档私有权限、清单不含客户正文 / 对象路径及输出不含连接 / 合成私人内容通过 | | 中文文档与主预览 | 新增[数据库备份与恢复](./backup-restore)并接侧栏;部署、复制新站、数据库概览及计划同步。正常主库仍为已授权 0012 / 13 条迁移,普通账号、20 积分、4 文件和原成功任务保留;自有临时数据库、归档与测试环境文件清理 | 第一轮实际验收发现跨模式表标识符被误当成单独标识符,修正为分别转义模式与表,再通过全表恢复。缺客户端验收发现运行时默认路径查询未使用修改后的 PATH,改为显式传入当前路径并复验,失败归档清理通过。失败运行不计入完成证据;临时资源仍经原清理路径删除。 证据在忽略的 `.local/i15-backup-*`。没有连接远程数据库、创建云资源、调用真实供应商、复制 R2 对象或上传归档;不把本地数据库恢复称为完整云端灾备。恢复教程明确权限重新安排、文件核查与供应商对账后再接业务流量。 **下一项:** 补文件对象与数据库的共同恢复验收,以及明确真实环境后的云端完整流程、备份策略、恢复耗时与成本记录;I14 真实供应商和 I16 两个真实独立产品继续按原计划推进。 ## 2026-10-11:I15 冻结文件检查点与私有结果共同恢复 **状态:受控本地文件 / 数据库共同恢复已验证,I15 真实云端运行及完整 I0—I16 目标继续进行。** 接续 `c45785fe`。复用已完成的数据库归档 / 恢复与原私有文件、任务、积分入口,增加对象检查点函数和实际 `files:validate-backups`。没有新增公开文件搬运接口、另起任务执行框架或修改数据迁移。 对象清单绑定具体数据库归档 SHA-256 及冻结的 `ready` 文件引用;只在源对象长度、类型、元数据与内容 SHA-256 一致后发布唯一私有目录。单文件 16 MiB、最多 100,000 引用、清单 64 MiB,按文件读取;目录 700、内容与清单 600。对象键保存在私有对象清单,不写业务日志;编号派生本机文件名,拒绝重复与路径形编号。新站生成器与忽略规则也排除对象检查点,实际二次复制不带入源归档。 恢复先确认对应数据库与冻结引用,并校验每个本地内容文件,再要求独立空桶,使用条件写入避免覆盖并发对象,读回核对完整内容,恢复原文件接口所需的类型 / 编号 / 摘要元数据。缺失、损坏或换错清单不写目标;中途存储失败可能留下部分目标对象,保持隔离,不自动清空或删除已有对象。来源停写 / 停消费仍是操作者的前置安排,函数不承诺跨数据库与 R2 原子事务。 | 验收 | 结果与边界 | | --- | --- | | 回归与检查 | 全仓 103 文件、1044 项通过;类型、严格 lint、格式、默认配置与 Drizzle 检查通过。9 项对象检查点测试涵盖正常字节 / 元数据、缺失 / 损坏源、全部本地文件先校验、错误数据库 / 引用、并发目标保留、路径 / 重复拒绝及部分写入保留;原生成器实际不复制对象归档 | | 实际任务与共同归档 | 自有两个回环数据库完整迁移、两个隔离本地 Worker / R2;实际注册两用户、上传、取得报价并确认,任务扣 3 积分、成功结算为 17,原执行次数 1。数据库归档恢复通过全表摘要,再按恢复后的文件引用归档两个真实输入 / 结果对象,不造文件元数据冒充实际对象 | | 故障与保护 | 受控破坏源内容时不发布完整检查点,临时目录清理;损坏本地内容时目标桶仍为空。已占用桶拒绝;实际 R2 对已有键的条件写入返回未写入并保留原对象,未覆盖或删除源数据 | | 原工作流与重启 | 恢复后原用户经原私有文件接口下载正确结果,匿名 401、另一用户 404、私有不缓存响应;同一原任务请求重放三次仍是原编号,多轮维护不再次执行 / 扣费,账本与账户保持。实际销毁并重启目标 Worker 后原结果仍可下载 | | 主预览与中文教程 | 数据库恢复教程扩展文件检查点、范围 / 限制与可执行验收入口,部署、计划及能力表同步。主库保持授权 0012 / 13 条迁移,普通用户、20 积分、4 文件与原成功任务不变;自有数据库、归档、R2 / Worker 目录与会话清理 | 第一次实际任务请求漏带报价确认,原计费入口正确返回 409。验收改为通过原报价接口取得并确认价格,未关闭确认门禁或降低计费要求;最终任务、扣费和恢复通过。数据与文件验证复用原任务 / 文件入口,没有为验收增加强制成功或越权下载路径。 证据在忽略的 `.local/i15-files-*`。所有网络出口受控,源 / 目标只使用自有回环数据库与本地 R2,没有调用真实支付、模型或云端服务。数据库原清单的 `externalObjectsIncluded: false` 保留,文件在独立目录;`db:backup` / `db:restore` 不自动为任意云桶搬运文件。操作步骤、对象清单隐私与暂停来源的边界见[恢复教程](./backup-restore)。 **下一项:** 准备明确真实测试环境后的云端传输 / 恢复、保留删除策略、权限与资源绑定、供应商对账和成本记录;I14 真实供应商与 I16 两个真实独立产品继续保留。 ## 2026-10-11:I15 本机归档保留预览与人工清理 **状态:本机保留 / 清理与清理后实际恢复已验,真实云端运行及完整 I0—I16 目标继续进行。** 接续 `077d5b2e`。新增 `db:backup:prune`,默认仅预览,必须明确选择目录、保留数与年龄,再以 `--apply` 执行;没有定时清理、云端生命周期设置或连接数据库的清理分支。复用原归档格式与完整内容摘要,不增加备份状态数据库或另一套归档引擎。 来源指纹与环境分别计算保留数,至少保留每组最新的一份校验通过归档;最近年龄窗口内的归档全部保留,只有数量与年龄条件都满足时才列为删除。损坏的最新归档不占有效保留名额,未来 / 倒置时间、未知内容、链接、未完成目录均保留或跳过。清理快照时数据库与同目录配套文件检查点一起移除;不改写保留归档,也不操作线上 R2 / 队列。 实际执行前重新校验全部保留与待删集合,变化在首次删除前拒绝。没有跨进程锁或文件系统事务,其他备份 / 恢复 / 搬运操作仍需暂停;中途文件系统失败可能已移除部分快照,报告完整移除数量并停止。不以 SHA 校验或保留数量宣称已通过实际恢复,也不认证配套对象内容完整。 | 验收 | 结果与边界 | | --- | --- | | 回归与检查 | 全仓 104 文件、1055 项通过;类型、严格 lint、格式、默认配置与 Drizzle 检查通过。新增 11 项保留测试覆盖分组、数量与年龄、边界、损坏最新归档、未知 / 未来条目、链接、配套对象删除及保留 / 待删 / 新增集合变化前拒绝;生成器实际二次生成带入新命令与相同保留实现,不带源归档 | | 实际 PostgreSQL | 两个自有回环数据库,完整迁移后生成三份真实归档。采用模拟的未来策略时钟达到年龄条件,不修改归档捕获时间或内容;实际移除两份后,保留归档在空库真实恢复,全表摘要、34 条迁移、22 积分余额与账本核对通过 | | 实际命令 | 通过根命令的数据库工作区转发执行预览,目录逐项保持;非法保留数拒绝且不删除。清理后再次运行 `--apply` 移除数为零;进程提供不可用数据库连接,清理命令仍完成且不使用数据库。捕获输出不含连接、邮箱或合成私人正文 | | 教程与主预览 | 中文恢复教程补齐命令、路径、分组、对象共同删除、暂停其他操作、部分失败和真实云端边界;部署、计划与能力表同步。六项预览更新,主库保持授权 0012 / 13 条迁移、普通用户、20 积分、4 文件和原成功任务 / 一次尝试;自有临时数据库与归档目录清理 | 初次测试将文件存在性检查的成功返回固定为 `undefined`,当前 Bun 返回 `null`,导致断言失败;改为验证检查正常完成与实际目录保留 / 删除,再完成全量回归,没有放宽归档、变化或分组规则。实际命令验收随后改为走根目录转发入口,确认教程所列命令可用。 证据在忽略的 `.local/i15-retention-*`。本轮只删除自有一次性验收归档,没有删除用户正常备份、迁移主库、创建云资源或调用真实供应商。保留策略不替代近期实际恢复验收、独立副本和外部付款对账,详见[中文教程](./backup-restore)。 **下一项:** 确定真实测试环境后完成云端传输 / 恢复、实际保留 / 删除与成本记录,补密钥轮换与权限验收;I14 真实供应商及 I16 两个真实独立产品继续按原计划保留。 ## 2026-10-11:I15 认证版本密钥与实际重启轮换 **状态:认证轮换已完成受控本地验收,真实云端权限 / 轮换与完整 I0—I16 目标继续进行。** 接续 `2ebb2cb4`。当前库支持版本密钥,但模板原契约只有单一值;新增可选 `BETTER_AUTH_SECRETS`,复用 Better Auth 1.7.4 的 `secrets`,不另写加密算法、Cookie 鉴权或密钥服务。 显式配置一至十个唯一版本 / 不同密钥,第一个用于新认证数据;版本必须是非负安全整数,密钥为 32—1024 字符且无空白,诊断只输出编号与字段。未配置时明确版本 0 和原基础密钥,避免库另选进程中的密钥列表;旧格式读取保留基础密钥后备。生产 / 测试环境各旧版本占位值也拒绝,示例仅增加空的可选字段,没有分发密钥或修改正常主预览值。 实际安装版本只用当前密钥验证会话 Cookie;旧版本读取加密数据不代表旧登录持续有效。模板自己的营销链接、任务接收人和运营目标摘要继续使用 `BETTER_AUTH_SECRET`,不会自动跟随认证当前版本;过渡保持基础用途,撤销旧基础密钥需要另外处理链接与选择,不用旧密钥仍在后备中宣称已撤销。 | 验收 | 结果与边界 | | --- | --- | | 全量回归 | 104 文件、1060 项通过。新增 2 项实际认证库配置 / 加解密测试、2 项环境输入测试和 1 项营销链接测试:显式版本不读其他进程列表、旧格式与版本 0 读取、版本 1 写入 / 撤销旧版、坏列表与私密错误、非开发环境旧占位值、基础用途过渡与撤销拒绝 | | 实际 Worker / 数据库 | 自有回环数据库完整迁移,独立 Worker / R2;实际注册、上传、报价确认、扣 3 积分、任务成功和私有结果下载。销毁 / 重启两次;当前密钥切换时旧 Cookie 401,原密码重新登录成功;只删除旧密钥且当前不变时新 Cookie 继续下载。原任务请求重放仍原编号 / 一次尝试,账号仍普通用户,账户 / 账本逐字段保持 17,无重复执行或扣费 | | 邮件边界与诊断 | 自有邮箱资格记录仅为合成验收条件,不冒充外部邮箱验证。实际选择接口确认后过渡期仍有效,基础密钥撤销后有效选择关闭、旧 revision 保留,原设置重新明确确认成功。实际合法 / 非法配置诊断只输出编号 / 字段,不含密钥、连接、邮箱、密码或输入正文 | | 构建与主预览 | 类型、严格 lint、格式、默认配置与 Drizzle 检查通过;完整本地预览与中文文档构建通过。新增中文轮换教程并接侧栏,环境、部署、计划和能力表同步。六项预览更新,主库保持授权 0012 / 13 条迁移、普通账号、20 积分、4 文件及原任务 / 一次尝试;自有数据库、Worker / R2 目录、邮件与会话清理 | 没有将新版官方网站的 Cookie 兼容说明直接当成本项目安装版本证据;检查实际包的当前密钥验证路径,再在 workerd 中证明旧 Cookie 拒绝与原密码可用。认证库旧格式 / 带版本解密由实际库验证,未以自写模拟算法替代;没有添加 Cookie 旧密钥绕过或降低鉴权要求。 证据在忽略的 `.local/i15-rotation-*`。真实 OAuth / Passkey 回跳、云端发布、历史密文批量处理、外部投递、数据库角色与供应商凭据仍待实际环境;营销旧链接失效也不等于用户已退订,需安排可用退订通道。操作边界及复验入口见[认证密钥轮换](./auth-key-rotation)。 **下一项:** 验收数据库应用角色、凭据变更与连接边界,再在明确真实测试环境后完成绑定 / 密钥 / 外部服务与恢复成本;I14 真实供应商和 I16 两个真实独立产品保留原范围。 ## 2026-10-11:I15 数据库应用角色与密码连接边界 **状态:受限应用角色、密码认证、授权回滚和原业务恢复已本地验收,真实云端及完整 I0—I16 目标继续进行。** 接续 `17beecda`。补强原 `db/scripts/grant-app-role.sql`,新增 `db:validate-app-role`;复用原迁移、账号、文件、队列与积分入口,没有新增公开角色管理接口、权限框架或数据迁移。 授权要求实际数据库 / 公共对象所有者运行,检查已有角色的特权属性、成员关系、全局对象所有权、数据库直接授权,以及所选库的模式、表 / 序列、列、函数与默认授权。拒绝额外权限与混合对象所有者,避免仅警告而部分跳过授权。创建角色、新密码、当前与默认权限放在同一事务中;新密码通过 `psql` 交互设置,不放明文参数 / SQL,旧 `-v password` 被拒绝。已有角色重复授权保持原密码。 | 验收 | 结果与边界 | | --- | --- | | 回归与检查 | 全仓 104 文件、1060 项通过;类型、严格 lint、格式、默认配置与 Drizzle 检查通过。中文教程构建通过;本轮权限不由 PGlite 或模拟密码认证代替,使用 PostgreSQL 17 的实际 SCRAM 实例 | | 自有权限实例 | 仅回环监听,独立目录、所有者及两份完整迁移;应用角色实际业务读写与未来表 / 序列可用。建表、模式、临时表、修改 / 删除表、清空表、重置序列和读取迁移记录拒绝;两库分别加固后另一环境账号连接拒绝,错误密码拒绝 | | 预检查与回滚 | 已有其他库对象所有者、额外建表 / 清空表 / 函数 / 默认权限、继承所有者代运行及公共表混合所有者拒绝。受控最后默认授权失败时,新角色未留下、原数据库 / 模式 / 表授权保持不变 | | 真实受限业务 | Worker 使用应用角色,实际注册普通用户、上传、报价确认、队列任务扣 3 积分、成功结算为 17;原私有结果下载内容正确,尝试次数 1。未为验收增加管理员身份或绕过报价确认 | | 密码与旧连接 | 实际交互更改密码后旧密码新连接拒绝,新密码可用;原连接后端编号不变,改密码 / 撤销 `CONNECT` 后仍可查询。明确终止本次自有后端才断开;没有对主预览或其他环境批量终止连接 | | 凭据更新与原结果 | 恢复连接授权并用新凭据重启隔离 Worker;原会话下载原结果成功,同一提交仍原任务编号。账户 / 账本逐字段保持,17 积分、一次尝试、无再次执行 / 扣费 | | 模板复制与主预览 | 实际新站及二次复制包含角色脚本、复验入口及中文教程,未复制私有环境与临时数据。六项预览可访问;新增及更新教程逐字匹配构建产物。主库保持已授权 0012 / 13 条迁移、普通用户、20 积分、4 文件与原成功任务 / 一次尝试,自有实例及目录清理 | 早期验收发现 `psql` 引入文件的引用方式没有读取目标脚本,改为同一连接顺序执行两个文件;又发现 PostgreSQL 17 的 `\quit 1` 不能产生预期错误状态,改为 SQL 异常配合 `ON_ERROR_STOP`。生成器也正确拒绝了源仓库内目标,改用自有仓库外临时目录。失败运行不计入完成证据,最终完整流程、额外授权与末尾故障回滚均实际通过;没有降低权限检查来使验收通过。 证据在忽略的 `.local/i15-role-*`。正常预览集群未改角色 / 密码 / 授权,所有迁移与断开操作仅在本轮自建实例中;没有远程迁移、云端资源或真实供应商调用。脚本只处理所选数据库及公共模式,不认证其他库的内部授权或整个集群隔离;数据库业务角色也不等于逐用户行权限。Cloudflare 官方说明更新源凭据不会自动关闭旧池,本地连接测试不能替代真实 Hyperdrive 重连验收。维护步骤见[数据库角色与凭据](./database-roles)。 **下一项:** 在明确测试环境后验收真实 Neon / Hyperdrive 权限与连接池、资源绑定 / 外部服务及恢复成本;继续按原范围推进 I14 真实供应商与 I16 两个真实独立产品,不把受控本地结果当作完整目标已完成。 ## 2026-10-11:I2 手机文件工作台与键盘删除补充验收 **状态:补齐本地手机 / 键盘 / 永久删除浏览器流程,完整 I0—I16 及真实云端目标继续进行。** 接续 `50f26f96`,核对能力表后优先处理文件工作台仍明确标记的实际界面缺口。本轮没有新增业务权限、表结构或后台平台;改动集中在原文件组件、共享附件预览的两处调用和原验收命令。 浏览器发现关闭预览 / 取消删除时焦点落到页面空白,删除失败时被临时禁用的按钮也会丢失焦点。现在预览 / 取消回到该文件的原按钮,失败回到可重试按钮,成功回到选择文件。共享预览在任务附件中也传入对应按钮引用;不读渲染阶段的 ref。列表刷新较慢时,确认删除后恢复操作焦点,且不抢走用户已主动移动的焦点。 | 验收 | 结果与边界 | | --- | --- | | 回归与构建 | 全仓 104 文件、1061 项通过;原删除失败 / 待确认 / 取消与西班牙语用例继续通过,新增预览返回焦点,并扩展成功删除后延迟刷新与失败重试焦点断言。类型、严格 lint、默认配置及完整预览构建通过 | | 实际手机布局 | 同一隔离文件工作台在 320 / 390 像素分别切换浅色 / 深色,四种组合记录实际视口、文档宽度和弹窗边界;无横向溢出,确认弹窗在视口内。手机直接刷新仍在原 `/files`,导航初始收起;不是只缩放桌面截图 | | 上传、预览与下载 | 用真实文件选择器上传长文件名 JSON、PNG 和文本;真实私有预览与键盘打开 / Escape 关闭。实际浏览器下载原 JSON 43 字节,与上传样本字节及 SHA-256 一致;PNG 实际解码为 1×1,不仅核对 HTTP 200 | | 删除与任务保护 | 实际点击 / 回车永久删除后,列表与配额更新、焦点回到选择文件。一次性库中原任务存储入口创建未派发任务,占用文本时删除真实返回 409;弹窗、文件及配额保持,失败焦点回到重试。通过原取消入口解除占用后,在同一失败弹窗回车重试成功,另一张图片仍保留 | | 真实清理终态 | 最后图片也经界面永久删除;停止前核对一次性数据库文件记录 0、文件 / 字节配额 0、实际 R2 对象 0,然后才清理测试库、Worker、回环站和目录。原文件接口 / 定时验收以最终代码另跑通过 | | 生成与资料 | 实际新站及二次复制包含最新文件工作台、共享附件调用与复验脚本,私有环境和临时数据未复制。文件教程、计划及能力表补充具体证据与 `--browser` 复验,不把本地模拟当真机或云端验证 | 列表刷新延迟的测试先证明成功删除后仍会丢失焦点,再补齐恢复,未通过扩大等待时间或降低断言使其通过。构建检查还发现共享预览的任务附件调用遗漏,补齐第二处真实调用;渲染阶段读取 ref 被 lint 拒绝,改为在关闭事件读取。最终类型、lint、行为与全仓回归通过,早期失败不计入完成证据。 证据在忽略的 `.local/i2-mobile-*` 和 `.local/evidence/files-mobile/`,包括四种布局实测数据、原件字节核对、失败提示与完成截图。所有账号 / 文件 / 未派发任务只在本轮一次性数据库,外部请求阻断;手机尺寸测试是浏览器视口,未声称真实设备。测试结束还原浏览器尺寸并关闭自有页面,原主预览账号、0012 / 13 条迁移、20 积分、4 文件和原成功任务保持。中文文档、构建产物与六项预览按最终版本核对。 **下一项:** 继续补原计划剩余实际交付,真实供应商、Neon / Hyperdrive 连接池、云端恢复成本及两个真实独立产品仍保留原范围;不以本轮文件界面验收宣布整个模板完成。 ## 2026-10-11:I6 / I9 未标记旧结账的人工恢复 **状态:补齐本地管理员核查 / 关联、永久审计与原用户恢复,完整 I0—I16 继续。** 接续 `1434fbf9`,解决能力表仍明确留待开发的旧版本结账:创建已发送但本地失去响应,且没有新请求标记,无法安全自动查找原会话。 管理员在订阅视图填写原 Stripe 会话编号,先核查再填写原因 / 确认。服务端检查新鲜站点身份、近期会话、来源、原租约、唯一客户归属和未解决插件行;分页读取固定一小时窗口的全部状态,同一本地订阅编号 / 到期时间必须只有一个候选。当前会话再次核对主体、客户、模式、环境、创建时间、期限和缺失标记;关联提交使用核查摘要,状态或原请求变化则重新核查。原幂等键仍需要人工对照供应商原始请求,本入口不将归属核对当作原请求证据。 关联只补原会话及继续地址,和两条审计在同一事务提交。审计失败全部回滚,失去响应保留请求编号重试;完成后重放也不复活或重新关联原请求。审计保留主体 / 尝试 / 会话 / 摘要 / 状态 / 原因,省略完整载荷、支付地址和凭据。用户随后通过原核查流程继续 open / complete / expired 处理,管理员关联不创建付款、退款、权益或直接解除购买阻塞。原插件保持订阅状态的唯一写入者,沿用现有协调表与审计表,没有新增 SQL 迁移或调整主预览权限。 | 验收 | 结果与边界 | | --- | --- | | 回归 | 全仓 105 文件、1080 项通过;真实 SDK / 已迁移 PGlite 验三种状态的人工关联后原插件恢复、同请求重放 / 不重复创建;歧义、错误主体 / 期限 / 标记、分页异常、摘要变化、过程中降权 / 会话撤销 / 过期,以及审计故障全部保持阻塞或回滚 | | 原生数据库与 Worker | 原 `admin:validate-payments` 增加一次性旧请求数据;实际生产 bundle / SDK 受控读取验站点权限和来源、歧义 / 状态变化、PostgreSQL 审计触发器故障、同请求修复重试及原用户接口返回 ready;全部资源实际清理。旧请求为明确测试记录,未冒充真实 Stripe 历史 | | 实际管理页 | 隔离账单账号在真实浏览器完成核查、取消重新核查、填写原因、明确确认;实际落库原会话、两条审计,账号原恢复可继续,既有积分未变。页面始终显示原订阅未完成,关联没有伪造权益。核查、取消、确认及完成有对应键盘焦点,失去响应的页面重试保留同一 ID;西班牙语失败提示另经组件验收 | | 生成与交付 | 新站及二次复制包含最新恢复入口、API、审计类型 / 展示、双语文案、原生验收及中文教程;不复制私有环境或临时目录。类型、严格 lint、数据库历史 / 默认配置和完整预览构建通过 | 早期检查发现返回状态推断泄露供应商内部类型、旧审计展示未覆盖新变更类型、速率命名未沿用现有额度,均修正并重新通过;配置关闭场景的测试改为实际前置条件错误,未放宽业务验证。实际页面核查后旧按钮消失时焦点落到空白,补上原因输入、取消返回、确认 / 重试和完成提示的焦点处理,再加载最终组件走通确认。失败运行不计入完成。 证据在忽略的 `.local/legacy-checkout-*` 和 `.local/evidence/legacy-checkout/linked.jpg`。正常主预览保留 0012 / 13 条迁移、原账号、20 积分、4 文件、原成功任务和 1 次执行;中文资料、六项预览与最终构建核对。测试没有真实供应商写入、远程迁移或云部署。 **下一项:** I6 真实月付 / 年付沙箱和生产绑定仍待明确环境;继续推进 I7 真实处理器、I14 真实供应商、I15 云端权限 / 恢复成本与 I16 两个真实独立产品,受控本地验收不等于整个目标完成。 ## 2026-10-11:I9 审计记录精确检索 **状态:补齐本地历史检索与实际页面验收,完整 I0—I16 继续。** 接续 `fc674fa9`。原管理页只能翻页查记录,现在可组合审计目标、执行者和请求编号;粘贴完成编号也能同时找到请求与完成记录。三个字段只在提交后应用,清空筛选重置页码;每页 20 条,按时间及记录编号稳定排序,不提供模糊匹配或私有任务正文搜索。 原审计展示移到独立组件,查询键包含当前管理员、筛选及页码。查询失败隐藏旧缓存,不将先前读到的历史当成权限仍有效的证明;沿用新鲜站点权限检查。记录明确展示原请求和记录编号,说明历史不等于当前付款或任务状态;默认保留审计,没有定时删除。账号删除后保留历史标识,这些记录仍用于行政操作幂等及首次管理员证据。 | 验收 | 结果与边界 | | --- | --- | | 回归 | 全仓 106 文件、1085 项通过;实际迁移 PGlite 验同一 UUID 的不同执行者隔离、组合条件、完成编号归一化、部分编号 / 通配符不匹配、严格参数与稳定分页。删除目标账号仍能查记录,管理员降权后读取拒绝;原私有输入及摘要未增加到返回值 | | 原生数据库与 Worker | 原 `admin:validate-payments` 使用独立完整迁移库和实际 Worker,读取人工旧结账的两条真实落库记录;普通账号 403、额外字段 400。通过 PostgreSQL 目录核对新增请求字段的有效 btree 索引;保留原支付对账和关联回滚验收 | | 浏览器 | 最终组件实际填三项精确筛选、刷新、部分编号空结果及清空。请求 / 完成两条记录同屏,搜索和刷新后焦点恢复;320 / 390 像素分别检查黑白主题,四种组合无横向溢出。停止前核对审计数量与原积分不变,测试库及 Worker 已清理;视口验收不声称真机测试 | | 结构与迁移 | 只新增 `0034_admin_audit_request_lookup.sql` 及对应快照 / 历史条目,不修改旧 SQL。旧表已有请求字段,正常预览继续兼容 0012 / 13 条历史;新索引只在一次性库验收。正式选择升级环境后应运行完整增量历史,不能单独跳过中间迁移 | 首次原生索引断言错误地依赖数据库输出 SQL 的引号形式,改为读取真实索引属性;未降低索引有效性要求。实际点击又暴露禁用期间焦点丢失,补齐完成 / 失败后的恢复;短请求也通过成功 / 错误更新时间触发,且不抢用户主动移到输入框的焦点。最终组件复验 2 文件、10 项通过,失败运行不计入完成。 证据在忽略的 `.local/audit-search-*` 和 `.local/evidence/audit-search/`。中文审计教程、计划和能力表同步;正常主预览继续保留原普通账号、20 积分、4 文件及原成功任务 / 一次尝试。没有真实供应商请求、远程迁移或云部署。具体站点的审计保留期限、归档和隐私处理仍需实际政策,不能直接按备份保留天数清理源审计。 **下一项:** 继续原计划的实际产品处理器和生产环境验收;Image to SVG 首站及真实测试环境仍待用户选择,先推进不依赖它们的模板交付。I7、I14、I15 云端及 I16 两个真实独立产品保留原范围。 ## 2026-10-11:I15 已有站增量升级与旧代码回退 **状态:补齐 0012 已发布基线到当前历史的本地升级证据,完整 I0—I16 继续。** 接续 `0feb56b7`。新增 `db:validate-upgrades` 及中文升级演练说明;没有改业务接口、生成器覆盖规则或 SQL 历史,也没有升级正常主库。 基线为实际 Git 提交 `3e6dd9df578e2f927a5493a2b06f1dbcc14f8293`,不是当前源码另起一个 Worker。读取完整旧版本归档,以旧锁文件安装 / 构建邮件;仅在副本把文本任务价格由 0 改为 3,作为明确的产品定价配置。原注册、文件、报价、任务及队列入口产生一个已交付任务;第二个由旧任务存储入口创建、尚未派发,代表提交和投递之间的崩溃边界。 | 验收 | 结果与边界 | | --- | --- | | 已有数据 | 旧 Worker 实际注册普通账号,上传文本、确认 3 积分报价、Queue / R2 交付并下载私有结果。第二次预扣后余额 14,任务仍 pending / reserved;旧表全部原列和行保存摘要,迁移编号 / 哈希 / 时间保存原值 | | 故障升级 | 仅在私有迁移副本的最后语句注入除零错误,明确核对 PostgreSQL `22012`,证明跑到末尾;整批增量回滚、旧公共字段 / 原历史保留,新增密钥表及请求索引不存在。内部序列间隔不冒充数据丢失 | | 正常升级与重跑 | 使用未修改的完整历史升级到 0034 / 35 条;旧公共字段摘要保持、原 13 条记录保持。再次迁移不重复应用,业务和历史仍保持;没有跳过中间增量或修改已发布 SQL | | 新版与在途恢复 | 同一 Cookie 下载原结果;原请求返回原编号。实际维护入口补投第二个任务、Queue 处理、R2 私有下载内容正确;余额仍 14、两条预扣、各任务尝试 1,账号仍普通用户 | | 旧代码回退 | 再启动原基线 Worker,使用扩展后的数据库及原资源下载两份结果、重放原请求;原业务字段摘要保持。正常请求推进限流计数,运行阶段单独排除该计数表,停机迁移阶段仍完整核对 | 首次请求把报价响应的余额一并放入严格报价输入,被原接口正确拒绝;改为只提交价格和版本,没有放宽 API。随后迁移记录前缀比较因数据库驱动返回 `Result` 子类与普通数组原型不同而失败,改为比较真实记录数组;原行数、编号、哈希和时间继续保留严格断言。失败运行均清理资源,不计入完成;最终完整三次 Worker 启动、故障 / 正常 / 重复迁移及原结果恢复实际通过。 本轮不改产品行为,验证重点是实际旧依赖 / workerd / PostgreSQL 的端到端演练与新脚本的类型、严格 lint、格式和中文文档构建,不以重复原全仓测试代替升级证据。资料同步升级、部署、能力与计划;新站及二次复制保留复验入口,但没有原 Git 历史的副本必须在有历史的源仓库验收。 证据在忽略的 `.local/i15-upgrades-*`。自有旧版本归档、安装目录、数据库、Queue / R2 与 Worker 清理;原配置、正常主库 0012 / 13 条、普通账号、20 积分、4 文件及原任务 / 一次尝试保持。没有真实供应商、远程迁移或云部署。旧付款 / 联盟存量、其他基线、产品自定义处理器、真实云端混合发布及费用需分别验,不能据此宣布 I15 或 I16 完成。 **下一项:** 按完整计划继续真实处理器 / 第一产品、真实供应商、云端升级 / 恢复与运行费用;已有站合并自定义代码仍按明确差异在产品分支处理,生成器不覆盖现有产品。 ## 2026-10-11 · I16 开发源码交付包与安装验收 **状态:分发准备的本地验收完成,完整 I0—I16 目标继续进行。** 接续 `48c0771f`。新增 `site:package`,从干净的确切 Git 提交归档并复用原新站生成器;没有新增产品复制规则、改数据迁移或把模板提升为正式销售版本。产品 README 改为中文安装与配置说明。 | 验收 | 结果 | | --- | --- | | 实际源码交付 | `c7ba533f` 的提交归档生成独立产品,压缩包与最后写入的清单记录来源、产品身份、835 个文件的字节数 / SHA-256、工作区直接依赖声明和原许可证 | | 完整性与复制边界 | 实际解压逐文件核对;LICENSE / THIRD-PARTY-NOTICES 原文保持;无 Git、私有配置、本地数据、依赖 / 构建产物及官网源码;解压产品可再次生成新站并保留模板来源 | | 拒绝与清理 | 已有输出目录保留;未提交源代码、子目录来源、无效端口、源码符号链接、损坏归档与错误归档路径拒绝;失败只清理本次拥有的新输出 | | 实际独立安装 | 新空目录中普通安装更新产品工作区锁文件,再冻结安装成功;使用本次随机认证密钥,离线配置检查、公开页 / 工作台 / 邮件与中文文档构建通过 | | 检查与教程 | 两个测试文件共 7 项通过;类型、严格 lint、格式、配置诊断、数据库历史检查和文档构建通过;交付教程、侧栏、能力、计划和复制说明同步 | | 主预览保护 | 六个预览仍正常,主库保持已授权 0012 / 13 条迁移,普通账号、20 积分、4 文件和原任务 / 一次尝试保留;本次解压源码、依赖和私有验收配置清理 | 清单中的依赖是源码直接声明及版本范围,不是安装解析清单、全部传递依赖或完整许可审计。归档摘要仅校验这一份文件,不能认证作者身份;校验命令不解压或执行源码。产品来源提交与模板原始来源分别记录,生成时间不同可能产生不同归档。 本轮没有连接真实模型 / 支付供应商、远程迁移或云部署,也没有宣称两个样例是两个真实上线产品。开发包与本地证据保存在忽略的 `.local/distribution/` 和 `.local/i16-package-*`;正式分发、价格 / 授权 / 支持和完整 I16 验收仍待原计划条件满足。 **下一项:** 按已验证需求确定第一真实处理器及产品,在明确测试环境后完成真实服务、独立部署、成本与第二产品复用;完整 I7、I14、I15 和 I16 范围保持。 ## 2026-10-11 · I16 安装后的依赖版本与许可报告 **状态:依赖核查资料已实际本地验收,完整 I0—I16 继续。** 接续 `ee7296f7`。交付包原先只保存直接声明的版本范围,新增 `site:dependencies`,复用 Bun 已有的许可报告功能;导出准确版本、报告标签及分组,移除本机路径、作者、描述和主页,并记录当前锁文件摘要、工具、系统 / 架构和范围。报告不会自动改第三方声明或判断授权。 | 验收 | 结果 | | --- | --- | | 实际独立产品 | `a190cd12` 已提交归档解压后逐文件核对摘要,普通安装及冻结再安装成功;在解压产品中实际运行两种导出 CLI | | 全部 / 生产范围 | 当前本机样本分别为 1180 / 627 个依赖版本;包含实际 Astryx、StyleX 和 Better Auth。全部范围含开发 / 构建,生产范围依照 Bun 图,不代表最终构建产物 | | 文件与版本关联 | 两份报告的摘要均对应本次安装后的同一 `bun.lock`,导出前后锁文件字节不变;附保留的安装后锁文件。导出资料不含模板 / 临时目录绝对路径,原 LICENSE / THIRD-PARTY-NOTICES 保持 | | 未知标签核查 | 全部报告中 `khroma@2.1.0` 为 `Unknown`,生产报告没有未知标签。实际随包 `package.json` 无许可字段,小写 `license` 原文标明 MIT;报告不覆盖原标签,教程记录二者差异 | | 拒绝与回归 | 原有报告不可覆盖;未安装的目录不自动安装或生成报告;无效数据、重复版本 / 冲突声明拒绝。生成器、交付包与依赖报告三个文件共 10 项测试,以及类型、严格 lint、格式、配置诊断和中文文档构建通过 | 首轮验收脚本误写 Astryx 包名为 `@astryx/react`,在真实导出结果核对时失败;改为源码实际声明的 `@astryxdesign/core` 后重新完成整套独立产品验收,没有放宽产品检查。失败与成功两次自有安装目录均清理,完成证据为忽略的 `.local/i16-dependencies-native-final.log`。 报告来自本平台当前安装,不能代替其他平台、原许可 / 二进制 / 素材核查或完整法律审计。实际核查资料及安装后锁文件放在 `.local/distribution/agentbuff-dependencies-a190cd12-verified/`。没有主库迁移、真实供应商调用、云部署或正式销售发布。 **下一项:** 保持真实处理器 / 产品、真实服务、两站部署复用和费用验收的原依赖;其他平台依赖、分发素材与供应商条款在对应实际交付时逐项核对,不以依赖报告宣布 I16 完成。 ## 2026-10-11 · 全计划核对与独立测试域名 **状态:独立测试配置与本地产物准备完成,云端发布仍未执行,完整 I0—I16 继续。** 接续 `c910fcdf`。逐项保留原计划 17 阶段、94 条任务与 17 条阶段验收,新增[当前验收核对](./completion-audit-2026-10-11)及中文侧栏入口;纠正模块、语言、事件观测与环境示例中的过时状态,仍明确保留真实模型 / 供应商、云端运行 / 费用 / 恢复和两个真实产品的门槛。 用户指定 `stack.agentbuff.dev` 后,只修改 API 与 Web 的 `staging` 公开地址及自定义域名。复制检查发现旧生成器按源域名是否以 `staging` 开头推断环境,会让任意命名的测试入口复制成正式入口;复用原 JSONC 配置编辑器,按实际环境分别生成正式、测试域名,去除正则猜测。源域名不再带入新站,原注释和其他配置保持;不支持的非自定义域名路由明确拒绝。 | 验收 | 结果与边界 | | --- | --- | | 原源码全仓回归 | `c910fcdf` 基线 108 文件、1092 项通过;随后域名 / 生成器增量复验为四文件、32 项通过,不混记为新增全仓回归 | | 实际复制 | 原生成器测试实际生成与二次生成,分别核对正式 / 测试 / 本地入口;第二次来源刻意使用没有 staging 前缀的自定义测试域名,原资源重置和品牌 / 归档排除仍通过 | | 配置和隔离 | 新测试域名离线检查已接受;只剩两个 Hyperdrive 占位及测试发信地址三个字段错误。逐字段对比原配置,开发与正式环境保持;没有用假编号消除问题 | | 独立构建产物 | 公开页、工作台、邮件输出到本次独立目录;入口非空、17 页规范链接及 sitemap / RSS 使用 `https://stack.agentbuff.dev`,主预览产物未被覆盖。仅检查构建,不代表域名 / 证书在线 | | 云端身份 | 本机 Wrangler 只读检查返回未登录;私有仓库可读的 Actions 秘密名称列表为空。未能验证账号、资源、域名权限或供应商;不据此称远程资源不存在 | | 代码和中文教程 | 类型、严格 lint、格式、中文文档构建及主预览保护完成;正常库保持 0012 / 13 条迁移、普通账号、20 积分、4 文件和原任务 / 一次尝试 | 首次临时构建的命令位置只打印工具帮助,未产生有效构建;改为在对应工作区执行后核对真实输出。验收脚本首次未从依赖所属工作区解析 JSONC 包,调整导入后重新完整核对通过。失败尝试不计入构建验收,没有改发布门禁或放宽域名断言。 本机证据在忽略的 `.local/completion-audit-*` 和 `.local/staging-domain-*`。本轮未创建云资源、执行远程迁移、上传 Worker、发送真实邮件或调用模型 / 支付接口。 **下一项:** 在本机完成 Cloudflare 登录与独立 Neon 测试库准备,核对域名所属账号、真实资源及发信配置;第一真实任务仍等用户选择,再接入已有任务 / 扣返 / 结果链路。完整 I7、I14、I15、I16 条件继续保留。 ## 2026-10-11 · I7 候选图片转矢量接口核对 **状态:完成官方契约与源码接入差异核对,真实处理器仍未实现,完整 I0—I16 继续。** 接续 `2642fd42`。沿用用户此前关注的 Image to SVG 作为候选研究,没有代替用户确认首个产品;新增[中文接入核对](./image-to-svg-contract)及侧栏入口,模块和全计划核对同步。 实际读取官方转换、输入 / 结果、限额、价格、错误和 OpenAPI 资料,保存公开 JSON 规格、获取时间与摘要。解析请求 / 响应引用,确认 JSON 数据 URL、单个图片对象、Base64 格式与计费单位。发现二进制字段在说明和规格不一致、转换模型版本未公开,以及本次规格没有幂等或转换任务查询 / 取消入口;明确区分图片编号、请求编号、本站处理器版本与供应商模型版本。 这改变下一项接入决策:优先用已被两份资料共同描述的 JSON 数据 URL 与 Base64 结果;付费调用的不确定超时沿用待核对,不能照搬一般重试建议;接入前还需确认供应商版本与恢复能力是否满足产品要求。当前报价推算与实际单位 / 已核实费用分开,不能把用户积分返还当作供应商没有成本。源码逐项确认原任务提交、输入 pin、消费者、结果命名与记录仍按文本实现,记录需要扩展的位置;没有先堆多个模型适配器、未使用的密钥或假响应。 本轮只运行公开规格的字段 / 引用 / 摘要核对、中文文档构建、格式检查及实际教程读取;没有业务代码变更,不重新用全仓回归代替接口证据。本机 Cloudflare 只读登录复查仍为未登录,没有修改正常主库、发送图片、调用收费接口、创建资源或发布。资料在忽略的 `.local/recraft-contract-2026-10-11/`,不是成功转换样本或成本账单。 **下一项:** 首个任务与实际测试环境准备到位后,实现一个明确处理器、增量执行记录、SVG 内容检查与原图对照预览,完成受控故障和真实固定样本质量 / 费用验收。模型未选择 / 未接入期间遵守原 I7.6,图片转换保持待开发;I15 云端与 I16 两个真实独立产品门槛保持。 ## 2026-10-11 · 干净检出的类型检查与产品复制 **状态:本地复现和修复完成,完整 I0—I16 继续。** 接续 `dcac4f60`。继续核对时发现该提交与前一提交的 GitHub Build 在类型检查失败:官网博客集合未生成类型,内容、RSS 与 sitemap 共六处隐式宽类型错误。此前本地检查有 Astro 构建留下的生成文件,不能证明首次检出可检查。 统一 `bun typecheck` 先执行公开站 / 官网的 `astro sync`,成功后再检查 TypeScript 项目图。两个 Astro 工作区分别增加同步命令,生成器清除产品中官网的类型检查过滤器;原业务接口、依赖版本、数据库历史与严格类型设置保持。检查准备放在用户与 CI 共用入口,不只修复 CI;同步失败会停止检查。中文快速开始和类型工具链决策同步。 | 实际验收 | 结果 | | --- | --- | | 旧命令复现 | 从 `dcac4f60` 的 Git 归档建立无生成目录的独立源码,以原锁冻结安装;原强制类型检查实际出现与 GitHub 相同的六处 `TS7006` | | 修复后的干净检查 | 在上述副本仅应用四个命令 / 生成器文件变更,实际生成两个工作区的 Astro 类型,强制类型检查通过;安装锁文件字节保持 | | 类型仍约束数据 | 在副本临时索引不存在的博客字段,强制检查明确返回 `TS2339`;恢复原文件后再次通过,没有给集合加宽类型 | | 新产品首次安装 | 由副本实际生成独立产品,不含官网 / Astro 缓存;普通安装、冻结安装与仅公开站的强制类型检查通过,检查前后产品锁文件字节保持 | | 相关回归 | 生成器与交付包两文件、7 项通过;本地强制类型、严格 lint、公开站 113 文件与官网 49 文件模板检查均通过,模板检查无错误 / 警告 | 干净归档、源码与产品安装目录均为本次自有,成功后清理;证据在忽略的 `.local/ci-fresh-*`、`.local/ci-product-*` 和 `.local/ci-typecheck-*`。本次新命令不连接数据库或供应商,不上传代码;Cloudflare 只读登录复查仍为未登录,目标测试配置文件仍未准备。真实任务选择、外部服务、云端 / 费用 / 恢复及两个真实产品条件继续保留。 **下一项:** 推送后核对实际 GitHub 干净环境的完整检查结果,再继续真实任务与测试环境的准备;不能以本地修复通过宣布线上检查或完整目标完成。 同一门禁的后续项目图核对发现,公开站已有的通配路径没有纳入隐藏的 Astro 类型入口;官网则显式包含该文件。为公开站补齐 `.astro/types.d.ts`,避免新增集合时查询退化为宽类型。在另一份实际安装的生成产品中加入自有内容集合:移除入口后,错误字段可通过检查;恢复入口后明确返回 `TS2339`,改用正确字段后通过。实际项目图包含内容生成文件,检查不改锁文件;自有安装目录已清理。证据在 `.local/ci-product-type-graph-*`,不属于真实产品上线或模型验收。 ## 2026-10-11 · 官网域名用途澄清与配置 **当前范围:先做好模板官网配置,暂不云端发布,暂不接真实产品。** 用户明确 `stack.agentbuff.dev` 用于 AgentBuff Stack 官网。此前将它配置为产品测试域名是理解错误;撤回 API / Web 的对应设置,产品测试环境恢复 `staging.example.com` 示例,并在 `apps/website/wrangler.jsonc` 单独声明官网自定义域名。官网是静态站,不需要产品数据库、Hyperdrive、任务队列或模型凭据。 新增 `.env.website.example`,只保存已确认的官网域名;公开中文教程和模板演示地址留空,正式构建保持缺项拒绝,避免发布本地地址或猜测的域名。官网示例不会进入生成产品;已有本地教程与模板样例演示继续使用原端口。部署、官网、环境与全计划核对的中文教程同步澄清。 此前 `925a2047` 的实际 GitHub 完整检查通过:108 个测试文件、1093 项测试与所有应用 / 文档构建成功,部署步骤未执行。此结果属于该提交,不冒充当前配置修改的回归。本次官网独立目录构建与中文教程构建通过,生成器 / 产品发布检查两文件 24 项测试通过;原生核对确认官网只声明静态资源、API / Web 仅撤回错误域名,实际生成产品没有官网及专用环境文件。直接加载未补齐链接的官网示例时,正式构建明确拒绝而未生成发布产物;自有生成目录已清理。证据在忽略的 `.local/official-website-domain-*`。历史域名构建与接口研究仍是历史证据,不说明官网上线、产品上线或模型已接入。 原 I0—I16 完整验收条件保留,真实产品和云端阶段按当前要求暂缓;当前官网配置不以完成这些后续阶段为前置条件。没有创建云资源、迁移数据库、修改本地预览数据、发布站点或调用真实模型。 ## 2026-10-11 · 模板、官网及核心教程中英文国际化 **当前范围:全站双语已完成本地验收,暂不接真实产品、暂不发布云端。** 接续 `33e0e827`。用户确认核心使用指南和教程界面双语,历史记录保留中文。产品默认启用英文 / 简体中文,保留可选西语;官网独立维护自己的双语内容,不导入产品运行配置。英文产品路径保持,中文使用 `/zh/`;教程原中文路径保持,新增 `/en/` 核心指南。 扩展已有语言登记、公开页面组件、工作台地址改写和完整字典,复用同一套业务流程。覆盖认证 / 团队 / 管理、工具交互 / FAQ、文件 / 任务 / 购买、推荐 / 营销同意及邮件,日期和金额按语言展示;原始输入、文件名、标识、执行历史和账本原文不改写。官网首页 / 功能 / 案例 / 定价 / 博客共用页面组件,已有两篇文章提供完整中文译文,未制造真实产品案例。 | 验收 | 结果与边界 | | --- | --- | | 全仓回归 | 109 个测试文件、1108 项通过;涵盖中文请求 / 捕获邮件、认证回跳、真实路由树、语言停用、原邮件载荷兼容、通知重试、营销同意及支付原请求保持 | | 编译与页面 | 类型、严格 lint、格式、配置诊断和迁移元数据检查通过;产品公开页 119 文件、官网 63 文件页面检查无错误 / 警告;产品 33 页、官网 14 页和工作台 / 邮件实际构建成功 | | 核心教程 | 新增 20 篇英文核心指南及英文入口;中文语言配置教程同步重写。搜索、目录、页尾、主题与代码复制提示按语言展示;英文指南能直接回到对应中文页,中文历史入口明确标注。文档实际构建通过 | | 内容与搜索元数据 | 页面、RSS、站点地图及规范 / 语言替代链接只指向实际发布版本;中文缺译文不混入英文,草稿原稿不发布其译文。官网校验重复身份、孤立译文与空正文,沿用实际案例要求 | | 原生浏览器 | 中文官网完整导航 / 文案、中文 JSON 格式化成功及错误提示、已登录工作台与原任务详情实际可用;任务切成英文并刷新后保留同一标识和记录。英文核心指南实际显示英文侧栏、复制按钮和英文搜索结果,切回中文同一指南成功 | | 独立复制 | 实际生成、首次安装并检查独立产品,类型和邮件 / 公开页 / 工作台构建通过;生成站包含中文工具 / 博客、20 篇英文指南和 0035 迁移,不含模板官网。将生成站改为仅英文后重新构建,中文页面目录、站点地图与替代链接消失 | | 数据兼容 | 原字段虽为文本,四处检查约束原来仅允许英文 / 西语。新增 `0035_chinese_locales.sql` 放宽对应约束;隔离测试核对旧偏好 / 待投递原载荷保留,随后可保存中文。未修改既有迁移,不在主库执行 | | 主预览 | 六个自有预览已重启;主库仍为 0012 / 13 条迁移、2 个普通账号 / 0 管理员、20 积分、4 文件和 1 个原任务。浏览器只读核对后再次确认状态保持 | 首轮增量测试中的西语固定回跳断言未随中文参数化,按当前语言修正后完整回归通过。独立验收最初选择源码内部目录,被生成器按原隔离规则拒绝;随后改用本次自有临时目录完成生成和构建,没有放宽生成器。窄屏浏览器检查遇到控制通道超时,已恢复默认尺寸,本轮不将其记为移动端验收。所有临时证据保存在忽略的 `.local/i18n/`,自有安装目录完成后清理。 中文语言持久化的可选模块需在实际目标库升级完整增量历史后启用;默认主预览仍不启用通知 / 营销 / 推广等后续表。真实邮件送达、托管支付、模型、云端及两个真实产品的验收条件保留,没有调用供应商、云部署或正式销售发布。 **下一项:** 继续本地模板与官网迭代,依据实际使用排查双语措辞与配置;真实产品接入和云端发布等待用户重新安排。历史记录保留中文,不把当前双语页面和样例构建当作真实产品上线。 ## 2026-10-11 · 静态官网与双语教程发布准备 **当前范围:用户已授权发布 `stack.agentbuff.dev` 官网及 `/docs/` 双语教程,暂时隐藏演示;产品 Worker、数据库和真实供应商仍不在本次范围内。** 接续 `76179d69`。这项授权更新此前暂不发布的官网安排,不表示整个模板云端验收完成。 官网公开链接改为可选:缺失目标隐藏入口,已填写公开地址拒绝本地地址;本地预览保留原默认端口。教程支持可配置挂载路径,导航、配对语言切换、搜索资源、规范链接、站点地图和模型阅读入口统一带 `/docs/` 前缀;历史中文页不伪造英文替代链接。 `website:package` 先核对官网域名及教程入口,再将官网与教程构建到新的忽略目录,产生专用 Worker 配置、合并站点地图索引和目录外版本清单。`website:deploy` 也使用同一构建入口,避免上传仅有官网或本地预览地址的旧产物。生成独立产品排除官网专用发布脚本。 | 验收 | 结果与边界 | | --- | --- | | 增量回归 | 公开 URL、教程地址与独立生成器共 3 文件、7 项通过;核对生成产品确实不含官网发布器 | | 静态检查 | 类型、严格 lint 与格式检查通过;官网页面检查 65 文件、零错误、零警告 | | 合并发布包 | 官网 14 页与双语教程实际构建通过;产物规范地址、教程资源及站点地图均使用官网目标地址;演示入口隐藏 | | Cloudflare 离线打包 | 专用配置 631 项静态资源打包成功,没有产品服务绑定或数据库绑定;离线成功不表示线上发布 | | 原生浏览器 | 实际静态 Worker 中中文官网无演示按钮;英文快速开始切到同一中文指南成功,中文文档搜索实际返回正确挂载路径 | | 主预览 | 只读核对原数据库迁移、账号角色、积分、文件及任务与此前快照一致;发布包没有覆盖现有预览产物 | 本机 Wrangler 尚未认证,登录页面已交给用户完成授权,当前未上传或验证线上域名。第一次登录请求超时,不作为已登录证据。本地安装的 Worker 运行时只支持到 `2026-09-17`,静态预览使用命令行兼容日期覆盖完成验证,正式配置的 `2026-10-09` 保持。证据保存在忽略的 `.local/website-cloud/` 和 `.local/website-release-*`。 **下一项:** 完成 Cloudflare 登录与目标域名权限核对,上传同一份已检查静态产物,再检查实际 HTTPS 页面、双语教程、搜索和站点地图。产品接入、数据库及原 I0—I16 完整验收条件继续保留。 --- --- url: /docs/agentbuff-stack/auth-mail.md --- # 账号与邮件 ## 配置入口 网站选项:`websiteConfig.auth`。服务端:`apps/api/lib/auth.ts`。登录组件:`apps/app/components/auth/`。环境与诊断见 [环境变量](./env.md)。 密码、OTP 和 Passkey 的 UI 与服务端使用相同配置。Google / GitHub 的按钮列表来自 `config.socialProviders`,只有完整凭据组才出现。OAuth Client Secret 从不交给前端。 ## 第三方登录 * Google 回调:`APP_ORIGIN/api/auth/callback/google`。 * GitHub 回调:`APP_ORIGIN/api/auth/callback/github`。 * 本地示例:`http://localhost:4410/api/auth/callback/github`。 回调路径必须与第三方应用配置相同。参考 [Better Auth GitHub 文档](https://better-auth.com/docs/authentication/github):GitHub App 还需要读取账号邮箱的权限;OAuth App 与 GitHub App 的设置不同。真实授权仍待验收,不能因按钮出现就标记为完成。 ## 邮件 所有模板发送经过 `apps/api/lib/email.ts` 的 `sendEmail()`,提供纯文本与可选 HTML,验证收件邮箱。邮箱密码在非开发环境要求邮件验证;开发环境为了隔离预览不强制验证。 `preview:start` 和 `bun dev` 都使用真实 Workers 本地运行时,通过仅在本地注入的 `LOCAL_MAIL` 绑定捕获邮件,写入被忽略的 `.local/outbox.jsonl`。捕获失败会报错,不会回退为外发;即使电脑上有真实 Resend key,本地运行仍只捕获邮件。文件包含验证码和链接,只留在本机,服务日志不打印 OTP。 云端不配置 `LOCAL_MAIL`,生产 Worker 使用 Resend。线上需要已验证的发信域及有效 API key,真实送达仍需单独验收。 ## 即时邮件语言 账号验证、重置、验证码与组织邀请已提供英文 / 西班牙语的主题、预览、HTML 和纯文本。默认仍只启用英文;开启西班牙语后,认证客户端按当前页面发送 `x-agentbuff-locale`,API 拒绝未启用语言并回退英文。邀请使用发起者的页面语言,不自动推断接收者语言。签名链接、验证码、角色和原邀请 ID 不翻译,语言不会改变权限。字典位置和检查步骤见[产品语言](./languages)。 当前验证与重置令牌分别有效 1 小时,验证码默认 5 分钟,邀请 48 小时;验证邮件原有的 24 小时说明已更正为实际期限。任务邮件已有独立的持久化语言选择及重试,旧投递保留原英文载荷,见[任务邮件](./task-notifications)。营销确认邮件也已保存同意语言并按原语言重试,旧投递保留原英文载荷,见[邮件订阅](./marketing-subscriptions)。真实发信域与邮箱投递仍须单独验收。 ## 营销邮件与账号邮件分开 公开邮件订阅和候补名单使用单独状态,不会把注册自动当成营销同意,也不创建账号。确认 / 退订和发信重试第一阶段已本地验收;联系人同步及签名回调第二阶段已本地验收,真实投递仍待验。账号验证、邀请和恢复邮件不受营销退订影响,见[邮件订阅与候补名单](./marketing-subscriptions)。 ## 团队邀请 在工作台「Members」选择团队,邮箱已验证的 owner / admin 可以输入邮箱及 member / admin 角色并发送邀请。普通成员不会获得邀请表单。邀请有效期 48 小时;待处理邀请可以重新发送、延长有效期或取消,历史按每页 20 条展示。过期邀请再次发送时会生成新 ID,旧记录保留;已拒绝、已取消的地址也能再次邀请。已加入团队的账号不能重复邀请。 邀请邮件的链接是 `APP_ORIGIN/invitations?id=...`,启用西班牙语且发起者当前选择该语言时使用 `APP_ORIGIN/es/invitations?id=...`。未登录者会跳转登录并保留该链接。接收者必须使用被邀请的邮箱且邮箱已验证;本地密码注册允许跳过验证,但邀请流程仍要求验证,可通过邮箱验证码登录完成。接受后由 Better Auth 新增成员、更新会话的 active organization,页面同步刷新工作区。拒绝不会添加成员。错误邮箱、过期、已使用或已取消的 ID 均不能接受。个人文件、任务、结果与积分不会随加入团队共享。 维护入口:`apps/api/lib/organization-mutations.ts`、`apps/api/lib/auth.ts`、`apps/api/routers/organization.ts`、`apps/email/templates/organization-invitation.tsx`。邀请决策与成员降级 / 移除 / 退出使用组织行锁,真实角色与邀请状态在锁内重新读取,成员仍由认证插件写入。认证错误响应会触发事务回滚。新增迁移 `0011_simple_inhumans.sql` 用只覆盖 pending 的邮箱大小写无关索引替换旧的终身唯一约束;保留 0000—0010 历史。 邮件在数据库提交后发送。若发送失败,接口返回 503,明确说明邀请已保存;列表仍显示 pending,可选择 Resend。这里尚未实现持久化邮件任务:若进程在提交后、发送前终止,管理员需要重新发送,I12 将接入可靠事件交付。真实发信域、垃圾箱归类与实际收件仍须独立验证。 运行 `bun organizations:validate` 会创建、迁移并最终删除临时 loopback PostgreSQL,使用八个独立连接检查重复邀请、并发接受 / 拒绝 / 取消、最后一名 owner 和套餐最后一个名额。另用认证插件的可信 `addMember` 接口并发验证实际写入门禁,再启动真实 Worker 检查捕获邮件、接受、角色修改、移除后的旧会话和退出流程。付费人数使用本地订阅夹具;所有 Stripe SDK HTTP 被本地出站服务拦截,邮件仅捕获,不访问真实供应商;不会迁移正常预览。可选 `ORGANIZATION_UI_ACCEPTANCE=1 bun organizations:validate` 保留临时 `http://127.0.0.1:14310/members` 供浏览器操作(需先构建 app)。测试结束创建 `.local/organization-ui-stop`,脚本会销毁服务和临时数据库;不要把临时 fixture 凭据用于真实站点。 ## 成员人数与管理 人数来自 `websiteConfig.plans`:Free 1 人、Starter 5 人、Pro 50 人,所有者也占名额。账单页面与成员写入复用 `apps/api/lib/subscription-access.ts`,按团队自身的当前订阅、试用 / 周期结束、取消日期判断;个人订阅不会升级团队。支付未启用或付费访问已过期时使用 Free 上限,既有成员不自动删除,但必须先释放名额或恢复有效套餐才能新增成员。待处理邀请不占名额,满额仍可以准备邀请;接受时名额不足返回错误,并保留 pending,供释放名额后重试。 `apps/api/lib/member-limit-adapter.ts` 在认证插件实际新增成员的数据库写入前锁定组织行、重新读取人数与套餐,并在同一事务中执行插件写入。HTTP 接受邀请和可信服务端 `addMember` 都经过此门禁;认证组件内部的 adapter 事务回调仍使用带门禁的实例。组织变更的外层事务负责成员 / 邀请决策的整体提交,不能开启 Drizzle 独立 adapter 事务绕过该边界。直接调用服务端组织 API 不代替公开 HTTP 的身份与角色校验;不要将它直接暴露给客户端。 owner / admin 在成员行编辑 admin / member 角色,点击 Apply 后保存;admin 不管理 owner,只有 owner 能降级其他 owner,且最后一位 owner 不能被降级、移除或退出。移除与退出都要求页面内二次确认;实际授权由服务端插件检查。退出成功后重新读取会话、切换个人工作区并刷新团队列表;移除后旧会话立即失去该团队的成员及账单读取权限,个人账号与工作仍保留。所有者转移使用单独的确认操作,公开普通角色接口拒绝 string / array / 逗号组合中的 owner 提升,授予所有权必须经过专用转移操作。 ## 所有者转移 在 Members 中,已验证邮箱的 owner 可在另一位已验证、非匿名团队成员旁选择 Transfer ownership。搜索和分页仍可使用,不要求目标在第一页;已有 owner 也可接收。确认内容说明目标将获得团队完整控制、自己会成为 admin,其他 owner 保持角色。转移后重新读取成员列表与会话;原所有者仍可作为管理员工作或再确认退出,不能继续转移或管理 owner。 维护入口为 `apps/api/lib/ownership-transfer.ts`,公开操作是 `POST /api/auth/organization/transfer-ownership`,仅接受显式 `organizationId`、`memberId`。沿用认证客户端发送 Cookie 和同源请求,服务端要求匹配 Origin 与有效会话,在组织行锁内重新读取发起者与目标的团队资格和邮箱验证状态。先调用同一事务绑定的完整 Better Auth handler 提升目标,再调用其实际角色接口将发起者降为 admin;并未直接修改成员表。插件任何错误或数据库故障都会回滚两步。重复或并发请求只有仍为 owner 的发起者可以执行;原请求响应丢失时先刷新核对当前角色,前端不会自动重试转移。 该动作只转移团队角色,不变更个人文件、任务、积分或支付供应商账单账户。云端审计与安全通知将在 I9 / I12 接入;当前不会发送所有权通知邮件。它尚未要求二次密码或近期登录校验,当前门槛为有效登录会话、已验证邮箱与 owner 身份,进一步的账号安全策略随会话模块推进。 `bun organizations:validate` 另外检查八连接并发转移只有一个成功,并在原生 Worker 中用真实 PostgreSQL 触发器拒绝第二步写入,确认第一步也回滚;解除故障后可以重试,降级者的原 Cookie 无法再转移。 ## 登录会话管理 设置页的 Signed-in devices 展示当前账号的有效会话,标记 This device,并显示浏览器 / 系统提示、登录时间、到期时间与已有 IP;日期使用当前浏览器时区。客户端提供的 User-Agent 只能帮助辨认,不能当成真实设备证明或地理定位,IP 缺失显示不可用,不填虚构位置。列表仅属于个人账号,不随团队切换共享。 单个 Sign out session 和 Sign out other devices 都有二次确认。批量操作保留当前会话;当前设备退出继续使用侧栏 Sign out。撤销复用 Better Auth 的实际接口,失效 Cookie 在后续私有 API 请求中立即被拒绝;已有页面不会通过推送瞬间清空,需要下一次请求或刷新。已排队的后台任务不因登录会话撤销而自动取消。 设备列表保留认证组件的近期登录校验,当前安装版本默认 24 小时,测试用 25 小时会话确认边界,不关闭该检查。会话仍有效但不够近期时,页面提供 Sign in again:只有服务端成功退出后才清理当前会话并跳转登录,携带安全的 `/settings` 回跳,登录后自动返回。退出失败保留原登录,不把前端清空伪装为成功。已被其他设备撤销的会话同样显示重新登录入口。 维护入口:`apps/app/lib/queries/login-sessions.ts`、`apps/app/components/settings/sessions-card.tsx`。按用户 ID 隔离查询缓存,只使用认证客户端的 `listSessions`、`revokeSession`、`revokeOtherSessions`;没有新建会话写入接口。SDK 按自身契约返回撤销所需 token,前端只在内存中使用,不写入页面、链接、日志或持久存储;不要把认证查询缓存持久化或发送到统计服务。批量撤销并非单个数据库事务,部分删除后报错也是可能的;成功和失败都重新读取实际列表,失败不显示“已全部退出”。按钮操作不自动重试。 `bun sessions:validate` 创建并最终删除临时回环 PostgreSQL,启动生产 Worker bundle,用实际注册 / 密码登录 Cookie 验证用户隔离、单个撤销后的私有 API 拒绝、8 个并发批量撤销保留当前及其他用户会话、近期登录过期与重新登录;所有外部请求拒绝。可选 `SESSION_UI_ACCEPTANCE=1 bun sessions:validate`(先构建 app)保留独立 `http://127.0.0.1:15310/settings` 供浏览器验收。创建 `.local/session-ui-age` 可让夹具账号的会话超过近期登录窗口;完成后创建 `.local/session-ui-stop`,脚本销毁临时资源。控制文件只用于一次性验收数据库,勿用于正式账号。 ## 通行密钥管理 设置页的 Passkeys 按个人账号列出已保存的通行密钥,提供添加、改名和删除确认。名称限制 1—80 个字符;只显示名称和创建时间,不把公钥、凭据 ID 或签名计数当成设备识别。用户在支持 WebAuthn 的 HTTPS 或 localhost 浏览器中点击添加,再由系统屏幕锁或安全密钥完成注册;取消或失败不显示成功,不自动重复调用设备弹窗。关闭 `websiteConfig.auth.passkey` 后不显示管理卡片,也不启用插件接口。 列表、注册选项 / 验证、改名和删除统一要求近期登录,复用当前认证组件的 `freshSessionMiddleware`,默认窗口为 24 小时;匿名会话不能管理。不够近期时通过实际退出、重新登录与安全 `/settings` 回跳恢复。读取与变更都使用 Better Auth 客户端;每次变更无论成功或失败都刷新真实列表,响应丢失不自动重试。改名 / 删除无需浏览器支持 WebAuthn,但仍需有效且近期的账号会话。 删除会使该凭据无法用于下一次登录,既有会话保持有效;如果也要退出设备,另用登录会话管理。这里删除的是站点保存的凭据,不能抹掉系统或密码管理器中的副本。成功提示来自实际认证组件结果,刷新后仍保存。 最后一个通行密钥的删除必须保留邮箱恢复底线:当前账号邮箱已验证,并且启用了邮箱验证码登录,或启用了密码登录且该账号确实保存非空密码凭据。其他通行密钥可以作为下一次登录入口;仅有 OAuth 绑定不视为已验证的恢复路径,因为无法保证外部账号仍可访问。未满足条件时保留最后一个并返回 `LAST_LOGIN_METHOD`。本地开发允许未验证邮箱的密码注册,但仍遵守这条删除保护;可先通过已启用的邮箱验证码流程验证账号邮箱,不因开发模式跳过底线。真实邮件送达和外部账号恢复还需线上验收。 插件契约参见 [Better Auth 官方通行密钥文档](https://better-auth.com/docs/plugins/passkey),实现以当前安装的 1.7.4 源码和实际接口测试为准。维护入口为 `apps/api/lib/passkey-management.ts`、`apps/app/lib/queries/passkeys.ts` 和 `apps/app/components/settings/passkeys-card.tsx`。公开删除由账号行锁串行化,锁内调用绑定到同一事务的完整 Better Auth handler 执行删除,再读取剩余凭据和当前恢复条件;不满足条件时回滚实际插件删除。并发删除不能同时删掉最后两个凭据。公开解绑账号接口也使用同一账号行锁;如果解绑会移除已启用的密码凭据,且已无通行密钥与合格邮箱恢复路径,则回滚解绑,避免删掉通行密钥之后再移除密码底线。仅移除 OAuth 绑定仍按认证插件原规则授权。插件仍是唯一的凭据写入器,不另设自定义凭据表;直接调用可信服务端 `auth.api` 不替代公开 HTTP 边界,不要将它直接暴露给客户端。 `bun passkeys:validate` 在一次性回环 PostgreSQL 和生产 Worker 中,用软件认证器生成真实 ES256 公钥、注册数据和签名消息,验证注册 / 登录、挑战重复使用拒绝、改名、用户隔离、8 个并发删除、最后凭据回滚、近期登录和已删除凭据登录拒绝;验证邮箱恢复条件后再次密码登录,另检查真实解绑密码凭据后,捕获本地验证码并通过实际验证码接口登录。软件认证器仅用于协议验收,不代表 Touch ID、Windows Hello、手机或物理安全密钥已通过。脚本禁止外部请求并销毁临时资源,不迁移正常预览。 可选 `PASSKEY_UI_ACCEPTANCE=1 bun passkeys:validate`(先构建 app)保留独立 `http://passkeys.localhost:16310/settings`,与主预览 Cookie 隔离。浏览器验收使用两个通过协议注册的夹具凭据检查列表 / 改名 / 删除;首次设备注册弹窗仍须人工实测。`.local/passkey-ui-age` 让夹具会话超过近期窗口;`.local/passkey-ui-verify` 只将一次性夹具邮箱标为已验证,不能作为真实验证证据。完成两个凭据的页面删除后创建 `.local/passkey-ui-stop`,脚本再用实际签名登录确认两者均拒绝、密码仍可登录,然后销毁资源。这些控制文件不能用于正式账号。 ## 已知边界 * 通行密钥管理和签名协议已本地验收;首次注册 / 登录的真实设备与系统弹窗尚待验收,当前不能用新用户只开通行密钥的配置。 * 团队页已接入邀请、重新发送、取消与历史分页;`/invitations` 可接受或拒绝发送给当前已验证邮箱的邀请。套餐人数写入限制、角色修改、移除与退出已本地验收;所有者转移已本地验收;会话列表与撤销已本地验收;通行密钥列表 / 添加入口 / 改名 / 删除及恢复保护已本地验收;真实收件箱、OAuth 和通行密钥设备流程仍待验收。 * 不使用客户端隐藏菜单替代 API 成员校验。组织账单读取和变更检查实际成员及角色。 * 修改站点 ID 改变 cookie 前缀;需要重新登录,不能复用其他新站的账号数据。 ## 验收 测试新账号注册、验证、重复注册、密码错误、过期 OTP、重置链接过期及再次使用、退出后私有 API 拒绝访问、OAuth 取消 / 回调及用户邮箱、组织成员移除后的读写权限。成功与失败路径都要实际运行。 --- --- url: /docs/agentbuff-stack/admin-audit.md --- # 站点管理员与操作审计 I9 已实现站点授权、用户搜索、封禁 / 解封、任务状态与带原因恢复、积分调整、账本 / 支付记录、主动支付对账和审计查看。已提供显式维护计划与确认执行命令;正式数据库上的初始化和恢复尚待独立环境验收。 ## 角色与管理入口 站点角色保存在 `user.role`:普通用户 `user` 和管理员 `admin`。团队角色仍在 `member.role`;团队所有者或管理员不会获得全站权限。每次请求从新鲜连接检查角色、邮箱验证、匿名与封禁状态;降权后的旧会话不能继续调用管理接口。菜单隐藏只负责呈现。管理员必须是邮箱已验证、未封禁的正式账号。 | `/admin` 入口 | 当前行为 | | --- | --- | | 账号 | 名称 / 邮箱搜索,每页 20 条;`%`、`_` 按普通字符搜索 | | 账号管理 | 原因、确认、封禁 / 解封、授予 / 移除站点角色 | | 积分 | 查看余额 / 审核限制、账本;选择账号后带原因调整,先复核再确认 | | 支付 | 购买、退款 / 争议快照、已存事件、个人与团队订阅;每页 20 条,积分包购买可带原因主动核对 | | 任务 | 按选中账号分页查看状态、尝试预算、故障码与扣费;带原因复核 / 确认重试或取消 | | 审计 | 分页查看执行者、目标、动作、原因、前后角色 / 封禁、余额或任务状态、账本 / 任务 / 对账事件 ID 与时间 | 任务接口不返回原始输入、文件名、摘要、参数或私有下载。任务和审计的高级检索界面尚未实现;服务端支持账户 / 状态筛选,后续补齐运营流程。 ## 数据升级 新增 `0012_public_invisible_woman.sql`,保留 0000—0011 和全部既有 ID。增加 `user.role`、`banned`、`banReason`、`banExpires` 与组件兼容字段 `session.impersonatedBy`。现有账号默认未封禁的普通用户,不因注册顺序或团队角色被提升。 新增 `admin_audit`,ID 前缀为 `aud_`。执行者和目标 ID 故意没有级联外键,账号删除不能抹去历史管理证据。审计不保存密码、会话 token 或原始输入;无公开修改 / 删除接口,但不宣称数据库所有者无法篡改。正式环境保留期限与数据库权限发布前另配。 运行新 API 前按[增量迁移教程](../database/migrations)检查并应用本站历史。本任务主预览 `127.0.0.1:54329/launchkit` 已在明确授权后应用 0012,核对全部旧迁移摘要与账号普通用户角色。自动验收仍只创建、迁移并销毁自己的一次性数据库;其他数据库需要独立升级。积分、任务和支付对账审计使用现有 JSON 与动作字段,这些阶段没有新增 SQL 迁移或改写 0012。 ## 明确指定首个管理员 选定人员先走本站注册和邮箱验证,维护者核对准确 `usr_` ID,再运行: ```sh bun admin:bootstrap --user-id usr_替换为实际账号ID --reason '明确指定本站首个管理员' ``` 命令只允许 `ENVIRONMENT=development` 和 loopback PostgreSQL,拒绝远程数据库。不选第一个账号、不创建账号、不修改验证状态、不迁移;未验证、匿名、封禁或不存在的账号拒绝。中文占位不是有效 ID。 初始化调用 Better Auth 实际用户写入器,与审计处于同一事务;失败回滚。相同账号重跑只返回已处理,不能恢复后来撤销的角色;不同账号或已有管理员拒绝。后续由现有管理员填写原因并确认授权。正式环境或失去全部有效管理员时使用下节维护命令,不绕过限制或清空记录;没有公开初始化接口、默认超级管理员或环境变量账号白名单。 ## 正式环境初始化与恢复 `admin:operator` 是由可信数据库维护者运行的本地命令,没有 HTTP 初始化入口、启动时自动授权或环境变量管理员白名单。适用于 `development`、`staging` 和 `production`;本次仅用一次性本地 PostgreSQL 在 production 模式执行验收,未连接真实远程数据库。数据库 owner 凭据具有管理权限,必须与 Worker 的应用角色分开;维护命令会检查 owner 权限,不能用普通应用凭据初始化。 先确认目标站点的迁移已经由独立升级流程应用。命令核对当前 checkout 全部 SQL 摘要、时间及记录数量;缺失、多出或不一致都拒绝,不自动生成或运行迁移。维护者让选定人员完成本站注册及真实邮箱验证,并独立核对准确的 `usr_` ID。命令不会创建账号、验证邮箱、解除封禁、改密码、找回凭据或提升团队 owner。 为每个站点和环境准备一份私有文件,例如 `.local/administrator-production.env`。文件使用本站 API 的环境变量契约,明确填写 `ENVIRONMENT=production`、本站裸 HTTPS `APP_ORIGIN`、`APP_NAME`、`BETTER_AUTH_SECRET`、邮件配置及数据库 owner 的 `DATABASE_URL`;已启用模块所需配置也必须完整。文件放在版本控制之外,限制本机读取权限。命令只解析 `--config` 指定的文件,完全不继承 shell 的数据库或服务凭据,也不回退 `.env.local`。远程数据库要求 TLS 和非 pooler origin;development 只接受 loopback。 ### 第一次初始化 以下是维护步骤示例,ID、站点、操作者和 UUID 必须替换为准确值: ```sh bun admin:operator plan \ --config .local/administrator-production.env \ --environment production \ --origin https://your-product.example \ --operation initialize \ --user-id usr_替换为实际账号ID \ --operator your-maintainer-id \ --reason '明确指定该站点首位已验证管理员' \ --request-id 替换为新的UUID \ --plan .local/administrator-initialize.json ``` `plan` 只读数据库,暂时锁定授权状态以获得一致快照,不改角色或写审计。计划文件默认权限 0600,拒绝覆盖已有文件或符号链接。输出可核对的环境、站点、账号、原因、请求 ID、数据库指纹、状态指纹和 30 分钟有效期,以及 `approval` 摘要;不输出数据库 URL、密码、会话或服务密钥。维护者对照私有配置确认数据库目标、目标人员与操作内容。摘要用于匹配已复核内容,是本机确认机制,不是签名、权限凭证或防止数据库 owner 篡改的保护。 只有本站不存在管理员角色记录,也没有历史初始化 / 恢复审计时,`initialize` 才能执行。曾初始化后删除管理员的站点也必须走显式恢复,不能当作新站。复核计划后执行: ```sh bun admin:operator apply \ --config .local/administrator-production.env \ --environment production \ --origin https://your-product.example \ --plan .local/administrator-initialize.json \ --approve 替换为刚才复核的approval摘要 ``` 执行重新核对站点、环境、数据库身份、原指令摘要、有效期和锁定后的实际授权状态。账号或管理员状态变化会拒绝旧计划,重新生成并复核;不能在 `apply` 额外传入另一目标、原因或动作。确认后的角色变更调用 Better Auth 实际写入器,和 `user.initialize_admin` 审计同事务提交。审计保存 `operator:<维护者标识>`、目标、原因、实际前后角色、环境、站点及数据库指纹,不将维护者冒充浏览器登录用户。维护者标识为其自报名称,实际操作身份还需由组织的数据库凭据管理与外部操作记录核查。 ### 失去全部有效管理员时恢复 有效管理员指 `role=admin`、邮箱已验证、非匿名、未封禁的账号。只要还有一位有效管理员,维护恢复拒绝;忘记密码但账号仍有效时应使用已有账号找回方式,或由其他管理员通过带审计的角色管理授权。命令不能用“忘记密码”当作绕过角色管理的理由。 确实没有任何有效管理员、但已有管理员记录或初始化历史时,维护者指定另一个已注册、已验证且未封禁的普通账号,以新 UUID 重新生成计划,将 `--operation` 改为 `recover`,计划文件另命名。复核后使用同一 `apply` 步骤。保存独立 `user.recover_admin`,保留既有角色 / 封禁和全部历史审计,不清空记录、不恢复被封禁账号或旧会话。没有历史管理的站点必须用 initialize。旧的开发初始化命令也不能绕过新维护历史。 状态检查、Better Auth 角色写入和审计使用与站内管理相同的事务级授权锁。8 个相同计划竞争只提交一次;不同恢复候选竞争只能产生一个有效管理员,等待者必须重新复核状态。请求按外部维护者标识与 UUID 去重,改变目标、动作、原因或环境却沿用 UUID 会拒绝。响应不确定可在有效期内以原计划和摘要手动重试;已提交重放只返回 `replayed: true`,不会恢复之后撤销的角色、解封或复活删除账号。过期后可用原始相同指令 / UUID 生成新计划核对已处理状态;新的恢复操作使用新 UUID。 完成后由获授权者正常登录本站并检查 `/admin`、当前账号角色与操作审计,再归档私有计划。操作失败不宣称已授权,刷新数据库及审计后恢复;审计存储故障使角色变更回滚,修复后原计划可以继续。正式站点的凭据交付、维护人员身份核实、邮箱送达与远程执行仍由本站发布验收承担,本地命令测试不代替这些证据。 ## 原子变更与重试 身份和会话仍由 Better Auth Admin 插件写入。账号权限边界负责输入、授权、并发和审计:只接受同源的封禁、解封和角色变更 POST 操作,原因 5—500 字符,正文至多 16 KiB;重新确认执行者,锁定管理变更与目标,调用真实插件,再保存前后状态。审计失败整个事务回滚,包括实际会话删除。 最后一位有效管理员不能被封禁 / 降权,不能封禁自己;授予角色前检查目标已验证且未封禁。事务级全局咨询锁串行处理较少发生的站点授权、人工积分、管理员任务恢复与对账写入,防止交叉降权移除所有管理员;普通任务与读取不走这把锁。 确认生成请求 ID,审计按执行者与 ID 去重。响应丢失后的手动重试沿用 ID;改变动作、目标、角色或原因却复用 ID 会拒绝。解封后重放旧封禁只返回已处理,不重新封禁;不同 ID 的无状态差异请求仍保存审计。前端不自动重试,成功 / 失败都重新读取状态和审计。 变更要求近期登录,过期时实际退出、登录并回到 `/admin`;退出失败不清空会话。模拟登录、创建 / 删除账号、替人改密码和其他未纳入审计的插件管理端点关闭。兼容字段存在不等于该功能可用;普通注册 / 资料更新不能注入管理角色。 ## 账本与支付排查 先在账号列表选择管理对象,积分区会显示其当前余额与支付审核限制。只读查看不会创建钱包或赠送欢迎积分;尚未初始化时明确提示。取消选择可查看全站账本、购买和个人 / 团队订阅。个人订阅筛选按 `user.id`,团队订阅按 `organization.id`,不会把团队套餐误作成员个人套餐。 购买区展示已保存的测试 / 正式模式、商品、金额、积分、退款 / 争议、扣回与不足差额,以及便于排查的 Checkout / Payment ID。事件仅返回保存的处理结果与快照;未保存显示“未记录”,真实零值显示零。没有访问支付供应商或用空值补造成功记录,不返回 Checkout URL、内部租约、请求键或原始供应商负载。展示数据库记录的 `active` 不替代已有套餐服务对当前周期、试用与配置的权益判断。 列表每页 20 条,按时间及 ID 排序;允许翻页。用户 / 购买 / 账单归属的筛选由服务端执行,每次重新检查站点权限。普通账号、团队 owner / admin 和已失权的旧会话无法读取。积分包购买的主动查询见下一节;订阅人工变更仍待后续阶段。 ## 主动核对积分包购买 切换到购买记录,选中购买查看事件。在服务器确认本站已配置对应测试 / 正式密钥及独立积分包 webhook 密钥,并且购买有 Checkout 或 Payment 引用后,可填写 5—500 字符原因,复核并确认当前支付检查。没有配置或引用时明确提示不可核对;读取列表不会访问供应商。检查原有购买不要求重新开启新积分包销售,不创建支付、退款或订阅变更。 服务端严格只接受购买 ID、请求 UUID 与原因,使用实际站点管理员、同源和近期 24 小时会话;每位管理员每分钟最多 10 次,重放也计入。请求审计 `purchase.reconcile_requested` 在供应商查询前提交,记录当时快照和稳定对账事件 ID。该历史记录只证明请求已记录,不能单独证明当前仍未完成或已经完成。 随后复用原结算服务读取 Stripe 当前 Checkout、PaymentIntent、Charge、退款与争议,校验归属、SKU / Price、模式、金额、币种及实际到账。供应商读取期间不持有管理 / 账本事务锁;购买仍使用原有限操作租约。读取结束、结算锁等待后再次检查实际管理员及会话,查询期间已撤销 / 降权的会话无法提交权益变更。 原结算规则决定发积分、退款扣回、争议恢复、差额和审核限制;管理员不能输入“已支付”、退款金额或手动清除审核。核对成功可能得到 `review`,不是自动判定支付正常;金额不符不发购买积分,余额不足返还扣回形成原有 shortfall / 账户限制,其他购买的审核限制不会被错误解除。 购买状态、钱包 / 账本、支付事件与 `purchase.reconcile_completed` 在同一事务提交。完成审计保存事件 ID、已应用 / 需审核结果与实际前后支付快照,包括积分是否已发、扣回和差额;不保存 Checkout URL、供应商原始负载或密钥。完成审计失败时全部财务写入回滚,先前请求记录保留以便排查和恢复。 同 UUID 的同购买 / 原因恢复使用同一稳定事件 ID;已完成请求重放不再查询供应商,也不恢复历史余额。改变原因、购买或复用其他账号 / 积分 / 任务动作 UUID 会拒绝。并发查询由原购买租约限制,正在查询可返回忙碌;刷新后沿用同 ID 手动重试,不自动重试。完成后的新一次核对必须重新复核并生成新 UUID,以读取后来发生的退款 / 争议。 供应商失败、超时或进程中断可能只留下请求审计,不能当作完成。页面保留原确认供手动恢复,成功 / 失败都重新读取购买、事件、余额、账本及审计。功能本地协议验收使用真实 SDK 和生产 Worker,但供应商响应受控;真实 Stripe 测试账户与四国币种流程仍按 I6 独立验收,不把本地结果宣称为真实交易。 ## 带原因调整积分 填写非零整数增减量与 5—500 字符原因,复核后确认。单次绝对值最多 1,000,000,扣减后余额不能低于零,也不能超过数据库整数上限。只允许本站 Origin 和实际近期登录会话;窗口与 Better Auth 共用 24 小时,过期先重新登录。锁等待后再次检查实际会话及角色,已撤销会话拒绝。 调整调用原有事务账本服务,记录独立 `adjustment` 类型;不伪造购买、任务扣费、退款或支付证明,不带任务 / 购买关联。未初始化钱包按本站欢迎积分规则在同一事务初始化,并记录独立 welcome。明确选择的封禁账号可修正账目,封禁本身不改变;增加积分仍保留支付审核限制,不自动填补 / 改写购买差额或解除限制。 余额、账本和 `credits.adjustment` 审计在同一事务提交;审计记录执行者、目标、原因、增减量、实际前后余额 / 审核状态和账本 ID。审计失败会回滚余额、账本,以及本次新建的钱包 / 欢迎积分。用户账本只保存通用调整说明,详细维护原因保存在管理审计。 请求 ID 与账号权限变更共用执行者命名空间,改变动作、对象、原因或金额的重用拒绝。相同请求只返回已处理,不把历史余额冒充当前余额;中途消费后重试不会恢复旧余额。账本存在却没有对应审计时拒绝继续,并要求排查,不能声称已经记录新的调整。页面失败保留原 ID 供手动重试,没有自动重试;每次结束重新读余额、账本和审计。 人工调整与任务预扣 / 返还、支付冲正共用钱包锁。账号锁采用 `NO KEY UPDATE` 保持账本外键的 `KEY SHARE` 可用;执行者账号先于会话锁定,与凭据管理一致。原生并发验收曾检出完整 `UPDATE` 锁造成的等待环,修复后验证独立连接同时调整、预扣、支付冲正及后续退款,余额与账本合计一致。 ## 带原因恢复任务 先选择账号并刷新任务记录。重试 / 取消按钮按当前状态显示;填写 5—500 字符原因,复核后再确认。实际近期登录、角色与未撤销会话在事务锁等待后重查;只接受本站 Origin,不接受客户端传入的尝试上限、扣费状态或任务所有者。每个管理员的任务恢复最多每分钟 10 次请求,重放也计入;读取不计入。 重试复用原任务服务:保留处理器、原尝试次数 / 上限、收费快照和原积分预扣,不重新扣费或增加预算。只有当前可恢复状态可以重试;超额尝试、已退款、未知处理器 / 版本、不可恢复故障、失效输入、容量用尽、支付审核限制和封禁账号均不能绕过。按钮只是提示,最终以服务器对新鲜状态的校验为准。 取消未运行的任务按原规则结束并释放输入保留,原积分服务只退款一次。正在运行或待核对的任务仅记录取消请求,仍保留原扣费;由持有实际租约的执行器结束 / 核对并结算。页面提示“请求已记录”,不将该提示冒充中断完成或已退款;刷新查看后续状态。封禁账号不能重试,但管理员可以取消其既有保留任务清理账目,账号不会自动解封。 任务状态、输入保留、退款与 `task.retry` / `task.cancel` 审计在同一事务提交;审计失败全部回滚。审计仅保存任务 ID、实际前后状态 / 扣费状态、尝试次数、派发版本、已知故障码及取消请求时间。原始输入、文件名、摘要、参数和私有结果不进入管理接口 / 审计。 请求 ID 与账号 / 积分变更共用执行者命名空间。相同动作、任务和原因只能提交一次;响应丢失可沿用 ID 手动重试,改变内容复用会拒绝。任务后来完成、取消或退款后,旧重放不会重新执行或恢复过去状态。前端不自动重试,每次请求结束重新读取任务、账本和审计。 重试事务提交后才发送 Queue 消息;队列发送失败或进程中断时,已提交的任务由原有数据库待派发记录和维护流程恢复。审计记录的是管理请求已提交,不宣称 Queue / 执行已完成;重复派发仍由原执行版本与租约保护。 ## 封禁及在途任务 当前支持永久封禁与显式解封,不接受过期时间,避免未经审计的自动恢复。封禁删除所有实际会话并拒绝后续登录,新私有请求没有有效身份;解封不会复活旧会话,需重新登录。 已经接受的后台任务继续原执行、扣费、取消和退款规则,封禁不额外取消或重复退款;不承诺中断所有已完成身份检查的在途请求。用户 API Key 已检查新鲜账户状态,封禁、失去邮箱验证或匿名会拒绝后续调用,见[用户密钥教程](./api-keys)。 ## 审计检索与历史保留 管理页的审计面板可填写对象、执行者和请求编号。使用完整编号精确匹配,不按邮箱、原因片段或通配符猜记录;填写多个条件时取交集。编辑输入不会立即查询,点击检索后从第一页开始;清空恢复全部历史,刷新重新向服务端核对权限和记录。每页最多 20 条,按记录时间和记录编号倒序。失败提示出现时不继续展示先前缓存结果,查询只读,不改变权限、账本、任务或审计。 请求编号是审计操作的去重标识,不是用户任务的原始请求摘要、登录凭据或供应商密钥。页面同时展示请求编号和审计记录编号,方便核对一次操作;同一 UUID 可由不同执行者使用,不能据此认定是同一人或唯一一项操作。需要单独定位时再填写执行者和对象编号。维护命令的 `operator:<标识>` 也可以检索,不能把维护者自报标识当作身份认证证据。 输入原请求编号,或复制带 `:completed` 的完成记录编号,都会读取该请求及其完成记录。支付对账和旧结账关联可成对核对;只有请求记录时仍可能失败或在处理中。历史审计只证明记录中的操作发生过,不能用旧完成记录推断当前支付已到账、任务已成功或账号仍有原权限;继续查看对应业务资源的当前状态。 增量 `0034_admin_audit_request_lookup.sql` 只为跨执行者请求检索增加索引,不改变旧表或审计内容。新站应用完整历史;本地一次性验收库已验证实际索引和查询。正常主预览仍保持 0012 / 13 条历史,不自动升级;旧表已有请求字段,检索仍兼容,但没有新增索引的站点需要在明确数据库升级时完成完整增量,不能手工跳过中间迁移。 默认保留审计,不提供前端删除、自动到期或清空按钮。账号删除不会连带删除历史对象 / 执行者编号;封禁和角色撤销也不删除历史。审计同时是重复行政请求、初始化 / 恢复历史和权益变更的证据,不能直接套用普通通知、使用事件或营销名单的定时清理。归档文件保留清理也不删除源库审计;复制一份备份不代表数据库里的去重证据可以删除。具体站点的保留期限、隐私安排与生产归档仍需要另行确定,本轮不引入未经验收的自动删除流程。 ## 验收与排查 旧订阅结账恢复位于管理页的订阅视图,具体人工检查与用户后续恢复见[未标记旧结账核查](./billing-tasks#未标记旧结账的人工核查)。只补原会话关联,原购买阻塞仍保留;原因、原尝试和会话编号在本页审计展示。读供应商与确认分开,确认保留同一请求编号供失去响应后重试。当前会话或本地原请求发生变化时,重新核查,不能使用过期的核查摘要强行提交。 ```sh bun run test -- --run bun admin:validate bun admin:validate-tasks bun admin:validate-payments bun admin:validate-operator bun typecheck bun lint bun db:check bun config:check ``` `admin:validate`、`admin:validate-tasks` 与 `admin:validate-payments` 各自创建独立本地数据库、应用完整历史、启动生产 Worker bundle;前两者禁止外部请求,对账验收仅以受控响应接收实际 SDK HTTP,最后销毁数据库 / Worker / 资源目录;不迁移当前配置数据库,不创建真实管理员。 `admin:validate-payments` 同时验旧结账人工关联:真实站点管理员 / 来源门禁、多个候选 / 当前状态变化保留待核查、原生审计触发器故障回滚、同请求恢复与重放,以及账号持有人使用原核查接口继续结账。旧请求记录为一次性库中的明确测试数据;供应商读取使用真实 SDK 与受控 HTTP,不发送真实付款请求。 同一验收还读取实际关联操作的请求 / 完成审计,按对象、执行者和完成编号组合检索,验证真实权限、严格输入及 PostgreSQL 请求索引。可选只读页面复验:`bun app:build` 后运行 `ADMIN_AUDIT_UI_ACCEPTANCE=1 bun admin:validate-payments`,登录上面的测试管理员,按日志提供的对象、执行者和请求编号在审计面板检索。完成后创建该次日志所示私有目录中的 `browser-stop` 文件;脚本核对审计总数、积分未变及原请求仍可查询后,销毁一次性资源。此开关与另外两种支付 / 旧结账页面验收互斥,不读取主预览数据。 可选界面复验:先运行 `bun app:build`,再运行 `ADMIN_CHECKOUT_UI_ACCEPTANCE=1 bun admin:validate-payments`。脚本提供独立本地管理页、测试账单主体和原会话编号;登录测试管理员 `admin-payments@acceptance.example.test`,密码 `LocalPaymentAdministrator2026!`,选择旧账单测试账号并在订阅视图核查 / 确认。验收原因填写 `Browser legacy checkout verification`;完成后创建日志中该次私有目录的 `browser-stop` 文件。脚本先核对实际关联、两条审计、原用户恢复及积分未变,再清理自己的 Worker / 数据库 / 目录。不要用真实账号或主预览数据复验,也不要同时开启旧积分对账界面开关。 已验证:明确初始化、团队 / 站点分权、财务筛选分页 / 私有字段、封禁撤销会话及新任务拒绝、账号 / 积分审计故障回滚、八请求去重 / 竞争扣减 / 交叉降权、旧会话失权、未审计端点拒绝;页面确认 / 取消 / 失败重试与近期登录。原生实际连接同时执行人工调整、任务预扣及支付冲正,后续退款与账本合计一致。任务恢复另验八请求去重 / 重试取消竞争、实际 Queue / R2 结果下载、审计触发器故障回滚、旧请求不复活任务、封禁与速率门禁;既有失败尝试为明确控制的元数据,重试执行来自实际消费者。浏览器使用独立测试账号,未修改主预览权限。 | 问题 | 排查方向 | | ---------------------- | -------------------------------------------------- | | 菜单不显示 / API 403 | 查 `user.role`、验证、封禁;团队角色不等于站点角色 | | 提示近期登录 | 重新登录,不关闭 freshness | | 最后一位管理员不能降权 | 先明确授权另一位已验证且活跃的管理员 | | 变更 500 | 查日志 / 数据库;刷新状态与审计,保留原 ID 重试 | | 旧封禁重试仍解封 | 正确重放行为;新封禁需重新确认和新 ID | | 新 API 缺字段 | 检查迁移,确保数据库和 bundle 版本匹配 | `admin:validate-operator` 独立创建一次性本地 PostgreSQL,以实际 production 模式 CLI 验只读计划、显式文件覆盖错误 shell、owner / 迁移 / 审批 / 环境 / 数据库门禁、8 并发一次授权、双候选恢复竞争、真实审计故障回滚及原计划恢复、降权后的重放。它不连接远程资源,验收后销毁一次性数据库、角色与私有计划 / 配置。管理员审计页展示外部维护者、环境及站点。 可选用户 API Key 已本地验收并复用原权益 / 幂等规则;后续继续增长模块和真实外部环境验收。I9 与整体目标继续进行。 --- --- url: /docs/agentbuff-stack/api-keys.md --- # 用户 API 密钥与外部接口 I10 已提供默认关闭的个人 API 密钥模块。用户在设置页创建、一次性查看、改名和撤销密钥,外部脚本可调用实际文件与异步任务接口。生产 Worker、独立本地 PostgreSQL、Queue / R2 和浏览器已验收;真实云端部署、第三方调用与负载仍待独立验证。它不是站主配置的模型、邮件、支付或数据库密钥。 ## 开启与数据库升级 `packages/core/website.ts` 的公开构建配置: ```ts apiKeys: { enabled: false, maxActiveKeys: 10, requestsPerMinute: 120, maxExpirationDays: 365, }, ``` | 字段 | 默认 | 范围与行为 | | --- | --- | --- | | `enabled` | `false` | 关闭时不显示设置卡片,所有已定义密钥管理 / 外部接口返回 404;不删除历史密钥 | | `maxActiveKeys` | `10` | 1—50;每人同时启用且未过期的密钥数量,创建时检查 | | `requestsPerMinute` | `120` | 1—1000;新密钥的插件请求窗口上限,签发时保存;改变配置不重写旧密钥 | | `maxExpirationDays` | `365` | 1—365;新密钥最长有效期,不改变旧密钥到期时间 | 密钥属于个人账号,不读取当前团队的套餐或成员身份。至少使用已验证邮箱、非匿名、未封禁账号;改变密钥要求实际存在且创建不足 24 小时的登录会话。不会根据注册顺序或团队角色授予管理权。 新增 `0013_big_thunderbolts.sql`:`apikey` 使用安装的 `@better-auth/api-key@1.7.4` 数据库字段及哈希,`api_key_issuance` 保存签发请求的 UUID、参数摘要和密钥 ID。后者不保存明文,也不级联到密钥记录:插件清理已过期密钥后,旧创建请求仍不能重新签发。账号删除会清理其密钥及签发记录。ID 分别使用 `key_` / `kis_` 加 16 位 CUID2。 按[增量迁移教程](../database/migrations)核对目标数据库并升级历史,再一起重建前后端。不开外部 API 的站点可保持关闭。本任务主预览仍只有 0012,0013 **只在独立一次性数据库验收,尚未应用到主数据库**;主预览保持关闭,不会查询新表。维护命令核对全部迁移历史,升级代码后在迁移未齐的数据库会拒绝初始化管理员,不能跳过检查。 本模块没有新增 `.env` 密钥或云资源。文件操作需要原有 `STORAGE`;提交 / 重试任务需要原有 `TASK_QUEUE` 和任务开关。开启 `security.turnstile.enabled` 后,API 的上传、提交、重试也需要相应动作的 `X-Turnstile-Token`;密钥不绕过验证码,自动化脚本的使用方式需要先考虑这个配置。 ## 设置页流程 1. 进入 `/settings`,刷新密钥列表。列表每页 20 条,仅显示名称、短前缀、权限、状态和到期时间。 2. 填写名称、有效天数和实际需要的权限,先复核再确认。取消复核不写入。 3. 创建成功后立即保存明文;关闭展示或离开页面后不能再次查看。它只保留在当前组件内存,不进入查询 / 变更缓存或本地存储。 4. 可以改名和撤销;不提供重新启用、续期或增加权限。需要新权限时签发新密钥,并撤销旧密钥。 创建 UUID 与签发在同一事务提交,同一账号 / UUID / 参数只签发一次;同 UUID 换参数返回 409。若服务端已成功但响应丢失,再发同一请求只返回 `{ id, key: null, replayed: true }`,不重发明文,不额外创建密钥。先刷新列表,未保存时撤销对应密钥、以新 UUID 再创建。页面保留原确认用于手动重试,没有自动重试。 关闭展示清除的是页面可见状态,不承诺擦除浏览器开发工具、历史网络响应或已复制到其他位置的凭据。数据库只保存插件的不可恢复哈希;管理列表、审计、邮件和正常应用日志不输出完整密钥。站主也不能从数据库取回明文。 ## 鉴权与作用域 外部接口固定在 `/api/v1`,仅接受 `Authorization: Bearer <用户密钥>`。密钥前缀为 `abk_`;不接收 URL 参数中的密钥,不回退到 Cookie,也不创建模拟会话。密钥不能登录后台、购买 / 修改订阅、修改账号或管理其他密钥。 | 权限 | 允许的接口 | | ------------- | -------------------------------------- | | `files:read` | 文件列表、单条元数据、私有文件内容下载 | | `files:write` | 上传和删除自己的文件 | | `tasks:read` | 任务列表、报价 / 余额、单条任务状态 | | `tasks:write` | 提交、取消和有版本条件的重试 | 每次读取实际密钥、当前账号和作用域。过期、撤销、邮箱不再验证、匿名或封禁会拒绝后续请求;已接受的在途请求及任务仍按原执行 / 扣费 / 退款规则完成,不宣称撤销能中断全部已接受工作。每把密钥有插件请求限制;所有密钥与浏览器进一步共用原有每账号文件 / 任务限制、容量、配额和余额,增加密钥数量不能扩大这些业务额度。 ## 文件接口 | 方法与路径 | 结果 | | --- | --- | | `GET /api/v1/files` | `{ items, nextCursor }`;下一页用 `cursor` 传 JSON 编码的服务端游标 | | `GET /api/v1/files/:id` | 自己文件的元数据 | | `GET /api/v1/files/:id/content` | 实际文件字节,附件下载、禁止缓存;结果文件同样走此接口 | | `POST /api/v1/files` | 原始二进制正文,`Content-Type` 声明类型,`X-File-Name` 为编码后的文件名;201 返回文件元数据 | | `DELETE /api/v1/files/:id` | 原删除规则;任务仍保留的输入不能强删 | 上传继续验证实际字节、类型、大小及个人配额,不提供公开 R2 地址。上传没有请求 UUID 去重;响应不确定先查询文件列表,直接重发可能生成第二个文件。删除不会绕过任务输入保留或结果结算。 下面是调用示例,环境变量由调用者在自己的终端设置;不要把真实密钥提交进代码、教程或共享日志。 ```sh curl "$SITE_ORIGIN/api/v1/files" \ -H "Authorization: Bearer $USER_API_KEY" \ -H 'Content-Type: text/plain' \ -H 'X-File-Name: input.txt' \ --data-binary @input.txt ``` ## 报价、任务和重试 | 方法与路径 | 结果 | | --- | --- | | `GET /api/v1/tasks/quote` | 原 `text-normalize` 报价、版本、个人余额与支付审核限制;按原规则首次初始化欢迎积分 | | `GET /api/v1/tasks` | 个人任务分页列表 | | `GET /api/v1/tasks/:id` | 个人任务、结果文件 ID 和当前 `revision` | | `POST /api/v1/tasks` | 相同原任务参数、稳定 `requestKey` UUID 和已确认报价;201 表示提交已记录,不代表执行成功 | | `POST /api/v1/tasks/:id/cancel` | 原取消规则;重复取消不会重复退款,运行中可能只是取消请求已记录 | | `POST /api/v1/tasks/:id/retry` | 必须携带 `If-Match: ""`,复用原尝试预算和预扣,不重复扣费 | 提交正文示例;文件 ID、UUID 和报价版本必须来自当前站点: ```json { "requestKey": "替换为本次稳定UUID", "inputFileId": "替换为上传返回的fil_ID", "processorId": "text-normalize", "processorVersion": 1, "parameters": { "trimTrailingWhitespace": true }, "acceptedQuote": { "credits": 0, "version": "从报价接口读取" } } ``` 同账号相同 `requestKey` 与输入,API 与浏览器共用一条任务和一笔原账本预扣;改变内容沿用 UUID 返回 `REQUEST_CONFLICT`。改价后旧报价返回 `QUOTE_CHANGED`,必须重新复核。实际执行、Queue 重发、R2 结果、取消返还与故障核对继续使用[原任务生命周期](./task-lifecycle),不另建收费路径。 重试前读单条任务的 `revision`,按 HTTP 引号格式发送 `If-Match: "2"`。服务在原任务事务锁内比较版本;重试或其他派发改变版本后,旧请求返回 `STALE_REVISION`,不能在下一次失败后重放旧请求再多执行一次。响应不确定先读取任务当前状态;重新重试是一次新的明确操作,重新读取版本,不能把 409 当成可自动无限重试。重试仍可能因原尝试预算、退款、失效文件、容量或支付审核而拒绝。 ## 错误与排查 外部接口错误形如 `{ "error": { "code": "…", "message": "…" } }`;浏览器密钥管理保留认证客户端的 `{ code, message }` 格式。 | 状态 | 处理 | | --- | --- | | 400 / 413 | 检查严格输入、大小、文件名及重试版本头;不修改后盲目重发 | | 401 | 缺少、无效、过期、撤销或作用域不足;密钥不能当浏览器会话 | | 403 | 当前账号不可用;管理变更可能要求重新登录或正确 Origin | | 404 | 模块关闭、未开放路径,或资源不属于当前账号 / 不存在 | | 409 | UUID 内容冲突、任务版本 / 报价变化、容量 / 积分 / 状态限制;先读现状 | | 429 | 密钥或账号额度用尽;参考 `Retry-After: 60`,它不是精确的剩余窗口承诺 | | 503 | 所需资源 / 验证未配置或操作未确认;读取现状后手动恢复,不能当作未写入 | 密钥管理每账号每分钟最多 30 次,列表和创建重放也计入。`SESSION_NOT_FRESH` 的确认流程会先实际退出,再重新登录;退出失败不清空当前会话。上线前让调用方明确提交 UUID、上传响应丢失和重试版本规则。 ## 本地验收与维护 ```sh bun keys:validate bun run test -- --run bun typecheck bun lint bun db:check bun config:check ``` `keys:validate` 只接受 development 和 loopback PostgreSQL,创建 `api_key_acceptance_…` 一次性数据库,应用全部迁移,并只在独立 Worker bundle 中开启密钥及三积分任务。使用实际生产入口、真实 Queue / R2、真实插件及 PostgreSQL;外部 HTTP 被拒绝,邮件进入独立本地收件箱。8 并发签发只有一把明文、其余返回已处理;文件字节、单账本扣费、浏览器服务重放、结果下载、取消退款、版本重试、并发限流、作用域、过期、封禁和撤销都有断言。测试结束销毁临时数据库 / Worker / 资源,不升级当前配置数据库或改动源配置。 `bun keys:validate --ui` 额外构建独立工作台并暂停供浏览器检查,使用脚本中的一次性测试账号,不涉及真实账号或支付。验收创建 / 撤销后创建 `.local/api-key-ui-stop` 文件,让脚本核对落库状态并清理;暂停最多 12 分钟。不要将此验收服务器作为长期预览或生产部署。 实现契约参考当前安装包与[插件官方文档](https://better-auth.com/docs/plugins/api-key)。插件升级时重新核对哈希、字段、默认端点和限流行为;不能直接开放其全部管理操作或启用 API 密钥模拟会话。 --- --- url: /docs/agentbuff-stack/marketing-subscriptions.md --- # 邮件订阅与候补名单 ## 当前交付范围 I11 已提供匿名表单、独立用途同意、确认邮件、主动确认 / 退订,并在第二阶段接入 Resend 联系人 / 主题同步、签名退订回调和故障恢复。产品页面默认英文,可随 `i18n` 开启西班牙语;维护教程使用中文。名单入口和联系人同步默认关闭;本任务主预览数据库仍停留在 0012,后续可选迁移只在独立测试数据库运行,不会通过刷新页面自动升级。 **仍待推进:** 工具 / 收入转化事件、真实 Resend 环境与邮箱投递验收。没有批量营销发送器。当前本地证明只涵盖本站状态、受控供应商契约、实际 Worker 和数据库;不代表真实账户配置、真实退订页或邮箱已验收。 ## 配置入口 唯一公开配置在 `packages/core/website.ts`: ```ts marketing: { newsletter: false, waitlist: false, contactsSync: false, consentVersion: "2026-10-10", confirmationHours: 24, resendMinutes: 15, }, ``` | 字段 | 范围与含义 | | --- | --- | | `newsletter` | 开启邮件订阅申请,生成 `/newsletter` 并展示页脚入口 | | `waitlist` | 开启候补名单申请,生成 `/waitlist` 并展示页脚入口 | | `contactsSync` | 独立开启联系人 / 主题同步和签名回调;关闭申请页面不会自动关闭此项 | | `consentVersion` | 1—80 字符。修改同意文案时同步更新此版本 | | `confirmationHours` | 1—24 小时;每一代确认链接的有效期 | | `resendMinutes` | 1—60 分钟;同一邮箱、同一用途再次申请的最短间隔 | 同意文案统一定义在 `packages/core/marketing.ts`,表单展示和服务端记录使用同一份内容。确认邮件复用现有 `APP_ORIGIN`、`BETTER_AUTH_SECRET`、`RESEND_API_KEY`、`RESEND_EMAIL_FROM`;联系人配置见下文。启用 Turnstile 时,还需已有的公开站点 key 和服务端 secret,新增申请使用 `marketing_signup` 动作。 当前版本启用名单前,审查并应用包含 0023 的完整增量迁移,再一起重建产品公共页与 API。0014 保存订阅与限流;0015 保存同一邮箱的同步状态和已验证回调的去重记录,0016 记录已成功同步的各用途同意代次;0023 保存同意语言;即使只开启本站名单,也需应用包含这些结构的完整历史。生成器不会替你执行迁移。 ## 访客流程 1. 访客输入邮箱,并主动勾选当前用途。复选框没有预先勾选,不要求登录,也不会创建账号。 2. 页面提交成功只显示申请已收到。服务端先记为待确认,再尝试发送确认邮件;重复地址和发信失败使用同样的公开响应,不暴露此地址是否已订阅。 3. 邮件中的链接打开英文 `/email-preferences` 或西班牙语 `/es/email-preferences`。只打开页面、邮件预览或读取状态都不会修改订阅。 4. 访客点击确认按钮,才把待确认记录改为已确认。重复确认保留第一次确认时间。 5. 退订链接先展示该用途的当前状态,再由访客点击退订按钮。待确认申请也可以撤回。重复退订保留第一次退订时间。 同一邮箱可以分别申请邮件订阅和候补名单;确认或退订其中一个用途,不改变另一个用途。账号验证、团队邀请、密码恢复和安全邮件不依赖营销状态。 ## 同意与确认邮件语言 开启 `i18n.locales: ["en", "es"]` 后,已启用的名单生成 `/es/newsletter` 和 `/es/waitlist`,页脚指向对应语言。表单展示该语言的同意文案,并在显式提交时传递 `locale`;服务端保存文案原文、版本、语言与同意代次,不从浏览器或账号猜测。修改原文或译文时同步更新 `consentVersion`。未传语言的旧客户端新申请默认为英文;未知语言拒绝,关闭的语言不接受新申请。 每封新确认邮件的主题、HTML / 纯文本、按钮、到期说明和确认 / 退订链接使用记录语言。页面切换与 Worker 重启不会改写该代请求;发送失败后的同键重试保持原语言。匿名重复提交在冷却期内不会修改待确认代次,也不能修改已确认的订阅;需要更换语言时先退订,再按冷却期限用新语言主动申请和确认,不提供无需验证的邮箱偏好修改入口。名单之间的语言分别保存。 0023 仅增加可为空的语言及约束,旧记录保持空值。空值投递继续使用原英文模板、链接与幂等 key,不能自动改成新版英文模板,避免已尝试请求的载荷改变。关闭西班牙语后不再领取对应语言的确认邮件,也不把它改发英文;未过期且未超过尝试上限的记录在重新开启后可继续领取。关闭语言后的旧西班牙语页面不再发布,仍可将原 `#mkt_...` 片段用于根路径 `/email-preferences` 退订。语言不参与签名校验或名单权限,不会阻止历史退订。领取后已经发出的邮件仍可能到达。 确认与退订页保持 `noindex`、不进入 sitemap、不添加公开语言替代链接,不加载营销统计;访问令牌留在 URL 片段,只有显式按钮提交才修改状态,成功后清除片段。切换路径本身不能确认、退订或改变订阅语言。隐私政策仍为英文,西班牙语入口明确标注。译文来自现有英文流程的代码字典翻译,人工语言审校与真实邮箱显示仍待验收。 ## 数据与状态 `marketing_subscription` 按邮箱和用途唯一,邮箱去掉首尾空格并转为小写。它记录当前同意版本、文案、时间、入口,以及确认 / 退订时间和发信尝试。 | 状态 | 可证明的事实 | | -------------- | ---------------------------------------------- | | `pending` | 当前用途有一条申请,尚未完成确认 | | `confirmed` | 有效确认链接经明确操作,本站已记录确认 | | `unsubscribed` | 该用途已在本站撤回或退订;旧确认链接不能恢复它 | 再次申请已确认的地址不会降回待确认,也不会重复发送确认邮件。待确认 / 已退订地址过了冷却时间后,可以通过新的明确同意创建新一代申请;旧确认链接失效,仍需重新确认。该表保存当前申请状态和时间,不是完整的历次同意事件档案。 新一代申请复用该名单的退订能力,先前尚未过期的退订链接仍能撤回该名单,避免重发邮件使旧退订入口失效。确认链接按配置过期,退订链接有效期为签发时起一年。后续营销邮件必须提供当时有效的退订入口;当前没有批量营销发送器,真实提供商侧退订仍需外部验收。 ## 链接与页面边界 链接是绑定站点、用途、操作、记录和有效期的签名能力,不含邮箱。确认和退订不能互换。确认 / 退订操作使用同源 POST;前台不自动重试修改请求。有效链接可以由持有人使用,应当像其他邮箱确认链接一样保密。 签名放在 URL 的片段中,普通网页请求和来源头不携带它;页面不加载统计 beacon,并声明不发送来源。偏好页面禁止搜索索引。链接只在当前页面内存中使用,不写入持久化浏览器缓存;修改成功后移除当前地址栏中的片段,这不保证删除浏览器之前的历史。 同一标签页打开另一封邮件的链接,会重置页面状态并读取新链接。键盘跳过导航不会覆盖邮件片段。无 JavaScript 时页面不执行确认或退订。 ## 重复与失败处理 申请在同一邮箱 / 用途的事务锁内串行,并对已有记录加行锁。确认、退订和再次申请竞争时,写入使用最新的实际状态,已确认的记录不能被较早读到的申请覆盖。 确认邮件通过数据库原子领取发送权,发送时不持有数据库锁;回写只匹配原申请代次和发送租约。租约为 45 秒,重试间隔 15 分钟,同一代最多三次,过期、退订或关闭该入口后不再领取。 | 发信状态 | 实际含义 | | --- | --- | | `pending` | 尚未获得一次成功发送响应 | | `accepted` | 本地捕获或发送接口已经接受请求;不代表邮箱实际收到,更不代表订阅已确认 | | `failed` | 本次响应未能确认成功,保留通用错误码和下一次尝试时间 | 同一代重试沿用同一个供应商幂等 key。核对日期为 2026-10-10:[Resend 的幂等窗口为 24 小时](https://resend.com/docs/dashboard/emails/idempotency-keys),因此确认有效期最多配置为 24 小时。网络结果不确定、租约超时和本地捕获重启仍需要排查,不能承诺所有环境下绝对只收到一封邮件。 确认重试使用已有 API 定时入口。先完成文件清理与任务恢复,再处理联系人同步,最后处理确认邮件与限流清理;可选营销故障分别隔离。关闭名单停止其新申请、确认和发信,历史退订仍可使用。关闭两个名单但保留 `contactsSync`,仍会处理历史退订;三项都关闭时定时处理不读取营销表。已记录供应商全局退订的地址不再领取确认发信,也不能通过新的本站确认解除阻止。 ## 联系人同步与供应商退订 接口核对日期:2026-10-10。采用当前 [Contacts](https://resend.com/docs/api-reference/contacts/create-contact) 与 [Topics](https://resend.com/docs/api-reference/contacts/update-contact-topics),不使用旧 Audience。名单用途分别映射到两个独立主题;同步前实际读取主题配置,要求二者的 `default_subscription` 都为 `opt_out`,防止默认授权另一用途。主题 ID 相同或配置不完整会被离线检查拒绝,远端配置不符则保留 `TOPIC_CONFIGURATION` 并停止本次写入。 | 服务端变量 | 规则 | | --- | --- | | `RESEND_API_KEY` | 复用邮件密钥;联系人操作需要 [Full access 权限](https://resend.com/changelog/new-api-key-permissions),仅 Sending access 不足 | | `RESEND_MARKETING_WEBHOOK_SECRET` | 本站营销回调独立的 `whsec_` 签名 secret,不能使用 Stripe secret | | `RESEND_NEWSLETTER_TOPIC_ID` | 邮件订阅主题 UUID;每站、每环境独立 | | `RESEND_WAITLIST_TOPIC_ID` | 候补主题 UUID;与订阅主题不同 | 后三项一起设置或一起留空;`contactsSync: true` 时全部必需。先在 Resend 创建两个默认退订的主题,为该环境配置 `POST /api/marketing-webhook`,选择 `contact.updated`、`contact.deleted`、`contact.topics.updated`。本轮没有替你创建这些云资源。复制生成站不会复制本地 secret;两个站不能共用主题与回调配置。 当前实现按 Resend 工作区的邮箱联系人操作;两个站共享同一工作区时,独立主题不隔离联系人级别的全局退订。各站的数据库、用途与确认链接仍独立。 确认与退订在同一数据库事务里更新订阅和待同步版本;两个名单按同一邮箱串行,供应商 HTTP 在事务外运行。只申请未确认的邮箱不会被新建到供应商。新联系人先以两个主题均退订创建,再应用已确认用途;创建响应丢失时先查询现有联系人,避免直接重新创建。 每个邮箱用一个同步记录合并最新状态,租约 60 秒;每次定时处理最多三个邮箱。Contacts 请求有 5 秒中止信号,同一客户端请求启动间隔至少 650 毫秒;这不是跨所有 Worker 的全局限流,遇到供应商限流仍需重试。失败记录只保存通用码,以 2、4、8 分钟逐步延后,最多间隔一小时。它不改变账号、任务、积分或本站确认结果。成功后记录各用途的确认代次,每天复核时以代次判断是否存在已确认的供应商退订;不依靠两个本地时间的大小区分新同意。0016 只为已确认同步版本回填代次,未完成同步的版本不假报完成。升级前已有的订阅按每轮最多 20 个缺失邮箱补充同步记录。 回写匹配原租约与状态版本,租约过期或较新的退订不会被标成同步完成,旧响应结束后留下待恢复标记。外部 API 与数据库没有共同事务:退订期间已经发出的请求仍可能暂时写入旧状态,后续同步与回调负责修正,不能保证两边零延迟一致。联系人删除 / 更换 ID 或全局退订会记录持续阻止状态;本站和供应商的主动加入通知都不能自动解除它,本阶段没有解除阻止的管理入口。 回调使用 Resend SDK 在原始请求体上[验签](https://resend.com/docs/webhooks/verify-webhooks-requests),验证时间窗后再访问营销表;最大请求体 64 KiB。只保存事件 ID、负载摘要、类型、时间和是否应用,不保存原始签名、邮箱或完整负载。相同 ID 去重;同 ID 不同负载报冲突。无关邮箱和其他事件不会创建账号或订阅。 主题回调可能是本站创建时的退订回声。接收 [主题更新事件](https://resend.com/docs/webhooks/contacts/topics-updated) 时读取供应商当前主题状态,再结合最新本站确认时间判断;未完成首次同步会返回可重投的 503,而不是提前消费事件。旧通知不能覆盖较新的明确确认。真实主题退订只影响对应名单;供应商主动加入通知不作为本站同意,不能激活未确认名单。漏掉的主题退订会在每日复核中处理;全局退订保持阻止。事务邮件不依据营销状态停用。 ## 匿名请求保护 申请和偏好修改分别使用一分钟窗口,每个可信来源地址最多 20 次。限流表只保存带密钥摘要,不存明文 IP 或邮箱;超过一天未更新的桶由定时维护清理。来源取 Cloudflare 提供的 `CF-Connecting-IP`;缺失时合并到共享桶。本地代理可以模拟该头,不能据此宣称实际公网抗滥用效果已验收。 请求体限制为 8 KiB,只接受约定的 JSON 字段。公开申请不能填写订阅状态、来源、账号或回调地址。开启 Turnstile 后,申请还需完成对应动作与站点的服务端验证;本轮没有使用真实 CAPTCHA 供应商。 ## 本地验收 ```bash ENVIRONMENT=development bun --env-file .env --env-file .env.local \ apps/api/local/validate-marketing.ts ``` 命令只接受回环 PostgreSQL,创建独立数据库并应用完整迁移;使用实际生产 Worker 入口和本地邮件绑定,拒绝外部请求。它验证八并发申请只捕获一封确认、确认与申请竞争、用途隔离、退订后旧确认拒绝、失败发信与实际定时恢复、没有创建账号。结束后删除本次数据库与资源,不改主数据库。 增加 `--contacts` 会开启本次隔离配置与内存 Contacts 绑定,验证实际 Worker 的八并发定时领取、两主题写入、失败写入与后续恢复、SDK 原始验签、回调重放和全局阻止。`development` 的联系人服务必须提供 `LOCAL_MARKETING` 测试绑定;普通预览没有该绑定时明确失败,不会静默转成真实 Resend 请求。正式 API 请求格式另外与已安装 SDK 的受控 HTTP 请求做一致性检查。 增加 `--ui` 会构建独立公共页面,并短暂提供 `http://marketing.localhost:21311/newsletter`。使用 `browser@acceptance.example.test` 完成申请、确认和退订,再创建 `.local/marketing-ui-stop` 让验证器核对数据库并清理。捕获邮件存放在验证器显示的独立目录,不要上传或公开完整邮件链接。 实际浏览器已验证桌面、320 像素手机、黑白主题、同意门禁、确认与退订按钮、同标签页切换链接和键盘跳过导航。服务与页面测试还覆盖无同意、错误地址、未知字段、篡改 / 过期链接、代次失效、限流和失败请求不自动重试。 ## 接下来继续的能力 下一阶段补真实转化事件;真实 Resend 环境、实际邮箱投递和供应商退订页面另行验收。当前受控本地接口不替代这些证据。 工具开始、成功、下载发起与购买确认将使用同一事件契约,并与收入状态去重。页面访问不算使用,下载发起不证明保存到本机,展示价格不算购买确认。完成这些能力及真实外部环境验收之前,I11 保持进行中。 ### 两种语言与浏览器验收 增加 `--languages --ui`,命令使用同一独立环境开启西班牙语,实际验证同意原文、Worker 重启后的同键同载荷重试,并在 `http://marketing.localhost:21311/es/newsletter` 提供临时页面。使用合成邮箱 `browser@acceptance.example.test` 主动申请,再从该环境的 `/__local/mail/browser` 本地捕获邮件检查确认和退订。打开链接只读取,点击按钮才修改;完整流程结束后创建 `.local/marketing-ui-stop`,命令核对已退订、保留西班牙语同意且没有创建账号,然后清理本次环境。该固定邮件路径只存在于本地验收程序,不发布到产品 Worker。验收 HTML 留在忽略目录,不代表真实 Resend 或真实邮箱客户端已通过。 --- --- url: /docs/agentbuff-stack/files-storage.md --- # 私有文件存储 **当前状态:文件 API 与定时清理已通过本地真实 Workers 验收,工作台已接入主预览。** 两个干净生成站已有文件与隔离验收;本轮补齐 320 / 390 像素、浅深主题、键盘预览 / 取消 / 重试以及永久删除的实际浏览器流程。云端 R2 绑定、定时触发与生产验收尚未完成。 ## 配置与数据 `packages/core/website.ts` 的 `storage` 控制产品策略,`apps/api/wrangler.jsonc` 的 `STORAGE` 绑定提供私有 R2 桶。它们分别表示“允许怎样使用”和“实际存在哪里”。这里没有公开桶地址,也不接受客户端传入对象路径或用户 ID。 | 配置 | 默认值 | 作用 | | ------------------ | ------ | ------------------------------------------- | | `enabled` | `true` | 关闭后文件 API 拒绝访问;不自动删除现有数据 | | `maxFileBytes` | 5 MiB | 单文件上限;可配置 1 字节至 16 MiB | | `maxFiles` | 25 | 每账号保留文件数量;可配置 1 至 1000 | | `maxTotalBytes` | 50 MiB | 每账号累计字节上限;不能小于单文件上限 | | `retentionDays` | 7 | 上传时确定有效期;可配置 1 至 365 天 | | `uploadsPerMinute` | 10 | 上传与删除共用的每账号每分钟写入次数 | | `readsPerMinute` | 120 | 列表、详情与下载共用的每账号每分钟读取次数 | 这些是模板的产品限制,不是 Cloudflare 免费额度。降低上限不会删除已有文件;修改保留天数只影响新上传文件。过期后立即拒绝读取,物理删除由后续清理批次完成。 增量迁移 `0002_private_files.sql` 增加三张表:`user_file` 保存所有权、摘要、状态与有效期;`file_quota` 保存预留数量和字节;`operation_limit` 保存账号操作窗口。既有迁移保持不变。普通预览需要先核实自己的本地数据库目标,再按[迁移教程](../database/migrations)应用迁移;代码更新不会自动迁移预览或共享数据库。 `0003_file_cleanup.sql` 增加 `file_maintenance` 扫描游标 / 租约和 `file_cleanup_run` 批次记录,文件模块运行需要两项增量迁移。 ## 使用文件工作台 登录产品后打开 `/files`,主预览地址为 。侧栏的 Files 是个人文件入口,切换团队不会改变文件归属。文件能力关闭时隐藏导航,直接打开页面仍明确提示不可用;私有路由不会进入公开站点的搜索索引。 1. 点击 Choose a file 选择文件;页面显示名称和实际大小。空文件、超出配置上限的文件不能提交,服务端仍会重新验证内容和大小。 2. 点击 Upload file 上传。发送进度来自实际传输字节;100% 表示传输完成,页面继续等待存储确认,只有服务端返回 `ready` 才显示成功。 3. 在 Your library 查看有效期、状态与个人配额。View 打开会话授权预览,Download 下载原始字节;图片使用临时对象 URL,关闭预览后释放。文本和 JSON 按纯文本显示,最多预览 64 KiB,下载保留完整内容。 4. Delete 打开永久删除确认框,可用 Keep file 或 Escape 取消。删除失败显示原因并保留记录,只有服务端确认删除后才显示存储释放。 5. Refresh 更新当前状态;超过 20 条时使用 Load more files 翻页。处于保存或删除中的记录会自动轮询,失败和到期文件禁止查看 / 下载。 键盘可用回车打开预览或删除确认,Escape 关闭预览 / 取消未执行的删除,焦点回到原操作按钮。删除请求未结束时不能取消确认;失败后焦点回到重试按钮,成功后回到选择文件。列表刷新较慢时,等待状态结束后也恢复焦点。任务详情使用同一个私有预览组件,关闭后回到对应附件的预览按钮。 断网或超时可能发生在服务端已保存之后。工作台不会自动重传文件;先刷新列表确认,再决定是否重试,避免重复占用配额。登录过期交给已有认证恢复页面处理,不继续显示旧账号预览。配额包括尚未完成确认或删除的记录,页面也会解释这部分占用。 浏览器原件下载已核对 SHA-256,伪图片拒绝、超限提示、长文本限制与取消确认已实测。两个新生成站已有上传 / 查看 / 下载、实际 API 删除、定时清理和隔离验收;本轮另在隔离测试账号完成手机上传、长文件名、原件下载、实际图片解码、永久删除与任务占用拒绝。任务取消后在原失败弹窗用键盘重试成功;结束核对实际数据库、配额及 R2 对象全部归零。四种尺寸 / 主题组合均无横向溢出,确认弹窗在视口内;这是本地浏览器证据,不能替代真机、云端资源与生产验收。 ## 上传与下载接口 | 接口 | 用途 | | --- | --- | | `POST /api/files` | 直接发送文件二进制;不是表单上传 | | `GET /api/files/:id/content` | 会话授权读取;加 `?download=1` 强制下载 | | tRPC `files.list` | 每页 20 条,使用返回的 `nextCursor` 翻页;同时返回配额与策略 | | tRPC `files.get` | 当前账号的文件元数据 | | tRPC `files.remove` | 删除当前账号文件并释放配额 | 上传请求使用当前账号 cookie,`Origin` 必须等于 `APP_ORIGIN`;`X-File-Name` 填写经过 `encodeURIComponent` 编码的文件名,`Content-Type` 填写真实类型。服务端按实际读入字节执行大小限制,再检查内容。当前支持 PNG、JPEG、UTF-8 文本与 JSON,不支持 PDF、SVG 或其他格式。 PNG / JPEG 检查签名与必要结构,不代表完整图片解码或恶意内容扫描。文本拒绝非法 UTF-8 与不允许的控制字符;声明为 JSON 的输入必须能解析。摘要基于原始字节计算,下载不会改变内容。名字会移除路径和控制字符,不作为对象路径使用。 每次请求都重新核实账号及文件所有权。匿名会话不可使用文件能力;切换组织不会把个人文件变成团队文件。其他账号访问文件返回不存在,列表只包含自己的记录。JSON 默认作为附件;下载响应使用私有且不可缓存的策略,以及禁止内容嗅探和限制执行的响应头。 ## 状态与失败处理 上传先在数据库事务中预留数量和字节,再写入 R2,最后标记 `ready`。`uploading` 只表示正在写入;`deleting` 表示删除尚未完成;`failed` 保存故障代码。下载只接受未过期的 `ready` 文件。 数据库与 R2 无法共用一个事务。上传失败会尝试删除对象,只有确认对象删除后才释放配额;补偿失败保留记录与占用,等待后续清理重试。删除失败也保留占用,重复删除不会重复减额度。读取发现对象不存在时记录故障并只释放一次配额。不要把失败记录当成已经物理清理。 ## 定时清理与故障排查 实际 `worker.ts` 的 `scheduled` 入口调用 `lib/files/cleanup.ts`,使用新鲜数据库连接。当前 `dev` 配置为每 15 分钟一次;云端绑定和调度仍待独立部署验收。本地 Miniflare 不自动执行这个计划,验收命令显式调用真正的定时入口,普通本地预览暂不自动清理。 每批最多处理 20 条到期、失败、删除中或超过一小时未完成的上传记录,再扫描当前站点前缀下最多 100 个对象。游标保存在数据库,下一批继续翻页;扫描完重新开始。不属于本站前缀的对象不会被删除。无元数据的对象保留至少一小时后再删除,包括账号级联删除或上传进程中断后的遗留对象。 批次使用两分钟租约避免常规重叠;过期租约可重新领取。旧执行器不能覆盖新租约的游标,重复删除不会重复释放配额。超时上传清理后,迟到上传不能重新标记为可下载;没有完成补偿的迟到对象由后续扫描回收。 `file_cleanup_run` 保存开始 / 完成时间、文件删除数、扫描数、孤立对象删除数、故障数和稳定故障代码,保留 30 天;不保存内容和凭据。未完成且租约已到期的记录表示执行可能中断,下一批重新核对。文件本身保留 `DELETE_FAILED` 等原因;失败记录更新时间用于后续重试,避免只反复处理第一批文件。扫描页删除失败时保留该页游标,重新扫描。 清理失败会记录批次并使定时调用报告失败。检查批次记录、文件状态及仅含记录 ID 的日志,再核对 R2 与数据库;恢复服务后下一次调用继续处理。管理员查看界面尚待 I9。云端清理延迟与调用成本仍需实测,不承诺到期瞬间完成物理删除。 | 错误代码 | 排查方向 | | --- | --- | | `UNAUTHORIZED` | 登录状态失效,或当前是匿名账号 | | `DISABLED` / `NOT_CONFIGURED` | 检查配置开关和实际 `STORAGE` 绑定 | | `BAD_FILE` / `TOO_LARGE` | 检查内容、声明类型、文件名和大小 | | `QUOTA_EXCEEDED` | 检查数量、字节,以及未完成补偿的记录 | | `RATE_LIMITED` | 等待操作窗口结束;HTTP 文件路由返回 `Retry-After` | | `NOT_FOUND` / `FILE_NOT_READY` | 检查账号、文件 ID 和处理状态 | | `EXPIRED` / `MISSING_OBJECT` | 文件已到期或实际对象不存在 | | `STORAGE_UNAVAILABLE` | 检查本地资源服务、R2 与元数据写入;重试删除或保留故障记录 | ## 本地验收 ```sh bun run files:validate ``` 命令要求开发环境与 loopback PostgreSQL,并需要该本地数据库用户具备创建和删除测试数据库的权限。它创建独立临时数据库,应用真实迁移,启动实际 Worker bundle 与隔离 R2,通过两个账号验证四种文件类型、下载字节、摘要、安全响应头、权限拒绝、到期拒绝和删除;两次触发定时清理,检查实际删除、配额只释放一次及批次记录;结束后销毁测试资源。它不迁移配置中的数据库,不调用真实邮件、OAuth 或支付服务。 需要复验浏览器流程时,先构建工作台,再运行可选模式: ```sh bun app:build bun run files:validate -- --browser ``` 原文件验收完成后,命令继续保持自己的回环测试站运行,终端输出具体 `/files` 地址和该次临时目录内的 `browser.local.json` 路径。私有记录包含合成账号、上传样本、所选一次性数据库和停止标记;不要分享记录或复制到新站。用记录里的测试账号登录,只上传该目录的合成样本,不操作主预览文件。手机测试需在实际浏览器设置尺寸并使用页面主题按钮,不能用静态截图代替操作。 完成上传、预览、下载及删除后,确认库内文件与配额归零,再在该次记录的 `stop` 路径创建文件以结束。命令会核对数据库、配额和实际 R2 对象均为空,随后关闭自己的服务、删除临时库和目录。若还有文件则报告验收失败,清理不算浏览器删除成功。此模式默认阻断外部请求,不与 `--turnstile` 同跑;任务占用用例需在这份一次性库中安排未派发的测试任务,不能改正常预览的任务状态。 `apps/api/lib/files/storage.test.ts` 与 `cleanup.test.ts` 另用实际迁移和本地 R2 验证并发数量 / 字节配额、补偿失败、删除重试、缺失对象、操作限流、迟到上传、翻页与租约边界。孤立对象测试仅调整测试返回的对象时间来检查一小时宽限,实际对象由本地 R2 写入和删除。测试范围仍为本地模拟资源;生产权限、收费和配额需要独立云端验收。 --- --- url: /docs/agentbuff-stack/execution-security.md --- # 执行限制与验证 **当前状态:I3 与 I4 保护已接入,本地验收进行中。** 文件与个人任务接口已接入服务端原子限制,上传 / 任务提交 / 手动重试支持可选 Turnstile。收费与用户 API 密钥已复用业务限额;密钥自己的插件限额及版本接口见[教程](./api-keys)。真实验证码与云端负载仍待独立验收。 ## 限制在哪里执行 文件写入和读取分别使用 `storage.uploadsPerMinute`、`storage.readsPerMinute`。服务端按当前登录账号和操作,在 PostgreSQL 用同一条件更新原子计数;通过账号与入口校验后,受限步骤的失败请求也计入。每分钟窗口根据数据库时间开始,拒绝后返回稳定的 `RATE_LIMITED`。HTTP 文件入口带 `Retry-After: 60`,tRPC 错误含 `executionError` 和 `retryAfterSeconds`。 限制在服务端执行,修改前端按钮或直接请求不会绕过。tRPC 批量请求的每个过程分别计数。当前策略是固定窗口,并不代表滚动窗口、全站并发上限或模型成本预算。独立站使用各自数据库,不能共享计数表。文件配额另用原子预留,不能用请求限流代替字节 / 数量占用。 当前个人文件与任务拒绝匿名会话;公开 JSON / 文本工具在浏览器本地处理,不调用收费接口。认证继续使用 Better Auth 自身限制。任务使用 `tasks.writesPerMinute` / `readsPerMinute` 和独立操作键,提交 / 重试 / 取消与读取分别计数;来源必须匹配 `APP_ORIGIN`,组织切换不扩大所有权。取消不要求验证码,便于用户停止等待。访客服务端入口及外部 API 限制随实际功能落实。 ## 开启 Turnstile 1. 在 Cloudflare 创建属于该站和环境的 widget,配置实际允许的域名。 2. 在 API 环境填入 `PUBLIC_TURNSTILE_SITE_KEY` 和 `TURNSTILE_SECRET_KEY`。前者可公开,后者只保留在服务端,不提交到 Git。 3. 设置 `websiteConfig.security.turnstile.enabled = true`,执行 `bun config:check`,再一起构建前后端。 开启但缺少配置会使配置检查 / API 初始化失败,不静默变成未保护模式。两个值需成对配置;Cloudflare 官方测试键仅允许 development,staging / production 拒绝这些已知测试键。生成站不复制源站凭据,需要单独配置。 文件列表 API 只返回公开 site key 和动作 `file_upload`。工作台按此配置显式加载官方 SDK,关闭时不插入验证脚本。组件跟随浅深主题,使用紧凑尺寸,避免窄屏表单被 iframe 的最小宽度撑开;加载失败、验证失败或过期会清空 token 并提示重试。路由退出 / 重新验证时移除旧 widget,迟到回调不能重新激活旧 token。 上传把 token 放入 `X-Turnstile-Token` 请求头,原始文件仍为请求体。服务端先确认会话、本站来源与操作频率,再调用固定 Siteverify 地址,最后才读取文件并预留存储。只有成功且 `action === file_upload`、`hostname === new URL(APP_ORIGIN).hostname` 的主机名匹配时继续。客户端传来的域名或动作不决定服务端期望值。 任务提交和手动重试将 token 放在独立的 `verificationToken` 字段,分别要求 `task_submit` / `task_retry` 动作。服务端验证后才受理或开启新的尝试,token 不保存在请求指纹与数据库。页面复用同一 SDK 生命周期和错误恢复,公开接口只返回 site key 与期望动作。验证失败、错误域名 / 动作不会创建任务;批量请求的每项均验证并计数。 验证请求最长等待 10 秒;网络错误、服务异常、非成功 HTTP 响应或非法响应结构都拒绝操作。token 使用后不可再用,所以每次提交结束都会清空并重新生成;不自动重传上传文件,也不自动重试结果不确定的验证。失去上传响应时先刷新文件列表确认是否已保存。日志不记录 token、secret、provider 响应或文件内容。 Siteverify 请求使用 `redirect: "manual"`,仅接受成功 HTTP 响应,3xx 不跟随、不继续转发服务端 secret 或用户 token。2026-10-11 的实际 workerd 验收确认,原 `error` 模式在当前运行器中会于请求发送前失败;修复后正常请求、外站 Location 拒绝和十秒期限均通过。Cloudflare 文档仍列出三种重定向值,因此该结论限定于本项目已安装运行器的实际行为,不推断所有云端版本均不支持 `error`。 | 错误代码 | 行为 / 排查 | | --- | --- | | `RATE_LIMITED` | 等待窗口结束,再提交;不要连续自动调用 | | `VERIFICATION_REQUIRED` | token 缺失或超长,先重新验证 | | `VERIFICATION_FAILED` | 验证失败、过期 / 重放,或动作 / 域名不匹配 | | `VERIFICATION_UNAVAILABLE` | 验证服务故障,保留输入并稍后重试;不会继续写存储 | | `VERIFICATION_NOT_CONFIGURED` | 检查开关、公开 site key 与服务端 secret | 目前工作台 HTML 尚未设置 CSP。若后续增加,需按 Cloudflare 官方说明允许 widget 所需的脚本与 iframe 来源;文件下载的独立限制响应头保持原有策略。 ## 验证口径与尚未完成项 服务模块测试控制 Siteverify 响应,覆盖成功、缺失配置、token 过长、错误动作 / 域名、重复 / 过期、网络失败、异常状态与响应结构。组件测试使用受控 SDK 回调,检查脚本失败重试、过期清空、卸载及旧回调失效,不下载第三方脚本或完成真实验证码。 实际 Hono 文件路由、Better Auth 会话、真实迁移的 PGlite 和本地 R2 共同验证:未通过验证时文件与配额表为空;成功后存储一份文件;公开列表不泄露 secret。真实 tRPC 三项批量读取、上限为二时,只有两项成功,第三项有 429 业务错误和重试提示,数据库计数为二。这些是本地集成证据,不代表真实 Cloudflare widget 验收或线上负载表现。 任务路由测试另验证缺 token、上传动作误用于任务、提交动作通过后只创建一个任务,以及真正 tRPC HTTP 批量读取的逐过程限制与重试字段。主预览默认保持验证关闭,实际任务操作可以检查且未加载第三方验证码脚本。真实 widget 的前端 / 服务端匹配、上线域名、网络错误下体验、真实验证码手机尺寸及负载成本仍需独立验收;收费与 API 密钥路径保留原计划要求。 ```bash bun files:validate bun files:validate --turnstile ``` 两个命令均使用一次性 loopback PostgreSQL、生产 workerd 和本地 R2,不给主库迁移或写入。第二条在自己的配置文件开启校验,使用受控 Siteverify 接口,不连接 Cloudflare、不下载 widget 或完成真实验证码。它核对缺失 / 超长 token、错误域名 / 动作、重放、非法响应、503、带外站 Location 的 302 以及实际十秒超时;拒绝后文件 / 配额表和 R2 都为空,正确响应只写一份文件,随后仍执行原上传 / 私有读取 / 删除 / 清理验收。额外探测消耗真实业务限额,程序只调整一次性账号的窗口以继续验证,没有提高产品限额。最终清理自己资源并核对正常网站配置字节未变。 文件入口的原生证据与共享校验函数五种动作的单元测试分别记录,不将这次检查扩大为真实任务 / 收费 / 营销 widget 或云端验收。 参考官方文档(服务端 / 请求重定向于 2026-10-11 复核):[服务端校验](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/)、[Worker Request 与重定向](https://developers.cloudflare.com/workers/runtime-apis/request/)、[显式组件渲染](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/)、[测试键与真实验证的区别](https://developers.cloudflare.com/turnstile/troubleshooting/testing/)。 --- --- url: /docs/agentbuff-stack/billing-tasks.md --- # 支付、积分与任务 ## 当前支付能力 `websiteConfig.payment` 控制是否允许计费及 Pro 试用天数,Stripe 完整凭据组控制服务是否实际可用。`apps/api/lib/auth.ts` 配置 Stripe 插件,`routers/billing.ts` 提供账单可用性、当前计划与可管理权限。 I6 已增加一次性积分包、Checkout、签名回调和退款 / 争议对账;**默认关闭,真实 Stripe 沙箱与订阅完整生命周期仍待验收**。公开定价页明确为模板示例价;不是已售产品。服务端 Price ID 决定真实收费,网页展示价格需要站主同步。 关闭计费只停用插件与可用性,不删除用户订阅记录,也不能代替 Stripe 平台取消实际订阅。沙箱验收至少覆盖 Checkout、webhook 签名、重放、回跳、账单门户、取消 / 恢复及非管理员访问。 ## 托管支付与回站语言 新积分包购买保存发起页面的 `en` / `es` 语言:成功与取消都进入同语言的购买详情,Stripe Checkout 同时收到明确语言。`0024_credit_checkout_language.sql` 新增可选的一对一语言快照表,不改既有购买字段或历史迁移;启用积分包前完整迁移至 0024,重建 API 与工作台。正常预览仍使用已授权的 0012,积分包关闭时不会读取新增表。语言不是权限或发放依据,返回页面仍不能发积分。 语言与购买请求一同确定。同一请求更换商品或显式语言返回冲突;购买详情的恢复从原记录读取,不按当前页面重新生成。旧客户端未提供语言、旧购买没有快照时,保持原英文地址,并保留不含 `locale` 的原 SDK 请求;不能给同一个幂等键临时增加语言参数。未知语言拒绝,新购买使用已关闭语言也拒绝。 订阅的新结账成功 / 取消地址来自当前语言的设置页,已有未解决结账使用保存的原地址。Stripe 语言从这对保存地址确定,两个地址的语言必须一致;兼容已经接受的旧参数摘要,原请求是否包含请求标记和语言都保持不变。订阅成功返回入口仍在根路径 `/api/auth/subscription/success`,只有原有归属、当前资源与保存结果核查通过后,才回到保存的语言地址。未付款返回仍拒绝,不发积分、不释放未解决请求。 账单门户使用当前界面语言,返回固定的同语言 `/settings`,丢弃无关查询参数与片段;个人 / 团队账单主体、当前管理员权限与新鲜会话要求保持。外部 Checkout 和门户的实际翻译由供应商负责,本地只能核对送出的参数及本站返回行为,不能据此声称托管页面已实测。 **停用语言前,先等待该语言的未解决结账到期或完成核查。** 已接受的付款请求仍保留原语言与地址;直接关闭路由可能使历史付款回站 404。本阶段没有实现改写历史付款或自动替换到其他语言,修改幂等请求也不是修复办法。 `bun payments:validate-language` 使用实际 workerd、一次性本地 PostgreSQL 与受控 Stripe HTTP,验证两次真正 Worker 重启、丢失响应后的同键同载荷、改变语言拒绝、未付款拒绝、确认后的西班牙语回站与门户参数,并确认余额不增加;程序清理专用资源,不迁移主库。开发环境可追加 `--ui` 启动专用双语言工作台,使用合成账号检查返回页和设置页;不能跟随夹具的托管付款链接。实际浏览器已检查 320 / 390 像素与浅深主题,购买详情的长操作文案允许换行。真实 Stripe 沙箱仍是外部待验收。 ## 当前积分能力 `apps/api/lib/credits.ts` 保存可用余额和不可重复的增减事件。欢迎积分按 `credits.signupCredits` 在账户首次初始化时发放一次(整数 0—1,000,000);已有账户不会因改配置重复领取。任务受理、预扣、输入保留与待投递状态使用同一事务,任一步失败全部回滚。外部队列和模型调用在提交之后。积分属于个人,切换组织不会转移积分。 JSON / 文本浏览器工具继续免费。I4 本地任务 API、队列执行、私有结果、工作台、死信恢复与预算限制已实现;I5 新增报价确认、预扣、成功结算、取消 / 最终失败返还与输入到期维护。确定性文本任务默认免费,尚未接入收费模型;积分购买代码已接入,默认关闭。 ## 配置任务价格 在 `packages/core/website.ts` 设置: ```ts tasks: { enabled: true, maxActive: 5, maxAttempts: 3, writesPerMinute: 10, readsPerMinute: 120, creditCost: 0, priceVersion: "text-normalize:v1", }, ``` `creditCost` 是整数积分,范围 0—1,000,000;0 表示免费,无预扣事件。`priceVersion` 长度 1—64,使用小写字母、数字、冒号、点、下划线或连字符,以字母 / 数字开头。调整价格时同时更新版本,运行离线检查并重建前后端。服务端同时核对积分数和版本,不信任页面提交的价格。 登录后 `tasks.quote` 返回处理器报价和本人可用余额。收费提交需要 `acceptedQuote: { credits, version }`;页面显示价格、余额和确认选项。旧报价返回 `QUOTE_CHANGED`,余额不足返回 `INSUFFICIENT_CREDITS`,都不创建任务或扣积分。免费任务保留旧接口兼容,可不传报价。 每个任务保存受理时的价格与版本,后续改价不改变旧任务。响应丢失时页面保存完整原请求与报价,原键重放返回原任务,不再扣费。同键换输入、参数或报价返回冲突。收到明确报价 / 余额错误后,点击重新查看价格,确认新报价再提交。 ## 预扣与返还规则 `balance` 表示可用积分,预扣后立即减少。任务保存收费状态;账本使用 `task:<任务 ID>:reserve` 和 `task:<任务 ID>:refund` 唯一事件键。历史任务关联无级联外键,后续归档不删除账本;账号删除仍沿用原级联规则,这不是永久财务审计存储。 | 任务情况 | 积分处理 | | --- | --- | | 免费任务 | `free`,无预扣 | | 已受理、等待、执行、自动等待重试 | `reserved`,保留原预扣 | | 成功发布结果 | 同一事务标为 `settled`,不二次扣费 | | 可手动重试的失败,仍有预算且输入有效 | 保留预扣,按原价和原预算重试 | | 本人放弃失败任务 | 点击取消并返还,标为 `canceled/refunded` | | 不可重试失败或预算耗尽 | 与失败终态同事务返还一次 | | 运行中取消 | 先记录请求,执行器结束或失联恢复后返还 | | 可重试失败的输入删除、变更或到期 | 定时维护核对后返还,退款后禁止继续重试 | | 执行结果不确定,`reconciling` | 保留预扣,等待核查;请求取消不能直接退款 | 终态、结算 / 退款与账本在同一事务提交。并发取消、失败和发布由任务行锁裁定;旧执行版本不能发布,已退款任务不能领取或重试。数据库异常或原预扣不一致时回滚并报错,不伪装成已退款。 ## 本地检查与验收 ```sh bun run test -- --run apps/api/lib/tasks/charges.test.ts apps/api/routers/tasks.test.ts apps/app/components/tasks/workspace.test.tsx bun tasks:validate-charges ``` 前者使用实际迁移 PGlite / 本地 R2;页面接口为受控夹具。后者创建临时 PostgreSQL 与八个独立连接,检查重复只预扣一次、余额不足、取消 / 发布竞争和其他消费竞争,不迁移当前配置数据库。它验证数据库与服务,不代表实际业务 Queue。 真正收费 Worker 验收命令为 `bun tasks:validate-paid`。先用 `site:create` 创建独立验收站、独立 loopback 数据库与端口,完成安装与邮件包构建;仅该站配置正数 `creditCost`(如 3),欢迎积分至少覆盖三个任务。脚本不修改价格,主模板免费配置会明确拒绝执行。脚本另建 / 销毁临时数据库与 Queue / R2 目录,用实际 HTTP 与生产 Worker 核对报价、重复提交、成功结算、取消返还、原价重试、定时到期返还及重启下载。收费夹具不连接 Stripe,不代表真实付款。 增量迁移 `0007_task_charges.sql` 增加价格快照、收费状态与账本任务关联。上线前按历史应用迁移,不能只更新 Worker。任务执行细节见[任务生命周期](./task-lifecycle);已执行证据见[迭代记录](./iteration-progress)。云端、真实验证码、Stripe 沙箱与模型仍按后续迭代验收。 ## 订阅:权限、恢复与本地证据 状态维护仍归 Better Auth Stripe 1.7.4,本项目只读取其 `subscription` 表,不新增第二套订阅状态写入器,不将组织订阅自动发成个人积分。当前 SDK 22.6.2 使用 Workers 兼容的 Fetch 客户端,10 秒超时、SDK 自动网络重试关闭;事件处理失败交给投递重试。 服务端返回两组信息:`subscriptionPlan` / `status` 是现有账单状态,`plan` / `accessActive` / `accessUntil` / `limits` 是当前有效访问。有效访问要求 active 周期或 trialing 试用截止可核实、仍在有效期,截止还取周期、提前取消或 endedAt 的最早值。时间缺失、到期、续费失败和暂停都停止付费限制;当前未配置宽限期。显示成员上限不表示邀请入口已经执行该限制,相关实施与账号流程按 I8 验收。 设置页保留原方案和付款异常,待付、逾期、未付、暂停或周期信息缺失时不提供新的 Checkout。没有订阅、已取消,或原 Checkout 已确认过期且未关联订阅、本人有管理权时才显示新订阅入口。门户入口按本人的 Stripe 客户、活动组织客户或插件支持的活动订阅客户判断;不能将个人客户当成组织客户。组织成员可以查看,只有 owner / admin 可以管理。门户返回仍查询服务器,不按返回网址宣布付款成功。页面每 30 秒、回到窗口和手动刷新时检查最新状态;查询失败隐藏旧操作,缓存同时按用户与组织隔离。 ### 订阅结账保护与恢复 设置页新增 Pro 年付入口,仅在完整计费组启用且配置独立 `STRIPE_PRO_ANNUAL_PRICE_ID` 时提供;Starter 仍为月付。现有订阅改周期走门户,未解决原请求只能按原方案 / 周期继续。页面显示服务器保存的 `billingInterval` 与原请求的月付 / 年付选择,不根据本地按钮或网址猜测已开通。升级响应失败后会重新查询原主体的账单,避免丢失已接受的年付请求;核查和后续动作清除旧错误提示,忙碌时禁用刷新和其他付款动作。 Better Auth 当前版本的价格读取会吞掉异常并回退到配置 ID。本项目在实际 SDK Checkout 创建前另核对收费项:恰好一个已配置 Price ID、正整数数量、同模式、已启用、recurring / per\_unit / licensed、一月或一年且 `interval_count=1`、正整数金额。读取失败、价格已停用或信息不符不会发送 Checkout POST。这里比插件多一次当前 Price 读取,外部调用仍在事务外,租约与请求指纹规则保持。 年度入口不展示推算的折扣或年度金额;`plans.price` 仍是公开月度展示价。最终金额与周期在 Checkout 确认。当前本地验收使用受控年度 Price 与无需付款的试用会话,不代表真实年费已扣款。 插件仍是 `subscription` 状态的唯一写入者。`subscription_operation` 是结账请求和短期操作租约,不是另一张订阅状态表;迁移 `0010_subscription_operations.sql` 仅新增这张协调表,保留 0000—0009。 每个个人 / 组织账单主体持有两分钟数据库租约,插件的单行创建 / 更新在短事务内重新校验令牌、期限和当前管理权限。外部调用不持有数据库事务;旧持有者到期后不能覆盖新操作,也不能释放新令牌。不同账单主体使用不同租约。受保护的 HTTP 入口覆盖升级、取消、恢复、门户、结账回跳和订阅签名事件;直接使用未分配 scope 的服务端插件 API 不能写订阅。其他认证请求保持原生命周期。组织客户必须明确使用组织模式,个人模式不能借组织 ID 误用个人 Stripe 客户。 升级入口先读取新鲜本地订阅,再保存原方案、年付选项、回跳地址、配置价格指纹和固定一小时期限。已有有效、试用、欠费、暂停、未知或未完成订阅时不能另开 Checkout;改方案走现有账单门户。插件创建后的数据库订阅 ID 绑定到同一次请求。供应商请求参数只保存摘要,不保存整段 provider 载荷;Checkout SDK 请求使用稳定的 `subscription-checkout-` 幂等键,期限保持不变,客户创建也沿用该次请求的稳定键。失去响应后,只能重试原请求;更改商品、数量、元信息、回跳地址或价格配置会被拒绝。[Stripe 幂等请求说明](https://docs.stripe.com/api/idempotent_requests)。 创建和恢复已知 Checkout 都分页检查该客户的当前全部订阅,最多 1,000 条;不能只检查第一页,遇到未结束订阅或超过上限时保留核查。设置页从当前本人 / 组织账单接口获得原请求,提供“Resume original checkout”,不同时提供新购按钮;用户无法从查询其他账户拿到该请求。续费失败或旧取消记录不会遮住新的待支付记录,多笔未解决订阅不任意挑一笔。 新请求在 Checkout 元信息中保存 `subscriptionAttemptId`,用于查找已接受但本地失去响应的会话。升级前已经发送的旧请求若没有标记,仍沿用原 SDK 参数摘要和幂等键,不在重试时追加字段。设置页提供“Check previous checkout”,可以在原请求超过本地期限后继续核查。 该按钮调用 `/api/auth/subscription/recover`,只核查服务端保存的原请求,不能由浏览器指定待认领的会话 ID。已有 ID 时读取当前会话;未知时,按原客户和固定一小时创建窗口分页读取 Checkout,最多检查 1,000 条,并要求恰好一个请求标记匹配。读取后重新核对主体、客户、模式、本地插件行、原期限及标记;列表中的历史状态不能直接作为结果。[Stripe 会话列表接口](https://docs.stripe.com/api/checkout/sessions/list)。 | 当前原会话 | 恢复行为 | | --- | --- | | open | 核对当前客户没有未结束订阅后保存原会话;页面刷新并允许继续原请求,核查本身不跳转或创建付款 | | complete 且 paid / no\_payment\_required | 复用已验证当前资源和插件回跳处理,事务内保存校验、提交后核对,再确认原请求已解决;页面按实际订阅状态显示权限 | | expired | 会话没有关联订阅,客户没有未结束订阅,且原本地行仍 incomplete / 没有供应商订阅时,释放原请求;供应商提前过期也适用,随后才允许新请求键并复用插件行 | | 未付款、找不到、多个匹配、列表不完整 / 超限或信息不符 | 保留核查;不从“查不到”推断可以再付一次,失败不会创建替代 Checkout | 供应商不可用返回 503,可以再次核查;主体忙或仍无法确认返回 409。核查中的旧租约和已撤销组织管理权不能写回。**旧请求未保存会话 ID,且实际 Checkout 没有新增标记时,仍不能自动归并**;已有 ID 的旧请求可以继续核查,没有 ID 的旧请求使用下面的管理员人工流程。不要手删操作记录来解锁付款。 ### 未标记旧结账的人工核查 站点管理员在管理页选择账号,进入订阅记录中的旧结账恢复表单;组织账单可在全部记录中填写实际组织 ID。需要当前有效的站点管理员身份及近期登录,团队所有者不因此获得管理权限。账单配置必须完整,正常未配置支付的本地预览不会调用真实 Stripe。 1. 在正确测试 / 正式账户的 Stripe 控制台核对原始创建请求:客户、本地订阅编号、`subscription-checkout-` 幂等键及返回的会话编号。单凭邮箱、金额或“看起来相同”的会话不能认领。此步骤需要实际人工确认;接口的归属核对不能证明原幂等键。 2. 填入原会话编号,先核查。服务端按原客户和固定一小时窗口分页读取所有状态,最多 1,000 条;同一本地订阅编号及原到期时间必须恰好对应这一个会话。再读取当前会话,核对主体、客户、模式、测试 / 正式环境、原期限及创建时间,且确实没有新请求标记。本地原请求必须仍未解决、没有已知会话,原插件行仍未完成且没有供应商订阅,客户只能归属这一账单主体。找不到、多个候选、分页异常或信息不符均保持待核查。 3. 页面展示原尝试编号、当前状态和到期时间。填写原请求核对的原因,再确认关联。提交重新读取供应商,按核查摘要防止会话状态或本地请求在确认期间变化;变化后取消并重新核查。管理员权限、会话及原租约在提交事务再次验证。 4. 原会话编号 / 原继续地址和两条永久审计一起提交:`subscription.checkout_link_requested`、`subscription.checkout_link_completed`。审计失败全部回滚,保留原请求 ID 重试;失去响应后再次确认相同请求不会重复关联。审计保存账单主体、原尝试、会话编号、摘要、状态及人工原因,不保存供应商完整载荷、支付地址或密钥。 5. 关联成功仅补齐原会话记录;没有创建付款、退款、发放权益或解除购买阻塞。账号持有人随后在账单页核查原结账,复用上表的 open / complete / expired 处理。即使会话显示完成,实际付款和订阅状态仍由原核查与插件验证。 缺少原始请求证据、相同时间窗存在多条候选、价格 / 客户配置变化导致原恢复仍拒绝时,继续调查供应商记录;本入口没有强制清空或指定“已支付”的选项。带新请求标记的未知会话使用原自动核查,不走这个兼容入口。[Stripe 会话读取接口](https://docs.stripe.com/api/checkout/sessions/retrieve)、[分页与状态过滤](https://docs.stripe.com/api/checkout/sessions/list)用于当前资源核对;本地受控响应不能代替真实沙箱验收。 回跳要求新鲜登录和账单管理权限,先核对 provider 会话、元信息、本地订阅、客户及测试 / 正式模式;插件再次读取供应商时也经过相同归属校验。不能用其他人的 Checkout ID 让插件写入其订阅。升级 / 门户等写入口每人每分钟最多 10 次,回跳及原请求核查共用每分钟 30 次的独立额度;跨站来源在处理前被拒绝,回跳地址只能是当前站点。 ### 当前状态同步与故障重投 订阅回调先限制原始载荷为 256 KiB,保持原始字节验签,再核对模式和本地主体后获取租约。过大返回 413;错误签名、归属或模式返回 400;主体忙、供应商读取失败、当前商品列表不完整或无法确认受支持方案返回 503,允许再次投递。所有非 2xx 都不能当作处理成功。 租约内通过真实 SDK 读取当前订阅;Checkout 通知还先读取当前会话,核对完成状态、关联订阅、客户和元信息。校验当前对象与本地记录的主体、客户、订阅 ID、模式、配置价格、月付 / 年付周期及试用范围一致。已存在的行将当前对象交给插件的更新处理器;只属于一个已知客户且尚无记录的订阅交给创建处理器。旧 created / updated / deleted 或 Checkout 通知因此不会直接用历史快照恢复旧试用、覆盖新的取消状态。Stripe 不保证事件按顺序到达,应在需要时读取最新对象,见[官方回调说明](https://docs.stripe.com/webhooks)和[订阅读取接口](https://docs.stripe.com/api/subscriptions/retrieve)。 订阅表仍只由 Better Auth 插件写入。插件写入的短事务内读取刚保存的记录,核对主体、客户、方案、席位、周期、试用、取消 / 结束时间和日程关联;不一致则整个写入回滚,插件吞掉错误时也不返回成功。锁定版本创建处理器遗漏的取消 / 结束 / 日程字段,由适配器根据已校验当前对象补入同一次插件创建,避免先提交缺失字段的权限记录。提交后仍再次核对保存结果。历史已使用的试用时间允许插件保留,当前 trialing 必须有完整可核对的范围,访问权限另按截止时间判断。 内部处理事件的类型代表当前对象应走的创建或更新路径,**不保证调用原通知对应的完成 / 删除钩子**。目前没有在这些钩子发邮件或积分。原通知 ID / 类型另行保留于本次处理上下文;需要一次性副作用时,必须在 I12 实现持久事件去重和通知发件箱,不能依赖本次重投天然只执行一次。供应商读取和本地提交也不是跨系统原子事务,读取后发生的新变化仍需后续通知收敛。 每个 SDK 客户端在包装验签方法前复制其共享的回调对象,防止不同请求的模式 / 租约校验串用。GET 结账回跳现在复用相同的当前资源 / 保存校验;回到设置页仍不代表付款成功。 ### 结账回跳:当前资源与保存结果 回跳要求新鲜登录和当前账单管理权,核对当前 Checkout、本地主体 / 客户与模式;只有 complete 且 paid 或 no\_payment\_required 的会话进入更新,open、expired 或 unpaid 返回 409。读取失败返回 503,保存失败返回非 2xx,均没有成功跳转,也不提前释放原结账请求。失败修复后可以重新访问同一回跳,不重新创建付款。 回跳在主体租约内读取当前订阅。本次请求内插件复用已核实的 SDK 会话与订阅响应,避免重复外部读取造成快照不一致;订阅仍由原插件更新,事务内 / 提交后检查状态、周期、试用、席位、取消和日程。回跳不伪造已签名 Stripe 通知,也不发个人积分。当前 canceled / past\_due 可以同步并返回设置页,页面必须按服务器实际状态判断权限。 锁定 Better Auth Stripe 1.7.4 的原回跳在本地 active / trialing 时会提前返回,并遗漏 endedAt / stripeScheduleId。`patches/` 小型补丁取消提前返回并补齐字段,API 精确锁定版本;Bun 安装自动应用,生成器及二次生成保留补丁。新生成站已实际离线安装并核对补丁生效。升级和移除补丁的流程参考 [Bun 官方说明](https://bun.sh/docs/pm/cli/patch);同时保留项目的补丁说明与第三方许可证。 `apps/api/lib/subscriptions.test.ts` 使用真实 Better Auth 路由、实际迁移 PGlite、真实 SDK 和受控 HTTP,覆盖正常生命周期、原请求复用 / 响应丢失、门禁 / 回跳归属、旧事件与旧 Checkout、供应商失败恢复、创建和 Checkout 保存失败、年付状态同步及客户端校验隔离,另覆盖未知会话查找、分页 / 歧义 / 超限、供应商失败、旧请求参数兼容、提前过期、完成保存失败重试和旧租约写回拒绝。另覆盖年付首购 SDK 参数、同键重试与改月付拒绝、缺少配置、错误价格类型 / 模式 / 周期 / 归属、插件读取后商品停用、价格读取失败;年付首购页面另由实际浏览器和受控原生 Worker 验收,组件测试接口仍为受控夹具。 `bun subscriptions:validate` 使用八个实际 PostgreSQL 连接和插件 HTTP handler,验证结账并发 / 原键重试、旧操作被拒绝、不同主体与撤权保护,并验证一条签名通知处理中其余七条返回 503、重投后均保存当前 active 状态。另让原请求恢复查询暂停,其余七个并发核查返回 409;解除后全部继续核查同一会话,没有再次创建。SDK HTTP 全部受控,不访问 Stripe;这条脚本的插件 HTTP 在 Bun 执行,Worker 验收由下一条脚本覆盖。 `bun payments:validate` 另在生产 workerd 和一次性 PostgreSQL 验证订阅签名、大小、模式、正常状态更新、旧 created / deleted、当前读取失败及触发器故障后的相同事件重投。用实际 Worker 注册会话验证 GET 回跳更新、失败不跳转与修复后重试。本批另验证 Worker 发出的实际 SDK Checkout POST:受控服务先接受带请求标记的会话再模拟响应丢失;核查找回 open、确认提前 expired,重新请求后完成会话经插件同步,核查不增加创建次数。所有外部请求都由本地拦截返回受控响应,其他目的地拒绝;没有实际 provider API、门户 HTTP 或浏览器订阅付款。 浏览器在独立一次性数据库 / 受控 Worker 走过核查失败、修复重试、确认过期后新购按钮与刷新保持,整个验收只有一份受控创建;320 像素无横向溢出,临时资源已删除。本批另在独立回环主机名 127.0.0.1 的 13410 站点,通过浏览器选择年付、接受后失去响应、自动读取原年付请求,再核查同步 yearly / trialing,刷新保持;只有一个受控 Checkout POST,原主预览 Cookie 不受影响。320 像素下年付选项无横向溢出,临时资源删除。未标记旧未知会话的人工核查 / 关联、审计故障回滚、同请求重试及原用户恢复已在一次性 PostgreSQL / Worker 和实际管理页验收;完成 / 过期会话另以真实 SDK 受控响应和实际插件流程验收。真实 Stripe 沙箱和生产绑定仍按 I6 / I9 验收。 ## 一次性积分包:I6 本地实现 状态:服务端、页面和本地受控测试已接入,不代表实际收款已验收。当前支持 USD、GBP、CAD、AUD、EUR,金额按整数最小货币单位保存;暂不支持零小数币种、优惠码、自动税费或运费。 在 `packages/core/website.ts` 配置独立目录。以下为未启用示例,金额 900 表示 9.00 美元;必须替换为本站 Stripe 测试账户的真实一次性 Price ID 后才能启用: ```ts creditPacks: { enabled: false, packs: [{ sku: "credits-100", name: "100 credits", version: "credits-100:v1", amount: 900, currency: "usd", credits: 100, priceId: null, }], }, ``` SKU 与 Price ID 均不能重复,最多 20 个积分包;金额和积分为 1—1,000,000 的整数。调整报价时更新版本,前后端一起重建。`payment.enabled` 与 `creditPacks.enabled` 必须同时为真,服务凭据完整时目录才允许创建购买。离线检查不证明 Stripe 商品有效,创建 Checkout 时还会核对该账户当前 Price 的模式、金额、币种及一次性类型。 ### 环境和事件职责 当前 API 沿用完整订阅凭据组:`STRIPE_SECRET_KEY`、`STRIPE_WEBHOOK_SECRET`、`STRIPE_STARTER_PRICE_ID`、`STRIPE_PRO_PRICE_ID` 必须一起配置。积分包另需 `STRIPE_CREDITS_WEBHOOK_SECRET`;仅填写两个积分包变量不足以启动整套 API。测试密钥使用 `sk_test_`,生产使用 `sk_live_`,商品及回调必须属于同一模式。 订阅继续由 Better Auth 的 `/api/auth/stripe/webhook` 处理。积分包使用独立的 `/api/payments/stripe` 回调及独立签名 secret,不能复用订阅 endpoint secret。当前已安装 Stripe SDK 22.6.2、Better Auth Stripe 1.7.4;本批没有添加第二套订阅状态处理器。 积分 endpoint 接收以下事件:`checkout.session.completed`、`checkout.session.async_payment_succeeded`、`checkout.session.async_payment_failed`、`checkout.session.expired`、`charge.refunded`、`refund.created` / `updated` / `failed`、`charge.dispute.created` / `updated` / `closed` / `funds_withdrawn` / `funds_reinstated`。其余已验签事件被忽略,不参与积分发放。 回调先限制原始请求为 256 KiB,再用 SDK 与 Web Crypto 校验签名、时间和测试 / 生产模式。错误签名或模式返回 400,过大请求返回 413,处理失败返回 503 以允许 Stripe 重试;只有事务已提交或事件确实不属于本模块时返回 200。不要将解析后的 JSON 重新编码来验签。[Stripe Webhook 文档](https://docs.stripe.com/webhooks)。 ### 创建、回跳和发放 1. `/credits` 显示本站公开目录及本人购买历史。个人所有权不随组织切换改变,匿名会话与跨站创建被拒绝;操作有独立限流和可选 Turnstile 验证。 2. 服务端先保存 `creating` 购买和不可变报价,再调用 Stripe;客户端仅确认 SKU、版本、金额、币种和积分,不能指定 Price ID 或其他用户。原请求键用于断线重试,同键改商品或报价被拒绝。 3. 每笔购买有 60 秒操作租约。Checkout 使用固定购买 ID 作为 provider 幂等键,返回链接只能来自 `https://checkout.stripe.com`。外部调用在数据库事务之外;未知创建结果保留原购买,避免另开一笔。购买固定一小时到期,到期后不再重新创建。 4. 成功和取消都回到发起语言的购买详情:英文 `/credits/purchases/<购买 ID>`,西班牙语 `/es/credits/purchases/<购买 ID>`。页面查询服务端状态,URL 不携带发放权限。等待状态定时刷新,已确认状态显示积分;网络故障时不提供可能过时的继续支付按钮。 5. 签名事件处理期间读取 Stripe 当前 Checkout、PaymentIntent 和 Charge,核对归属、商品、数量、金额、币种、模式及实际支付状态。延迟支付未成功时不发积分,旧失败或过期事件不能覆盖已经发放的购买。 6. 唯一事件记录、`paid` 购买状态、账户余额与 `purchase::grant` 账本事件在同一事务提交。不同事件 ID 指向同一笔付款也只能发一次;失败回滚后可重新投递。账本可进入关联购买详情。 迁移 `0008_credit_purchases.sql` 增加购买和去重记录,并给账户增加支付核查标记、账本增加购买关联。历史迁移不被重写。账号删除当前仍沿用购买级联删除;事件和账本的购买关联不是级联外键,**尚未构成永久财务审计保留制度**,生产售卖前需补齐 I9 的保留 / 删除规则。 ### 退款、争议与积分差额 每次事件和本人发起的服务端核查都会读取当前 Checkout、PaymentIntent、Charge,并分页读取全部退款与争议,不按事件载荷或到达顺序直接加减积分。单种记录最多自动检查 1,000 条;超过上限、未知状态或付款信息不符时保留核查,不用截断结果计算。重读 Charge 与列表不一致时等待后续核查,不据此恢复积分。[退款状态](https://docs.stripe.com/api/refunds/object)、[争议状态](https://docs.stripe.com/api/disputes/object)。 | 情况 | 当前积分规则 | | --- | --- | | 成功退款 | 只累计 `succeeded` 金额,按购买快照计算扣回 | | 待处理 / 需要操作退款 | 不扣回该退款积分,暂停进一步收费使用 | | 失败 / 取消退款 | 不计入当前成功退款;如曾扣回,仅恢复实际扣过的部分 | | 争议处理中,包括 inquiry | 暂停收费,并按争议金额预留扣回;暂时资金恢复事件不能代替最终结果 | | 争议败诉 `lost` | 保留扣回;扣回齐且无其他未解决情况时可解除该笔核查 | | 胜诉、询问关闭或争议被阻止 | `won` / `warning_closed` / `prevented` 不再占用争议扣回额度;已成功退款的额度仍保留 | | 信息不匹配 | 持续人工核查,普通重查不能自动清除;管理员处理界面留在 I9 | 扣回总额采用可复核的累计公式:`floor(购买积分 × min(原付款金额, 成功退款总额 + 未恢复争议金额) / 原付款金额)`。全额退款扣回全部积分,多次部分退款统一累计后向下取整,避免每个退款单独取整造成偏差。退款与争议重叠时最多扣回原积分包数量;手续费和汇率损失不换算成额外积分。超过购买金额或不支持的币种状态转入核查。 例如 100 积分售价 900 最小货币单位,两笔各退款 200 时,累计应扣回 `floor(100 × 400 / 900) = 44`;第一次扣 22,第二次再扣 22。补齐全额退款时累计扣回 100。 余额不足时先扣可用部分,余额保持非负,并保存 `shortfallCredits`。例如账户只剩 10,应扣回 100,则余额为 0、已扣回 10、差额 90。暂停新积分包购买、新收费任务和收费任务手动重试;已有 Checkout 链接也在本站界面隐藏。免费任务、历史查看、取消返还仍可用,执行中的已预扣任务继续原结算。本站隐藏链接不会使已在外部打开的 Stripe 页面失效,最终仍以签名事件和当前付款状态核对。 争议胜诉时,只恢复实际扣过的积分,不把 90 的差额也当成可返积分。上例恢复 10,差额清零。若是退款且未解除,任务取消等使余额重新可用后,差额仍保持核查,直到另一次可信事件或 `purchases.reconcile` 从当前支付状态重新核对并扣回剩余部分;不自动谅解差额,不通过手改余额绕过状态。 详情页显示退款 / 争议金额、累计应扣回、实际已扣回和差额,另显示最近 20 条事件与服务端核查记录。`Check payment status` 只读取本站状态;已配置支付服务时的 `Recheck with payment provider` 会调用当前支付方核查。后者要求本人、同站来源和业务限流,客户端仅提交购买 ID;关闭新售卖后仍可核查既有购买,不能随便提交扣回金额或解除标记。 迁移 `0009_credit_purchase_settlement.sql` 增加累计对账字段和事件快照。每次余额调整使用单调递增版本 `purchase::adjustment:<版本>`,并以 `reversal` / `restoration` 保存账本。事件、购买快照、余额和核查解除同事务提交。所有购买处理先持有自己的租约,再持有余额锁;解除账户标记时检查本人全部购买,避免另一笔并发争议被清掉。 ### 验收边界 本地支付测试使用真实 Stripe SDK、受控 HTTP 响应和实际 PGlite 迁移,覆盖响应丢失、报价变化、重复 / 乱序 / 延迟事件、事务回滚再投递、信息不匹配、退款 / 争议核查与越权拒绝。页面测试覆盖旧请求保留、服务端确认、差额显示、核查返回的实际恢复额,以及另一笔付款暂停时禁用已有 Checkout;这些均不是真实支付。 `bun payments:validate` 使用生产 workerd bundle 与一次性本地 PostgreSQL,实测签名、超时签名、模式及大小边界;合法的订阅发票事件被忽略,未调用 Stripe API,也未验收 Checkout 发放。该命令另验收上文 Better Auth 在原生 Worker 中的订阅更新边界;其临时资源会清理。`bun payments:validate-settlement` 另用八个实际 PostgreSQL 会话、真实 SDK 和受控 HTTP 验证并发 Checkout、不同事件只发一次、退款与任务消费竞争、多笔核查 / 恢复、回滚再投递;它不是 Worker HTTP 或真实支付。真正 Stripe 沙箱购买、退款 / 争议和订阅完整生命周期继续保留为 I6 外部验收门槛。[Checkout 创建参数](https://docs.stripe.com/api/checkout/sessions/create)、[Better Auth Stripe 插件](https://better-auth.com/docs/plugins/stripe)。 --- --- url: /docs/agentbuff-stack/task-lifecycle.md --- # 任务生命周期 ## 当前状态 I4 本地任务闭环已验收。任务记录、幂等提交、待投递恢复、执行租约、取消与有限重试已实现,并通过实际 PostgreSQL 并发验收。业务队列消费者、结果发布和定时恢复已接入同一生产 Worker 入口,真正 workerd + 本地 Queue / R2 + 临时 PostgreSQL 的接口、下载与重启验收通过。个人任务 API、列表与详情工作台已接入主预览,浏览器实际完成文本任务、结果预览 / 下载和刷新恢复。死信消费 / 账号恢复、执行预算耗尽及两份干净新站的任务闭环已通过本地验收。官网仍区分本地能力与云端交付;上述验收不代表已部署云端。 现有 JSON / 文本客户端工具继续同步运行且免费。首个队列处理器将采用确定性的 UTF-8 文本规范化,不调用外部模型,不收积分。I5 已接入可配置报价与事务预扣 / 结算,见[收费教程](./billing-tasks);外部供应商对账归实际供应商接入时验证。 ## 数据与服务边界 I5 增量迁移 `0007_task_charges.sql` 保存任务价格 / 收费状态与账本任务关联,新代码运行前需应用。 增量迁移 `0004_task_lifecycle.sql` 增加三个表,`0005_task_results.sql` 增加执行结果引用与允许零字节结果的文件约束,保留此前迁移与已有数据。更新后的文件删除 / 清理、队列与定时维护会查询这些结构,所以部署新代码前必须依次应用迁移;不要只更新 Worker。自动验收仅迁移脚本创建的临时数据库;本轮另更新已授权的任务自有 loopback 主预览库并重启预览,原三份验收文件保留,真实浏览器任务另新增一份结果。生成站与远程数据库本轮未迁移。 | 表 | 用途 | | --- | --- | | `task` | 个人所有者、请求键和指纹、处理器 / 版本、输入快照、参数、状态、次数、执行 / 投递租约、结果引用与稳定错误码 | | `task_attempt` | 每次领取版本的执行记录与该次预留结果 ID;`task_id + lease_version` 唯一 | | `task_input_pin` | 活跃任务保留的输入文件;终态释放,历史任务仍保留输入 ID / 名称 / 摘要 | 历史输入 / 结果 ID 没有直接文件外键,避免文件到期删除顺带丢失任务记录。活跃引用有文件外键,账号删除仍遵守当前个人数据级联规则;任务记录不是财务审计账本。 代码按实际职责分开:`api/lib/tasks/store.ts` 负责用户请求与所有权;`delivery.ts` 负责投递;`lifecycle.ts` 负责领取、失败与失联恢复;`consumer.ts` 负责确定性处理与消息结算;`results.ts` 负责结果预留、发布与补偿。首版只接受个人账号和一个现有 `text/plain` 文件,参数严格校验,固定处理器 `text-normalize` 版本 1;没有任意脚本或 URL 执行器。 ## 幂等与并发 `user_id + request_key` 唯一。请求键为客户端生成的 UUID,同一次提交重试沿用原键;同键与同输入返回已有任务,同键换输入 / 参数报冲突。终态和原文件删除后,同一请求仍返回原任务,不重新执行。 每账号默认最多五个活跃任务,由 `tasks.maxActive` 控制。创建与手动重试锁定账号行,再核对活跃计数;次数和文件输入不能通过并发请求绕过。单账号事务串行的实际吞吐 / 云端负载还未测试,后续依据负载证据调整,不先添加另一套限流存储。 创建任务和保留输入在同一数据库事务内。提交与删除都先锁定同一文件行,再检查最新状态或引用。删除先赢时文件转为 `deleting`,提交拒绝;提交先赢时引用提交,删除返回 `FILE_IN_USE`。过期清理跳过引用中的文件,终态释放后再正常清理。保留输入不延长用户下载权限;过期文件仍不能由用户直接下载。 ## 投递与领取 队列只发送 `{ taskId, dispatchVersion }`,不携带文件内容或报价。任务先持久化为 `pending`,投递采用 30 秒的独立租约;发送失败保留记录,30 秒后可补投。成功投递后记录 `queued` 与时间;两分钟仍未领取的队列记录可以再次投递。维护单批最多检查 20 条。 迟到发送端只更新仍持有自己投递 token 的记录。消费者领取会清除该 token;取消也会清除未执行任务的 token。因此消息已开始执行甚至到达终态后,旧发送端不能把它覆盖成 `queued`。发送失败与业务执行失败分别记录,失败投递不消耗执行次数。 Cloudflare Queues 默认为至少投递一次,可能重复,结果去重由任务服务承担。[官方投递保证](https://developers.cloudflare.com/queues/reference/delivery-guarantees/)(核对:2026-10-10)。`tasks:validate` 已通过实际本地 Queue 投递给真正业务 Worker;`tasks:validate-store` 的受控发送夹具仍仅用于数据库竞态。两者都不证明云端运行。 领取原子更新状态、执行次数与递增版本,并在同一事务插入尝试记录。两分钟执行租约到期后旧版本不得写回,重复消息不能再领取 `running` 或终态任务。数据库约束要求只有 `running` 有执行租约,只有 `succeeded` 有结果引用。 ## 重试与取消 单任务总执行预算默认三次,由 `tasks.maxAttempts` 在创建时保存;改配置不增加已有任务的预算。自动和手动尝试共用,不因刷新或手动重试清零。只有已分类的 `STORAGE_UNAVAILABLE` 自动重试,按尝试次数延迟 30 / 60 秒;输入缺失、处理器不可用、存储配额不足或结果过大先失败。手动重试仅允许明确的存储、租约或处理器故障,包含腾出空间后的 `STORAGE_FULL`,重新核对输入有效期 / 摘要、活跃名额和剩余次数。 `pending`、`queued`、`retry_wait` 的取消立即终止并释放输入;`running` 的取消先记录请求,保留输入直到执行器结算或租约恢复。终态重复取消返回原状态,不重新释放。收费任务的取消与返还在同一事务结算;可重试失败仍保留预扣,本人可放弃并返还。 失联恢复只自动重试已知的确定性文本处理器。未知处理器进入 `reconciling`,保留引用并停止自动投递,取消也仅记录请求。这个状态边界不等于已实现供应商查询 / 对账;必须等真实外部服务接入后按其能力交付。 ## 执行与结果发布 文本处理器读取活跃引用中的输入,核对所有者、文件状态、大小、元数据摘要和实际字节的 SHA-256,严格解码 UTF-8。它把 CRLF / CR 换行改为 LF,按参数移除行末空格 / 制表符,不添加额外换行。已经受理并保留的输入到期后仍可完成任务,但用户下载权限不延长。 输出复用文件模块的配额预留、私有对象路径、到期策略和下载权限。先在当前租约的事务中预留 `uploading` 文件,关联该次尝试;该结果不出现在文件列表,也不可下载。对象写入成功后,在同一数据库事务标记文件 `ready` 与任务 `succeeded`,结束尝试并释放输入引用。空白文本可转换成零字节结果并正常下载;上传入口仍拒绝空文件。 发布检查当前执行版本、两分钟租约、该次结果引用、所有者、文件状态与有效期。发布时发现取消请求则结算 `canceled` 并丢弃输出;旧执行器不能覆盖新结果。对象写入失败只补偿自己的未发布文件,不删除 `ready` 结果:数据库提交成功但响应丢失时,消息会重试,已提交的结果必须保留。 读取输入对象、读取对象字节和写入结果对象分别有 15 秒等待上限。R2 API 没有这里可用的中止接口,超时不保证底层写入立即停止;迟到写入不能通过过期版本发布,若落成无记录对象,沿用文件模块带一小时宽限的孤立对象清理。删除失败保留文件记录和配额,待定时维护重试。此处没有承诺立即清除迟到对象,也没有新增第二套配额。 定时维护依次恢复过期执行、返还输入失效的失败任务预扣、清理非运行尝试的未发布结果、清理文件、补投到期任务;每批有上限。本地不会自动触发 Cron,验收显式调用真正 Worker 的 scheduled 入口。已分类业务失败按数据库状态重试;未知基础设施错误要求消息延迟十秒重投,原始供应商响应不写入任务记录 / 日志。 ### 死信与恢复 业务队列最多追加三次基础设施重投,耗尽后由独立死信消费者处理。`TASK_DEAD_QUEUE_NAME` 必须与 Wrangler 的死信消费者名字一致,本地运行时从配置派生;生成新站时,目标队列、死信消费者与该变量一起重新命名。官方要求为死信配置独立消费者,见[死信队列文档](https://developers.cloudflare.com/queues/configuration/dead-letter-queues/)(核对:2026-10-10)。 迁移 `0006_task_dead_letters.sql` 增加投递版本与 `task_dead_letter` 记录。每次投递、手动恢复或结束旧租约都会推进版本;消费者只领取与数据库当前版本一致的消息。旧消息不能领取新一轮执行,迟到的死信也不能终止新投递、覆盖成功结果或撤销取消。 死信事务先锁定任务,再以 `queue_name + message_id` 唯一保存收据与状态转换,提交成功才确认消息。数据库暂时不可用或提交响应丢失会重投;唯一约束避免重复收据 / 重复转换。记录只含队列 / 消息标识、已核实任务引用、投递版本、时间和分类,不保存原始消息体、输入、验证码或供应商错误。分类为 `applied`(已停止等待任务)、`deferred`(保留运行租约)、`stale`(旧版本 / 终态)、`missing`、`invalid`;无对应任务的消息不保存其原始任务编号。 当前等待任务进入 `failed / DELIVERY_EXHAUSTED`,释放输入保留,停止自动补投。运行任务仍保留租约,可发布真实完成的结果;后续已知失败或租约过期才停止自动重试,未知处理器仍进入 `reconciling`。详情只向任务本人返回历史故障时间,恢复后保留该记录,不向浏览器返回队列名、消息编号或内部版本。管理员集中诊断界面归 I9,当前维护者可按任务编号核对 `task_dead_letter` 与 `task_attempt`。 恢复使用既有 `tasks.retry` 和详情重试按钮,继续要求个人所有权、本站来源、频率限制及已启用的验证码;检查处理器版本、当前名额、原输入状态 / 摘要 / 到期及剩余预算。恢复不清零 `attemptCount`,也不清空死信历史。未领取的基础设施投递失败不计处理次数;已经耗尽处理次数的任务拒绝继续执行,需要本人明确提交新任务,不由后台偷偷重置预算。 死信消费者自身设置十次重投。数据库持续不可用时,有限重投不能承诺无限保存消息;上线前需接入队列失败告警与运维恢复,归 I15。生产资源、部署、保留期限和真实容量仍待环境验收。 ## 本地验收 ### 在主预览中操作 1. 登录后打开 `/files`,保存一个 UTF-8 文本文件。 2. 打开 `/tasks`,选择已保存、未到期的文本文件;选择是否去掉行末空白,点击开始任务。 3. 提交成功自动进入 `/tasks/<任务 ID>`。等待、执行、重试等待和终态来自服务器;首版没有伪造进度百分比。 4. 仅成功后展示结果文件,可预览和下载。原文件保持不变,结果使用个人文件配额与到期策略。 5. 等待 / 运行中的任务可请求取消;运行中需等执行器结算。符合条件且仍有预算时才显示手动重试,次数不会清零。 6. 刷新、离开后返回仍能看到任务。断线会提示重连并禁止按旧状态操作;恢复联网或回到页面重新核对,活跃列表每五秒、详情每三秒检查;查询出错时暂停自动轮询。 提交响应丢失时,页面保留原请求键与输入,再次提交沿用该键。也可刷新任务列表确认是否已保存。更换文件或处理参数才生成新请求键;不要在未知结果时直接改参数或建立新请求来代替检查。 ### 接口与配置 `tasks.submit/list/get/cancel/retry` 均要求有效个人账号,用新鲜数据库核对所有权,组织切换不会扩大任务范围。变更请求的 `Origin` 必须匹配 `APP_ORIGIN`。列表使用 `createdAt + id` 游标,每页二十条,重复时间不会漏页;详情和列表只返回公开状态,不返回租约、输入摘要、队列 token 或请求指纹。越权查询 / 修改返回不存在。 `packages/core/website.ts` 的 `tasks` 控制 `enabled`、`maxActive`、`maxAttempts`、`writesPerMinute`、`readsPerMinute`。关闭新执行仍保留查询与取消。新提交 / 重试还必须存在 `STORAGE` 与 `TASK_QUEUE`,缺失时明确报错,不创建任务;开启任务同时需开启文件存储。个人写入 / 读取使用各自原子计数,批量请求逐项计算,不能通过批量打包绕过限制。 全站 Turnstile 开启时,提交动作固定为 `task_submit`,手动重试为 `task_retry`;取消不消费验证码。token 作为独立请求字段传到服务端验证,不进入请求摘要或数据库。页面显示的只有公开 site key / action,复用文件上传的 SDK 生命周期。真实 widget 仍待环境验收,默认关闭。 tRPC 错误含稳定的 `taskError`、`fileError` 或 `executionError`;限流带 `retryAfterSeconds: 60`。`REQUEST_CONFLICT` 检查请求键是否用于不同输入;`ACTIVE_LIMIT` 先结束或取消旧任务;`NOT_RETRYABLE` 检查状态与次数;`DISABLED` / `NOT_CONFIGURED` 核对策略与绑定。服务器异常只返回通用提示,不返回内部诊断消息 / 调试堆栈;任务入口也不记录原始错误体。实际接口异常不会自动再提交或清零执行预算。 ### 自动验收命令 ```sh bun run test -- --run apps/api/lib/tasks/tasks.test.ts apps/api/lib/tasks/results.test.ts bun tasks:validate-store bun tasks:validate bun files:validate ``` Vitest 使用实际迁移的 PGlite 和本地 R2,覆盖幂等冲突、个人隔离、五个活跃名额、输入保留、投递失败 / 迟到确认、并发领取、旧版本写回、有限重试、取消与未知处理器暂停。 `tasks:validate-store` 只允许 development 与 loopback PostgreSQL。它创建临时数据库并应用迁移,通过八个不同 PostgreSQL 会话测试真实锁等待、20 轮输入 / 删除竞争、幂等、次数与名额限制;实际 R2 保存测试输入。结束销毁临时数据库 / R2,正常配置指向的数据库不迁移。输出明确标记:**没有测试业务队列消费者**。 `results.test.ts` 使用实际迁移的 PGlite 与 R2,验证并发 / 重复执行只发布一次、精确字节与摘要、零字节结果、未发布隔离、过期版本拒绝、运行中取消、失败补偿 / 配额、输入实际摘要、到期保留、空间不足后的有限重试,以及提交成功但响应丢失时不误删结果。PGlite 不能代替真实多连接锁竞争,后者仍由 `tasks:validate-store` 验证。 `tasks:validate` 使用当前生产 Worker bundle、临时 PostgreSQL 和实际本地 Queue / R2,验证登录后的真实 HTTP 提交 / 幂等 / 列表 / 详情 / 取消 / 重试及越权 404,消费后私有下载核对原字节 / 摘要 / 响应头。重复消息和已取消消息不重复执行;失联 / 故障状态由内部受控夹具建立,再由实际维护 / 重试入口恢复。死信验收只在临时数据库安装拒绝领取的故障触发器,实际配置经历首次投递及三次十秒重投,保存一次收据并停止自动补投;移除故障后经真实 HTTP 重试恢复和下载。另一个临时数据库故障让实际消费者领取后失联,显式推进租约期限,实际维护 / 队列三轮后耗尽保存预算并拒绝手动重试。生产代码没有验收故障开关,也没有缩短实际队列重投配置。停止该验收 Worker、复用其持久目录重启后,原结果仍可下载且新任务继续执行。结束销毁临时数据库与资源目录,不代表浏览器或真实验证码验收。 `files:validate` 继续验证真正 Worker bundle 的文件流程和定时清理,使用另一个临时数据库。内部服务先保留测试文件,实际 HTTP 删除返回 409,内部取消后删除成功。额外保护检查消费真实写入次数,脚本核对 429 / 重试提示后,仅推进临时用户的计数窗口继续检查,不改变配置上限。 `routers/tasks.test.ts` 通过实际迁移 PGlite / R2 验证个人隔离、禁用 / 缺绑定、来源拒绝、幂等、发送失败保留、相同时间分页、并发限流和验证码动作;真正 tRPC HTTP 批量读取验证每项计数与错误格式。组件测试验证丢失响应沿用请求键、参数变更换键、断线禁止旧状态操作、重连显示结果和运行中取消等待结算。主预览浏览器已完成实际提交、状态轮询、结果预览 / 下载 / 摘要、直接详情刷新;320 / 390 像素任务 / 文件布局已检查实际宽度,无横向溢出,文件长文本对话框在 390 像素保留 64 KiB 提示。 两份新生成站分别在 8410 / 9410 独立安装、应用迁移并构建,使用不同数据库、认证命名空间和 R2 / Queue 名称。两站实际网关接口与浏览器都完成任务、预览 / 下载和直接详情刷新;混合 Cookie 选择本站会话,外站任务 / 取消 / 结果拒绝,详见[迭代记录](./iteration-progress)。生成时源仓库含本轮未提交改动,`template.json` 如实记录该情况,不将其声称为已发布版本。真实执行验证码、云端负载与第三方处理器仍保留后续验收门槛。 --- --- url: /docs/agentbuff-stack/image-to-svg-contract.md --- # 图片转矢量:候选接口与接入核对 核对日期:2026-10-11,北京时间;源码基线 `2642fd42`。本页是 I7 的接入准备,**未实现或启用图片转 SVG,也未调用收费接口**。首个真实任务仍待用户选择;现有文本规范化演示继续使用原处理器。接口可用与有报价不证明搜索需求、付费用户或订阅成立。 ## 为什么保留这个候选 如果第一站沿用此前讨论的 Image to SVG,其交付应为上传图片 → 矢量化 → 对照预览 → 下载可编辑 SVG。Recraft 有直接转换接口,适合继续验证单一任务;能否保留细节、文字与可编辑路径仍需真实样本。文字提示生成一幅新的 SVG 使用另一接口,不能当成保留原图的转换质量证据。[官方转换说明](https://www.recraft.ai/docs/api-reference/tools/vectorize) ## 已核对的外部契约 | 项目 | 当前公开资料 | 对接选择 / 尚缺证据 | | --- | --- | --- | | 请求 | `POST https://external.api.recraft.ai/v1/images/vectorize`,服务端 Bearer 认证 | 使用 Worker 原生 HTTP,不为一个操作新增模型路由器或客户端 SDK。[接口说明](https://www.recraft.ai/docs/api-reference/tools/vectorize) | | 输入 | PNG / JPEG / WebP,低于 20 MB;短边至少 256、长边不超过 4096 像素,最多 16 MP | 模板先保持已有 PNG / JPEG 与 5 MiB 限制;新增像素尺寸检查,不宣称已有 WebP 支持。[限制说明](https://www.recraft.ai/docs/api-reference/appendix) | | 传送方式 | JSON 可传图片 URL 或数据 URL,也支持二进制表单 | 首轮采用 JSON 数据 URL,从私有 R2 读取原始字节后提交;不公开私有输入对象。[输入说明](https://www.recraft.ai/docs/api-reference/image-inputs-and-results) | | 响应格式 | 可以返回链接、Base64 或多段字节 | 首轮选择 Base64,限制响应与解码大小后存入原私有结果链路;不把外部链接交给用户作为永久结果。[结果说明](https://www.recraft.ai/docs/api-reference/image-inputs-and-results) | | 成功对象 | 单个 `image`,含 `image_id`;顶层有 `created` 与 `credits` | 与生成图片的数组响应不同;图片编号不是可查询的请求编号,不混用。[转换响应](https://www.recraft.ai/docs/api-reference/tools/vectorize) | | 结果链接 | 公开、无需登录,约 24 小时;链接丢失不能恢复 | 不用链接模式替代本站所有权校验及存储保留策略。[保存限制](https://www.recraft.ai/docs/api-reference/appendix) | | 限额 | 每个供应商用户每分钟 100 图片、每秒 5 请求;所有令牌共享 | 本站用户限流不能证明供应商账号总量受限,真实环境需验证总预算 / 并发。[限额说明](https://www.recraft.ai/docs/api-reference/appendix) | 以上是官方公开契约,不是实测结果。输出为 SVG 的声明不能证明内容全是矢量路径,也不能证明复杂照片、文字、渐变或切割用途的质量。 ## 规格中发现的三个接入边界 直接下载并解析官方 JSON OpenAPI:61,391 字节,SHA-256 为 `06ca4897b0eb27435f370c2242813a748e52ff28f843a3859a46d44f1e23fba4`。确认转换操作、响应引用和结果格式后,按完整路径与字符串检索:本次规格没有记录幂等参数,也没有公开转换任务的查询 / 取消 / 结果找回操作。这个结论只覆盖本次公开规格,不能推断供应商内部没有相关能力。[官方规格](https://www.recraft.ai/docs/api-reference/openapi.json) 第一,文档表单示例使用 `file`,本次 OpenAPI 的二进制请求却继承名为 `image` 的字段;两者不一致。JSON 的图片字段在两份资料一致,因此首轮选择数据 URL。不要照搬表单示例并假称已确认运行。[表单说明](https://www.recraft.ai/docs/api-reference/tools/vectorize)、[官方规格](https://www.recraft.ai/docs/api-reference/openapi.json) 第二,转换操作没有可指定的模型版本字段,响应也没有模型版本。规格顶部的 `0.0.1` 是 API 文档版本;本站处理器版本也只能表示自己的代码契约。供应商算法版本保持未知,不能填成 V4.1,也不能宣称锁定了不可变模型。是否能满足本站对固定版本的要求,需供应商确认或另选具备版本证据的服务。[官方规格](https://www.recraft.ai/docs/api-reference/openapi.json) 第三,官方错误说明建议对网络超时与服务错误退避重试,但未承诺重试免费或收费去重。本项目要求不确定调用进入 `reconciling`:付费请求已可能到达供应商而响应丢失时,保持待核对,不直接补发。收到成功结果而本站落库失败,也必须与重新调用供应商分开恢复;这属于本项目交付规则。[官方错误说明](https://www.recraft.ai/docs/api-reference/error-handling) ## 成本怎样记录 当前公开价格为每次转换 10 个 API 单位,即 0.01 美元;单位包换算为 1 美元 / 1000 单位。按此报价,100 次为 1 美元,1000 次为 10 美元;这是报价推算,仅包含转换,不是实际账单,也不包含存储、失败、不确定重复、支付费用或其他处理。[官方价格](https://www.recraft.ai/docs/api-reference/pricing) 真实接入时分别保存供应商实际返回的单位数、报价版本 / 日期、估算金额与货币,以及本站用户的积分报价和扣返结果。只有报价而没有请求响应时,实际用量与已核实费用留空;供应商已收费但结果不合格导致用户积分返还,也不能把供应商成本清零。若要对照余额,需隔离其他同账号请求;总余额差不能直接归因给某次任务。 ## 现有代码需要怎样扩展 以下按当前源码核对,不是已完成改造。继续复用同一任务、账本、私有存储和维护入口。 | 入口 | 当前限制 | 接入时需要的具体改动 | | --- | --- | --- | | `tasks/store.ts` 与 `db/schema/tasks.ts` | 提交只接受文本处理器;参数类型与输入 pin 都按文本设置 | 增加一个明确的图片处理器与严格参数分支;按处理器校验输入,原文本请求摘要与重放保持 | | `tasks/consumer.ts` | 只执行文本规范化 | 先核对输入哈希、尺寸和预算,再记录执行边界;一次外部请求之后的存储故障不重新请求供应商 | | `tasks/lifecycle.ts` 与 `tasks/rules.ts` | 未知处理器过期进入待核对;仅文本可自动重试 | 为已收到结果 / 已知拒绝 / 不确定发送分别定义恢复,不把存储重试复用为付费调用重试;取消不能伪称供应商已停止 | | 任务执行记录 | 没有供应商编号、单位数、版本或费用字段 | 在新的增量迁移中记录实际已知事实、空值和调用状态,原 SQL 历史保持;不会给旧文本任务补造零成本 | | `tasks/results.ts` 与文件校验 | 结果固定命名为文本;上传允许四种已有类型 | 增加服务端 SVG 结果契约、结构 / 大小 / 内容检查和文件类型;不因此默认允许用户上传任意 SVG | | 文件预览与下载 | 图片用 Blob 图片预览,私有读取有 CSP;文本任务页面文案固定 | 增加原图 / 结果对照及可理解的限制;SVG 不作为 HTML 插入;保留跨账号隔离、下载和到期清理 | SVG 验收需要解析真实结构并检查视口、路径与资源引用;拒绝脚本、外部依赖、嵌入位图冒充矢量以及超限输出。受限图片预览和下载还需实际浏览器复验,不能仅依靠字符串含有 SVG 标签。颜色 / 路径复杂度与文字保真属于质量样本,结构合格不自动等于产品可用。 ## 下一次真实验收 任务选定且测试调用范围明确后,使用自有或有许可的固定输入:简单图标、透明图案、带文字标识、渐变插画、复杂照片,以及尺寸 / 类型边界。每份记录输入和输出摘要、像素尺寸、路径 / 嵌入对象、耗时、供应商编号 / 单位、人工质量判定及费用来源;不先填写成功率或“一天能做完”。 本地受控流程需覆盖关闭 / 缺配置、明确拒绝、限流、坏输出、发送后超时、成功结果持久化中断、重复队列、取消和积分返还。真实小批次再确认格式契约、转换质量、实际费用与 Worker 限制;两类证据分别保存。正常主库保持已授权的 0012,新增模式只在对应独立环境验收。 本轮仅获取公开文档与规格,本机缓存位于忽略的 `.local/recraft-contract-2026-10-11/`。Cloudflare 登录复查仍为未登录;没有新增未使用的模型密钥字段、执行迁移、发送图片、调用模型或部署。I7、I15 云端及 I16 两个真实产品继续未完成。 --- --- url: /docs/agentbuff-stack/task-notifications.md --- # 任务邮件通知 I12 的第一阶段提供可选的任务完成 / 失败邮件、投递记录和有限重试。站内任务状态继续沿用原任务工作台。第二阶段站主运营频道和已核实付款通知见[独立教程](./operator-notifications);真实外部投递与云端维护仍待验,不能将本阶段视为整个 I12 完成。 ## 配置与升级 公开配置位于 `packages/core/website.ts`: ```ts notifications: { taskEmail: false, preferenceVersion: "2026-10-11", retentionDays: 30, operator: { enabled: false, since: null }, } ``` 默认关闭。启用前完整升级迁移至 `0022_task_email_language.sql`,同步构建 API、工作台与公共页,再确认已有邮件配置与定时入口。0020 新增任务邮件选择和投递表,0022 增加语言字段,没有修改已应用的旧迁移;正常预览主库仍停在 0012,两张通知表尚未应用。关闭时界面不显示卡片,偏好 / 投递列表和定时入口不读取通知表,旧库仍能运行。 使用现有 `RESEND_API_KEY`、`RESEND_EMAIL_FROM`、`APP_NAME`、`APP_ORIGIN` 与 `BETTER_AUTH_SECRET`,不需要新的供应商密钥。`preferenceVersion` 为 1—80 字符,保留期为 1—90 天。改变通知用途时升级版本,让用户重新明确选择。新站继承该配置文件,但选择和投递记录属于各自数据库;仍需检查启用状态、独立数据库、品牌、发信地址和认证密钥。 ## 用户选择与权限 启用后,设置页出现任务邮件卡片。默认不勾选,当前账号必须有已验证邮箱;勾选并保存成功后才允许新通知。取消勾选也需要保存,失败会显示未确认,并提供重新读取按钮。每次修改带当前 revision,旧标签页不能覆盖较新的选择;重复保存相同有效选择不延长起点。 服务端从新鲜数据库状态检查账号、封禁和邮箱,接收人只取当前账号,客户端不能指定邮箱、所有者或外发 URL。变更要求本站 Origin,并有限频;选择与列表属于个人账号,团队切换不会把任务邮件发送给团队其他成员。这里的选择不授权营销订阅或活动统计。 选择只保存最新状态、邮件语言、版本、起点和 revision,不作为完整同意历史。接收地址以认证密钥派生的 HMAC 摘要绑定,不在通知表复制邮箱。邮箱或认证密钥变化后,需要重新保存当前邮箱的选择;旧通知不会改投新邮箱。撤回后尚未领取的旧通知会跳过,再次开启不补发先前完成的任务。已经进入发送阶段的邮件可能仍会到达,撤回无法收回已外发请求。 ## 邮件语言与升级兼容 设置页提供独立的“邮件语言”选择。默认英文,只能开启本站 `i18n.locales` 已启用的语言;选择后仍需保存。页面语言、活动团队或请求头不会自动改写邮件偏好。已保存西班牙语的账号可以在英文页面查看并保留西班牙语偏好;未提交的选择在页面语言切换或刷新时不保留。 新投递将当前偏好的语言保存到自己的记录,成功 / 失败的主题、HTML / 纯文本、任务与设置链接按该语言生成。任务 ID、所有权、结果字节和文件保留期不变,西班牙语邮件使用 `/es/tasks/:id` 与 `/es/settings`。内容摘要包含新的模板版本及记录语言,重试、租约恢复和 Worker 重启仍使用同一载荷与请求键。 显式保存不同语言会增加 revision 并重新设置选择起点,尚未领取的旧邮件会跳过,不为同一终态重复建立通知。已经领取的请求可能仍按原语言发出;更改语言不能收回正在发送的邮件。站点关闭西班牙语后,其已保存偏好仍如实显示,用户可以改成英文或撤回;排队的西班牙语请求停止,不以同一幂等键改发英文。之后重新启用语言不会补发已跳过的记录。 0022 给旧偏好填入英文,新建投递会保存明确语言;旧投递的语言字段保留空值,继续使用原英文模板和 `task-mail-v2` 内容摘要,不改写已尝试的请求。新投递使用 `task-mail-v3`。两种渲染路径暂时共存,旧 HTML / 纯文本逐字节兼容测试必须保留,不能仅把空字段补成英文后用新版模板发送。此兼容规则只用于旧任务邮件;营销邮件的语言迁移仍在后续阶段。 ## 已提交结果与去重 复用现有每 15 分钟定时维护入口,从已提交的任务终态派生投递记录。没有增加第二条任务队列、公开发送探针或新的通知执行引擎。任务消费者、租约恢复、死信或管理员恢复产生的真实终态都可以被补偿扫描发现;任务成功不等待邮件。 每轮最多扫描 20 个尚无记录的终态,稳定事件为任务 ID、执行租约版本与成功 / 失败结果。数据库唯一约束处理重放和并发。仅纳入当前明确选择起点之后、保留期内完成的任务;取消、中间重试和需人工核对的任务不发送失败邮件。失效接收人保存跳过记录,避免连续占据扫描预算。扫描有积压或维护延迟时,通知可能晚于 15 分钟。 手动重试会使旧未发失败通知失效;新一轮真实终态形成新事件。发送前重新检查任务结果、租约版本、选择、邮箱和配置。通知只有任务 ID、成功 / 失败说明、本站任务页及设置页链接;没有原始输入、文件名、结果附件、签名下载地址或供应商密钥。结果只在原工作台通过登录与所有权校验下载,邮件不延长文件保留期。 ## 投递状态与重试 | 状态 | 含义与处理 | | --- | --- | | 等待发送 | 已保存通知,尚未领取 | | 发送中 | 一次两分钟租约正在处理,其他消费者不能同时领取 | | 等待重试 | 供应商接收未确认,15 分钟后可再次领取 | | 供应商已接收 | 已得到有效邮件请求回执;不等于送达收件箱或用户阅读 | | 接收未确认,停止重试 | 耗尽三次尝试或超过第一次尝试的 23 小时范围;保留排查状态,不自动换键重发 | | 已跳过 | 选择、账号、任务或发信配置变化;不改投新地址 | 邮件复用 `sendEmail` 的 SDK 边界,同时生成 HTML 与纯文本。通知请求超时为十秒,邮件 ID 必须有效。相同投递记录使用固定供应商幂等键,保持内容与接收人一致。接收人、品牌、Origin、发信地址、供应商密钥或环境变化会停止旧请求,避免在新地址或新供应商账号重新发送。 [Resend 官方幂等说明](https://resend.com/docs/dashboard/emails/idempotency-keys)在 2026-10-11 核对:键保留 24 小时。本模块最多尝试三次,第一次尝试后仅在 23 小时内重试,包括超时租约恢复;不会跨窗口重新使用旧键。未知返回、供应商错误或回执保存失败都不能证明邮件没有发出,不承诺绝对无重复。接收成功后数据库写入还需匹配本次未过期租约,迟到响应不能覆盖另一消费者的记录。 设置页显示本人最近 20 条、保留期内的投递状态、实际记录语言、次数与下一次可重试时间,可主动刷新。失败只保存固定原因,不记录供应商原始错误、邮箱、正文或秘密。当前不提供普通用户强制重发或管理员重发命令;未确认记录需要结合供应商回执排查后再决定处理,避免随意再发。 ## 保留与本地验收 旧记录按事件时间进入清理资格,每轮最多删除 100 条;积压、关闭模块或维护故障可能延迟物理删除。列表不显示超过保留期的记录,扫描也不会把清理掉的旧任务重新入队。最新选择保留至账号删除,删除账号级联删除选择与投递;删除任务也会删除其投递记录,不影响财务审计自身的保留规则。 本地 Worker 的 `LOCAL_MAIL` 捕获全部邮件,即使环境里有真实 Resend 密钥也不会外发。捕获回执只证明本地接口完成,不能证明真实送达;开发捕获文件可能包含认证邮件和合成接收地址,不应提交仓库或公开分享。 ```bash bun --cwd apps/api validate:notifications ``` 该验收只建立独立 loopback 临时数据库,应用完整迁移,运行实际 Worker、Queue、R2 和定时入口,执行成功 / 失败任务、重放、捕获故障及恢复、撤回与再次开启,并销毁全部临时资源。`--languages` 额外开启英 / 西双语,核对真实西班牙语邮件、故障后的 Worker 重启与原请求恢复;`--ui` 额外构建专用工作台供桌面 / 手机验收,不改变正常预览配置或数据库。组合 `--languages --ui` 时按提示依次保存英文、保存西班牙语、撤回,再创建 `.local/notifications-ui-stop` 结束验收;只检查页面但不完成这些写入,会明确失败。 真实发信域名、供应商故障、邮箱投递与云端定时维护仍需独立验收。站主渠道及支付事件通知已有独立配置与投递边界,接收者、内容与授权规则不复用个人任务邮件选择,见[站主运营通知](./operator-notifications)。 --- --- url: /docs/agentbuff-stack/operator-notifications.md --- # 站主运营通知 站主可以选择将已提交的任务成功 / 失败、积分包付款与订阅账单付款发送到一个固定 Discord 频道。该模块默认关闭,与个人任务邮件、营销订阅、活动统计分别配置。管理员可以在后台查看投递记录;普通用户不能配置目的地或发送消息。 本地实际任务、受控付款与捕获接口用于验证实现。捕获回执不能证明真实频道收到了消息;真实供应商和云端定时维护仍需独立验收。 ## 配置与迁移 公开开关位于 `packages/core/website.ts`: ```ts notifications: { taskEmail: false, preferenceVersion: "2026-10-11", retentionDays: 30, operator: { enabled: false, since: null, }, } ``` 启用时设置 `operator.enabled=true`,并将 `since` 改为明确的 UTC 时间,例如 `2026-10-11T00:00:00Z`。它决定首次纳入的业务事件起点,不使用部署时间猜测,不自动补发之前的历史事件。时间与保留期共同限制扫描范围。 服务端秘密变量为 `DISCORD_NOTIFICATION_WEBHOOK_URL`,仅接受 `https://discord.com/api/webhooks/频道通知编号/令牌` 这一固定格式的标准地址。完整地址内的令牌是秘密,不写入公开配置、浏览器、通知表或日志。没有任意用户地址、私有主机、额外端口、查询参数或自动重定向路径。启用缺少地址 / 起点或地址不合格时,配置检查明确失败。 先在目标环境完整升级到 `0021_operator_notifications.sql`,再重建 API、工作台和公共页。0021 新增最小投递记录及独立订阅检查时间表,保留所有旧迁移。正常预览主库仍为 0012,尚未应用 0020 / 0021,通知开关保持关闭;此时页面和维护不会读取这两张站主通知表。 云端需将秘密变量注入 API Worker,并配置现有每 15 分钟维护入口。没有新增通知队列,也没有公开测试发送接口。支付通知依赖已正确配置、验收的原 Stripe 支付入口,设置通知地址不能开通收款。 ## 事件来源与付款凭证 | 事件 | 纳入依据 | 不纳入的情况 | | --- | --- | --- | | 任务完成 / 失败 | 已保存终态、完成时间及当前执行租约版本 | 取消、中间重试、未完成、起点之前和过期任务 | | 积分包付款 | 原购买已付且发放已提交,金额 / 币种 / 模式与对应积分流水相符 | 待付、仅回跳成功、缺少对应实际发放流水 | | 订阅账单付款 | 原始通知验签后重新核对当前账单、客户 / 本站订阅、完整付款分配和实际成功收款 | 零元、待付、余额抵扣、线下付款记录、客户或模式不符、不完整分页 | 个人和团队的已核实订阅账单都可形成站主通知;团队付款不会记到某个成员的个人活动统计。付款校验与个人统计共用,但启用、接收者和授权分别检查。运营通知不要求个人开启任务邮件、营销或活动统计,也不会替用户保存这些选择。 记录的是原始付款确认,不是扣除退款后的净收入、利润、订阅留存或阅读证明。通知不改写订阅权限、不额外发积分,不替代财务流水与审计。后续退款属于另一个业务事件,本阶段不提供退款通知。 漏回调恢复使用独立检查时间,每轮最多检查三个有效个人或团队客户,同一客户至少间隔 15 分钟;失败也让出轮换位置。每次最多读取 1,000 张已付账单,每张最多核查 100 个付款分配。按实际付款时间筛选,早期创建但近期付款的账单仍可恢复;重复页、超限和缺页不假报完整恢复。该检查不占用用户账单操作租约,不复用个人活动统计的同意或检查时间。 ## 消息内容与去重 消息只包含固定事件名称、内部任务 / 购买 / 账单编号、付款最小货币单位金额、币种、测试 / 正式模式和本站管理员入口。没有姓名、邮箱、团队名称、原文件名、输入、输出、提示词、附件、签名下载链接、通知令牌或供应商原始错误。明确禁止自动提及频道成员。 任务使用任务编号、当前执行租约版本和结果组成事件键;付款使用购买或账单编号与固定类型。数据库唯一键处理重放与并发,发送前再次核对任务或购买。任务重试替换旧执行时,旧未发通知跳过,新终态可产生新的事件。已核实订阅付款保存最小凭证后独立投递。 目标地址、环境、认证密钥、网站 Origin、起点与消息版本通过 HMAC 摘要绑定。配置变化后旧待发送记录跳过,不把旧消息改投到新频道。管理员入口仍需登录及最新管理员权限,不因收到链接获得访问权。 ## 投递状态与排查 | 状态 | 含义与下一步 | | --- | --- | | 等待发送 | 业务事件已入库,维护尚未领取 | | 发送中 | 一次两分钟租约,其他维护不能同时领取 | | 限频等待 | 明确收到 429;按要求等待且至少 15 分钟,最多三次尝试 | | 渠道接口已接收 | 收到有效消息编号;不代表有人阅读 | | 已拒绝并停止 | 明确拒绝、耗尽尝试、过长限频等待或本地捕获缺失 | | 接收未确认并停止 | 断网、超时、服务错误、缺少有效回执或发送租约过期;不自动重发 | | 已跳过 | 业务来源、保留范围或绑定配置已变化 | 请求超时十秒,不跟随重定向;要求 `wait=true` 取得实际消息回执。[Discord 官方说明](https://docs.discord.com/developers/resources/webhook)于 2026-10-11 核对:执行通知接口可等待返回消息;默认不等待的成功响应不能提供同等回执依据。本模块只接受带有效消息编号的返回。 Discord 没有本模块可用的请求幂等键。响应丢失不能证明消息没发出,因此只有明确限频才自动重试;请求结果不确定或租约过期时停止。不会用新的事件键绕过这项限制。回写还需匹配本次未过期租约,迟到回执不能覆盖已停止的记录。限频要求超过一天时停止,不提前截断等待时间再发送。 管理员后台显示每页 20 条、状态、次数、事件时间、限频的下次时间及有效回执,可刷新、翻页。读取检查最新数据库角色、验证状态和封禁;撤权后原会话不能继续读取,接口限频返回明确响应。列表不暴露目标摘要、秘密地址或供应商错误正文。当前没有强制重发按钮;未确认时先检查实际频道与供应商记录,再决定人工处理。 ## 保留与本地验证 列表隐藏起点之前或超过 `retentionDays` 的事件;每轮最多删除 100 条过期投递。订阅检查时间表也按最后检查时间清理过期行,每轮最多 100 条。默认保留期 30 天,允许 1—90 天;关闭模块、积压或维护故障可能延迟物理删除。清理后的旧业务不会再次进入扫描。 站主记录不存个人账号编号,不随用户 / 任务删除立即级联,而按本模块保留范围清理;多类型的来源编号仅用于排查,不是跨表外键。供应商客户检查标识同样仅用于轮换。财务和管理员审计继续遵守原独立规则。正式隐私说明需准确披露配置频道的提供方、用途和保留范围。 开发运行时总是注入 `LOCAL_OPERATIONS`,消息写入独立本地 `operations-outbox.jsonl`,即使环境提供真实地址也不会外发。开发环境缺少捕获时不回退网络;非开发环境意外带捕获绑定也会停止。捕获文件属于本地排查资料,不能提交或公开分享。 ```bash bun --cwd apps/api validate:operator-notifications ``` 验证器创建自己的 loopback 临时数据库,应用完整迁移,运行实际 Worker、Queue、R2、原始签名付款回调及受控 SDK 请求。覆盖成功 / 失败任务、积分包已提交发放、零元排除、并发账单、分配分页、通知表故障后的续费恢复、捕获故障停止、普通角色禁止读取与原结果下载。结束删除临时资源;`--ui` 额外构建专用管理员页面供桌面 / 手机与浅 / 深主题验收。不会提升正常预览账号或升级主数据库。 本地验证与真实外部验收分别记录:实际频道消息回执、真实限频 / 故障、部署秘密及云端维护仍需验收。个人邮件配置见[任务邮件通知](./task-notifications)。 --- --- url: /docs/agentbuff-stack/tool-usage-events.md --- # 工具使用事件 当前阶段为 I11 的公开工具使用观测,默认关闭。它和 Cloudflare 的公共页访问统计、营销邮件同意、账号注册、任务执行及支付结算各自独立。 ## 统计口径 | 状态 | 从哪里触发 | 能证明什么 | | --- | --- | --- | | 处理开始 | 用户点击处理,浏览器实际调用同步工具处理器 | 浏览器报告一次处理尝试,失败也可计入 | | 处理成功 | 处理器返回可预览结果 | 浏览器报告这次处理成功 | | 下载发起 | 有当前处理结果时点击下载,并实际调用下载链接 | 浏览器报告下载发起;不证明最终保存到磁盘 | API 的响应来源固定为 `browser_reported`,数据库 `tool_observation` 也只存浏览器上报。它不是服务端运行日志、独立用户数、付费转化率或收入证据。公开请求即使具有正确来源和同意字段,也可能由自动程序构造;限流与严格校验不能证明处理真的发生在浏览器。当前没有收入归因、服务端任务统计、支付购买事件或统计管理面板,不能把这一阶段写成完整转化分析已完成。 打开页面、试用示例、编辑输入、复制、保存账号、恢复标签页里的输入都不触发这些事件。处理一次生成一个只存在于内存的随机 UUID;再次点击处理是新的尝试。一个尝试内的重复成功 / 下载上报只计一次,因而下载数是“发生过下载的处理次数”,不是下载按钮点击总数。重新加载页面后恢复的结果没有观测 ID;仅下载它不会补造一次处理记录。 ## 配置与升级 在 `packages/core/website.ts` 中配置: ```ts analytics: { provider: "none", toolEvents: { enabled: false, consentVersion: "2026-10-10", retentionDays: 30, }, serverEvents: { enabled: false, consentVersion: "2026-10-10.2", retentionDays: 30, }, }, ``` `provider` 只控制原有公共页 beacon。`toolEvents.enabled` 独立控制工具内的明确选择与第一方 API,不需要 Cloudflare 统计 token 或新的供应商密钥。`consentVersion` 必须非空且不超过 80 字符;变更用途或同意文案时更新版本,旧版本会重新出现未勾选选择。`retentionDays` 为 1—90 天。 启用前应用完整增量迁移至 `0017_striped_korath.sql`,再一起重建公共页与 API。该迁移只新增观测与短期限流表,不改旧 SQL、账号、任务或账本。现有站升级还需给自己的配置加入上述 `toolEvents` 字段。主预览继续保留 0012 且关闭此模块;独立验收数据库使用完整迁移。API 默认关闭时先返回 404,定时任务也不读取这两个表,因此不会要求主预览提前迁移。 ## 明确同意与隐私 公开工具底部的选择默认不勾选。勾选后只记录之后发生的处理,不回填先前行为;拒绝、撤回、浏览器存储不可用及上报失败均不影响处理、预览或下载。英文是产品界面语言,本页为中文操作教程。 浏览器仅保存按站点 ID 和同意版本区分的 `yes` / `no` 选择,不保存运行 ID、输入、结果或用户追踪标识。存储不可用时选择仅在当前页面有效。用户可以取消勾选;另一个标签页撤回会通过存储变化同步到当前页面。撤回会中止当前请求并丢弃排队请求,重新勾选也不会复用撤回前的运行 ID。已经被服务器接收的匿名观测无法按账号或浏览器反查并撤销,按保留策略清理;请求到达服务器后再中止也不能保证撤回该次接收。 请求体只允许运行 UUID、固定工具 ID、同意版本、肯定同意、成功与下载状态。拒绝额外字段,不接收输入、结果、文件名、邮箱、账号 ID、页面 URL、客户端时间、任意事件名或购买金额。请求省略账号 Cookie,并设置不发送来源页面 URL;私有工作台没有新增统计脚本或自动账号营销同意。上报失败只显示简短提示,不显示服务端内部异常。 为限制公开端点,服务器按可信平台 IP 使用现有鉴权密钥计算每日轮换的 HMAC 摘要,最多接收每分钟 20 次合法报告;不存原始 IP,摘要也不与运行记录关联。平台没有 IP 信息时共享一个受限桶。该摘要不是无任何元数据的“完全匿名”,也不能用作用户留存统计。原有基础设施日志仍需由部署者按自己的隐私政策管理。 ## 去重、失败与清理 `POST /api/tool-observations` 校验本站精确 `Origin`、JSON 类型、最多 1 KiB 请求体、已知工具与当前同意版本。一个 UUID 只允许同一工具与同一同意版本;并发请求在 PostgreSQL 事务中串行收敛成一行。成功 / 下载接收时间只首次写入,晚到的“只开始”报告不降级已成功状态。下载报告包含累计成功状态,即使第一次处理报告丢失,随后实际下载仍能补齐这次观测。 浏览器队列最多四个请求,每个请求最多等待四秒;不自动重试,不持久化待发内容。离线、拦截、超时、限流、离开页面或队列满都可能导致漏报。上报完成与处理结果互不等待,不能为了统计阻塞工具。记录时间是服务端接收时间,不是浏览器精确发生时间,也不是处理耗时。 已存在运行超过 24 小时后拒绝更新。默认保留 30 天,定时任务每批最多删除 500 行过期观测和 500 行超过一天未更新的限流桶;实际删除取决于任务执行和积压,不能承诺到点立即删除。关闭模块后同时停止自动清理;站主如需删除旧观测,应在已升级的目标库中明确执行清理或在保留窗口后完成清理再关闭。去重覆盖仍被保留的记录,不保证已删除 UUID 的永久去重;浏览器不会跨页面恢复或自动重放旧 UUID。 可在受授权的数据库管理环境按 UTC 接收日期查看汇总: ```sql SELECT (created_at AT TIME ZONE 'UTC')::date AS received_day, tool_id, count(*) AS reported_attempts, count(succeeded_at) AS reported_successes, count(download_initiated_at) AS reported_download_runs FROM tool_observation GROUP BY received_day, tool_id ORDER BY received_day DESC, tool_id; ``` 此查询没有公开 HTTP 入口。统计清理失败会记录通用错误,且在原有文件 / 任务维护之后运行,不能阻塞任务恢复。实际任务、附件下载与积分包确认已另外接入[工作台服务端事件](./server-activity-events),需账号独立同意;购买确认页读取共用服务端唯一键。订阅支付事件与真实外部验收仍按 I11 后续推进。 ## 本地验收 定向测试覆盖默认关闭且表缺失、同意 / 来源 / 字段校验、并发去重、累计状态、不降级、旧运行、限流与清理;产品交互测试覆盖选择前零请求、失败处理、真实成功与下载、撤回丢弃、上报失败不阻塞及关闭后忽略旧选择。 独立真实 Worker / PostgreSQL 验收入口: ```sh bun --env-file .env --env-file .env.local apps/api/local/validate-marketing.ts --tool-events ``` 该入口复用已有受控本地验收器,创建并迁移自己持有的一次性数据库,真实邮件仍捕获,外部请求禁用,结束时删除数据库与运行资源;不升级主库、不修改默认配置。加 `--ui` 可检查独立英文工具页,完成一次同意后的文本处理与下载,再撤回;验收器会核对数据库接收记录。正常主预览没有同意控件是关闭状态的预期行为。 --- --- url: /docs/agentbuff-stack/support-chat.md --- # 可选客服聊天 I14 首版采用 Crisp,默认关闭。产品公开页的联系入口和工作台的 Support 链接都进入 `/contact`;聊天只在该页主动点击后加载。工具页与私有工作台不嵌入聊天,模板官网仍保持独立。联系页支持英文;开启西班牙语后发布完整的 `/es/contact`,语言切换与工作台跳转使用相应路径。 ## 配置 在 `packages/core/website.ts` 中设置: ```ts metadata: { // 其他品牌字段保持原值 supportEmail: "help@your-domain.com", }, support: { provider: "crisp", enabled: false, websiteId: null, }, ``` 要开启聊天,先在自己的 Crisp 工作区取得公开的 Website ID,将 UUID 填入 `websiteId`,再把 `enabled` 改为 `true`。它是公开站点标识,不是 API 密钥;不要把 REST API 凭据放在产品配置或前端。开启时必须同时填写真实支持邮箱,作为供应商异常时的联系方式。只需要邮箱时保持聊天关闭即可。 执行 `bun config:check` 后重建产品公开页和工作台。离线检查与公开页构建都会拒绝未知供应商、非 UUID 的站点标识、非法邮箱,以及开启但缺少标识或邮件回退的配置。原生成站合入新代码后,需补齐显式的 `support` 对象;没有新数据库迁移或 API 密钥。 没有支持邮箱且聊天关闭时,联系页显示暂不可用,保留 `noindex`,不会进入 sitemap。配置了邮箱后才作为正常联系页索引;开启第二语言时 canonical、hreflang 和 sitemap 对应实际译文,不发布空语言页面。译文来源为 Codex 草稿,人工审校继续列在语言模块的外部验收中。 ## 按需加载与故障 页面初次打开只显示邮箱和聊天入口,不加载 Crisp 的第三方脚本。只有用户点击 Open chat 才使用固定的 `https://client.crisp.chat/l.js`。同一页面的并发点击复用一次加载,已经打开后关闭 / 重开继续复用该实例。 脚本下载完成不等于聊天窗口就绪。模板等待 SDK 会话事件,发起打开操作,再由窗口打开事件确认界面状态;15 秒仍未确认或脚本失败时显示失败并保留邮件入口。失败不会自动重试;用户可以重新加载页面后再次主动选择。超时后的迟到会话事件不会重新打开窗口。提示“已打开”不代表客服在线、消息送达或问题已经解决。 实现依据:[官方异步命令与事件](https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/dollar-crisp/)、[语言配置](https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/language-customization/),核对日期为 2026-10-11。没有为其他供应商预建通用插件框架;发生真实切换需求时再调整。 ## 内容与会话边界 模板不自动设置聊天姓名、邮箱、账号 / 团队 ID、付款信息、任务输入、文件名或结果,也不预填或发送消息。工作台链接和页脚联系链接不携带引用来源;联系页与脚本使用 `no-referrer`,主动加载前移除 URL 查询和片段。联系页不加载既有 Cloudflare 营销统计脚本。 用户主动打开后,Crisp 脚本仍能访问该联系页,并使用其自身网络、Cookie 与访客会话机制。因此界面明确说明连接供应商及可能使用聊天 Cookie,不能称为匿名统计或完全本地处理。当前不采用账号身份校验或跨设备会话连续性;同一浏览器可能恢复此前的访客对话。关闭窗口只关闭界面,不承诺停止供应商网络或删除远程记录;重新加载页面后不会自动再次加载。 站主上线前需在自己的隐私说明中写清供应商、实际 Cookie / 数据范围和联系方式,并核实真实工作区设置。模板不提供通用法律文本,也不把本地契约预览当作实际客服投递。用户手动填写消息的内容由其自行选择,验收程序不会向供应商发送消息。 ## 新站与本地验收 `site:create` 在目标新站中将聊天重置为关闭、Website ID 重置为 `null`,同时清空原支持邮箱。连续复制测试使用已启用的来源配置,第二个站点不会继承原客服工作区或收件人。源站配置保持原样。 ```bash bun support:validate bun support:validate --ui ``` 验证器在忽略目录生成独立的开启 / 关闭、英 / 西双语构建,检查联系页、邮件回退、脚本加载边界与 sitemap。`--ui` 使用 `support.localhost:21316` 和受控的本地 SDK 协议,不连接真实 Crisp;临时页面通过内容安全策略限制为本地资源。第一次请求允许打开,第二次模拟脚本失败,第三次在真实 15 秒期限之后返回会话事件;按程序提示完成浏览器流程,再写入它提示的本地结束标记。程序检查请求次数、打开次数及无引用来源,随后停止并清理自己的文件和服务,不修改正常配置或数据库。 当前本地验收与真实外部验收分别记录在[迭代记录](./iteration-progress)。真实工作区的窗口显示、消息往返、Cookie 设置、浏览器限制及供应商故障恢复仍需在明确的测试环境验收;联盟营销的归因、付款 / 退款和结算保持 I14 后续范围。 --- --- url: /docs/agentbuff-stack/affiliates.md --- # 联盟营销:推荐绑定与后台核查 I14 选用 Rewardful,默认关闭。目前已本地验收签名回调、当前佣金与结算读取、Stripe 收款 / 退款核对,以及公开推荐采集与登录后的个人 / 当前团队设置。推荐选择、权限复核、过期、重复请求和写入响应丢失都有明确状态。浏览器与设置界面使用受控 SDK 协议本地验收;新积分包与个人 / 团队订阅的首次结账冻结、共享客户创建和丢响应恢复已受控本地验收,具体订单 / 佣金联查与管理员只读证据列表已受控本地验收;已知资源人工复核与有界漏回调补查已受控本地验收,真实购买归因与完整外部对账仍待完成。 这是一份供应商核查记录,不是银行流水或自行计算佣金的账本。本站保留 Rewardful 返回的佣金金额,核对相关收款、退款与结算成员;佣金比例、返佣周期和真实付款仍需供应商测试环境验收。当前不会根据这些记录发积分、改变订阅权限或转移资金。 ## 配置与依赖 在 `packages/core/website.ts` 中保留显式配置: ```ts affiliates: { provider: "rewardful", enabled: false, campaignId: null, stripeAccountId: null, tracking: { enabled: false, publicKey: null }, }, ``` 要启用后台核查,填写自己的 Rewardful Campaign UUID 和商户 Stripe Account ID,再设置 `enabled: true`。这些是配置标识,不是密钥。API 使用 Stripe 私钥读取自身账户,必须与 `stripeAccountId` 一致;佣金也必须属于该推广计划、该商户账户和相关 Stripe 客户。 服务端增加两个环境变量: | 变量 | 职责 | | --- | --- | | `REWARDFUL_API_SECRET` | Rewardful REST API secret,作为 Basic 认证的用户名;密码为空。当前接受 16—256 个可打印 ASCII 字符且不含冒号,不输出实际值 | | `REWARDFUL_WEBHOOK_SECRET` | 独立回调签名 secret,16—256 字符;不是浏览器站点标识 | 两项一起设置或一起留空。开启时还必须配置 `sk_test_` 或 `sk_live_` 开头的 `STRIPE_SECRET_KEY`。当前 API 沿用既有 Stripe 四项完整凭据组:私钥、订阅回调 secret、Starter 和 Pro Price ID;不能只填写一个 Stripe 私钥。测试和正式账户、推广计划及密钥分别配置,见[环境变量](./env)。 先检查配置、备份目标数据库,再按[迁移教程](../database/migrations)完整升级至 **0033** 并重建 API。0026 新增 `affiliate_resource`、`affiliate_event` 和 `affiliate_payout_commission`;0027 新增 `affiliate_binding`;0028 新增 `affiliate_checkout` 和 `affiliate_customer_request`,不修改已有账号、积分或支付表。0029 新增独立 `affiliate_order` 证据表;0030 保存订阅编号和结账正文摘要,并将缺少新核查依据的旧已关联记录重新排队,保留其历史订单、金额及核对时间。0031 加固部分空值的证据约束,避免 PostgreSQL CHECK 的未知值放过不完整关联。0032 保存人工核查来源与审计关联;0033 新增 `affiliate_scan` 和补查事件来源,保留旧通知、人工任务和历史操作者。已生成的旧站合入代码时需补齐显式 `affiliates` 配置。正常主预览仍停留在已授权的 0012,主预览缺少新表时仍可运行;关闭后不调用 Rewardful。已发出结账的冻结记录仍允许按原正文恢复,不能因关模块而改写已接受请求。 ```bash bun config:check bun affiliates:validate bun affiliates:validate-bindings bun affiliates:validate-ui ``` 第一条离线检查不证明账户权限、商品、推广计划或供应商真实可用。后三条分别验收回调核查、推荐绑定和受控浏览器流程,只使用独立本地数据库,不会替正常预览应用迁移或连接真实 Rewardful / Stripe。 ## 公开采集与明确选择 浏览器采集另外配置 `tracking.enabled` 和 Rewardful 的公开浏览器 key。它与服务端 REST / 签名 secret 分开;检查拒绝常见私钥前缀,以及与本机服务 secret 相同的值。开启采集要求联盟后台、计划和商户先配置完整。生成新站时后台和采集均关闭,所有标识归空。 SDK 只在 `/referral` 和已启用西语的 `/es/referral` 页面,经访客点击同意按钮后加载。页面不索引、不进入站点地图,不加载统计;页脚入口只传递有效的 `via` 或 `referral` 参数。加载 SDK 前删除其他查询及 fragment;使用固定官方地址、公开 key 与唯一 `ready` 回调,不调用邮件转换或改写来源命令。实际 SDK 可能设置供应商 Cookie,其真实行为仍需供应商环境验收。 仅在 `ready` 返回有效 UUID 且存储成功时保存候选。候选在当前标签页的 sessionStorage 最多保留 24 小时,按计划 / 商户 / 公开 key 隔离;这个时限不代替后台核对的供应商有效期。未确认、空来源、脚本错误、15 秒超时、存储不可用均不会显示已绑定;迟到回调也不能改变失败状态。失败后可继续使用工具。清除候选会重新加载公开页,但不承诺删除供应商 Cookie 或取消后台已保存的选择。 工作台不加载 SDK,也不将邮箱、账号、团队或工具内容传给浏览器供应商。个人 / 团队首次选择各自有未勾选的确认项;不会自动绑定,也不会把管理员个人选择变成团队选择。切换工作区会重置界面确认,旧请求只能更新原范围。清除本地候选后,`awaiting_customer` / `pending` 仍可明确重试后台已保存的选择;重试接口不需要再次传 UUID,重新检查 Origin、当前会话及数据库权限。附有核对时间的 `attached` 是历史元数据确认,不能显示为佣金到账。 ## 登录后的推荐绑定 设置页提供个人和当前团队两个独立区域。受保护的 `affiliates.binding` 查询、`affiliates.attach` 首次选择及 `affiliates.retry` 保存后重试共用后台权限。写入必须来自本站 Origin,带明确的 `accepted: true`、推荐 UUID 和 `scope`。个人范围不能夹带团队 ID;团队范围必须提交与当前会话一致的 `organizationId`,并在新鲜数据库中确认操作者仍为 owner / admin。普通成员、被移除成员和过时工作区会话不能修改团队推荐,团队推荐不会自动归到管理员个人名下。 | 状态 | 含义与下一步 | | --- | --- | | `awaiting_customer` | 推荐已由官方列表核实,但本站账单客户还不存在。保留选择,等待既有账单流程创建客户;此操作不会创建客户 | | `pending` | 已固定客户与推荐,尚未确认元数据结果。服务不可用或响应丢失后,由原账户显式重试同一选择 | | `attached` | 曾确认固定客户的 `metadata.referral` 与选择一致,附核对时间。不是实时供应商状态、计佣成功或银行到账证明 | | `review` | 推荐失效、客户被替换、归属矛盾、旧付款 / 未解决结账、尝试达到上限等需要复核。不自行改成已绑定 | 推荐 UUID 格式合法不代表有效。当前使用官方 `GET /v1/referrals`、`limit=100`、`expand=affiliate`,最多检查三页;没有猜测单条推荐接口。检查计划、推荐者、商户、客户、停用与转化状态,以及未绑定访客的明确有效期。列表已结束仍找不到则拒绝;超出三页范围则显示暂时无法核查,不把未知写成不存在。无有效期的未绑定推荐目前也需复核,不推断为永久有效。 每个账单范围固定一个推荐选择,同一商户 / 计划的推荐和同一商户的客户有数据库唯一约束。重试不能更换推荐、计划、客户或偷偷迁移归属。客户 ID 只读取已授权的用户 / 团队账单记录,不接受浏览器传入;客户同时对应多个本站归属时拒绝。决策操作者 ID 作为历史保留,不用于后续授权;每次操作重新校验权限。查询只返回状态、尝试次数和核对时间,不暴露供应商 UUID、客户 ID 或邮箱。 首次写入前读取自身 Stripe 账户、当前客户及其支付 / 结账历史,核对测试 / 正式模式。已有不同推荐元数据、已付正金额、未解决结账、异步付款处理中、无法确定已付金额、历史超过单次 100 条或数据不完整均拒绝,不补记既有付款的归因。写入只更新固定客户的 `metadata.referral`,保留其他元数据;原 `client_reference_id` 不变。五秒外部写入期间持有本站归属和成员锁,权限或客户修改不能插入该事务;供应商控制台或其他外部系统的并发修改仍需真实集成验收。 每个已固定客户的绑定最多尝试八次,租约 90 秒;每个用户写入最多每分钟五次。幂等键来自已持久化的绑定 ID,重试保持正文。如果外部已接受写入却丢失响应,重新读取该客户的相同元数据即可确认,不重复写入,也不因原访客随后过期而丢弃此前接受的结果。未发生写入的过期推荐仍拒绝。Worker 重启后由原账户显式重试;绑定当前没有后台定时扫描或人工重放按钮。最后一次租约过期后,下一次合法重试转为复核。 ## 首次购买与原请求恢复 开启联盟模块后,只为新积分购买和新订阅尝试创建结账快照。快照保存首次个人 / 团队范围、当时推荐选择、推广计划、商户和测试 / 正式模式;已有客户也在此时固定。没有推荐选择同样保存,之后选择推荐不会补写旧尝试。模块启用前已有的尝试不补快照,继续既有幂等键和正文;`client_reference_id` 始终表示本站账单归属。 开启时注册不提前创建 Stripe 客户;首次购买才按账单账户创建。积分和订阅共用一份 `affiliate_customer_request`,团队使用团队名称及归属元数据,不发送管理员邮箱。最少的个人名称 / 邮箱与归属参数在 POST 前持久化;幂等键为 `billing-customer:<请求 ID>`。响应丢失后不因资料变化而重建正文,恢复成功并提交固定客户后清除名称 / 邮箱正文。最多尝试八次、租约 90 秒;未知结果超过创建请求起始的 24 小时后必须复核,不自动换键创建新客户。尚未提供人工恢复入口。 已明确保存的推荐,在新结账发送前完成固定客户的元数据绑定。提交前再次读取 Stripe 商户、客户模式、本站唯一归属和当前 `metadata.referral`;历史 `attached` 不能代替当前核对。未选推荐的快照遇到后来出现的推荐元数据也拒绝旧请求,不悄悄补记归因。本站账单客户被替换、记录损坏、回跳站点改变或成员已降权 / 移除,均暂停结账等待复核。 完整 Checkout 正文及其摘要在 POST 前持久化。外部接受但响应丢失后,原积分价格 / 语言 / 回跳地址及订阅试用期、原操作者元数据继续使用;不根据当前配置重构已发送正文。关闭联盟模块后,已冻结且客户已准备的请求仍可重试,不再写入 Rewardful 推荐;未完成客户创建或推荐绑定的请求需恢复配置后继续。整个流程不自动发放购买积分、不确认佣金,也不打款。 Stripe 官方[幂等请求说明](https://docs.stripe.com/api/idempotent_requests)允许相同键重试同一参数,并说明键可能在至少 24 小时后清理;因此未知客户创建不会越过本地保守时限自动重发。归因依据 Rewardful 官方[自定义 Stripe 集成方法](https://help.rewardful.com/en/articles/14859642-custom-stripe-integration-method),使用 Customer `metadata.referral`;该协议说明不能代替真实付款和计佣验收。 ## 回调与当前资源 回调入口是 `POST /api/affiliates/webhook`。关闭模块时返回 404;开启后按原始请求字节计算 HMAC-SHA256,核对 `X-Rewardful-Signature` 的 64 位十六进制值,再解析官方 `object` / `event` 信封。载荷最多 64 KiB,超限返回 413;签名失败 401、信封错误 400、同事件身份冲突 409、无法持久化 503。 回调中的金额与状态只用于唤醒核查,不作为入账证据。当前支持 `commission.created / updated / paid / voided / deleted` 以及 `payout.created / updated / due / paid / failed / deleted`;其他合法事件返回已忽略,不写财务记录。未来时间超过五分钟的事件拒绝。供应商重投可能更换 `request.id`,因此使用 `event.id` 去重,只比较事件类型、创建时间和资源 ID,不把整个载荷摘要当作身份。 先保存事件,再读取当前 Rewardful 资源和商户 Stripe Charge。八个并发重复回调只产生一份事件;已核查事件的重复通知不再调用供应商。较晚到达的旧回调仍读取当前资源,旧版本响应不能覆盖较新记录,同版本出现矛盾也转为待复核。回调核查只使用固定供应商地址的 GET,不跟随重定向,每次请求有五秒期限,Rewardful 响应最多 512 KiB;不保存原始载荷、推荐者邮箱或客户邮箱。登录后的绑定后端另有上述固定客户元数据 POST,不能把整个模块称为只读。 佣金核对包括推广计划、商户账户、客户、币种、测试 / 正式模式、已付款且已捕获的 Charge、争议状态、捕获金额及退款金额。回调不会把外部客户自动归为某个本站账号或订单;本站推荐选择由上述登录后绑定操作建立。当前通过独立证据表关联具体订单,不自行计算佣金比例,也不将供应商状态解释为银行到账。 部分退款可能先出现在 Stripe,稍后 Rewardful 才重算;两方未一致时保留旧记录并等待重试。全额退款后,供应商佣金必须为 `voided` 才可核查成功。作废记录可能仍保留原佣金金额,不强行改成零。原金额及状态只表达当时读取的供应商记录,不代表仍可支付。 ## 结算与历史 每份结算最多核对 20 个不重复佣金成员;逐个重读佣金和 Charge,要求推荐者、币种、成员金额一致、没有作废佣金,成员合计等于结算金额。供应商标为 `paid` 的结算还要求成员佣金均为 `paid`。超过限制、重复成员、归属不符、版本矛盾或资源删除转为待复核,不删除此前已核查历史。 相关佣金进入新核查时,原佣金和相关结算设置 `reviewRequired: true`;供应商失败、删除或退款同步滞后不会让旧结算继续被视为刚刚确认。自己的资源完成当前核查后清除此标记;结算需要再次完整核查才能清除。佣金金额、状态或退款发生变化也会重新标记相关结算。退款发生在已标记付款之后,仍保留原 `paid` 历史和金额,不能假装已经追回资金。 Rewardful 的 `paid` 可能来自人工标记,也可能涉及其 Managed Payouts 服务。本站当前只读取状态,不能仅凭该字段宣称银行到账;没有调用供应商付款接口或实现追款。真实打款、费用、币种、退款后扣回和对账仍是外部验收项目。 ## 恢复与人工复核 事件状态为 `pending`、`checked`、`review`。回调返回 200 只说明通知已持久接收;响应中的状态才说明核查进度,不代表真实打款成功。`checked` 保存当次核查的金额、币种、状态、退款和供应商更新时间;资源表保存最新已核查快照,关联表保存最新完整核查的结算成员。 每个事件最多尝试八次,包含第一次即时核查。每次有 90 秒租约防止重复执行;到期可由已有维护入口恢复。失败按 30 秒到一小时退避,但实际执行受既有定时入口频率影响,当前为每 15 分钟一次,不保证 30 秒后立即重试。每批最多三个事件;最后一次租约过期会转为待复核,不永远停留在等待。 不可恢复的身份 / 数据矛盾直接转为 `review`;供应商超时、限流、退款同步滞后等继续有限重试。推广计划或商户配置改变后,旧事件不能被当作新计划重新接受。维护故障单独记录错误代码或错误名称,文件与任务维护不因此撤回业务结果;不记录密钥或完整响应。 已知佣金和结算可由站点管理员在 `/admin` 请求重新核查。每次先填写 5—500 字原因,再检查确认内容;提交只排队,不把等待当作核实成功。原始供应商通知保留 `source=rewardful`,人工任务单独记录为 `source=administrator`,不能伪造签名回调。每个资源只能有一项未完成的人工请求;进行中的订单租约不能被重置。 请求固定资源版本、操作者、实际登录会话、原因与独立请求编号。相同编号和内容重试返回原受理结果,不重新排队、不清空已完成结果;编号复用不同内容、资源版本已变化、推广计划 / 商户不符均拒绝。客户端不自动重试写操作;响应丢失时保留已确认内容,再次提交使用同一编号。取消确认不提交写入。每位管理员每分钟最多五次请求,包含重试。 人工流程沿用已有财务核查与订单证据队列,不新增一套后台执行器。先核查当前供应商资源,再核查具体订单;财务尚在等待时不消耗订单尝试次数。每批三个、90 秒租约、每轮八次和既有退避继续适用。佣金进入人工核查会同时标记相关结算待复核,结算要独立重新完整核查才能清除。漏回调可通过下述有界补查发现;不会定期无界扫描全部供应商历史。 排队和外部读取后的提交都重新检查原操作者的数据库角色、封禁、邮箱验证、非匿名身份,以及原会话仍存在、未过期、创建距今不超过 24 小时。会话撤销、降权或过旧会停止确认,结果进入复核;新管理员或新登录不能替旧请求继续确认,需检查记录后重新发起。已经完成的人工请求保留历史来源,后续正常维护不再依赖旧管理员登录。 操作审计分别保存“请求已排队”和“最终结果”,包括原因、操作者、资源、任务及前后状态。佣金最终结果为已关联、无匹配或待复核;结算最终结果为财务已核查或待复核。连续失败或最后一次租约过期也留下终态审计,维护会补齐中断后的完成记录。历史订单归属仍不可改给其他订单,缺少原始推荐或固定结账仍保持无匹配。没有强制确认、补计佣、付款或追款按钮。关闭模块暂停核查并保留数据,不注销远程推广计划。 ## 漏回调补查:范围、进度与边界 站点管理员在 `/admin` 的补查面板选择佣金或结算,填写起始页(1—10000)、最多页数(1—3)与原因(5—500 字),检查确认内容后排队。每页最多 20 条,每次维护只读取一个列表页;最多发现 60 条候选,同一推广计划 / 商户 / 类型只能有一个进行中的范围。请求与人工复核共用每位管理员每分钟五次的限额。取消确认不写入;响应未确认时保留原页码、原因和请求编号,再次提交返回原任务,不启动第二次扫描。 官方[佣金列表](https://developers.rewardful.com/rest-api/commissions/list)及[结算列表](https://developers.rewardful.com/rest-api/payouts/list-payouts)按创建时间倒序,[通用分页说明](https://developers.rewardful.com/rest-api/overview)使用页码和下一页。核对日期为 2026-10-11;未核实到更新时间筛选或稳定快照游标。新记录可能使页码偏移,历史旧记录的后续变化也不会自动出现在第一页。因此界面记录所选范围、已读页数、下一页和未覆盖情况,不把翻完几页称为全量对账。需要补后续范围时另行检查并提交;同一范围内的重复记录按类型 / 供应商编号去重。 列表仅用于发现编号。佣金要求展开推广计划并跳过其他计划;结算列表没有已核实的计划筛选,发现后仍逐个核对全部佣金成员及商户。列表金额、邮件或其他正文不作为财务证据,不保存完整列表响应。新事件单独记录为 `source=reconciliation` 并关联原补查请求,不伪装为签名通知或人工资源复核。发现记录进入原财务队列,重新读取当前 Rewardful 对象和 Stripe 收款;具体本站订单关联由原独立订单队列完成。找不到原选择或冻结结账时仍显示无匹配,不补绑历史订单。 进度分为发现记录中、等待财务核查、所选范围处理结束、补查待复核。分别展示发现数、财务核查数、等待数、复核数和跳过其他计划的条数。范围处理结束只表示该范围的列表与财务任务进入终态,不表示全部候选已关联本站订单,也不证明银行到账。财务复核结果可能仍需人工处理;列表停止时已发现但尚未核查的数量会保留,停止审计不写成成功。 列表进度持久保存;服务重启后从原下一页恢复。每页有 90 秒租约,失效或替换的租约不能提交旧进度;连续失败最多八次、30 秒至一小时退避,实际运行仍受每 15 分钟维护入口影响。成功读取一页后重置连续失败次数;最后租约过期可由维护转为待复核。列表领取、列表提交以及发现记录的财务领取 / 提交都重新核对原管理员和近期会话,并核对原推广计划、商户与测试 / 正式模式。权限撤销或配置改变后停止确认,不沿用过期权限。请求和终态分别留下范围、原因、操作者及当时计数的审计。 ## 具体订单与管理证据 财务回调完成核查后只排队订单关联,不在回调中追加整条订单查询链路。已有每 15 分钟维护入口最多处理三个到期记录;首批若多于三个,余下记录等后续批次。丢失排队写入时,维护会从已核查佣金补建记录,不能因此撤销商品付款或任务交付。 积分包沿 `Charge → PaymentIntent → purchaseId → 本地购买 → 固定 Checkout` 核对。付款、已捕获金额、币种、测试 / 正式模式、客户、原用户与 SKU 均须一致;本地已发放并完成支付核查才能确认。付款若同时被分配到发票,拒绝猜测用途。 订阅沿 `Charge → PaymentIntent → InvoicePayment → Invoice → Stripe Subscription → 本地订阅 → 首次固定 Checkout` 核对。付款分配使用当前 SDK 的实际 PaymentIntent 过滤参数,不假设 Charge 有 `invoice` 字段。最多读取 100 条付款分配及 100 条结账记录,不能存在下一页;要求全笔已捕获金额只分配到一张已付发票,并找到唯一已完成的原订阅结账。多发票、部分分配、重复成员、歧义结账或远程元数据不符均待复核,不从客户编号单独推算账号。 原推荐必须先于结账保存,原结账须冻结该推荐、客户、个人 / 团队归属及正文摘要。提交关联前再次锁定佣金版本、自己的操作租约、绑定、结账和本地订单,核对实际订阅编号及正文摘要;外部查询期间本地记录变化不能让旧读取结果提交。已保存的订单、付款、发票、范围和结账依据不可被后来的回调重新归给其他订单。 | 关联状态 | 含义与后续 | | --- | --- | | `pending` | 等待核查、付款 / 本地退款状态同步或可重试供应商响应;有限重试 | | `linked` | 在显示的核对时间,具体订单及财务证据一致;后续每 15 分钟到期复核,受每批三个限制 | | `unmatched` | 缺少原本站推荐、原结账或本地订单证据;保留未归属,不补绑历史购买 | | `review` | 数据矛盾、读取范围超限、关联发生变化或连续八次未知结果;不能手改为已关联 | 每轮最多八次连续失败、90 秒租约,成功核查后重置失败次数;长期正常复核不会耗尽终身尝试额度。源佣金版本变化会开启新一轮,但历史关联仍保持。旧已关联记录进入等待 / 复核时保留历史编号与金额,界面明确标为“此前关联订单”。积分订单的退款须与本地支付复核一致;存在本地争议或待复核则不能确认。 启用模块后的 `/admin` 提供站点管理员证据列表:佣金 / 结算分类、人工复核进度、具体订单、个人 / 团队范围、积分包 / 订阅类型、发票、收款 / 退款、供应商佣金及核对时间。每页 20 条,刷新只重新读取本站记录,不触发供应商查询或付款。普通用户和团队所有者无站点管理权限;当前数据库角色、封禁与验证状态每次重新确认。关闭模块时隐藏面板并停止可选表查询,旧 0012 主预览仍可运行。 页面不返回客户编号、PaymentIntent、绑定标识、账号邮箱、冻结正文或私钥。英文与西班牙语共用业务结构;美元、英镑、加元、澳元和欧元按百分之一单位展示,其他币种明确标出原始最小货币单位,避免假设所有货币都有两位小数。供应商 `paid` 只表示其标记已付,不能作为银行转账或实际佣金到账证明。缺少真实供应商验收时保持模块关闭。 ## 本地验收与新站 `bun affiliates:validate` 使用一次性 loopback PostgreSQL、实际生产 workerd 和受控的官方 HTTP 形状。它验证签名 / 超限、八个并发重复回调、当前资源金额、退款滞后、实际 Worker 销毁重启后的原事件恢复、已付结算成员、全退作废、旧通知、供应商删除、重定向拒绝与八次上限。所有供应商请求均被受控接口截获且只能 GET,没有真实账号、积分发放或资金转移;结束后清理自己的数据库、Worker 和文件,核对正常配置字节未改变。 测试另外执行实际 0026 SQL,确认已有普通账号和积分余额保持;连续两次生成站验证推广配置不继承。`site:create` 将模块关闭、计划与商户 ID 重置为 `null`,新站不会复制服务密钥。 `bun affiliates:validate-bindings` 在已有普通账号、余额和联盟事件的实际 0026 数据库上升级至 0027,确认原数据保持,再使用真实 workerd 验证 Origin / 明确选择、个人 / 团队权限、八个并发请求、等待账单客户、旧付款 / 结账拒绝及 Worker 销毁重启后的丢响应恢复。它还观察 PostgreSQL 的真实 Lock 等待:元数据 POST 期间,组织成员修改必须等绑定事务结束;写入前已降权则拒绝。仅受控的固定客户元数据 POST 被允许,没有访问真实供应商、创建客户 / 结账 / 佣金或转账,验收库和文件随后清理。 当前后端采用官方 Stripe **Customer metadata 的 `referral`** 方案,保留现有 `client_reference_id` 的用户 / 团队及支付归属。公开采集与设置已受控本地验收,原结账幂等与绑定已接入受控本地购买路径,仍需真实供应商付款验收,不能据此宣称已交付完整计佣。 兼容边界仍保留:启用前已经准备的积分尝试没有归因快照,继续原先不含 `customer` 的正文;新尝试才会固定客户及推荐选择。已经尝试创建的积分包 / 订阅可能丢失响应,重试不能增加客户或推荐参数而改变原幂等请求。个人和团队客户按既有权限分别绑定,不从当前页面或工作区切换推算旧购买归属。 官方公开 REST 文档当前提供推荐列表和客户过滤,没有列出按推荐 UUID 获取单条对象的接口。本阶段按上述三页范围实现;大型推广计划超出范围时需要补充官方可核实的定位方案,不无界遍历全部访客或擅自调用猜测地址。 `bun affiliates:validate-checkouts` 在独立 PostgreSQL 中实际从 0027 升级至 0028,核对普通账号、初始积分和旧购买逐字段保持,再运行生产 workerd。它验证注册延迟创建客户、首次积分归因、客户及结账响应丢失后的实际 Worker 重启恢复、关闭模块后仍沿用旧正文、旧积分请求不补客户、个人订阅共用客户、真实 Better Auth 结账恢复、八个并发请求只创建一个客户,以及团队结账 POST 期间真实成员锁与之后降权拒绝。所有供应商请求截获,新增 POST 只允许客户创建、明确元数据绑定和受控 Checkout;没有真实支付或购买积分发放。主库仍为 0012,验收资源会清理。 `bun affiliates:validate-orders` 在独立 PostgreSQL 实际应用 0028→0029→0030→0031→0032→0033,保存普通账号、初始积分、已付购买、推荐和结账;0029 历史已关联记录在 0030 重新排队,事实保持。生产 workerd 验证八次并发通知只排队一次、具体积分 / 团队订阅订单、退款等待本地结算及实际服务重启恢复、管理员降权 / 封禁即时拒绝和连续八次失败转复核。0031→0032 保持已有事件来源为供应商通知,原账号和历史事实保持。实际管理员接口验证当前 Origin、普通账号拒绝、强制状态拒绝、并发重复意图只排队一次、人工任务重启后完成原团队订单与独立结算,并分别保存请求 / 完成审计。此验证使用预先保存的历史付款事实,只读受控供应商,不代表真实付款交付。 加 `--ui` 会构建真实管理界面并开启本地浏览器验收:实际 API 读取、等待 / 已关联 / 无匹配 / 待复核、英 / 西文案、哑光黑白主题及刷新;真实表单提交后的响应主动丢失,再次提交保持同一请求编号,随后通过原维护入口完成并核对终态审计。全部验收资源属于一次性环境,结束后清理。正常预览仍为 0012,没有自动应用 0029—0033 或提升主账号权限。 加 `--scan-ui` 验证实际补查表单。原生验收先保存 0032 的人工任务,升级 0033 后逐字段核对旧来源 / 操作者;在没有本地回调或财务记录时发起有界扫描,重复意图只受理一次。实际销毁并重启 workerd,继续第二页并找回原团队订单。浏览器首次成功提交后主动丢响应,重试同一正文,执行原维护入口后核对范围、财务计数和两阶段审计;英 / 西文案、哑光黑白及 320 / 390 像素布局已检查。所有供应商请求截获,只读 GET,历史付款事实预先保存;没有真实购买积分发放、计佣或转账。 ## 官方依据与外部待验 核对日期:2026-10-11。签名与事件依据 [Signed webhooks](https://developers.rewardful.com/webhooks/signed-webhooks)、[Requests](https://developers.rewardful.com/webhooks/requests)、[Event types](https://developers.rewardful.com/webhooks/event-types);当前对象依据 [Commission](https://developers.rewardful.com/rest-api/commissions/object)、[Payout retrieval](https://developers.rewardful.com/rest-api/payouts/retrieve-a-payout)。客户元数据方案依据 [Custom Stripe Integration Method](https://help.rewardful.com/en/articles/14859642-custom-stripe-integration-method),浏览器 API 依据 [JavaScript overview](https://developers.rewardful.com/javascript-api/overview)。推荐核查依据 [Referral list](https://developers.rewardful.com/rest-api/referrals/list)、[Referral object](https://developers.rewardful.com/rest-api/referrals/object)与 [REST API overview](https://developers.rewardful.com/rest-api/overview)。订阅分配参考 Stripe [Invoice Payment](https://docs.stripe.com/api/invoice-payment)及 [Charge 对象](https://docs.stripe.com/api/charges/object),具体过滤字段另以已安装 Stripe 22.6 的类型定义核对。付款边界参考 [人工佣金付款](https://help.rewardful.com/en/articles/2773351-how-do-i-pay-commissions)与 [Managed Payouts](https://help.rewardful.com/en/articles/11930744-merchants-faq-managed-payouts)。 尚未使用真实 Rewardful 账户、浏览器 SDK、推荐链接、Stripe 沙箱付款 / 退款、真实佣金结算或 Cloudflare 定时运行。本地返回值不能证明供应商规则、延迟、实际 Cookie、真实归因或银行到账。完整 I14 与 I0—I16 目标仍进行中,进度见[迭代记录](./iteration-progress)。 --- --- url: /docs/agentbuff-stack/server-activity-events.md --- # 工作台使用与购买事件 这是 I11 的服务端观测模块,默认关闭。它依据已经提交的任务、文件读取和积分包结算与个人 / 团队订阅账单付款事实记录事件,和[公开工具的浏览器报告](./tool-usage-events)、Cloudflare 访问统计、营销邮件同意各自独立。目前没有统计后台,也不提供浏览器自报购买金额的接口。 ## 配置与升级 在 `packages/core/website.ts` 的 `analytics` 中加入: ```ts serverEvents: { enabled: false, consentVersion: "2026-10-10.2", retentionDays: 30, }, ``` 启用当前版本前,把目标数据库按完整顺序升级至 `0025_team_payment_activity.sql`,然后一起重建 API 与工作台。0018 新增个人选择表 `activity_consent` 和事件表 `server_activity`,0019 新增个人账单恢复检查时间;0025 新增 `team_activity_consent`,事件必须归属个人或团队之一,团队只能记录订阅付款。旧个人事件保持原数据与全局唯一键,已有站点继续追加迁移,不重写历史。旧生成站还需补齐配置字段;本任务主预览仍为 0012,此功能关闭,不读取这些可选新表。独立测试数据库的迁移验收不代表主库已升级。 不需要新供应商密钥。`consentVersion` 为 1—80 字符,`retentionDays` 为 1—90 天;默认 30 天。修改同意用途或数据范围时更新版本,旧版本不再授权新事件。个人订阅账单用途使用版本 `2026-10-10.2`,更早的个人同意需重新明确选择。本轮团队选择单独从未授权状态开始,个人用途保持该版本,不能把已有个人同意升级成团队同意。`provider` 和 `toolEvents.enabled` 不会自动开启此模块。 ## 用户选择 启用后,工作台 Settings 出现 Activity reporting 卡片。初始不勾选,只有点击 Save choice 并获得服务端确认才生效;取消勾选后也要保存才能撤回。Last confirmed choice 展示最后得到确认的选择,勾选框自身不是已保存状态。失败时显示未确认,并可 Reload choice 读取实际状态,不自动重试写入。 选择属于当前个人账号,不接受客户端传入所有者。服务端重新检查账号状态、同源请求和操作限额。每次变更带当前 `revision`,旧标签页不能覆盖较新的撤回;返回冲突时先重新加载。相同选择与版本重复保存不延长同意起点。表中保留最新选择、版本、同意 / 撤回时间和版本号,不是完整同意历史档案。 公开工具页面的选择、注册账号或加入邮件名单都不授予此权限。撤回后停止新记录;已经收到的记录按保留规则清理。删除账号会级联删除这两张表中该账号的数据,不改变账本或管理审计各自的保留规则。 ## 事件口径 | 事件 | 真实触发点 | 去重与限制 | | --- | --- | --- | | `task_started` | 原任务首次领取执行租约,第一条尝试已提交 | 每个任务一次;排队 / 提交不算开始,后续重试不增加开始次数 | | `task_succeeded` | 当前租约的结果实际发布成功,任务状态已提交 | 每个任务一次;失败、取消或过期租约不能造成功事件 | | `file_download_initiated` | 私有文件实际读取成功,并进入附件响应路径 | 每个文件在保留记录期间一次;普通预览不计,外部 API 的附件响应计入;不证明完整传输或用户已保存 | | `purchase_confirmed` | 积分包结算已验证、购买的 `grantedAt` 存在且匹配原积分发放账本 | 每个购买一次;未付款的 Checkout、详情页浏览和客户端金额不构成购买 | | `subscription_payment_confirmed` | 个人或团队订阅的当前已付账单、完整已付分配记录和实际成功 PaymentIntent / 已捕获 Charge 经服务端核查 | 每张账单一次;同一订阅的续费账单各自计数;零元试用、客户抵扣和线下 PaymentRecord 不计 | 事件唯一键是资源 ID 加事件类型。已签名回调、后台恢复与拥有者的购买详情读取共用同一记录;重复回调或确认页刷新不会重复增加购买与积分。这里没有另建前端购买上报通道。 购买保存原购买的最小货币单位金额、币种及 `livemode`。测试付款和真实付款必须分开,币种不能直接相加。这是原始购买确认,后续退款或争议不会把它改写成净收入;利润、留存、净收入归因尚未实现。订阅付款详见下一节;订阅激活、试用或打开结账页不能当作已付款。 ## 订阅账单付款 接收已有 `/api/auth/stripe/webhook` 的 `invoice.paid`,使用现有原始正文 SDK 验签和测试 / 真实模式门禁。默认关闭时不读新增表、不向供应商查询。开启时要求账单的订阅 ID、客户 ID 与已保存的订阅、当前个人 / 团队客户及该主体的明确同意全部匹配;个人和团队身份同时匹配时保守跳过,不推断归属。团队账单不会归到管理员或成员个人账号。 签名事件只提供核查线索。服务端重新读取账单,要求当前 paid、正数付款额、余额为零、正确币种 / 模式和有效付款时间;再分页读取该账单完整的已付分配记录,核查对应 PaymentIntent 的 succeeded、客户 / 模式 / 币种及实际收款,或旧式 Charge 的 paid / captured 与金额。同一收款来源的分配合计不能超过其实际收款,分配总额还必须等于账单 `amount_paid`。不是直接存事件载荷里的金额。 零元试用 / 全额折扣、客户余额抵扣和线下标记付款不记为新收款。存在 PaymentRecord 等未支持来源或分配不完整时保守跳过,不用部分金额冒充完整付款;超过每张账单 100 个付款分配、重复页或分页不完整时记录通用故障。本模块不会修改订阅状态、权限、额度或积分。参考 [Stripe 账单](https://docs.stripe.com/api/invoices/object)、[付款分配](https://docs.stripe.com/api/invoice-payment/object)。契约核对日期为 2026-10-10;本地 SDK 固定 API 版本为 `2026-08-26.dahlia`,Stripe Endpoint 应采用对应账单对象结构。 唯一键使用供应商账单 ID 加固定事件类型。回调与后台恢复共用该键;个人和团队工作台的确认 / 状态页面只读取原订阅状态,不发购买上报或扫描供应商历史,刷新不会增加事件。续费的另一张账单可新增另一条事件。金额是所核实账单的原始收款确认,不扣后续退款 / 争议,也不是 MRR 或净收入。测试与真实、不同币种须分开。 有效签名账单在可选统计故障时仍应答成功,不让观测故障干扰原账单流程;后台扫描供应商的当前已付账单恢复,普通工作台读取无需等待这项扫描。每个个人或团队主体两次尝试至少相隔 15 分钟,每轮后台合计最多三个主体,按上次尝试时间轮换,失败也会让出下一轮位置。每个主体最多查 1,000 张已付账单,未完整分页不假报恢复完成;已写入的部分真实事件保留并按唯一键去重。创建时间很早但最近付款的账单仍按实际 `paid_at` 与同意起点筛选,不用 created 过滤误删迟付。 恢复扫描按最新同意版本原子领取统计自己的检查时间,不占用账单操作租约;供应商历史查询暂停时,用户仍可管理账单。检查时间在开始尝试时写入,失败同样等待下一轮,旧扫描没有完成后回写时间的路径。处理过程中撤回或变更版本,最终写入再次检查当前选择;重新同意会重置待检查时间。限额、供应商延迟和保守跳过会漏报,不保证即时或完整统计。参考 [账单列表接口](https://docs.stripe.com/api/invoices/list)。 ## 团队的独立选择 切换到团队后,Settings 还显示 Team payment reporting 卡片。初始不勾选,个人账号是否同意不改变这里的选择。当前 owner / admin 可以明确保存或撤回;普通成员只能查看已确认状态,已移除成员不能读取或修改。团队选择只授权该团队的新订阅收款,成员的任务、文件、积分包仍属于各自个人选择。 请求带页面看到的团队 ID、当前同意版本和 revision。服务端核对所选团队与实际会话当前团队一致,重新检查仍有效的会话、未封禁个人账号和最新成员角色;成员写操作与选择保存共用团队锁。保存等待期间被降级或移除时拒绝,旧页面不能覆盖新撤回。切换团队后丢弃未保存的勾选,查询缓存按账号和团队分别隔离。这个隐私选择没有额外要求 24 小时新鲜会话。 团队同意和最终付款插入受事务保护,供应商查询期间撤回会阻止新记录;重新同意只接纳新起点后的付款,不能补齐旧同意区间的遗漏。团队付款与个人付款共用收款核查、事件表和账单 ID / 类型唯一键,即使账单以后改绑也不会再写一条。重复回调、详情页刷新和续费恢复不发积分、不修改订阅权限。 团队选择保存最后决策者的本站账号 ID、版本、revision、同意 / 撤回时间和恢复检查时间,不是完整同意历史。该决策者离队或删除个人账号不撤销团队选择,也不删除团队付款;决策者 ID 作为历史标识保留,不存姓名或邮箱。删除团队会级联删除团队选择与团队事件,原个人事件仍按个人规则保存。 ## 保存哪些数据 记录包含本站账号 ID 或团队 ID(恰好一个)、资源 ID(任务 / 文件 / 积分包使用内部 ID,订阅付款使用供应商账单 ID)、固定事件类型、同意版本、发生 / 入库时间,任务另含固定处理器 ID,购买另含上述金额、币种和测试状态。它是可关联账号或团队的第一方内部记录,不能称为匿名统计,也没有发送到外部分析供应商。 不记录输入、结果、文件名、邮箱、页面 URL、任意参数或支付原始回调。统计失败只记录通用错误名称。删除文件或任务不会删除其历史观测;观测仍受保留期限约束。上线前须把实际数据范围、站主、保留与联系方式写入自己的隐私说明;模板隐私页只是草稿。 ## 故障恢复与保留 记录在业务提交后尝试。观测表不可用不会撤销已经完成的任务、积分结算或文件内容响应,但可能漏报,也会增加少量本地查询时间。 定时入口在既有文件 / 任务维护之后补查缺失的任务开始、成功和积分包确认,每类每轮最多 100 个候选。只补当前有效同意起点之后、保留期内、仍然存在的业务事实;每次插入再次检查账号和最新选择。没有用户访问也可恢复,但不保证完整或实时统计。 撤回、改变版本或重新同意发生在恢复之前时,旧同意区间内未写入的事实不会补齐。重新同意不追溯旧行为。下载没有独立持久化基础事实,失败不能后台补造。 清理每次最多删除 500 条超过保留天数的事件,按发生时间判断;维护失败或积压会延迟实际删除。模块关闭也会停止自动清理,需要站主单独安排已有数据清理。个人同意记录保留至账号删除,团队同意记录保留至团队删除。旧任务 / 购买事实超过期限不会被恢复程序重新插入;文件原记录过期后,新的实际下载请求可以产生新记录,因此该指标不能称为终身唯一下载文件数。 ## 本地验收 ```bash bun --env-file .env --env-file .env.local apps/api/local/validate-server-activity.ts ``` 验证器只接受本地 PostgreSQL,建立并迁移自己的一次性数据库,运行真实生产入口的 Worker、Queue 与 R2。Stripe HTTP 使用受控响应,回调由已安装 SDK 生成签名;邮件捕获且其他外部请求拒绝。退出时销毁自己创建的资源,不升级主数据库。 加 `--ui` 可在独立 `activity.localhost:21312` 预览卡片。完成选择 / 保存 / 撤回后,用验证器提示的本地结束标记收尾,它会检查最终选择和事件数量后清理。测试账号和口令只用于一次性环境,不能作为部署配置。 已有证据包括 8 项真实迁移数据库行为测试、个人选择交互,以及支付测试中的已签名结算恢复 / 确认页去重。原生验收涵盖实际执行、八并发附件下载、观测故障后任务成功与定时补齐、签名购买回调和撤回。11 项个人订阅账单行为测试和 1 项普通账单读取边界测试继续通过;`--invoices` 同时验收签名账单、八并发去重、分页收款分配和续费恢复。 本轮新增团队选择的 6 项真实数据库 / RPC 与增量迁移测试、5 项账单归属与撤回测试,工作台选择交互扩展到 7 项(含西班牙语失败确认)。`--teams` 包含已有 `--invoices`,原生验收在真实 PostgreSQL 锁等待期间降级角色,随后八并发签名回调只生成一条团队记录;统计表故障不阻断账单,实际定时入口补回续费。`--teams --ui` 验证个人和团队分别保存 / 撤回,最终均为不分享、revision 4;390 像素浅深主题与 320 像素浅主题无横向溢出。真实 Stripe / Resend 与完整 I11 外部验收继续保留在[开发计划](./development-plan)中。 --- --- url: /docs/agentbuff-stack/content-seo.md --- # 内容、搜索优化与统计 ## 内容位置 公开页在 `apps/web/pages/`,工具目录和同步处理器在 `packages/core/tools.ts`,英文文章在 `apps/web/content/blog/*.md`,西班牙语译文在 `apps/web/content/blog/es/`,按同名文件配对。公开页面是构建得到的静态 HTML,React 处理交互工具,博客搜索使用轻量原生脚本。 文章 frontmatter 包含 `title`、`description`、`date`、`author`、`category`,可选 `updated`、`draft` 和关联 `tool`。草稿不会生成文章页,RSS 和 sitemap 使用同一份可发布文章列表。英文草稿也会抑制对应译文;缺译文的目录卡片明确回退英文,RSS 不冒充翻译。已启用语言的空正文、无来源译文和无效元数据拒绝构建,详细规则见[产品语言](./languages)。 ## 导航与博客浏览 公开导航明确提供首页、功能、工具、博客、定价。英文产品界面分别显示 `Home`、`Features`、`Tools`、`Blog`、`Pricing`;教程使用中文。导航数据集中在 `packages/core/navigation.ts`,公开页头、页脚和工作台的浏览入口共同读取。工作台访问公开页使用文档导航,避免由 SPA 错接博客路径。 博客索引 `/blog` 与启用后的 `/es/blog` 支持按标题、简介和分类搜索,多个关键词按同时匹配筛选;分类取自已发布文章的 `category`,不需要维护第二份分类列表。清除筛选后恢复全部文章。筛选只在浏览器运行,不请求 API,不增加数据库或第三方搜索服务。可通过 `/blog?q=json` 或 `/blog?category=Practical%20guide` 给首次加载设置筛选条件;交互筛选不会改写 URL。 全部文章卡片和正文在构建时输出,禁用 JavaScript 仍能浏览文章;交互筛选栏仅在脚本加载成功后显示。当前只有两篇示例教程,分类随真实文章扩充。它们说明工具行为与限制,不能当作需求或流量验证材料。 文章页显示作者、发布日期、可选更新日期、预估阅读时间、相关文章和关联工具。阅读时间按各语言正文约每分钟 200 词估算,不作为精确阅读耗时。目录从 Markdown 二级、三级标题提取,锚点链接无需 JavaScript。首页与博客索引共用 `apps/web/components/public/PostCard.astro`(使用通用 `@repo/ui/marketing/BlogCard.astro`),文章来源、草稿过滤和排序仍集中在 `apps/web/lib/posts.ts`。 ## 当前输出 默认 title / description、canonical、Open Graph / Twitter 卡片、文章结构化数据、sitemap、robots 和 RSS。OG 图片来自 `metadata.ogImage`,默认未提供;配置 URL 本身不会生成图片。 `blog.enabled=false` 会移除导航 / 首页文章、文章路径、RSS 条目和 sitemap 中的博客地址。`/blog` 索引页保留无文章的 noindex 页面,西班牙语博客和订阅不生成,robots 禁止博客和 RSS。任何语言没有实际已发布文章时,目录设置 noindex 并从 sitemap 排除。构建会清理旧产物;部署时也必须替换整套静态资产,不能残留旧文章。 账号、API 与工作台不作为搜索内容。模板法律页仍是 noindex 占位,需要站主提供真实适用内容。新增文章不会自动提升 Google 可信度或保证排名;内容应解释真实任务、处理限制、结果与作者责任。 ## Cloudflare 统计 ```ts analytics: { ...websiteConfig.analytics, provider: "cloudflare", } ``` 同时设置公开的 `PUBLIC_CF_ANALYTICS_TOKEN`。默认 `none` 时不加载统计脚本;只有公共 Astro 页面插入 beacon,工作台及 API 不植入。配置检查和构建都会拒绝开启统计却缺少 token。 参考 [Cloudflare Web Analytics 启用说明](https://developers.cloudflare.com/web-analytics/get-started/)。如果 Cloudflare 控制台已自动注入统计,不能再重复使用本站手动 beacon,选择一种注入路径。公开工具另有默认关闭、需明确同意的第一方[使用事件](./tool-usage-events),它不由 beacon 提供,也不是服务端任务或收入证据。实际任务、附件下载与积分包确认有独立的[服务端事件](./server-activity-events),默认关闭且需账号明确选择;它不代表净收入或订阅留存。收入归因与真实 Cloudflare 统计数据尚未验收。 ## 检查 查看页面原始 HTML 的标题、h1、canonical、结构化数据与图片 URL;核对 sitemap 无草稿 / 私有页、RSS 日期与链接;检查文章移动端和禁用博客后的产物。发布新工具任务页要有真实可交付结果,不生成只有换关键词的空页面。 --- --- url: /docs/agentbuff-stack/deployment.md --- # 部署与验收 ## 目标架构与当前状态 一个公开域名 → Web Worker(Astro 静态资产与路由)→ App / API 内部服务绑定。App 是 React SPA,API 是 Hono / Better Auth / tRPC。API 用 Hyperdrive 连接 Neon PostgreSQL,鉴权、支付和写后读取使用无缓存连接。 测试与正式环境已分别声明存储桶、任务队列、死信消费者和十五分钟维护入口;资源清单与离线发布检查已接入发布脚本和持续集成。**声明不等于创建或线上验收。** 正式环境仍保留示例域名;两个环境的 Hyperdrive 编号和发信地址仍待实际配置,发布检查会拒绝占位值。本地预览不受这项检查影响。 ## 本仓库的官网域名与当前安排 用户于 2026-10-11 明确 `stack.agentbuff.dev` 用作 AgentBuff Stack 模板官网,现已授权发布静态官网和双语教程,暂不接真实产品。官网是 `apps/website` 的独立静态站,Wrangler 的自定义域名只声明在 `apps/website/wrangler.jsonc`。它不需要产品数据库、Hyperdrive、任务队列或模型密钥。 此前把这个域名当作产品测试入口是理解错误,已撤回 API / Web 的对应配置,产品 `staging` 恢复 `staging.example.com` 示例。它需在未来选定真实产品测试域名和资源后再配置。下面的产品离线检查只检查产品三个 Worker,不检查官网,也不能以官网域名代替产品的发布条件。 官网构建变量示例在 `.env.website.example`;教程位于官网 `/docs/` 下,演示地址留空并隐藏入口。`bun website:package` 在独立目录构建合并发布包,不覆盖本地预览;完成 Cloudflare 登录和域名权限核对后才上传。准备与发布命令见[官网配置](./website-showcase)。本地继续使用 4400 官网、4406 教程与 4410 模板演示。静态发布不创建产品数据库、任务队列或模型接入;构建和离线打包通过不能代替线上 DNS、证书与页面验收。 ## 资源清单与创建职责 以下名称以站点编号 `agentbuff-stack` 为例;生成站使用自己的编号。测试环境是 `staging`,正式环境是顶层配置,对应 `production`。 | 资源 | 配置入口 / 绑定 | 创建与交接 | | --- | --- | --- | | 三个 Worker | `apps/{api,app,web}/wrangler.jsonc` | Wrangler 发布;Web 是唯一公开域名入口,App / API 通过服务绑定访问 | | 内部服务 | Web 的 `API_SERVICE`、`APP_SERVICE` | 指向该环境的实际 Worker 名;测试环境默认在基础名字后加 `-staging` | | 缓存与无缓存连接 | API 的 `HYPERDRIVE_CACHED`、`HYPERDRIVE_UNCACHED` | Terraform 管理;将对应环境输出的两个不同编号写入 Wrangler,不复制另一环境的编号 | | 私有上传存储 | API 的 `STORAGE` | Terraform 可选上传资源,需启用 `uploads_enabled`;名称为 `agentbuff-stack-{环境}-uploads`,与生成器一致 | | 任务队列 | API 的 `TASK_QUEUE` | 操作者预先创建该环境队列;模板名为 `agentbuff-stack-{环境}-tasks`,Wrangler 声明生产者与消费者 | | 死信队列 | API 消费者与 `TASK_DEAD_QUEUE_NAME` | 操作者预先创建 `agentbuff-stack-{环境}-task-dead`;变量、主消费者的死信引用和死信消费者名称必须相同 | | 定时维护 | API 的 `triggers.crons` | Wrangler 声明 `*/15 * * * *`;关闭新任务时仍保留历史任务、支付和通知的恢复入口 | | 数据库 | Neon 与迁移连接 | 为每个站、每个环境准备独立数据库;原始连接凭据交给 Hyperdrive,迁移使用独立非池化连接 | | 认证与邮件 | API 秘密及 `RESEND_EMAIL_FROM` | 当前环境分别保存认证秘密和邮件密钥;发信域名仍需在服务商验证 | | 支付、统计、模型及可选模块 | [环境变量](./env)、[模块总览](./modules) | 按启用模块准备完整变量、真实价格与接口;资源检查不证明供应商可用 | Terraform 只管理 Hyperdrive 与可选 R2,不管理 Worker 或域名。两个环境入口为 `infra/envs/staging`、`infra/envs/production`,仍须替换示例组织 / 工作区并核对自己的资源安排。队列创建和 Secrets 保存由操作者另行执行,不把它们混入 Terraform 的职责或新站生成流程。 环境变量、资源绑定、服务绑定和 Secrets 需按命名环境分别声明;定时入口具有继承规则,因此这里仍显式写出两个环境。规则依据[Wrangler 官方配置说明](https://developers.cloudflare.com/workers/wrangler/configuration/)。任务与死信消费配置参见[队列官方说明](https://developers.cloudflare.com/queues/configuration/configure-queues/)。这两份文档的核对日期为 2026-10-11。 ## 环境隔离 每个新站使用独立数据库、认证秘密、Stripe 产品、邮件配置和 Worker 名。每个站再区分开发、测试、正式环境,沙箱与生产凭据分开。Worker 上的 Secrets 独立保存,不会读取电脑上的 `.env.local`。 公开域名、API 的 `APP_ORIGIN`、Worker 名与服务绑定必须一致。不要把 `DATABASE_URL` 当成生产 API 已使用的连接,生产 API 读取 Hyperdrive 绑定,迁移命令单独读取数据库连接。离线检查能发现配置编号相同,无法证明两个不同编号背后使用了不同数据库。 数据库应用账号、迁移所有者与网站用户分别管理。受限应用角色、完整任务流程及密码变更已在独立本地 SCRAM 实例验证;改密码 / 撤销连接权限都不会断开现有连接。两个 Hyperdrive 的源凭据和旧连接池需在所选环境一起处理,见[数据库角色与凭据](./database-roles),真实云端验收仍待进行。 ## 离线检查 先准备所选环境的 Wrangler 配置、实际资源编号、裸 HTTPS 域名和自己的发信地址。只检查名称与配置关系,不需要云端密钥: ```sh bun deploy:check --target staging bun deploy:check --target production --json ``` 输出包含 Worker、资源名称、必需秘密的**变量名**及字段问题,不包含 Hyperdrive 编号或秘密值。检查缺失绑定、环境串用、主队列 / 死信引用、维护入口、服务名、存储命名和示例值。`onlineVerified: false` 表示没有验证资源存在、账户权限、Secrets 保存情况、数据库版本或供应商行为。模板初始配置检查失败是预期结果,不能用假的编号消除报错后当作真实验收。 服务端变量的类型、凭据组合和模块要求另行检查。命令使用自己的目标文件,报错只列字段;文件不提交: ```sh bun --env-file .env.staging.local scripts/config-check.ts --target staging bun typecheck bun web:check bun run test -- --run bun lint bun run format:check ``` 为所选域名构建公开页,再检查发布产物。下面的地址仅展示用法,需换成已通过资源检查的真实地址: ```sh APP_ORIGIN=https://staging.your-domain.com PUBLIC_SITE_URL=https://staging.your-domain.com bun run build bun deploy:check --target staging --artifacts bun run docs:build ``` 产物检查要求邮件、工作台、公开页的入口文件存在且非空,并核对公开首页唯一的规范链接与目标域名相同。它不验证每篇文章的索引结果,也不证明部署版本已在线生效。构建本地预览时使用 `bun preview:build`,它恢复本地地址配置。 ## 发布顺序 `bun deploy:staging` 与 `bun deploy:production` 在构建 / 上传前检查资源;正常构建会注入目标公开地址,再检查入口产物与首页规范链接,然后按 API → App → Web 发布。`--skip-build` 仍检查恢复的产物,拒绝本地或另一环境的首页链接。**这两个命令不会迁移数据库。** 持续集成默认只检查与构建。只有显式开启 `DEPLOY_ENABLED` 的发布任务才选择目标地址;构建产物和部署记录使用同一个已校验地址。发布任务恢复产物后,先运行资源 / 产物检查,再检查所需凭据、Cloudflare 认证与 Worker 离线打包,之后才进入原数据库迁移和上传流程。不会因为新加检查而自动开启部署。 资源创建、数据库迁移和 Worker 发布仍是独立操作;需要明确环境请求才能实际执行。发布不是原子切换,中途失败可能留下混合版本。数据库升级保持兼容,回退 Worker 版本不删除用户数据、不重写已应用迁移。 0012 已发布版本 → 当前完整增量 → 新 Worker → 旧 Worker 的本地兼容演练已通过;原账号、私有文件、收费任务 / 在途补投、原迁移和末尾故障回滚均使用实际数据库与运行时核对。复验入口与限制见[模板版本与升级](./template-upgrades#本地旧站升级与代码回退演练)。它只作用于自有一次性库,不代替所选真实环境的升级安排。 新增完整数据库归档与独立空库恢复已在实际本地 PostgreSQL 验证,使用同一导出快照核对表数据和迁移记录;配置、恢复命令、对象 / 外部事实边界见[数据库备份与恢复](./backup-restore)。独立文件对象检查点与私有结果恢复也已在受控本地 R2 绑定验收;本机归档可预览保留策略并人工清理,清理后保留归档的实际数据库恢复已验。这不替代下列云端验收,也没有自动设置 R2 生命周期。 ## 云端验收与恢复 选定真实测试环境后,逐项保留时间、环境、资源与结果;不能以离线检查代替以下验收: 1. 核对实际账户、资源、独立数据库、绑定、Secrets、域名证书、发信域名和启用模块的数据库版本。 2. 注册 → 登录 → 上传 → 收费任务 → 检查结果 → 下载;验证另一用户 / 团队无法访问私有文件,邮件与支付回跳使用所选域名。 3. 在真实服务验证队列消费、死信、十五分钟维护、中断重试、失败返还、重复支付通知与退款;核对账本,不只观察页面提示。 4. 备份并在独立目标恢复数据库,核对文件元数据、对象、任务、账本和购买;验证对象保留 / 删除及密钥轮换安排。 5. 记录实际请求、对象、存储、队列、数据库与模型用量,依据所选服务套餐核算成本,并记录日期和来源。 I15 的云端运行、成本与恢复仍待真实环境验收;I16 的两个真实独立产品也未因此完成。Cloudflare、Neon 或第三方的免费额度属于当前服务政策,本模板不承诺永久免费。 认证版本密钥、旧登录重新认证与基础密钥的邮件用途已实际本地验收,操作范围和历史加密边界见[认证密钥轮换](./auth-key-rotation)。数据库角色、Cloudflare 绑定、外部供应商密钥及其轮换仍独立验收,不能以认证密钥切换证明全部权限已撤销。 ## 文档部署 `bun run docs:build` 生成 `docs/.vitepress/dist`。它是独立静态文档站,当前没有产品域名 `/docs` 的自动服务绑定。设置 `DOCS_SITE_URL` 才生成正确 sitemap;不设置时 noindex。选择自己的文档域名与托管后,再设置链接与索引规则。 --- --- url: /docs/agentbuff-stack/backup-restore.md --- # 数据库备份与恢复 ## 当前能力与范围 I15 新增完整数据库归档、校验清单与独立空库恢复,已在本地实际 PostgreSQL 和系统 `pg_dump` / `pg_restore` 验收。恢复核对账号、文件元数据、任务、账本、购买与迁移记录,文件对象检查点、独立空桶恢复和私有结果下载也已在受控的本地 R2 绑定验证;**没有接入真实云端服务或自动为任意桶执行备份**。正常预览仍为已授权的 0012,验收使用一次性数据库,不升级或覆盖主库。 原 `bun db:export` 默认只导出结构,适合查看 SQL,不能当作完整备份。本教程使用新增的 `db:backup` / `db:restore`。需要先安装 PostgreSQL 客户端工具并加入 PATH;备份客户端不能比源数据库更旧,具体版本兼容参见[官方备份说明](https://www.postgresql.org/docs/17/app-pgdump.html)。核对日期:2026-10-11。 ## 1. 选择来源并生成归档 开发来源沿用原数据库配置,测试与正式环境只从对应 `.env.<环境>.local` 读取 `DATABASE_URL`。环境文件必须存在并定义来源连接,不回退到开发或 shell 中旧地址。建议使用有完整读取权限的非池化直连;Hyperdrive 不是备份连接。 ```sh bun db:backup bun db:backup:staging bun db:backup:production ``` 选择一种命令对应自己的环境,不依次备份所有环境。`--output` 可指定本机私有目录,默认是 `db/backups`。当前执行这些命令仍是人工动作;持续集成、发布脚本和定时维护不会自动备份或恢复,也没有新增云端上传。 成功后输出目录 `snapshot-<唯一编号>`,内含: | 文件 | 内容与边界 | | --- | --- | | `database.dump` | 自定义格式完整数据库归档,包含数据、表结构、约束、序列及迁移记录;不包含 R2、队列消息、Worker Secrets 或集群角色 | | `manifest.json` | 格式版本、来源指纹、环境、时间、数据库版本、归档大小 / SHA-256,以及每张表的行数 / 数据摘要;不保存来源连接、密码、客户正文或对象路径 | 归档目录权限为 700,两个文件为 600。归档本身含用户数据和认证记录,保持私有存储;新站生成器排除备份目录、归档文件、已完成快照与临时目录。文件校验用于发现损坏,不证明归档来源可信;恢复自己的已确认归档。 备份在只读可重复读事务中导出快照,数据摘要与 `pg_dump` 使用同一快照,避免备份期间的积分 / 账本更新落入不同时间点。普通表、分区表与物化视图逐行排序、流式计算摘要;这需要额外扫描和排序,规模较大时应选择维护窗口并评估磁盘、执行时间和数据库负载。 没有按表筛选、增量或自动保留策略;人工清理命令见下文。失败归档不会发布为完成目录,并清理本次临时目录;进程被强制结束后可能留下 `.partial-*`,恢复命令不接受缺少完整清单的目录。单数据库快照不等于数据库、文件对象与外部支付共同事务。[官方说明](https://www.postgresql.org/docs/17/app-pgdump.html)也区分数据库归档与集群级角色等对象。 ## 2. 准备独立空目标 先准备独立恢复数据库及权限。不要把来源或正在提供服务的数据库作为目标;已有表、函数、类型、非默认扩展、对象或自定义模式都会拒绝恢复。命令不删除、清空或创建目标数据库。 根目录创建 `.env.restore.local`,只写目标连接: ```dotenv RESTORE_DATABASE_URL=postgresql://restore_role:自己的密码@目标主机:5432/独立空库 ``` 文件不提交。恢复命令只读取这个文件,忽略 shell 中的 `RESTORE_DATABASE_URL`、来源 `DATABASE_URL` 和开发环境回退。不要将完整连接写入命令参数;PostgreSQL 客户端接收连接环境字段,密码不进入参数列表,原始数据库错误也不输出。 ## 3. 恢复并核对 ```sh bun db:restore --from backups/snapshot-自己的编号 ``` 命令从数据库工作区解析相对路径,也可直接使用本机归档的绝对路径。按照备份命令实际输出的目录选择,避免猜测路径。 恢复顺序为归档 / 清单校验 → 来源与目标区分 → 空库检查 → 单事务恢复 → 表清单、行数与数据摘要核对。不会使用 `--clean` 或 `--create`。单事务执行依据[官方恢复说明](https://www.postgresql.org/docs/17/app-pgrestore.html);恢复完成后的数据核对另行执行,若核对失败,目标仍需隔离,不标为验收完成。 恢复不复制原对象所有者及授权,避免把原角色安排直接带入新环境。之后需按自己的角色策略重新授予应用所需权限,由实际恢复 / 迁移所有者执行 `db/scripts/grant-app-role.sql`,见[数据库角色与凭据](./database-roles);不能把恢复成功当作业务角色已具备权限。 ## 4. 接入服务前检查 先保持目标隔离,不直接启动任务消费者、邮件发送或支付维护。 1. 使用归档对应的模板版本核对迁移记录;新版本的增量迁移需另行检查,恢复命令不会自动升级。 2. 按文件元数据核对私有对象是否存在、长度与内容摘要是否对应;同步准备对象备份 / 恢复。数据库里的 `ready` 只说明历史状态,不证明目标桶现在有文件。 3. 核对新环境的 Hyperdrive、R2、队列、域名、邮件地址、供应商模式和 Secrets;这些不在数据库归档中。 4. 对恢复时间点之后的购买、退款、通知、计佣等外部事实另行对账,再决定何时恢复消费与定时维护,避免向用户报告过期结果。 5. 在独立测试环境走注册、上传、处理、预览与下载,核对私有访问、账本、失败返还和恢复后的重复请求。 如需数据库与文件对象共同恢复,应先确定停写 / 停消费安排及共同检查时间,再备份数据库和对象;本阶段未提供原子跨服务快照、自动对象复制或云端故障切换。真实服务、备份保存 / 删除策略、恢复耗时与费用继续按[部署验收](./deployment)记录。 ## 文件对象检查点 新增 `scripts/lib/file-backups.ts` 的对象归档 / 恢复函数,当前用于受控 R2 绑定与实际本地验收。它们没有新增公开下载端点、后台任意桶复制入口、R2 云端凭据或自动运行计划;`db:backup` / `db:restore` 仍只处理数据库。 操作顺序是先停止来源写入、任务消费和清理,再完成数据库归档;保持来源暂停,将归档恢复到隔离空库并通过表数据校验,从这份冻结数据中取得全部 `ready` 文件引用。按冻结引用读取源对象,验证长度、SHA-256 和所需元数据,全部通过才发布对象检查点。这不是数据库和 R2 的原子事务,暂停来源仍是操作者的前置安排,函数不会替操作者停服务。 对象目录位于数据库归档的私有快照目录内,使用独立唯一编号: | 内容 | 用途 | | --- | --- | | `file-manifest.json` | 关联数据库归档 SHA-256、冻结文件清单摘要、捕获时间、文件编号、对象键、长度、内容摘要与类型;它含私有对象键,需保持私有 | | `<文件编号>.blob` | 原始文件内容,文件名从有效编号派生,避免把对象路径当成本机路径 | 归档目录为 700、文件为 600。当前单文件上限与模板一致为 16 MiB,引用最多 100,000 项、清单最多 64 MiB;按文件逐个读取,不将整个文件集合载入内存。数据库归档中的 `externalObjectsIncluded: false` 保持原义,文件内容在独立对象目录中,不改写旧数据库清单。 对象恢复要求文件检查点与指定数据库归档、目标冻结引用全部一致。先检查每个本地文件,缺失、损坏或换错清单时不写目标;再要求独立空桶,条件写入避免覆盖并发出现的对象,逐个读回核对。必要的类型、文件编号和摘要元数据恢复后,原私有文件接口仍按本站账号与所有权读取。 目标已存在对象时拒绝,不清空或覆盖。写入期间失败可能留下本次部分对象,目标需继续隔离;没有自动删除、断点修复或跨服务回退。该归档仅覆盖冻结的 `ready` 文件,不复制孤立对象、未就绪对象或队列消息;历史任务与外部付款状态仍按原维护和对账规则处理。 条件写入与对象元数据依据[Cloudflare R2 官方说明](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/),核对日期 2026-10-11;实际云端与 S3 传输、对象保留 / 删除策略和运行成本继续待验。 ## 本机归档保留与清理 `db:backup:prune` 只维护指定本机目录,不读取数据库环境配置、不连接数据库或云服务,也不删除正在使用的 R2 对象。暂停该目录的备份、恢复、复制与其他清理操作,先查看预览: ```sh bun db:backup:prune --directory backups --keep 7 --older-than 30 ``` 路径从数据库工作区解析,可使用绝对路径。参数均需明确指定;`--keep` 至少为 1,`--older-than` 为正整数天数,以运行时间前的连续 24 小时计算。上例是用法,不是适用于所有产品的保留承诺。 规则按来源指纹和环境分别计算:始终保留每组最新的 7 份校验通过归档,以及完成时间在最近 30 天内的所有归档;只有同时超出数量且严格早于年龄界线的归档才列为 `DELETE`,恰好位于界线的仍保留。来源指纹沿用数据库归档的主机 / 端口 / 数据库,不包含密码。`KEEP`、`DELETE`、`SKIP` 分开显示,默认不修改任何文件。 只识别该目录直接下的 `snapshot-<标准唯一编号>`。逐一核对数据库清单、大小及完整 SHA-256,完成时间倒置或位于未来、损坏 / 缺失归档、符号链接、目录中的未知文件或未完成对象目录均跳过,不作为有效保留名额。其他目录与 `.partial-*` 留在原处,不打印这些未知条目的名称。 确认预览和保留安排后,再执行同样参数并追加 `--apply`: ```sh bun db:backup:prune --directory backups --keep 7 --older-than 30 --apply ``` 执行时重新校验保留与待删除的全组归档和文件列表;发现变化则在开始删除前停止。删除以整个快照目录为单位,同目录下配套的私有对象检查点也一起删除,不留下只能恢复数据库的半份本机归档。被保留快照不会被改写;再次运行不重复删除已移除快照。 命令没有跨进程锁,重新校验也不是文件系统事务;其他操作必须保持暂停。删除期间的权限 / 磁盘错误可能已经移除部分快照,命令报告完整移除数后停止,需重新检查;不提供恢复已删除归档或安全擦除介质的承诺。没有定时清理、云端生命周期设置、跨账户副本或远程保留策略。 清理校验不执行 `pg_restore`,也不认证对象检查点内容完整,更不证明支付事实已对账。保留数量不能代替近期实际恢复验收;在决定期限前,先确认至少一份所需版本的完整归档可以在独立目标恢复,并检查配套文件与外部事实。云端保留 / 删除及实际存储费用仍按 I15 继续验收。 ## 本地复验 ```sh bun db:validate-backups ``` 命令只接受回环 PostgreSQL 来源,创建自己的三个临时数据库,应用完整迁移并保存合成账号、文件、成功任务、积分账本与购买。真实备份期间并发写入,恢复后核对同一快照的全表摘要、积分 / 账本、迁移、序列、约束与外键;验收损坏归档、原库 / 已占用目标拒绝、私有文件权限、环境文件选择以及失败后的临时文件清理。实际恢复命令另在隔离目录执行,不创建正常项目的恢复环境文件。 验收结束删除本次数据库与归档,主预览不变。该命令没有恢复 R2 文件或调用供应商;云端完整恢复与 I16 的两站实际交付仍待验收。 另外运行文件与数据库共同恢复验收: ```sh bun files:validate-backups ``` 命令创建两个自有回环数据库和两个独立本地 Worker / R2 目录,使用实际注册、上传、报价确认、扣积分和队列执行;任务花费 3 积分,成功后从 20 变为 17。归档并恢复数据库及输入 / 结果对象后,验证原用户下载真实结果、匿名和另一用户拒绝,重复原请求 / 定时维护不再次执行或扣费,实际重启仍可下载。受控破坏源对象和归档文件会拒绝完整检查点或在目标写入前停止;实际 R2 条件写入保留已有对象。 文件验收不会修改正常预览配置或数据库,结束删除自己的数据库、对象目录、归档与会话。主预览仍为 0012;这不是任意云桶的备份命令,也不代表真实云端故障切换已验收。 本机保留规则的实际数据库验收: ```sh bun db:validate-retention ``` 创建两个自有回环数据库,源库完整迁移后生成三份真实 PostgreSQL 归档。命令预览不删除,非法策略拒绝;使用模拟的未来策略时钟让归档达到年龄条件,不改写真实捕获时间或归档内容。清理两份旧归档后,实际恢复保留的一份并核对全表摘要、迁移和 22 积分 / 账本;重复清理无新增删除。结束清理本次数据库与目录,正常主库和六项预览保留。这是本地清理验收,不把模拟年龄称为已保存多日的云端备份。 --- --- url: /docs/agentbuff-stack/auth-key-rotation.md --- # 认证密钥轮换 本教程对应 I15 的认证操作准备,当前已完成实际本地 Worker 重启、原账号与收费任务验收;真实云端仍待明确环境后验证。每站、每环境单独保存密钥,操作文件放在版本控制之外,公开页面与构建参数不放私钥。 ## 配置与默认行为 `BETTER_AUTH_SECRET` 仍必填,至少 32 字符。它既是旧格式认证加密的后备密钥,也用于模板自己的营销链接、通知接收人和投递目标摘要。 新增可选 `BETTER_AUTH_SECRETS`,采用按顺序排列的 `版本:密钥`,条目用逗号分隔。第一个版本用于新认证签名和加密,其余版本供库读取对应的旧加密数据。空值视为未配置,此时明确使用版本 0 和 `BETTER_AUTH_SECRET`;原旧格式密文仍由基础密钥读取,新的带版本密文使用版本 0。 版本是非负安全整数,0 可用,不接受前导零、重复版本或同值不同版本。最多十项,每个密钥为 32—1024 字符且不含空白;使用独立生成的随机值。环境检查仅报告字段与问题类别,诊断只显示版本号,不显示密钥值。直接传递显式配置,防止认证库另选进程中的密钥列表。 这项能力复用已安装 Better Auth 1.7.4 的版本配置,没有另外实现加密算法或密钥服务。[官方轮换说明](https://better-auth.com/docs/reference/security)限定于相同加密格式和用途:保留旧密钥用于解密,数据在后续写入时采用当前密钥。核对日期 2026-10-11。具体 Cookie 行为仍以本项目安装版本的实际验收为准。 ## 先区分轮换目的 正常切换当前认证密钥时,可保留此前密钥作为读取过渡,并保持基础密钥不变。这样模板自己的邮件选择、营销链接和投递摘要继续沿用原密钥。 旧密钥泄露后的撤销需要移除所有仍接受它的配置,包括版本列表与 `BETTER_AUTH_SECRET` 后备。保留已泄露密钥不能达到撤销目的。与此同时,历史密文、旧营销链接和通知选择可能失效,需先确定恢复 / 重新确认与退订处理安排。 ## 过渡阶段 以下仅示意格式,用自己的实际随机密钥替换括号内容,不将示例复制为可用凭据: ```dotenv BETTER_AUTH_SECRET=<现有基础密钥> BETTER_AUTH_SECRETS=1:<新随机密钥>,0:<现有基础密钥> ``` 0 是当前模板未配置版本列表时的版本;已有自定义版本列表的站点保留其实际旧编号和密钥,不改编号来冒充重写历史密文。先在独立测试环境核对配置,再让同一认证入口的实例使用一致的当前版本。 ```sh bun --env-file .env.staging.local scripts/config-check.ts --target staging ``` 真实 Worker 的秘密需由操作者在所选环境保存,电脑上的私有环境文件不会自动进入云端。轮换与部署不是原子切换,混合当前版本的实例可能相互拒绝 Cookie;实际发布仍按[部署教程](./deployment)明确范围执行。 **本版本实测:切换第一个认证密钥后,原登录 Cookie 返回未登录,原密码重新登录成功。** 保留旧版本并不保证已有会话连续可用。重新登录后的账号、积分、原文件和收费任务继续使用原数据库记录;轮换不会自动执行任务、再次扣费或提高账号权限。正在进行的认证 / OAuth 回跳需重新开始,真实第三方回跳另行验收。 ## 撤销旧密钥 先核对仍依赖旧版本或旧格式的加密数据与待完成认证流程。库只在相应数据再次写入时采用新密钥,模板没有批量重写历史密文的操作;从配置删除旧版本不代表数据已迁移。不可读的历史密文不能靠恢复同一数据库备份自动变为新密钥密文。 确认所需数据可用及基础用途处理方式后,示意配置为: ```dotenv BETTER_AUTH_SECRET=<新基础密钥> BETTER_AUTH_SECRETS=1:<新随机密钥> ``` 新基础密钥可与当前认证密钥相同,亦可独立安排;在新配置内检查各自用途,确保旧密钥确实不再被接受。只删除版本 0 却保留原基础密钥,仍可能通过旧格式后备读取旧密文。 | 用途 | 当前边界与处理 | | --- | --- | | 认证 Cookie | 已用当前密钥重新登录的 Cookie 在只删除旧版本、当前密钥不变时继续有效;仅基础密钥变化不等于撤销全部会话 | | 历史加密内容 | 旧版本 / 旧基础密钥移除后相应内容可能无法解密;需保留读取能力或先完成实际重写 / 重新认证验收 | | 任务邮件选择 | 基础密钥变化后旧接收人摘要不再匹配,原账号需在设置中明确重新确认;不能自动假定仍可发送 | | 站主通知 | 基础密钥变化会改变目标摘要,待发送记录按原规则复核 / 停止;实际 Discord 目标与投递仍须验收 | | 营销确认与退订链接 | 由基础密钥签名,认证版本列表不参与验证。基础密钥变化后旧链接失效,需安排有效退订通道和站主处理;模板没有批量重签历史链接命令 | | 用户 API Key 与外部服务 | 使用各自的签发 / 撤销和供应商密钥,不把认证密钥轮换当成它们已全部撤销 | | 数据库、R2、队列与权限 | 独立核对凭据、绑定、访问角色和历史状态;本教程不会替操作者修改云资源或数据库角色 | 营销同意、订阅状态与签名链接不同;链接失效不会自动将联系人退订,也不会证明供应商已停止投递。轮换期间保持可用的退订与联系处理方式,原供应商同步和管理员恢复按[营销教程](./marketing-subscriptions)处理。 ## 本地复验与证据 ```sh bun auth:validate-rotation ``` 命令只接受回环 PostgreSQL,创建自己的完整迁移数据库和隔离 Worker / R2 目录。实际注册、上传、报价确认、扣 3 积分并完成任务;销毁 / 重启 Worker 两次,验证当前密钥切换后的旧 Cookie 拒绝、密码重新登录、旧密钥撤销后当前 Cookie 继续访问原私有结果。账户始终为普通用户,余额 17、原任务编号和一次尝试保持,重复请求不再次扣费。 任务邮件使用合成的邮箱资格记录和实际明确选择,未验证外部邮箱。过渡期选择有效,基础密钥撤销后有效选择变为关闭,再通过原设置接口重新确认。实际配置诊断同时验收合法 / 非法列表与输出不含秘密;认证库的旧格式 / 版本密文、当前写入和撤销拒绝另由回归测试验证。 结束删除本次数据库、资源目录与会话,不修改正常主预览的密钥或账号。真实 Cloudflare 发布、OAuth / Passkey 回跳、历史加密记录处理、外部投递和访问权限仍需在明确的测试环境验收,完整 I0—I16 目标继续保留。 --- --- url: /docs/agentbuff-stack/database-roles.md --- # 数据库角色与凭据变更 ## 当前能力 I15 已在独立的 PostgreSQL 17 密码认证实例中验收应用角色、环境连接权限与密码变更,再用真实本地 Worker 完成注册、上传、任务、扣费和私有下载。验收使用两个一次性数据库,不修改正常预览的角色、密码、授权或 0012 迁移;真实 Neon / Hyperdrive 连接池仍待指定环境验收。 ## 三种角色分开安排 | 身份 | 用途 | 权限与配置 | | --- | --- | --- | | 数据库迁移所有者 | 建表、增量迁移、备份和独立恢复 | CLI 的管理连接;本教程授权脚本要求实际 `current_user` 是目标数据库、公共模式及其中表 / 序列的所有者 | | 数据库应用角色 | API 经 Hyperdrive 日常读写 | 每个站、每个环境独立登录账号;只授予公共模式的业务读写与序列使用 | | 网站用户 / 管理员 | 登录、查看自己的文件、站内管理 | 数据库 `user.role` 和接口鉴权;与数据库登录账号分开 | 应用角色是服务端凭据,能读写公共模式的业务表。文件与任务的用户隔离由原接口鉴权实现,本脚本不提供数据库逐用户行隔离,也不会把网站普通用户提升为管理员。 ## 1. 在指定环境授予应用权限 先审查并应用该环境需要的增量迁移,再执行 `db/scripts/grant-app-role.sql`。脚本不会创建数据库或应用迁移,正常启动、发布与生成新站也不会自动执行它。 从仓库根目录运行,连接字段替换为自己已确认的环境;管理密码放在权限为 600 的私有 `PGPASSFILE` 中。按实际服务配置 TLS,避免沿用上一环境的连接字段: ```sh export PGHOST='自己的数据库主机' export PGPORT='5432' export PGDATABASE='自己的测试数据库' export PGUSER='实际迁移所有者' export PGPASSFILE='/自己的私有目录/数据库密码文件' psql --no-psqlrc --no-password \ --set role=app_staging \ --file db/scripts/grant-app-role.sql ``` 首次创建时 `psql` 会请求新应用密码并确认,不把密码写进命令参数或 SQL 文本。**不要使用旧写法 `-v password=...`**,脚本会拒绝。已有角色重复授权不会修改密码;密码变更在下节单独处理。这一方式依据 [PostgreSQL 的 `\password` 说明](https://www.postgresql.org/docs/17/app-psql.html),核对日期为 2026-10-11。 脚本先检查实际所有者和已有角色,拒绝继承其他所有者权限后代运行、已拥有对象的账号、特权属性、角色成员关系、额外直接授权及不合规默认授权。公共表 / 序列分属不同所有者也会拒绝,避免 PostgreSQL 仅警告而跳过授权。直接函数授权也需另行审查,不能作为普通应用角色自动接入。 创建角色、密码设置、当前授权与默认授权都在同一事务中;中途失败回滚。本地受控验收已在最后的默认授权处制造失败,确认新角色未留下,原授权不变。脚本不会偷偷撤销已有角色的其他直接权限;需要额外权限的角色应由操作者单独处理。 ## 2. 核对实际权限 | 操作 | 本地实际结果 | | --- | --- | | 公共业务表查询、插入、更新、删除 | 允许;真实账号、文件、任务与积分流程通过 | | 同一迁移所有者之后新增的公共表与序列 | 默认授权生效,插入及 `nextval` 可用 | | 建表、建模式、临时表、修改或删除表 | 拒绝 | | `TRUNCATE`、序列 `setval` | 拒绝;清理数据应走原业务入口 | | 读取 `drizzle.__drizzle_migrations` | 拒绝;迁移核查使用管理连接 | | 另一环境的应用账号连接已加固的目标库 | 拒绝 | 脚本撤销**所选数据库**对 `PUBLIC` 的连接 / 临时表权限,以及所选库公共模式的公开建表权限,再授予指定账号连接与业务权限。测试库和正式库需分别执行;不自动调整其他库、其他账号或自定义公开授权。全局对象所有权和数据库直接授权会检查,但其他数据库内部的模式、函数与表授权不在本次检查范围。不要据此声称整个集群已完全隔离。 授权返回成功后,仍要用应用账号新建连接并走完整业务流程,确认认证、默认权限和环境绑定。恢复归档不带原所有者与授权,重新安排权限后执行同样的检查,见[备份与恢复](./backup-restore)。 ## 3. 更换密码与处理旧连接 用已确认环境的所有者连接执行: ```sh psql --no-psqlrc --no-password --command '\password app_staging' ``` 交互输入新密码后,更新所选环境缓存和无缓存两个 Hyperdrive 的源凭据。部署使用 Terraform 管理这两项资源,实际变更需遵循该环境的资源管理流程,不能只改电脑的 `DATABASE_URL`。 本地实测:旧密码的新连接被拒绝,新密码的新连接可用,**修改密码不会断开已经建立的连接**。撤销 `CONNECT` 同样只影响新连接。验收记录同一连接的后端编号,在修改密码及撤销连接权限后仍能查询;只有明确终止该自有测试连接后才断开。真实环境需先安排业务暂停、在途任务和连接更新,再决定是否终止旧连接;本教程不提供跨环境批量终止操作。 Cloudflare 也明确说明,更新 Hyperdrive 配置不会自动清除缓存或关闭原连接池;新连接采用新配置,立即替换已有连接需另行重启连接池。也可创建新配置并切换 Worker,但模板的资源管理与回滚安排需一起核对。[官方凭据变更说明](https://developers.cloudflare.com/hyperdrive/configuration/rotate-credentials/),核对日期为 2026-10-11;这不是本项目已完成云端连接池验收的证明。 更换后检查原用户登录、原私有结果下载、任务重复提交、积分账本及队列消费。本地在更新 Worker 凭据后验证了原结果可下载,同一任务仍原编号、一次尝试、17 积分,无再次扣费。数据库密码变更不自动撤销网站登录会话;[认证密钥轮换](./auth-key-rotation)是另一项维护。 ## 本地复验 准备 `initdb`、`pg_ctl`、`psql` 并加入 PATH,从仓库根目录运行: ```sh bun db:validate-app-role ``` 命令自建仅回环监听、SCRAM 密码认证的 PostgreSQL 实例,应用两份完整迁移,验证权限、错误密码、环境连接隔离、额外授权拒绝、事务回滚及真实 Worker 流程。随后更改自有角色密码、处理自有连接、重启隔离 Worker,核对原下载和扣费去重,再停止并清理自己的实例与目录。它不读取管理连接去修改正常预览或远程角色,也不向真实供应商发送请求。日志只报告阶段和结论;私有本地诊断会隐藏生成的密码与密码校验值。 --- --- url: /docs/agentbuff-stack/new-site.md --- # 用这套模板建立下一个英文工具站 ## 1. 先确定可交付任务 记录真实搜索/社区证据,明确输入、处理、预览和输出。示例 JSON/文本工具是模板验收样例,不是新词推荐。目标市场默认英文美国、英国、加拿大、澳大利亚。 ## 2. 复制源代码 需要交付一份独立源码归档时,使用[开发交付包](./distribution):从已提交版本生成产品,附文件摘要、来源提交与依赖声明。直接复制仍使用下面的原命令。 运行 `bun run site:create /absolute/path/my-tool --name "My Tool" --domain https://my-tool.com --port 4700`。不复制密钥、Git 历史、本地数据、依赖和构建产物。生成后依然需要在每个站安装依赖、配置独立数据库和服务。官网与案例不会复制进产品;来源版本记录在 `template.json`,见[升级说明](./template-upgrades)。 工厂按绑定和环境重新命名 R2 桶、预览桶、业务 / 死信队列,并同步死信消费者及 `TASK_DEAD_QUEUE_NAME`,生产 Hyperdrive / Cloudflare 账号 ID 改为待替换占位,不沿用源站资源。即使源站手工配置了不带品牌前缀的名字也会重置;本地远程资源开关被关闭。当前未支持的其他资源绑定会明确拒绝生成,需要先增加对应的隔离规则。本地只读取 `DATABASE_URL`,不再生成旧 Hyperdrive 连接覆盖变量。 公开地址也按配置环境重新生成:上述新站的正式入口为 `my-tool.com`,测试入口为 `staging.my-tool.com`,开发入口为本地端口。源产品站使用任意命名的测试域名,都不会复制成新站的正式域名;这项隔离来自 Wrangler 环境,不能靠源域名的前缀猜测。源站若使用非自定义域名的路由规则,生成器会明确拒绝,需先确认其路由设计。 修改 `packages/core/website.ts`:品牌、说明、联系方式、欢迎积分、历史上限、计划价格。当前 Free/Starter/Pro 的计划 ID 与 Stripe 集成绑定;改名字/价格可以只改配置,增删计划需同时改 API 计费和后台入口。配置价格必须与 Stripe Price 实际金额一致。生成器会重置名称、`brand`、OG 图片、社交链接和联系邮箱,并在新目录 `/brand/` 生成自己的 SVG 图标;详见[品牌素材](./brand-assets)。业务文案和文章仍需自行替换。生成器会清空 `creditPacks` 并关闭积分售卖;新站必须配置自己的目录、Price ID 和独立 webhook secret,不能沿用源站商品。 `PUBLIC_SITE_URL` 控制 canonical、sitemap、RSS 的公开域名,`APP_ORIGIN` 控制 API/登录信任来源。生产时两者都设为同一实际域名。本地执行 `bun preview:build` 会按 `preview.config.json` 构建产品 origin;生产构建使用实际域名。工厂可预设未来域名,配置诊断会提示与本地 origin 的差异。 模板默认启用英文与简体中文。开启第二语言前检查 `i18n.translations` 和实际字典;生成器保留样例业务文案,修改产品时应同步调整启用语言。当前公开页范围与尚未翻译入口见[产品语言](./languages)。 ## 3. 接入工具 新站的 Crisp 客服默认关闭,公开 Website ID 重置为 `null`,原支持邮箱清空。需要聊天时填写自己工作区的 ID 与真实邮箱;只需要邮件联系时保持聊天关闭,见[客服教程](./support-chat)。 新站的 Rewardful 联盟后台核查也默认关闭,推广计划 / 商户账户 ID 重置为 `null`,不复制服务密钥。启用前使用自己环境的完整配置并迁移至 0033;个人 / 团队推荐绑定后端已本地验收,公开采集与个人 / 团队设置已本地验收,具体订单 / 佣金联查及管理证据与带原因 / 审计的安全人工复核已受控本地验收,真实购买归因继续验证,见[联盟营销教程](./affiliates)。 `packages/core/tools.ts` 定义每个工具的 ID、标题、说明、样例、步骤、FAQ、下载类型以及 `runTool` 处理器。 * 增加定义和真正处理器后,工具目录、详情页和 sitemap 会随构建生成。 * API 的工具 ID 校验从同一目录生成,不另外维护一份名单。 * 当前 ToolWorkbench 适用于文本输入与文本输出,同一处理器在浏览器/服务器执行。 * 图片/PDF/视频或模型 API 需要实现对应输入校验与结果预览;不应把第三方密钥放进 core 或客户端。 * 服务端收费生成需要任务、文件存储、限流和退款链路。当前已有私有文件、异步任务、收费扣返、预览下载与失败恢复的本地验收;真实模型、外部支付和云端完整交付仍按计划验收,不能只改文案就声称某项业务支持。 ## 4. 写页面和文章 英文文章在 `apps/web/content/blog/*.md`,中文译文在 `apps/web/content/blog/zh/`,可选西班牙语译文在 `apps/web/content/blog/es/`,用相同文件名配对。Frontmatter:title、description、date、author、category;可选 updated、tool、draft。draft: true 会从详情、列表、RSS、sitemap 全部排除;英文草稿的译文也不发布。缺中文译文时不混入英文列表,不创建虚假中文详情;可选西语仍使用明确标注的英文回退。作者署名和样例正文保留,复制后应逐项核对,见[产品语言](./languages)。 发布与任务直接相关、可复现的使用教程和输出限制。不要批量建立功能相同、文案近似的关键词页。隐私、条款和联系方式目前是占位,必须替换。首页的演示说明与样例文案也需随真实产品一起更换。 ## 5. 启动与验证 本地 PostgreSQL 准备好后,用 `.env.example` 创建 `.env.local`。生成唯一 auth secret,再执行: ```sh bun install bun run config:check bun db:migrate bun preview:build bun preview:start ``` 打开生成器报告的产品地址,例如 localhost:4700。按真实输入走一遍粘贴/文件 → 处理 → 检查 → 下载。测试注册登录、Save to account、History、不同账号隔离。所有本地邮件只写 `.local/outbox.jsonl`。 修改 API 后用 `bun preview:stop` 再 `bun preview:start`;修改公开页或 App 后重新 `bun preview:build` 并刷新。端口统一位于 `preview.config.json`。`--port 4700` 设置公开入口并派生内部服务端口;第二个产品可选择 `--port 5700`。不传端口时按站点 ID 生成默认端口,启动器仍会检查占用并拒绝接管其他站的服务。 不同项目的会话 Cookie 与登录提示 Cookie 使用各自的站点 ID;更换端口本身不会隔离 Cookie。每个产品仍需配置独立数据库与认证 secret。 ## 6. 配置生产服务 生产目标为 Cloudflare 三个 Worker(web/app/api)+ 服务绑定 + Neon PostgreSQL/Hyperdrive。修改 `apps/*/wrangler.jsonc` 中域名、服务名、Hyperdrive ID 和环境变量;模板里的 example.com 与 Hyperdrive 占位不是有效配置。 通过 Wrangler secret 配置真实密钥,勿提交到 Git。设置环境为 production,配置并验收 Resend 和 Google / GitHub(如果启用)。Stripe 四项必须完整配置;同一站的产品价格与配置对应,验证 webhook/订阅取消/portal/失败回跳。 生成器排除备份目录、归档文件与恢复环境文件,不复制旧站的私有数据。生成器会重置资源编号并为各环境重命名存储 / 队列;云端 `STORAGE` 与 Terraform 的 `{站点编号}-{环境}-uploads` 一致,资源尚未自动创建。先运行 `bun deploy:check --target staging` 检查资源关系,补真实资源与 Secrets,再按[部署教程](./deployment)核对目标构建产物和真实服务。文件型任务的业务限制已接原任务入口;真实模型及 Cloudflare、Neon、Stripe / 发信仍待验收。本地启动成功不代表上线验收完成。 ## 出售模板前 至少用两个真实站验证配置复用、部署文档、升级和数据迁移。保留 MIT 原版权声明,并核对新引入依赖/素材的授权。不要将演示积分账本描述为已经完成积分包售卖。 --- --- url: /docs/agentbuff-stack/distribution.md --- # 开发交付包 这是供自用验证的源码交付包,复用[新站生成器](./new-site)。当前仍为模板 0.3,交付状态为开发阶段;两个真实独立产品、真实服务和生产运行验收仍按 I16 原计划推进。 ## 从已提交代码打包 在模板或生成产品的 Git 仓库根目录运行。需要 Bun、Git 和 tar,工作区中应提交的变更必须先提交;忽略的本地配置不会进入交付包。 ```sh bun site:package .local/distribution/my-tool --name "My Tool" --domain https://my-tool.example --port 6700 ``` 输出目录必须不存在。失败时只清理本次新建的输出目录,已有交付目录不会被覆盖。打包从当前提交的 Git 归档读取源码,再交给原生成器重置站点名称、品牌、资源标识与本地端口;不执行安装、数据库迁移或部署。 目录中有两个文件: | 文件 | 用途 | | --- | --- | | `product.tar.gz` | 一个 `project/` 目录,包含独立产品源码、配置示例、中文教程与保留的许可证 | | `manifest.json` | 最后写入的完成清单:来源提交、模板来源、产品身份、归档和每个源码文件的字节数 / SHA-256,以及各工作区直接声明的依赖 | 官网源码与案例、Git 历史、私有环境文件、本地数据、邮件捕获、依赖目录和构建产物均不分发。生成器过滤规则统一维护,打包脚本不再维护另一份产品复制清单。交付源码中的符号链接及控制字符文件名会拒绝。 `sourceRevision` 表示本次打包读取的确切提交。`template.revision` 表示产品沿用的模板来源;产品经过自己的提交后,两者可以不同。生成时间会变化,每次归档也可能不同,清单用于校验收到的这一份交付包。 ## 校验与安装 保留交付包和清单在同一目录,在模板仓库执行: ```sh bun site:package .local/distribution/my-tool --verify ``` 此命令只检查清单中的归档文件名、字节数和 SHA-256,不解压或运行源码。它用于发现文件损坏,不能认证作者身份,也不会逐个比对归档内文件;逐文件摘要可用于解压后的单独核查。 在新的空目录解压,随后进入 `project/`: ```sh tar -xzf /absolute/path/my-tool/product.tar.gz cd project bun install ``` 首次安装要使用 `bun install`:生成器移除了官网工作区,安装会据本产品工作区更新 `bun.lock`。建立产品自己的 Git 仓库并提交更新后的锁文件,之后才使用 `bun install --frozen-lockfile`。仓库不随包分发,不会继承原站远程地址。 按 `README.md` 和[复制新站](./new-site)配置独立数据库、新认证密钥与资源。敏感值仅放 `.env.local` 或对应环境的 Secrets。未启用的外部模块保持关闭;示例域名、资源编号和服务变量需要分别核对。 ```sh bun config:check bun preview:build bun preview:start ``` 本地预览需要先完成该站的数据库准备;安装与打包不会替你执行迁移。生产服务按[部署教程](./deployment)配置并独立验收。 ## 依赖与许可记录 `workspaces` 记录交付源码中各 `package.json` 的直接依赖声明,包括开发、可选和同伴依赖。版本范围原样保存;这不是安装后的准确版本清单、全部传递依赖清单或完整许可证审计。实际解析以本产品安装后提交的 `bun.lock` 为准。 安装完成后,在本产品仓库导出两种范围的依赖报告。输出文件必须不存在,旧报告不会被覆盖: ```sh bun site:dependencies .local/dependencies-all.json bun site:dependencies .local/dependencies-production.json --production ``` 此入口调用 Bun 自带的 `pm licenses --json`,记录其返回的包名、准确版本、许可标签与分组,并移除报告中的本机安装路径、作者、描述和主页。报告另含当前 `bun.lock` 的 SHA-256、Bun 版本、系统 / 架构、时间与范围;读取过程中锁文件改变会拒绝导出。它不安装依赖、不改锁文件,也不执行包脚本、迁移或部署。 `all` 包含开发和构建依赖,`production` 是 Bun 的生产依赖图,两者都不是最终 Worker 或浏览器构建产物的组成清单。报告依赖当前安装及平台条件,不能用本机结果代替其他平台;先完成安装并提交本产品的锁文件,报告摘要应与这份锁文件对应。未安装的产品不会自动补装。 `reportedLicense` 是 Bun 返回的标签,缺字段保留为 `null`,`Unknown` 也原样保留,不能据此判断授权。此次本地样本中 `khroma@2.1.0` 报为 `Unknown`:其 `package.json` 没有许可字段,但随包的小写 `license` 文件标明 MIT。核查时要读实际原文、保留适用声明,并区分源码、二进制和素材;不能直接把所有依赖统一标为 MIT,也不能把包元数据缺失当作没有许可。 导出的报告可以作为同版本交付的附加核查资料。它是在产品安装后生成的独立文件,不属于原压缩包摘要清单;也不会自动扩写 `THIRD-PARTY-NOTICES.md` 或改变未知标签。 `LICENSE` 和 `THIRD-PARTY-NOTICES.md` 保留原内容,清单记录它们的摘要。第三方说明覆盖现有上游代码、设计系统、字体及补丁来源;新增代码、素材和供应商需继续核对其来源与条款,不能用依赖声明替代这一步。 维护升级按[模板版本与升级](./template-upgrades)操作,核对产品自定义代码、完整迁移历史与自己的备份。交付包不包含旧模板的 Git 历史,历史升级演练需要在拥有基线提交的模板仓库运行。 ## 常见问题 | 现象 | 处理 | | --- | --- | | 提示工作区未提交 | 提交需要交付的代码;不把真实凭据提交进仓库 | | 提示输出目录已存在 | 选择新的交付目录;保留旧包用于核对来源 | | 字节数或摘要不符 | 重新取得原包和对应清单,再校验;不要直接继续安装 | | 首次冻结安装失败 | 使用普通安装更新生成产品的锁文件,再提交本产品的锁文件 | | 原账号或原文件没有出现 | 新产品使用独立数据库,本包不复制旧站数据;已有站升级使用升级教程 | 本地验收覆盖实际归档 / 解压、逐文件摘要、许可证原文、私有文件排除、再次生成,以及破损归档、已有目录、未提交源码和源码符号链接的拒绝。从已提交版本生成的实际产品包已通过首次安装、冻结锁文件再安装、离线配置检查,以及公开页、工作台、邮件和中文文档构建;没有执行数据库迁移或云部署。销售价格、授权方案、购买流程、支持范围和正式发布另行确定。 --- --- url: /docs/agentbuff-stack/website-showcase.md --- # 模板官网、案例与独立演示 ## 三个职责 `apps/website` 是介绍 AgentBuff Stack 的静态官网;`apps/web`、`apps/app` 与 `apps/api` 是可生成的产品底座;案例对应独立域名上的产品。生成新站不会复制官网、官网文章、案例素材或销售配置。 官网不读取用户订阅套餐,不连接产品数据库。当前尚处于自用验证阶段,定价页没有商业报价或购买流程。现有 JSON 与文本工具是独立演示中的样例,不作为已上线案例。 ## 配置官网 官网文案与导航位于 `apps/website/config/site.ts`,功能与验收状态位于 `apps/website/lib/modules.ts`。只记录已实现的能力;外部服务尚未实测时继续标明待验收。 环境配置为 `WEBSITE_ORIGIN`、`WEBSITE_DOCS_URL`、`WEBSITE_DEMO_URL`,分别控制官网公开域名、教程入口和独立演示地址。公开地址必须使用 HTTPS,不能指向 localhost;未配置的教程或演示入口会隐藏。本地预览仍使用原端口。 本仓库发布范围是 `stack.agentbuff.dev` 静态官网与 `/docs/` 下的双语教程,暂时隐藏演示。自定义域名声明在 `apps/website/wrangler.jsonc`,不属于产品三个 Worker 的测试环境。官网无需 Neon、Hyperdrive、任务队列或模型服务;产品仍暂不上线。 复制 `.env.website.example` 为 `.env.website.local`: ```dotenv WEBSITE_ORIGIN=https://stack.agentbuff.dev WEBSITE_DOCS_URL=https://stack.agentbuff.dev/docs/agentbuff-stack/ WEBSITE_DEMO_URL= ``` 运行 `bun website:package`,会先核对域名与教程入口,再在新的 `.local/website-release-*` 目录构建官网与双语教程,生成专用 `wrangler.jsonc` 和发布清单。官网与教程共用根站点地图索引;教程资源、搜索、语言切换与规范链接均带 `/docs/` 前缀。该命令不发布,也不覆盖本地预览产物。清单包含源码版本和构建时是否有未提交修改,保存在静态资源目录外。 检查产物后,使用输出中的配置路径运行 `bunx wrangler deploy --dry-run --config <发布目录>/wrangler.jsonc`;离线打包通过不代表上线。完成 Cloudflare 登录并确认目标域名权限后,运行 `bunx wrangler deploy --config <发布目录>/wrangler.jsonc` 上传已检查的同一份产物。`bun website:deploy` 会重新生成发布包并上传,需先准备账号权限;这两个官网入口都不迁移数据库、不部署产品 Worker。 `bun preview:build` 根据 `preview.config.json` 构建本地官网和演示。本仓库官网为 `http://localhost:4400`,产品演示为 `http://localhost:4410`,教程为 `http://localhost:4406`。生成项目只有自己的产品预览,不包含官网、官网发布脚本或专用环境文件。 单独官网构建命令是 `bun website:build`,检查命令是 `bun website:check`。公开官网与教程合并发布应使用 `website:package` 的产物;现有 `deploy:staging/production` 仍只部署产品三个 Worker。 ## 添加真实案例 在 `apps/website/content/showcase/` 添加小写文件名,如 `my-product.md`。文件名生成案例路径 `/showcase/my-product`。截图放在 `apps/website/public/showcase/my-product.webp`。 以下是字段示例,不是已上线案例;确认网站真实上线并放入实际截图后,才能把状态改为 `live`。 ```yaml --- name: My Product summary: Describe the task and its deliverable. siteUrl: https://your-real-product.com screenshot: /showcase/my-product.webp screenshotAlt: Describe the actual product screenshot. category: File conversion modules: - accounts - content templateVersion: 0.3.0 templateRevision: deb7b185 publishedAt: "2026-10-09" status: draft --- ``` 正文描述具体任务、输入、预览、输出、复用模块与自定义部分。版本与 revision 从独立产品的 `template.json` 读取,示例值必须更换为真实记录。 状态包括 `draft`(草稿)、`preview`(本地记录)、`live`(已上线)。只有 `live` 进入首页、案例列表、公开详情和 sitemap。预览记录也不作为线上案例发布。所有记录仍需完整字段和存在的截图,避免未来发布时才发现内容损坏。 首页、列表、详情与 sitemap 消费同一份已发布记录。URL、日期、图片字段或截图文件无效时构建失败,错误包含对应字段或记录。尚无上线案例时显示说明,仅在演示地址已配置时显示演示入口。 流量、收入不是案例必填项。补充时记录数据来源、日期、统计期间和全站或页面口径,不从报价页推断收入。 ## 官网文章与产品文章 官网文章在 `apps/website/content/blog/*.md`,写建站实践、架构决策和案例拆解。字段为 title、description、date、author、category,可选 updated 与 draft。草稿不生成公开详情、列表、RSS 或 sitemap。 产品文章仍位于 `apps/web/content/blog/*.md`,面向产品用户。两套文章分别生成自己的 canonical、RSS 和 sitemap,不共享文章清单。两套应用复用展示卡片与设计变量。 ## 公共界面边界 `packages/ui/marketing` 提供导航、主题脚本、文章卡片及公共排版。导航接收品牌、链接、主操作与可选次操作,不读取 `core`、用户会话或固定工作台路径。各应用负责文案、内容加载和页面组合。 修改导航后检查浅深主题、滚动收缩、手机菜单、键盘退出与外链。修改产品认证和授权则运行对应回归测试。 --- --- url: /docs/agentbuff-stack/template-upgrades.md --- # 模板版本与升级 ## 记录来源 模板源根目录的 `template.json` 记录名称和版本。生成项目增加来源 revision、生成时间和源目录是否包含未提交修改。 从已经生成的产品再次生成时,保留原模板来源 revision,更新新产品身份与生成时间。它不会把上一站的 Git HEAD 假装成模板版本。无 Git 来源且没有已有 revision 时记录为空,不编造提交。 用于正式产品时优先从明确版本的干净模板提交生成。未提交修改标记帮助识别与对应 revision 不完全一致的副本。 ## 新站生成范围 生成器明确复制 web、app、api、email、共享包、db、infra、中文教程、产品脚本和必要根配置。官网应用、案例资产、官网博客、改造计划、原 Git 历史与原自动发布工作流不进入副本。 凭据、环境私有文件、邮件记录、缓存、依赖和构建产物被排除。生成器只写入非秘密默认环境;认证 secret 和实际服务凭据由每个产品独立配置。 首次执行 `bun install` 更新新项目工作区成员对应的锁文件,提交该产品自己的锁文件后可使用 `--frozen-lockfile`。不要沿用包含官网工作区的源锁文件直接声称副本已经通过干净安装。 ## 升级现有产品 1. 读取产品 `template.json` 与当前模板变更说明,确认来源和差异。 2. 在产品自己的分支中应用与所用模块相关的修改。 3. 保留产品处理器、文章、品牌、套餐和环境配置。 4. 如果改动数据结构,先审查迁移,再按对应环境安排执行;生成器不会自动执行迁移。 5. 运行受影响的构建、类型与行为验证,确认后更新来源记录。 生成器拒绝覆盖已有目标目录,不会自动刷新一个已经自定义的产品。当前没有自动合并产品代码、站群后台或一键线上部署能力。 ## 本地旧站升级与代码回退演练 I15 提供 `db:validate-upgrades`,验收已经发布的 0012 版本到当前增量历史与 Worker,再切回旧 Worker。命令仅使用一次性回环数据库、独立 Queue / R2 和旧代码归档;需要能创建 / 删除测试库的本机 PostgreSQL 维护连接。开发连接只用于创建 / 清理自有测试库与核对主库迁移数,不升级主预览。不要将该命令当作真实产品的迁移或发布入口。 在包含历史提交的模板源仓库执行: ```sh bun db:validate-upgrades 3e6dd9df578e2f927a5493a2b06f1dbcc14f8293 ``` 参数必须是本地存在的 Git 提交;当前流程要求该版本包含到 `0012_public_invisible_woman` 为止的 13 条历史。命令比较旧 SQL 及历史条目和当前前缀,拒绝重写历史,然后通过 `git archive` 读取旧代码、按旧锁文件安装依赖并构建邮件。安装忽略依赖生命周期脚本,不修改源仓库或源锁文件。生成站没有原 Git 历史,不能直接套用这个提交;需在保留对应历史的源仓库执行,或选择符合前提的本地历史提交。缺少旧版本时失败,不用当前代码假装旧版本。 演练流程: 1. 旧版实际 Worker 注册普通账号,上传文本,通过报价确认扣 3 积分,Queue 处理后下载原私有结果。 2. 停止 Worker,用旧版原任务存储入口提交第二个带报价的收费任务,但不发送队列。这是“数据库已提交、尚未投递”的明确故障边界,余额为 14;没有伪造模型调用或真实外部付款。 3. 比较旧公共表的全部原字段、行数及数据摘要,并保存原迁移编号 / 哈希 / 时间。仅在临时迁移副本的最后 SQL 注入故障,实际确认整批增量回滚、原数据与历史保留、新增表及索引不存在。 4. 应用未修改的完整当前历史;原字段与记录不变,新字段 / 表通过各自迁移增加。重复执行不重复应用迁移;内部迁移序列可能因 PostgreSQL 回滚产生编号间隔,不以连续编号判断成功。 5. 启动新版 Worker,原 Cookie 下载旧文件、重复提交返回原任务;维护入口补投第二个任务,真实 Queue / R2 成功交付,保持 14 积分、两条预扣记录和每任务一次执行。 6. 切回旧 Worker,在扩展后的数据库上下载两份原结果、重放原请求;核对原业务字段保持。正常请求会推进限流计数,因此运行阶段不把计数不变当作业务兼容条件;停机迁移阶段仍核对该表全部原字段。 结束时清理旧版本归档、依赖、数据库和本地资源。输出不含连接、密码、原文件内容或完整数据库错误;失败标明阶段及安全错误编号,主库和源配置另行核对。 **边界:** 本次已验证 0012 基线、默认关闭的可选模块、定价为 3 积分的文本示例和固定本地样本。旧付款 / 联盟存量、所有历史版本、产品自定义处理器、真实云端混合版本、负载及外部服务需分别验收。此流程没有自动合并产品代码、更新 `template.json` 来源或宣布 I15 / I16 完成。 --- --- url: /docs/agentbuff-stack/design-system.md --- # AgentBuff Stack 界面复用指南 本地组件预览:。该页有 `noindex`,不进入 sitemap。公开页、登录页和工作台共享纯白浅色底、炭黑深色主题、轻磨砂表面与 Figtree 无衬线排版;字体在本站提供,不向 Google Fonts 请求。 ## 修改一个新站的外观 * 品牌名称、文案和业务配置:`packages/core/website.ts`。 * 颜色、字体、圆角和浅深主题:`packages/ui/design/tokens.css`。 * 共享 HTML 视觉模式:`packages/ui/design/patterns.css`。 * React 组件和 StyleX 布局:`packages/ui/design/index.tsx`。 * 营销站布局:`apps/web/layouts/BaseLayout.astro` 和 `apps/web/styles/globals.css`。 * 工作台导航:`apps/app/components/layout/`;后台排版:`apps/app/styles/globals.css`。 `--ui-*` 是品牌语义变量。Astryx 的 `--color-*`、现有 Radix/shadcn 的颜色变量都映射到它们;不要在页面另写一套颜色。新品牌至少同时修改 `--ui-brand`、`--ui-brand-hover` 和 `--ui-on-brand` 的浅深两组值,并检查文字对比度。 磨砂表面使用 `--ui-frost` 和轻量背景模糊;`--ui-surface-tint` 提供柔和表面层次。定价重点卡片和底部引导区使用 `--ui-contrast`、`--ui-on-contrast`、`--ui-contrast-muted` 与 `--ui-contrast-border`,在浅深主题下自动反转黑白关系。背景的细颗粒纹理为静态样式,不覆盖交互控件。 改变 `--theme-color` 时,还需同步 `apps/app/index.html`、`apps/app/public/site.manifest`、`apps/web/layouts/BaseLayout.astro` 的预绘制值。这些在 CSS 加载前执行,无法从样式表读取颜色。`site:create` 会更新副本的名称、manifest 名称和域名,配色默认继承这份模板。 ## 浅色首页与公开页面 浅色主题参考 EasyStarter 的留白和黑白关系:纯白背景、淡灰边框、黑色主按钮、圆角胶囊操作与居中标题。保留本站 Figtree 字体和品牌标记,深色继续使用哑光黑。浅色颗粒更轻,强度由 `--ui-grain-opacity` 控制;浅深颜色仍统一维护在共享变量中。 首页淡网格仅是 CSS 装饰,不参与交互。输入与输出示例由共享 `runTool` 在构建时产生,链接进入实际 JSON 工具,不是模拟工具功能。首屏没有新增 React 岛,也没有引入远程字体、背景图片或动画库。 公开导航共用 `packages/core/navigation.ts`,首页根路径只在 `/` 上高亮,博客文章路径会高亮 `Blog`。新增同级公开页面时更新导航及 sitemap;`blog.enabled=false` 自动隐藏所有消费端的博客导航。博客卡片、搜索栏和文章目录使用同一套语义配色,手机尺寸下筛选、示例和目录改为纵向排列。 功能内容在 `apps/web/lib/features.ts`,由首页和 `/features` 复用。只列当前工具、保存历史、工作台与教程等已实现能力;支付、存储、生成任务等模块需要各自真实验收后再加入公开描述。 ## 公共导航与滚动 公共导航组件为 `apps/web/components/SiteHeader.astro`,所有公开页面通过 `BaseLayout` 复用。页首不使用分隔横线,滚动超过 48px 后导航收窄为带磨砂底、细边框和柔和阴影的悬浮栏;回到顶部恢复开放形态。宽度与内边距过渡使用 CSS,滚动监听为被动监听,并通过 `requestAnimationFrame` 合并更新,没有添加动画库或 React 岛。 960px 及以下改用原生 `details` 菜单,支持键盘展开、Escape 关闭并还原焦点、点击外部关闭和跨页访问。禁用脚本仍可通过原生展开菜单访问所有公共页面。系统减少动效时关闭收拢过渡与菜单入场动画,悬浮导航仍可用。 导航固定于视口,初始占位保持正文位置;文章目录和锚点保留顶部空间,避免标题被导航遮挡。主题图标使用 SVG,与 HTML 主题属性同步,导航的颜色仍来自共享语义变量。 ## React 组合示例 ```tsx import { Action, Page, PageHeading, Surface } from "@repo/ui/design"; export function Example() { return ( { /* 调用实际处理器 */ }} /> ); } ``` `Action`、`Surface`、`TextInput` 和 `TextArea` 直接导出 Astryx 原组件,不复制库源码,也不创建只转发 props 的组件包装。`Page`、`PageHeading`、`Metric`、`Brand`、`AuthLayout` 提供已在多个真实页面复用的版式。业务请求、会话和路由继续放在消费应用。 Astryx 使用 `label`、`isDisabled`、`isLoading`,不是旧组件的 `children`、`disabled`。`TextArea.onChange` 的第一个参数是字符串。输入标签由库关联生成的 ID,不要额外添加重复标签。密码表单仍使用支持原生 `minLength`/`maxLength` 的现有输入控件,错误、验证、无障碍和授权逻辑保持原流程。 保留的 Radix 对话框、下拉菜单、单选主题控件、原生验证表单通过共享变量获得同样的外观。没有为了换样式重写这些行为。以后新增界面优先 Astryx,已有复杂控件按真实业务需要迁移。 工作台在 `apps/app/tailwind.config.css` 预先声明样式层顺序,把 Astryx 的 `reset` 放在组件与工具类之前。否则晚导入的重置层会覆盖现有按钮背景、文字颜色和输入框边框,导致操作按钮看起来只剩普通文字;不要通过每个页面重复覆盖来修复这个顺序问题。 ## 样式编译与主题 Astryx 0.6.6 使用随包提供的预编译 CSS。我们自己的 React 版式由 StyleX 0.19.1 编译;App 的 Vite 和 Web 的 Astro/Vite 都配置 `@stylexjs/unplugin`,顺序在 React 编译前。Vitest 使用同一插件的 Rollup 转换器,避免启动只供开发 CSS 热更新的轮询服务器。生产构建输出静态 CSS,不在浏览器调用 `stylex.create` 生成样式。 两端都导入 `@repo/ui/design/styles.css`。CSS 包含 Astryx reset、组件、neutral 主题,再用未分层的品牌变量覆盖库的主题层。没有导入 Astryx 的 Tailwind 颜色桥,因为该桥的 `text-primary` 语义与现有表单的 `primary` 主色不同。 HTML 的 `data-astryx-theme="neutral"` 激活库主题。我们不增加 Astryx `Theme` 根提供器:它会再次写 `` 的主题属性。App 继续由原有 Jotai `useTheme()`、预绘制脚本和 `ThemeSync` 控制;公开页的轻量脚本使用相同 `theme` JSON 存储协议,支持系统主题、跨标签页切换和存储不可用的当前页切换。没有第二套持久化主题状态。 Astro 公开页和博客仍构建完整静态 HTML。只有工具和组件预览加载 React 交互岛;公开页的标题、正文、canonical、结构化数据不依赖浏览器执行 JavaScript。 ## 验收与升级 1. 更换变量后检查 `/`、`/pricing`、`/tools/json-formatter`、`/design`、`/dashboard`、`/history`、`/settings`、`/login`。 2. 查看浅色、深色和手机宽度;确认导航可关闭,键盘焦点可见,表单标签与错误提示可读。 3. 工具走一遍输入、处理、复制、下载和保存;不要只看截图。 4. 执行 `bun app:build`、`bun web:build`、`bun typecheck`、`bun web:check`、`bun lint`、`bun format:check`;主题和认证改动再运行已有测试。 Astryx 为 Beta,版本已固定。升级时核对组件 API、构建和上述真实流程。Astryx 与 StyleX 的 MIT 及字体的 OFL 许可见根目录 `THIRD-PARTY-NOTICES.md`,原模板 `LICENSE` 继续保留。 --- --- url: /docs/agentbuff-stack/brand-assets.md --- # 品牌名称与素材 产品品牌统一配置在 `packages/core/website.ts`。公开页、工作台导航、登录页、安装清单和实际邮件读取同一配置。模板官网 `apps/website` 保持自己的品牌配置;修改产品不会替换官网案例或名称。 ## 默认方案 默认显示短字符标记与网站名称,不依赖外部图片。`metadata.name` 控制名称,`brand.mark` 为一到三个无空白的可见字符。复制新站时,工厂根据名称生成首字母标记与独立 SVG 图标;例如 `Paper Studio` 得到 `PS`。 ```ts brand: { mark: "PS", logo: { light: null, dark: null }, favicon: "/brand/favicon.svg", touchIcon: null, emailLogo: null, }, ``` `metadata.language` 控制产品 HTML 的默认语言声明,实际启用语言由 `i18n` 配置。公开页、工作台和账号 / 任务 / 营销邮件的英 / 西语言路径已有本地验收;译文人工审校、真实邮件显示和外部回跳仍待,见[产品语言](./languages)。维护教程保持中文。 ## 使用自己的图片 所有品牌素材使用同源 `/brand/` 路径,分别放进 `apps/web/public/brand/` 与 `apps/app/public/brand/`。两套构建独立提供静态文件,必须有相同路径的对应素材。这里是工厂保留目录,不放业务文件或用户上传内容。 | 配置 | 支持格式 | 使用位置 | | --- | --- | --- | | `brand.logo.light` | SVG、PNG、JPEG、WebP、ICO | 浅色主题中的标记图片 | | `brand.logo.dark` | 同上 | 深色主题中的标记图片;必须同时配置浅色图片 | | `brand.favicon` | 同上 | 浏览器图标与安装清单,必填 | | `brand.touchIcon` | PNG | 可选 Apple 触屏图标与安装清单 | | `brand.emailLogo` | PNG、JPEG | 可选邮件图片,以 `APP_ORIGIN` 转成绝对地址 | | `metadata.ogImage` | PNG、JPEG、WebP | 产品公开页面的分享预览 | 图片标记显示在导航原有的方形区域,旁边保留文本名称;建议使用方形透明标记而非带文字的横向商标。只配置浅色图片时两个主题共用它;浅深图片同时配置时随现有主题切换,不另建主题状态。 邮件图片必须能通过公开 HTTPS 地址读取,不能用私有文件、需要 Cookie 的下载地址或内联 SVG。默认 `null` 时只显示文字品牌,图片被邮箱屏蔽时仍有名称。当前本地捕获仅检查 HTML 和文字内容,不能替代真实邮箱客户端显示验收。 ## 修改与检查 1. 将自己的素材放入上述两处目录。 2. 修改配置中的路径,运行 `bun config:check`。 3. 重新 `bun preview:build`,刷新公开页、登录页与工作台。 4. 分别检查浅深主题、手机宽度、浏览器图标、`/site.manifest` 和公开页分享元数据。 配置诊断与实际构建都检查路径、文件存在和格式签名。不接受外部地址、目录跳转、查询参数或片段;错误指出配置字段,不输出文件内容。这是构建预检,不是图像解码或 SVG 内容安全审计,使用自己确认过的素材。 安装清单由当前配置生成,不能再编辑旧 `public/site.manifest`。公开页和工作台均提供 `/site.manifest`;名称、语言与图标不需要重复维护。独立产品的主题配色仍由共享设计变量控制,见[设计系统](./design-system)。 ## 新站与升级 工厂在新复制目录中清空 `/brand/`,生成新站图标,并重置 `brand`、`metadata.ogImage`、`metadata.social` 和 `metadata.supportEmail`。源站不被修改。生成后重新填写自己的联系方式与社交链接,提供自己的品牌素材;首页、博客和工具业务文案仍需按真实产品调整。 已有站升级时,先备份自己的品牌配置和素材,按新字段补齐,再重新构建;不对已有站运行清空素材操作。路径限制从可选外部 OG 图片收紧到同源 `/brand/`,原有图片须迁入保留目录。 任务邮件内容版本更新为 `task-mail-v2`。启用了可选任务邮件的站点,旧版或品牌名称、邮件图片已改变的未投递记录会标记 `CONFIG_CHANGED`,避免用同一幂等键重试不同内容;不会重新发送已接受的邮件。未启用任务邮件的站点不读取该投递表。本阶段没有新增数据库迁移。 --- --- url: /docs/agentbuff-stack/languages.md --- # 全站语言与翻译 当前模板默认启用英文与简体中文,保留可选西班牙语。官网、产品公开页、工作台、相关邮件、核心使用指南和教程界面支持双语;历史迭代及验收记录保留中文。产品仍在本地迭代,不接真实产品;静态官网与双语教程已单独授权发布。 ## 配置产品语言 唯一入口为 `packages/core/website.ts` 的 `i18n`。英文必须排第一项,`metadata.language` 保持 `en`。中文的标语、两行首页标题、描述和全部方案说明放在 `translations.zh`,不能为空。可选西语读取 `translations.es`。 ```ts i18n: { locales: ["en", "zh"], translations: { zh: { /* 完整中文产品文案,参考源码 */ }, es: null, }, }, ``` 以上是结构示意,不是完整可运行配置。仅英文产品可使用 `locales: ["en"]`,停用语言的译文可设为 `null`。运行 `bun run config:check` 后重建全部应用。重复或未知语言、启用但缺少产品说明会拒绝检查及构建。 不添加浏览器语言自动跳转、语言 Cookie 或新环境密钥。语言由网址明确表达;修改启用列表需要完整替换构建资产,避免旧页面残留。 ## 页面范围与位置 | 页面 | 英文 | 中文 | | --- | --- | --- | | 首页、功能、价格 | `/`、`/features`、`/pricing` | `/zh/`、`/zh/features`、`/zh/pricing` | | 工具、博客、联系与页脚页面 | 原路径 | `/zh` 加原路径 | | 登录、注册、找回与重置 | 原路径 | `/zh` 加原路径 | | 工作台、任务、文件、历史、积分、购买 | 原路径 | `/zh` 加原路径 | | 成员、邀请、设置、站点管理 | 原路径 | `/zh` 加原路径 | | 产品 RSS | `/rss.xml` | `/zh/rss.xml` | | 模板官网 | 原路径 | `/zh` 加原路径 | | 核心教程 | `/en/agentbuff-stack/` | `/agentbuff-stack/` | API、认证签名入口、私有文件下载和 `/_app/` 资产始终使用根路径。未知功能不会因为加上 `/zh` 就存在。关闭中文后,公开页不再生成,边缘也不将中文路径交给工作台。 | 文案或规则 | 位置 | | --- | --- | | 产品说明与方案描述 | `packages/core/website.ts` | | 语言登记与路由 | `packages/core/locales.ts`、`routing.ts` | | 公开页与功能文案 | `apps/web/lib/locales/en.ts`、`zh.ts`、`es.ts` | | 工具名、步骤与完整 FAQ | `packages/core/tool-locales.ts` | | 推荐、营销同意及申请界面 | `packages/core/referral-copy.ts`、`marketing.ts`、`apps/web/lib/marketing-copy.ts` | | 工作台完整字典及稳定错误码 | `apps/app/lib/locales/`、`locale-errors.ts` | | 邮件主题、预览与正文 | `apps/email/locales.ts` | | 请求语言的服务器选择 | `apps/api/lib/email-locale.ts` | | 产品文章目录与校验 | `apps/web/lib/posts.ts`、`blog-catalogue.ts` | | 独立官网字典与模块 | `apps/website/lib/i18n.ts`、`modules.ts` | | 核心英文教程与教程界面 | `docs/en/agentbuff-stack/`、`docs/.vitepress/` | 双语共用页面组件与业务逻辑,中文字典按英文字典类型校验。原始输入、代码、品牌、标识、金额、密钥、审计原因和历史流水备注不翻译。日期与金额的展示使用所选语言,不修改权威数值。 ## 切换与登录回跳 公开语言菜单只链接当前页面真实存在的版本,并提供对应规范链接、语言替代标记和站点地图。公开切换不复制任意查询参数或令牌。 工具切换语言会重新加载,应先复制尚未保存的输入。匿名保存时,输入暂存在当前标签页,安全登录后返回同语言同工具恢复预览,不自动保存。存储不可用时会保留页面提示,不丢弃输入后跳走。 工作台只有一份路由树。地址改写保留支持的任务或购买标识、查询及片段,并同步调整安全 `returnTo`。密码、验证码、注册验证、第三方回调及密码重置沿用所选受支持路径。语言不影响账号、团队、权限、积分或执行次数。 ## 博客与缺译文 产品英文原稿在 `apps/web/content/blog/*.md`,中文在 `zh/`,文件名相同。标题、简介、分类和正文都必须完整;保留示例代码及原数据,内部链接指向实际语言版本。作者、发布日期、更新日期和事实应一起核对。 英文草稿不发布任何译文。中文缺失或为草稿时,中文列表不偷偷混入英文,也不产生中文详情、RSS 条目或替代链接。空正文、孤立译文、无效日期、未知工具会拒绝构建。可选西语保留原有明确标注的英文回退。 官网内容独立放在 `apps/website/content/`,使用 `locale` 与稳定的 `translationKey` 配对,不读取产品文章。案例应是基于模板建成的独立站,演示内工具不算案例。 ## 邮件与托管支付 即时认证邮件按请求中已启用语言选择,未知或停用值回退英文。营销请求保存当时展示的同意文案及语言;任务邮件偏好保存用户主动选择的语言。后续切换页面或重启不会更改已排队请求的语言和载荷。停用语言会停止新投递,但保留历史撤回入口;旧未保存语言的邮件沿用原兼容载荷。 积分包和订阅保存原支付请求的语言及回站路径。同键重试保留原选择,换语言不能借同键建立另一次支付。中文支付配置与模板流程可本地检查,真实供应商页面、真实付款和送达仍需独立验收。 现有语言字段是文本,但邮件、营销和积分购买有语言检查约束。新增 `0035_chinese_locales.sql` 仅放宽对应约束,保留原值和旧请求;启用相关中文持久化模块前需在已核实的目标库完整升级。主预览维持已授权的 0012 水平;通知、营销、推广等可选表不因翻译自动升级。 ## 教程界面与历史 中文教程保留原网址,英文核心指南在 `/en/`。核心页正文上方提供对应语言链接;全局语言菜单回到对应语言首页,避免历史中文页切出不存在的英文网址。搜索、目录、页尾、主题切换及复制提示随教程语言变化。 英文目录中的历史链接明确标注中文,历史验收内容不改写。译文根据现有文案编写,适配真实产品时应复核业务说明、隐私草案及措辞。未完成的真实服务验收仍如实保留。 ## 本地核对 检查两种语言的主页面、移动菜单、主题、键盘焦点、工具输入输出和登录回跳;检查实际文章正文、分类搜索、RSS、规范与替代链接;验证关闭语言、缺译文及草稿不会产生虚假页面。邮件与支付测试使用捕获器及隔离测试数据,不接真实产品或发布云端。 --- --- url: /docs/agentbuff-stack/capabilities.md --- # AgentBuff Stack 能力清单 · 2026-10-11 公开参考:[MkSaaS](https://mksaas.com/)。只对照公开产品能力;本表记录我们的实现状态,不宣称功能或服务已全部验收。 完整 I0—I16 的逐项条件、当前源码证据和剩余外部门槛见[本次验收核对](./completion-audit-2026-10-11)。 | 能力 | 0.3 状态 | 还需要什么 | | --- | --- | --- | | 共享设计系统 | Astryx + StyleX、公共变量、浅深主题、响应式、组件预览 | 按品牌修改变量与任务文案 | | 通用品牌与站点配置 | 集中类型化配置、离线诊断和运行时环境校验;品牌素材、安装清单、分享图片和实际邮件统一读取,生成器重置旧品牌已本地验收 | 发布前补真实品牌、联系方式 | | 首页、功能页、定价、工具目录、工具任务页 | 本地可用,静态 HTML;公开导航与工作台入口共用 | 换成具体任务的文案和处理器 | | 博客、RSS、sitemap、canonical、结构化数据 | 已实现;博客搜索、分类、文章目录、阅读时间本地验收 | 内容持续维护;OG 图片另配 | | 邮箱密码注册登录、资料、找回密码 | 本地接口/页面可用 | 真实发信与生产邮件验证验收 | | OTP、Google / GitHub、Passkey | 继承接口及界面;本地 OTP 捕获 | Google / GitHub 和 Passkey 真实环境验收 | | 通行密钥管理 | 设置页添加入口、列表、改名 / 删除确认与最后登录方式保护;实际签名协议和八连接删除已本地验收 | 系统弹窗与真实设备注册 / 登录仍待人工验收 | | 组织与成员 | 创建、切换、成员搜索分页、邀请闭环、套餐人数写入门禁、角色修改 / 移除 / 退出、原子所有者转移已本地验收 | 真实邀请送达待补齐 | | 登录会话 | 设置页的设备列表、当前标记、单个 / 批量撤销及重新登录已本地验收 | 真实远端设备场景按生产验收补齐 | | 站点管理员与审计 | I9 前五阶段本地验收:受控初始化、角色 / 封禁、搜索分页、任务 / 财务记录、带原因积分调整、任务恢复与主动支付对账 | 生产模式维护命令已在一次性本地数据库验收,真实远程初始化待验;对账供应商响应受控,真实支付仍待验;[中文教程](./admin-audit) | | 订阅支付、账单管理 | 正常生命周期、原结账重试、当前状态同步 / 故障重投、回跳完整保存校验、原请求查找恢复、年付首购页面 / 价格门禁、未标记旧请求人工关联 / 永久审计 / 用户恢复及八连接门禁已本地验;主预览禁用 | 真实月付 / 年付沙箱及生产绑定仍待验 | | 历史记录 | 已实现 | 用户隔离、重试幂等、结果删除/导出已测试 | | 积分 | 已实现余额、账本、任务报价与事务预扣 / 结算 / 返还 | 真实模型与支付外部验收待做 | | 文件存储 | API、清理与文件工作台已实现;I2 手机及键盘浏览器补充验收已完成 | 两个生成站已有文件与隔离验收;320 / 390 像素、浅深主题、实际永久删除、任务占用拒绝及原位重试已验,真实云端与真机仍待,见[教程](./files-storage) | | 数据库备份与恢复 | I15 完整归档、同一快照校验、独立空库、受控文件对象恢复与本机归档保留清理已本地验收 | 真实云端共同恢复与保留策略待验;原私有结果下载、扣费去重和清理后保留归档实际恢复已验,见[恢复教程](./backup-restore) | | 认证密钥轮换 | I15 版本配置、实际重启、旧 Cookie 拒绝、重新登录和原任务去重已本地验收 | 基础密钥用途、历史密文与外部权限独立处理,真实云端仍待,见[轮换教程](./auth-key-rotation) | | 数据库应用角色 | I15 独立 SCRAM 实例、受限业务读写、环境连接隔离、授权事务回滚与密码变更已本地验收 | 旧连接需单独处理;实际新凭据恢复原下载 / 一次扣费已验,真实 Neon / Hyperdrive 连接池仍待,见[角色教程](./database-roles) | | 已有站升级与回退 | I15 已发布 0012 Worker、完整增量 / 故障回滚、新 Worker 与旧 Worker 回退已本地验收 | 原账号、私有结果、在途任务与两次预扣保持;真实云端、其他基线和产品自定义代码待验,见[升级教程](./template-upgrades) | | 开发交付包 | I16 本地源码归档、提交来源、文件摘要、直接依赖声明、安装后实际依赖报告与中文教程已验收 | 报告区分全部 / 生产范围并关联锁文件摘要,不带机器路径;完整许可核对、两个真实产品与正式发布待验,见[教程](./distribution) | | 异步任务 | I4 本地任务闭环已验收 | 实际本地 Queue、死信恢复、次数耗尽、私有结果、重启及两份新站接口 / 浏览器闭环通过;云端待验,见[任务生命周期](./task-lifecycle);收费归 I5 | | 按次付费、一次性积分包 | I6 购买、退款 / 争议对账、差额和恢复已本地验收,默认关闭 | 八连接本地并发已验;真实 Stripe 沙箱与订阅完整生命周期待验,见[教程](./billing-tasks) | | AI 模型 | 未接入 | 按已验证需求选择接口、成本和质量验收 | | 管理员运营后续 | 任务恢复、主动对账、维护计划 / 初始化 / 恢复与审计精确检索已本地验收;默认保留历史 | 正式环境维护、本站保留期限 / 归档策略待验;查询结果不能代替当前业务核对 | | 用户 API 密钥 | I10 本地已验收,默认关闭;一次性展示、改名 / 撤销、有效期、文件 / 任务作用域、双层限额与原积分账本 | 主库 0013 未应用;真实云端和第三方调用待验,见[教程](./api-keys) | | Turnstile / 业务接口限流 | 文件与任务原子限制,可选上传 / 任务提交 / 重试验证已接入 | 当前 workerd 重定向兼容已修复,实际文件入口的域名 / 动作、重放、故障 / 十秒超时和拒绝时无存储写入已验;真实 widget 待验,见[教程](./execution-security)。收费与外部 API 复用原业务限额 / 验证,密钥限流本地已验 | | 统计与转化事件 | 公共页 Cloudflare beacon,以及第一方浏览器报告 / 服务端业务观测已本地实现,均默认关闭 | 独立同意、去重和恢复已有本地证据;真实统计供应商、Stripe / Resend 仍待验;第一方记录不等于外部统计或收入留存 | | 客服聊天 | Crisp 联系页按需加载已本地验收,默认关闭 | 独立配置、主动加载、SDK 窗口事件确认、失败 / 超时邮件回退、英 / 西页面和新站重置;真实工作区待验,见[教程](./support-chat) | | 联盟营销 | Rewardful 核查与推荐绑定后端已本地验收,默认关闭 | 签名 / 去重、佣金 / 退款及结算复核、个人 / 团队推荐绑定、重启恢复与新站重置;0026—0033 主库未应用,浏览器采集、明确选择与新购买首次结账冻结已受控本地验收;具体订单 / 佣金联查与管理证据与带原因 / 审计的安全人工复核已受控本地验收;有界漏回调补查与进度 / 审计已受控本地验收;真实购买归因与打款待验,见[教程](./affiliates) | | 邮件订阅与候补名单 | I11 表单与联系人同步本地已验收,默认关闭 | 匿名同意、确认 / 退订、用途隔离、并发与定时恢复已验;0014—0016 主库未应用;联系人同步、签名回调、失败恢复与全局退订,以及业务观测已有本地证据,真实投递仍待验,见[教程](./marketing-subscriptions) | | 多语言 | I13 公开页与账号 / 任务阶段本地可用:默认英文,西班牙语可选;公开九组页面、账号入口、工作台首页及任务 / 文件 / 历史 / 积分与购买记录 / 成员 / 邀请 / 设置 / 管理后台共用结构、字典与语言导航;认证回跳及公开页 SEO 已验 | 公开工具列表与 JSON / 文本完整交互已交付;博客目录 / 两篇全文与分语言 RSS 已验;即时账号与邀请邮件已验;任务邮件语言已验;营销确认邮件持久化语言已验;托管支付回站语言已本地验收,真实 Stripe 和人工审校仍待;[中文教程](./languages) | | 文档站 | 自有 VitePress 文档入口、配置与模块说明 | 部署文档域名后验收 | | 法律页面 | 已建占位,noindex | 发布前替换为真实适用条款 | | Cloudflare / Neon 部署 | 保留并改名配置 | 尚未部署;需独立服务和真实凭据 | 完整模块及后续交付要求见 [模块总览](./modules.md)。 2026-10-09 对照 MkSaaS 公开文档后的分批路线见[功能扩展路线](./feature-roadmap)。功能覆盖保持通用,具体模型和产品交付由真实需求决定。 ## 下一版顺序 按[功能扩展路线](./feature-roadmap)依次交付文件、任务与执行保护、收费闭环、账号与运营、增长与本地化。通用模块建设与真实产品选题可以并行;具体模型、交付质量、成本与收费方式仍需真实产品验证,订阅需要持续使用证据。 范围边界:0.3 不是完整 MkSaaS 替代品,也不是某个图片工具的专用模板。模板能力独立于示例需求。 ## 0.3 官网与新站生成 官网为独立 Astro 应用;案例、博客与产品订阅配置分开。新站生成采用明确目录,记录模板版本与来源提交,预览端口集中配置,会话及登录提示 Cookie 使用站点 ID。实际验收记录见[改造验收](./refactor-validation-2026-10-09)。 ## 公开工具使用观测 I11 新增默认关闭的浏览器使用报告,明确选择后接收处理尝试、成功和下载发起;并发重放只保留一个处理记录。其来源固定为浏览器,不计服务端任务或收入,也不代表独立用户。0017 未应用主预览;配置、口径、保留与验收见[工具使用事件](./tool-usage-events)。工作台实际任务 / 附件下载与积分包购买确认已本地验收,独立账号同意、重复确认页去重与后台补查见[服务端事件](./server-activity-events);0025 未应用主预览。个人订阅已付账单经当前资源 / 付款分配核查、续费去重和恢复;团队独立同意、当前管理权限、共享账单去重与续费恢复已本地验收;真实外部验收继续保留在完整计划中。 --- --- url: /docs/agentbuff-stack/completion-audit-2026-10-11.md --- # I0—I16 当前验收核对 · 2026-10-11 核对源码基线:`c910fcdf2d90b37ce2fa6ff930bc1fb1a4630dec`,北京时间;模板仍为 `0.3.0`。本页逐项保留[原计划](./development-plan)的要求和验收条件,区分已有本地证据、受控供应商契约与真实外部验收。**完整目标未完成**,本地回归不能替代真实模型、云端运行或两个真实产品。 ## 当前执行范围 用户后续明确:`stack.agentbuff.dev` 是模板官网域名,先做好配置、暂不云端发布、暂不接真实产品。官网的独立静态配置与本地预览是当前范围;真实模型、云端验收及两个真实产品保留在原计划后续阶段,不作为这一步官网配置的前置条件。此前将该域名用于产品测试环境是理解错误,已撤回。 ## 本轮新证据 * 当前全仓回归:108 个测试文件、1092 项通过;不是 1092 项真实服务测试。 * 本地配置诊断通过;Google / GitHub、Stripe、Crisp、Rewardful、营销 / 通知和观测模块在主预览关闭,邮件仅本地捕获。 * 产品测试发布离线预检拒绝域名、发信地址及两个 Hyperdrive ID 占位。后续曾误将官网域名用于产品测试配置,现已按用户澄清恢复产品示例域名;官网自定义域名声明在独立静态站配置。历史三个字段缺项的预检结果不作为当前配置的通过证据。 * 本机 Wrangler 只读登录检查返回 `loggedIn: false`;私有仓库可读的 Actions 秘密名称列表为空。尚不能核对真实账号、资源及该域名的管理权限;不据此认定远程资源不存在。 * 当前没有 `.env.staging.local` / `.env.production.local`。以主预览变量运行 staging 配置检查还会被 ENVIRONMENT 不一致拒绝;这只是本地诊断,不证明远程账号或资源不存在。 * 实际任务消费者只接受 `text-normalize` 版本 1;没有真实模型调用、供应商请求 ID、模型用量或输出质量 / 费用样本。 * 官网案例目录没有已登记的产品 Markdown;现有 JSON / 文本演示不是两个真实产品。 本轮命令:`bun run test -- --run`、`bun config:check`、`bun config:check --target staging`、`bun deploy:check --target staging`,及已有主预览保护入口。staging 两项非零结果是明确的未就绪证据。本轮未重新执行全部原生 Worker / 浏览器流程,未调用真实供应商、迁移远程库或部署。原生与界面历史证据见[迭代记录](./iteration-progress);本机日志在忽略的 `.local/completion-audit-*`。 历史增量复验(澄清域名用途前):生成器按环境重写域名,复制与二次复制、资源隔离、发布检查及交付包四个文件共 32 项测试通过。公开页、工作台与邮件分别构建到忽略的独立产物目录;三个入口文件、17 个页面规范链接、sitemap / RSS 的域名核对通过,原本地及正式配置保持。产物检查没有连接域名或上传代码,不冒充真实云端验收;记录在 `.local/staging-domain-*`。 ## 源码与复验入口 下面的路径已核对存在;测试文件属于本轮全仓回归,原生脚本为复验入口,不表示本轮逐一运行。每个阶段的全部任务、接口 / 文件位置及验收原文在后面的逐项表保留。 | 证据组 | 当前源码 / 测试 / 原生入口 | 完整条件的缺口 | | --- | --- | --- | | C0 / I0 | `apps/api/worker.ts`;`apps/api/lib/bindings.ts`;`apps/api/local/runtime.ts`;`scripts/lib/create-site.test.ts`;`db/migrations/meta/_journal.json` | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | | C1 / I1 | `db/schema/files.ts`;`apps/api/lib/files/storage.ts`;`apps/api/lib/files/storage.test.ts`;`apps/api/routers/files.ts`;`apps/api/local/validate-files.ts` | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | | C2 / I2 | `apps/app/components/files/workspace.tsx`;`apps/app/components/files/workspace.test.tsx`;`apps/api/lib/files/cleanup.ts`;`apps/api/lib/files/cleanup.test.ts`;`scripts/lib/create-site.ts` | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | | C3 / I3 | `apps/api/lib/turnstile.ts`;`apps/api/lib/turnstile.test.ts`;`apps/app/components/security/turnstile.tsx`;`apps/api/lib/tasks/policy.ts`;`apps/api/lib/files/storage.ts` | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | | C4 / I4 | `db/schema/tasks.ts`;`apps/api/lib/tasks/consumer.ts`;`apps/api/lib/tasks/lifecycle.ts`;`apps/api/lib/tasks/tasks.test.ts`;`apps/api/local/validate-tasks.ts` | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | | C5 / I5 | `apps/api/lib/credits.ts`;`apps/api/lib/tasks/charges.ts`;`apps/api/lib/tasks/charges.test.ts`;`apps/api/local/validate-task-charges.ts`;`apps/api/local/validate-paid-tasks.ts` | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | | C6 / I6 | `apps/api/lib/payments/payments.test.ts`;`apps/api/lib/subscriptions.test.ts`;`apps/api/local/validate-payments.ts`;`apps/api/local/validate-subscriptions.ts`;`apps/api/lib/subscriptions/recovery.ts` | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | | C7 / I7 | `apps/api/lib/tasks/consumer.ts`;`packages/core/tools.ts`;`apps/api/lib/tasks/policy.ts` | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | | C8 / I8 | `apps/api/lib/organization-invitations.test.ts`;`apps/api/lib/organization-mutations.ts`;`apps/api/lib/session-management.test.ts`;`apps/api/lib/passkey-management.test.ts`;`apps/api/local/validate-organizations.ts` | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | | C9 / I9 | `apps/api/routers/admin.ts`;`apps/api/routers/admin.test.ts`;`apps/api/lib/admin-mutations.ts`;`apps/api/lib/admin-credits.test.ts`;`apps/app/components/admin/admin-audit-panel.test.tsx`;`apps/api/local/validate-admin-operator.ts` | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | | C10 / I10 | `apps/api/lib/api-keys/access.ts`;`apps/api/lib/api-keys/api-keys.test.ts`;`apps/api/lib/api-keys/management.ts`;`apps/api/local/validate-api-keys.ts` | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | | C11 / I11 | `apps/api/lib/tool-observations.test.ts`;`apps/api/lib/server-activity.test.ts`;`apps/api/lib/team-activity.test.ts`;`apps/api/lib/marketing/marketing.test.ts`;`apps/api/lib/marketing/contact-sync.test.ts`;`apps/api/local/validate-server-activity.ts` | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | | C12 / I12 | `apps/api/lib/task-notifications.test.ts`;`apps/api/lib/operator-notifications.test.ts`;`apps/api/local/validate-task-notifications.ts`;`apps/api/local/validate-operator-notifications.ts`;`apps/api/worker.ts` | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | | C13 / I13 | `packages/core/locales.ts`;`apps/app/lib/locale-routing.ts`;`apps/app/lib/locales/es.ts`;`apps/email/locales.ts`;`scripts/lib/locales.test.ts`;`scripts/lib/branding.test.ts` | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | | C14 / I14 | `scripts/validate-support.ts`;`apps/api/lib/affiliates/events.ts`;`apps/api/lib/affiliates/orders.ts`;`apps/api/lib/affiliates/scans.ts`;`apps/api/local/validate-affiliates.ts`;`apps/api/local/validate-affiliate-bindings.ts` | Crisp / Rewardful 的受控本地契约、选择与支付归因 / 退款核查已有证据;真实工作区、SDK、计佣 / 结算未验。 | | C15 / I15 | `scripts/deploy-check.ts`;`scripts/lib/deployment-check.test.ts`;`scripts/validate-backups.ts`;`scripts/validate-file-backups.ts`;`scripts/validate-app-role.ts`;`scripts/validate-upgrades.ts` | 离线发布门禁和本地恢复 / 轮换 / 权限 / 升级证据已有;真实云端、用量、成本与完整恢复未验。 | | C16 / I16 | `scripts/lib/create-site.test.ts`;`scripts/lib/product-package.test.ts`;`scripts/lib/dependency-inventory.test.ts`;`scripts/lib/create-site.ts`;`scripts/package-site.ts`;`scripts/dependency-inventory.ts`;`apps/website/content.config.ts` | 独立生成、再次生成、开发包、安装 / 构建、依赖报告已有证据;两个真实独立产品及正式交付条件未证明。 | ## 原计划逐项结果 编号按原计划每阶段的任务条目顺序分配,E 是完整验收原文,P 是计划指定的接口 / 位置。标注本地证据不意味着整项完成;缺少对应外部或真实产品证据的条件继续保留。 ### I0:本地资源、配置与迁移规则 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I0.1 | 在当前版本的 Wrangler / Workers 模拟环境中验证 R2 读写、队列事件和定时处理入口;先做最小运行验证再确定预览启动方式,新增内部端口统一进入 `preview.config.json`。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.2 | 统一资源绑定类型与运行时注入;`worker.ts`、`dev.ts`、`local.ts` 的环境校验与资源处理有明确分工,普通字段和 `R2Bucket` / `Queue` 等对象分别处理。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.3 | API Worker 组合 `fetch`、`queue`、`scheduled` 入口,保留 Hono 的请求处理、权限与错误行为;事件消费者独立调用业务服务,不通过伪造登录 HTTP 请求执行任务。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.4 | 保留本地邮件捕获;新运行模式不允许把本地邮件静默转为外部发送。本地资源默认独立持久化,重启后可恢复,生成站不共享状态目录。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.5 | 将本项目后续迁移确定为增量历史,更新与现状冲突的贡献说明;保留已发布迁移,生成站不修改已应用迁移。迁移生成与执行区分。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.6 | 统一模块三种运行状态:关闭、启用但缺配置、已配置但服务故障。新增模块完成后才加入公开配置契约,不提前批量放入不可运行的选项。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.E | 两个新生成站的本地 R2 互不相见;队列发送触发消费者;定时处理可手动触发;认证与捕获邮件回归通过;关闭资源模块仍可启动;没有连接远程资源。只有本地并发模拟通过不能宣称线上并发验证完成。 | 已有本地资源与复制隔离证据;本轮全仓回归支持当前源码,云端不在本项证据范围。 | C0 | | I0.P | 代码位置:`apps/api/{worker,dev,local}.ts`、`lib/{context,env}.ts`、`scripts/preview.py`、预览配置、相关 Wrangler 配置、数据库规范和生成器。 | 原位置及主要入口已落地;实现的最终名称以对应源码为准,草案本身不能作为接口成功证据。 | C0 | ### I1:私有文件 API 与元数据 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I1.1 | 新增文件表,建议字段为 `id`、`userId`、`objectKey`、原始名称、检测类型、字节数、状态、用途、校验摘要、过期时间及创建 / 更新时间。对象 key 唯一;用户、状态和过期时间支持必要查询索引。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.2 | 首版采用登录用户个人文件,与现有个人结果历史一致。工作台说明归属;切换组织不会改变文件所有权。团队共享文件后续另设实际权限契约,不预埋未使用的多态所有者系统。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.3 | 元数据采用 tRPC;二进制上传、预览和下载采用 Hono HTTP 路由。先做经服务端校验的受限上传与读取,优先 R2 原生绑定;暂不为了直传引入 S3 凭据和签名体系。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.4 | 首版演示允许 PNG、JPEG、UTF-8 文本和 JSON,小文件上限建议为 5 MiB,这是产品初始策略而非平台额度。各限制从完成的配置读取;文件名、后缀与客户端 MIME 都不能单独决定实际类型。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.5 | 状态包含 `uploading`、`ready`、`deleting`、`failed`;数据库与对象存储没有共同事务,明确写对象前后的失败补偿与恢复路径。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.6 | 每次读写从会话解析所有者并查新鲜数据,不接受客户端传来的 userId 或任意对象 key 作为授权。私有下载设置适当缓存策略;未知与可执行内容默认作为附件下载。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.7 | 文件操作具有大小 / 数量限制和请求频率保护;先实现必要保护,再在 I3 统一到任务侧策略。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.E | 合法文件上传后字节与摘要一致;其他用户不能列出、读取或删除;无会话、超限、伪装类型和非法文本拒绝;R2 写入失败不形成可下载记录;对象缺失呈现明确状态。 | 已有本地私有文件 / 数据约束证据;真实 R2 与 Neon 仍需 I15 验收。 | C1 | | I1.P | 接口草案:`files.list`、`files.get`、`files.remove`;`POST /api/files`、`GET /api/files/:id/content`。最终命名在实现前固定,避免同一动作维护两套写入口。 | 原位置及主要入口已落地;实现的最终名称以对应源码为准,草案本身不能作为接口成功证据。 | C1 | ### I2:文件工作台、清理与资源复制 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I2.1 | 在 `apps/app/routes/(app)/files.tsx` 提供上传进度、文件列表、大小、状态、查看、下载、删除、过期提示和空状态。上传百分比只代表传输,不能代表后台处理百分比。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.2 | 更新工作台导航、`APP_PATHS` 和边缘路由验收;进入文件页、刷新与登录回跳都正确。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.3 | 定时清理过期对象、超时上传与失败删除;按批次处理,重复执行不重复扣减配额。为清理保留可排查记录。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.4 | 并发上传的额度检查需要数据库原子预留与释放,不能通过“先查总量再写文件”绕过上限。文件被任务引用后的保留与删除规则在 I4 扩展。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.5 | 更新生成器的 R2 名称、绑定占位与本地资源路径;不复制源项目资源 ID,生成后需要重新配置生产资源。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.6 | 交付 `文件与存储` 中文教程:类型 / 大小策略、本地预览、私有访问、清理与故障处理。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | | I2.E | 浏览器走完上传 → 查看 → 下载 → 删除;并发上传不超额;清理失败后可恢复;320 / 390 像素和浅深主题可用;生成两个站运行同一流程且资源隔离。 | 已有本地工作台、四种手机主题组合与两份生成站证据;未重新执行本轮浏览器验收。 | C2 | ### I3:执行限制与 Turnstile | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I3.1 | 定义各操作限制:上传、提交任务、查询、下载与外部 API,登录用户、访客采用各自策略。收费任务首版只接受登录用户。 | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | C3 | | I3.2 | 使用可核对的原子计数或预留策略。起步按现有 PostgreSQL 条件更新 / 唯一约束实现和测负载;有真实性能证据后再迁到专用存储,不先叠加多种限流后端。 | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | C3 | | I3.3 | Turnstile 前端交互与服务端验证一起交付,检查预期 action / hostname;缺凭据、失效 token、服务异常有明确结果。关闭时不加载脚本;登录认证已有的限制继续保留。 | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | C3 | | I3.4 | 返回稳定错误码和重试提示,客户端避免连续自动提交;跨项目配置不共享计数。 | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | C3 | | I3.E | 并发绕过按钮仍受服务端限制;批量请求逐项受约束;无效验证不能触发任务 / 扣费;验证服务故障不会静默放行已要求验证的操作;本地测试与真实验证码验收分开记录。 | 原子限制与受控 Siteverify 已本地验;真实 Turnstile widget / 域名仍待验。 | C3 | ### I4:任务生命周期与可靠投递 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I4.1 | 新增任务表和必要执行记录,保存所有者、处理器 ID / 版本、输入文件引用、参数、请求幂等键、状态、尝试次数、领取租约、结果引用、错误分类与时间。`userId + requestKey` 唯一。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.2 | 使用明确状态:`pending`、`queued`、`running`、`retry_wait`、`reconciling`、`succeeded`、`failed`、`canceled`。把任务阶段与进度百分比分开,首版不伪造连续百分比。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.3 | 创建数据库任务后发送队列;投递失败保留待发送记录,定时处理补投。处理“消息先消费、生产者还没更新投递状态”的竞争,不能把迟到的投递标记覆盖任务终态。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.4 | 消费者原子领取租约并使用领取版本保护写回;重复消息、旧消费者和已完成任务不能再次发布结果。重启和租约过期后按规则恢复。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.5 | 仅可重试错误使用有限重试;死信、超时和处理中失联均可核查。手动重试创建明确的新尝试,遵守次数与权限限制。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.6 | 外部服务可能已成功但响应丢失时进入 `reconciling`。支持供应商查询 / 幂等键时据此对账,无法确认时暂停自动重试;不承诺所有外部操作都能严格只执行一次。 | 任务状态与恢复边界已有;供应商请求查询 / 幂等对账未接,需 I7 真实处理器证据。 | C4 | | I4.7 | 取消尚未执行任务立即终止;已发起的外部调用先记录取消请求,依据真实供应商能力停止或继续对账。取消不是立即退款的同义词。 | 本地任务取消和权益一致性有证据;外部请求停止 / 继续对账尚未验证。 | C4 | | I4.8 | 文件引用有保留规则:输入被运行任务占用时拒绝删除或延后删除;输出文件只在成功完成后成为用户可下载结果。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.9 | 使用确定性文本文件规范化处理器验收实际输入、队列、输出文件和下载;同步 JSON 工具继续保留同步执行。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.E | 创建成功而消息发送失败可补投;重复与并发消费只留下一个有效结果;旧租约不能覆盖新结果;超时、重试耗尽和死信可恢复;刷新浏览器不丢任务;跨账号访问拒绝。 | 文本样例、队列 / 租约 / 死信 / 私有结果已本地验;真实供应商的不确定调用与取消仍待 I7。 | C4 | | I4.P | 界面与接口:`tasks.submit/list/get/cancel/retry`、任务列表 / 详情,真实状态轮询、断线恢复、输入与结果文件链接。 | 原位置及主要入口已落地;实现的最终名称以对应源码为准,草案本身不能作为接口成功证据。 | C4 | ### I5:任务收费与账本事务 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I5.1 | 明确积分语义:`balance` 表示可用积分,预扣即减少可用余额;每个收费任务对应固定报价快照与预扣记录,成功后标为结算,失败按规则一次性返还。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.2 | 改造现有积分方法的事务边界:任务创建、余额锁定、预扣账本与待投递状态在同一个事务完成。外部队列和模型调用在事务提交后执行。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.3 | 保留现有账户与账本唯一事件约束,增加必要任务关联 / 结算记录;不另建第二套积分账户。稳定事件键关联预扣与返还。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.4 | 终态更新与返还一致提交;成功 / 失败 / 取消并发时只有一个最终结算决定。重试沿用原预扣,不每次重试再扣用户。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.5 | 前端先展示服务端报价与余额,用户确认后提交。请求重复返回原任务;同一幂等键携带不同参数时报冲突,不覆盖原任务。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.6 | 供应商结果仍不确定时保持预扣与对账状态,运营人员有明确处理路径,不能一面退款一面继续产生有效收费结果。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | | I5.E | 并发请求不会负余额;任务插入失败不扣积分;余额不足不创建可执行任务;重试不再次扣费;重复终态不重复退款;管理员尚未完成时保留可核对的只读记录与本地诊断路径。 | 本地预扣 / 结算 / 返还、并发与故障证据已有;真实收费模型仍待 I7。 | C5 | ### I6:积分包与订阅完整流程 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I6.1 | 在产品价格配置增加类型明确的积分包,包含 SKU、整数最小货币单位金额、币种、积分数量与真实 Price ID。由同一配置生成购买卡片和服务端商品映射。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.2 | 从服务端创建 Checkout,只信任服务端商品与会话用户;支付完成页查询购买状态,不依据 URL 成功标记发放积分。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.3 | 新增购买记录与支付事件去重记录;验签、校验模式 / 商品 / 金额 / 币种 / 归属后,购买终态和积分发放在同一个事务完成。延迟支付只在实际成功后发放。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.4 | 事件写入与业务处理可安全重试;不同事件指向同一支付对象时仍不能重复发放。退款与争议事件可关联原购买,不能按到达顺序覆盖新状态。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.5 | 退款积分已被使用时,不强行将当前非负余额改成负数。先进入人工核查状态并限制进一步收费使用,显示原因,待明确业务规则后处理差额;部分退款有独立数量与账目规则。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.6 | 保留 Better Auth 对已有订阅的状态维护,完善购买、试用结束、续费失败、取消、到期与门户回跳验收。积分包先属于个人;组织订阅不自动变成组织积分池或给个人重复发额度。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.7 | 锁定当前已安装 Stripe / Better Auth 插件的接口与事件职责,避免一次事件同时由两个处理器维护同一订阅或发放同一权益。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | | I6.E | 错误签名拒绝;重复 / 乱序 / 处理失败再投递无重复积分;延迟成功与失败正确;退款和争议可核对;个人 / 组织账单权限正确。凭据未提供时记录“沙箱待验收”,不把本地签名测试当真实支付。 | 本地签名、权益、乱序 / 重复、退款 / 争议与个人 / 团队边界已有证据;真实 Stripe 沙箱未验。 | C6 | ### I7:一个真实 AI 处理器与成本记录 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I7.1 | 先根据真实产品的输入与输出选择一个模型服务。SDK 和模型版本在接入时核对官方资料,不预先铺设多个供应商、模型路由器和全能 AI Playground。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | | I7.2 | 请求只在服务端发起;模型密钥、预算和供应商回调配置不进入 core 或页面。公开的处理器目录只描述产品能力与输入约束。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | | I7.3 | 每次执行记录供应商请求 ID、模型版本、用量和可核实成本;未知成本留空,不写成零。输入内容的保存目的、时长与结果清理有配置。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | | I7.4 | 对结果做格式、尺寸或结构检查,必要时使用真实输入人工验收质量;质量失败按确定规则进入失败 / 退款路径,不能只检查 HTTP 200。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | | I7.5 | 处理超时、限流、余额不足、拒绝生成、坏结果和供应商回调乱序;无法确认的调用沿用 `reconciling`。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | | I7.6 | 如果当前没有可用凭据或产品尚未选定,文件与任务能力可发布本地版本,AI 保持待接入;演示处理器不会标成真实模型。 | 源码和文档遵守此边界:只有本地演示处理器,AI 明确待接入;不是 I7 完成。 | C7 | | I7.E | 一组固定真实输入记录输出、耗时、失败率与费用来源;完成预览和下载;密钥不出现在客户端产物或日志;不确定结果不会自动重复造成成本。 | 真实模型处理器尚未实现;不能用文本规范化替代本项。 | C7 | ### I8:账号与团队操作完整性 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I8.1 | 补齐组织邀请创建、邮件、接受 / 拒绝、取消、过期与再次邀请;处理现有 `invitation(organizationId, email)` 唯一约束对再次邀请的限制。 | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | C8 | | I8.2 | 角色编辑、移除、退出与所有者转移分别授权;最后一名 owner 不能被移除或降级。已失去成员资格的旧会话不能继续访问组织数据。 | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | C8 | | I8.3 | 补齐 Passkey 添加、命名、列表和撤销,以及会话列表与撤销。删除最后一种可用登录方式前,确保用户仍有可验证的恢复路径。 | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | C8 | | I8.4 | 邀请、恢复与安全邮件复用现有发送入口,提供中文维护说明和英文用户文案;本地捕获与真实邮箱验收区分。 | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | C8 | | I8.5 | OAuth / Passkey 在真实浏览器与第三方环境验收,不把登录按钮出现当流程成功。 | 未完成:真实 OAuth 浏览器及 Passkey 系统 / 设备验收缺失。 | C8 | | I8.E | 过期、已使用、被取消和错误身份的邀请拒绝;角色变更立即影响接口权限;Owner 边界正确;撤销会话 / Passkey 后实际访问或登录失败;现有注册与恢复仍正常。 | 邀请、角色、所有者、会话与 Passkey 协议已有本地证据;真实发信、OAuth 和实际 Passkey 设备仍待验。 | C8 | ### I9:站点管理员与操作审计 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I9.1 | 站点角色与组织角色分开。按已安装 Better Auth 的能力确定管理员集成和数据映射;新增插件表或字段时更新 ID 映射及迁移。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.2 | 首个管理员通过受控初始化步骤明确指定,不能把第一个注册用户或任意组织 owner 自动提升为站点管理员。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.3 | 增加服务端管理员过程与 `/admin` 路由,提供用户搜索、封禁 / 解封、任务与支付状态、积分账本和失败原因查看;默认不展示用户原始输入。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.4 | 积分调整必须有原因并通过同一账本服务执行;重试 / 对账操作复用既有任务与结算规则,管理员也不能绕过扣费或退款幂等。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.5 | 新增操作审计记录,保存执行者、动作、对象、必要变更和时间;审计失败时涉及权益变更的操作不能部分完成。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.6 | 封禁应影响现有会话与后续 API Key 调用,同时定义已经运行任务的处理方式。后台前端菜单隐藏只负责呈现。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | | I9.E | 普通用户、团队 admin 无法调用站点管理员 API;权限撤销及时生效;封禁用户不能继续新建收费任务;积分调整和恢复操作有可核对审计;列表分页不会泄漏全量私有内容。 | 本地角色 / 明确初始化 / 封禁 / 财务与审计已有证据;真实远程权限及本站保留安排待验。 | C9 | ### I10:可选用户 API Key | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I10.1 | 接入 Better Auth API Key 插件前核对当前安装版本、包、生成 schema、ID 和客户端接口;不与站主环境密钥混用。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | | I10.2 | 增加创建、一次性展示、名称 / 前缀列表、过期和撤销界面;不自行保存可恢复明文密钥。作用域先覆盖真正公开的文件 / 任务接口,拒绝管理、支付和身份配置操作。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | | I10.3 | 外部 API 明确版本、鉴权、请求幂等和错误码,不把全部 tRPC 管理接口直接暴露为可用能力。Key 解析出的用户仍需执行业务限制与所有权检查。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | | I10.4 | 基于 Key 和账户限制请求与收费使用;撤销、过期、封禁及权限变化对后续请求生效。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | | I10.5 | 默认关闭。没有对外 API 需求的产品仍只用浏览器会话,关闭后 API Key 路由和设置页不可用。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | | I10.E | 缺失、撤销、过期或错误作用域拒绝;普通 Key 不能调用管理员操作;调用任务的积分与会话请求一致;明文只展示一次且不进入日志。 | 本地签发、个人作用域、撤销 / 过期 / 封禁、限额与账本已有证据;真实外部调用待验。 | C10 | ### I11:转化事件、候补名单与营销订阅 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I11.1 | 定义真实发生的事件:开始、成功、下载发起、购买确认。浏览器触发下载不证明用户最终保存;页面访问不代表使用或收入。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | | I11.2 | 服务端购买事件与前端确认页去重,默认不记录输入、文件内容、邮箱和完整带参 URL。统计启用与同意策略有明确配置,默认不在私有工作台加载营销脚本。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | | I11.3 | 候补名单与营销订阅区分账号;新增订阅状态、确认 token、同意时间 / 来源及退订时间,确认与退订链接可过期或幂等重用。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | | I11.4 | 默认采用已有 Resend 邮件能力;当前 API 契约在实现时核对,不照搬旧 Audience 字段。注册和营销同意分别记录,恢复邮件不受营销退订影响。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | | I11.5 | 处理重复邮箱、确认失败、退订、提供商异常和退订状态同步;联系人同步故障不会阻塞账号或任务。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | | I11.E | 重复提交不会重复创建有效订阅;未确认状态不作为订阅成功;退订实际生效;匿名候补名单不强制登录;捕获邮件可检查,真实投递状态单独验收。 | 本地同意、去重、确认 / 退订、同步 / 恢复与实际业务观测已有证据;真实 Resend / Stripe 与统计供应商仍待验。 | C11 | ### I12:任务通知与站主通知 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I12.1 | 用户通知首版提供站内任务状态与可选完成 / 失败邮件;站主通知首版提供一种明确配置的运营 Webhook,二者的接收者与内容策略分开。 | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | C12 | | I12.2 | 从已提交的任务 / 支付结果生成通知投递记录,稳定事件键去重,有限重试与失败列表;通知失败不撤回已经成功的结果或支付权益。 | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | C12 | | I12.3 | 通知只发送必要事件与内部详情链接,不附原始文件、提示词或外部服务密钥。目标地址来自站主服务配置,不能由普通用户传任意 Webhook URL。 | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | C12 | | I12.4 | 优先沿用已有 Queues 与邮件入口,不创建第二套通知任务引擎。第三方不支持幂等且发送结果不确定时保留可排查状态,不承诺绝对无重复邮件。 | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | C12 | | I12.E | 重复业务事件不创建重复通知记录;投递失败可重试和查看;没有配置时明确关闭;恶意接收地址不进入任意网络请求路径;业务成功不依赖通知成功。 | 本地独立接收者、稳定事件、投递记录、有限恢复已有证据;真实邮件 / Discord 送达仍待验。 | C12 | ### I13:多语言与品牌素材 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I13.1 | 默认继续英文单语言。以一个真实第二语言验证完整路径,维护教程仍中文;先记录译文负责人或译文来源,不生成无内容的语言目录。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | | I13.2 | 建立共享语言配置与实际译文,Astro 公共页、React 工作台、邮件按各自入口消费;使用与当前框架适配的机制,不为采用 next-intl 迁移到 Next.js。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | | I13.3 | 默认语言无前缀,其他语言采用固定前缀;工作台语言前缀同步处理边缘路由、登录回跳、API origin、静态资产及直接刷新。语言不改变租户和权限。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | | I13.4 | canonical、hreflang、sitemap 与真实翻译内容对应,缺翻译采用明确回退或不发布策略;不发布空语言页。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | | I13.5 | 品牌 Logo、favicon、OG 图片、社交链接和任务文案有清楚配置入口,保留哑光浅深主题、焦点状态和减少动画偏好。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | | I13.E | 英文单语言构建和多语言构建都通过;无错域名、死链接与重复 canonical;登录 / 任务 / 邮件语言可预测;两种视口和主题验收;生成器不残留原品牌素材。 | 本地英 / 西路径、实际字典、SEO、邮件语言与品牌已有证据;译文人工审校及真实外部显示 / 回跳待验。 | C13 | ### I14:可选客服与联盟营销 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I14.1 | 客服选一个真实供应商,提供启用配置、必要 ID、按需加载与联系页回退;关闭时不加载第三方请求,任务输入不自动进入聊天上下文。 | Crisp / Rewardful 的受控本地契约、选择与支付归因 / 退款核查已有证据;真实工作区、SDK、计佣 / 结算未验。 | C14 | | I14.2 | 联盟营销选一个真实供应商,明确推荐归因、支付元数据、过期、重复事件、退款撤销与结算核查。前端放脚本只能标记接入,完整计佣需真实流程验收。 | Crisp / Rewardful 的受控本地契约、选择与支付归因 / 退款核查已有证据;真实工作区、SDK、计佣 / 结算未验。 | C14 | | I14.3 | 默认关闭;切换供应商发生实际需求后再确定公共接口。没有账号或测试能力时保持“外部验收待做”,不放虚假的佣金面板。 | Crisp / Rewardful 的受控本地契约、选择与支付归因 / 退款核查已有证据;真实工作区、SDK、计佣 / 结算未验。 | C14 | | I14.E | 开关与实际网络加载一致;缺配置报告字段;推荐支付、重复事件、退款有可追踪记录;提供商异常不阻塞工具交付。 | Crisp / Rewardful 的受控本地契约、选择与支付归因 / 退款核查已有证据;真实工作区、SDK、计佣 / 结算未验。 | C14 | ### I15:生产验收、运行成本与恢复 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I15.1 | 编写可执行的资源与绑定清单:Workers、Hyperdrive、R2、队列、定时入口、Neon、邮箱、Stripe、模型;资源创建与代码部署的职责保持清楚。 | 产品离线清单已实现;产品测试域名、发信地址与 Hyperdrive 编号为占位,真实配置待后续。stack.agentbuff.dev 已归属独立官网,不能代替产品入口。 | C15 | | I15.2 | 先做独立测试环境,核对域名、Cookie、CORS、私有文件、邮件链接和支付模式。使用真实服务验证队列并发、失败恢复及资源限制,本地模拟不能替代。 | 未选产品测试域名、未实际发布或执行云端验收;用户当前明确暂不发布。官网域名已配置,不计作本项产品测试域名。 | C15 | | I15.3 | 按选定计划核算存储、请求、数据库、队列和模型费用;记录日期、来源及实际用量,免费额度不作为成本永远为零的保证。 | 未完成:没有本产品真实用量、负载和供应商成本记录。 | C15 | | I15.4 | 做数据库备份与恢复、对象保留 / 删除验证、任务中断恢复与支付对账。升级采用兼容数据变更,版本回退不依赖删除用户数据或重写已应用迁移。 | 本地数据库 / 对象恢复、保留清理、任务恢复与旧 Worker 回退有证据;真实云端恢复和支付对账待验。 | C15 | | I15.5 | 密钥轮换与操作权限安排写入维护步骤,不在模板生成时分发真实密钥;生产账号、资源、付费行为与发布由明确环境请求执行。 | 本地密钥轮换和受限数据库角色有证据;真实环境权限 / 资源 / 外部行为待安排。 | C15 | | I15.E | 在测试环境完成注册 → 上传 → 收费任务 → 预览 → 下载及失败返还;恢复后能核对文件、任务、账本与购买;记录负载与费用,保留具体限制。 | 离线发布门禁和本地恢复 / 轮换 / 权限 / 升级证据已有;真实云端、用量、成本与完整恢复未验。 | C15 | ### I16:第二个独立产品与模板分发 | 编号 | 原要求 | 核对结果 | 证据 | | --- | --- | --- | --- | | I16.1 | 依据本项目新词研究规则选择真实任务,保存社区 / 查询 / 竞品证据;需求与订阅持续性分别验证,不把模板能力演示当市场需求。 | 未完成:第一真实产品与对应需求证据尚未确认,两个模板样例不能替代市场验证。 | C16 | | I16.2 | 第一个和第二个产品使用独立仓库、数据库、资源、域名与模板 revision。优先选择不同交付形态验证复用边界,例如同步本地任务与异步文件任务;这只是验证结构,不是具体产品推荐。 | 未完成:没有两个真实产品各自仓库、数据库、域名、资源和运行记录的完整证据。 | C16 | | I16.3 | 记录每站复用模块、自定义代码、迁移步骤、构建与运行成本。只有出现真实两处消费者的能力再回收到共享代码,保持产品业务可直接定位。 | 未完成:没有两个真实产品的复用 / 自定义差异、运行及构建成本记录。 | C16 | | I16.4 | 新站生成、从生成站再次生成、已有站升级各走一遍;检查密钥、缓存、官网案例和源站资源 ID 均未混入。 | 本地生成 / 再生成和 0012 已有站升级已验;两个真实产品的业务自定义合并仍缺证据。 | C16 | | I16.5 | 完善分发包、依赖与许可证说明、配置教程、升级说明、故障排查和真实案例。模板价格、授权方案、购买流程和支持范围另行确定,不复用演示站订阅充当模板销售。 | 开发包、来源 / 依赖报告与中文教程已交付;完整许可核对、真实案例、价格 / 授权 / 支持条件仍未确定。 | C16 | | I16.E | 两站独立运行;可指出共用与自定义代码;生成和升级证据完整;官网只展示真实上线站点;销售状态与实际交付一致。 | 独立生成、再次生成、开发包、安装 / 构建、依赖报告已有证据;两个真实独立产品及正式交付条件未证明。 | C16 | 共核对 94 条任务、17 条阶段验收及原计划指定的接口 / 文件位置。 ## 跨阶段发布门槛 | 原计划门槛 | 当前判断 | | --- | --- | | 通用底座、职责分离、原三 Worker 与单一配置入口 | 源码及本地证据存在;不新建平行任务、身份或支付平台 | | 每轮代码 / 配置 / 故障 / 界面 / 中文教程一并交付 | 已有分批记录;本轮纠正文档中已过时的文件与转化状态,记录仍存在的真实验收缺口 | | 增量迁移、原授权 / 账本 / 终态与不泄漏密钥 | 现有全仓回归与主预览保护支持本地行为;原迁移保持,主预览只应用已授权 0012 / 13 条。没有扩大主库可选迁移范围 | | 0.4 / 0.5 本地文件与任务 | 已有受控本地里程碑证据;本轮未重跑每个浏览器 / 原生场景,也没有更新发布版本 | | 0.6 商业与模型 | 真实 Stripe 沙箱及真实模型两项均缺,不能宣布达到 | | 0.7 账号 / 运营、0.8 增长 / 语言 | 本地证据存在;对应真实身份 / 邮件 / 通知 / 客服 / 联盟与语言审校仍待,不能据按钮或模拟响应宣布整体完成 | | I15 发布候选 | 缺独立云端注册 → 上传 → 收费处理 → 预览 → 下载 / 返还、完整恢复、负载与实际费用证据 | | I16 1.0 交付 | 缺两个真实独立产品、每站复用 / 自定义 / 成本、真实上线案例和正式分发条件;开发包不能代替 | | 每轮和里程碑的命令 / 截图 / 数据 / 记录 | 原计划列出的八个常用命令均存在;本轮只声明实际执行的检查,历史截图不冒充本轮新验收 | ## 后续阶段进入条件 后续 GitHub 干净检出暴露官网内容类型尚未生成的六处检查错误。已在独立已提交源码归档 / 冻结安装中复现,并修正统一命令为先同步 Astro 类型、再检查项目图;新生成产品的独立安装 / 冻结安装 / 强制类型检查,以及错误博客字段的拒绝均通过。这个证据补齐干净环境的检查准备,不替代真实处理器或云端验收。 2026-10-11 后续完成[候选图片转 SVG 的接口核对](./image-to-svg-contract):已读取官方公开资料及实际 OpenAPI,发现模型版本未知、表单字段不一致和不确定重试的计费边界。它是 I7 的选择证据,不是处理器实现或收费调用验收;原 I7 各项未完成判断保留。 1. 确定第一真实任务及输入 / 输出,核对真实需求与一个可用服务的官方接口;随后把模型处理器接入原任务 / 账本 / 结果链路,补固定样本、质量、失败与费用记录。 2. 当前先完成 `stack.agentbuff.dev` 模板官网的独立配置与本地验证。真实产品和云端发布按用户要求暂缓;将来明确恢复该阶段时,再选独立产品测试域名、准备 Cloudflare / Neon 与各项真实配置,并确认环境操作范围。官网不依赖这些产品资源。 3. 完成第一产品云端验收后,按原计划选第二真实任务,核对独立资源、复用差异、升级和费用;官网只登记可核实的实际上线站。 这些是原计划进入条件,目标继续保持 I0—I16,不改成“本地测试通过即可完成”。 --- --- url: /docs/getting-started.md --- # 项目介绍 AgentBuff Stack 是面向英文工具站的全栈模板。公开页面用于介绍产品、承接搜索访问;工具提供输入、处理、预览和导出;工作台负责账号、历史记录、积分及订阅。维护教程先统一使用中文,客户网站仍按英文市场配置。 ## 技术分工 | 部分 | 实现 | 主要目录 | | --- | --- | --- | | 公开网站与博客 | Astro,构建时输出静态页面 | `apps/web` | | 登录与工作台 | React、TanStack Router、TanStack Query | `apps/app` | | 服务端接口 | Hono、tRPC、Better Auth | `apps/api` | | 数据库 | PostgreSQL、Drizzle | `db` | | 共享样式 | Astryx、StyleX、共享设计变量 | `packages/ui` | | 邮件模板 | React Email,Resend 发送 | `apps/email` | | 部署目标 | Cloudflare Workers,Neon 与 Hyperdrive | `infra`、各应用的 `wrangler.jsonc` | 版本以各目录的 `package.json` 与根目录 `bun.lock` 为准。公开站不依赖登录状态才能返回正文,私有数据始终由服务端验证身份和权限。 ## 先读哪些内容 先完成 [本地启动](../agentbuff-stack/quick-start.md),再看 [网站配置](../agentbuff-stack/website.md)、[环境变量](../agentbuff-stack/env.md) 和 [模块总览](../agentbuff-stack/modules.md)。这些页面给出实际配置入口及完成状态。 深入改动时,按目录查看前端、接口、账号、数据库和支付教程。扩展教程会明确标注尚未接入的能力;示例存在不代表已经在客户网站启用。 ## 许可证与来源 模板基于 Kriasoft 的开源项目迭代,保留原 MIT 许可证与版权声明。出售模板或复制新站时也要保留许可证,并分别遵守第三方依赖和外部服务的条款。 --- --- url: /docs/getting-started/quick-start.md --- # 启动项目 ## 使用哪种启动方式 首次排查建议使用完整的 [本地预览流程](../agentbuff-stack/quick-start.md)。它使用构建产物,在 `http://localhost:4400` 提供公开站、登录、工作台和接口;邮件写入本地收件记录。文档使用 `http://localhost:4406`。 准备 Bun 1.4.2 或更新版本、Python 3 和独立 PostgreSQL 数据库。先复制 `.env.example` 为 `.env.local`,设置数据库地址、独立认证密钥及本地站点地址,再执行: ```sh bun install --frozen-lockfile bun run config:check # 确认连接的是新建的隔离数据库后再迁移 bun db:migrate bun build bun run docs:build bun run preview:start bun run preview:status ``` 启动器不会安装数据库。已有本机演示数据库和账号不属于复制新站的默认配置。 ## 开发时热更新 ```sh bun dev ``` 此方式并行启动公开站、工作台和接口开发服务,实际端口以终端输出为准。接口走 Wrangler 的本地代理,需要配置两条 Hyperdrive 本地连接变量;邮件使用真实 Resend,配置见 [环境变量说明](./environment-variables.md)。不要把它和捕获邮件的完整预览流程混淆。 只改文档时可单独运行: ```sh bun run docs:dev --host 127.0.0.1 --port 4406 ``` 若 4406 已由预览管理器占用,先停止该预览再启动开发服务。 ## 常见问题 * 页面无法访问:检查 `bun run preview:status`,再看 `.local` 中对应服务日志,确认端口未被其他程序占用。 * 页面没有更新:预览读取构建产物。先重建,再停止并启动预览;文档预览同样需要重启。 * 接口提示缺表:检查 `DATABASE_URL` 目标及迁移记录,不要直接对未知数据库执行结构推送。 * 登录成功后跳回登录页:检查 `APP_ORIGIN`、浏览器地址与会话请求,详见 [会话与访问控制](../auth/sessions.md)。 --- --- url: /docs/getting-started/project-structure.md --- # 目录结构 ## 应用目录 ```text apps/ web/ 公开网页、工具页、博客、搜索优化与边缘入口 app/ 登录页和私有工作台 api/ 认证、业务接口及不同运行环境入口 email/ 邮件模板及模板预览 ``` 公开站页面在 `apps/web/pages/`,工作台页面在 `apps/app/routes/`,业务接口在 `apps/api/routers/`。三个应用可分别构建,通过公开站入口统一对外提供服务。 ## 共享目录 | 目录 | 职责 | | -------------------------- | ------------------------------------------ | | `packages/core/website.ts` | 品牌、导航、套餐、工具与功能开关的统一配置 | | `packages/core/site.ts` | 从统一配置派生兼容视图,通常不直接修改 | | `packages/ui/design/` | 设计变量、共享组件与布局样式 | | `packages/ui/components/` | 现有工作台基础组件 | | `packages/ws-protocol/` | 实时消息协议与独立示例,尚未接入主站 | | `db/schema/` | 账号、组织、订阅、工具记录与积分数据模型 | | `db/migrations/` | 已提交的数据库迁移及元数据 | | `scripts/` | 复制新站、配置诊断、本地预览与部署脚本 | | `infra/` | 各环境基础设施配置 | | `docs/` | 本中文教程 | ## 配置和生成文件 根目录 `.env.example` 是变量说明,`.env.local` 保存本机值;线上秘密写入对应环境的服务配置。三份 `apps/*/wrangler.jsonc` 定义部署名称及绑定。 `apps/app/lib/routeTree.gen.ts` 是路由生成文件,通过开发或构建更新,不要手写。各应用 `dist/`、文档 `.vitepress/dist/`、本地 `.local/` 和密钥文件不作为新站源码复制。 改文件前先看所在目录的 `AGENTS.md`。跨应用复用逻辑放共享包,单个应用专用逻辑留在该应用内。 --- --- url: /docs/getting-started/environment-variables.md --- # 环境变量说明 ## 两类配置 公开品牌和功能选项在 `packages/core/website.ts`;数据库地址、认证密钥、第三方服务凭据在环境变量中。不要把服务端密钥放入公开站配置或带有 `PUBLIC_` 前缀的变量。 完整字段表、凭据组和诊断命令见 [环境变量](../agentbuff-stack/env.md)。新增变量时同步修改 `.env.example`、`apps/api/lib/env.ts` 和对应教程。 ## 本地文件 | 文件 | 用途 | | ----------------------- | ---------------------------------- | | `.env` | 根目录基础值 | | `.env.local` | 本机开发与完整预览的覆盖值,不提交 | | `.env.staging.local` | 预发布数据库命令的目标,不提交 | | `.env.production.local` | 生产数据库命令的目标,不提交 | 运行时环境解析由 `parseEnv()` 执行。Google、GitHub 的标识与密钥需要成对填写;Stripe 的四项基础变量必须全部填写或全部留空。关闭支付模块不会让半组错误配置变成有效配置。 ## 两种数据库连接 完整本地预览使用 `DATABASE_URL` 直连 PostgreSQL。`bun dev` 的接口通过 Wrangler 本地代理,需要 `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE_CACHED` 和 `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE_UNCACHED`。 生产接口读取 `HYPERDRIVE_CACHED` 与 `HYPERDRIVE_UNCACHED` 资源绑定;数据库迁移仍通过独立的管理连接执行。资源绑定不是普通字符串环境变量。 ## 排查顺序 ```sh bun run config:check bun --env-file .env.staging.local scripts/config-check.ts --target staging bun --env-file .env.production.local scripts/config-check.ts --target production ``` 后两条需要先准备对应文件中的完整目标环境变量,不会自动读取 Cloudflare 秘密。检查目标、网址格式、认证密钥及第三方凭据是否完整。诊断只检查配置,不连接服务,也不证明真实登录、发信或支付已经成功。修改环境后重启接口;修改构建时公开配置后重建网页。 --- --- url: /docs/architecture.md --- # 系统架构概览 ## 请求如何流转 ```mermaid flowchart TD U[浏览器] --> W[公开站边缘入口] W -->|公开页面| S[Astro 静态页面] W -->|登录与工作台路径| A[React 工作台] W -->|接口路径| H[Hono 接口服务] H --> B[Better Auth 与业务接口] B --> D[Hyperdrive 与 PostgreSQL] H --> E[邮件与支付服务] ``` 首页 `/` 始终返回公开网页,包括已经登录的用户。`/dashboard` 等私有路径交给工作台;工作台读取会话并决定是否跳转登录,接口再校验身份与数据归属。 ## 三个部署单元 | 应用 | 职责 | 关键入口 | | --- | --- | --- | | 公开站 | 静态网页、工具、博客及请求分流 | `apps/web/worker.ts` | | 工作台 | 单页应用、会话状态及私有页面 | `apps/app/wrangler.jsonc`(静态资源与单页回退) | | 接口 | 认证与类型化业务接口 | `apps/api/worker.ts` | 公开站通过 `APP_SERVICE` 和 `API_SERVICE` 调用另外两个服务。接口使用两个 Hyperdrive 绑定;认证、权限、账单和写后读走无查询缓存的连接。 ## 公开内容与私有数据 Astro 在构建时生成公开正文、元信息和博客页面;交互工具按需加载 React。搜索访问不需要登录或等待私有工作台脚本。用户历史和积分通过登录后的接口获取,不写入公开构建产物。 认证模块仍设置会话提示 cookie,但当前公开入口不据此将首页切换为工作台。该 cookie 不是授权凭据,历史设计见 [认证提示 cookie 决策](../adr/001-auth-hint-cookie.md)。 ## 本地与线上 完整预览由 `scripts/preview.py` 启动各服务,再用 `scripts/local-gateway.ts` 在 4400 分流。线上使用 Worker 服务绑定。两者必须保持相同路径归属,相关检查在 `apps/app/lib/edge-routing.test.ts`。 构建顺序应让邮件模板先可供接口引用;使用根目录 `bun build`,避免手动漏掉依赖。部署依次更新接口、工作台和公开入口,细节见 [部署上线](../deployment/index.md)。 --- --- url: /docs/architecture/edge.md --- # 边缘路由与绑定 ## 路径归属 `apps/web/worker.ts` 是客户站点的统一入口。`/api/*` 转给接口服务;下列路径及其子路径转给工作台;其余请求交给公开站静态资源。 ```text /_app /login /signup /forgot-password /reset-password /dashboard /history /credits /members /settings ``` 匹配采用完整路径或以斜杠分隔的子路径,避免 `/members-only` 之类公开页面被 `/members` 误拦截。新增工作台顶层路由后必须同步入口列表和本地网关,并检查直接刷新地址的结果。 ## 服务与资源绑定 | 绑定 | 所属服务 | 用途 | | --------------------- | -------------- | ------------------------------ | | `ASSETS` | 公开站、工作台 | 提供各自构建产物 | | `APP_SERVICE` | 公开站 | 转发登录与工作台请求 | | `API_SERVICE` | 公开站 | 转发接口请求 | | `HYPERDRIVE_UNCACHED` | 接口 | 认证、权限、账单与实时数据查询 | | `HYPERDRIVE_CACHED` | 接口 | 可接受短期旧结果的查询 | 资源名称和环境值在各应用的 `wrangler.jsonc`。接口启用 Node 兼容能力以使用数据库及服务依赖,公开站和工作台无需照搬这些设置。 ## 缓存边界 公开静态资源可以缓存。会话、成员权限、积分余额、订阅状态及写入后的查询不使用 Hyperdrive 查询缓存,也不能被公开响应缓存。服务端是否允许操作由数据库里的当前身份和角色决定。 ## 排查 页面跳错应用时,先查路径分流;接口 404 时,检查公开入口服务绑定和接口路由;只有线上数据库失败时,检查两个 Hyperdrive 资源与连接权限。完整本地预览使用直连数据库,不能替代线上绑定验收。 基础设施与应用发布的责任边界见 [部署决策](../adr/002-terraform-wrangler-boundary.md)。 --- --- url: /docs/frontend/routing.md --- # 页面路由 ## 文件路由 工作台使用 TanStack Router,页面文件在 `apps/app/routes/`。`__root.tsx` 提供根布局,`(auth)` 放登录和找回密码页面,`(app)` 放需要会话的工作台页面;括号目录用于分组,不作为网址的一部分。 `lib/routeTree.gen.ts` 由路由插件生成。新增页面后启动开发服务或构建以更新它,不手动维护生成内容。 ## 私有页面与公开页面 `(app)/route.tsx` 的 `beforeLoad` 读取统一会话查询,未登录时跳转登录。公开营销、博客和工具页面放在 `apps/web/pages/`,由 Astro 输出静态正文。 新工作台页面还要在 `apps/web/worker.ts` 和 `scripts/local-gateway.ts` 注册对应顶层路径,否则站内跳转可能正常,直接打开或刷新却落到公开站。验证双向路径归属的测试在 `apps/app/lib/edge-routing.test.ts`。 ## 新增页面流程 1. 参考相邻路由创建文件并导出 `Route`,设置加载和错误状态。 2. 需要私有访问时放入 `(app)` 分组,使用统一会话与查询模块。 3. 增加导航入口,并同步边缘路径归属。 4. 构建后分别检查站内点击、直接访问、刷新和退出后访问。 网址查询参数通过路由的校验配置读取,限制长度和可选值。登录回跳地址必须校验为站内路径,不能接受任意外部地址。 详见 [新增页面](../recipes/new-page.md) 和 [会话与访问控制](../auth/sessions.md)。 --- --- url: /docs/frontend/state.md --- # 状态与数据查询 ## 状态放在哪里 服务端数据由 TanStack Query 管理;网址可分享的状态放路由查询参数;组件短期状态用 React;跨组件的纯界面状态可使用 Jotai。不要把会话、订阅和积分复制到多个状态容器。 统一查询客户端在 `apps/app/lib/query.ts`:一般查询新鲜期为两分钟,网络重新连接时刷新;变更默认不重试,避免响应丢失后重复创建记录。 ## 会话查询 `apps/app/lib/queries/session.ts` 使用固定查询键 `['auth', 'session']`,新鲜期为 30 秒。用户和会话同时存在才算登录;401、403 不自动重试,网络错误可以重试。组件用 `useSessionQuery()`,路由守卫使用同一查询配置。 注册、登录或切换活动组织后调用 `revalidateSession()`,删除旧会话查询并重新运行路由守卫。退出只有在服务端成功后才清空会话并跳转登录。 ## 业务查询 查询集中在 `apps/app/lib/queries/`。组织查询键包含组织 ID;没有活动组织时跳过请求。写入成功后只刷新受影响的数据,避免把所有查询一起失效。 ```tsx import { useSessionQuery } from "#lib/queries/session"; import { useMembersQuery } from "#lib/queries/organization"; export function MemberCount() { const session = useSessionQuery(); const members = useMembersQuery(session.data?.session.activeOrganizationId); if (members.isPending) return

正在读取成员

; if (members.error) return

成员读取失败

; return

成员数量:{members.data?.members.length ?? 0}

; } ``` 这个组件示例用于说明查询依赖;无活动组织时应提供创建组织入口,而不是永久显示加载中。 ## 排查缓存问题 先检查查询键是否包含用户或组织范围,再检查失效逻辑。权限和账单还需要服务端使用 `ctx.db` 的实时连接,前端刷新无法修正后端缓存了旧权限的问题。 --- --- url: /docs/frontend/ui.md --- # 组件与主题 ## 当前设计系统 公开站和工作台共享 `packages/ui/design/` 的颜色、字体、间距及圆角变量。React 共享组件使用 Astryx 与 StyleX,Astro 页面使用同一组样式变量。原有工作台基础组件保留在 `packages/ui/components/`,逐步按共享设计规范维护。 完整入口、组件用法和调整方法见 [设计系统](../agentbuff-stack/design-system.md)。不要在每个页面各自定义一套主色和按钮。 ## 样式文件职责 | 文件 | 内容 | | --------------------- | -------------------------- | | `design/tokens.css` | 颜色、字体、间距和主题变量 | | `design/patterns.css` | 常用布局与页面组合样式 | | `design/styles.css` | 设计系统样式入口 | | `design/index.tsx` | 可复用的 React 组件 | | `styles.css` | 基础组件及全局样式 | StyleX 的编译设置与共享包的路径需要保持一致。改动后分别构建公开站和工作台,不能只检查其中一个应用。 ## 主题配置 默认主题和切换入口由 `websiteConfig.appearance` 控制。工作台主题逻辑在 `apps/app/lib/theme.tsx`;公开站使用同一配置初始化主题。修改默认主题需要重新构建。 ## 新增组件 先复用已有组件;确实需要新增时,以命名导出提供小而明确的接口,支持键盘操作、可见焦点、加载和禁用状态。客户界面文案仍遵循产品目标语言,中文仅用于本维护教程。 `bun ui:add` 可用于引入现有基础组件,生成后仍需检查项目命名、导入和样式。不要将生成代码直接当作最终设计。 ## 验证 检查浅色、深色、窄屏、键盘焦点以及错误状态。遇到样式缺失,依次确认入口导入、变量定义、StyleX 编译路径和构建产物。 --- --- url: /docs/frontend/forms.md --- # 表单与校验 ## 三层校验 输入框先提供格式提示,提交前用 Zod 校验,服务端再次校验。客户端校验改善体验;身份、权限、价格、积分和资源归属必须由服务端判断。 登录组件位于 `apps/app/components/auth/`,账号表单遵循认证配置返回的可用方式,不能自行显示一个服务端未启用的入口。 ## 表单状态 表单应区分空白、输入错误、提交中、服务端错误和成功。提交中禁用重复提交,保留用户输入;失败后显示可理解的说明并提供重试。不要将原始错误堆栈或密钥暴露给用户。 ```ts import { z } from "zod"; const organizationInput = z.object({ name: z.string().trim().min(1).max(100), }); const result = organizationInput.safeParse({ name: formName }); if (!result.success) { // 将字段错误显示在对应输入框旁 return; } ``` 这是校验示例,长度上限应与实际接口和数据库要求保持一致。 ## 字段错误与访问性 给输入框绑定可见标签,使用 `aria-describedby` 关联帮助和错误说明,错误字段设置 `aria-invalid`。成功后的跳转应在接口确实成功后发生;组织创建后还需要刷新会话,等待活动组织写入。 tRPC 的字段校验错误通过 `error.data.zodError` 读取;统一错误状态工具位于 `apps/app/lib/errors.ts`。验证码、密码重置和通行密钥失败流程见 [账号认证](../auth/index.md)。 ## 排查 先看提交请求是否发出、请求输入是否符合契约,再查字段错误与网络错误是否分开处理。提交按钮卡住时检查是否遗漏结束状态,或等待会话刷新失败后没有显示错误。 --- --- url: /docs/api.md --- # 接口概览 ## 入口与职责 `apps/api/lib/app.ts` 组合业务路由与认证处理,`apps/api/worker.ts` 初始化数据库和认证实例。`apps/api/local.ts` 在本地 workerd 中运行同一 Worker bundle,`apps/api/dev.ts` 在开发监听模式下复用本地入口。 | 路径 | 职责 | | ------------- | ------------------------------------------ | | `/api/auth/*` | Better Auth 登录、会话、组织和订阅插件接口 | | `/api/trpc/*` | 类型化业务查询与变更 | 具体健康检查等路径以 `lib/app.ts` 为准。接口类型从 `@repo/api` 导出,工作台通过 `apps/app/lib/trpc.ts` 创建客户端。 ## 业务模块 `routers/config.ts` 返回可公开的功能配置;`routers/billing.ts` 查询套餐和账单状态;`routers/workspace.ts` 处理工具记录与积分。新增模块后在 `lib/app.ts` 注册,才能被客户端调用。 ## 身份与授权 公共接口用 `publicProcedure`,私有接口用 `protectedProcedure`。后者只保证登录,仍要针对用户 ID、组织成员和角色检查资源归属。不要相信前端传来的用户身份或积分价格。 每次请求的上下文包含 `db`、`dbCached`、`user`、`session` 和服务环境。默认使用实时 `db`;缓存连接只用于能接受旧数据的读取。 ## 排查入口 接口 404:检查注册路径与公开站分流。401:检查会话 cookie 与请求来源。403:检查成员和角色。输入错误:查看字段校验结果。数据库错误:先核对连接、迁移和表名。服务配置错误:运行 `bun run config:check`。 继续阅读 [查询与变更接口](./procedures.md)、[请求上下文](./context.md) 和 [错误处理](./validation-errors.md)。 --- --- url: /docs/api/procedures.md --- # 查询与变更接口 ## 两类操作 查询使用 `.query()`,不产生业务写入;变更使用 `.mutation()`,用于创建、修改和删除。输入使用 Zod 定义,私有操作使用 `protectedProcedure`。 以下是独立示例,演示接口结构;若加入项目,还要注册路由。 ```ts import { z } from "zod"; import { protectedProcedure, router } from "../lib/trpc"; export const profileRouter = router({ summary: protectedProcedure.query(({ ctx }) => ({ id: ctx.user.id, name: ctx.user.name, })), validateName: protectedProcedure .input(z.object({ name: z.string().trim().min(1).max(100) })) .mutation(({ input }) => ({ name: input.name })), }); ``` `validateName` 仅返回校验后的输入,不保存资料。真实写入还需数据模型、权限校验及相应测试。 ## 注册与调用 在 `apps/api/lib/app.ts` 的 `appRouter` 中加入新路由,客户端类型随之更新。工作台使用统一 tRPC 客户端;把查询键、请求及失效逻辑放在 `apps/app/lib/queries/`。 ## 安全与幂等 登录不代表可以读取任意用户或组织资源。查询条件需要同时包含当前身份范围;写入必须重新验证。涉及积分、支付和可重试任务时,使用唯一业务事件键与数据库事务,避免重复扣费。 限制输入大小及返回字段,只返回调用方需要的数据。列表接口设数量上限并选择明确排序,避免无界查询。 ## 验证 至少检查合法输入、非法输入、未登录、越权及重复请求。纯格式展示改动无需新增接口测试;新增数据边界应使用真实数据库语义测试,见 [测试与排查](../testing.md)。 --- --- url: /docs/api/validation-errors.md --- # 输入校验与错误处理 ## 输入校验 接口在 `.input()` 中使用 Zod,明确字符串长度、枚举、数量和格式。对外部文件和网址,还需要在处理阶段验证真实内容、响应大小和可访问范围;仅校验后缀或网址格式不够。 ```ts import { z } from "zod"; const listInput = z.object({ limit: z.number().int().min(1).max(100).default(20), }); ``` ## 统一错误 `apps/api/lib/trpc.ts` 为校验错误附加 `data.zodError`,客户端可把字段错误放回表单。业务错误使用 `TRPCError`,选择与实际情况一致的类型。 | 错误类型 | 场景 | | ----------------------- | ---------------------- | | `BAD_REQUEST` | 输入或请求状态不合法 | | `UNAUTHORIZED` | 缺少有效会话 | | `FORBIDDEN` | 已登录但无操作权限 | | `NOT_FOUND` | 资源不存在或不可见 | | `CONFLICT` | 唯一约束或当前状态冲突 | | `INTERNAL_SERVER_ERROR` | 未预期的内部失败 | ```ts import { TRPCError } from "@trpc/server"; throw new TRPCError({ code: "FORBIDDEN", message: "当前账号没有管理权限", }); ``` 示例说明错误表达方式;客户站文案按产品语言配置。 ## 前端处理 使用 `apps/app/lib/errors.ts` 提取状态和可展示消息,区分校验错误、权限错误与网络故障。401 可以引导重新登录;403 应解释权限不足;网络故障保留输入供用户重试。 不要把所有异常改成“没有登录”,也不要在支付失败后自动重试创建订阅。日志保留内部原因和请求标识,响应隐藏堆栈、数据库连接和密钥。 ## 环境错误 启动时 `parseEnv()` 检查字段组合,诊断只报告错误字段名。先修复缺失变量再重启;不要为了绕过报错把半组凭据补成无效占位值。 --- --- url: /docs/api/context.md --- # 请求上下文与中间件 ## 请求上下文 定义位于 `apps/api/lib/context.ts`,业务接口每次请求获得独立上下文。 | 字段 | 用途 | | ------------------- | -------------------------------------- | | `req`、`info` | 原始请求与接口调用元信息 | | `db` | 实时数据库连接,默认选用 | | `dbCached` | 有查询缓存的连接,仅在允许旧数据时使用 | | `user`、`session` | 解析后的身份,未登录时为空 | | `env` | 已校验的环境配置 | | `res`、`resHeaders` | 特定响应处理所需的可选字段 | Hono 上下文保存数据库和认证实例,并不提前声明一个没有赋值的用户。会话由 tRPC 上下文构造过程解析。 ## 中间件顺序 线上入口先安装错误处理,再启用安全响应头、请求标识和日志,然后解析环境并创建数据库与认证实例,最后挂载业务路由。入口代码在 `apps/api/worker.ts`。 `protectedProcedure` 在执行业务逻辑前检查用户和会话,成功后提供非空类型。资源级权限检查仍在具体业务操作内执行。 ## 请求标识与日志 请求标识由 `apps/api/lib/middleware.ts` 生成,排查时将客户端看到的标识与服务端日志对应。记录路径、耗时和错误类型即可;不记录认证 token、密码、完整支付凭据或用户文件正文。 ## 缓存连接的限制 认证、成员权限、订阅、积分以及写入后立即读取统一用 `db`。Hyperdrive 查询缓存不会因为业务写入自动失效,前端重新请求也可能得到旧结果。只有明确允许短期旧数据的读取才选 `dbCached`。 本地数据库实现与线上驱动不同,业务层通过 `@repo/db` 的 `Database` 类型约束,避免依赖驱动私有字段。 --- --- url: /docs/auth.md --- # 账号认证概览 ## 已接入的能力 认证服务使用 Better Auth,配置在 `apps/api/lib/auth.ts`,客户端在 `apps/app/lib/auth.ts`。账号包括密码登录、邮箱验证码、按凭据启用的 Google 和 GitHub 登录、通行密钥登录入口、组织及订阅插件。 | 方式 | 配置入口 | 当前边界 | | --- | --- | --- | | 密码 | `websiteConfig.auth.password` | 注册、登录、找回与重置;非开发环境要求邮箱验证 | | 邮箱验证码 | `websiteConfig.auth.emailOtp` | 六位验证码,需邮件发送服务 | | 第三方登录 | 服务端完整凭据组 | Google、GitHub;真实授权需分别验收 | | 通行密钥 | `websiteConfig.auth.passkey` | 现有账号登录;凭据管理界面尚未完整接入 | | 组织 | 组织插件及成员页面 | 创建与成员查询;邀请邮件尚未接入 | 关闭开关时,界面入口与相应服务端能力使用同一配置。客户端插件存在并不代表服务端启用,最终以服务器返回的能力和路由为准。 ## 数据与请求 `user` 保存用户,`identity` 保存第三方账号或密码身份,`session` 保存会话,`verification` 保存验证信息;组织和通行密钥有独立表。表定义在 `db/schema/`,迁移由 Drizzle 管理。 认证路径是 `/api/auth/*`,`APP_ORIGIN` 定义允许来源、回调及通行密钥域名。`BETTER_AUTH_SECRET` 必须每个站独立生成,不放进前端。 ## 会话与权限 界面只通过统一查询模块读取会话。路由守卫改善访问体验,服务端的私有操作仍校验身份和资源归属。活动组织 ID 只选择操作范围,不能替代实时成员校验。 完整本地预览捕获邮件,不验证真实 Resend 投递;开发服务与线上服务使用真实邮件。详见 [账号与邮件](../agentbuff-stack/auth-mail.md)。 ## 排查顺序 先运行配置诊断,再检查浏览器请求、会话 cookie、数据库迁移和服务日志。分别测试成功、取消、过期及越权路径,不以按钮出现或登录页能打开判断功能已经完成。 --- --- url: /docs/auth/email-otp.md --- # 邮箱与验证码 ## 服务端配置 密码开关是 `websiteConfig.auth.password`,验证码开关是 `websiteConfig.auth.emailOtp`。配置位于 `apps/api/lib/auth.ts`,邮件发送位于 `apps/api/lib/email.ts`。 密码注册在开发环境不强制邮箱验证;预发布和生产环境需要验证邮件。找回密码通过一次性重置链接完成。验证码插件使用六位验证码、五分钟有效期和最多三次错误尝试。 ## 验证码流程 1. 用户选择邮箱验证码,输入邮箱。 2. 客户端请求 `auth.emailOtp.sendVerificationOtp()`,使用 `sign-in` 类型。 3. 用户输入邮件中的验证码,调用 `auth.signIn.emailOtp()`。 4. 登录成功后刷新统一会话查询,再进入工作台。 登录和注册页的验证码流程都可能为新邮箱创建账号,上线前应提供真实服务条款与隐私政策。客户端重发倒计时为 30 秒,只是界面反馈,不替代服务端限流。 ## 本地排查 完整预览使用邮件捕获器,邮件记录写入 `.local/outbox.jsonl`。只在本机查看,不提交、不分享完整记录。`bun dev` 则会使用配置的 Resend 真实发送。 线上核对 `RESEND_API_KEY`、`RESEND_EMAIL_FROM` 和已验证的发信域。修改环境后重启接口;模板改动后重新构建邮件包和接口。 ## 常见错误 | 现象 | 排查方向 | | ------------------ | ----------------------------------------------- | | 邮件没有收到 | 区分本地捕获与真实发送;查看发送错误及垃圾邮件 | | 验证码已过期 | 重新发送并使用最新验证码 | | 尝试次数超限 | 返回邮箱步骤,重新获取验证码 | | 验证成功但仍未登录 | 查看会话刷新、请求来源和 cookie | | 重置链接打不开 | 检查 `APP_ORIGIN` 和 `/reset-password` 路由分流 | 不要记录生产验证码、重置 token 或密码。验收要覆盖重复注册、错误密码、过期验证码、重复使用重置链接和退出后的接口访问。 --- --- url: /docs/auth/social-providers.md --- # 第三方登录 ## 凭据决定可用性 当前支持 Google 和 GitHub。`apps/api/lib/auth.ts` 的 `socialProviders()` 同时用于认证插件和公开能力列表,前端通过 `config.socialProviders` 显示入口。 | 服务 | 必须同时填写的变量 | 回调路径 | | --- | --- | --- | | Google | `GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET` | `/api/auth/callback/google` | | GitHub | `GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET` | `/api/auth/callback/github` | 两项都空时不启用;只填一项时配置诊断和服务端解析报错。密钥仅保存在服务端,能力查询只返回服务名称。 ## 配置步骤 1. 在对应服务创建应用,并选择正确的应用类型。 2. 将完整回调地址设置为 `APP_ORIGIN` 加上表中的路径。 3. 在本地或目标 Worker 环境写入凭据,运行配置诊断并重启接口。 4. 打开登录页,实际授权,检查回调后会话及账号邮箱。 本地 GitHub 回调示例为 `http://localhost:4400/api/auth/callback/github`。不同环境使用各自凭据和回调。GitHub OAuth 应用与 GitHub App 设置不同,后者还需要账号邮箱读取权限。 ## 客户端调用 ```ts const result = await auth.signIn.social({ provider: "github", callbackURL: "/dashboard", }); if (result.error) { // 显示可重试的授权错误 } ``` 示例中的 `auth` 来自统一认证客户端。实际回跳地址应先通过站内地址校验,不能接受任意外站链接。 ## 排查 入口不显示:检查能力查询及完整凭据。回调失败:检查域名、端口、协议和回调路径是否完全相同。邮箱缺失:检查授权范围与账号隐私设置。授权取消应回到可继续操作的登录页,不算登录成功。 新增其他服务时同步服务端凭据解析、能力查询、客户端入口及环境文档。当前 Google、GitHub 的真实外部授权仍需要使用自己的应用验收。 --- --- url: /docs/auth/passkeys.md --- # 通行密钥 ## 当前完成范围 通行密钥通过 WebAuthn 使用设备生物识别或安全密钥登录。服务端保存公钥,不保存设备上的私钥。 当前模板提供已有账号的登录入口。Better Auth 提供注册凭据接口,但模板尚未完成面向用户的凭据注册、列表、撤销和恢复界面。新用户必须保留密码、邮箱验证码或第三方登录中的可用入口,不能把通行密钥作为唯一注册方式。 ## 域名配置 `websiteConfig.auth.passkey` 控制插件与入口。`apps/api/lib/auth.ts` 从 `APP_ORIGIN` 提取 `rpID`,使用站点品牌作为 `rpName`。本地可使用 localhost;线上使用 HTTPS 和与凭据一致的域名。 不同域名注册的凭据不能默认互用。更换站点域名、预发布子域或 `APP_ORIGIN` 后,需要重新检查注册和登录。 ## 登录流程 `apps/app/components/auth/passkey-login.tsx` 检查 `window.PublicKeyCredential`,用户点击后调用 `auth.signIn.passkey()`,成功后刷新会话。 当前没有启用自动填充式凭据选择。如果后续添加,需要持久显示带 `autocomplete="email webauthn"` 的输入框,并先检测浏览器是否支持相应模式。 ## 数据与错误 凭据表在 `db/schema/passkey.ts`,包括公钥、凭据 ID、计数器、设备类型和传输方式。扩展字段不代表插件会自动填充,应核对写入逻辑。 取消设备提示、超时、不支持 WebAuthn、凭据不匹配和网络故障需要分别处理。取消不是认证成功;浏览器不支持时要保留其他登录方式。 ## 验收 补齐凭据管理界面后,分别测试首次注册、已绑定设备登录、跨设备同步、撤销后拒绝登录、取消提示及账号恢复。真实设备验收完成前,模块状态仍是部分接入。 --- --- url: /docs/auth/organizations.md --- # 组织与角色 组织用于把多个账号放进同一成员和账单范围。个人工作区和团队工作区可以切换;工具历史和积分当前仍属于个人账号,不会随着团队切换共享给其他成员。 ## 当前可以做什么 * 在工作台侧栏的工作区选择器切换个人工作区或自己所属的团队。 * 在 `/members` 创建团队;已有团队时仍可创建第二个团队。创建后自动切换到新团队。 * 按姓名或邮箱搜索整个团队的成员,每页 20 人,提供上一页、下一页和结果数量。 * 切换后重新读取会话、刷新工作区列表和成员查询,并使旧账单缓存失效。切换完成前禁用重复操作。 **邀请邮件投递、邀请接受、成员角色编辑与成员移除界面仍待开发。** 不应把现有成员目录当作完整的邀请或权限管理产品。 ## 配置与代码入口 | 位置 | 内容 | | --- | --- | | `apps/api/lib/auth.ts` | 组织插件、会话初始组织、支付权限 | | `db/schema/organization.ts` | 组织和成员数据 | | `apps/app/components/layout/workspace-switcher.tsx` | 工作区切换、加载与失败恢复 | | `apps/app/lib/queries/organization.ts` | 组织列表、创建、切换、搜索缓存 | | `apps/app/routes/(app)/members.tsx` | 成员搜索、分页和新团队表单 | | `apps/api/routers/organization.ts` | 带成员权限校验的跨页姓名 / 邮箱搜索 | | `apps/api/routers/billing.ts` | 活动组织成员校验和账单查询 | 插件允许用户创建组织,每个用户创建上限为五个,创建者角色为 `owner`。限制由服务端执行;组织选择器展示的是自己所属的组织,不能据其总数判断自己创建的组织数量。 ## 角色与权限 | 角色值 | 中文含义 | 当前组织账单权限 | | -------- | -------- | ------------------------------ | | `owner` | 所有者 | 可以管理 | | `admin` | 管理员 | 可以管理 | | `member` | 普通成员 | 可以查看所属组织套餐,不能管理 | 创建与切换调用 Better Auth 组织插件,由插件校验成员关系。成员搜索新增 tRPC 读取接口,因为插件原有过滤字段不能跨成员表搜索用户姓名和邮箱。该接口每次使用实时数据库重新校验成员关系;不信任前端组织 ID 或会话中的活动组织 ID,也不使用 Hyperdrive 缓存连接查询权限。 成员目录对组织内成员开放,只返回成员 ID、角色及用户 ID / 姓名 / 邮箱,不返回密码、凭据或其他账号字段。搜索只作用于授权组织,分页采用加入时间和成员 ID 的稳定排序;`%`、`_` 和反斜杠按普通字符搜索。查询参数限制搜索文本长度和页码范围。 ## 活动组织如何选择 新建会话时,`findInitialOrganization()` 按加入时间升序选择用户最早加入的组织;时间相同时按成员 ID 排序。没有成员关系时,活动组织为空。当前选择保存在当前服务端会话,重新登录仍按上述初始规则选择,不承诺跨会话记住上次工作区。 注册账号不会自动创建组织。成员页使用随机后缀生成唯一标识,同名团队和中文名称都可创建;创建成功后等待会话刷新,再显示新团队成员。创建失败保留表单和当前工作区,并显示服务端错误。 切换到个人工作区向插件提交 `organizationId: null`。只有服务端响应后才刷新界面,不先在本地假设切换成功。插件在拒绝已失去成员关系的工作区时可能清空活动组织,因此切换失败也要重新读取会话。侧栏保留错误提示;不可用团队可通过选择个人工作区恢复,组织列表加载失败可点击重试。 查询键包含用户、组织、搜索文本和页码。不同团队及不同搜索页面不复用结果;切换后回到第 1 页并清空成员搜索。工作区创建与切换串行执行,避免两个操作同时修改服务端会话。 ## 组织与个人账单 存在活动组织时按组织计费,否则按用户个人计费。套餐查询使用实时 `ctx.db` 校验当前成员;订阅变更由 Stripe 插件的 `authorizeReference` 再校验所有者或管理员角色。 切换时取消旧账单请求、使账单缓存失效,再刷新会话和活动账单查询。会话查询在原缓存条目上更新,保持已挂载页面的订阅连接;不能直接移除正在使用的会话查询,否则侧栏和正文可能读取不同的会话状态。成员页不会用某个团队的套餐缓存展示另一个团队的状态。真实 Stripe 收费和回调仍需单独沙箱验收。 ## 排查与验收 1. 无团队账号打开成员页:显示个人工作区说明和创建表单。 2. 创建两个团队:分别成为活动团队,两者都出现在侧栏选择器中。 3. 在两支团队与个人工作区之间切换:选择器、成员目录和账单范围保持一致;历史和积分仍为本人数据。 4. 搜索位于第二页的成员:第 1 页搜索结果能找到,不只搜索当前已加载数据。 5. 超过 20 名成员:上一页 / 下一页覆盖全部成员,无重复;无匹配项显示空状态。 6. 使用他人组织 ID 或已撤销成员关系:搜索与切换由服务端拒绝;旧会话不授予访问权。 7. 切换失败:显示错误并重新同步会话,可回个人工作区;创建达到上限时显示错误且不改变当前工作区。 自动化检查涵盖真实数据库搜索、分页、成员隔离、撤销后的访问、特殊搜索字符、工作区切换接口与客户端缓存恢复。Cloudflare 部署、邀请投递和 Stripe 沙箱不属于这次已验证范围。 --- --- url: /docs/auth/sessions.md --- # 会话与访问控制 ## 单一会话来源 会话查询在 `apps/app/lib/queries/session.ts`。页面组件使用 `useSessionQuery()`,路由守卫使用 `sessionQueryOptions()`,不要再用独立的本地存储或另一份认证会话状态。 查询结果同时含 `user` 和 `session` 才算有效。未登录返回空值,不应当作服务器故障;网络失败则不能直接当作未登录。 ## 私有路由 `apps/app/routes/(app)/route.tsx` 在进入页面前读取会话,未登录跳转 `/login`。登录回跳地址先通过安全校验,仅允许站内地址。 这是界面访问控制。真正的安全边界在服务端:私有 tRPC 操作使用 `protectedProcedure`,组织、用户数据与账单再校验归属和角色。 ## 登录与退出 登录、注册或活动组织改变后,调用 `revalidateSession()` 删除旧查询并让路由重新检查。退出时先请求服务端结束会话;请求失败显示错误,不能只把界面改成退出。 服务端退出成功后清空会话查询并整页跳转 `/login`,同时丢弃内存中的用户数据和界面状态,避免下一个账号看到旧缓存。 ## Cookie 与域名 `APP_ORIGIN` 应和访问网站的协议、域名、端口完全一致。生产使用 HTTPS;本地使用统一入口 `http://localhost:4400`。改变站点 ID 会改变认证 cookie 前缀,需要重新登录。 认证提示 cookie 只是历史路由提示,不证明会话有效;当前首页始终公开,不能据此判断登录状态。 ## 排查清单 * 登录成功后检查 `/api/auth/get-session` 是否返回用户与会话。 * 检查请求是否带上对应域名 cookie,是否混用 localhost 与 IP 地址。 * 检查会话查询是否在认证成功后刷新,网络错误是否被错误地当作未登录。 * 退出后直接请求私有接口应被拒绝。 * 活动组织成员被移除后,旧会话不能继续读取组织账单。 会话需要实时数据库连接,不能使用 Hyperdrive 的旧查询结果作为授权依据。 --- --- url: /docs/database.md --- # 数据库概览 ## 当前模型 数据库使用 PostgreSQL 和 Drizzle,开发可使用独立本地库,目标部署可使用 Neon。模型集中在 `db/schema/`,从 `db/schema/index.ts` 和 `@repo/db` 导出。 账号、会话、组织及订阅由 Better Auth 插件使用;工具记录和积分由业务接口维护。类型 `Database` 屏蔽底层驱动差异,不在业务代码依赖驱动私有连接对象。 ## 连接与权限 完整本地预览用 `DATABASE_URL` 直连;线上接口用 Hyperdrive。认证、权限、订阅和积分走实时连接,只有可接受旧结果的读取才使用缓存连接。 应用连接使用日常业务角色,迁移使用有建表权限的管理角色。角色授权脚本位于 `db/scripts/grant-app-role.sql`,由实际数据库 / 公共对象所有者在指定库执行;新密码交互输入,已有账号不自动轮换。权限、连接池与实际复验见[数据库角色与凭据](../agentbuff-stack/database-roles)。 ## 常用命令 ```sh bun db:generate bun db:check bun db:typecheck bun db:studio ``` 生成迁移和检查不会替你完成线上发布。`db:migrate` 应用于明确选择的数据库;`db:push` 直接调整结构,仅适合已确认的可丢弃本地库。 ## 环境选择 `db/drizzle.config.ts` 只接受开发、预发布和生产。预发布与生产必须存在各自 `.env.<环境>.local`,并由该文件明确提供 `DATABASE_URL`,不能回退到开发库或沿用 shell 中旧地址。 测试使用进程内 PGlite,不使用真实开发或生产连接;`ENVIRONMENT=test` 不作为数据库迁移目标。 完整归档、校验和独立空库恢复使用新增 `bun db:backup` / `bun db:restore`,详见[数据库备份与恢复](../agentbuff-stack/backup-restore)。原导出命令默认只导出结构,不作为完整备份。 继续阅读 [数据模型](./schema.md)、[数据库迁移](./migrations.md) 和 [查询写法](./queries.md)。 --- --- url: /docs/database/schema.md --- # 数据模型 ## 表的职责 | 模型 | 保存内容 | | --- | --- | | `user` | 用户资料与邮箱验证状态 | | `identity` | 认证身份,Better Auth 的 account 模型映射到此表 | | `session` | 会话及活动组织 | | `verification` | 邮箱验证、验证码等验证信息 | | `organization`、`member` | 组织资料、用户成员关系及角色 | | `invitation` | 组织邀请,投递流程尚未完整接入 | | `passkey` | 通行密钥公钥和凭据资料 | | `subscription` | Stripe 插件同步的订阅状态 | | `toolRun` | 用户工具保存结果、输入指纹和请求 ID | | `creditAccount`、`creditLedger` | 当前积分余额和事件流水 | 具体列、索引和关系以 `db/schema/*.ts` 为准。某列存在不代表已经有界面或写入流程,例如邀请扩展时间字段需要单独核对。 ## 命名与标识 TypeScript 字段用驼峰,数据库通过 `snake_case` 转换。实体使用固定前缀加 16 位 CUID2,例如 `usr_...`、`org_...`,生成器在 `db/schema/id.ts`。用户密钥使用 `key_`,签发请求记录使用 `kis_`;都保留 16 位 CUID2。前缀方便排查,不承担权限判断。 日期使用带时区时间戳;关键列设非空、外键、唯一约束和索引,不只依赖前端校验。引用用户的数据按模型定义设置删除行为。 ## 订阅与组织范围 订阅的 `referenceId` 可以指向用户或组织,没有单一外键可替代业务归属检查。读取前从会话选择范围,再实时核对组织成员。 工具记录和积分当前按用户隔离。`toolRun` 的用户与请求 ID 唯一约束防止重复保存;积分事件的用户与事件键唯一约束防止重复发放,余额有非负约束。 ## 新增或改列 修改 `db/schema/` 并补齐导出与关系,然后生成迁移、审查 SQL,再应用到隔离开发库。新增 Better Auth 字段还要同步插件配置;不要只改表然后期待插件自动读写。 涉及上线数据时分阶段增加新列、迁移数据、切换代码、最后清理旧列。详见 [新增数据表](../recipes/new-table.md) 和 [数据库迁移](./migrations.md)。 --- --- url: /docs/database/migrations.md --- # 数据库迁移 ## 本地工作流 ```sh # 修改模型后生成 SQL bun db:generate # 审查新增 SQL 和迁移元数据 bun db:check # 仅在确认目标为隔离开发库后执行 bun db:migrate ``` 生成结果位于 `db/migrations/`。SQL、快照和迁移顺序元数据一起提交,不修改已经在线上应用的旧迁移。 ## 迁移与结构推送 `db:migrate` 按已记录迁移执行,适合可追踪的发布;`db:push` 根据当前模型直接修改数据库,只用于可丢弃本地库。项目有结构推送保护,不应绕过后向生产执行。 ## 选择目标环境 ```sh bun db:migrate:staging bun db:migrate:production ``` 以上是有写入影响的命令示例,执行前确认对应 `.env.staging.local` 或 `.env.production.local` 的连接目标、备份和 SQL。文件缺失或没有显式 `DATABASE_URL` 时命令应失败,不回退到其他库。 ## 发布兼容性 数据库迁移和三个 Worker 部署不是原子操作。先增加兼容列或表,使旧代码仍能运行;部署新代码并验证后,再在后续版本删除旧结构。不要在一次发布中先删列再更新依赖该列的代码。 ## 失败排查 保留失败 SQL、迁移记录和请求标识,确认是哪一步失败。不要盲目再次推送结构或删除迁移记录;修正未应用的迁移或新增补救迁移,并在隔离数据库复现。恢复数据库与回滚代码是不同操作。 --- --- url: /docs/database/seeding.md --- # 初始化数据 ## 用途与命令 初始化脚本用于开发和预发布的测试资料,入口为 `db/scripts/seed.ts`,数据文件在 `db/seeds/`。 ```sh bun db:seed bun db:seed:staging ``` 执行前核对目标库。项目没有生产初始化命令;真实参考数据需要可审查的一次性迁移或明确的管理流程,不能把演示账号导入客户库。 ## 幂等不是自动保证 现有用户初始化使用冲突忽略。只有数据存在稳定唯一键时,`onConflictDoNothing()` 才能防止重跑重复;每次随机生成新主键且没有其他唯一约束仍会插入新数据。 新增初始化函数时,先定义稳定唯一字段,再按依赖顺序执行:用户、组织、成员、业务数据。不要使用真实邮箱或真实用户文件作为样例。 ## 演示账号与可登录账号 表里有用户不代表已经具备可用密码身份或有效会话。需要通过认证流程创建凭据,不能直接把明文密码写入身份表。本机完整预览的隔离演示账号也不随复制新站分发。 ## 排查 缺表先检查迁移;唯一冲突检查数据键;外键冲突检查执行顺序;无法登录检查认证身份而非只看用户行。开发测试优先使用 `@repo/db/testing` 的进程内数据库,减少污染真实数据库。 --- --- url: /docs/database/queries.md --- # 查询写法 ## 默认实时查询 业务接口使用 `ctx.db`。认证、组织权限、积分、订阅以及写入后的立即读取不能选 `dbCached`。缓存只在产品明确允许短期旧数据时使用。 ## 按当前用户隔离 ```ts const runs = await ctx.db.query.toolRun.findMany({ where: (run, { eq }) => eq(run.userId, ctx.user.id), orderBy: (run, { desc }) => [desc(run.createdAt)], limit: 20, }); ``` 示例需要私有接口提供有效 `ctx.user`。读取和更新都带身份条件,不能先仅按记录 ID 查出敏感数据,再依靠前端隐藏。 ## 组织权限 活动组织 ID 只代表当前范围。对组织资源查询成员表,同时匹配组织和当前用户;管理操作再检查 `owner` 或 `admin`。成员移除后旧会话不能继续拥有原权限。 ## 列表与性能 明确排序和数量上限,返回所需列即可。大量数据使用稳定分页字段;避免循环逐条查询形成多次数据库往返。索引应与实际过滤和排序匹配,通过查询计划检查后再添加。 ## 事务与唯一约束 积分余额更新、流水插入和保存结果需要按业务语义形成事务。唯一事件键防止并发或重试重复发放;非负余额约束防止越界扣款。网络响应丢失不代表服务端没执行,不能无条件重试非幂等操作。 ## 测试 验证跨用户读取、跨组织写入、并发重复事件、余额不足和事务失败回滚。使用真实数据库约束测试这些边界,避免仅模拟查询函数返回值。 --- --- url: /docs/billing.md --- # 订阅与账单概览 ## 当前实现 Stripe 订阅由 Better Auth 的 Stripe 插件管理。插件负责客户、结账跳转、账单管理和支付事件回调;`apps/api/routers/billing.ts` 返回当前套餐及可管理权限。 代码接入不等于真实支付验收完成。当前外部测试支付、事件投递、退款及取消仍需使用你的 Stripe 测试账号验证。一次性积分包购买尚未实现,现有积分流水与订阅是两个模块。 ## 启用条件 `websiteConfig.payment.enabled` 开启,并同时填写: ```text STRIPE_SECRET_KEY STRIPE_WEBHOOK_SECRET STRIPE_STARTER_PRICE_ID STRIPE_PRO_PRICE_ID ``` 四项全部留空时支付关闭;只填一部分时报错。`STRIPE_PRO_ANNUAL_PRICE_ID` 可选,但不能脱离基础凭据组配置。 ## 范围与权限 有活动组织时使用组织订阅,否则使用个人订阅。组织成员可查看所属套餐,所有者和管理员才能修改。服务端每次敏感操作重新读取成员关系,避免旧会话延续已撤销权限。 套餐名称当前固定为 `free`、`starter` 和 `pro`。价格及成员上限由站点配置派生,Stripe 价格对象必须与展示相符。 ## 代码入口 | 文件 | 内容 | | --------------------------------- | ------------------------ | | `apps/api/lib/auth.ts` | 插件启用、价格和变更授权 | | `apps/api/lib/plans.ts` | 套餐限制与管理角色判断 | | `apps/api/routers/billing.ts` | 当前订阅与权限查询 | | `apps/app/lib/queries/billing.ts` | 查询、升级和账单管理跳转 | | `db/schema/subscription.ts` | 订阅状态 | 继续阅读 [套餐与定价](./plans.md)、[结账与管理](./checkout.md) 和 [支付事件回调](./webhooks.md)。 --- --- url: /docs/billing/plans.md --- # 套餐与定价 ## 统一配置 套餐公开文案、价格和成员上限在 `packages/core/website.ts`。`packages/core/site.ts` 生成兼容视图,`apps/api/lib/plans.ts` 从该视图派生限制,认证插件和订阅查询共用。 当前 `free`、`starter`、`pro` 是固定代码标识。改显示名称不必改标识;增加新套餐要同步类型、插件配置、订阅识别、界面和测试,不能只加一张价格卡。 ## Stripe 价格对象 在 Stripe 测试模式创建对应产品和循环价格,把价格 ID 写入环境。正式模式需要另外的价格 ID、密钥和回调签名,不能混用测试对象。 `STRIPE_STARTER_PRICE_ID` 和 `STRIPE_PRO_PRICE_ID` 是基础价格;可选年付价格使用 `STRIPE_PRO_ANNUAL_PRICE_ID`。当前年付界面是否完整应按实际购买入口验收,环境变量存在不保证用户已经能选择。 ## 试用与权限 Pro 试用天数来自 `websiteConfig.payment.trialDays`,大于零才配置试用。当前订阅查询只在 `active` 的确认周期、`trialing` 的确认试用期仍有效时返回付费限制;截止还考虑提前取消 / 结束时间。周期或试用时间缺失、已到期、`past_due`、`unpaid`、`paused` 和未完成首付均不授予付费权限,当前没有宽限期。`subscriptionPlan` / `status` 仍保留原账单状态,不能将续费失败显示为没有订阅。预约取消在有效期内继续提供原方案,截止后停止;未结束记录优先于旧取消历史;检测到多笔 active / trialing 或未解决订阅进入查询错误,不能随意选一笔。 套餐里列出成员限制不代表所有业务入口已自动执行限制。增加组织成员、文件大小或任务额度限制时,要在对应服务端操作实施,界面只显示结果。 ## 修改后的验证 检查展示金额、币种、周期与 Stripe 结账一致,试用结束和取消后的权限变化符合预期。不要把订阅价格直接当成积分扣费规则,一次性积分购买已有独立事件与账本衔接,见[支付与任务教程](../agentbuff-stack/billing-tasks)。 ## 新购与继续结账 本站新购入口有服务端门禁:已有有效、试用、欠费、暂停或未确认订阅时不能新开 Checkout,方案调整使用现有门户。设置页可恢复同一请求的原方案和年付选项,不因刷新或超时重新生成付款。已知过期会话经供应商确认后才允许新请求,过期未知结果保持核查;当前管理员解除界面尚未交付。具体租约、归属和幂等规则见[支付与任务](../agentbuff-stack/billing-tasks#订阅结账保护与恢复)。 --- --- url: /docs/billing/checkout.md --- # 结账与账单管理 ## 升级流程 工作台通过 `useUpgradeSubscription()` 调用 `auth.subscription.upgrade()`,传入 `starter` 或 `pro`。有活动组织时传组织范围,否则使用当前个人账号;插件在服务端检查管理权限并创建结账会话。 成功与取消回跳目前返回发起页面。回到网站后要重新查询订阅状态,不能仅凭网址里出现成功参数授予付费能力,支付结果以已验证的事件同步为准。 ## 账单管理 `useBillingPortal()` 调用 `auth.subscription.billingPortal()` 打开 Stripe 客户门户。需要在 Stripe 配置门户允许的套餐修改、取消和资料更新功能。 前端两个操作都检查 Better Auth 返回的 `error` 并转为错误状态。不能丢弃返回错误,否则按钮看似执行却没有任何跳转。 ## 权限 个人订阅由本人管理。组织订阅由当前真实成员中的所有者或管理员管理,普通成员不能靠手动发请求绕过。`canManage` 用于界面展示,`authorizeReference` 是服务端执行边界。 ## 测试步骤 1. 以个人账号在测试模式升级,核对金额与回跳。 2. 完成回调同步,再检查数据库与工作台套餐。 3. 取消结账,确认没有错误授予套餐。 4. 用组织所有者、管理员和普通成员分别操作。 5. 从门户取消或修改,等待事件同步并重新查询。 6. 测试网络失败和重复点击,确认不会重复创建业务权益。 本地页面可见与模拟账单查询只证明代码路径,外部测试流程仍需单独完成。 --- --- url: /docs/billing/webhooks.md --- # 支付事件回调 ## 路径与签名 Stripe 插件提供 `POST /api/auth/stripe/webhook`,验证签名后更新 `subscription` 表。回调签名使用 `STRIPE_WEBHOOK_SECRET`,不能用普通 API 密钥替代。 配置目标网址为你的 `APP_ORIGIN` 加上该路径。插件处理的核心事件包括: ```text checkout.session.completed customer.subscription.created customer.subscription.updated customer.subscription.deleted ``` 这些事件用于同步创建、状态、周期和取消信息。具体行为以锁定版本插件及真实事件验收为准,不能只以浏览器结账回跳为成功依据。 ## 本地转发 ```sh stripe listen --forward-to http://localhost:4400/api/auth/stripe/webhook ``` 使用 Stripe CLI 输出的签名密钥更新 `.env.local`,填齐其他支付凭据并重启接口。如果 CLI 会话、账号或转发目标变化,重新核对当前签名密钥。 ## 原始请求体 签名验证需要原始请求体。入口保护可以有界读取并保留原始字节,再交给插件;不要转换或重新序列化它。反向代理也要保留回调方法、请求体与签名头。 ## 排查与验收 签名失败检查测试或正式模式、密钥及请求体;404 检查支付启用条件与路径;数据库未更新检查事件投递日志和迁移。 测试事件重投、乱序到达、取消和试用变化。新增积分发放等自定义处理时使用唯一事件键,不能每收到一次通知就重复加积分。当前插件订阅接入不包含一次性积分包到账处理。 ## 锁定版本的保护与缺口 回调在原始字节验签和模式核对之后,按本人 / 组织主体获取数据库租约,再用 SDK 读取当前资源。Checkout 通知还读取当前会话并核对关联。已存在订阅走插件更新,已知唯一客户的新订阅走插件创建,保持 Better Auth 对订阅表的写入职责。历史通知只触发当前状态核对,不能直接把旧载荷写回。原始请求仍由插件真实验签;没有将新对象重新编码成签名请求。 插件创建 / 更新的短事务内检查保存结果,不一致回滚;`onEvent` 在提交后继续使用 uncached 数据库核对,覆盖创建、状态、方案、席位、周期、取消与日程关联。供应商读取失败或资源列表不完整返回 503;插件内部吞掉数据库错误后,保存校验仍返回非 2xx。适配器将锁定创建处理器遗漏的取消 / 结束 / 日程字段补入同一次插件创建,不另写状态表;数据库触发器静默改动状态也会被校验并回滚。每个 SDK 客户端独立包装验签方法,避免共享对象串用模式或租约。 内部事件类型用于选择当前对象的插件处理路径,不保证原通知对应的完成 / 删除钩子触发。新邮件、积分等一次性副作用需要独立持久去重与发件箱,按 I12 实施;本轮没有新增此类副作用。当前状态读取不是 Stripe 和本地数据库的原子事务,新变化依赖后续通知收敛。GET 回跳复用当前资源和保存校验;锁定版本补丁修复已可用订阅跳过更新及结束 / 日程字段遗漏。没有伪造签名事件,回跳不作为单独付款确认。 原始载荷上限为 256 KiB,超过返回 413;租约忙返回 503,旧令牌不能写插件状态。`bun subscriptions:validate` 验证八个实际 PostgreSQL 连接、Better Auth handler 和受控 SDK HTTP,包括七个并发通知先重试、随后均保存当前状态。`bun payments:validate` 在生产 Worker 验证正常事件、旧通知、读取失败和数据库故障重投,SDK 订阅读取经本地出站拦截提供受控结果,没有访问实际 Stripe。原生 Worker 另用实际注册会话验证 GET 回跳、已可用状态更新、读取 / 保存失败无跳转及修复重试;所有客户创建、Checkout / 订阅读取均为受控 SDK HTTP。两者不能证明真实收款或原生 Checkout 创建 / 门户 HTTP 已验收。 真实沙箱、年付首购界面、未知过期 Checkout 自动找回和生产绑定继续按 I6 验收。结账原请求恢复、状态权限和其他已知缺口见[中文教程](../agentbuff-stack/billing-tasks#订阅结账保护与恢复)。 --- --- url: /docs/email.md --- # 邮件发送 ## 结构 邮件模板在 `apps/email/`,API 的 `apps/api/lib/email.ts` 提供验证邮件、密码重置和验证码的发送函数。模板导出同时生成 HTML 与纯文本,所有发送经过 `sendEmail()`。 ```sh bun email:dev bun email:build bun email:export ``` 模板预览默认使用 3001 端口。构建先生成邮件包,再让接口引用;根目录 `bun build` 已按依赖处理。 ## 两种发送模式 完整本地预览捕获 Resend 请求,将记录写入被忽略的 `.local/outbox.jsonl`,不调用真实发送接口。`bun dev` 和线上 Worker 使用真实 Resend。 真实发送需要有效 `RESEND_API_KEY`、`RESEND_EMAIL_FROM` 和已验证发信域。先测试普通邮箱、垃圾邮件和退信,再验证认证链接;不要把本地捕获成功当成投递成功。 ## 新增模板 创建模板并从邮件包导出,在 API 增加对应封装,传入站点品牌和正确网址。模板要有纯文本版本,链接地址从可信配置派生,不接受任意外部回跳地址。 组织邀请尚未配置发送回调,需要增加模板和插件衔接后再验收。通知、营销邮件与退订管理也不是现有事务邮件自动具备的能力。 ## 排查 先确认运行模式,再检查配置、模板构建、发送返回错误和投递记录。`sendEmail()` 验证所有收件人;服务返回错误会继续抛出,调用方需要显示或处理失败。 邮箱、验证码和重置链接属于敏感记录,本地排查后不提交收件箱文件;生产日志不能打印完整验证码或认证链接。 --- --- url: /docs/testing.md --- # 测试与排查 ## 按改动选择检查 ```sh bun test --run bun typecheck bun web:check bun lint bun format:check bun build bun run docs:build ``` 根配置在 `vitest.config.ts`,测试靠近实际代码。接口和数据边界使用进程内数据库;React 行为测试使用浏览器模拟环境;Astro 模板另由 `web:check` 检查。 只改教程时检查格式、文档构建和浏览器内容即可,不必为可逆文案改动编写测试。修改认证、数据归属、积分或账单时,需要有意义的边界测试。 ## 数据库测试 `@repo/db/testing` 的 `createTestDatabase()` 创建独立 PGlite,应用已提交迁移。每个测试文件建立自己的实例,测试间调用 `reset()`,结束时 `close()`。 ```ts import { createTestDatabase } from "@repo/db/testing"; const database = await createTestDatabase(); try { // 使用 database.db 插入测试身份并执行实际查询 await database.reset(); } finally { await database.close(); } ``` 不要向真实开发或生产库运行测试。测试应用迁移,能发现漏迁移、唯一约束和外键问题,不能改成结构推送来掩盖失败。 ## 接口测试 使用路由调用器和明确上下文,覆盖成功、未登录、跨用户、跨组织、移除成员及重复事件。涉及事务、余额和唯一约束时使用真实数据库语义,避免只模拟“数据库返回成功”。 现有参考包括 `apps/api/routers/workspace.test.ts`、`billing.test.ts`、`db/schema/index.test.ts` 和账号配置测试。测试数量会随迭代变化,验收记录按当时日期保存。 ## 界面测试 覆盖加载、空数据、提交失败、键盘和主题;认证成功后检查会话刷新,退出失败时保留真实登录状态。新增路由还要检查直接加载与边缘分流,不能只点击站内链接。 ## 手工验收与限制 检查公开原始 HTML、工具真实输出、复制与下载、账号切换后的数据隔离。第三方登录、邮件投递、Stripe 测试支付及线上 Hyperdrive 必须分别实测;本地测试通过不自动证明这些服务完成。 失败时先定位配置、网络、路由、认证、数据库或界面状态,再运行对应的最小复现。保留必要错误和请求标识,不输出秘密或用户内容。 --- --- url: /docs/deployment.md --- # 部署上线 ## 准备顺序 先在隔离本地环境确认网站、账号和工具流程,再准备预发布资源,最后配置生产。部署目标由公开站、工作台和接口三个 Worker,加上 PostgreSQL、两个 Hyperdrive 及外部邮件和支付服务组成。 完整配置及验收表见 [部署与验收](../agentbuff-stack/deployment.md)。本教程说明命令和责任边界,不表示已经替你创建或部署这些资源。 ## 发布前检查 1. 替换域名、品牌、发信地址和占位配置,生成各环境独立认证密钥。 2. 配置数据库、应用角色及两个 Hyperdrive,写入正确环境的绑定 ID。 3. 设置 Worker 环境变量与秘密,核对第三方回调和价格。 4. 完成类型、格式、测试、网站和文档构建。 5. 备份数据库并审查本次迁移,确保旧代码兼容新结构。 ## 手动发布 ```sh # 以下命令会更新对应环境,准备完成后再执行 bun db:migrate:staging bun deploy:staging ``` 生产对应 `db:migrate:production` 和 `deploy:production`。部署脚本不自动迁移数据库;CI 则先迁移再部署 Worker。发布顺序为接口、工作台、公开入口,三个步骤不是原子操作。 ## 发布后验证 检查首页静态正文、工具预览及下载、注册与会话、历史隔离、积分幂等、邮件投递、订阅事件与组织权限。配置诊断和编译通过不能替代线上验收。 文档单独构建和发布;本地文档默认禁止搜索索引,公开部署需要正确 `DOCS_SITE_URL`。客户站的 `PUBLIC_SITE_URL` 与 `APP_ORIGIN` 应指向同一正式域名。 详细步骤见 [部署边缘服务](./cloudflare.md)、[生产数据库](./production-database.md) 和 [自动检查与发布](./ci-cd.md)。 --- --- url: /docs/deployment/cloudflare.md --- # 部署边缘服务 ## 配置文件 三个服务分别使用 `apps/api/wrangler.jsonc`、`apps/app/wrangler.jsonc` 和 `apps/web/wrangler.jsonc`。生产配置位于文件顶层,预发布在 `env.staging`;Wrangler 的生产选择器是空环境名,根部署脚本已负责转换。 复制新站时修改 Worker 名称和服务绑定,不能把新站入口绑定到旧站的账号接口。`APP_ORIGIN`、公开域名、资源 ID 和发信地址都需要替换占位值。 ## 设置秘密 以下是交互写入示例,不把值写入命令历史: ```sh bun wrangler secret put BETTER_AUTH_SECRET --config apps/api/wrangler.jsonc --env staging bun wrangler secret put RESEND_API_KEY --config apps/api/wrangler.jsonc --env staging ``` 生产选择 `--env=""`。按需配置完整 Google、GitHub 或 Stripe 凭据组;只配置正在使用的集成。两项基础秘密属于必需项,开发捕获邮件模式的临时值不能用于生产。 ## 资源绑定 接口绑定 `HYPERDRIVE_CACHED` 和 `HYPERDRIVE_UNCACHED`;公开站绑定 `API_SERVICE` 与 `APP_SERVICE`;公开站和工作台各有静态资源绑定。生产 ID 必须是该环境真实资源,而不是本地占位名称。 创建基础设施与部署应用分开管理。Terraform 可创建 Hyperdrive 等依赖;Wrangler 部署 Worker 代码及绑定,见 [基础设施边界](../adr/002-terraform-wrangler-boundary.md)。 ## 构建与发布 ```sh bun build # 确认配置与迁移后执行 bun deploy:staging ``` 脚本检查邮件、公开站和工作台构建目录,再依次部署接口、工作台和公开入口。不要使用缺失或旧构建产物。单个服务命令仅适合明确范围的维护,完整发布优先用统一脚本。 ## 排查 绑定失败检查目标服务名称和环境;数据库失败检查两个 Hyperdrive;邮件 403 检查发信域,`onboarding@resend.dev` 不是面向用户的正式发信地址;注册失败还要检查非开发环境的验证邮件流程。 部署完成后实际检查线上会话、日志与请求标识,不能以 Wrangler 上传成功认定产品流程通过。 --- --- url: /docs/deployment/production-database.md --- # 生产数据库 ## 连接分工 可使用 Neon PostgreSQL。应用运行通过 Hyperdrive 使用业务角色,数据库迁移通过管理连接。不同站点和环境保持独立数据与凭据,生产不使用本机演示数据库。 使用直接数据库主机连接 Hyperdrive,不叠加 Neon 的事务连接池。现有 Terraform 配置会拒绝带 `-pooler` 的主机及不支持的连接串格式。 ## 准备步骤 1. 创建生产数据库及独立业务角色,保留管理连接用于迁移。 2. 审查并执行初始迁移,授予业务角色必要权限。 3. 为实时和缓存查询分别创建 Hyperdrive,配置正确源连接。 4. 将两个资源 ID 写入接口生产配置,再设置应用秘密。 5. 在预发布先验证连接、权限与迁移兼容性。 `db/scripts/grant-app-role.sql` 已在独立本地密码认证实例验收,由实际数据库 / 公共对象所有者运行;不由部署自动执行。新密码交互输入,已有账号重复授权不会改密码,旧参数传密码方式被拒绝。权限边界、事务回滚与复验见[数据库角色与凭据](../agentbuff-stack/database-roles);真实 Neon 权限仍需在自己的环境验证。 ## 缓存和连接额度 权限、会话、账单、积分和写后读使用实时连接。缓存连接只用于能接受短期旧结果的读取。两个 Hyperdrive 分别占用连接预算,要把它们的总量纳入源数据库限制。 缓存时间和连接上限以 `infra/` 当前配置与账户限制为准,不能仅因使用免费套餐就假设不会产生费用或限制。 数据库改密码不会自动断开已有连接。更新所选环境两个 Hyperdrive 的源凭据后,也需核对旧连接池的处理和原业务恢复;不能只改本机连接或据此宣称旧连接已撤销,具体步骤见上述角色教程。 ## 备份与恢复 发布前确认数据库备份和恢复能力;在隔离环境验证恢复,而不是只确认有一个备份按钮。回滚 Worker 不会撤销数据库迁移,破坏性改动需要单独恢复或补救方案。 ## 排查 连接失败核对主机、数据库、角色、SSL 和网络;权限错误核对表及序列授权;缺表检查目标库的迁移记录;只在生产出现旧权限结果时检查是否误用了查询缓存。 --- --- url: /docs/deployment/ci-cd.md --- # 自动检查与发布 ## 工作流分工 | 文件 | 触发与职责 | | --- | --- | | `.github/workflows/ci.yml` | 主分支提交、拉取请求、合并队列和手动检查;验证并构建 | | `.github/workflows/deploy.yml` | 被检查工作流调用;迁移与部署应用 | | `.github/workflows/infra.yml` | 手动规划与应用基础设施变更 | | `.github/workflows/pr-title.yml` | 检查拉取请求标题 | 实际条件以工作流源码为准。模板携带这些流程,不代表你的仓库已配置所需变量、环境权限和秘密。 ## 检查与构建 检查安装锁定依赖,运行格式、静态检查、基础设施验证、类型检查、Astro 检查、测试、应用和文档构建。基础设施验证用 `init -backend=false -lockfile=readonly`,不读取远程状态,也不创建资源。 应用构建产物供部署任务复用,避免检查一个版本又部署另一个未经检查的产物。产物恢复到 `apps`,邮件包也需要存在,因为接口引用它。 ## 启用应用发布 部署默认通过仓库变量 `DEPLOY_ENABLED` 控制,只有明确设为 `true` 才启用。主分支提交可发布预发布;手动运行并选择生产发布时,必须从主分支发起,配置不满足会失败。 在 GitHub 配置 `staging` 和 `production` 环境,各自保存 Cloudflare 发布凭据、数据库管理连接及所需应用秘密,字段以 `deploy.yml` 为准。使用环境级秘密与主分支规则,避免未审查分支接触发布凭据。 ## 发布步骤与失败处理 任务先检查构建产物,再迁移目标数据库,然后调用 `scripts/deploy.ts` 依次更新接口、工作台和公开入口。统一脚本本身不执行迁移,手动发布需要先单独完成。 这不是原子事务。中途失败可能出现部分 Worker 已更新,数据库也可能已迁移。修复时先确认各服务实际版本和迁移记录,再补齐发布;不要无条件取消、回滚数据库或重做破坏性迁移。 数据库变更采用分阶段兼容:增加新结构、发布新代码、后续清理旧结构。发布任务不取消正在执行的发布,拉取请求检查可取消过时运行。 ## 基础设施工作流 基础设施只手动执行,每个环境串行。规划任务保存计划并写入运行摘要;应用任务应用同一份已保存计划,不重新计算。无变更时跳过应用。 规划与应用使用不同 GitHub 环境:`infra-staging-plan`、`infra-production-plan`、`infra-staging-apply`、`infra-production-apply`。规划使用专用令牌,应用令牌按环境对应的 HCP 工作区隔离。生产审批规则是否可用取决于仓库可配置能力,必须到设置中核实;若不可用,保留规划,在可信机器执行明确审查过的操作。 规划不是对不可信代码的沙箱,远程配置可接触工作区凭据。两个任务都限制主分支,环境分支规则是工作流自身不能绕过的约束。基础设施凭据与应用发布凭据分别管理。 ## 复制新站后检查 更新仓库变量、环境名、主分支规则、资源名称、远程状态工作区与秘密。确认实际检查结果及发布目标;一次绿色运行也可能只有检查,没有发布,要查看被跳过的任务与条件。 修改工作流时保留固定提交版本的第三方 Actions、最小权限和不持久保存的检出凭据。不要把密钥或完整环境值写入运行日志和摘要。 --- --- url: /docs/deployment/monitoring.md --- # 日志与监控 ## 服务端日志 ```sh bun wrangler tail --config apps/api/wrangler.jsonc --env staging ``` 生产选择空环境名。先确认目标服务和环境,再用请求标识关联接口错误、用户操作和数据库状态。三个 Worker 的日志分别查看,入口成功不等于下游接口成功。 ## 本地排查 `bun run preview:status` 显示受管理服务状态,`.local` 保存对应日志和邮件捕获记录。更改构建产物后重新启动预览;不要误停其他对比模板服务。 ## 观察什么 关注接口错误率、响应耗时、数据库连接、邮件发送失败、支付回调失败、工具失败与重复请求。公开站访问统计由 `websiteConfig.analytics` 控制,可按需接入 Cloudflare 的公开页面统计;它不是业务错误监控。 日志只保存必要的错误类型和标识,不记录密码、认证 token、支付密钥或用户文件正文。建立告警时先确认通知通道真实可用。 ## 回滚与成本 发布失败先确认各服务版本及数据库迁移是否已执行,再选择补齐发布或回滚兼容代码。Worker 回滚不会恢复数据库和第三方订阅状态。 分别观察 Worker、数据库、存储与外部服务用量。当前主站未接入对象存储、队列或生成模型,不应把这些模块的费用纳入“已验证免费运行”结论。 --- --- url: /docs/recipes/new-page.md --- # 新增页面 ## 新增工作台页面 创建 `apps/app/routes/(app)/projects.tsx`: ```tsx import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/(app)/projects")({ component: Projects, }); function Projects() { return (

项目列表

在这里显示当前账号的项目。

); } ``` 这是新增路由示例,当前模板没有项目列表模块。启动开发或构建后生成路由树,再使用统一组件完善布局和数据状态。 ## 注册入口路径 把 `projects` 加入 `apps/web/worker.ts` 的工作台路径列表,并同步 `scripts/local-gateway.ts`。不要与 `apps/web/pages/` 的公开页面重名。直接打开、刷新与站内点击都要落到同一个应用。 导航使用 TanStack Router 的类型化 `Link`,数据从统一查询模块读取,显示加载、错误与空列表状态。界面中的中文是教程示例,客户网站文案按产品语言编写。 ## 公开页面 营销、博客和搜索任务页创建在 `apps/web/pages/`,保留静态正文、标题、规范网址及结构化信息。无需登录的交互应用页面可放在账号路由分组,但仍要注册边缘路径。 ## 验证 构建后检查正常账号、未登录、无数据、加载失败和直接刷新。公开页检查原始 HTML 正文;私有页检查退出后接口拒绝访问。不要手改 `routeTree.gen.ts`。 --- --- url: /docs/recipes/new-procedure.md --- # 新增接口 ## 创建与注册 先明确接口是公开查询还是私有操作,输入、返回字段和身份范围。下面创建一个当前用户摘要,不涉及新增数据表: ```ts // apps/api/routers/profile.ts import { protectedProcedure, router } from "../lib/trpc"; export const profileRouter = router({ summary: protectedProcedure.query(({ ctx }) => ({ id: ctx.user.id, name: ctx.user.name, })), }); ``` 在 `apps/api/lib/app.ts` 导入并加入 `appRouter` 的 `profile` 属性。工作台即可通过统一客户端调用 `trpcClient.profile.summary.query()`。 ## 查询模块 在 `apps/app/lib/queries/` 定义唯一查询键与查询函数,再供组件使用。不要在多个页面各自创建不同键读取同一份数据。 变更使用 `.mutation()`,成功后刷新受影响查询。输入通过 Zod 校验;私有操作默认不重试,只有业务已具备幂等键时才能考虑自动重试。 ## 数据与权限 按 `ctx.user.id` 或已校验组织范围过滤数据。组织账单、积分、权限及写后读用 `ctx.db`,不使用缓存连接。资源不存在与不可见如何返回,要避免泄露他人资源是否存在。 ## 验证 使用私有路由调用测试合法输入、未登录和越权。涉及唯一约束、积分和事务时使用 PGlite 测试真实数据库语义,见 [测试与排查](../testing.md)。 --- --- url: /docs/recipes/new-table.md --- # 新增数据表 ## 示例模型 以下新增个人项目表的示例尚未存在于模板: ```ts // db/schema/project.ts import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { generateId } from "./id"; import { user } from "./user"; export const project = pgTable("project", { id: text() .primaryKey() .$defaultFn(() => generateId("prj")), userId: text() .notNull() .references(() => user.id, { onDelete: "cascade" }), name: text().notNull(), createdAt: timestamp({ withTimezone: true, mode: "date" }) .defaultNow() .notNull(), }); ``` 从 `db/schema/index.ts` 导出新表;根据查询补充索引和关系。字段长度等输入约束仍需服务端明确校验。 ## 迁移 ```sh bun db:generate bun db:check # 审查 SQL,确认目标为隔离开发库后执行 bun db:migrate ``` 提交 SQL、快照和元数据。测试数据库应用已提交迁移,漏迁移会直接暴露为缺表问题。 ## 接口与页面 在私有接口按当前用户过滤,写入用户 ID 从会话派生,不接收客户端指定身份。然后增加统一查询模块和工作台页面。 新增初始化资料时设置稳定唯一键,不依赖随机 ID 来防重。生产结构改动采用兼容迁移,禁止使用结构推送代替审查。 ## 验证 检查删除行为、跨用户隔离、索引和迁移执行,确认导出可以被 API 正确引用。新增表不会自动获得界面、组织权限或套餐限制。 --- --- url: /docs/recipes/teams.md --- # 扩展组织分组 ## 与现有组织的区别 模板已接入组织、成员及角色。这里的分组指一个组织内部的多个小组,不是现有 `/members` 页面,也不是另一套个人账号。 当前没有启用 Better Auth 的完整分组模型、配置和管理界面。这是一份扩展流程,不能作为已经交付的功能使用。 ## 实现步骤 1. 明确成员属于组织还是小组,哪些操作由组织角色管理,哪些需要小组权限。 2. 根据项目锁定版本的组织插件接口启用分组能力,并补齐相应表、关系和迁移。 3. 同步服务端和客户端插件类型,验证创建、加入、移除及活动小组选择。 4. 增加工作台管理页面及包含组织、小组 ID 的查询键。 5. 在具体业务接口实施小组资源权限,不能只依靠当前小组 ID。 不要复制一段插件配置后假设数据库和权限自动齐全。先对照当前 `apps/api/lib/auth.ts` 与 `db/schema/`,避免重复创建已有组织表。 ## 验收 测试跨组织加入被拒绝、成员移除后的旧会话、删除小组的关联数据、普通成员的管理请求及切换后的查询刷新。组织邀请邮件尚未接入,若小组流程依赖邀请,要先完成邀请投递和接受流程。 --- --- url: /docs/recipes/websockets.md --- # 实时连接 ## 当前状态 `packages/ws-protocol/` 提供消息定义、路由与独立 Bun 示例。主网站尚未把实时连接接入登录、任务进度或 Cloudflare 生产架构。 演示服务器的认证函数只返回连接时间,广播接口也是测试用途;它们不能直接作为生产身份和权限实现。 ## 运行独立示例 ```sh bun --cwd packages/ws-protocol run example ``` 示例默认监听 3000,连接路径是 `ws://localhost:3000/ws`。先确认端口未占用,结束后停止本次进程。消息结构见 `messages.ts`,示例支持探测、回声和请求响应。 ## 接入主站需要补齐 在升级连接前校验有效会话与来源,按用户或组织授权订阅主题;限制消息大小、频率及连接数,校验所有消息输入。断线重连需要恢复订阅并防止重复业务写入。 独立 Bun 示例使用内存发布订阅,不能保证多个实例间共享。Cloudflare 部署需要适配其连接生命周期及持久协调方式,不能直接部署 `Bun.serve()`。 ## 是否需要实时连接 短任务可先使用普通请求,长任务可先使用队列和状态查询。队列和模型任务当前也未接入;选择实时连接应基于真实进度需求,而不是仅为模板增加功能名称。 验收需覆盖未登录、跨用户主题、消息超限、断线恢复和多实例行为。 --- --- url: /docs/recipes/file-uploads.md --- # 文件上传 ## 当前实现 私有文件 API 与定时清理已通过本地 Workers 验收;文件工作台和两个生成站的完整流程仍待 I2,云端资源未部署。配置、请求格式、迁移、错误代码及排查以[私有文件存储教程](../agentbuff-stack/files-storage)为准。 ```mermaid flowchart LR U[登录账号发送二进制] --> V[服务端检查来源、身份、格式与大小] V --> Q[事务预留数量和字节] Q --> S[通过私有 R2 绑定写对象] S --> M[标记元数据可读取] M --> D[会话授权读取或下载] M --> C[定时批次清理与配额释放] ``` 元数据使用 tRPC,二进制使用 Hono。服务端原生 R2 绑定负责读写,当前无需 S3 访问密钥、浏览器直传许可或桶 CORS。文件名与客户端 MIME 只作提示,实际内容需通过检查。 ## 扩展新类型 先确认真实产品是否需要服务器保存文件;纯客户端处理可以避免上传。增加 PDF、SVG、视频或其他输入时,在 `apps/api/lib/files/validation.ts` 增加实际格式检查,明确最大大小、可执行内容的下载方式和保留策略,再补前端与真实样例验收。仅添加 MIME 字符串或上传控件不能构成安全支持。 首版文件归个人所有,切换组织不改变所有权。团队共享需另定义真实成员授权;异步任务的文件引用、删除与结果保留在 I4 接入,当前不能将正在执行的任务当成已有功能。外部 URL 输入还需独立实现网络范围、重定向、大小和超时限制。 ## 故障与验证 数据库与 R2 之间使用显式补偿,不能共同事务提交。失败上传不可下载,确认对象删除后才释放额度;失败删除保留占用,由定时批次重试。孤立对象扫描只处理本站前缀并使用持久化游标,未知对象保留至少一小时。 运行 `bun files:validate` 验证真实本地 Worker 的四种类型、下载字节、摘要、跨账号拒绝、过期拒绝、删除和定时清理。并发配额与故障恢复另由实际迁移及 R2 测试覆盖。云端绑定、触发和限制需要真实环境单独验收。 --- --- url: /docs/security/checklist.md --- # 上线检查 ## 已有边界 模板有服务端会话校验、字段校验、组织账单成员及角色检查、用户历史隔离、积分事务及唯一事件约束。已有代码和测试仍需要在你的站点配置及实际环境下验收。 ## 发布前逐项检查 | 范围 | 检查内容 | | ---------- | ---------------------------------------------------- | | 密钥 | 独立认证密钥;环境文件不提交;公开构建没有服务端凭据 | | 账号 | 注册、验证、找回、过期和退出;保留可用注册方式 | | 权限 | 私有接口未登录拒绝;跨用户、跨组织及已移除成员拒绝 | | 数据库 | 应用与管理角色分离;迁移审查;备份可恢复 | | 积分 | 重试不重复发放;不足不负余额;失败事务不留半条记录 | | 支付 | 正确模式与价格;签名校验;事件重投和取消同步 | | 邮件 | 真实发信域、投递和退信;生产日志不打印验证码 | | 网页 | 用户文本正确转义;依赖更新;可信来源和 HTTPS | | 文件与网址 | 若启用,检查大小、真实格式、访问范围及下载授权 | | 运营信息 | 真实联系方式、服务条款、隐私政策和数据保留说明 | 组织邀请、完整通行密钥管理、上传和队列当前未完整接入,必须完成相应实现后再勾选验收。 ## 自动检查 ```sh bun run config:check bun typecheck bun web:check bun test --run bun lint bun format:check bun build bun run docs:build ``` 根据改动范围选择检查。自动检查不能证明第三方授权、邮件投递或支付成功;线上验收需要真实测试账号和失败路径。 ## 问题处理 发现凭据泄露应立即轮换并审查访问记录;发现权限问题先限制受影响入口再修复。参考 [故障与安全事件处理](./incident-playbook.md),不要把密钥和用户资料贴到公开问题中。 --- --- url: /docs/security/incident-playbook.md --- # 故障与安全事件处理 ## 准备 保留服务版本、发布记录、数据库备份、请求标识及负责人联系方式。确认谁可以停止发布、轮换密钥、撤销会话和恢复数据,提前在隔离环境验证恢复步骤。 ## 判断影响 记录首次发现时间、症状、相关环境、受影响接口和版本。区分服务故障、权限绕过、凭据泄露和数据完整性问题。不能把没有观察到异常写成已经证明没有影响。 ## 暂时控制 停止相关发布或限制受影响功能;泄露密钥立即轮换,对被盗会话撤销。保留必要日志和数据库证据,避免盲目删除记录或重建生产库。 处理要聚焦真实受影响范围。公开站统计失败不必停用登录;账号权限绕过则需要及时限制相关私有操作。 ## 调查与修复 关联请求标识、版本及数据变更,建立事件时间线,在隔离环境复现。修复后验证原复现路径、相邻权限及重复操作。记录已经确认和仍然未知的事实。 发布兼容修复时核对各 Worker 和数据库实际状态。回滚代码不等于恢复数据,也不会撤销第三方支付操作。 ## 恢复与沟通 逐步恢复功能,监控错误和越权情况。按实际数据影响与适用义务处理通知,沟通只包含必要事实,不公开可继续利用的细节或个人资料。 ## 复盘 记录原因、影响范围、发现渠道、处理时间和改进项。将缺失的权限测试、告警、备份或发布检查补进维护流程,明确负责人和完成标准。 --- --- url: /docs/security/policy-template.md --- # 安全政策模板 本页是维护者填写模板,不是已经对外生效的承诺。发布到自己的仓库或网站前,替换占位字段并按照实际维护能力填写支持版本、响应时间和联系渠道。 ## 适用范围 `[项目名称]` 的安全政策适用于 `[仓库地址与组件范围]`,包括账号、接口、数据处理和发布配置。第三方平台的问题按其政策报告,测试必须限于得到授权的范围。 ## 支持版本 | 版本 | 是否提供安全修复 | | ---------------- | ---------------- | | `[当前支持版本]` | 是 | | `[停止支持版本]` | 否 | ## 私下报告 报告渠道:`[安全联系邮箱或私密报告地址]`。不要在公开问题中发布密钥、个人资料或可立即利用的漏洞细节。 报告包括受影响版本、问题描述、最小复现步骤、实际影响及可选修复建议。只提供复现所需资料,不收集或泄露其他用户数据。 ## 响应流程 维护者在 `[能够兑现的初次响应时间]` 内确认收到;验证后说明严重程度、临时控制措施和后续进度。修复与披露时间根据实际影响共同确认,不填写无法兑现的固定承诺。 研究人员避免破坏数据、拒绝服务、持续利用或社交工程。是否授予测试权限、提供奖励及采用何种披露安排,需要项目方明确说明,模板本身不授予任何法律权限。 ## 修复与公开说明 验证修复后发布受影响版本、修复版本、必要升级步骤和研究人员署名意愿。保留事件处理记录,依据 [事件处理教程](./incident-playbook.md) 复盘。 --- --- url: /docs/agentbuff-stack/validation-2026-10-09.md --- # 0.1 本地验收 · 2026-10-09 来源基底:Kriasoft React Starter Kit,commit `dbdb0d4991bbaae1671ff62180089f2733dd406a`。独立目录 `saas-starter/launchkit`,原演示项目保留。MIT 许可证未改动。 ## 已执行的检查 | 检查 | 结果 | | --- | --- | | 全工作区构建(email / web / api / app) | 通过 | | TypeScript project build | 通过 | | Astro check | 0 errors / 0 warnings / 0 hints | | Vitest | 11 文件、104 测试通过 | | Oxlint | 通过 | | Oxfmt check | 通过 | | web / app / api 的 Wrangler dry-run dev 打包 | 均通过;未上传或部署 | | 新站工厂真实临时副本 | 品牌/域名/Worker 名称替换、保留许可证、无凭据或本地数据、拒绝覆盖,均通过 | | 公开 HTML | 首页/两个工具/博客在未执行 JavaScript 时含正文、H1、canonical | | sitemap / RSS | XML 可解析;私有页面和法律占位不在 sitemap | | 法律占位与本地预览 | noindex | | 不存在的公开路径 | 返回 404 | | 找回密码邮件 | 本地 outbox 捕获;不向外发信 | ## 浏览器实际流程 在 localhost:4400 完成:打开 JSON 工具 → 使用示例 → 格式化预览 → 未登录保存 → 登录本地测试账号 → 回到工具且输入/预览保留 → 保存成功 → 下载 JSON → Dashboard 显示 1 条私有历史和 20 欢迎积分 → History 展开并看到真实保存结果。 截图在被 Git 忽略的 `.local/evidence/home.png` 和 `.local/evidence/history.png`。下载样例位于浏览器下载目录。 ## 安全与数据检查范围 使用实际迁移建立 PGlite 测试数据库,验证未登录拒绝、不同账号隔离及无法越权删除、并发重试仅保存一次、复用 request ID 修改输入被拒绝、无效/过大/过深或过度展开 JSON 不写入数据库、历史数量上限、欢迎积分仅发一次、并发重复扣费/退款幂等、超余额扣费拒绝。登录回跳拒绝其他来源、反斜杠和控制字符。 本机另有隔离的 PostgreSQL 数据库 `launchkit`。本地服务以 detached process 启动,避免结束当前对话时丢失预览进程。 ## 尚未验收 Cloudflare 实际部署、Neon/Hyperdrive 线上连接、第三方 Google/Passkey、真实 Resend 发信、Stripe 收费/webhook/取消与 portal。R2、队列、一次性积分包、管理员、统计、多语言等尚未实现;完整状态见 [能力清单](capabilities.md)。 dry-run 是打包检查,不证明线上 CPU/额度、服务绑定、账号权限或真实支付成功。 --- --- url: /docs/en/agentbuff-stack/auth-mail.md --- # Accounts, teams and email The workspace uses Better Auth with TanStack Query session wrappers. Pages requiring authentication wait for a valid user and session. Do not read `auth.useSession()` directly when extending the application; use existing query wrappers so sign-in, sign-out and invalidation share one cache. ## Local workflow 1. Open `/signup` or `/zh/signup`, register with an email and password. 2. Inspect the captured verification email in the local outbox; open its signed link. 3. Sign in and use the dashboard. Password recovery opens the reset page in the chosen language. 4. Use settings to review sessions, linked providers and passkeys. Sensitive changes can require fresh sign-in. The language header selects presentation only. It never grants permissions or changes account identity. Unknown/disabled language requests fall back to English for immediate emails. Email themes, preview text and body come from one dictionary. Signed authentication endpoints remain under root `/api/auth`; localized callbacks preserve safe return destinations. ## Teams Create or switch a team in the workspace. Owners and authorized administrators can invite members and manage permitted roles. Invitations require the receiving email to match a verified account. Personal files, saved results, tasks and credits stay personal after switching teams. Team ownership is separate from site administration. A team owner does not gain site-wide access. Leaving as the only owner requires an ownership transfer first. Review confirmations before removing members or transferring a team. ## Configure services Password, email OTP and passkey switches are in product configuration. Google/GitHub require complete server credential pairs. Local email is captured by the launcher; production uses the configured Resend sender. Building templates successfully does not prove external delivery or OAuth/passkey browser acceptance. After editing email templates, rebuild the email package and API before restarting preview. Never share outbox verification codes or include them in a source commit. See [Environment](./env), [Languages](./languages) and [Administration](./admin-audit). --- --- url: /docs/en/agentbuff-stack/admin-audit.md --- # Administration and audit Site administrators are a separate role from organization owners and members. Ordinary accounts do not become administrators after a migration or language change. The admin page is protected by server authorization, not just a hidden menu. ## Review and change records Use `/admin` or `/zh/admin` with a deliberately bootstrapped administrator. Account search, task records, financial records, audit and optional notification/referral evidence are separated views. Exact audit filters can be combined; request and completion records show the lifecycle of a recorded action. Sensitive actions require a reason, review confirmation and fresh authentication when requested. Refresh after uncertain responses before repeating a change. Credit adjustments retain payment holds; task retries retain original attempt limits and reservations. Payment checks do not initiate a charge/refund at the provider. Raw identifiers, state codes, reasons, ledger notes and user-entered content stay as recorded. UI labels and explanations are translated; historical evidence is not rewritten. ## Bootstrap and operation Use the repository's `admin:bootstrap` only against a verified intended database/account. Do not grant all preview users administrator access. The operator commands expose bounded, audited recovery rather than direct unrecorded edits. Normal preview schema remains through migration 0012. Later optional capabilities require a deliberately upgraded isolated acceptance database. This translation iteration does not migrate the main preview or change existing account roles. For detailed original acceptance and operator examples, use the [Chinese administration record](/agentbuff-stack/admin-audit). Real cloud operations remain deferred. --- --- url: /docs/en/agentbuff-stack.md --- # AgentBuff Stack AgentBuff Stack is a reusable foundation for independent tool sites. The template and its official website have separate builds. JSON formatting and text cleanup are working examples for input, preview, download and private account history; they are sample content, not evidence of keyword demand. The current iteration focuses on the template, its official website and English/Chinese support. Real products, model integrations and cloud deployment are deferred. `stack.agentbuff.dev` is the intended official template website domain. ## Architecture | Layer | Implementation | Configuration | | --- | --- | --- | | Official website | Independent static Astro site, blog and showcase | `apps/website/` | | Product public pages | Astro, React islands and an edge router | `apps/web/` | | Account workspace | React, TanStack Router/Query and Jotai | `apps/app/` | | Design | Astryx, StyleX and retained Radix controls | `packages/ui/` | | API and identity | Hono, tRPC, Zod and Better Auth | `apps/api/` | | Database | PostgreSQL and Drizzle; Neon/Hyperdrive prepared | `db/` | | Email and payments | React Email/Resend and Stripe | Server-only environment | | Documentation | VitePress with local search | `docs/` | Bun is the local toolchain; production code runs in Cloudflare Workers. Installed versions come from `package.json` and `bun.lock`. Keep original MIT notices and third-party licenses. ## Configure in three places 1. Public product options: `packages/core/website.ts`. 2. Private service credentials: ignored local environment files or Worker Secrets. 3. Resource bindings and routes: application Wrangler files and `infra/`. Changes to public options require rebuilding the applications. API environment changes require restarting the API. The official site does not import product runtime configuration. Follow [Quick start](./quick-start) → [Product configuration](./website) → [Environment](./env) → [Module boundaries](./modules). Core usage guides have paired Chinese versions. Detailed [iteration history](/agentbuff-stack/iteration-progress) and [acceptance records](/agentbuff-stack/capabilities) are intentionally kept in Chinese. --- --- url: /docs/en/agentbuff-stack/task-lifecycle.md --- # Background task workflow The shipped queue example normalizes a UTF-8 text file deterministically. It does not call an external model. Browser JSON/text tools remain synchronous and free; background tasks use a separate saved-file workflow. ## Complete a task 1. Save a UTF-8 text file in the file library. 2. Open `/tasks` or `/zh/tasks`, select the file and decide whether to remove trailing spaces. 3. Check the current quote and available credits. If configured as paid, explicitly approve the reservation. 4. Submit once and follow the server-reported status. There is no invented progress percentage. 5. After success, inspect and download the private result while available. The original file is unchanged. The request key is stable for a retry of the same submission. If a response is lost, refresh the list or retry without changing input/options. Changing input/options creates a new request. Do not submit a new request merely to bypass an uncertain result. ## Retry and cancel Retries retain the original attempt limit and credit reservation. Failed tasks may be retried only when input, processor, quota and remaining budget are still valid. Cancelling a running execution records a request; its input and reserved credits are released only through settlement. Tasks retain history when individual files expire or are deleted. An unknown processor outcome enters review rather than blindly retrying. Old execution/delivery versions cannot overwrite newer results. Dead-letter recovery does not reset attempt counts. ## Extend safely Use the existing task store, delivery, lifecycle, consumer and results modules for their separate responsibilities. `tasks.maxActive`, `maxAttempts`, rate limits and processor configuration bound execution. New submissions need storage and queue bindings. Disabling new work preserves existing read/cancel access. The API checks personal ownership, same-origin mutations and fresh state. Language changes affect presentation, not request identity, execution count or ownership. New processors require actual input validation, output checks, cost accounting and provider reconciliation; a new label is not an implementation. Local queue/object/database checks have been exercised. Cloud resources and real product/model delivery are deferred. See [Credits and payments](./billing-tasks). --- --- url: /docs/en/agentbuff-stack/content-seo.md --- # Content and search metadata Product public pages are static Astro HTML with React islands for interactions. Titles, descriptions, main text and article contents are available before client JavaScript. The private workspace is a separate SPA and is not intended as indexable marketing content. ## Publish a product article Put English Markdown in `apps/web/content/blog/*.md`. Put Chinese translations under `zh/` with the same filename. Required frontmatter: `title`, `description`, `date`, `author`, `category`. Optional fields: `updated`, known `tool`, and `draft`. Use valid `YYYY-MM-DD` dates; an update cannot precede publication. Translate the title, description, category and complete body. Preserve code, field names and original sample data. Update internal links to actual localized tools/articles. Draft English sources suppress their translations. Draft or missing Chinese versions do not create translated details, feed items or language alternates. Chinese lists show actual Chinese content instead of silently inserting English articles. Invalid dates, unknown tool IDs, empty bodies and orphan translations fail validation. ## Indexing outputs Canonical URLs use `PUBLIC_SITE_URL`. Supported complete language pairs expose reciprocal alternates and an English default. The sitemap lists real published pages, not private routes, drafts or nonexistent translations. Privacy/terms drafts remain noindex. English RSS is `/rss.xml`; Chinese RSS is `/zh/rss.xml`. Each contains only published articles in that language, with matching URLs and GUIDs. Disabling the blog removes localized article/feed routes and excludes them from the sitemap; rebuild and replace all assets to avoid stale files. Search filters use titles, descriptions and categories. Article reading estimates account for Chinese text rather than treating a paragraph without spaces as one word. Content and table-of-contents links remain readable without JavaScript. ## Separate official content The official website has its own content collections in `apps/website/content/`; it never reads product posts. Use locale and a stable `translationKey` to pair official translations. An independent showcase case is a real standalone site built from the template, not a tool embedded in the template demo. Write useful, reproducible guides tied to actual output. Template samples do not establish market demand, search volume, revenue or search-engine trust. Real published crawling/indexing remains an external acceptance step. --- --- url: /docs/en/agentbuff-stack/new-site.md --- # Create an independent site Start with a clearly deliverable task: input → processing → checkable preview → download/export. Validate actual market evidence separately. The template's sample tools are engineering examples. ## Generate the project ```sh bun run site:create /absolute/path/my-tool \ --name "My Tool" --domain https://my-tool.com --port 4700 ``` The factory creates independent source and configuration. It excludes secrets, Git history, databases, local state, dependencies, builds and the template official website. `template.json` records provenance for later upgrades. Install dependencies and prepare a separate database and authentication secret in the new directory. Bindings, storage/queue names, dead-letter configuration and routes are rewritten by environment. Cloud account/Hyperdrive IDs become placeholders; remote local-resource flags are disabled. Unsupported resource bindings cause a deliberate failure rather than an unsafe copy. ## Replace product content Edit `packages/core/website.ts`: brand, English and enabled-language descriptions, contact, limits, plans and actual tool behavior. The factory resets brand assets/social/contact and clears credit-pack sales; never reuse the source site's provider products. Add real processor/input/output handling in the existing tool/task boundaries. The shipped workbench handles text input/output. Images, PDF, video and external models require their own validation, preview, delivery, costs and recovery; changing labels is insufficient. Put English articles in the root blog folder and complete Chinese versions in `zh/` with matching filenames. Preserve IDs and source data. Replace sample author/date/facts and policy drafts before publishing. Missing Chinese articles do not generate fake translations. ## Run locally ```sh bun install --frozen-lockfile cp .env.example .env.local # Configure a fresh secret and isolated loopback database first. bun run config:check bun db:migrate bun preview:build bun preview:start ``` Use the generated preview URL and its `/zh/` counterpart. Test the full task, sign-in return, private history, another account's access and refresh behavior. Resource/credential preparation for production is a later deliberate step; generation does not deploy or create cloud resources. English/Chinese are enabled by the template. If your product only supplies English, use `locales: ["en"]`, check and rebuild. Never publish untranslated business claims under a Chinese language label. --- --- url: /docs/en/agentbuff-stack/deployment.md --- # Deployment preparation Current authorization covers the static official website and bilingual guides. Real product integration and product cloud deployment remain deferred. Prepared configuration and passing local tests do not establish a deployed service. ## Official website The domain is `stack.agentbuff.dev`, configured for the independent static website with bilingual guides under `/docs/`. The demo address stays empty and its links are hidden. Build an isolated combined release with `bun website:package`; after Cloudflare login and domain checks, upload the reviewed package using its printed configuration path. See [official website configuration](./website-showcase) for the exact commands. Build and dry-run results do not establish live DNS, TLS or page acceptance. The official website has its own Worker configuration and does not require product database, payment or model credentials. Its Chinese routes are `/zh/`; the domain is not used as product staging. ## Product architecture A product uses web, app and API Workers with internal service bindings and one public origin. Public Astro assets and SPA assets are separate; `/api/*` remains root-scoped. Neon and Hyperdrive are prepared for PostgreSQL, with fresh reads for permissions/payments. R2, business/dead-letter queues and scheduled maintenance need actual environment resources. Each generated product resets resource names/IDs and supplies its own secrets. Development/staging/production must not share unintended data or provider credentials. Database schema upgrades are deliberate, separate from Worker deployment. ## Before an authorized deployment 1. Verify target environment, origins, routes and public docs/demo URLs. 2. Replace placeholder account/resource IDs and configure secrets through the intended environment. 3. Confirm actual schema and binding compatibility; rehearse required additive migrations separately. 4. Test provider email/payment/OAuth, queue retries, private storage and recovery in a controlled environment. 5. Build the correct target and review configuration checks before publishing. 6. Verify real routes, language variants, ownership, downloads, callbacks and maintenance after deployment. `deploy:check` validates configuration relationships but does not prove cloud resources exist. Do not run deploy, infrastructure apply or remote migrations as part of ordinary local translation checks. Historical environment acceptance is kept in [Chinese deployment documentation](/agentbuff-stack/deployment). The current local main preview remains at its already authorized schema level. --- --- url: /docs/en/agentbuff-stack/design-system.md --- # Design system The interface uses shared matte black/white tokens, Astryx and StyleX, while retaining existing Radix controls where they serve real interactions. Public pages and the account workspace share primitives instead of maintaining two unrelated visual systems. ## Extend a component Use `packages/ui/design/` for tokens and reusable controls. Keep component variants explicit and typed. Product-specific copy belongs in dictionaries, not styling files. A second real use case is a reason to extract a component; speculative wrappers make upgrades harder. Support light, dark and system appearance. The public header has responsive desktop/mobile navigation, keyboard focus, language choices and scroll behavior. Workspace theme state is shared through the existing atom and synchronization component; do not toggle the root theme class from unrelated components. The early application theme script and later synchronization must agree on storage encoding, resolved colors and language. New controls need labels, visible focus and understandable loading/error states. Respect reduced-motion preferences and keep layout usable at narrow widths. ## Language-aware layout Give labels room to grow. Chinese and English use the same component structure but different complete copy. Avoid fixed-width text controls, decorative line breaks that depend on English length, and icons as the only description of an action. Keep implementation terminology out of ordinary product journeys unless it helps the user decide. The official website can describe template modules; a generated product should describe its own user task and output. Check both themes, keyboard navigation, mobile menus and both language versions after changing shared styles. Translation work preserves the existing design direction and does not introduce a new styling framework. --- --- url: /docs/en/agentbuff-stack/marketing-subscriptions.md --- # Email lists and waitlists Email updates and waitlists have independent consent and state. They do not create/verify an account and do not change account recovery mail or task notification preferences. Both public modules are disabled by default. ## Request and confirm Enable the required product switch only after preparing its schema and mail configuration. Open `/newsletter` or `/waitlist`, or their `/zh/` versions. Enter an email and explicitly accept the displayed consent. The request remains pending until the recipient opens the email and clicks the confirmation button on the preference page. Merely opening a link does not subscribe the recipient. Withdrawal can cancel a pending request or stop that list's updates. Rejoining requires a new explicit request. A generic success response does not reveal whether an email was already subscribed. ## Persist language and consent The saved request includes the displayed consent, version, purpose, source and language. Confirmation subject/body and preference links use that saved language. Requests and retries do not infer or overwrite it from a later browser header. A disabled language stops new localized delivery but does not prevent historical withdrawal. Local preview captures confirmation mail. Optional provider contact sync is a separate switch, with its own topic/webhook requirements. Provider acceptance does not prove inbox delivery; actual broadcasts are not performed by this iteration. These preference pages do not load an analytics beacon. Use [Environment](./env) for private configuration. Later marketing migrations are prepared only in isolated acceptance databases, not automatically in the normal preview. The [Chinese guide](/agentbuff-stack/marketing-subscriptions) retains detailed historical checks. --- --- url: /docs/en/agentbuff-stack/env.md --- # Environment variables Copy `.env.example` to ignored `.env.local`. Real credentials must not be written to tracked defaults. The site factory does not copy local credentials, databases or Git history. API environment validation lives in `apps/api/lib/env.ts`; errors report field names and categories, not secret values. ## Core settings | Variable | Purpose | | --- | --- | | `ENVIRONMENT` | `development`, `staging` or `production` | | `APP_NAME` | API/Worker brand; align with product metadata | | `APP_ORIGIN` | Bare trusted origin; HTTPS outside development | | `PUBLIC_SITE_URL` | Public canonical, social, sitemap and RSS origin | | `DATABASE_URL` | Private local/CLI connection; complete preview requires loopback | | `BETTER_AUTH_SECRET` | Unique secret, at least 32 characters; placeholders rejected outside development | | `DOCS_SITE_URL` | Optional public docs URL; omitted local builds are noindex | | `WEBSITE_ORIGIN`, `WEBSITE_DOCS_URL`, `WEBSITE_DEMO_URL` | Public build values for the separate official website | `PUBLIC_*` values can enter public assets. `VITE_APP_NAME`, appearance and default language are derived from product configuration. Do not expose private keys through client build variables. ## Provider groups Google and GitHub client ID/secret pairs must be complete or empty. Stripe requires its secret, subscription webhook secret and both monthly Price IDs as a complete group. Optional Pro annual pricing requires that group. Credit packs require their own configured catalogue and a distinct credits webhook secret; real prices, cycles and mode must be checked against the provider. Production email needs `RESEND_API_KEY` and a verified `RESEND_EMAIL_FROM`. Marketing contact sync also requires its own webhook secret and two different topic IDs. Rewardful secrets are server-only and the public referral key is a separate value. Optional Turnstile needs both the public site key and private verification secret. Configuration validation does not prove provider permissions or delivery. `BETTER_AUTH_SECRETS` supports versioned authentication rotation. Review the detailed Chinese maintenance guide before changing live keys; do not rotate keys during ordinary translation work. ## Worker bindings `HYPERDRIVE_CACHED`, `HYPERDRIVE_UNCACHED`, `STORAGE`, `TASK_QUEUE`, API/App service bindings and assets are resource objects, not environment strings. Development derives its two Hyperdrive connections from the local database URL. Local storage/queue state lives in `.local/workerd/` and is never copied to a new site. Local mail, marketing and operator capture bindings are development fixtures. They do not enable real external delivery. Optional modules require their documented migrations in an isolated acceptance database; the normal preview is not automatically upgraded. ```sh bun run config:check bun run config:check --json ``` Target-specific checks read the supplied environment; they do not convert local values into production settings. No cloud publish or real product connection is part of the current iteration. --- --- url: /docs/en/agentbuff-stack/languages.md --- # Languages and translation The template ships English and Simplified Chinese. Optional Spanish support is retained. The official site always has English/Chinese pages independently of product language configuration. Core documentation is bilingual; historical acceptance and iteration records stay in Chinese. ## Configure a product Keep English first in `websiteConfig.i18n.locales`, and keep `metadata.language` set to `en`. Supply complete translated tagline, two hero lines, description and Free/Starter/Pro descriptions in `translations.zh` before enabling Chinese. Optional disabled translations may be `null`. English routes stay unchanged. Chinese uses `/zh/`: public pages, tools, articles, authentication, dashboard, tasks, files, history, purchases, members, invitations, settings and administration. API endpoints, signed auth endpoints, private download endpoints and application assets keep root paths. No automatic browser-language redirect or language cookie is introduced. URL selection is explicit. Rebuild every application after changing enabled languages; disabling Chinese removes its public pages and edge entry rather than presenting a fake translation. ## Add or edit copy | Content | Location | | --- | --- | | Product metadata/plan descriptions | `packages/core/website.ts` | | Language registry and paths | `packages/core/locales.ts`, `routing.ts` | | Public UI and tools | `apps/web/lib/locales/`, `packages/core/tool-locales.ts` | | Workspace and stable error codes | `apps/app/lib/locales/`, `locale-errors.ts` | | Email subjects, preview and bodies | `apps/email/locales.ts` | | Actual product articles | `apps/web/content/blog/zh/` | | Independent official site copy | `apps/website/lib/i18n.ts`, `modules.ts` | | English core guides | `docs/en/agentbuff-stack/` | Chinese dictionaries match English dictionary types. Do not translate identifiers, amounts, brand names, secrets, user inputs, raw error evidence or original audit notes. Dates and money use the selected locale while authoritative values remain unchanged. ## Switching and authentication Public language menus link only to existing versions of the current page. Public switching does not copy arbitrary query/token values. Tools reload on a language change; copy unsaved inputs first. The anonymous save flow preserves tab-local input through its safe sign-in return, then restores preview without automatically saving it. Workspace switching retains supported task/purchase IDs, query and fragment, and updates safe `returnTo` destinations. Password/OTP, registration, provider callbacks and reset links preserve the supported selected route. Unknown origins and unsafe destinations are rejected by the existing redirect guard. Immediate emails use the enabled request language, falling back to English for unknown/disabled values. Marketing requests and task preferences persist their chosen language; later page changes do not rewrite queued retries. Checkout language is retained with its original request. This is presentation, never permission or tenant selection. ## Documentation Chinese guides retain their original routes; English core guides use `/en/`. Each paired guide has a direct counterpart link. The global language menu returns to a language home, avoiding nonexistent English versions of Chinese-only history. English search indexes English guides; history links clearly say “Chinese.” Translations were written from the repository's shipped English content and should be reviewed when adapting business descriptions. Local behavior checks do not prove real inbox delivery, provider checkout localization or published search indexing. --- --- url: /docs/en/agentbuff-stack/modules.md --- # Module boundaries The template exposes working local capabilities and explicit switches. A switch or prepared binding is not proof of a deployed integration. The official website keeps these distinctions visible. | Capability | Local scope | External acceptance still needed | | --- | --- | --- | | Public pages and blog | Static HTML, task previews, filters, canonical links, feeds and language versions | Real published indexing and editorial review | | Accounts and teams | Password/OTP flows, sessions, role-protected membership and invitations | OAuth providers, passkey browser matrix and real email delivery | | Private files | Ownership, preview/download, expiry, quota and local object storage | Real storage resources and cloud limits | | Background tasks | Idempotent requests, queues, bounded retries, cancellation and deterministic text results | Real processors, providers and production capacity | | Credits and payments | Atomic reservation/refund, purchases, subscription reconciliation and audit | Actual payment sandbox, webhooks and financial review | | Administration and keys | Separate site administrator role, audited changes and scoped user keys | Production recovery and operations | | Optional communications | Explicit email choices, confirmation/withdrawal, task and operator notices | Real provider delivery and configured channels | | Optional reporting/referrals | Consent-bound first-party records and verified referral evidence | Real reporting/referral provider acceptance | | Site factory and upgrades | Independent source/configuration, resource reset and upgrade reports | Independent deployed products | | Cloudflare + Neon | Prepared bindings, routes and validation | Resources, secrets, deployment and end-to-end cloud acceptance | Optional modules stay disabled in the normal preview until their configuration and schema are deliberately prepared. Existing default preview data is not silently migrated to every optional module. See [Environment](./env) and [Deployment preparation](./deployment). English and Chinese support covers public pages, workspace, relevant emails, official website and core guides. Original user data, IDs and historical audit notes remain unchanged. Spanish dictionaries remain available, enabled only with complete product translations. Historical module acceptance details are maintained in [Chinese records](/agentbuff-stack/capabilities). Real product/model integration and cloud publishing are deferred in this iteration. --- --- url: /docs/en/agentbuff-stack/website-showcase.md --- # Official website, showcase and demo The template official website, generated products and complete product demo are different surfaces. `stack.agentbuff.dev` is the intended official template website domain. It is not a real product staging domain. The authorized release contains the static official website and bilingual guides under `/docs/`. The product demo is hidden until it has a public address; real products remain undeployed. ## Independent build `apps/website/` is a static Astro site for the template introduction, features, showcase, blog, pricing availability and docs/demo links. It does not import product runtime configuration or require the product database, authentication, payment credentials or model APIs. | Public variable | Purpose | | ------------------ | -------------------------------------- | | `WEBSITE_ORIGIN` | Official canonical/feed/sitemap origin | | `WEBSITE_DOCS_URL` | Public documentation entry | | `WEBSITE_DEMO_URL` | Separate complete product demo | Copy `.env.website.example` to `.env.website.local`. Set the origin to `https://stack.agentbuff.dev`, the docs entry to `https://stack.agentbuff.dev/docs/agentbuff-stack/`, and leave `WEBSITE_DEMO_URL` empty. Missing destinations hide their links; supplied public destinations must use HTTPS and cannot point to localhost. Local previews retain their ports. Official URLs do not change product `APP_ORIGIN`. Run `bun website:package` to validate the domain and docs entry, then build both sites into a fresh `.local/website-release-*` directory. The package includes its own Worker configuration and a manifest outside public assets, recording the source revision and whether the source had uncommitted changes. It preserves local preview output. Docs assets, search, paired guide links and canonical addresses use `/docs/`; the root sitemap index lists both sites. Check the printed configuration with `bunx wrangler deploy --dry-run --config /wrangler.jsonc`. Dry-run success does not establish deployment. After Cloudflare login and domain access are ready, upload that reviewed package with `bunx wrangler deploy --config /wrangler.jsonc`. Alternatively, `bun website:deploy` builds a fresh combined package and uploads it. Neither website command deploys product Workers or migrates a database. The site factory excludes the official publisher and environment file from generated products. ## Bilingual content English official routes remain unchanged; Chinese uses `/zh/`. Shared page components use independent website dictionaries. English and Chinese blogs contain full actual articles with localized feeds, schema and canonical links. Only existing content versions appear as language alternates. Official Markdown uses `locale` and an optional stable `translationKey` to pair versions. Drafts do not publish. A translated published entry needs its published source. Missing translations do not produce a false translated detail page. ## Showcase cases A case is an independent site built from the template, with its own origin, screenshots and factual description. The demo's JSON/text tools are not separate showcase cases. Until real cases exist, the site displays an honest empty state. A demo link appears only when its address is configured. Before adding a case, confirm its independent URL, attribution, source of screenshots and real status. A case page is not proof of traffic, revenue or demand. The site factory excludes official content from generated products. --- --- url: /docs/en/agentbuff-stack/billing-tasks.md --- # Payments, credits and tasks The existing browser tools are free. Personal credits support configured task prices and optional one-time credit packs. Subscription plans govern their configured entitlements; team billing does not make personal files or credit balances shared. ## Task charging The server quotes the current configured task price. Paid submission requires explicit approval, then atomically reserves credits while saving the task. Retries use the original reservation and budget. Successful settlement consumes the reservation; cancellation/final recovery returns it according to current task state. A ledger records each change. Do not treat a page return or successful HTTP response as proof of payment. Access follows confirmed server/provider state. The workspace shows reserved, settled and returned credits separately and preserves original ledger notes. ## Purchase credits Credit packs stay unavailable unless the site has its own enabled catalogue, valid prices and distinct webhook secret. Select a pack, review the quote and continue to checkout. Retrying the same request keeps the original purchase and chosen checkout language. Changing the pack or language on an existing request is a conflict, not a new silent checkout. The purchase detail shows payment state, confirmed grant and later adjustments. Refunds/disputes can reverse credits. Used credits become a recorded shortfall rather than a negative available balance; paid use may remain on hold pending review. ## Subscriptions Configured Starter/Pro monthly prices and optional Pro annual price must match real provider products, billing intervals and test/live mode. Only authorized billing roles can manage a team subscription. Recovery and reconciliation retain original checkout evidence and audited operations. English and Chinese checkout choices and return routes are supported in the template. Actual provider payments, webhooks, portal behavior and delivery still require separate sandbox acceptance. No real payment is collected by the current local preview. Configure private values in [Environment](./env). Prepare optional modules in isolated databases before enabling them. Administrative recovery is documented in [Administration](./admin-audit). --- --- url: /docs/en/agentbuff-stack/files-storage.md --- # Private files Files belong to the personal account, even when the user switches teams. Anonymous or other-account requests do not gain access through a preview URL. Public brand assets are separate from private storage. ## Use the file library Open `/files` or `/zh/files`. Select a valid PNG, JPEG, UTF-8 text or JSON file within the configured limit. Uploading is complete only after the server confirms storage. Refresh the list if the response is lost before uploading again. Open a saved file to preview it or download its original bytes. Text previews are bounded; a preview may show only the first 64 KiB, so download the original for complete content. Preserve originals needed for later work. Expired or unavailable files cannot be downloaded. Deletion releases storage through the confirmed server workflow; pending uploads and deletions can still occupy quota. A task using a file may prevent deletion until the task finishes or is cancelled. ## Configuration and execution `websiteConfig.storage` controls enablement, maximum bytes/files/total quota, retention and read/write limits. Optional Turnstile is controlled by security configuration and requires a matching public/private key pair. Client checks help the user; server validation remains authoritative. The API reserves quota, writes private objects, confirms records and uses bounded cleanup for incomplete/expired files. Real R2 acceptance is still deferred. Local objects persist in `.local/workerd/`, which the site factory does not copy. The file schema requires the relevant existing migrations before new code is run against a separate database. Translation changes do not create a new storage model or migrate the normal preview. See [Background tasks](./task-lifecycle) and [Environment](./env). --- --- url: /docs/en/agentbuff-stack/website.md --- # Product configuration The single editing entry is `packages/core/website.ts`, typed as `WebsiteConfig`. It contains public data that can be bundled into Astro, React and the API. Never put credentials here. `packages/core/site.ts` is a compatibility view derived from this configuration. ## Main sections | Section | Controls | | --- | --- | | `metadata`, `brand` | Stable site ID, name, hero, descriptions, support address and brand assets | | `i18n` | Enabled languages and product-specific translated metadata/plan descriptions | | `ui`, `auth` | Default appearance, theme switch and sign-in methods | | `blog` | Publishing switch and article limits | | `plans`, `payment`, `creditPacks` | Product entitlements, example prices and payment availability | | `storage`, `tasks`, `security` | File limits, task budgets, request limits and optional verification | | `notifications`, `marketing` | Separately enabled task email, operator notices and email lists | | `analytics`, `support`, `affiliates` | Optional reporting, support chat and referral integrations | The stable site ID scopes authentication cookies. Changing it after launch changes which existing cookies are read. Free/Starter/Pro IDs also have backend billing meanings: changing labels differs from adding or removing plans. Prices must match actual provider prices before payments are enabled. ## Languages The template enables `en` and `zh`. English remains first, and `metadata.language` remains `en`. Chinese hero, description, tagline and all plan descriptions belong in `i18n.translations.zh`. Spanish remains available as an optional complete dictionary. ```ts i18n: { locales: ["en", "zh"], translations: { zh: { /* complete product-specific Chinese copy */ }, es: null, }, }, ``` This shows structure, not a copyable complete configuration. Run `bun run config:check` and rebuild after changing languages or business copy. Empty required translations and duplicate/unsupported language entries fail validation. See [Languages](./languages). ## URLs and private services `PUBLIC_SITE_URL` determines public canonical/feed links; `APP_ORIGIN` determines API and authentication trust. A deployed product uses the same real public origin for both. Secrets and provider credentials belong to [Environment variables](./env). The independent official site uses `apps/website/config/site.ts` and `WEBSITE_*` public variables. It does not inherit product prices, credentials or billing state. See [Official site](./website-showcase). --- --- url: /docs/en/agentbuff-stack/quick-start.md --- # Quick start ## Prepare an isolated local project Install Bun 1.4.2+, Python 3 and local PostgreSQL. Use a separate database and a unique authentication secret for every product. ```sh bun install --frozen-lockfile cp .env.example .env.local bun -e 'console.log(crypto.randomUUID()+crypto.randomUUID())' ``` Put the generated value in `BETTER_AUTH_SECRET`. Set `DATABASE_URL` to your own loopback database. Keep `APP_ORIGIN` and `PUBLIC_SITE_URL` equal to the configured product preview origin, normally `http://localhost:4410`. ```sh bun run config:check # Run only against the newly prepared, isolated database: bun db:migrate bun preview:build bun run preview:start bun run preview:status ``` The launcher does not install PostgreSQL or create a production database. Local mail is captured in `.local/outbox.jsonl` rather than sent through Resend. That file can contain verification codes and links; keep it local. ## Preview the three surfaces | Surface | Default local URL | | ------------------------- | ----------------------- | | Template official website | `http://localhost:4400` | | Complete product demo | `http://localhost:4410` | | Documentation | `http://localhost:4406` | English public pages use the original routes. Chinese pages use `/zh/`. Documentation defaults to Chinese, with English core guides under `/en/`. Ports are managed by `preview.config.json`. Build documentation with `bun run docs:build`; start/stop owned previews with `bun run preview:start` and `bun run preview:stop`. Rebuild after frontend edits, and restart the API after backend changes. `bun dev` provides development hot reload. ## Check a change ```sh bun run config:check bun typecheck bun web:check bun website:check bun run test -- --run bun lint bun format:check bun preview:build bun run docs:build ``` Type checking generates Astro collection types but does not replace Astro page checks. None of these checks performs a deployment or a database migration. Existing preview accounts and data are local fixtures, not defaults to ship to a new product. --- --- url: /docs/en/agentbuff-stack/task-notifications.md --- # Task email preferences Task email is an optional, separate preference. It is not marketing consent and is disabled in the normal preview. Prepare the notification schema and configured mail service before enabling it. ## Save an explicit choice Open settings with a verified account email. Choose whether to receive notices and select an enabled language. Save the preference. The saved language remains stable across page language changes, refreshes and Worker restarts. Changing the account email requires a new saved choice. Only tasks completing or failing after the recorded choice generate eligible notices. Mail contains the task ID, status and protected sign-in links, without inputs, results, file names or attachments. The recipient is selected by the server, not supplied by the client. Withdrawal stops pending notices; a request already being sent may still arrive. Bounded retries preserve the same payload and identity. Unknown outcomes stop automatic resend for review, and provider acceptance is not proof of inbox delivery. ## Retention and compatibility Choices/delivery records follow configured retention and bounded scheduled cleanup. Account/task deletion removes their appropriate notification records, without changing billing or audit retention. Older queued mail keeps its original payload; language changes do not silently mutate an accepted retry. The main preview remains at its authorized schema level with task mail disabled. Use a separate upgraded acceptance database for this module. English and Chinese templates are rendered locally; real delivery remains deferred. See [Languages](./languages) and the detailed [Chinese guide](/agentbuff-stack/task-notifications). --- --- url: /docs/en/agentbuff-stack/template-upgrades.md --- # Template versions and upgrades Generated products have their own repository, brand, resources and business code. The factory records the template source version in `template.json`; it does not make products share a database or overwrite them automatically. ## Prepare a reviewable upgrade Commit your product work first. Compare the recorded source version with the intended new template revision. Use the repository's distribution/upgrade reporting workflow to review changed files, configuration contracts, dependencies and migrations. Retain original licenses and provenance. Separate template changes from product-specific processors, content and provider configuration. Resolve conflicts explicitly. Do not replace a customized product directory with a fresh template copy or reuse new template example prices as real product settings. ## Check compatibility Run configuration validation, type checks, public page checks and relevant behavioral tests in the product. Rebuild its public site, workspace, emails and API together when public configuration or dictionaries change. Language upgrades need complete metadata and actual content for every enabled language. English original paths remain stable. Existing user input, ledger notes, audit evidence and queued mail identity are not rewritten by a translation upgrade. Database migrations are a separate intentional operation against a verified target. Preserve original migration history and use additive changes; never apply optional module migrations merely because the source includes them. Back up and rehearse operational changes before a real environment upgrade. The current task upgrades only the local template/official site and adds bilingual core guides. No real product or cloud deployment is being performed. Detailed original distribution/upgrade acceptance remains in the [Chinese guide](/agentbuff-stack/template-upgrades). --- --- url: /docs/en.md --- --- --- url: /docs/en/agentbuff-stack/api-keys.md --- # User API keys User keys provide scoped access to supported external endpoints. They do not replace browser sessions, bypass personal ownership or grant site administration. Optional key support requires its documented schema and enabled server capability. ## Create and manage a key In settings, choose the intended personal/team scope, expiration and supported permissions. Review before creating. The secret is shown once; store it securely and do not include it in public URLs, source code, logs or screenshots. The server stores a verifier rather than a recoverable secret. Use the endpoint's documented authorization header and an explicitly supported operation. A team key must respect its role and billing scope; switching the browser's team does not rewrite an existing key. Revoke unused or compromised keys. Expired/revoked keys must stop working at the server. Lists, creation confirmations, key details, errors and revocation labels support English and Chinese. The secret, key identifier and raw recorded evidence are never translated. An API language choice affects presentation only. The normal preview does not silently enable every optional migration. Prepare a separate acceptance database and test ownership, scope, expiration, revocation and request limits before exposing a new endpoint. Detailed implementation and original verification are available in the [Chinese guide](/agentbuff-stack/api-keys). --- --- url: /docs/adr/001-auth-hint-cookie.md --- # 决策 001:认证提示 Cookie **状态:** 原路由决策已被当前公开首页策略替代;设置和清除钩子仍保留。 **原决策日期:** 2025-12-28。**当前说明核对日期:** 2026-10-09。 ## 原问题与方案 上游项目希望边缘入口快速根据登录提示选择首页,不读取完整会话,也不向认证服务发额外查询。原方案在新会话时写入值为 `1` 的提示 cookie,退出或会话无效时清除。 HTTPS 使用 `__Host-auth`,本地 HTTP 使用 `auth`,因为浏览器不接受缺少安全连接的 `__Host-` cookie。 ## 当前行为 AgentBuff Stack 的 `/` 始终返回公开 Astro 页面,工作台位于 `/dashboard`。`apps/web/worker.ts` 不再根据提示 cookie 切换首页。设置和清除逻辑仍在 `apps/api/lib/auth.ts`,但不能依据它决定用户权限。 ## 安全边界 提示 cookie 只表示可能存在会话,不能证明有效登录。工作台从统一会话查询读取真实身份,私有接口由服务端验证会话、成员和数据归属。 ## 取舍 公开入口不承担认证逻辑,静态页面稳定返回,有利于搜索访问与分享。保留旧钩子需要维护,但不应将历史行为误写成现行路由说明。 相关教程:[边缘路由与绑定](../architecture/edge.md)、[会话与访问控制](../auth/sessions.md)。 ## 独立产品预览中的命名 2026-10-09:登录提示 Cookie 改为 `.auth`,HTTPS 环境使用 `__Host-.auth`。Better Auth 会话 Cookie 已按站点 ID 设置前缀。提示本身仍不是权限判断依据;公开边缘路由不读取提示决定访问权限。 多个 localhost 端口共用 Cookie 范围,因此每个独立产品必须具有唯一站点 ID。端口、数据库、认证 secret 和邮件记录也分别配置。 --- --- url: /docs/adr/002-terraform-wrangler-boundary.md --- # 决策 002:基础设施与应用发布分工 **状态:** 已采纳。**日期:** 2026-08-09。 ## 问题 上游配置曾让 Terraform 和 Wrangler 同时管理 Worker 字段,部署工具相互覆盖,名称规则也需要两套配置同步。空 Worker 占位没有必要,因为应用部署可创建服务。 ## 决策 Terraform 只管理依赖资源:每个环境的两个 Hyperdrive,以及按需启用的上传存储桶。Wrangler 管理 Worker 名称、代码、静态资源、路由、域名、绑定、变量和应用秘密,同一字段不交给两个工具。 公开主机通过 Worker 自定义域名配置,基础设施无需同时管理相同 DNS。工具之间只传递非秘密的资源标识,例如 Hyperdrive ID 和存储桶名称。 ## 备选方案 Terraform 同时发布代码会使应用更新依赖基础设施变更,并复制 Wrangler 配置。只用命令创建依赖更简单,但缺少声明式重建和差异检查。按路径拆分同一主机的多个入口时,可重新选择路由和 DNS 方案。 ## 影响 应用发布不执行 Terraform,基础设施令牌只授予必要资源权限。代价是每个环境需要把生成的资源 ID 写入对应 Wrangler 配置,自定义域名方案也有主机归属限制。 上传基础设施可选配置不代表主站已经实现上传。参见 [文件上传](../recipes/file-uploads.md) 和 [基础设施规格](../specs/infra-terraform.md)。 --- --- url: /docs/adr/003-hcp-terraform-state.md --- # 决策 003:远程基础设施状态 **状态:** 已采纳。**日期:** 2026-08-09。 ## 问题 Hyperdrive 保存源数据库凭据,因此 Terraform 状态含敏感信息,需要受控访问、锁定和可恢复历史。把状态直接存本地或放没有恢复方案的对象桶,会增加丢失和泄露风险。 ## 决策 每个环境根目录使用 HCP Terraform,固定 `cloud` 的主机、组织及工作区名称。复制模板后显式替换占位配置;预发布与生产各有独立状态,不能靠临时环境变量任意改指向。 默认远程执行,Cloudflare 凭据和数据库输入留在对应工作区。GitHub 基础设施任务使用专用 `TF_API_TOKEN`,应用发布秘密与此分开。 ## 需要手动核对的工作区设置 * 使用命令行驱动工作流。 * 使用远程执行模式。 * 工作目录设为 `envs/staging` 或 `envs/production`,确保上传包含共享模块。 * Terraform 版本符合根目录约束。 * 工作区环境变量与敏感输入完整且对应正确环境。 这些设置不全由源码声明,不能只复制文件就认定远程配置完成。 ## 风险与替代方案 `sensitive` 只隐藏输出,不使状态文件不含凭据。远程状态访问者仍需要最小权限。调用者的 `TF_VAR_*` 可覆盖部分输入,所以规划前检查临时环境值,并审阅真实计划。 本地执行、远程保存状态可以使用,但凭据回到执行机器且工作区变量不按远程模式供应。对象存储后端需要额外解决初始化、锁定、版本和恢复;模板不同时维护多种后端。 参见 [基础设施规格](../specs/infra-terraform.md) 和 [自动检查与发布](../deployment/ci-cd.md)。 --- --- url: /docs/adr/004-typescript-7-native-compiler.md --- # 决策 004:类型检查工具链 **状态:** 已采纳。**日期:** 2026-09-11。 ## 问题 项目迁移到 TypeScript 7 原生编译器后,部分依赖旧编程接口的工具不能直接使用。Astro 模板仍需要专用检查,不能仅因为普通 TypeScript 文件通过就省略模板验证。 ## 决策 根目录 `bun typecheck` 先对公开站和官网执行 `astro sync`,生成当前内容集合类型,再使用项目引用图运行 TypeScript 7。新检出目录没有 `.astro`,不能依赖本地预览 / 构建留下的缓存;同步失败会停止后续类型检查,不提交生成文件或给内容条目强加宽类型。生成产品只同步自己的公开站,不保留独立官网的命令过滤器。 两个 Astro 项目的 `tsconfig.json` 都显式包含 `.astro/types.d.ts`,由它引入内容类型。生成文件存在不代表项目图已读取;仅用通配路径会漏掉隐藏目录,使内容查询失去集合约束。 `apps/web` 与 `apps/website` 为 `astro check` 单独声明 TypeScript 6 依赖别名,保留 `.astro` 模板检查;公开站 Worker 的普通 TypeScript 文件仍由根检查覆盖。同步内容类型不代替 Astro 模板检查,两者继续分别运行。 编辑器配置使用原生工具链对应扩展,不继续指向不存在的旧会话服务器。具体安装版本以锁文件和应用依赖为准。 ## 备选方案与影响 全部停留旧编译器会延后迁移;删除 Astro 检查会失去模板验证;包装语言服务器会增加维护。窄范围依赖别名保持两种检查各自明确。 代价是安装两个版本,出现类型差异时需要区分 `bun typecheck` 与 `bun web:check`。待 Astro 依赖支持同一工具链后再评估移除别名,不根据发布时间自动升级。 原迁移记录中的耗时是当时本机观察,不作为当前每台机器的性能承诺。相关决策:[静态检查与格式化](./005-oxlint-oxfmt.md)。 --- --- url: /docs/adr/005-oxlint-oxfmt.md --- # 决策 005:静态检查与格式化 **状态:** 已采纳。**日期:** 2026-09-11。 ## 问题 旧检查工具依赖 TypeScript 编程接口,与原生编译器迁移产生冲突;静态检查和格式化也需要统一、可快速运行的基线。 ## 决策 Oxlint 替代原静态检查工具,配置在 `.oxlintrc.json`;Oxfmt 负责格式化,配置在 `.oxfmtrc.json`。根目录提供 `bun lint`、`bun format` 和 `bun format:check`。 保留 `lint-staged` 检查暂存内容,避免部分暂存时检查与真正提交的版本不同。未匹配到支持文件时不应无故使提交失败。 ## 规则范围 基线侧重正确性,补充 React Hooks、明确类型、注释约束和模块导入规则;不把旧工具全部规则机械移植。无用变量等由检查发现,格式由格式化器统一。 部分旧 React 规则没有对应覆盖,需要审查事件监听、计时器和观察器是否正确释放,以及生成组件是否遵循项目规范。 ## 模板与文档边界 Astro 模板使用 `bun web:check` 和编辑器支持,不假定普通静态检查能验证所有标记。Markdown 段落保持单行;VitePress 提示容器的开始和结束标记与内容之间留空行,防止格式化破坏渲染。 不手动按编辑器喜好重排导入,统一使用格式化器,见 [导入顺序](./007-oxfmt-sorts-imports.md)。 --- --- url: /docs/adr/006-subpath-imports.md --- # 决策 006:包内导入约定 **状态:** 已采纳。**日期:** 2026-09-11。 ## 问题 原项目混用仅由 TypeScript 配置解释的路径别名和包声明的子路径导入,共享配置可能意外把一个应用的导入解析到另一个应用。 ## 决策 `apps/app`、`apps/web` 和 `packages/ui` 的包内导入使用 `#` 前缀,并在各自 `package.json` 的 `imports` 声明。跨共享包使用 `@repo/*`;接口、数据库和直接运行的脚本可使用相对路径。 工作台还在 TypeScript 配置声明对应路径映射,以同时满足编译器和打包器对扩展名的不同处理。修改时保持两处一致,不增加跨应用的全局回退路径。 ## 示例 ```ts import { auth } from "#lib/auth"; import { websiteConfig } from "@repo/core"; ``` 第一条引用当前工作台文件,第二条引用共享包。包内无扩展名的浏览器代码由打包器解析,不能默认直接用原生 Node 运行。 ## 取舍 统一约定减少复制组件时解析到错误目录的风险,但工作台有两处映射需要同步。枚举所有扩展名的条件导入会增加目录变更成本,因此不作为默认方案。 --- --- url: /docs/adr/007-oxfmt-sorts-imports.md --- # 决策 007:统一导入顺序 **状态:** 已采纳。**日期:** 2026-09-12。 ## 问题 只靠编辑器整理导入,结果取决于开发者打开过哪些文件,生成代码和 CI 也没有统一检查。 ## 决策 `.oxfmtrc.json` 的 `sortImports` 交给 Oxfmt。编辑器保存、提交检查与 CI 使用同一规则,不增加独立导入排序工具。 保留默认内部路径模式:`@repo/*` 作为其他包,`#*` 作为本包内部文件。副作用导入不自动排序,避免改变环境初始化和样式加载顺序。 ## 影响 导入顺序可以确定且可检查。格式化不会自动删除全部无用导入,静态检查仍负责发现;出现冲突时按格式化器结果处理。 `db/scripts/seed.ts` 的环境配置副作用导入等依赖执行顺序,不能因为看起来不整齐就手动移动。相关决策:[包内导入](./006-subpath-imports.md)。 --- --- url: /docs/specs/infra-terraform.md --- # 基础设施规格 ## 责任边界 Terraform 管理两个 Hyperdrive 及可选上传存储桶,Wrangler 管理应用代码、域名、路由、绑定、变量和秘密。两者不同时管理同一字段,交接值仅为非秘密资源标识。 结构为 `infra/modules/cloudflare/` 和 `infra/envs/staging/`、`infra/envs/production/`。每个根对应独立状态和固定环境;开发不创建远程资源,也没有共享的逐请求预览环境。 ## 版本与命名 根配置约束 Terraform `>= 1.12, < 2.0`、Cloudflare 提供者 `~> 5.0`,锁文件随代码提交。资源名称使用项目标识和环境,项目标识与 Worker 前缀相符。 升级版本同时检查根目录、CI 和远程工作区设置,不仅修改单个工具安装命令。 ## 数据库与缓存 每个环境创建有缓存和无缓存两条 Hyperdrive。缓存最大时间和重新验证窗口由模块明确配置;连接软上限默认每条 20,源数据库要预算两条连接之和及余量。 连接串解析拒绝不支持的格式、百分号编码凭据及 `-pooler` 主机。密钥包含特殊字符时先按当前解析限制准备连接方式,不能让未解码文本成为真实密码。 权限、认证、账单和积分使用无查询缓存的连接。上传存储桶的可选配置不代表主站已经有上传接口或清理流程。 ## 状态与远程设置 HCP Terraform 保存状态、锁及历史,状态含源数据库凭据。根固定主机、组织和工作区;复制新站时替换占位名称。 工作区选择命令行驱动、远程执行、对应 `envs/<环境>` 工作目录、符合约束的版本和完整变量。Cloudflare 令牌作为工作区环境变量,数据库地址作为敏感输入。调用者 `TF_VAR_*` 可能覆盖输入,运行前检查并审阅计划。 ## CI 与权限 普通 CI 仅无后端验证,使用只读锁文件,不持有远程资源凭据。手动基础设施工作流先保存计划,后应用同一计划;规划与应用环境分开,应用令牌按工作区隔离。 规划执行远程配置,不是针对不可信分支的安全沙箱。主分支规则与实际环境审批需要维护者配置。具体操作见 [自动检查与发布](../deployment/ci-cd.md) 与 `infra/README.md`。 --- --- url: /docs/agentbuff-stack/refactor-validation-2026-10-09.md --- # 官网与独立项目改造验收 日期:2026-10-09。范围为改造计划的首轮四阶段:官网边界、案例与内容、独立演示及预览、新站生成器。真实产品选题、真实上线案例和商业售卖仍是后续工作。 ## 架构与内容 * 官网在 `apps/website`,不导入产品配置或连接产品 API、数据库;现有产品架构保留。 * 导航、主题脚本、文章卡片与公共排版在 `packages/ui/marketing`,由应用传入展示数据。 * 官网首页、功能、案例、官方博客、定价与中文文档入口可访问。定价没有用户订阅套餐或虚构购买入口。 * 临时案例集成验收确认:已发布案例生成首页、列表、详情与 sitemap;案例草稿不生成公开详情或 sitemap;文章草稿不进入详情、列表、RSS 或 sitemap;缺截图构建失败。 * 验收发现并处理 Astro 空目录的旧记录缓存问题;删除全部案例后,构建恢复空状态,旧详情及链接消失。临时记录与素材已移除,没有作为真实产品发布。 * 案例路径由小写文件名确定;忽略额外 frontmatter slug,非法文件名构建失败,避免路径出现两个配置来源。 ## 独立预览与生成 官网为 `localhost:4400`,产品演示为 `localhost:4410`,中文教程为 `localhost:4406`。产品入口与内部服务端口集中于 `preview.config.json`。 生成器采用明确目录,排除官网、案例、官网文章、私有环境、本地数据、依赖、缓存与 Git 历史。验证了目标目录保护、无效域名和端口、带引号品牌名、Worker 与绑定更新、从已生成站再生成第二站、来源 revision 保留。 另外从当前源生成两个独立验收项目 Alpha(4710)与 Beta(5710),其中 Beta 从安装后的 Alpha 再生成。两站分别完成: 1. 首次干净安装,再执行锁文件固定安装。 2. 离线配置诊断与产品构建。 3. 在专门新建的本地空数据库执行初始化迁移;没有操作生产数据库。 4. 独立注册、登录会话、保存结果历史、密码恢复邮件捕获。 5. 混合两站 Cookie 后仍分别识别自己的用户;Alpha 的 Cookie 无法登录 Beta。 6. 两站历史数据和邮件记录分别保存;停止 Alpha 后 Beta 接口仍可访问,Alpha 能重新启动。 每站使用独立本地数据库和新生成的认证 secret,没有复制原站私有环境。没有发送外部邮件或进行真实支付。这两个站是生成器验收样例,不是已验证需求或上线案例。 ## 界面验收 桌面检查了哑光浅深主题、滚动导航、官网跳转独立演示、博客搜索与分类、文章详情和案例空状态。独立演示保留已有账号与历史,JSON 处理、预览和实际下载内容一致。 手机布局使用真实 390 与 320 像素 iframe 视口验收:没有横向溢出,导航切换到手机菜单,展开内容完整,键盘 Escape 可以关闭。浏览器视口覆盖接口未改变实际尺寸,因此没有把该接口调用当作手机验收依据。 ## 自动检查 全仓库类型检查、两个 Astro 应用检查、lint、格式检查、产品与官网构建、中文文档构建均通过;19 个测试文件中的 133 项现有回归与新增行为测试通过。详细命令: ```sh bun typecheck bun web:check bun website:check bun run test -- --run bun lint bun format:check bun preview:build bun docs:build ``` ## 后续边界 首个真实产品仍需根据实际关键词、竞品与可交付性证据确定,再做独立业务与线上验收。Cloudflare、Neon、OAuth、Stripe 和真实发信不因本地验收而视为上线完成。R2、异步任务、积分包等按实际产品需求推进。 --- --- url: /docs/specs/prefixed-ids.md --- # 带前缀的数据库标识 ## 格式 实体标识使用三位小写前缀、下划线和 16 位 CUID2,共 20 字符,例如 `usr_ght4k2jxm7pqbv01`。前缀帮助日志排查,客户端应将完整值作为不透明字符串,不用前缀判断权限。 | 认证模型 | 前缀 | | ------------------------------- | ----- | | `user` | `usr` | | `session` | `ses` | | `account`,映射到 `identity` 表 | `idn` | | `verification` | `vfy` | | `organization` | `org` | | `member` | `mem` | | `invitation` | `inv` | | `passkey` | `pky` | | `apikey` | `key` | | `subscription` | `sub` | 工具记录和积分流水分别使用 `run` 和 `crd`。映射和生成器位于 `db/schema/id.ts`。 私有文件使用 `fil`,任务使用 `tsk`,任务执行记录使用 `tat`;输入引用以任务 ID 为主键。用户密钥签发请求使用 `kis`,用户密钥使用 `key`;明文凭据的 `abk_` 前缀与数据库 ID 分开。前缀仍不能作为授权证据。 ## 调用 ```ts import { generateAuthId, generateId } from "@repo/db"; const userId = generateAuthId("user"); const uploadId = generateId("upl"); ``` 认证模型必须出现在前缀映射;普通前缀必须是恰好三个小写字母,否则抛错。生成器惰性初始化,避免模块加载时不必要副作用。 Better Auth 的自定义生成器与 Drizzle 的 `.$defaultFn()` 使用同一约定。直接 SQL 不会执行应用层默认函数,插入时要显式提供标识。 ## 新增模型 认证模型先增加映射及类型,再同步插件和数据库模型。普通业务模型选择未混淆的三位前缀并配置应用默认生成器。结构改变后生成并审查迁移;仅改变生成函数不会自动重写已有数据。 --- --- url: /docs/adr/000-template.md --- # 架构决策记录模板 **状态:** 待讨论 / 已采纳 / 已替代 **日期:** `[日期]` ## 问题 说明需要解决的具体问题、触发条件和已有约束。 ## 决策 说明采用的实现、责任边界,以及哪些配置或代码是事实来源。 ## 备选方案 列出认真考虑过的方案及取舍原因。 ## 影响 记录收益、维护成本、风险和后续撤销条件。 ## 相关资料 链接到实现文件、验证记录和相关决策。 --- --- url: /docs/specs/billing.md --- # 订阅集成规格 ## 设计选择 通过 Better Auth Stripe 插件管理客户、订阅、组织授权与回调,使用托管结账和客户门户。客户端查询走 tRPC,升级和门户跳转走认证客户端,避免重复实现插件已有边界。 当前不包含按量计费、一次性积分包、嵌入式结账及完整税务、多币种方案。这些需要实际业务扩展,不因 Stripe 支持就算模板已接入。 ## 状态流程 ```mermaid sequenceDiagram participant U as 用户 participant A as 账号接口 participant S as 支付服务 participant D as 数据库 U->>A: 请求升级 A->>S: 创建结账会话 S-->>U: 托管结账 S->>A: 签名事件回调 A->>D: 更新订阅 U->>A: 查询当前套餐 ``` 浏览器成功回跳不直接授予付费权限,以已验证事件同步的数据库状态为准。 ## 归属与角色 订阅范围为活动组织或当前个人。组织查询实时验证成员;变更由插件验证所有者或管理员。查询键包含活动组织,切换后不会复用另一组织的账单结果。 数据表是 `subscription`,用户和组织保存相应客户 ID。套餐限制从 `websiteConfig` 派生,插件与查询共用;新增限制还要在业务操作中实施。 ## 配置与验收 支付开关加四项完整基础凭据才启用,年付价格可选。配置、结账和回调教程见 [订阅与账单](../billing/index.md)。现有测试覆盖项目自己的权限、禁用状态、免费回退与查询键边界;真实外部结账和回调仍需单独验收,不能写成已经完成。 --- --- url: /docs/agentbuff-stack/design-validation-2026-10-09.md --- # 设计系统验收记录 · 2026-10-09 这是当次样式迭代的历史记录,后续配置迭代和测试数量另见对应验收页。 ## 实现内容 * 固定 Astryx 核心与中性色包 0.6.6、StyleX 0.19.1,共享导出位于 `@repo/ui/design`。 * 统一品牌变量、本地字体、公开页面、工具编辑器、登录布局、工作台和浅深主题。 * 共享标题、指标、品牌和认证布局;使用 Astryx 操作及输入组件,保留原 Radix 控件行为并统一配色。 * 新增 `/design` 组件预览,排除出站点地图并禁止索引。 * 检查 390 像素宽度的公开导航;工作台指标使用紧凑布局,移动侧栏默认关闭,选路由后关闭。 * 复制新站同步应用清单名称,保留设计源码与第三方声明。 ## 当次本地验证 * 工作台和公开站构建、类型检查通过;Astro 检查无错误、警告或提示。 * Vitest 当时为 11 个文件、104 项测试通过;修正测试模式的编译适配后进程正常结束。 * 静态检查、格式检查及 Git 差异空白检查通过。 * 浏览器检查首页、组件预览、登录布局、浅深工作台、390 像素首页与工作台及侧栏跳转历史。 * 无效 JSON 显示校验错误;有效 JSON 有预览;复制与输出一致;隔离演示账号保存成功;下载内容与测试输入一致。 * 组件预览操作更新实时状态,输入框受控值与关联文本区域同步。 * 原始 HTML 包含标题、规范网址、工具结构化信息和编辑器标签;站点地图可解析且不含私有和组件路径。 * 临时新站副本保留品牌、设计与声明,不复制本地凭据及依赖;验证后移除副本。 本地截图位于被忽略的 `.local/evidence/astryx-*.png`,统一入口为 `http://localhost:4400`。当次没有部署,也没有执行外部支付或发信;热更新与 Cloudflare 生产运行未在此次样式迭代重新验收。 --- --- url: /docs/specs/auth-form.md --- # 账号表单规格 ## 可用入口 共享表单位于 `apps/app/components/auth/auth-form.tsx`,登录与注册根据配置显示密码、邮箱验证码以及服务端已配置的第三方入口。通行密钥仅用于已有账号登录。 密码、验证码及通行密钥开关来自统一站点配置;Google、GitHub 入口来自服务端能力查询,不另外维护前端启用列表。真实服务条款和隐私政策由站点维护者提供,模板示例文本不能直接当成你的正式政策。 ## 验证码步骤 ```text 选择方式 → 输入邮箱 → 发送验证码 → 输入验证码 → 刷新会话 → 进入工作台 ``` 验证码六位、五分钟有效、三次错误尝试;重发按钮 30 秒倒计时不承担服务端防滥用。过期或次数超限时返回邮箱步骤并保留解释。 密码流程位于 `password-form.tsx`,第三方流程位于 `social-login.tsx`,通行密钥流程位于 `passkey-login.tsx`。第三方跳转回来后同样通过真实会话决定是否登录。 ## 成功与错误 子流程执行时禁用冲突操作,错误通过可被辅助技术读取的提示区展示。成功回调刷新会话并完成路由跳转;避免同一表单完成两次。回跳地址先经过站内校验。 品牌链接整页打开公开首页 `/`。私有工作台有独立路由,公开首页不根据登录提示切换。 ## 访问性与验收 输入使用可见标签、正确自动填充属性、焦点和回车提交。验证键盘操作、加载、取消、错误、重复点击及网络恢复。凭据管理界面尚未完整接入,参见 [通行密钥](../auth/passkeys.md)。 --- --- url: /docs/agentbuff-stack/configuration-validation-2026-10-09.md --- # 配置与模块文档验收 · 2026-10-09 本轮对照 MkSaaS 的公开模板介绍、网站配置和环境配置文档,未使用商业源码。 ## 完成内容 * 类型化 `websiteConfig`:品牌、主题、登录、博客、公开统计、订阅、积分、历史与计划;`site` 保持兼容读取。 * Google / GitHub 共用 OAuth 组件,完整服务端凭据决定可用 provider。 * API 三入口通过 `parseEnv` 校验;错误只包含变量名及问题,不打印值。 * `config:check` 离线诊断、目标环境、变量组合、域名、范围及 `.env.example` 覆盖。 * 自有 VitePress 文档首页、技术栈、网站配置、环境变量、完整模块总览和模块指南。 * 新站工厂同时更新网站配置、manifest、Worker 名和品牌环境示例。 ## 验证结果 类型检查、代码检查、Astro 检查、构建与 VitePress 构建通过。Vitest:13 个文件,115 项通过。 配置诊断正例通过;生产 / 开发不匹配与半组 GitHub 凭据返回非零,错误不包含测试 secret。用独立临时目录复制第二个站,验证品牌 ID、名称、Worker、manifest、环境示例与凭据 / 数据排除;复制后诊断通过,临时副本已删除。 构建独立配置变体,关闭博客和主题切换、默认暗色、启用带测试 token 的统计。检查真实静态 HTML:无博客链接与文章路径、sitemap 无博客、RSS 无条目、博客索引 noindex、无主题按钮、默认暗色字段及 beacon 存在。变体只写 `.local/qa/`;配置已恢复默认,公共预览未加载测试 beacon。 真实 Better Auth handler 测试:禁用密码拒绝注册且不写用户;禁用 OTP、Passkey 与订阅后对应端点返回 404。 ## 未验收 Google / GitHub 真实授权、Passkey 真实设备注册、生产邮件、Stripe 沙箱、统计数据、Cloudflare / Neon 部署。R2、队列、AI、管理员、newsletter、Turnstile、聊天、联盟和多语言仍按模块总览列为待开发。