@clawos-dev/clawd 0.2.143-beta.300.c2a3792 → 0.2.143-beta.301.814d293

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.
Files changed (26) hide show
  1. package/dist/cli.cjs +299 -238
  2. package/dist/deploy-kit/.secrets/aliyun.env.example +4 -0
  3. package/dist/deploy-kit/scripts/new-extension.sh +22 -0
  4. package/dist/deploy-kit/scripts/publish.sh +100 -0
  5. package/dist/deploy-kit/scripts/publish.spec.ts +69 -0
  6. package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/remove-extension.sh +15 -26
  7. package/dist/persona-defaults/persona-app-builder/CLAUDE.md +13 -13
  8. package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +24 -18
  9. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/ext.conf +5 -0
  10. package/dist/persona-defaults/persona-dataclaw-builder/CLAUDE.md +48 -2
  11. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/README.md +28 -25
  12. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/ext.conf +5 -0
  13. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/hooks/pre-deploy.sh +25 -0
  14. package/package.json +1 -1
  15. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/new-extension.sh +0 -66
  16. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +0 -110
  17. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/contract/bootstrap +0 -22
  18. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/ensure-toolchain.sh +0 -62
  19. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/new-extension.sh +0 -66
  20. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/publish.sh +0 -129
  21. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/remove-extension.sh +0 -70
  22. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/scripts/verify.sh +0 -38
  23. /package/dist/{persona-defaults/persona-app-builder → deploy-kit}/.secrets/aliyun.env +0 -0
  24. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/contract/bootstrap +0 -0
  25. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/ensure-toolchain.sh +0 -0
  26. /package/dist/{persona-defaults/persona-app-builder/extension-kit → deploy-kit}/scripts/verify.sh +0 -0
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 从示例模板生成一个新 extension
3
- # 用法: new-extension.sh <name> [示例模板, 默认 nestjs-react] [目标项目目录绝对路径]
4
- #
5
- # 目标目录解析(优先级从高到低):
6
- # - 显式目标目录(第三参,v2 per-user):daemon createProject 已在
7
- # users/<userId>/projects/<name>/ 建好(含 .clawd-project.json)→ 把模板展开到此目录。
8
- # v2 多人隔离后项目不再落 persona 目录下,脚本无法自行推导,必须由 daemon 显式传入。
9
- # - picker 模式(老布局兼容):persona-app-builder/projects/<name>/ 已由 daemon 建好
10
- # - 老路径兼容:以上都不存在时回退到 persona-app-builder/<name>/
11
- set -euo pipefail
12
- KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
13
- PERSONA_DIR="$(cd "$KIT_DIR/.." && pwd)"
14
-
15
- NAME="${1:?用法: new-extension.sh <name> [模板,默认 nestjs-react] [目标目录]}"
16
- TMPL="${2:-nestjs-react}"
17
- DEST_OVERRIDE="${3:-}"
18
- SRC="$KIT_DIR/examples/$TMPL"
19
-
20
- [ -d "$SRC" ] || { echo "示例模板不存在: $SRC (现有: $(ls "$KIT_DIR/examples"))"; exit 1; }
21
-
22
- # 把模板内容展开到一个已含 .clawd-project.json 的目录(daemon 预建)
23
- expand_into() {
24
- local dest="$1"
25
- local remaining
26
- remaining="$(find "$dest" -mindepth 1 -maxdepth 1 ! -name '.clawd-project.json' -print -quit)"
27
- [ -z "$remaining" ] || { echo "目标已有内容(非空目录): $dest"; exit 1; }
28
- cp -R "$SRC/." "$dest/"
29
- }
30
-
31
- PICKER_DEST="$PERSONA_DIR/projects/$NAME"
32
- if [ -n "$DEST_OVERRIDE" ]; then
33
- # v2 per-user:daemon 显式传入目标目录
34
- DEST="$DEST_OVERRIDE"
35
- [ -d "$DEST" ] && [ -f "$DEST/.clawd-project.json" ] || {
36
- echo "目标目录无效(必须已存在且含 .clawd-project.json): $DEST"; exit 1
37
- }
38
- expand_into "$DEST"
39
- elif [ -d "$PICKER_DEST" ] && [ -f "$PICKER_DEST/.clawd-project.json" ]; then
40
- # picker 模式(老布局):目录已存在且仅含 .clawd-project.json
41
- DEST="$PICKER_DEST"
42
- expand_into "$DEST"
43
- else
44
- DEST="$PERSONA_DIR/$NAME"
45
- [ -e "$DEST" ] && { echo "目标已存在: $DEST"; exit 1; }
46
- cp -r "$SRC" "$DEST"
47
- fi
48
-
49
- # 写 ext.conf(框架相关配置,publish.sh 会读)
50
- cat > "$DEST/ext.conf" <<EOF
51
- APP_NAME=$NAME
52
- CODE_DIR=./server
53
- FC_ENTRY=dist/main.js
54
- BUILD_CMD="cd web && pnpm install && pnpm build && cd ../server && pnpm install && pnpm build && mkdir -p dist/public && cp -R ../web/dist/. dist/public/"
55
- # 本 extension 拥有的 Supabase 表(空格分隔,必须 ${NAME}_ 前缀);remove-extension.sh 据此清理
56
- SUPABASE_TABLES=""
57
- EOF
58
-
59
- echo "✅ 新 extension: $DEST"
60
- echo ""
61
- echo "接下来:"
62
- echo " 1) 设计 Supabase 表(务必用 ${NAME}_ 前缀,别碰 clawos 已有表)"
63
- echo " 2) 改前端(web/)和后端(server/)业务逻辑"
64
- echo " 3) 本地验证后发布: extension-kit/scripts/publish.sh $DEST"
65
- echo ""
66
- echo "换框架/语言: 改 ext.conf 的 BUILD_CMD/FC_ENTRY;换语言再看 extension-kit/README.md"
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # 一键发布 extension 到阿里云 FC(框架无关)
4
- # 用法: publish.sh <extension目录>
5
- # extension 目录里可放 ext.conf 声明框架相关的: APP_NAME/CODE_DIR/FC_ENTRY/BUILD_CMD
6
- # ============================================================
7
- set -euo pipefail
8
-
9
- KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
10
- PERSONA_DIR="$(cd "$KIT_DIR/.." && pwd)"
11
-
12
- EXT_DIR="${1:?用法: publish.sh <extension目录>}"
13
- EXT_DIR="$(cd "$EXT_DIR" && pwd)"
14
-
15
- # 1) 平台变量 + extension 自身配置(后者覆盖前者)
16
- source "$KIT_DIR/config.env"
17
- [ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
18
- APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
19
- CODE_DIR="${CODE_DIR:-./server}"
20
- FC_ENTRY="${FC_ENTRY:-dist/main.js}"
21
- BUILD_CMD="${BUILD_CMD:-}"
22
-
23
- # 2) 工具链就绪(检测环境,缺 aliyun CLI / Serverless Devs 则装,已装跳过)+ 阿里云凭证
24
- source "$KIT_DIR/scripts/ensure-toolchain.sh"
25
- ensure_toolchain
26
-
27
- # 阿里云凭证(现读)+ Serverless Devs 认证(必须含 AccountID,SK 走 heredoc 不进命令行)
28
- set -a; . "$PERSONA_DIR/.secrets/aliyun.env"; set +a
29
- # aliyun CLI 需要 region;新机器没跑过 `aliyun configure`(无 ~/.aliyun/config.json) → "region can't be empty"。
30
- # 用 env 给 region(REGION 来自 config.env):一处覆盖脚本里所有 aliyun 调用,不进 argv,和 AK/SK 走 env 的方式对称。
31
- export ALIBABA_CLOUD_REGION_ID="$REGION"
32
- # aliyun 报错不再吞进 2>/dev/null —— 否则凭证失效 / CLI 异常时只剩一条无头 python traceback(老板踩过)。
33
- _ident="$(aliyun sts GetCallerIdentity 2>&1)" || { echo "❌ aliyun sts GetCallerIdentity 失败(检查 .secrets/aliyun.env 凭证):" >&2; echo "$_ident" >&2; exit 1; }
34
- ACC="$(printf '%s' "$_ident" | python3 -c 'import sys,json;print(json.load(sys.stdin)["AccountId"])')" || { echo "❌ 解析 AccountId 失败,aliyun 输出非 JSON:" >&2; echo "$_ident" >&2; exit 1; }
35
- mkdir -p ~/.s
36
- cat > ~/.s/access.yaml <<EOF
37
- default:
38
- AccountID: '$ACC'
39
- AccessKeyID: $ALIBABA_CLOUD_ACCESS_KEY_ID
40
- AccessKeySecret: $ALIBABA_CLOUD_ACCESS_KEY_SECRET
41
- EOF
42
- chmod 600 ~/.s/access.yaml
43
-
44
- # 3) 构建产物(框架相关,由 ext.conf 的 BUILD_CMD 定义)
45
- # 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.4):
46
- # echo ::stage::build 在 if 外,BUILD_CMD 为空时也通知 daemon 进入 build 阶段(即使啥也没构建)。
47
- # daemon 解析 stdout 命中此 marker 后 broadcast appBuilder:publish-progress 帧推 UI checklist。
48
- echo "::stage::build"
49
- if [ -n "$BUILD_CMD" ]; then
50
- echo "==> build: $BUILD_CMD"
51
- ( cd "$EXT_DIR" && eval "$BUILD_CMD" )
52
- fi
53
-
54
- # 4) 放置框架无关的 bootstrap 到 code 目录
55
- cp "$KIT_DIR/contract/bootstrap" "$EXT_DIR/$CODE_DIR/bootstrap"
56
- chmod +x "$EXT_DIR/$CODE_DIR/bootstrap"
57
-
58
- # 5) 渲染 s.yaml(| 作分隔符,避免 URL/层 ARN 里的 / : 冲突)
59
- sed -e "s|__APP_NAME__|$APP_NAME|g" \
60
- -e "s|__REGION__|$REGION|g" \
61
- -e "s|__FC_RUNTIME__|$FC_RUNTIME|g" \
62
- -e "s|__NODE_LAYER__|$NODE_LAYER|g" \
63
- -e "s|__FC_CPU__|$FC_CPU|g" \
64
- -e "s|__FC_MEMORY__|$FC_MEMORY|g" \
65
- -e "s|__FC_TIMEOUT__|$FC_TIMEOUT|g" \
66
- -e "s|__CODE_DIR__|$CODE_DIR|g" \
67
- -e "s|__FC_ENTRY__|$FC_ENTRY|g" \
68
- -e "s|__SUPABASE_URL__|$SUPABASE_URL|g" \
69
- -e "s|__SUPABASE_KEY__|$SUPABASE_KEY|g" \
70
- "$KIT_DIR/contract/s.yaml.tmpl" > "$EXT_DIR/s.yaml"
71
-
72
- # 6) 部署
73
- echo "::stage::deploy"
74
- echo "==> s deploy ($APP_NAME)"
75
- ( cd "$EXT_DIR" && s deploy -y )
76
-
77
- # 7) 取自定义域名 + 验证
78
- echo "::stage::verify"
79
- DOM="$(aliyun fc GET /2023-03-30/custom-domains --region "$REGION" 2>/dev/null | python3 -c "
80
- import sys,json
81
- d=json.load(sys.stdin)
82
- m=[c['domainName'] for c in d.get('customDomains',[]) if '$APP_NAME' in c.get('domainName','')]
83
- print(m[0] if m else '')")"
84
-
85
- echo ""
86
- echo "=================================================="
87
- if [ -n "$DOM" ]; then
88
- # daemon runner 优先解析这个 marker 作为 prodUrl(避免抠到 s deploy 中间的 FC system_url
89
- # fcapp.run,阿里云对它强制 Content-Disposition: attachment,iframe / 浏览器直访都会变成下载)。
90
- # 必须用 custom domain(<app>.fcv3.<accountId>.cn-hangzhou.fc.devsapp.net)。
91
- echo "::prod-url::http://$DOM"
92
- echo " ✅ 发布完成: http://$DOM"
93
- echo "=================================================="
94
- # verify.sh 失败(非 200 / Content-Disposition)→ exit 非 0,由 set -e 让 publish.sh
95
- # 整体失败 → daemon runner 走 publish-failed 路径 + 回灌 chat 让 assistant 接管修复。
96
- # 之前的 `|| true` 兜底已去掉,理由:线上挂了 prodUrl 仍写盘 = 老板看到"打开线上"按钮
97
- # 但点开 404,不如 fail-loud。
98
- #
99
- # 显式走 bash 解释器调用(不依赖 verify.sh 自己的 +x 权限):
100
- # daemon 调 publish.sh / new-extension.sh 都是 `spawn('bash', [scriptPath, ...])`,所以即使脚本
101
- # +x bit 丢了(历史上有过:OTA 解包 / cp -p 未带 / desktop 旧包 mode 不对)也能跑。但这一行
102
- # 之前是 direct-exec `"$KIT_DIR/scripts/verify.sh"`,OS 会检查 +x —— verify.sh 因此成为唯一
103
- # 暴露 +x 丢失问题的执行点(老板撞到过一次"verify.sh permission denied")。
104
- # 改 bash 调用后永远不再依赖 fs +x bit。
105
- bash "$KIT_DIR/scripts/verify.sh" "http://$DOM"
106
- else
107
- # 域名没拿到 = 部署本身有问题(fc3-domain 未成功),也算失败
108
- echo "❌ 部署完成但未取到自定义域名,检查 fc3-domain 是否成功" >&2
109
- exit 1
110
- fi
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # FC custom runtime 启动入口 —— 框架无关的 Node 参考实现
4
- # ============================================================
5
- # 契约: FC 只要求"一个监听 $PORT 的 HTTP server"。本脚本负责把它拉起来。
6
- # - 换 Node 框架(Express/Fastify/NestJS/Hono...): 只改 s.yaml 的 APP_ENTRY 环境变量
7
- # 指向你的入口文件(如 index.js / dist/main.js),本文件不用动。
8
- # - 换语言(Python/Go...): 替换本文件(如 `exec python app.py`),并在 config.env
9
- # 换 NODE_LAYER 为对应语言的运行时层。
10
- # ------------------------------------------------------------
11
- cd "$(dirname "$0")" || exit 1
12
-
13
- # custom.debian* 不自带 node,从挂载的官方 Node 层(/opt)动态定位,不赌固定路径
14
- NODE_BIN="$(command -v node || find /opt -maxdepth 6 -type f -name node 2>/dev/null | head -1)"
15
- if [ -z "$NODE_BIN" ]; then
16
- echo "[bootstrap] FATAL: node 不在 PATH 也不在 /opt,检查是否挂了 Node 运行时层" >&2
17
- exit 1
18
- fi
19
-
20
- ENTRY="${APP_ENTRY:-dist/main.js}"
21
- echo "[bootstrap] node=$NODE_BIN entry=$ENTRY cwd=$(pwd) port=${PORT:-9000}"
22
- exec "$NODE_BIN" "$ENTRY"
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # 确保发布/清理用到的工具链就绪:Serverless Devs (s) + 阿里云 CLI (aliyun)。
4
- # 检测环境、缺啥装啥、已装则跳过(幂等)。被 publish.sh / remove-extension.sh
5
- # 在调用 aliyun/s 之前 `source` 进来,再调 ensure_toolchain。
6
- #
7
- # 为什么需要它:FC 发布全靠 aliyun CLI + s 两个外部命令,二者都不随 clawd 安装。
8
- # 新机器上它们不在 PATH → publish.sh 第一条 `aliyun sts ...` stdout 为空 →
9
- # python json.load 空串 → `Expecting value: line 1 column 1`,且死在第一个
10
- # ::stage:: marker 之前 → daemon 只能报 [unknown] 阶段。这个脚本补的就是这一环。
11
- # ============================================================
12
-
13
- # aliyun CLI 官方 release 兜底版本(仅在无 brew、走二进制下载时用;brew 路径不读它)。
14
- CLAWD_ALIYUN_CLI_VERSION="${CLAWD_ALIYUN_CLI_VERSION:-3.3.21}"
15
-
16
- ensure_s() {
17
- command -v s >/dev/null 2>&1 && return 0
18
- echo "==> 安装 Serverless Devs (s)…"
19
- command -v npm >/dev/null 2>&1 || { echo "❌ 缺 npm,无法安装 @serverless-devs/s" >&2; return 1; }
20
- npm install -g @serverless-devs/s
21
- }
22
-
23
- ensure_aliyun() {
24
- command -v aliyun >/dev/null 2>&1 && return 0
25
- echo "==> 安装阿里云 CLI (aliyun)…"
26
- # 优先 brew(有 brew 的 mac 最省事,装到已在 PATH 的 /opt/homebrew/bin | /usr/local/bin)
27
- if command -v brew >/dev/null 2>&1; then
28
- brew install aliyun-cli
29
- return 0
30
- fi
31
- # 兜底:官方 release 二进制(没装 brew 的新机器 / Linux server)。
32
- local os arch bindir="$HOME/.clawd/bin"
33
- case "$(uname -s)" in
34
- Darwin) os=macosx ;;
35
- Linux) os=linux ;;
36
- *) echo "❌ 不支持的系统 $(uname -s),请手动安装 aliyun CLI" >&2; return 1 ;;
37
- esac
38
- case "$(uname -m)" in
39
- arm64|aarch64) arch=arm64 ;;
40
- x86_64|amd64) arch=amd64 ;;
41
- *) echo "❌ 不支持的架构 $(uname -m),请手动安装 aliyun CLI" >&2; return 1 ;;
42
- esac
43
- local ver="$CLAWD_ALIYUN_CLI_VERSION"
44
- local url="https://github.com/aliyun/aliyun-cli/releases/download/v${ver}/aliyun-cli-${os}-${ver}-${arch}.tgz"
45
- echo " 无 brew,下载官方二进制: $url"
46
- mkdir -p "$bindir"
47
- curl -fsSL "$url" | tar xz -C "$bindir"
48
- chmod +x "$bindir/aliyun"
49
- export PATH="$bindir:$PATH"
50
- }
51
-
52
- ensure_toolchain() {
53
- ensure_s
54
- ensure_aliyun
55
- }
56
-
57
- # 直接执行(bash ensure-toolchain.sh)= 自检;被 source 时不自动跑,留给调用方显式 ensure_toolchain。
58
- if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
59
- set -euo pipefail
60
- ensure_toolchain
61
- echo "✅ 工具链就绪: s=$(command -v s) aliyun=$(command -v aliyun)"
62
- fi
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 从示例模板生成一个新 extension
3
- # 用法: new-extension.sh <name> [示例模板, 默认 nestjs-react] [目标项目目录绝对路径]
4
- #
5
- # 目标目录解析(优先级从高到低):
6
- # - 显式目标目录(第三参,v2 per-user):daemon createProject 已在
7
- # users/<userId>/projects/<name>/ 建好(含 .clawd-project.json)→ 把模板展开到此目录。
8
- # v2 多人隔离后项目不再落 persona 目录下,脚本无法自行推导,必须由 daemon 显式传入。
9
- # - picker 模式(老布局兼容):persona-app-builder/projects/<name>/ 已由 daemon 建好
10
- # - 老路径兼容:以上都不存在时回退到 persona-app-builder/<name>/
11
- set -euo pipefail
12
- KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
13
- PERSONA_DIR="$(cd "$KIT_DIR/.." && pwd)"
14
-
15
- NAME="${1:?用法: new-extension.sh <name> [模板,默认 nestjs-react] [目标目录]}"
16
- TMPL="${2:-nestjs-react}"
17
- DEST_OVERRIDE="${3:-}"
18
- SRC="$KIT_DIR/examples/$TMPL"
19
-
20
- [ -d "$SRC" ] || { echo "示例模板不存在: $SRC (现有: $(ls "$KIT_DIR/examples"))"; exit 1; }
21
-
22
- # 把模板内容展开到一个已含 .clawd-project.json 的目录(daemon 预建)
23
- expand_into() {
24
- local dest="$1"
25
- local remaining
26
- remaining="$(find "$dest" -mindepth 1 -maxdepth 1 ! -name '.clawd-project.json' -print -quit)"
27
- [ -z "$remaining" ] || { echo "目标已有内容(非空目录): $dest"; exit 1; }
28
- cp -R "$SRC/." "$dest/"
29
- }
30
-
31
- PICKER_DEST="$PERSONA_DIR/projects/$NAME"
32
- if [ -n "$DEST_OVERRIDE" ]; then
33
- # v2 per-user:daemon 显式传入目标目录
34
- DEST="$DEST_OVERRIDE"
35
- [ -d "$DEST" ] && [ -f "$DEST/.clawd-project.json" ] || {
36
- echo "目标目录无效(必须已存在且含 .clawd-project.json): $DEST"; exit 1
37
- }
38
- expand_into "$DEST"
39
- elif [ -d "$PICKER_DEST" ] && [ -f "$PICKER_DEST/.clawd-project.json" ]; then
40
- # picker 模式(老布局):目录已存在且仅含 .clawd-project.json
41
- DEST="$PICKER_DEST"
42
- expand_into "$DEST"
43
- else
44
- DEST="$PERSONA_DIR/$NAME"
45
- [ -e "$DEST" ] && { echo "目标已存在: $DEST"; exit 1; }
46
- cp -r "$SRC" "$DEST"
47
- fi
48
-
49
- # 写 ext.conf(框架相关配置,publish.sh 会读)
50
- cat > "$DEST/ext.conf" <<EOF
51
- APP_NAME=$NAME
52
- CODE_DIR=./server
53
- FC_ENTRY=dist/main.js
54
- BUILD_CMD="cd web && pnpm install && pnpm build && cd ../server && pnpm install && pnpm build && mkdir -p dist/public && cp -R ../web/dist/. dist/public/"
55
- # 建页人 union_id(dataclaw 数据范围按它裁剪);persona 建页时调 auth.getIdentity 取到后填入。
56
- DATACLAW_AS_UNION_ID=""
57
- EOF
58
-
59
- echo "✅ 新 extension: $DEST"
60
- echo ""
61
- echo "接下来:"
62
- echo " 1) 填建页人 union_id 到 ext.conf 的 DATACLAW_AS_UNION_ID(调 auth.getIdentity 取)"
63
- echo " 2) 改前端(web/)和后端(server/)业务逻辑;数据只读,经 dataclaw-service"
64
- echo " 3) 本地验证后发布: extension-kit/scripts/publish.sh $DEST"
65
- echo ""
66
- echo "换框架/语言: 改 ext.conf 的 BUILD_CMD/FC_ENTRY;换语言再看 extension-kit/README.md"
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # 一键发布 extension 到阿里云 FC(框架无关)
4
- # 用法: publish.sh <extension目录>
5
- # extension 目录里可放 ext.conf 声明框架相关的: APP_NAME/CODE_DIR/FC_ENTRY/BUILD_CMD
6
- # ============================================================
7
- set -euo pipefail
8
-
9
- KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
10
- PERSONA_DIR="$(cd "$KIT_DIR/.." && pwd)"
11
-
12
- EXT_DIR="${1:?用法: publish.sh <extension目录>}"
13
- EXT_DIR="$(cd "$EXT_DIR" && pwd)"
14
-
15
- # 1) 平台变量 + extension 自身配置(后者覆盖前者)
16
- source "$KIT_DIR/config.env"
17
- [ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
18
- APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
19
- CODE_DIR="${CODE_DIR:-./server}"
20
- FC_ENTRY="${FC_ENTRY:-dist/main.js}"
21
- BUILD_CMD="${BUILD_CMD:-}"
22
-
23
- # 2) 工具链就绪(检测环境,缺 aliyun CLI / Serverless Devs 则装,已装跳过)+ 阿里云凭证
24
- source "$KIT_DIR/scripts/ensure-toolchain.sh"
25
- ensure_toolchain
26
-
27
- # 阿里云凭证(现读)+ Serverless Devs 认证(必须含 AccountID,SK 走 heredoc 不进命令行)
28
- set -a; . "$PERSONA_DIR/.secrets/aliyun.env"; set +a
29
- # dataclaw 服务间密钥(现读,不进 git);缺失则拒发布(app 没它查不到数)。
30
- [ -f "$PERSONA_DIR/.secrets/dataclaw.env" ] || { echo "❌ 缺 $PERSONA_DIR/.secrets/dataclaw.env(含 DATACLAW_INTERNAL_SECRET)。参考 .secrets/dataclaw.env.example 填写。" >&2; exit 1; }
31
- set -a; . "$PERSONA_DIR/.secrets/dataclaw.env"; set +a
32
- # aliyun CLI 需要 region;新机器没跑过 `aliyun configure`(无 ~/.aliyun/config.json) → "region can't be empty"。
33
- # 用 env 给 region(REGION 来自 config.env):一处覆盖脚本里所有 aliyun 调用,不进 argv,和 AK/SK 走 env 的方式对称。
34
- export ALIBABA_CLOUD_REGION_ID="$REGION"
35
- # aliyun 报错不再吞进 2>/dev/null —— 否则凭证失效 / CLI 异常时只剩一条无头 python traceback(老板踩过)。
36
- _ident="$(aliyun sts GetCallerIdentity 2>&1)" || { echo "❌ aliyun sts GetCallerIdentity 失败(检查 .secrets/aliyun.env 凭证):" >&2; echo "$_ident" >&2; exit 1; }
37
- ACC="$(printf '%s' "$_ident" | python3 -c 'import sys,json;print(json.load(sys.stdin)["AccountId"])')" || { echo "❌ 解析 AccountId 失败,aliyun 输出非 JSON:" >&2; echo "$_ident" >&2; exit 1; }
38
- mkdir -p ~/.s
39
- cat > ~/.s/access.yaml <<EOF
40
- default:
41
- AccountID: '$ACC'
42
- AccessKeyID: $ALIBABA_CLOUD_ACCESS_KEY_ID
43
- AccessKeySecret: $ALIBABA_CLOUD_ACCESS_KEY_SECRET
44
- EOF
45
- chmod 600 ~/.s/access.yaml
46
-
47
- # 3) 构建产物(框架相关,由 ext.conf 的 BUILD_CMD 定义)
48
- # 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.4):
49
- # echo ::stage::build 在 if 外,BUILD_CMD 为空时也通知 daemon 进入 build 阶段(即使啥也没构建)。
50
- # daemon 解析 stdout 命中此 marker 后 broadcast appBuilder:publish-progress 帧推 UI checklist。
51
- echo "::stage::build"
52
- if [ -n "$BUILD_CMD" ]; then
53
- echo "==> build: $BUILD_CMD"
54
- ( cd "$EXT_DIR" && eval "$BUILD_CMD" )
55
- fi
56
-
57
- # 4) 放置框架无关的 bootstrap 到 code 目录
58
- cp "$KIT_DIR/contract/bootstrap" "$EXT_DIR/$CODE_DIR/bootstrap"
59
- chmod +x "$EXT_DIR/$CODE_DIR/bootstrap"
60
-
61
- # 4.5) 推导/生成 dataclaw + TTC 登录所需的运行时变量
62
- # SESSION_SECRET:每个 app 一份、跨重部署稳定(否则重发后所有人 session 失效)。
63
- # 首次发布生成并持久化到 ext.conf;之后 source ext.conf 已带上。
64
- if [ -z "${SESSION_SECRET:-}" ]; then
65
- SESSION_SECRET="$(openssl rand -hex 32)"
66
- echo "SESSION_SECRET=$SESSION_SECRET" >> "$EXT_DIR/ext.conf"
67
- fi
68
- # APP_BASE_URL:飞书登录回调用。fc3 自定义域名可由 APP_NAME+ACC+REGION 推导(见下方域名注释),
69
- # 用 https(cookie secure 要求 HTTPS)。
70
- APP_BASE_URL="https://${APP_NAME}.fcv3.${ACC}.${REGION}.fc.devsapp.net"
71
-
72
- # 5) 渲染 s.yaml(| 作分隔符,避免 URL/层 ARN 里的 / : 冲突)
73
- sed -e "s|__APP_NAME__|$APP_NAME|g" \
74
- -e "s|__REGION__|$REGION|g" \
75
- -e "s|__FC_RUNTIME__|$FC_RUNTIME|g" \
76
- -e "s|__NODE_LAYER__|$NODE_LAYER|g" \
77
- -e "s|__FC_CPU__|$FC_CPU|g" \
78
- -e "s|__FC_MEMORY__|$FC_MEMORY|g" \
79
- -e "s|__FC_TIMEOUT__|$FC_TIMEOUT|g" \
80
- -e "s|__CODE_DIR__|$CODE_DIR|g" \
81
- -e "s|__FC_ENTRY__|$FC_ENTRY|g" \
82
- -e "s|__DATACLAW_SERVICE_URL__|$DATACLAW_SERVICE_URL|g" \
83
- -e "s|__DATACLAW_AS_UNION_ID__|${DATACLAW_AS_UNION_ID:-}|g" \
84
- -e "s|__DATACLAW_INTERNAL_SECRET__|$DATACLAW_INTERNAL_SECRET|g" \
85
- -e "s|__TTC_AUTH_BASE__|$TTC_AUTH_BASE|g" \
86
- -e "s|__TTC_API_BASE__|$TTC_API_BASE|g" \
87
- -e "s|__APP_BASE_URL__|$APP_BASE_URL|g" \
88
- -e "s|__SESSION_SECRET__|$SESSION_SECRET|g" \
89
- "$KIT_DIR/contract/s.yaml.tmpl" > "$EXT_DIR/s.yaml"
90
-
91
- # 6) 部署
92
- echo "::stage::deploy"
93
- echo "==> s deploy ($APP_NAME)"
94
- ( cd "$EXT_DIR" && s deploy -y )
95
-
96
- # 7) 取自定义域名 + 验证
97
- echo "::stage::verify"
98
- DOM="$(aliyun fc GET /2023-03-30/custom-domains --region "$REGION" 2>/dev/null | python3 -c "
99
- import sys,json
100
- d=json.load(sys.stdin)
101
- m=[c['domainName'] for c in d.get('customDomains',[]) if '$APP_NAME' in c.get('domainName','')]
102
- print(m[0] if m else '')")"
103
-
104
- echo ""
105
- echo "=================================================="
106
- if [ -n "$DOM" ]; then
107
- # daemon runner 优先解析这个 marker 作为 prodUrl(避免抠到 s deploy 中间的 FC system_url
108
- # fcapp.run,阿里云对它强制 Content-Disposition: attachment,iframe / 浏览器直访都会变成下载)。
109
- # 必须用 custom domain(<app>.fcv3.<accountId>.cn-hangzhou.fc.devsapp.net)。
110
- echo "::prod-url::http://$DOM"
111
- echo " ✅ 发布完成: http://$DOM"
112
- echo "=================================================="
113
- # verify.sh 失败(非 200 / Content-Disposition)→ exit 非 0,由 set -e 让 publish.sh
114
- # 整体失败 → daemon runner 走 publish-failed 路径 + 回灌 chat 让 assistant 接管修复。
115
- # 之前的 `|| true` 兜底已去掉,理由:线上挂了 prodUrl 仍写盘 = 老板看到"打开线上"按钮
116
- # 但点开 404,不如 fail-loud。
117
- #
118
- # 显式走 bash 解释器调用(不依赖 verify.sh 自己的 +x 权限):
119
- # daemon 调 publish.sh / new-extension.sh 都是 `spawn('bash', [scriptPath, ...])`,所以即使脚本
120
- # +x bit 丢了(历史上有过:OTA 解包 / cp -p 未带 / desktop 旧包 mode 不对)也能跑。但这一行
121
- # 之前是 direct-exec `"$KIT_DIR/scripts/verify.sh"`,OS 会检查 +x —— verify.sh 因此成为唯一
122
- # 暴露 +x 丢失问题的执行点(老板撞到过一次"verify.sh permission denied")。
123
- # 改 bash 调用后永远不再依赖 fs +x bit。
124
- bash "$KIT_DIR/scripts/verify.sh" "http://$DOM"
125
- else
126
- # 域名没拿到 = 部署本身有问题(fc3-domain 未成功),也算失败
127
- echo "❌ 部署完成但未取到自定义域名,检查 fc3-domain 是否成功" >&2
128
- exit 1
129
- fi
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # 清理一个 extension:删 FC 资源(函数+触发器+域名)+ 可选删它的 Supabase 表
4
- # 用法: remove-extension.sh <extension目录>
5
- # 删哪些表:读 ext.conf 的 SUPABASE_TABLES(extension 自己声明的)
6
- # 安全:① 校验 <app>_ 前缀防误删 clawos ② 列出+二次确认 ③ 数据不可逆
7
- # ============================================================
8
- set -euo pipefail
9
-
10
- KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
11
- PERSONA_DIR="$(cd "$KIT_DIR/.." && pwd)"
12
-
13
- EXT_DIR="${1:?用法: remove-extension.sh <extension目录>}"
14
- EXT_DIR="$(cd "$EXT_DIR" && pwd)"
15
-
16
- source "$KIT_DIR/config.env"
17
- [ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
18
- APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
19
- SUPABASE_TABLES="${SUPABASE_TABLES:-}"
20
-
21
- # 确保工具链就绪(缺 s / aliyun 则装,已装跳过)—— 下面 s remove 依赖它
22
- source "$KIT_DIR/scripts/ensure-toolchain.sh"
23
- ensure_toolchain
24
-
25
- set -a; . "$PERSONA_DIR/.secrets/aliyun.env"; set +a
26
-
27
- # 1) 删 FC 侧(函数 + 触发器 + 自定义域名)
28
- echo "==> 删 FC 资源: $APP_NAME"
29
- ( cd "$EXT_DIR" && s remove -y )
30
-
31
- # 2) 列出要删的 Supabase 表(脚本不直接调 DB,改打印 DROP 让 agent 用 supabase MCP 跑)
32
- #
33
- # 历史 bug(2026-06-03 老板手测 + persona-app-builder 自查发现):旧实现走 python3 读
34
- # .mcp.json 取 SUPABASE_ACCESS_TOKEN + curl https://api.supabase.com/v1/... 删表,三个
35
- # 错叠在一起:
36
- # ① 当前 .mcp.json 用阿里云 RDS 风格(--aliyun-ak 命令行参数),没有 env.SUPABASE_ACCESS_TOKEN
37
- # 字段 → python3 KeyError → set -euo pipefail exit
38
- # ② https://api.supabase.com 是云上 supabase.com 的 management API,跟阿里云自建实例
39
- # 120.26.157.138 不通(即便通,删的也是云上早废弃的表)
40
- # ③ SUPABASE_PROJECT_REF 是云上 project-ref 概念,阿里云自建用 instance_name
41
- # 结果:第 1 步删 FC 一直对,第 2 步从来没真删过表(脚本提前 crash exit)。
42
- #
43
- # 修法:bash 调不到 MCP(MCP 是 LLM 通道),pg 直连要密码 agent 拿不到,PostgREST RPC 又
44
- # 要装 drop_table_safe + 手刷 schema cache。最干净:打印 DROP,让 agent 看到 Bash tool
45
- # stdout 后用 mcp__supabase__execute_sql 接力。长期方案是参照 publish.sh 模式做
46
- # appBuilder.delete RPC + UI 按钮,等下一轮做。
47
- if [ -n "$SUPABASE_TABLES" ]; then
48
- # 安全锁:每个表必须 <app>_ 前缀,否则拒绝(防误伤 clawos 共享库)
49
- for t in $SUPABASE_TABLES; do
50
- case "$t" in
51
- ${APP_NAME}_*) ;;
52
- *) echo "❌ 表 '$t' 不是 '${APP_NAME}_' 前缀,拒绝输出(可能误伤 clawos)"; exit 1 ;;
53
- esac
54
- done
55
-
56
- echo ""
57
- echo "==> ⚠️ 下面是要删的 Supabase 表,脚本不直接调 DB"
58
- echo " (阿里云自建无 management API + bash 调不到 MCP)"
59
- echo ""
60
- for t in $SUPABASE_TABLES; do
61
- echo " DROP TABLE IF EXISTS public.$t CASCADE;"
62
- done
63
- echo ""
64
- echo "==> agent: 上面这几条 DROP 请用 supabase MCP 跑(mcp__supabase__execute_sql),"
65
- echo " 完了项目清理才算完成"
66
- else
67
- echo "ext.conf 未声明 SUPABASE_TABLES,无表要清理。"
68
- fi
69
-
70
- echo "✅ $APP_NAME 清理完成。"
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 发布后健康检查:可达性 + Content-Type + 【是否被强制下载】
3
- # 用法: verify.sh <url>
4
- #
5
- # 失败语义(spec 2026-06-03-app-builder-publish-scaffold-design follow-up):
6
- # 首页非 200 / 检测到 Content-Disposition 强制下载 → exit 1。
7
- # publish.sh 不加 `|| true` 兜底,verify 失败会让 publish.sh 整体 exit 非 0,
8
- # 触发 daemon runner 的 publish-failed 路径:广播帧 + 回灌 chat user_text 让 assistant 接管修复。
9
- #
10
- # 信息原则:失败原因 echo 到 stderr(runner 的 errorSummary 抠 stderr 最后 20 行)。
11
- set -euo pipefail
12
- URL="${1:?用法: verify.sh <url>}"
13
-
14
- echo "== 健康检查: $URL =="
15
- code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$URL/" || echo 000)
16
- ctype=$(curl -s -o /dev/null -w "%{content_type}" --max-time 30 "$URL/" || echo "")
17
- echo "首页: HTTP $code ($ctype)"
18
-
19
- # 关键检查: 有 Content-Disposition 说明会被浏览器当文件下载(没绑自定义域名的典型症状)
20
- hdr=$(curl -s -D - -o /dev/null --max-time 30 "$URL/" || true)
21
- has_cd=0
22
- if echo "$hdr" | grep -qi 'content-disposition'; then
23
- has_cd=1
24
- echo "❌ 检测到 Content-Disposition —— 浏览器会下载而非渲染!确认走的是 fc3-domain 自定义域名,而不是 fcapp.run" >&2
25
- else
26
- echo "✓ 无 Content-Disposition,浏览器可正常渲染网页"
27
- fi
28
-
29
- if [ "$code" = "200" ] && [ "$has_cd" = "0" ]; then
30
- echo "✓ 全链路 OK"
31
- exit 0
32
- fi
33
-
34
- # 失败:原因写 stderr 供 runner 抠成 errorSummary,UI / assistant 都能看清楚
35
- if [ "$code" != "200" ]; then
36
- echo "❌ verify failed: 首页 HTTP $code (期望 200);查 s logs 找原因" >&2
37
- fi
38
- exit 1