@clawos-dev/clawd 0.2.619 → 0.2.621
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/app-builder-plugin/mcp-server.cjs +79 -582
- package/dist/cli.cjs +313 -1720
- package/dist/dispatch/mcp-server.cjs +79 -595
- package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/collect-files.mjs +6 -15
- package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/list-projects.mjs +1 -2
- package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/record-push.mjs +3 -5
- package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/render-sql.mjs +4 -6
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +1 -7
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +3 -9
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/polyfill.ts +3 -9
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/table-name.ts +2 -5
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/App.jsx +1 -8
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/ensure-credentials.sh +10 -47
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/ensure-toolchain.sh +3 -17
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/new-extension.sh +8 -21
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/publish.sh +14 -67
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/remove-extension.sh +6 -24
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/deploy-kit/scripts/verify.sh +3 -15
- package/dist/persona-defaults/persona-app-builder/mcp/clawd-app-builder/mcp-server.cjs +79 -582
- package/dist/persona-defaults/persona-clawd-butler/.claude/skills/clawd-session-import/scripts/link-sessions.mjs +7 -23
- package/dist/persona-defaults/persona-clawd-butler/.claude/skills/scan-sessions-suggest-personas/scripts/extract-signals.mjs +6 -14
- package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/collect-files.mjs +6 -15
- package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/list-projects.mjs +1 -2
- package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/record-push.mjs +3 -5
- package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/render-sql.mjs +4 -6
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +2 -6
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/server/src/dataclaw/dataclaw.client.ts +2 -4
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/server/src/dataclaw/dataclaw.controller.ts +1 -2
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/server/src/main.ts +4 -11
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/server/src/polyfill.ts +1 -5
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/web/src/api.js +1 -4
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/web/src/components/BarChart.jsx +0 -1
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/web/src/components/ResultCards.jsx +0 -1
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/web/src/components/ResultTable.jsx +0 -1
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/examples/nestjs-react/web/src/enums.js +2 -3
- package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/hooks/pre-deploy.sh +0 -2
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/ensure-credentials.sh +10 -47
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/ensure-toolchain.sh +3 -17
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/new-extension.sh +8 -21
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/publish.sh +14 -67
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/remove-extension.sh +6 -24
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/deploy-kit/scripts/verify.sh +3 -15
- package/dist/persona-defaults/persona-dataclaw-builder/mcp/clawd-app-builder/mcp-server.cjs +79 -582
- package/dist/pi-mcp-bridge/extension.cjs +0 -1
- package/package.json +1 -1
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# 用法: new-extension.sh <name> <模板源目录绝对路径> <目标项目目录绝对路径>
|
|
4
|
-
# - 模板源目录:persona 的 extension-kit/examples/<tmpl>(daemon 按 persona 解析后传入)
|
|
5
|
-
# - 目标目录:daemon createProject 已建好(含 .clawd-project.json)的 projects/<name>/
|
|
2
|
+
# Usage: new-extension.sh <name> <template-directory> <precreated-project-directory>
|
|
6
3
|
set -euo pipefail
|
|
7
4
|
|
|
8
5
|
NAME="${1:?用法: new-extension.sh <name> <模板源目录> <目标目录>}"
|
|
@@ -14,20 +11,15 @@ DEST="${3:?缺目标目录}"
|
|
|
14
11
|
echo "❌ 目标目录无效(必须已存在且含 .clawd-project.json): $DEST" >&2; exit 1
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
#
|
|
14
|
+
# Refuse to overwrite a project containing anything beyond the daemon-created marker.
|
|
18
15
|
remaining="$(find "$DEST" -mindepth 1 -maxdepth 1 ! -name '.clawd-project.json' -print -quit)"
|
|
19
16
|
[ -z "$remaining" ] || { echo "❌ 目标已有内容(非空目录): $DEST" >&2; exit 1; }
|
|
20
17
|
|
|
21
|
-
#
|
|
22
|
-
# (tslib 等传递依赖)。cp 到新项目后这些软链 dangling(仍指模板原始路径);而 pnpm install 见
|
|
23
|
-
# node_modules 已在 + lockfile 没变会直接 skip、不重建软链 → 运行/打包时 Cannot find module。
|
|
24
|
-
# 新项目本来就要 install,故 scaffold 一律不拷 node_modules(保留 lockfile 以锁版本)。
|
|
18
|
+
# Exclude node_modules: copied pnpm symlinks can retain template paths, and install may skip rebuilding them.
|
|
25
19
|
rsync -a --exclude='node_modules' "$SRC/" "$DEST/"
|
|
26
20
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# publish.sh 和 remove-extension.sh 都 source ext.conf 共用同一个 slug。
|
|
30
|
-
# Agent 命名 Supabase 表时务必带上: <APP_NAME>_<SLUG>_<table>(见 CLAUDE.md「后端:supabase」)。
|
|
21
|
+
# Persist a random slug to reduce shared FC/DNS/table name collisions; publish and removal reuse it.
|
|
22
|
+
# Table names must include APP_NAME and SLUG to isolate this extension.
|
|
31
23
|
SLUG="$(openssl rand -hex 2)"
|
|
32
24
|
{
|
|
33
25
|
echo ""
|
|
@@ -39,11 +31,7 @@ SLUG="$(openssl rand -hex 2)"
|
|
|
39
31
|
echo "SLUG=${SLUG}"
|
|
40
32
|
} >> "$DEST/ext.conf"
|
|
41
33
|
|
|
42
|
-
#
|
|
43
|
-
# 这里用**本进程 env**(daemon spawn 时从凭据表注进来的,见 daemon/src/deploy/secret-table.ts)
|
|
44
|
-
# 展开成同目录的 .env,新项目 pnpm dev 就能直接跑。
|
|
45
|
-
# 通用实现(persona 无关):扫到哪个 ${VAR} 就取哪个同名变量,没有的渲成空串 + 提示,不 fail
|
|
46
|
-
# ——scaffold 不该因为一把 key 没配就整个失败,缺什么在 .env 里一眼看得出来。
|
|
34
|
+
# Render examples from process env, preserving existing files; missing variables warn and become empty.
|
|
47
35
|
render_env_example() {
|
|
48
36
|
local src="$1" dst="${1%.example}"
|
|
49
37
|
local sed_args=() ph var val
|
|
@@ -56,14 +44,13 @@ render_env_example() {
|
|
|
56
44
|
echo " ⚠️ $(basename "$src") 引用了 \$$var,但本次 scaffold 的环境里没有 → 渲成空串" >&2
|
|
57
45
|
fi
|
|
58
46
|
val="${val//\\/\\\\}"; val="${val//|/\\|}"; val="${val//&/\\&}"
|
|
59
|
-
#
|
|
47
|
+
# BRE braces are literal; [$] avoids treating the placeholder as a regex anchor.
|
|
60
48
|
sed_args+=(-e "s|[\$]{$var}|$val|g")
|
|
61
49
|
done
|
|
62
50
|
if [ ${#sed_args[@]} -eq 0 ]; then cp "$src" "$dst"; else sed "${sed_args[@]}" "$src" > "$dst"; fi
|
|
63
51
|
echo " 📄 渲染 ${dst#$DEST/}(值来自本次进程 env,不写回模板)"
|
|
64
52
|
}
|
|
65
|
-
#
|
|
66
|
-
# 这两个值在渲 .env 之前就地定死,不从进程 env 取——scaffold 的环境里就算有同名变量也不该漏进来。
|
|
53
|
+
# Local development always uses integration names, overriding inherited environment values.
|
|
67
54
|
export APP_ENV=integration
|
|
68
55
|
export TABLE_PREFIX="${NAME}_${SLUG}_int_"
|
|
69
56
|
while IFS= read -r f; do render_env_example "$f"; done < <(find "$DEST" -name '*.env.example' -not -path '*/node_modules/*')
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# 通用一键发布 extension 到阿里云 FC(persona 无关)
|
|
4
|
-
# 用法: publish.sh <extension目录> <persona根目录> <env>
|
|
5
|
-
# - <persona根目录>: daemon 按 session persona 解析后传入(.../personas/<id>)
|
|
6
|
-
# - <env>: integration | prod(必填,无默认)。integration 发到 <APP_NAME>-<SLUG>-int(测试地址),
|
|
7
|
-
# prod 发到 <APP_NAME>-<SLUG>(老地址不变)。见 spec 2026-09-15-app-builder-integration-env-design
|
|
8
|
-
# - 脚本骨架/bootstrap 来自本脚本所在的 deploy-kit(persona 目录 mcp/clawd-app-builder/ 下);平台凭证在进程 env
|
|
9
|
-
# - persona 注入值/占位符/特化逻辑/persona 密钥来自 <persona根>/extension-kit
|
|
10
|
-
# ============================================================
|
|
2
|
+
# Usage: publish.sh <extension-directory> <persona-root> <integration|prod>
|
|
11
3
|
set -euo pipefail
|
|
12
4
|
|
|
13
5
|
KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)" # deploy-kit 根(persona 目录 mcp/clawd-app-builder/deploy-kit/)
|
|
@@ -16,10 +8,9 @@ EXT_DIR="$(cd "$EXT_DIR" && pwd)"
|
|
|
16
8
|
PERSONA_ROOT="${2:?缺 persona 根目录}"
|
|
17
9
|
PERSONA_KIT="$PERSONA_ROOT/extension-kit"
|
|
18
10
|
|
|
19
|
-
# 1) 配置:persona config.env(含平台值+注入值)+ extension 自身 ext.conf(覆盖)
|
|
20
11
|
[ -f "$PERSONA_KIT/config.env" ] && source "$PERSONA_KIT/config.env"
|
|
21
12
|
[ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
|
|
22
|
-
# env
|
|
13
|
+
# Set env after sourcing config: only the positional argument chooses the deployment environment.
|
|
23
14
|
APP_ENV="${3:?缺 env(integration|prod)}"
|
|
24
15
|
case "$APP_ENV" in integration|prod) ;; *) echo "❌ env 只能是 integration|prod,收到:$APP_ENV" >&2; exit 1;; esac
|
|
25
16
|
APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
|
|
@@ -27,81 +18,54 @@ CODE_DIR="${CODE_DIR:-./server}"
|
|
|
27
18
|
FC_ENTRY="${FC_ENTRY:-dist/main.js}"
|
|
28
19
|
BUILD_CMD="${BUILD_CMD:-}"
|
|
29
20
|
|
|
30
|
-
#
|
|
31
|
-
# 由 new-extension.sh 锁定 + bake 进 ext.conf。这里的 fallback 兜底两种历史情况:
|
|
32
|
-
# 1) 老项目 (本改动前 scaffold) ext.conf 无 SLUG → 现场生成 + bake,保后续稳定
|
|
33
|
-
# 2) 手动 rm SLUG 或损坏 ext.conf → 同上,fail-safe
|
|
34
|
-
# 派生 (按 env):
|
|
35
|
-
# DEPLOY_NAME=${APP_NAME}-${SLUG}[-int] → FC functionName/子域名/s 项目名 (DNS-safe 连字符)
|
|
36
|
-
# TABLE_PREFIX=${APP_NAME}_${SLUG}_[int_] → Supabase 表 (SQL-safe 下划线,见 remove-extension.sh)
|
|
21
|
+
# Persist a missing slug before deriving stable deployment and table names.
|
|
37
22
|
if [ -z "${SLUG:-}" ]; then
|
|
38
23
|
SLUG="$(openssl rand -hex 2)"
|
|
39
24
|
printf '\nSLUG=%s\n' "$SLUG" >> "$EXT_DIR/ext.conf"
|
|
40
25
|
echo " 📝 锁定 SLUG=$SLUG (含 4 位散列防同名冲突,已 bake 进 ext.conf)"
|
|
41
26
|
fi
|
|
42
27
|
BASE_NAME="${APP_NAME}-${SLUG}"
|
|
43
|
-
#
|
|
44
|
-
# 一眼看域名:带 -int 的就是测试环境。派生段与 publish.spec.ts 的 DERIVE_SNIPPET 同步。
|
|
28
|
+
# Keep resource-name derivation aligned with publish.spec.ts DERIVE_SNIPPET.
|
|
45
29
|
if [ "$APP_ENV" = integration ]; then
|
|
46
30
|
ENV_TAG=int; DEPLOY_NAME="${BASE_NAME}-int"; TABLE_PREFIX="${APP_NAME}_${SLUG}_int_"
|
|
47
31
|
else
|
|
48
32
|
ENV_TAG=prod; DEPLOY_NAME="${BASE_NAME}"; TABLE_PREFIX="${APP_NAME}_${SLUG}_"
|
|
49
33
|
fi
|
|
50
|
-
# 两个环境各自的渲染产物;s 用 name: 键状态,DEPLOY_NAME 不同即隔离。
|
|
51
34
|
S_FILE="s.${ENV_TAG}.yaml"
|
|
52
35
|
echo " 🎯 env=$APP_ENV → $DEPLOY_NAME (表前缀 $TABLE_PREFIX, 渲到 $S_FILE)"
|
|
53
|
-
#
|
|
54
|
-
# 存量项目只有 s.yaml 一行,s.int.yaml 会被当源码推上 GitHub——像 bake SLUG 一样幂等地补一行(改了
|
|
55
|
-
# .gitignore 下次 publish 会被「先推再发」拦一次,推上去的只是 .gitignore,无害)。
|
|
36
|
+
# Generated YAML contains secrets; add the wildcard ignore rule for older projects too.
|
|
56
37
|
if ! grep -qxF 's.*.yaml' "$EXT_DIR/.gitignore" 2>/dev/null; then
|
|
57
38
|
printf 's.*.yaml\n' >> "$EXT_DIR/.gitignore"
|
|
58
39
|
echo " 📝 .gitignore 补 s.*.yaml (渲出的 s.int.yaml / s.prod.yaml 含 key,不进仓库;记得把 .gitignore 推上去)"
|
|
59
40
|
fi
|
|
60
41
|
|
|
61
|
-
# 2) 工具链 + 阿里云凭证(进程 env,本机云上同一条;见 ensure-credentials.sh)
|
|
62
42
|
source "$KIT_DIR/scripts/ensure-toolchain.sh"
|
|
63
43
|
ensure_toolchain
|
|
64
44
|
source "$KIT_DIR/scripts/ensure-credentials.sh"
|
|
65
45
|
ensure_credentials
|
|
66
46
|
|
|
67
|
-
# 3) persona hook(可选):校验/导出 persona 特化运行时变量(如 SESSION_SECRET / APP_BASE_URL / persona 密钥)
|
|
68
47
|
if [ -f "$PERSONA_KIT/hooks/pre-deploy.sh" ]; then
|
|
69
48
|
source "$PERSONA_KIT/hooks/pre-deploy.sh"
|
|
70
49
|
fi
|
|
71
50
|
|
|
72
|
-
# 4) build(框架相关)
|
|
73
51
|
echo "::stage::build"
|
|
74
52
|
if [ -n "$BUILD_CMD" ]; then
|
|
75
53
|
echo "==> build: $BUILD_CMD"
|
|
76
54
|
( cd "$EXT_DIR" && eval "$BUILD_CMD" )
|
|
77
55
|
fi
|
|
78
56
|
|
|
79
|
-
# 5) bootstrap(共享,框架无关)
|
|
80
57
|
cp "$KIT_DIR/contract/bootstrap" "$EXT_DIR/$CODE_DIR/bootstrap"
|
|
81
58
|
chmod +x "$EXT_DIR/$CODE_DIR/bootstrap"
|
|
82
59
|
|
|
83
|
-
# 6) 通用占位符替换渲染 s.yaml:扫 persona s.yaml.tmpl 的所有 __X__,从 env 取同名变量
|
|
84
|
-
#(APP_ENV / TABLE_PREFIX 也走这条:模板里 __APP_ENV__ / __TABLE_PREFIX__ 渲进 FC 的 environmentVariables)
|
|
85
|
-
#
|
|
86
|
-
# 自定义域名段按履约拼接(M1b Step 5c):APP_DOMAIN_SUFFIX 非空 = 部署级履约(域名与通配符
|
|
87
|
-
# 证书都在我们自己的阿里云账号上)→ 把 s.yaml.domain.tmpl 接上;空 = 使用者自带 key,
|
|
88
|
-
# 发布落在他自己的账号里,那里既没有我们的 DNS 也没有我们的证书 → 整段不渲染,用 FC 默认域名。
|
|
89
|
-
# 拼完再走同一套占位符替换,渲染契约本身不分叉。
|
|
90
60
|
TMPL="$PERSONA_KIT/contract/s.yaml.tmpl"
|
|
91
61
|
[ -f "$TMPL" ] || { echo "❌ 缺 $TMPL" >&2; exit 1; }
|
|
92
|
-
#
|
|
93
|
-
# 没有那个文件的 persona(persona-dataclaw-builder)把自定义域名段写死在自己的 s.yaml.tmpl 里,
|
|
94
|
-
# 它压根不参与这套切换,替它选分支只会把它的发布路径改坏。
|
|
62
|
+
# Only personas with a separate domain template participate in domain selection; others keep their own template policy.
|
|
95
63
|
DOMAIN_TMPL="$PERSONA_KIT/contract/s.yaml.domain.tmpl"
|
|
96
64
|
TMPL_RENDER="$TMPL"
|
|
97
65
|
TMPL_COMBINED=""
|
|
98
66
|
if [ -f "$DOMAIN_TMPL" ]; then
|
|
99
67
|
if [ -n "${APP_DOMAIN_SUFFIX:-}" ]; then
|
|
100
|
-
#
|
|
101
|
-
# **必须写全 XXXXXX 模板**:BSD mktemp(macOS)的 `-t prefix` 会自己补随机后缀,
|
|
102
|
-
# GNU coreutils(Linux,= M1b 的 Pod)要求模板以 ≥3 个 X 结尾,`mktemp -t s-yaml-tmpl`
|
|
103
|
-
# 直接 `too few X's in template` 退 1(coreutils 9.1 实测),`set -e` 下当场打死这条
|
|
104
|
-
# 默认路径。deploy-kit 的脚本两套 userland 都要跑,别用 BSD/GNU 语义有分歧的写法。
|
|
68
|
+
# Keep temporary templates outside the project and use XXXXXX for both BSD and GNU mktemp.
|
|
105
69
|
TMPL_COMBINED="$(mktemp "${TMPDIR:-/tmp}/s-yaml-tmpl.XXXXXX")"
|
|
106
70
|
trap 'rm -f "$TMPL_COMBINED"' EXIT
|
|
107
71
|
cat "$TMPL" "$DOMAIN_TMPL" > "$TMPL_COMBINED"
|
|
@@ -114,19 +78,16 @@ fi
|
|
|
114
78
|
sed_args=()
|
|
115
79
|
for ph in $(grep -oE '__[A-Z0-9_]+__' "$TMPL_RENDER" | sort -u); do
|
|
116
80
|
var="${ph#__}"; var="${var%__}"
|
|
117
|
-
# declare -p
|
|
81
|
+
# declare -p distinguishes missing from empty variables on Bash 3.2.
|
|
118
82
|
if ! declare -p "$var" >/dev/null 2>&1; then
|
|
119
83
|
echo "❌ s.yaml.tmpl 占位符 $ph 无对应 env 变量 \$$var" >&2
|
|
120
|
-
#
|
|
121
|
-
# 它起的那个子进程,别的地方起的 shell 拿不到。把这条写进报错里,是因为读它的下一个
|
|
122
|
-
# 读者往往是 agent——不给方向它就会去翻凭据、手动 export 或写回 config.env,
|
|
123
|
-
# 正好把「值不落 persona 目录」这条纪律拆掉。
|
|
84
|
+
# Missing injected credentials must be fixed through the daemon credential table, never written into templates.
|
|
124
85
|
echo " 如果你是手动跑这个脚本:凭据只注给 daemon 起的那个进程,手动跑必然缺。" >&2
|
|
125
86
|
echo " 正确做法是改调 clawd-app-builder 的 publish tool 重跑;**不要**去找 key 手动 export,也不要写回 config.env。" >&2
|
|
126
87
|
echo " 确实是漏配(新增了占位符 / 换了实例):改 daemon 的凭据表(~/.clawd/secrets/),不是改模板。" >&2
|
|
127
88
|
exit 1
|
|
128
89
|
fi
|
|
129
|
-
#
|
|
90
|
+
# Escape backslashes before the other sed replacement characters.
|
|
130
91
|
val="${!var}"
|
|
131
92
|
val="${val//\\/\\\\}"
|
|
132
93
|
val="${val//|/\\|}"
|
|
@@ -135,24 +96,13 @@ for ph in $(grep -oE '__[A-Z0-9_]+__' "$TMPL_RENDER" | sort -u); do
|
|
|
135
96
|
done
|
|
136
97
|
sed "${sed_args[@]}" "$TMPL_RENDER" > "$EXT_DIR/$S_FILE"
|
|
137
98
|
|
|
138
|
-
# 7) deploy + verify
|
|
139
99
|
echo "::stage::deploy"
|
|
140
100
|
echo "==> s deploy ($DEPLOY_NAME, env=$APP_ENV)"
|
|
141
101
|
( cd "$EXT_DIR" && s deploy -y -t "$S_FILE" --access "$S_ACCESS_ALIAS" )
|
|
142
102
|
|
|
143
103
|
echo "::stage::verify"
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
# 有 s.yaml.domain.tmpl(app-builder):
|
|
147
|
-
# APP_DOMAIN_SUFFIX 非空 → 自定义域名,从 custom-domains 精确匹配
|
|
148
|
-
# APP_DOMAIN_SUFFIX 为空 → 使用者自带 key,FC 默认域名,从 httpTrigger 取
|
|
149
|
-
# 没有(persona-dataclaw-builder 等):自定义域名段写死在它自己的 s.yaml.tmpl 里,
|
|
150
|
-
# **原样走老路**——它的 functionName 规则、域名策略都跟这套无关,别替它选分支。
|
|
151
|
-
#
|
|
152
|
-
# JSON 解析用 node 不用 python3:目标机器保证有 node(clawd 本身就是 node 跑的),不保证有
|
|
153
|
-
# python。而且原先 `2>/dev/null | python3 -c` 把 CLI 的 stderr 吞了再让 python 抛
|
|
154
|
-
# JSONDecodeError,`set -e` 下脚本直接崩在 traceback 上,真凶(凭证不对 / 超时)全丢——
|
|
155
|
-
# 下面的 fc_get 保留 CLI 原文并 fail-loud。
|
|
104
|
+
# URL discovery must use the same domain-template predicate as rendering.
|
|
105
|
+
# Preserve CLI stderr before parsing JSON with the guaranteed Node runtime.
|
|
156
106
|
fc_get() { # fc_get <path>:调 FC OpenAPI;失败把 CLI 原文写 stderr 后 return 1
|
|
157
107
|
local out rc
|
|
158
108
|
out="$(aliyun_ak fc GET "$1" 2>&1)" && rc=0 || rc=$?
|
|
@@ -174,16 +124,13 @@ console.log((d.customDomains || []).some((c) => c.domainName === target) ? targe
|
|
|
174
124
|
PROD_URL=""
|
|
175
125
|
ALLOW_CD=""
|
|
176
126
|
if [ -f "$DOMAIN_TMPL" ] && [ -z "${APP_DOMAIN_SUFFIX:-}" ]; then
|
|
177
|
-
#
|
|
178
|
-
# 为 httpTrigger(写死是确定的,不是猜的)。
|
|
127
|
+
# The template contract fixes the trigger name to httpTrigger.
|
|
179
128
|
PROD_URL="$(fc_get "/2023-03-30/functions/${DEPLOY_NAME}/triggers/httpTrigger" | node -e '
|
|
180
129
|
const d = JSON.parse(require("fs").readFileSync(0, "utf8"));
|
|
181
130
|
console.log((d.httpTrigger && d.httpTrigger.urlInternet) || "");
|
|
182
131
|
')"
|
|
183
132
|
MISS_HINT="未取到 FC 默认域名(函数 ${DEPLOY_NAME} 的 httpTrigger urlInternet 为空),检查 s deploy 是否真的建出了 httpTrigger"
|
|
184
|
-
# FC
|
|
185
|
-
# 自定义域名就消不掉。使用者自带 key 履约的验收口径本来就是「公网可访问即可」
|
|
186
|
-
# (design ③:per-user 部署用 FC 默认域名,不要求品牌域名),所以降级成警告不判失败。
|
|
133
|
+
# FC default-domain Content-Disposition is tolerated only on the no-custom-domain path.
|
|
187
134
|
ALLOW_CD="--allow-content-disposition"
|
|
188
135
|
else
|
|
189
136
|
EXPECTED_DOM="${DEPLOY_NAME}.${APP_DOMAIN_SUFFIX:-app.clawos.chat}"
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# 删哪些表:读 ext.conf 的 SUPABASE_TABLES(extension 自己声明的**基础名**,如 visits;非 supabase persona 留空即跳过)。
|
|
6
|
-
# 每个基础名打两套 DROP:<APP_NAME>_<SLUG>_int_<t>(integration)与 <APP_NAME>_<SLUG>_<t>(prod)。
|
|
7
|
-
# 值若已带完整前缀 <APP_NAME>_<SLUG>_ 视为双环境之前声明的完整表名,只打一条。
|
|
8
|
-
# 安全:① 校验 <APP_NAME>_<SLUG>_ 前缀防误删 clawos / 同名 app 别人副本 ② 列出 DROP 让 agent 用 supabase MCP 接力 ③ 数据不可逆
|
|
9
|
-
# ============================================================
|
|
2
|
+
# Remove published FC resources and print optional database DROP statements for the caller to execute.
|
|
3
|
+
# Legacy fully prefixed table declarations expand once; base names expand for integration and production.
|
|
4
|
+
# Deletion is irreversible: restrict emitted table names to this extension prefix.
|
|
10
5
|
set -euo pipefail
|
|
11
6
|
|
|
12
7
|
KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)" # deploy-kit 根(persona 目录 mcp/clawd-app-builder/deploy-kit/)
|
|
@@ -19,27 +14,20 @@ PERSONA_KIT="$PERSONA_ROOT/extension-kit"
|
|
|
19
14
|
[ -f "$EXT_DIR/ext.conf" ] && source "$EXT_DIR/ext.conf"
|
|
20
15
|
APP_NAME="${APP_NAME:-$(basename "$EXT_DIR")}"
|
|
21
16
|
SUPABASE_TABLES="${SUPABASE_TABLES:-}"
|
|
22
|
-
# SLUG
|
|
23
|
-
# prod 表名形如 ${APP_NAME}_${SLUG}_<table>,integration 多一段 int_;老项目走旧的 ${APP_NAME}_<table>。
|
|
17
|
+
# Missing SLUG retains the legacy project prefix.
|
|
24
18
|
TABLE_PREFIX="${APP_NAME}_${SLUG:+${SLUG}_}"
|
|
25
19
|
TABLE_PREFIX_INT="${TABLE_PREFIX}int_"
|
|
26
20
|
|
|
27
|
-
# 确保工具链就绪(缺 s / aliyun 则装,已装跳过)—— 下面 s remove 依赖它
|
|
28
21
|
source "$KIT_DIR/scripts/ensure-toolchain.sh"
|
|
29
22
|
ensure_toolchain
|
|
30
23
|
|
|
31
|
-
# 阿里云凭证(进程 env:本机 daemon 从凭据表渲进 MCP server env,云上 pod-setup 渲)+ deploy-kit 私有的 s profile
|
|
32
24
|
source "$KIT_DIR/scripts/ensure-credentials.sh"
|
|
33
25
|
ensure_credentials
|
|
34
26
|
|
|
35
|
-
# 1) 删 FC 侧(函数 + 触发器 + 自定义域名):integration / prod 各自有一份 publish.sh 渲的 s 文件
|
|
36
|
-
#(s.int.yaml / s.prod.yaml;双环境之前渲的叫 s.yaml),有哪份删哪份——从未发过的环境没有产物,跳过。
|
|
37
27
|
removed=0
|
|
38
28
|
for s_file in s.int.yaml s.prod.yaml s.yaml; do
|
|
39
29
|
[ -f "$EXT_DIR/$s_file" ] || continue
|
|
40
|
-
#
|
|
41
|
-
# 那就只按 s.prod.yaml 拆,对同一资源 s remove 两次第二次会因「不存在」失败。
|
|
42
|
-
# name: 不同(dataclaw 双环境之前 functionName 不带 slug)= 两个函数,两份都拆。
|
|
30
|
+
# Avoid removing the same legacy/prod resource twice; different template names still require both removals.
|
|
43
31
|
if [ "$s_file" = s.yaml ] && [ -f "$EXT_DIR/s.prod.yaml" ] \
|
|
44
32
|
&& [ "$(grep -m1 '^name:' "$EXT_DIR/s.yaml")" = "$(grep -m1 '^name:' "$EXT_DIR/s.prod.yaml")" ]; then
|
|
45
33
|
continue
|
|
@@ -50,14 +38,8 @@ for s_file in s.int.yaml s.prod.yaml s.yaml; do
|
|
|
50
38
|
done
|
|
51
39
|
[ "$removed" = 1 ] || echo "==> 没有渲染过的 s 文件(从未发布过),FC 侧无资源可删"
|
|
52
40
|
|
|
53
|
-
# 2) 列出要删的 Supabase 表(脚本不直接调 DB,改打印 DROP 让 agent 用 supabase MCP 跑)。
|
|
54
|
-
# bash 调不到 MCP(MCP 是 LLM 通道),pg 直连要密码 agent 拿不到 —— 最干净:打印 DROP,
|
|
55
|
-
# 让 agent 看到 Bash tool stdout 后用 mcp__supabase__execute_sql 接力。
|
|
56
41
|
if [ -n "$SUPABASE_TABLES" ]; then
|
|
57
|
-
#
|
|
58
|
-
#(判据用完整前缀而不是 ${APP_NAME}_:基础名本身以 <APP_NAME>_ 开头——shop 的 shop_items——是合法的)。
|
|
59
|
-
# 安全锁:展开后每个表必须 ${TABLE_PREFIX} 前缀(int 那套自然也满足),否则拒绝
|
|
60
|
-
#(防误伤 clawos / 同名 app 别人副本的表)。
|
|
42
|
+
# Match the complete extension prefix, not APP_NAME alone: base names can legitimately start with APP_NAME.
|
|
61
43
|
FULL_TABLES=""
|
|
62
44
|
for t in $SUPABASE_TABLES; do
|
|
63
45
|
case "$t" in
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# 失败语义(spec 2026-06-03-app-builder-publish-scaffold-design follow-up):
|
|
6
|
-
# 首页非 200 → exit 1。
|
|
7
|
-
# 检测到 Content-Disposition 强制下载 → 默认 exit 1;带 --allow-content-disposition 时
|
|
8
|
-
# 只警告不失败 —— FC 默认域名(*.fcapp.run)对所有响应强制注入这个头,没有自定义域名就
|
|
9
|
-
# 消不掉,而「使用者自带 key」履约的验收口径本来就只到「公网可访问」(design ③)。
|
|
10
|
-
# 调用方只在那条路径上传这个 flag,别当通用开关用。
|
|
11
|
-
# publish.sh 不加 `|| true` 兜底,verify 失败会让 publish.sh 整体 exit 非 0,
|
|
12
|
-
# 触发 daemon runner 的 publish-failed 路径:广播帧 + 回灌 chat user_text 让 assistant 接管修复。
|
|
13
|
-
#
|
|
14
|
-
# 信息原则:失败原因 echo 到 stderr(runner 的 errorSummary 抠 stderr 最后 20 行)。
|
|
2
|
+
# Usage: verify.sh <url> [--allow-content-disposition]
|
|
3
|
+
# Fail non-200 responses or Content-Disposition, except the explicit FC default-domain allowance.
|
|
4
|
+
# Write failure reasons to stderr for the daemon error summary.
|
|
15
5
|
set -euo pipefail
|
|
16
6
|
URL="${1:?用法: verify.sh <url> [--allow-content-disposition]}"
|
|
17
7
|
ALLOW_CD=0
|
|
@@ -22,7 +12,6 @@ code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 "$URL/" || echo 000)
|
|
|
22
12
|
ctype=$(curl -s -o /dev/null -w "%{content_type}" --max-time 30 "$URL/" || echo "")
|
|
23
13
|
echo "首页: HTTP $code ($ctype)"
|
|
24
14
|
|
|
25
|
-
# 关键检查: 有 Content-Disposition 说明会被浏览器当文件下载(没绑自定义域名的典型症状)
|
|
26
15
|
hdr=$(curl -s -D - -o /dev/null --max-time 30 "$URL/" || true)
|
|
27
16
|
has_cd=0
|
|
28
17
|
if echo "$hdr" | grep -qi 'content-disposition'; then
|
|
@@ -42,7 +31,6 @@ if [ "$code" = "200" ] && { [ "$has_cd" = "0" ] || [ "$ALLOW_CD" = "1" ]; }; the
|
|
|
42
31
|
exit 0
|
|
43
32
|
fi
|
|
44
33
|
|
|
45
|
-
# 失败:原因写 stderr 供 runner 抠成 errorSummary,UI / assistant 都能看清楚
|
|
46
34
|
if [ "$code" != "200" ]; then
|
|
47
35
|
echo "❌ verify failed: 首页 HTTP $code (期望 200);查 s logs 找原因" >&2
|
|
48
36
|
fi
|