@clawos-dev/clawd 0.2.451 → 0.2.453

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/cli.cjs CHANGED
@@ -8804,7 +8804,7 @@ var init_methods = __esm({
8804
8804
  args: WorkspaceReadArgs
8805
8805
  },
8806
8806
  "deploy:start": {
8807
- summary: "\u6253\u5F00\u67D0\u4E2A persona \u7684\u90E8\u7F72\u5355\uFF1A**\u5DF2\u6709\u5C31\u539F\u6837\u8FD4\u56DE\u90A3\u5F20**\uFF08\u8349\u7A3F\u7EED\u586B\uFF0C\u4E0D\u91CD\u626B\uFF0C\u4E5F\u4E0D\u65B0\u5EFA\uFF09\uFF0C\u6CA1\u6709\u624D\u5EFA\u4E00\u5F20\u5E76**\u5F02\u6B65**\u8D77\u73AF\u5883\u626B\u63CF\u3002\u4E00\u4E2A persona \u540C\u65F6\u53EA\u6709\u4E00\u5F20\u5355\uFF0C\u8981\u91CD\u626B\u8D70 deploy:rescan\uFF1Bowner-only",
8807
+ summary: "\u5BF9\u67D0\u4E2A persona \u8D77\u4E00\u6B21**\u65B0\u7684**\u4E0A\u4E91\u626B\u63CF\uFF1A\u5EFA\u4E00\u5F20\u65B0\u5355\u636E\u5E76\u5F02\u6B65\u626B\uFF08\u4EBA\u4F1A\u70B9\u8FD9\u4E2A\u53EA\u53EF\u80FD\u662F\u56E0\u4E3A persona \u8FED\u4EE3\u4E86\uFF0C\u62FF\u65E7\u6E05\u5355\u53BB\u90E8\u7F72\u662F\u9519\u7684\uFF09\u3002**\u5DF2\u586B\u7684\u51ED\u636E\u503C\u548C\u5C65\u7EA6\u9009\u62E9\u4ECE\u4E0A\u4E00\u5F20\u5355\u7EE7\u627F\u8FC7\u6765**\uFF0C\u4E0D\u7528\u6BCF\u8FED\u4EE3\u4E00\u6B21\u91CD\u8D34\u4E00\u904D key\u3002\u5DF2\u7ECF\u5728\u626B\u7684\u8BDD\u8FD4\u56DE\u90A3\u5F20\u3001\u4E0D\u5E76\u53D1\u8D77\u7B2C\u4E8C\u4E2A\u626B\u63CF\u4F1A\u8BDD\u3002\u56DE\u770B\u65E7\u7684\u90A3\u6B21\u8D70 deploy:list + deploy:get\uFF1Bowner-only",
8808
8808
  args: DeployStartArgsSchema
8809
8809
  },
8810
8810
  "deploy:rescan": {
@@ -54423,13 +54423,20 @@ function buildDeployHandlers(deps) {
54423
54423
  if (!deps.hasPersona(args.personaId)) {
54424
54424
  throw new ClawdError(ERROR_CODES.PERSONA_NOT_FOUND, `PERSONA_NOT_FOUND: \u6CA1\u6709\u8FD9\u4E2A persona\uFF08${args.personaId}\uFF09`);
54425
54425
  }
54426
- const existing = deps.receipts.list().find((r) => r.personaId === args.personaId);
54427
- if (existing) return view(existing);
54426
+ const inFlight = deps.receipts.list().find((r) => r.personaId === args.personaId && r.status === "scanning");
54427
+ if (inFlight) return view(inFlight);
54428
+ const prev = deps.receipts.list().find((r) => r.personaId === args.personaId);
54428
54429
  const receipt = deps.receipts.create({
54429
54430
  deployId: deps.genId(),
54430
54431
  personaId: args.personaId,
54431
54432
  status: "scanning"
54432
54433
  });
54434
+ if (prev) {
54435
+ deps.secrets.copyDeployment(prev.deployId, receipt.deployId);
54436
+ if (Object.keys(prev.fulfillment).length > 0) {
54437
+ deps.receipts.update(receipt.deployId, { fulfillment: prev.fulfillment });
54438
+ }
54439
+ }
54433
54440
  const scanSessionId = deps.startScan(receipt.deployId, args.personaId);
54434
54441
  if (scanSessionId) deps.receipts.update(receipt.deployId, { scanSessionId });
54435
54442
  return view(deps.receipts.get(receipt.deployId) ?? receipt);
@@ -54704,6 +54711,18 @@ function createSecretStore(deps) {
54704
54711
  }
54705
54712
  scheduleFlush();
54706
54713
  },
54714
+ copyDeployment(fromDeployId, toDeployId) {
54715
+ const src = entries.filter((e) => e.deployId === fromDeployId);
54716
+ let copied = 0;
54717
+ for (const e of src) {
54718
+ const exists = entries.some((x) => sameScope(x, toDeployId, e.name, e.userId));
54719
+ if (exists) continue;
54720
+ entries.push({ ...e, deployId: toDeployId, updatedAt: deps.now() });
54721
+ copied++;
54722
+ }
54723
+ if (copied > 0) scheduleFlush();
54724
+ return copied;
54725
+ },
54707
54726
  resolve(deployId, userId) {
54708
54727
  const out = {};
54709
54728
  for (const e of entries) {
@@ -63670,7 +63689,7 @@ function computeMethodAccess(args) {
63670
63689
  }
63671
63690
 
63672
63691
  // src/version.ts
63673
- var version = "0.2.451".length > 0 ? "0.2.451" : "dev";
63692
+ var version = "0.2.453".length > 0 ? "0.2.453" : "dev";
63674
63693
 
63675
63694
  // src/cli-probe/probe.ts
63676
63695
  var fs68 = __toESM(require("fs"), 1);
@@ -34,6 +34,25 @@ ensure_aliyun_env() {
34
34
  export ALIBABA_CLOUD_REGION_ID="${REGION:?config.env 缺 REGION}"
35
35
  }
36
36
 
37
+ # 调 aliyun CLI 的**唯一入口**:把凭证和 region 作为显式参数交出去,不依赖环境变量的隐式优先级。
38
+ #
39
+ # 为什么非这样不可(实测 aliyun 3.3.17,`--mode AK`):`ALIBABA_CLOUD_ACCESS_KEY_ID` /
40
+ # `_SECRET` / `_REGION_ID` 这三个环境变量在这个模式下**根本不被消费**,于是
41
+ # - 机器上没有 `~/.aliyun/config.json`(全新用户的常态,ensure_toolchain 只装 CLI、
42
+ # 从不跑 `aliyun configure`)→ `ERROR: region can't be empty`,第一次发布就跑不起来;
43
+ # - 机器上有 profile(比如用户自己配过阿里云)→ **静默走用户自己的 profile**,等于拿
44
+ # 用户的阿里云身份去部署 extension,跟 access.yaml 那个坑是同一类。
45
+ # 补 `--region` 一个 flag 不够,AK 也必须是 flag——两者都试过。
46
+ #
47
+ # 用法跟 aliyun 一样,省掉 `--mode AK` 和三个凭证参数:aliyun_ak sts GetCallerIdentity
48
+ aliyun_ak() {
49
+ aliyun --mode AK \
50
+ --access-key-id "${ALIBABA_CLOUD_ACCESS_KEY_ID:?aliyun_ak 需要先 ensure_aliyun_env}" \
51
+ --access-key-secret "${ALIBABA_CLOUD_ACCESS_KEY_SECRET:?aliyun_ak 需要先 ensure_aliyun_env}" \
52
+ --region "${ALIBABA_CLOUD_REGION_ID:?aliyun_ak 需要先 ensure_aliyun_env}" \
53
+ "$@"
54
+ }
55
+
37
56
  # 把 Serverless Devs 的配置家目录(access.yaml / 组件缓存 / 日志)指到 deploy-kit 名下,
38
57
  # 再把当前 env 里的凭证写成 $S_ACCESS_ALIAS profile。
39
58
  # s v3 的 rootHome = `$serverless_devs_config_home/.s`(默认 `$HOME/.s`),`s -v` 打印的 s-home 就是它——
@@ -46,9 +65,12 @@ ensure_s_access() {
46
65
  local s_home="$kit/.s-home"
47
66
  local ident acc
48
67
 
49
- ident="$(aliyun --mode AK sts GetCallerIdentity 2>&1)" || {
50
- echo "❌ aliyun sts GetCallerIdentity 失败(检查 .secrets/aliyun.env 凭证):" >&2
68
+ ident="$(aliyun_ak sts GetCallerIdentity 2>&1)" || {
69
+ # 别只说「检查凭证」——凭证不对只是可能之一,region 缺失 / CLI 装坏 / 网络不通报的是别的话。
70
+ # 把 CLI 原文摆出来,让读的人按原文判断。
71
+ echo "❌ aliyun sts GetCallerIdentity 失败,拿不到 AccountId。aliyun 原文:" >&2
51
72
  echo "$ident" >&2
73
+ echo " 排查顺序:① 上面这行 CLI 原文说的是什么 ② $kit/.secrets/aliyun.env(及 .local)里的 AK 是否有效 ③ config.env 的 REGION" >&2
52
74
  return 1
53
75
  }
54
76
  # 用 node 不用 python3:目标机器保证有 node(clawd 本身就是 node 跑的),不保证有 python
@@ -29,11 +29,33 @@ const USER_ACCESS_YAML = `default:
29
29
  AccessKeySecret: user-own-secret
30
30
  `
31
31
 
32
- /** aliyun CLI shim:`sts GetCallerIdentity` 回真 CLI 那样的 JSON,其余子命令原样成功。 */
33
- function writeAliyunShim(bin: string, accountId = '5566778899001122'): void {
32
+ /**
33
+ * aliyun CLI shim —— **照真 CLI 的凭证优先级建模**(实测 aliyun 3.3.17,`--mode AK`):
34
+ *
35
+ * - `--access-key-id` / `--access-key-secret` / `--region` 三个 **flag 缺一不可**;
36
+ * - `ALIBABA_CLOUD_*` 环境变量在这个模式下**根本不被消费**——没有 `~/.aliyun/config.json`
37
+ * 的干净机器上只会得到 `region can't be empty`(rc=3),有 profile 的机器上则**静默走用户
38
+ * 自己的 profile**(等于拿用户身份部署)。
39
+ *
40
+ * 所以 shim 只认 flag、不认 env:脚本要是退回去依赖 env 优先级,这里当场红。
41
+ */
42
+ function writeAliyunShim(bin: string, accountId = '5566778899001122', argvLog?: string): void {
34
43
  writeFileSync(
35
44
  join(bin, 'aliyun'),
36
- `#!/usr/bin/env bash\nprintf '{"AccountId":"${accountId}","Arn":"acs:ram::${accountId}:user/clawd-fc-developer"}\\n'\n`,
45
+ `#!/usr/bin/env bash
46
+ ${argvLog ? `printf '%s\\n' "$*" >> "${argvLog}"` : ''}
47
+ argv=" $* "
48
+ for f in --access-key-id --access-key-secret --region; do
49
+ case "$argv" in
50
+ *" $f "*) ;;
51
+ *) echo "ERROR: region can't be empty" >&2; exit 3 ;;
52
+ esac
53
+ done
54
+ case "$argv" in
55
+ *" sts "*) printf '{"AccountId":"${accountId}","Arn":"acs:ram::${accountId}:user/clawd-fc-developer"}\\n' ;;
56
+ *) printf '{"customDomains":[]}\\n' ;;
57
+ esac
58
+ `,
37
59
  { mode: 0o755 },
38
60
  )
39
61
  }
@@ -139,6 +161,27 @@ describe('ensure_credentials', () => {
139
161
  expect(kitAccessYaml()).toContain('AccessKeySecret: my-own-secret')
140
162
  })
141
163
 
164
+ it('调 aliyun 时显式传 AK 和 region,不靠环境变量的隐式优先级', () => {
165
+ // 真 CLI 在 `--mode AK` 下不消费 ALIBABA_CLOUD_*:干净机器上报 `region can't be empty`
166
+ // 直接跑不起来;有 ~/.aliyun/config.json 的机器上更坏——静默走用户自己的 profile,
167
+ // 等于拿用户的阿里云身份去部署。两种都必须由显式 flag 堵死。
168
+ const log = join(box.root, 'aliyun-argv.log')
169
+ writeAliyunShim(box.bin, '5566778899001122', log)
170
+ runInKit('ensure_credentials')
171
+ const argv = readFileSync(log, 'utf8')
172
+ expect(argv).toContain('--access-key-id LTAI-SHARED-DEMO')
173
+ expect(argv).toContain('--access-key-secret shared-demo-secret')
174
+ expect(argv).toContain('--region cn-hangzhou')
175
+ })
176
+
177
+ it('机器上没有 aliyun profile 也能拿到 AccountId', () => {
178
+ // 全新用户机器:ensure_toolchain 只装 CLI、从不跑 `aliyun configure`,所以 HOME 下
179
+ // 没有 .aliyun/config.json。严格 shim 复现真 CLI 的这条路径。
180
+ rmSync(join(box.home, '.aliyun'), { recursive: true, force: true })
181
+ runInKit('ensure_credentials')
182
+ expect(kitAccessYaml()).toContain("AccountID: '5566778899001122'")
183
+ })
184
+
142
185
  it('缺共享凭证文件时 fail-loud', () => {
143
186
  rmSync(join(box.kit, '.secrets', 'aliyun.env'))
144
187
  expect(() => runInKit('ensure_credentials')).toThrow()
@@ -150,13 +193,10 @@ describe('publish.sh', () => {
150
193
  it('用 clawd-fc profile 调 s deploy,不碰用户全局 ~/.s', () => {
151
194
  const log = join(box.root, 's-argv.log')
152
195
  writeSShim(box.bin, log)
153
- // 自定义域名查询回空 → publish.sh 在 verify 段 exit 1。deploy 之后的事,
196
+ // 严格 shim 的自定义域名查询回空 → publish.sh 在 verify 段 exit 1。deploy 之后的事,
154
197
  // 不影响本用例要断言的凭证行为,省掉 curl shim。
155
- writeFileSync(
156
- join(box.bin, 'aliyun'),
157
- `#!/usr/bin/env bash\ncase " $* " in\n *" sts "*) printf '{"AccountId":"5566778899001122"}\\n' ;;\n *) printf '{"customDomains":[]}\\n' ;;\nesac\n`,
158
- { mode: 0o755 },
159
- )
198
+ const aliyunLog = join(box.root, 'aliyun-argv.log')
199
+ writeAliyunShim(box.bin, '5566778899001122', aliyunLog)
160
200
 
161
201
  const ext = join(box.root, 'proj')
162
202
  const persona = join(box.root, 'persona')
@@ -5,9 +5,12 @@
5
5
  # 在调用 aliyun/s 之前 `source` 进来,再调 ensure_toolchain。
6
6
  #
7
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] 阶段。这个脚本补的就是这一环。
8
+ # 新机器上它们不在 PATH → ensure-credentials.sh 的 `aliyun_ak sts ...` stdout 为空 →
9
+ # JSON.parse 空串报错,且死在第一个 ::stage:: marker 之前 → daemon 只能报 [unknown] 阶段。
10
+ # 这个脚本补的就是这一环。
11
+ #
12
+ # 注意它**只装不配**:不会跑 `aliyun configure`,所以机器上不会因此有 `~/.aliyun/config.json`
13
+ # ——凭证由 ensure-credentials.sh 的 aliyun_ak 每次显式传参,不依赖任何本机 profile。
11
14
  # ============================================================
12
15
 
13
16
  # aliyun CLI 官方 release 兜底版本(仅在无 brew、走二进制下载时用;brew 路径不读它)。
@@ -86,7 +86,7 @@ echo "==> s deploy ($DEPLOY_NAME)"
86
86
  echo "::stage::verify"
87
87
  # 精确匹配 s.yaml.tmpl 里写死的 domainName 模式: <DEPLOY_NAME>.app.clawos.chat
88
88
  EXPECTED_DOM="${DEPLOY_NAME}.app.clawos.chat"
89
- DOM="$(aliyun --mode AK fc GET /2023-03-30/custom-domains --region "$REGION" 2>/dev/null | python3 -c "
89
+ DOM="$(aliyun_ak fc GET /2023-03-30/custom-domains 2>/dev/null | python3 -c "
90
90
  import sys,json
91
91
  d=json.load(sys.stdin)
92
92
  target='$EXPECTED_DOM'
@@ -23848,7 +23848,7 @@ var METHOD_DOCS = {
23848
23848
  args: WorkspaceReadArgs
23849
23849
  },
23850
23850
  "deploy:start": {
23851
- summary: "\u6253\u5F00\u67D0\u4E2A persona \u7684\u90E8\u7F72\u5355\uFF1A**\u5DF2\u6709\u5C31\u539F\u6837\u8FD4\u56DE\u90A3\u5F20**\uFF08\u8349\u7A3F\u7EED\u586B\uFF0C\u4E0D\u91CD\u626B\uFF0C\u4E5F\u4E0D\u65B0\u5EFA\uFF09\uFF0C\u6CA1\u6709\u624D\u5EFA\u4E00\u5F20\u5E76**\u5F02\u6B65**\u8D77\u73AF\u5883\u626B\u63CF\u3002\u4E00\u4E2A persona \u540C\u65F6\u53EA\u6709\u4E00\u5F20\u5355\uFF0C\u8981\u91CD\u626B\u8D70 deploy:rescan\uFF1Bowner-only",
23851
+ summary: "\u5BF9\u67D0\u4E2A persona \u8D77\u4E00\u6B21**\u65B0\u7684**\u4E0A\u4E91\u626B\u63CF\uFF1A\u5EFA\u4E00\u5F20\u65B0\u5355\u636E\u5E76\u5F02\u6B65\u626B\uFF08\u4EBA\u4F1A\u70B9\u8FD9\u4E2A\u53EA\u53EF\u80FD\u662F\u56E0\u4E3A persona \u8FED\u4EE3\u4E86\uFF0C\u62FF\u65E7\u6E05\u5355\u53BB\u90E8\u7F72\u662F\u9519\u7684\uFF09\u3002**\u5DF2\u586B\u7684\u51ED\u636E\u503C\u548C\u5C65\u7EA6\u9009\u62E9\u4ECE\u4E0A\u4E00\u5F20\u5355\u7EE7\u627F\u8FC7\u6765**\uFF0C\u4E0D\u7528\u6BCF\u8FED\u4EE3\u4E00\u6B21\u91CD\u8D34\u4E00\u904D key\u3002\u5DF2\u7ECF\u5728\u626B\u7684\u8BDD\u8FD4\u56DE\u90A3\u5F20\u3001\u4E0D\u5E76\u53D1\u8D77\u7B2C\u4E8C\u4E2A\u626B\u63CF\u4F1A\u8BDD\u3002\u56DE\u770B\u65E7\u7684\u90A3\u6B21\u8D70 deploy:list + deploy:get\uFF1Bowner-only",
23852
23852
  args: DeployStartArgsSchema
23853
23853
  },
23854
23854
  "deploy:rescan": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "_comment": "preinstall ship 进 daemon defaults,daemon 启动时 refreshDaemonManagedDirs 把这文件同步到 ~/.clawd*/personas/persona-app-builder/.mcp.json。cc CLI 启动时(cwd=projects/<name>/)会向上找 persona dir 的 .mcp.json 自动加载。用 @aliyun-rds/supabase-mcp-server Mode 2 单实例直连(不再用 aliyun AK),底下接阿里云托管 supabase(公网 endpoint 121.196.249.178:80,项目 ra-ae0i57kef06s6ix / clawd-app-builder,杭州可用区J)。旧自建 supabase 120.26.157.138 已退役,旧共享 aliyun AK LTAI5tSebp9pyBLk2nxF5wDg 已 revoke。**不要往这里加 --jwt-secret**:jwt-secret 是该实例的 JWT 签发密钥,拿到它可以自签任意 role/任意 user 的 token(含 service_role),下发出去等于把 anon/service key 的轮换能力一起交出去;而 MCP server 里它唯一的用处是 verify_jwt_secret 这个自检工具报个 found/preview(--jwt-secret 在其 CLI 里就标了 optional),建表读写和 auth user 管理都不经过它。**这份文件随 OTA 发给所有用户,所以这里放的每一把 key 都等于公开**:该实例上除 demo 数据外还有 clawd 自己的 clawd_prod/clawd_int schema(device_bindings 等),收敛方案见 doc/glossary/persona-cloud.md。",
2
+ "_comment": "preinstall ship 进 daemon defaults,daemon 启动时 refreshDaemonManagedDirs 把这文件同步到 ~/.clawd*/personas/persona-app-builder/.mcp.json。cc CLI 启动时(cwd=projects/<name>/)会向上找 persona dir 的 .mcp.json 自动加载。用 @aliyun-rds/supabase-mcp-server Mode 2 单实例直连(不再用 aliyun AK),底下接阿里云托管 supabase(公网 endpoint 121.196.249.178:80,项目 ra-ae0i57kef06s6ix / clawd-app-builder,杭州可用区J)。**不要往这里加 --jwt-secret**:jwt-secret 是该实例的 JWT 签发密钥,拿到它可以自签任意 role/任意 user 的 token(含 service_role),下发出去等于把 anon/service key 的轮换能力一起交出去;而 MCP server 里它唯一的用处是 verify_jwt_secret 这个自检工具报个 found/preview(--jwt-secret 在其 CLI 里就标了 optional),建表读写和 auth user 管理都不经过它。**这份文件随 OTA 发给所有用户,所以这里放的每一把 key 都等于公开**:该实例上除 demo 数据外还有 clawd 自己的 clawd_prod/clawd_int schema(device_bindings 等),收敛方案见 doc/glossary/persona-cloud.md。",
3
3
  "mcpServers": {
4
4
  "supabase": {
5
5
  "type": "stdio",
@@ -158,13 +158,13 @@ createProject 成功后 daemon 自动:
158
158
 
159
159
  ## 后端:supabase MCP
160
160
 
161
- **`supabase`** —— 后端数据/认证/存储,唯一的 MCP 支柱。这份 Supabase **是阿里云托管 supabase 实例**(`http://121.196.249.178:80`),全局 MCP / clawos / lovagent / moltoffer / clawd 都指向这一台。**不是**云上 `supabase.com` 的实例。**旧自建 supabase 120.26.157.138 已退役**,老项目一并迁过来,不再"共用两台"。开工前确认能连上:
161
+ **`supabase`** —— 后端数据/认证/存储,唯一的 MCP 支柱。这份 Supabase **是阿里云托管 supabase 实例**(`http://121.196.249.178:80`),全局 MCP / clawos / lovagent / moltoffer / clawd 都指向这一台。**不是**云上 `supabase.com` 的实例。开工前确认能连上:
162
162
 
163
163
  - 读表结构、跑 SQL、看 auth 用户都走这个 MCP,不要凭记忆猜 schema
164
164
  - 模板里 `extension-kit/config.env` + `server/.env.example` 已对齐这台阿里云托管实例(SUPABASE_URL / SUPABASE_KEY),创建 project 时拷进项目目录的 `.env` 也是同一套
165
165
  - **红线**:这是共享生产库。建表 / 迁移前先 `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 的就先问老板
166
166
 
167
- **易踩的坑**:MCP 工具连的就是 `121.196.249.178`,假如哪一步把 `.env` / `config.env` 写成云上 `qmhqmopgwsbmfxiqagay.supabase.co`(历史旧默认)或者残留的 `120.26.157.138`(旧自建,已退役),就会**MCP 建表落阿里云 / server 通过 supabase-js 查别处**,两台 PostgREST schema cache 各自独立 → `PGRST205 schema cache 找不到表`。看到 PGRST 系列错码先 `grep -rE 'supabase.co|120\.26\.157\.138'` 自查模板配置,再怀疑 schema cache。
167
+ **易踩的坑**:`config.env` 的 `SUPABASE_URL` 是**唯一真源** —— MCP 工具连它,`publish.sh` 也把它渲染进 s.yaml 给线上用。任何一处(项目 `.env`、`.env.example`)写成别的地址,就会**MCP 建表落一台 / server 通过 supabase-js 查另一台**,两边 PostgREST 的 schema cache 各自独立 → `PGRST205 schema cache 找不到表`。看到 PGRST 系列错码,先拿项目里的 `SUPABASE_URL` 跟 `config.env` 逐字比对,一致了再怀疑 schema cache。
168
168
 
169
169
  ## 阿里云凭证:在共享 deploy-kit 的 `.secrets/`,分层 override
170
170
 
@@ -184,7 +184,10 @@ source "$KIT_DIR/scripts/ensure-credentials.sh" && ensure_credentials
184
184
 
185
185
  ⚠️ **上面这几行和后续的 `aliyun` / `s` 命令必须在同一个 Bash 调用里跑完**——你的 Bash tool 每次调用是独立进程,shell 变量不跨调用留存。分两次跑 = 第二次丢了 `serverless_devs_config_home`,`s` 会回头去读 `~/.s` 然后报 `Not found access: clawd-fc`。**这时正确做法是把 source 那几行和 `s` 命令拼进同一条命令重跑,绝不是去写 `~/.s/access.yaml`。**
186
186
 
187
- 之后 `aliyun` / `ossutil` 直接可用(AK 已进 env)。**Serverless Devs(`s`)要显式带 profile**:凭证写在 deploy-kit 私有的 `.s-home/` 里、profile 名 `clawd-fc`,所以每次都是 `s deploy -y --access "$S_ACCESS_ALIAS"` / `s remove -y --access "$S_ACCESS_ALIAS"`。
187
+ 之后两个 CLI **都不能裸调**,各有各的入口:
188
+
189
+ - **`aliyun` 一律走 `aliyun_ak`**(`ensure-credentials.sh` 里的函数),例:`aliyun_ak fc GET /2023-03-30/custom-domains`。它把 AK 和 region 作为显式参数传进去。**别写 `aliyun --mode AK ...` 让它自己找凭证**:那个模式下 `ALIBABA_CLOUD_*` 环境变量根本不被消费,机器上没配过 `aliyun configure` 就报 `region can't be empty`,配过的话更糟——静默走用户自己的 profile,等于拿用户的阿里云账号部署。
190
+ - **`s` 要显式带 profile**:凭证写在 deploy-kit 私有的 `.s-home/` 里、profile 名 `clawd-fc`,所以每次都是 `s deploy -y --access "$S_ACCESS_ALIAS"` / `s remove -y --access "$S_ACCESS_ALIAS"`。
188
191
 
189
192
  **红线:绝不写用户全局 `~/.s/access.yaml`**。那里的 `default` 是用户自己账号的凭证,clawd 用的是共享 demo AK,盖上去用户的就没了、不可恢复。
190
193
 
@@ -11,9 +11,9 @@ NODE_LAYER=acs:fc:cn-hangzhou:official:layers/Nodejs22/versions/1
11
11
  # Supabase(阿里云托管 supabase 实例!建表务必加 <app>_ 前缀沉到 public schema,避免撞名)
12
12
  # 公网 endpoint 121.196.249.178:80(项目 ra-ae0i57kef06s6ix / clawd-app-builder,杭州可用区J)
13
13
  # 全局 MCP / clawos / lovagent / moltoffer / clawd 都指向这一台。
14
- # 旧自建 supabase 120.26.157.138 已退役,老项目也一起迁到这里,不再"共用两台"。
15
- # 之前误指云上 qmhqmopgwsbmfxiqagay.supabase.co 导致 MCP 建表落阿里云,server 走 supabase-js
16
- # 查云上,两台 PostgREST schema cache 独立 → PGRST205 找不到表。托管后同样要盯紧 .env 别串。
14
+ # 下面两行是**唯一真源**:examples/*/server/.env.example 必须跟这里逐值一致
15
+ # (defaults-supabase-parity.spec.ts 守这条)。不一致 = 本地连一台、线上连另一台,
16
+ # 两边 PostgREST 的 schema cache 各自独立 → PGRST205 找不到表。
17
17
  SUPABASE_URL=http://121.196.249.178:80
18
18
  # anon/publishable key(公开可暴露,客户端用)。阿里云托管实例的 anon key,跟 supabase MCP 用同一把。
19
19
  SUPABASE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzgzMzEwODU1LCJleHAiOjEzMjkzOTUwODU1fQ.sHLZH8q2SGWYbNPEFOzTFlQEtjVK3z1DlEqoU_5Uyyg
@@ -1,6 +1,7 @@
1
- # Supabase 共享阿里云自建实例(跟 extension-kit/config.env 对齐)。
2
- # 这台 supabase 也是全局 MCP / clawos / lovagent / moltoffer 在用的同一台 ——
3
- # 用 MCP 建表落到这里,server 通过 supabase-js 读也走这里,schema cache 对得齐。
4
- SUPABASE_URL=http://120.26.157.138:80
5
- SUPABASE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzY3OTU0MTY0LCJleHAiOjEzMjc4NTk0MTY0fQ.quOr1bQZ2OCQ49_UCto8LGY-n90Hlku1IbJYltFVWPc
1
+ # Supabase 连接串。**唯一真源是 extension-kit/config.env**——publish.sh 从那里读值渲染进
2
+ # s.yaml 的 environmentVariables,线上跑的就是那一份。这里是本地 dev 用的副本(copy 成 .env),
3
+ # 改实例地址时两处要一起改,否则本地连一台、线上连另一台,两边 PostgREST schema cache 各自独立,
4
+ # 症状是 PGRST205 找不到表。(defaults-supabase-parity.spec.ts 守这条一致性。)
5
+ SUPABASE_URL=http://121.196.249.178:80
6
+ SUPABASE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzgzMzEwODU1LCJleHAiOjEzMjkzOTUwODU1fQ.sHLZH8q2SGWYbNPEFOzTFlQEtjVK3z1DlEqoU_5Uyyg
6
7
  PORT=3000