网页应用双形态交付

SKILL开发

原名 网页应用双形态交付 · 在线版与单页版同时产出 · 标识 webapp-dual-delivery

开发 未标注权限 许可证不明确
安装这个技能

从零构建网页应用并同时产出两种形态:带后端代理、密钥不落前端的公网在线版,以及双击即用、使用者自填密钥的单页版,用同一份前端代码参数化生成,解决密钥泄露难题。

一句话选型结论

按资料字段整理

网页应用双形态交付是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_0c7f1934/webapp-dual-delivery
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_0c7f1934仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量11.5 KBSKILL.md 原文大小,越大说明说明书写得越细

能做什么

0 项能力
上游给出的能力概览与适用场景
安全分享带密钥网页生成可双击单页版发布公网在线版

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
网页应用双形态交付当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

11.5 KB
模型实际读到的说明书原文,来自技能仓库

webapp-dual-delivery · 网页应用双形态交付

从零做一个网页应用,一次产出两个可交付物:

形态 产物 密钥在哪 适用
在线版 online/(server.py + public/ + .env) 服务端环境变量 / .env 公网发布、多人共用
单页版 single/(index.html + css + js) 使用者自己填,存 localStorage 双击即用、离线单机、发给同事

同一份前端代码,参数化生成两种形态。 不维护两套。


零、先问清三件事(别急着写代码)

  1. 要不要调外部 API? 要 → 必须双形态;不要 → 只出单页版即可
  2. 谁会拿到密钥? 只有自己 → 单页版硬编码也行;给别人 → 必须走代理
  3. 发布目标是公网还是内网? 公网 → 必须有后端;内网 → 可只出单页

用户说"随便你定"时,按最保守方案来:双形态都出,密钥留空。


一、为什么必须分两个形态(这是本 skill 存在的理由)

核心风险:密钥泄露。

多数 AI / SaaS 接口的 CORS 是完全开放的——实测过连 https://evil.example.com
和 Origin: null(file:// 场景)都回显放行。这意味着:

> 把 API Key 写进前端 HTML,页面一旦传到公网,任何人按 F12 就能看到并盗用,
> 账单直接记在部署者头上。

所以:

  • 要分享给别人 / 要公网访问 → 在线版,密钥只在服务端
  • 自己本机用 / 离线 → 单页版,密钥可以内置或手填

绝不存在"既要公网又要密钥安全且没有后端"的方案。


二、生成脚手架

python scripts/scaffold.py \
  --out <输出目录> \
  --title "我的应用" \
  --api-base "https://api.example.com/v1/chat" \
  --mode both

产出:

<输出目录>/
  online/           # 在线版
    server.py       # 标准库 HTTP 代理:SSE 透传 / 限流 / 白名单 / 目录穿越防护
    .env            # 服务端配置(密钥只在这里)
    public/
      index.html    # 只放结构
      theme.css     # 全部样式,语义化变量驱动明暗双主题
      app.js        # 全部逻辑
  single/           # 单页版
    index.html
    theme.css
    app.js          # 直连上游,密钥由使用者填
  README.md

常用参数:

参数 说明
--mode online\|single\|both 只要一种就指定,能省一半文件
--allow-upstream "/v1/chat,/v1/models" 上游路径白名单,逗号分隔。公网部署必填
--rate-limit 20 --rate-window 60 限流:同 IP 每 60 秒 20 次
--upstream-timeout 300 上游超时(长回答要调大)
--subtitle --logo --placeholder --hint 文案定制

生成后必须替换 app.js 里的业务逻辑。脚手架给的是"能跑的骨架",不是成品。


三、在线版:server.py 要点

只用标准库,无需 pip install。发布平台要求:必须监听 0.0.0.0 的 $PORT,已满足。

内置的四道防护(都已实现,不要删):

防护 实现
SSE 真流式 text/event-stream 时逐块 write + flush,并发 X-Accel-Buffering: no 关掉 nginx 缓冲
目录穿越 规范化路径后校验是否仍在 public/ 内,否则 403
限流 内存记录同 IP 请求时间戳,超阈值 429
上游白名单 ALLOW_UPSTREAM 限制可转发路径,防 SSRF

★★ .env 优先于环境变量(反直觉,必须知道)

_conf() 的实现是 .env 优先:

def _conf(key, default=""):
    v = _DOTENV.get(key)          # 先读 .env
    if not v: v = os.environ.get(key)   # 再退环境变量
    return v

为什么不用常见的"环境变量优先"?

实测踩过:本机 shell 里存在一个 API_KEY 环境变量,值是 35 字符、以 sk- 开头的
无关 token(平台注入的)。它覆盖了 .env 里的正确 Key,导致所有请求 401,
而直连上游却是好的。排查花了不少时间。

结论:显式写下的配置文件才是部署者的真实意图,应该压过环境变量。
以后凡是"配置文件 vs 环境变量"双来源,都要考虑注入污染。


四、前端 SSE 解析:必须带残余缓冲

分块传输时,一行 JSON 会被网络切成多块。"收到就解析"必然丢事件。

function makeParser(){
  var buf = "";                      // 未消费的残余文本
  function feed(chunk){
    buf += chunk;
    var evs = [], idx;
    while(true){
      idx = buf.indexOf("\n");
      if(idx < 0) break;             // 行尾没到齐 → 等下一块
      var line = buf.slice(0, idx);
      buf = buf.slice(idx + 1);
      line = line.replace(/\r$/, "");
      if(!line) continue;            // 空行 = 事件分隔
      if(line.indexOf("data:") === 0){
        var p = line.slice(5).trim();
        if(!p) continue;
        try { evs.push(JSON.parse(p)); }
        catch(e){ buf = p + "\n" + buf; break; }  // 极少数跨行 data
      }
    }
    return evs;
  }
  function flush(){ /* 结束时把残余里完整的 JSON 补捞 */ }
  return { feed: feed, flush: flush };
}

验收方法(必做):用真实抓取的数据做三档对拍——整块 / 随机分块 / 单字节分块,
三者的事件签名必须完全一致。单字节分块是最严格的边界。


五、发布到公网

用 sites 能力发布(workbuddy_sites_deploy),参数:

{
  "action": "deploy",
  "directory": "<online 目录的绝对路径>",
  "language": "python",
  "startCmd": "python server.py",
  "port": 8080,
  "domainPrefix": "<短英文 slug>",
  "appName": "<中文展示名>",
  "userAskedToPublish": true
}

注意:发布只上传源码目录,不继承本机环境变量。 所以 API_KEY 必须写进
online/.env,让它随源码进沙箱。.gitignore 里排除 .env 只影响 git,不影响上传。

发布后必查四项:

U=https://<slug>.app.workbuddy.host
curl -s "$U/api/health"                              # hasKey 必须为 true
curl -s -o /dev/null -w "%{http_code}\n" "$U/"        # 200
curl -s "$U/app.js" | grep -c "<你的key>"              # 必须是 0
curl -s -N -X POST "$U/api/proxy/chat" \
  -H "Content-Type: application/json" -d '{"messages":[{"role":"user","content":"测试"}]}'
# 最后一问:真发一条,看有没有 answer 事件

第 3 项是安全底线——线上前端绝不能出现密钥字面量。


六、发布前自检清单

□ node --check public/app.js          前端语法
□ python -m py_compile server.py      后端语法
□ 本地起服务,三档分块对拍解析器      不能丢事件
□ curl 首页 / theme.css / app.js      静态资源 200
□ curl /api/health                    hasKey=true
□ 前端 grep 密钥                      0 处
□ 真发一条问答                        有 answer 事件
□ 错误路径:空 body / 坏 JSON / 目录穿越  都有合理报错

七、踩过的坑(都实测过)

1. .env 被环境变量里的同名变量覆盖 → 401

见第三节。.env 优先。

2. 中文文案里直接写英文双引号 → JS 语法错误

'<p>—— A" + (i+1) + " ——</p>'   // ❌ 引号不闭合
'<p>—— A' + (i+1) + ' ——</p>'   // ✅

Node --check 能抓到,浏览器上表现为整页白屏。

3. Git Bash 里 B / P 是内置变量

for f in ...; curl "$B$f" 会被展开成 PortableGit 路径,全部 502。
URL 变量至少写两个字母(BASE_URL),别用单字母。

4. t=t[5:].trim() 不是合法 JS

JS 不支持这种切片写法,必须 t.slice(5).trim()。node --check 能抓到。

5. Shell 吃掉 \u0001

用 heredoc 写 JS 时 \u0001 会被 shell 当转义处理掉,占位符变成裸数字。
写进 .py 文件用 chr(1) 构造真实控制字符注入,别在 shell 里写。

6. CSS 变量写错值很难肉眼发现

写完扫一遍变量定义。本次手误写了 --brand-b:#274karma;(无效值)。

7. .env 里 "Bearer " 的尾随空格被 strip 掉 → 认证失败

写 AUTH_PREFIX="Bearer " 想保住空格,但朴素的 .strip().strip('"') 会连空格一起去掉,
最终拼出 Authorization: Bearer(无空格),上游一律 401。

正确写法:判断"是否为空"用 .strip(),取值时保留原始空白,只脱配对引号:

if len(v) >= 2 and v[0] == v[-1] and v[0] in ("'", '"'):
    v = v[1:-1]          # 保留引号内的空格

需要干净值的字段(API_KEY / API_BASE)在使用处单独 .strip()。
生成后务必实测:repr(_conf('AUTH_PREFIX')) 应为 'Bearer '。

8. 生成器里 {{ 转义与 CSS 天然大括号冲突

用 str.format() 生成含大量 CSS 的文件时,CSS 的 }} 会被误判成模板残留。
排查时先确认是不是误报:@keyframes x{50%{opacity:0}} 结尾的 }} 完全合法。
真残留的特征是孤立出现的 {{ / }}(前文不是 CSS 语法)。


八、参考

  • references/api-integration.md —— 各家 API 的 CORS 行为、messages 条数限制、
    会话 ID 透传等实测结论
  • references/frontend-patterns.md —— SSE 解析、Markdown 渲染、引用标签还原、
    深色主题、导出 Word 的实现片段

九、与既有 skill 的边界

skill 做什么 与本 skill 的区别
site-clone-static 复刻需登录的 Web 后台 那个是扒已有站,本 skill 是从零建新站
webpage-offline-replica 复刻在线网页为离线静态页 同上,且那个只产出离线形态
any-site-to-offline 在线站整站镜像 同上
demo-site-config demo 换客户配置化 那个改已有 demo,本 skill 建新项目

本 skill 独有:同时产出「公网在线版 + 单页版」两种交付形态,并解决密钥安全。

常见问题

5 问
答案由本站依据条目字段整理
网页应用双形态交付怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_0c7f1934/webapp-dual-delivery。安装方式为未标注。装完刷新技能列表或重开客户端。

网页应用双形态交付支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

网页应用双形态交付权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

网页应用双形态交付收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

网页应用双形态交付用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。