@clawos-dev/clawd 0.2.146-beta.309.31014b1 → 0.2.146
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deploy-kit/scripts/new-extension.sh +17 -1
- package/dist/deploy-kit/scripts/publish.sh +17 -3
- package/dist/deploy-kit/scripts/remove-extension.sh +8 -5
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +7 -4
- package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +3 -3
- package/dist/persona-defaults/persona-app-builder/extension-kit/contract/s.yaml.tmpl +11 -6
- package/package.json +1 -1
|
@@ -23,4 +23,20 @@ remaining="$(find "$DEST" -mindepth 1 -maxdepth 1 ! -name '.clawd-project.json'
|
|
|
23
23
|
# node_modules 已在 + lockfile 没变会直接 skip、不重建软链 → 运行/打包时 Cannot find module。
|
|
24
24
|
# 新项目本来就要 install,故 scaffold 一律不拷 node_modules(保留 lockfile 以锁版本)。
|
|
25
25
|
rsync -a --exclude='node_modules' "$SRC/" "$DEST/"
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
# 全局唯一 slug (4 hex chars, ~65k 散列空间):同名 app 多用户/多副本部署到共享 FC + 共享
|
|
28
|
+
# Supabase 时,FC functionName / 子域名 / SQL 表前缀都得唯一。一次 scaffold 锁定,
|
|
29
|
+
# publish.sh 和 remove-extension.sh 都 source ext.conf 共用同一个 slug。
|
|
30
|
+
# Agent 命名 Supabase 表时务必带上: <APP_NAME>_<SLUG>_<table>(见 CLAUDE.md「后端:supabase」)。
|
|
31
|
+
SLUG="$(openssl rand -hex 2)"
|
|
32
|
+
{
|
|
33
|
+
echo ""
|
|
34
|
+
echo "# === 全局唯一身份 (scaffold 时锁定,不要改) ==="
|
|
35
|
+
echo "# APP_NAME = 项目名 (人类可读身份);SLUG = 4 位散列防同名冲突"
|
|
36
|
+
echo "# 派生:DEPLOY_NAME=\${APP_NAME}-\${SLUG} (FC 函数/子域名,DNS-safe 用连字符)"
|
|
37
|
+
echo "# 表前缀=\${APP_NAME}_\${SLUG}_ (SQL-safe 用下划线)"
|
|
38
|
+
echo "APP_NAME=${NAME}"
|
|
39
|
+
echo "SLUG=${SLUG}"
|
|
40
|
+
} >> "$DEST/ext.conf"
|
|
41
|
+
|
|
42
|
+
echo "✅ 新 extension: $DEST (APP_NAME=${NAME}, SLUG=${SLUG})"
|
|
@@ -22,6 +22,20 @@ CODE_DIR="${CODE_DIR:-./server}"
|
|
|
22
22
|
FC_ENTRY="${FC_ENTRY:-dist/main.js}"
|
|
23
23
|
BUILD_CMD="${BUILD_CMD:-}"
|
|
24
24
|
|
|
25
|
+
# 全局唯一身份:APP_NAME (人读身份/表前缀根) + SLUG (4 位散列)。一次性 scaffold 时
|
|
26
|
+
# 由 new-extension.sh 锁定 + bake 进 ext.conf。这里的 fallback 兜底两种历史情况:
|
|
27
|
+
# 1) 老项目 (本改动前 scaffold) ext.conf 无 SLUG → 现场生成 + bake,保后续稳定
|
|
28
|
+
# 2) 手动 rm SLUG 或损坏 ext.conf → 同上,fail-safe
|
|
29
|
+
# 派生:
|
|
30
|
+
# DEPLOY_NAME=${APP_NAME}-${SLUG} → FC functionName/子域名/s 项目名 (DNS-safe 连字符)
|
|
31
|
+
# 表前缀=${APP_NAME}_${SLUG}_ → Supabase 表 (SQL-safe 下划线,见 remove-extension.sh)
|
|
32
|
+
if [ -z "${SLUG:-}" ]; then
|
|
33
|
+
SLUG="$(openssl rand -hex 2)"
|
|
34
|
+
printf '\nSLUG=%s\n' "$SLUG" >> "$EXT_DIR/ext.conf"
|
|
35
|
+
echo " 📝 锁定 SLUG=$SLUG (含 4 位散列防同名冲突,已 bake 进 ext.conf)"
|
|
36
|
+
fi
|
|
37
|
+
DEPLOY_NAME="${APP_NAME}-${SLUG}"
|
|
38
|
+
|
|
25
39
|
# 2) 工具链 + 阿里云凭证(共享单源)
|
|
26
40
|
source "$KIT_DIR/scripts/ensure-toolchain.sh"
|
|
27
41
|
ensure_toolchain
|
|
@@ -77,12 +91,12 @@ sed "${sed_args[@]}" "$TMPL" > "$EXT_DIR/s.yaml"
|
|
|
77
91
|
|
|
78
92
|
# 7) deploy + verify
|
|
79
93
|
echo "::stage::deploy"
|
|
80
|
-
echo "==> s deploy ($
|
|
94
|
+
echo "==> s deploy ($DEPLOY_NAME)"
|
|
81
95
|
( cd "$EXT_DIR" && s deploy -y )
|
|
82
96
|
|
|
83
97
|
echo "::stage::verify"
|
|
84
|
-
# 精确匹配 s.yaml.tmpl 里写死的 domainName 模式: <
|
|
85
|
-
EXPECTED_DOM="${
|
|
98
|
+
# 精确匹配 s.yaml.tmpl 里写死的 domainName 模式: <DEPLOY_NAME>.app.clawos.chat
|
|
99
|
+
EXPECTED_DOM="${DEPLOY_NAME}.app.clawos.chat"
|
|
86
100
|
DOM="$(aliyun fc GET /2023-03-30/custom-domains --region "$REGION" 2>/dev/null | python3 -c "
|
|
87
101
|
import sys,json
|
|
88
102
|
d=json.load(sys.stdin)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 清理一个 extension:删 FC 资源(函数+触发器+域名)+ 可选删它的 Supabase 表(通用,persona 无关)
|
|
4
4
|
# 用法: remove-extension.sh <extension目录> <persona根目录>
|
|
5
5
|
# 删哪些表:读 ext.conf 的 SUPABASE_TABLES(extension 自己声明的;非 supabase persona 留空即跳过)
|
|
6
|
-
# 安全:① 校验 <
|
|
6
|
+
# 安全:① 校验 <APP_NAME>_<SLUG>_ 前缀防误删 clawos / 同名 app 别人副本 ② 列出 DROP 让 agent 用 supabase MCP 接力 ③ 数据不可逆
|
|
7
7
|
# ============================================================
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
@@ -17,6 +17,9 @@ PERSONA_KIT="$PERSONA_ROOT/extension-kit"
|
|
|
17
17
|
[ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
|
|
18
18
|
APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
|
|
19
19
|
SUPABASE_TABLES="${SUPABASE_TABLES:-}"
|
|
20
|
+
# SLUG 由 new-extension.sh scaffold 时 bake;空 = 老项目(本改动前 scaffold)走 legacy 表前缀。
|
|
21
|
+
# 新项目表名形如 ${APP_NAME}_${SLUG}_<table>;老项目走旧的 ${APP_NAME}_<table>。
|
|
22
|
+
TABLE_PREFIX="${APP_NAME}_${SLUG:+${SLUG}_}"
|
|
20
23
|
|
|
21
24
|
# 确保工具链就绪(缺 s / aliyun 则装,已装跳过)—— 下面 s remove 依赖它
|
|
22
25
|
source "$KIT_DIR/scripts/ensure-toolchain.sh"
|
|
@@ -27,18 +30,18 @@ ensure_toolchain
|
|
|
27
30
|
set -a; . "$KIT_DIR/.secrets/aliyun.env"; set +a
|
|
28
31
|
|
|
29
32
|
# 1) 删 FC 侧(函数 + 触发器 + 自定义域名)
|
|
30
|
-
echo "==> 删 FC 资源: $APP_NAME"
|
|
33
|
+
echo "==> 删 FC 资源: $APP_NAME${SLUG:+ (deploy=${APP_NAME}-${SLUG})}"
|
|
31
34
|
( cd "$EXT_DIR" && s remove -y )
|
|
32
35
|
|
|
33
36
|
# 2) 列出要删的 Supabase 表(脚本不直接调 DB,改打印 DROP 让 agent 用 supabase MCP 跑)。
|
|
34
37
|
# bash 调不到 MCP(MCP 是 LLM 通道),pg 直连要密码 agent 拿不到 —— 最干净:打印 DROP,
|
|
35
38
|
# 让 agent 看到 Bash tool stdout 后用 mcp__supabase__execute_sql 接力。
|
|
36
39
|
if [ -n "$SUPABASE_TABLES" ]; then
|
|
37
|
-
# 安全锁:每个表必须
|
|
40
|
+
# 安全锁:每个表必须 ${TABLE_PREFIX} 前缀,否则拒绝(防误伤 clawos / 同名 app 别人副本的表)
|
|
38
41
|
for t in $SUPABASE_TABLES; do
|
|
39
42
|
case "$t" in
|
|
40
|
-
${
|
|
41
|
-
*) echo "❌ 表 '$t' 不是 '${
|
|
43
|
+
${TABLE_PREFIX}*) ;;
|
|
44
|
+
*) echo "❌ 表 '$t' 不是 '${TABLE_PREFIX}' 前缀,拒绝输出(可能误伤 clawos / 别人同名 app)"; exit 1 ;;
|
|
42
45
|
esac
|
|
43
46
|
done
|
|
44
47
|
|
|
@@ -160,7 +160,7 @@ createProject 成功后 daemon 自动:
|
|
|
160
160
|
部署不要每次从零手搓。「前后端 + Supabase → 阿里云 FC」已固化成流水线,脚本骨架在共享 `$HOME/.clawd/deploy-kit/scripts/`(凭证 + publish/new-extension/ensure-toolchain/verify/remove,全 persona 共用一份),persona 目录只留模板 + config.env + contract/s.yaml.tmpl:
|
|
161
161
|
|
|
162
162
|
- **scaffold 由 daemon 自动跑**(createProject 内联调 `deploy-kit/scripts/new-extension.sh`,把本 persona 的 `extension-kit/examples/nestjs-react/` 模板复制进 project)。你不手动调。
|
|
163
|
-
- 你/老板设计 Supabase
|
|
163
|
+
- 你/老板设计 Supabase 表(`${APP_NAME}_${SLUG}_` 前缀,从 ext.conf 读这俩值)+ 写业务逻辑
|
|
164
164
|
- **publish 由「发布上线」按钮触发 daemon 自动跑** `deploy-kit/scripts/publish.sh <projDir> <persona根>`(build + 部署 FC + 绑域名 + 验证)。仅发布失败时你接管手动重跑(见下文「发布上线」)。
|
|
165
165
|
|
|
166
166
|
设计是「契约层(框架/语言无关)+ 可插拔技术栈示例」:换框架只改 `ext.conf`,换语言改运行时层 + `contract/bootstrap`。**踩过的 FC 坑全固化在配方记忆 `fc-nodejs-deploy-recipe` 和 `contract/` 里,开工前先看。**
|
|
@@ -169,7 +169,10 @@ createProject 成功后 daemon 自动:
|
|
|
169
169
|
|
|
170
170
|
- 一律走 **FC(函数计算,serverless)**:免 Docker、闲置缩到 0 按量、适合海量小 extension。
|
|
171
171
|
- Node web 应用走 `custom.debian12` + 官方 Node 层 + `bootstrap` + `fc3-domain` 自定义域名(否则 fcapp.run 默认 URL 强制下载、浏览器不渲染)。
|
|
172
|
-
-
|
|
172
|
+
- **全局唯一身份**:scaffold 时 `new-extension.sh` 用 `openssl rand -hex 2` 生成 4 位 `SLUG` 并连同 `APP_NAME` 一起 bake 进 `ext.conf`,**锁定后稳定**。多用户/多副本同名 app 都不撞 FC 函数 / 域名 / 共享 Supabase 表。两种派生形式:
|
|
173
|
+
- **DNS-safe**(连字符):`DEPLOY_NAME=${APP_NAME}-${SLUG}` → FC functionName / 子域名 / `s` 项目名
|
|
174
|
+
- **SQL-safe**(下划线):`${APP_NAME}_${SLUG}_` → Supabase 表前缀(见「后端:supabase」)
|
|
175
|
+
- **自定义域名 = `<DEPLOY_NAME>.app.clawos.chat`(HTTPS 直出)**。根域 `clawos.chat` 一条泛解析 CNAME `*.app → <FC accountId>.cn-hangzhou.fc.aliyuncs.com` 一劳永逸;TLS 走通配符证书 `*.app.clawos.chat`(已托管到 FC 账号 CAS,`certId` 在 `extension-kit/config.env`,3 个月自动续期,证书 ID 不变)。**不要**再回退 `domainName: auto` —— 那派的 `xxx.fcapp.run` 阿里云 3 天回收。
|
|
173
176
|
- 工具链:**`aliyun` CLI + Serverless Devs(`s`)**——即 `publish.sh` 内部做的事。两者由 `deploy-kit/scripts/ensure-toolchain.sh` 在 publish/remove 动手前自动检测+安装(缺 `aliyun`:有 brew 走 `brew install aliyun-cli`,否则下官方二进制到 `~/.clawd/bin`;缺 `s`:`npm i -g @serverless-devs/s`),新机器无需手动装。接管发布失败时**不必再排查"CLI 没装"**。
|
|
174
177
|
- 实时推送用 Supabase Realtime(FC 不维持长连接)。
|
|
175
178
|
|
|
@@ -179,7 +182,7 @@ createProject 成功后 daemon 自动:
|
|
|
179
182
|
|
|
180
183
|
- 读表结构、跑 SQL、看 auth 用户都走这个 MCP,不要凭记忆猜 schema
|
|
181
184
|
- 模板里 `extension-kit/config.env` + `server/.env.example` 已对齐这台阿里云自建实例(SUPABASE_URL / SUPABASE_KEY),创建 project 时拷进项目目录的 `.env` 也是同一套
|
|
182
|
-
- **红线**:这是共享生产库。建表 / 迁移前先 `list_tables` 看清现状,新项目的表**必须**用
|
|
185
|
+
- **红线**:这是共享生产库。建表 / 迁移前先 `list_tables` 看清现状,新项目的表**必须**用 `${APP_NAME}_${SLUG}_` 前缀沉到 `public` schema(如 `helloworld_a1b2_click_counter`,`APP_NAME` 和 `SLUG` 都从项目 `ext.conf` 读,scaffold 时 bake,**不要自己另起一套 slug**),避免和 clawos / 其它项目 / 别人同名 app 的表撞名;**绝不** drop / alter clawos 已有的表,不确定哪些是 clawos 的就先问老板
|
|
183
186
|
|
|
184
187
|
**易踩的坑**:MCP 工具走阿里云自建那台,假如哪一步把 `.env` / `config.env` 写成云上 `qmhqmopgwsbmfxiqagay.supabase.co`(曾经的旧默认),就会**MCP 建表落阿里云 / server 通过 supabase-js 查云上**,两台 PostgREST schema cache 各自独立 → `PGRST205 schema cache 找不到表`。看到 PGRST 系列错码先 `grep -r supabase.co` 自查模板配置,再怀疑 schema cache。
|
|
185
188
|
|
|
@@ -201,7 +204,7 @@ set -a; . "$HOME/.clawd/deploy-kit/.secrets/aliyun.env"; set +a
|
|
|
201
204
|
|
|
202
205
|
1. **创建 project**(fresh session 进来时)—— 问老板想做啥 app + 反问名字 + 调 `appBuilder.createProject` RPC(详见上文「触发 createProject」)。已绑 session 跳过这一步
|
|
203
206
|
2. **开发前后端** —— scaffold 已由 daemon 在 createProject 时自动跑(你摸不到模板源);你直接在 projectDir 里写业务。复杂任务走 superpowers 节奏,简单任务 TodoWrite 跟踪(分级见下)
|
|
204
|
-
3. **接 Supabase** —— 数据/认证/存储走 supabase MCP,schema 以线上为准,新表加
|
|
207
|
+
3. **接 Supabase** —— 数据/认证/存储走 supabase MCP,schema 以线上为准,新表加 `${APP_NAME}_${SLUG}_` 前缀(从 ext.conf 读)
|
|
205
208
|
4. **部署** —— 老板点「发布上线」按钮,daemon 自动跑 `deploy-kit/scripts/publish.sh`(build + 发 FC + 绑域名 + 验证 + 写 prodUrl),happy path 跳过你。仅发布失败时你接管手动重跑(见下文「发布上线」)。
|
|
206
209
|
|
|
207
210
|
## build 模式(clawd 双栏实时预览)
|
|
@@ -68,7 +68,7 @@ PR="$HOME/.clawd/personas/persona-app-builder"; DK="$HOME/.clawd/deploy-kit"
|
|
|
68
68
|
|
|
69
69
|
**脚本怎么知道删哪个表**:靠 `ext.conf` 里显式声明的 `SUPABASE_TABLES="myapp_xxx"`(建表时填进去,extension 的"自描述")。三道安全锁:
|
|
70
70
|
|
|
71
|
-
1. 每个表必须
|
|
71
|
+
1. 每个表必须 `${APP_NAME}_${SLUG}_` 前缀(从 ext.conf 读),否则**拒绝删**(物理上防误伤 clawos 共享库 / 同名 app 别人副本)
|
|
72
72
|
2. 删前列出所有要删的表
|
|
73
73
|
3. 要手输 `yes` 确认(数据不可逆,不用 `-y` 静默删)
|
|
74
74
|
|
|
@@ -92,8 +92,8 @@ PR="$HOME/.clawd/personas/persona-app-builder"; DK="$HOME/.clawd/deploy-kit"
|
|
|
92
92
|
|
|
93
93
|
## 红线(都是踩坑换来的,见记忆 fc-nodejs-deploy-recipe)
|
|
94
94
|
|
|
95
|
-
- **Supabase 是 clawos 共享生产库**:新表必须
|
|
96
|
-
- **必须绑自定义域名**(
|
|
95
|
+
- **Supabase 是 clawos 共享生产库**:新表必须 `${APP_NAME}_${SLUG}_` 前缀(从 ext.conf 读),**绝不 drop/alter clawos 已有表**
|
|
96
|
+
- **必须绑自定义域名**(`<DEPLOY_NAME>.app.clawos.chat`,HTTPS 直出,certId 在 config.env),否则 `fcapp.run` 默认 URL 强制下载网页、浏览器不渲染 + 阿里云 3 天回收
|
|
97
97
|
- **不用 SAE**(Node 无代码包,只能镜像=要 Docker);不用 `custom.debian10`/裸 command(node 找不到 / CAExited)
|
|
98
98
|
- **长连接 / WebSocket 推送不适合 FC** → 用 Supabase Realtime(FC 不维持长连接)
|
|
99
99
|
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
# s.yaml 模板 —— publish.sh 用 sed 替换 __占位符__ 后生成实际 s.yaml
|
|
2
2
|
# 框架无关:command 固定走 ./bootstrap,入口由 FC_ENTRY 环境变量决定
|
|
3
|
+
#
|
|
4
|
+
# 占位符约定:
|
|
5
|
+
# __APP_NAME__ = 用户给的项目名(描述/可读用)
|
|
6
|
+
# __DEPLOY_NAME__ = APP_NAME-<4位 slug>,FC functionName / 子域名 / s 项目名/资源 key 走这个
|
|
7
|
+
# 保证多用户/多副本同名时不撞 FC 函数 + 不撞域名
|
|
3
8
|
edition: 3.0.0
|
|
4
|
-
name:
|
|
9
|
+
name: __DEPLOY_NAME__-app
|
|
5
10
|
access: default
|
|
6
11
|
|
|
7
12
|
vars:
|
|
8
13
|
region: __REGION__
|
|
9
14
|
|
|
10
15
|
resources:
|
|
11
|
-
|
|
16
|
+
__DEPLOY_NAME__:
|
|
12
17
|
component: fc3
|
|
13
18
|
props:
|
|
14
19
|
region: ${vars.region}
|
|
15
|
-
functionName:
|
|
20
|
+
functionName: __DEPLOY_NAME__
|
|
16
21
|
description: __APP_NAME__ extension (FC serverless)
|
|
17
22
|
runtime: __FC_RUNTIME__
|
|
18
23
|
logConfig: auto
|
|
@@ -42,16 +47,16 @@ resources:
|
|
|
42
47
|
|
|
43
48
|
# 自定义域名:绑 *.app.clawos.chat 子域名(根域 DNS 已加泛解析 CNAME 到 FC)
|
|
44
49
|
# 证书走 FC 账号 CAS 托管(certId 在 config.env 里),续期 FC 自动跟新
|
|
45
|
-
|
|
50
|
+
__DEPLOY_NAME__Domain:
|
|
46
51
|
component: fc3-domain
|
|
47
52
|
props:
|
|
48
53
|
region: ${vars.region}
|
|
49
|
-
domainName:
|
|
54
|
+
domainName: __DEPLOY_NAME__.app.clawos.chat
|
|
50
55
|
protocol: HTTP,HTTPS
|
|
51
56
|
certConfig:
|
|
52
57
|
certId: __CERT_ID__
|
|
53
58
|
routeConfig:
|
|
54
59
|
routes:
|
|
55
60
|
- path: /*
|
|
56
|
-
functionName:
|
|
61
|
+
functionName: __DEPLOY_NAME__
|
|
57
62
|
qualifier: LATEST
|
package/package.json
CHANGED