@clawos-dev/clawd 0.2.124 → 0.2.126-beta.252.5b08fb0

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 (21) hide show
  1. package/dist/cli.cjs +6 -6
  2. package/dist/persona-defaults/persona-app-builder/.mcp.json +15 -0
  3. package/dist/persona-defaults/persona-app-builder/CLAUDE.md +5 -2
  4. package/dist/persona-defaults/persona-app-builder/extension-kit/config.env +8 -5
  5. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.env.example +5 -2
  6. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package.json +1 -0
  7. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +5 -8
  8. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +12 -2
  9. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.controller.ts +27 -0
  10. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.module.ts +9 -0
  11. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.service.ts +49 -0
  12. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/polyfill.ts +7 -0
  13. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/index.html +1 -1
  14. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/App.jsx +159 -9
  15. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +1 -8
  16. package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/remove-extension.sh +28 -19
  17. package/dist/persona-defaults/persona-bug-fixer/.mcp.json +15 -0
  18. package/dist/persona-defaults/persona-bug-fixer/CLAUDE.md +4 -26
  19. package/dist/persona-defaults/persona-developer/.mcp.json +15 -0
  20. package/dist/persona-defaults/persona-developer/CLAUDE.md +4 -26
  21. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -34926,7 +34926,7 @@ function copyBundleExtras(srcDir, dstDir) {
34926
34926
  }
34927
34927
  }
34928
34928
  }
34929
- var DAEMON_MANAGED_DIRS = ["extension-kit"];
34929
+ var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json"];
34930
34930
  function refreshDaemonManagedDirs(args) {
34931
34931
  const entries = args.entries ?? DEFAULT_PERSONAS;
34932
34932
  for (const entry of entries) {
@@ -34934,20 +34934,20 @@ function refreshDaemonManagedDirs(args) {
34934
34934
  if (!fs9.existsSync(bundleDir)) continue;
34935
34935
  const personaDir = args.store.personaDirPath(entry.personaId);
34936
34936
  if (!fs9.existsSync(personaDir)) continue;
34937
- for (const subdir of DAEMON_MANAGED_DIRS) {
34938
- const srcPath = path11.join(bundleDir, subdir);
34937
+ for (const relPath of DAEMON_MANAGED_PATHS) {
34938
+ const srcPath = path11.join(bundleDir, relPath);
34939
34939
  if (!fs9.existsSync(srcPath)) continue;
34940
- const dstPath = path11.join(personaDir, subdir);
34940
+ const dstPath = path11.join(personaDir, relPath);
34941
34941
  try {
34942
34942
  fs9.cpSync(srcPath, dstPath, { recursive: true, force: true, dereference: true });
34943
34943
  args.logger.info("persona.refresh.synced", {
34944
34944
  personaId: entry.personaId,
34945
- subdir
34945
+ path: relPath
34946
34946
  });
34947
34947
  } catch (err) {
34948
34948
  args.logger.warn("persona.refresh.failed", {
34949
34949
  personaId: entry.personaId,
34950
- subdir,
34950
+ path: relPath,
34951
34951
  error: err instanceof Error ? err.message : String(err)
34952
34952
  });
34953
34953
  }
@@ -0,0 +1,15 @@
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(阿里云 RDS 包装,跟 clawos / lovagent / extension-kit 一致),底下连阿里云自建 supabase 实例 120.26.157.138。AK 是共享 demo 凭证(跟 .secrets/aliyun.env 同性质,PR #808 设计前提),跟 lovagent .mcp.json 用同一把(RDS 权限)。",
3
+ "mcpServers": {
4
+ "supabase": {
5
+ "type": "stdio",
6
+ "command": "npx",
7
+ "args": [
8
+ "@aliyun-rds/supabase-mcp-server",
9
+ "--aliyun-ak", "LTAI5tSebp9pyBLk2nxF5wDg",
10
+ "--aliyun-sk", "DnkOBhZ9pLympYlwcLuQbsaGRIitwG",
11
+ "--aliyun-region", "cn-hangzhou"
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -163,10 +163,13 @@ createProject 成功后 daemon 自动:
163
163
 
164
164
  ## 后端:supabase MCP
165
165
 
166
- **`supabase`** —— 后端数据/认证/存储,唯一的 MCP 支柱。这份 Supabase **就是 clawos 在用的那一个**(`project-ref=qmhqmopgwsbmfxiqagay`),开工前确认能连上:
166
+ **`supabase`** —— 后端数据/认证/存储,唯一的 MCP 支柱。这份 Supabase **是阿里云自建实例**(`http://120.26.157.138:80`),全局 MCP / clawos / lovagent / moltoffer 都共用这一台。**不是**云上 `supabase.com` 的实例。开工前确认能连上:
167
167
 
168
168
  - 读表结构、跑 SQL、看 auth 用户都走这个 MCP,不要凭记忆猜 schema
169
- - **红线**:这是共享生产库。建表 / 迁移前先 `list_tables` 看清现状,新项目的表用清晰前缀(如 `<项目名>_xxx`)避免和 clawos 的表撞名;**绝不** drop / alter clawos 已有的表,不确定哪些是 clawos 的就先问老板
169
+ - 模板里 `extension-kit/config.env` + `server/.env.example` 已对齐这台阿里云自建实例(SUPABASE_URL / SUPABASE_KEY),创建 project 时拷进项目目录的 `.env` 也是同一套
170
+ - **红线**:这是共享生产库。建表 / 迁移前先 `list_tables` 看清现状,新项目的表**必须**用 `<项目名>_` 前缀沉到 `public` schema(如 `helloworld_click_counter`),避免和 clawos / 其它项目的表撞名;**绝不** drop / alter clawos 已有的表,不确定哪些是 clawos 的就先问老板
171
+
172
+ **易踩的坑**:MCP 工具走阿里云自建那台,假如哪一步把 `.env` / `config.env` 写成云上 `qmhqmopgwsbmfxiqagay.supabase.co`(曾经的旧默认),就会**MCP 建表落阿里云 / server 通过 supabase-js 查云上**,两台 PostgREST schema cache 各自独立 → `PGRST205 schema cache 找不到表`。看到 PGRST 系列错码先 `grep -r supabase.co` 自查模板配置,再怀疑 schema cache。
170
173
 
171
174
  ## 阿里云凭证:在 `.secrets/aliyun.env`,用时现读
172
175
 
@@ -8,11 +8,14 @@ REGION=cn-hangzhou
8
8
  FC_RUNTIME=custom.debian12
9
9
  NODE_LAYER=acs:fc:cn-hangzhou:official:layers/Nodejs22/versions/1
10
10
 
11
- # Supabase(clawos 共享库!建表务必加 <app>_ 前缀,绝不动 clawos 已有表)
12
- SUPABASE_URL=https://qmhqmopgwsbmfxiqagay.supabase.co
13
- SUPABASE_PROJECT_REF=qmhqmopgwsbmfxiqagay
14
- # anon/publishable key(公开可暴露,客户端用)
15
- SUPABASE_KEY=sb_publishable_mpSFrgfQ5UO1yTQeJDjYvg_4aUZgvmG
11
+ # Supabase(clawos 共享阿里云自建实例!建表务必加 <app>_ 前缀沉到 public schema,绝不动 clawos 已有表)
12
+ # 注意:这是阿里云自建 supabase(120.26.157.138),不是云上 supabase.com 实例 ——
13
+ # 全局 MCP / clawos / lovagent / moltoffer 都共用这一台,跟 supabase MCP 操作的也是这台。
14
+ # 之前误指云上 qmhqmopgwsbmfxiqagay.supabase.co 导致 MCP 建表落阿里云,server 走 supabase-js
15
+ # 查云上,两台 PostgREST schema cache 独立 → PGRST205 找不到表。
16
+ SUPABASE_URL=http://120.26.157.138:80
17
+ # anon/publishable key(公开可暴露,客户端用)。阿里云自建实例无 project-ref 概念。
18
+ SUPABASE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNzY3OTU0MTY0LCJleHAiOjEzMjc4NTk0MTY0fQ.quOr1bQZ2OCQ49_UCto8LGY-n90Hlku1IbJYltFVWPc
16
19
 
17
20
  # FC 资源规格(够跑轻量 extension;按需改)
18
21
  FC_CPU=0.35
@@ -1,3 +1,6 @@
1
- SUPABASE_URL=https://qmhqmopgwsbmfxiqagay.supabase.co
2
- SUPABASE_KEY=sb_publishable_mpSFrgfQ5UO1yTQeJDjYvg_4aUZgvmG
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
3
6
  PORT=3000
@@ -13,6 +13,7 @@
13
13
  "@nestjs/core": "^10.4.0",
14
14
  "@nestjs/platform-express": "^10.4.0",
15
15
  "@supabase/supabase-js": "^2.45.0",
16
+ "dotenv": "^16.4.5",
16
17
  "reflect-metadata": "^0.2.2",
17
18
  "rxjs": "^7.8.1",
18
19
  "ws": "^8.18.0"
@@ -1,13 +1,10 @@
1
1
  import { Module } from '@nestjs/common';
2
+ import { MessagesModule } from './messages/messages.module';
2
3
 
3
- // 默认空白骨架 —— 没有任何业务 controller / service / module 注册。
4
- // 历史背景:之前导入 MessagesModule 自带留言板 API + Supabase repo,用户起 dev server 时 nest
5
- // 启动会初始化 supabase 模块(控制台 noise + 旁边业务代码会暗示 assistant 沿用留言板风格)。
6
- // 瘦身后只留启动骨架,业务逻辑由 assistant 按用户需求从零加(添 controller/module 进 imports 即可)。
7
- //
8
- // vite middleware / 静态资源 serve 路径仍由 main.ts 处理(dev mount vite,prod express.static),
9
- // 不需要 ServeStaticModule。
4
+ // dev 模式:main.ts mount vite middleware 处理非 /api 路径(含 HMR);
5
+ // prod 模式:main.ts express.static serve build 后的 web/dist。
6
+ // 两种模式都不用 ServeStaticModule(vite middleware 跟它会撞),就近在 main.ts 处理。
10
7
  @Module({
11
- imports: [],
8
+ imports: [MessagesModule],
12
9
  })
13
10
  export class AppModule {}
@@ -56,10 +56,20 @@ async function bootstrap() {
56
56
  allowedHosts,
57
57
  },
58
58
  // appType: 'spa' 让 vite middleware 自己 serve index.html + fallback 路由(base 下任何
59
- // 非 vite asset 路径都返 index.html)。nest router 注册在 vite 之前,/api 路由优先匹配。
59
+ // 非 vite asset 路径都返 index.html)。
60
60
  appType: 'spa',
61
61
  });
62
- app.use(vite.middlewares);
62
+ // /api/* 守卫:实测 vite 'spa' catch-all 会先吃掉 /api/* 返 HTML,导致 API 拿不到 JSON。
63
+ // Express middleware 栈顺序:vite.middlewares 这一行加进去时 nest controllers 还没挂
64
+ // (nest router init 在 app.listen 时),所以 vite 拦截优先于 nest router → 命中 /api 也走 vite。
65
+ // 守卫:req.url 命中 apiPrefix 时直接 next() 跳过 vite,让 nest router 接管返 JSON。
66
+ const apiPathPrefix = `/${apiPrefix}/`; // 如 '/preview/6173/api/' 或 '/api/'
67
+ app.use((req: any, res: any, next: any) => {
68
+ if (typeof req.url === 'string' && req.url.startsWith(apiPathPrefix)) {
69
+ return next(); // skip vite,让 nest router 处理 /api/*
70
+ }
71
+ return vite.middlewares(req, res, next);
72
+ });
63
73
  console.log(`[clawd-dev] vite middleware mounted (root=${viteRoot}, base=${viteBase}, apiPrefix=${apiPrefix}, allowedHosts=${allowedHosts.join(',')})`);
64
74
  } else {
65
75
  // prod / 脱离 clawd 跑:直接 serve 前端静态资源。
@@ -0,0 +1,27 @@
1
+ import {
2
+ BadRequestException,
3
+ Body,
4
+ Controller,
5
+ Get,
6
+ Post,
7
+ } from '@nestjs/common';
8
+ import { MessagesService } from './messages.service';
9
+
10
+ @Controller('messages')
11
+ export class MessagesController {
12
+ constructor(private readonly messages: MessagesService) {}
13
+
14
+ @Get()
15
+ list() {
16
+ return this.messages.list();
17
+ }
18
+
19
+ @Post()
20
+ create(@Body() body: { name?: string; content?: string }) {
21
+ const content = body?.content?.trim();
22
+ if (!content) {
23
+ throw new BadRequestException('content 不能为空');
24
+ }
25
+ return this.messages.create(body.name?.trim() ?? '', content);
26
+ }
27
+ }
@@ -0,0 +1,9 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { MessagesController } from './messages.controller';
3
+ import { MessagesService } from './messages.service';
4
+
5
+ @Module({
6
+ controllers: [MessagesController],
7
+ providers: [MessagesService],
8
+ })
9
+ export class MessagesModule {}
@@ -0,0 +1,49 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { createClient, SupabaseClient } from '@supabase/supabase-js';
3
+
4
+ const TABLE = 'guestbook_messages';
5
+
6
+ // 模板示例 service。Supabase client **lazy 初始化**:
7
+ // - constructor 不读 env / 不 throw —— 让整个 nest server 能起来即使 SUPABASE_URL/KEY 没配
8
+ // (比如 fresh project 还没接 Supabase,老板只想看个 welcome 页面)
9
+ // - 实际调 list / create 时再 require,缺 env 时只报给这条 API 的 caller,不阻塞 server 启动
10
+ //
11
+ // 老 bug:constructor 里硬 require + throw → 没配 env 时 NestApplication 启动直接 crash,
12
+ // dev server 进 exit 1 → preview iframe upstream error 糊脸。
13
+ @Injectable()
14
+ export class MessagesService {
15
+ private _supabase: SupabaseClient | null = null;
16
+
17
+ private get supabase(): SupabaseClient {
18
+ if (this._supabase) return this._supabase;
19
+ const url = process.env.SUPABASE_URL;
20
+ const key = process.env.SUPABASE_KEY;
21
+ if (!url || !key) {
22
+ throw new Error(
23
+ 'SUPABASE_URL / SUPABASE_KEY 未配置 —— 请先接 Supabase 后端再调 /api/messages',
24
+ );
25
+ }
26
+ this._supabase = createClient(url, key);
27
+ return this._supabase;
28
+ }
29
+
30
+ async list() {
31
+ const { data, error } = await this.supabase
32
+ .from(TABLE)
33
+ .select('*')
34
+ .order('created_at', { ascending: false })
35
+ .limit(100);
36
+ if (error) throw new Error(`读取留言失败: ${error.message}`);
37
+ return data;
38
+ }
39
+
40
+ async create(name: string, content: string) {
41
+ const { data, error } = await this.supabase
42
+ .from(TABLE)
43
+ .insert({ name: name || 'anonymous', content })
44
+ .select()
45
+ .single();
46
+ if (error) throw new Error(`写入留言失败: ${error.message}`);
47
+ return data;
48
+ }
49
+ }
@@ -1,3 +1,10 @@
1
+ // Dev 模式 .env loader:`nest start --watch` 不会经过 `node --env-file=.env`,
2
+ // server 拿不到 SUPABASE_URL/KEY → createClient 跑了但 process.env 是空 → PGRST 系列错码。
3
+ // 这里在 nest bootstrap 之前显式 require dotenv 把 .env 填进 process.env。
4
+ // prod 路径(node --env-file=.env dist/main.js)Node 自带 loader 已经填了,dotenv 看到
5
+ // 已存在的 env 不会覆盖(默认行为),行为对齐。
6
+ require('dotenv/config');
7
+
1
8
  // Node < 22 没有原生 WebSocket,而 @supabase/supabase-js 的 realtime 客户端
2
9
  // 初始化时需要它。在应用启动前补一个全局 WebSocket,使任意 Node 版本
3
10
  // (FC 运行时给的 node 可能是 18/20)下都能正常 createClient。
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>App</title>
6
+ <title>留言板 · 全链路测试</title>
7
7
  </head>
8
8
  <body>
9
9
  <div id="root"></div>
@@ -1,11 +1,161 @@
1
- // 默认空白页骨架 —— createProject + startDevServer 起来时 iframe 第一帧就是干净白板,
2
- // 不会闪一下 demo 内容再被 assistant 改成用户实际想要的页面。
3
- //
4
- // 历史背景:之前模板自带留言板 demo(App.jsx + messages controller),用户从「新建项目 →
5
- // 起 dev server → 假设 assistant 改 App.jsx 成 hello world」整个流程里第一眼看到的是留言板
6
- // UI,HMR 替换后才变成真正的内容 —— 视觉闪动 + 业务代码被模板暗示带偏(spec follow-up
7
- // 2026-06-03 老板拍)。瘦身后骨架只保留 vite + nest + react 可启动结构,内容由 assistant
8
- // 按用户需求从零写。
1
+ import React, { useEffect, useState } from 'react';
2
+
3
+ const styles = {
4
+ page: {
5
+ minHeight: '100vh',
6
+ margin: 0,
7
+ background: 'linear-gradient(160deg, #0f172a 0%, #1e293b 100%)',
8
+ color: '#e2e8f0',
9
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
10
+ display: 'flex',
11
+ justifyContent: 'center',
12
+ padding: '48px 16px',
13
+ },
14
+ card: {
15
+ width: '100%',
16
+ maxWidth: 560,
17
+ },
18
+ title: { fontSize: 28, fontWeight: 700, margin: '0 0 4px' },
19
+ subtitle: { color: '#94a3b8', margin: '0 0 24px', fontSize: 14 },
20
+ form: { display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 28 },
21
+ row: { display: 'flex', gap: 10 },
22
+ input: {
23
+ flex: 1,
24
+ padding: '10px 12px',
25
+ borderRadius: 8,
26
+ border: '1px solid #334155',
27
+ background: '#0f172a',
28
+ color: '#e2e8f0',
29
+ fontSize: 14,
30
+ outline: 'none',
31
+ },
32
+ textarea: {
33
+ padding: '10px 12px',
34
+ borderRadius: 8,
35
+ border: '1px solid #334155',
36
+ background: '#0f172a',
37
+ color: '#e2e8f0',
38
+ fontSize: 14,
39
+ outline: 'none',
40
+ resize: 'vertical',
41
+ minHeight: 64,
42
+ },
43
+ button: {
44
+ padding: '10px 20px',
45
+ borderRadius: 8,
46
+ border: 'none',
47
+ background: '#6366f1',
48
+ color: '#fff',
49
+ fontSize: 14,
50
+ fontWeight: 600,
51
+ cursor: 'pointer',
52
+ },
53
+ msg: {
54
+ padding: '12px 14px',
55
+ borderRadius: 10,
56
+ background: '#1e293b',
57
+ border: '1px solid #334155',
58
+ marginBottom: 10,
59
+ },
60
+ msgHead: { display: 'flex', justifyContent: 'space-between', marginBottom: 4 },
61
+ msgName: { fontWeight: 600, color: '#a5b4fc', fontSize: 14 },
62
+ msgTime: { color: '#64748b', fontSize: 12 },
63
+ msgBody: { fontSize: 14, lineHeight: 1.5, whiteSpace: 'pre-wrap' },
64
+ error: { color: '#f87171', fontSize: 13, marginBottom: 12 },
65
+ empty: { color: '#64748b', fontSize: 14, textAlign: 'center', padding: '24px 0' },
66
+ };
67
+
9
68
  export default function App() {
10
- return <div />;
69
+ const [messages, setMessages] = useState([]);
70
+ const [name, setName] = useState('');
71
+ const [content, setContent] = useState('');
72
+ const [error, setError] = useState('');
73
+ const [loading, setLoading] = useState(false);
74
+
75
+ async function load() {
76
+ try {
77
+ const res = await fetch(`${import.meta.env.BASE_URL}api/messages`);
78
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
79
+ setMessages(await res.json());
80
+ setError('');
81
+ } catch (e) {
82
+ setError(`加载失败: ${e.message}`);
83
+ }
84
+ }
85
+
86
+ useEffect(() => {
87
+ load();
88
+ }, []);
89
+
90
+ async function submit(e) {
91
+ e.preventDefault();
92
+ if (!content.trim()) return;
93
+ setLoading(true);
94
+ try {
95
+ const res = await fetch(`${import.meta.env.BASE_URL}api/messages`, {
96
+ method: 'POST',
97
+ headers: { 'Content-Type': 'application/json' },
98
+ body: JSON.stringify({ name, content }),
99
+ });
100
+ if (!res.ok) {
101
+ const body = await res.json().catch(() => ({}));
102
+ throw new Error(body.message || `HTTP ${res.status}`);
103
+ }
104
+ setContent('');
105
+ await load();
106
+ } catch (e) {
107
+ setError(`提交失败: ${e.message}`);
108
+ } finally {
109
+ setLoading(false);
110
+ }
111
+ }
112
+
113
+ return (
114
+ <div style={styles.page}>
115
+ <div style={styles.card}>
116
+ <h1 style={styles.title}>留言板</h1>
117
+ <p style={styles.subtitle}>
118
+ 前端 React → NestJS → Supabase 全链路连通性测试
119
+ </p>
120
+
121
+ <form style={styles.form} onSubmit={submit}>
122
+ <input
123
+ style={styles.input}
124
+ placeholder="你的名字(可选)"
125
+ value={name}
126
+ onChange={(e) => setName(e.target.value)}
127
+ />
128
+ <textarea
129
+ style={styles.textarea}
130
+ placeholder="写点什么…"
131
+ value={content}
132
+ onChange={(e) => setContent(e.target.value)}
133
+ />
134
+ <div style={styles.row}>
135
+ <button style={styles.button} type="submit" disabled={loading}>
136
+ {loading ? '提交中…' : '提交留言'}
137
+ </button>
138
+ </div>
139
+ </form>
140
+
141
+ {error && <div style={styles.error}>{error}</div>}
142
+
143
+ {messages.length === 0 ? (
144
+ <div style={styles.empty}>还没有留言,来写第一条吧</div>
145
+ ) : (
146
+ messages.map((m) => (
147
+ <div key={m.id} style={styles.msg}>
148
+ <div style={styles.msgHead}>
149
+ <span style={styles.msgName}>{m.name}</span>
150
+ <span style={styles.msgTime}>
151
+ {new Date(m.created_at).toLocaleString('zh-CN')}
152
+ </span>
153
+ </div>
154
+ <div style={styles.msgBody}>{m.content}</div>
155
+ </div>
156
+ ))
157
+ )}
158
+ </div>
159
+ </div>
160
+ );
11
161
  }
@@ -86,14 +86,7 @@ if [ -n "$DOM" ]; then
86
86
  # 整体失败 → daemon runner 走 publish-failed 路径 + 回灌 chat 让 assistant 接管修复。
87
87
  # 之前的 `|| true` 兜底已去掉,理由:线上挂了 prodUrl 仍写盘 = 老板看到"打开线上"按钮
88
88
  # 但点开 404,不如 fail-loud。
89
- #
90
- # 显式走 bash 解释器调用(不依赖 verify.sh 自己的 +x 权限):
91
- # daemon 调 publish.sh / new-extension.sh 都是 `spawn('bash', [scriptPath, ...])`,所以即使脚本
92
- # +x bit 丢了(历史上有过:OTA 解包 / cp -p 未带 / desktop 旧包 mode 不对)也能跑。但这一行
93
- # 之前是 direct-exec `"$KIT_DIR/scripts/verify.sh"`,OS 会检查 +x —— verify.sh 因此成为唯一
94
- # 暴露 +x 丢失问题的执行点(老板撞到过一次"verify.sh permission denied")。
95
- # 改 bash 调用后永远不再依赖 fs +x bit。
96
- bash "$KIT_DIR/scripts/verify.sh" "http://$DOM"
89
+ "$KIT_DIR/scripts/verify.sh" "http://$DOM"
97
90
  else
98
91
  # 域名没拿到 = 部署本身有问题(fc3-domain 未成功),也算失败
99
92
  echo "❌ 部署完成但未取到自定义域名,检查 fc3-domain 是否成功" >&2
@@ -24,34 +24,43 @@ set -a; . "$PERSONA_DIR/.secrets/aliyun.env"; set +a
24
24
  echo "==> 删 FC 资源: $APP_NAME"
25
25
  ( cd "$EXT_DIR" && s remove -y )
26
26
 
27
- # 2) Supabase 表(仅当 ext.conf 声明了 SUPABASE_TABLES)
27
+ # 2) 列出要删的 Supabase 表(脚本不直接调 DB,改打印 DROP 让 agent 用 supabase MCP 跑)
28
+ #
29
+ # 历史 bug(2026-06-03 老板手测 + persona-app-builder 自查发现):旧实现走 python3 读
30
+ # .mcp.json 取 SUPABASE_ACCESS_TOKEN + curl https://api.supabase.com/v1/... 删表,三个
31
+ # 错叠在一起:
32
+ # ① 当前 .mcp.json 用阿里云 RDS 风格(--aliyun-ak 命令行参数),没有 env.SUPABASE_ACCESS_TOKEN
33
+ # 字段 → python3 KeyError → set -euo pipefail exit
34
+ # ② https://api.supabase.com 是云上 supabase.com 的 management API,跟阿里云自建实例
35
+ # 120.26.157.138 不通(即便通,删的也是云上早废弃的表)
36
+ # ③ SUPABASE_PROJECT_REF 是云上 project-ref 概念,阿里云自建用 instance_name
37
+ # 结果:第 1 步删 FC 一直对,第 2 步从来没真删过表(脚本提前 crash exit)。
38
+ #
39
+ # 修法:bash 调不到 MCP(MCP 是 LLM 通道),pg 直连要密码 agent 拿不到,PostgREST RPC 又
40
+ # 要装 drop_table_safe + 手刷 schema cache。最干净:打印 DROP,让 agent 看到 Bash tool
41
+ # stdout 后用 mcp__supabase__execute_sql 接力。长期方案是参照 publish.sh 模式做
42
+ # appBuilder.delete RPC + UI 按钮,等下一轮做。
28
43
  if [ -n "$SUPABASE_TABLES" ]; then
29
- echo ""
30
- echo "⚠️ 即将删除以下 Supabase 表(数据不可逆):"
31
- for t in $SUPABASE_TABLES; do echo " - public.$t"; done
32
-
33
44
  # 安全锁:每个表必须 <app>_ 前缀,否则拒绝(防误伤 clawos 共享库)
34
45
  for t in $SUPABASE_TABLES; do
35
46
  case "$t" in
36
47
  ${APP_NAME}_*) ;;
37
- *) echo "❌ 表 '$t' 不是 '${APP_NAME}_' 前缀,拒绝删除(可能误伤 clawos)"; exit 1 ;;
48
+ *) echo "❌ 表 '$t' 不是 '${APP_NAME}_' 前缀,拒绝输出(可能误伤 clawos)"; exit 1 ;;
38
49
  esac
39
50
  done
40
51
 
41
- read -r -p "确认删表? 输入 yes 继续: " ans
42
- if [ "$ans" = "yes" ]; then
43
- TOKEN="$(python3 -c "import json;print(json.load(open('$PERSONA_DIR/.mcp.json'))['mcpServers']['supabase']['env']['SUPABASE_ACCESS_TOKEN'])")"
44
- for t in $SUPABASE_TABLES; do
45
- curl -s -X POST "https://api.supabase.com/v1/projects/$SUPABASE_PROJECT_REF/database/query" \
46
- -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
47
- -d "{\"query\":\"drop table if exists public.$t cascade;\"}" >/dev/null \
48
- && echo " ✓ dropped public.$t"
49
- done
50
- else
51
- echo "跳过删表(FC 已删;表保留)。"
52
- fi
52
+ echo ""
53
+ echo "==> ⚠️ 下面是要删的 Supabase 表,脚本不直接调 DB"
54
+ echo " (阿里云自建无 management API + bash 调不到 MCP)"
55
+ echo ""
56
+ for t in $SUPABASE_TABLES; do
57
+ echo " DROP TABLE IF EXISTS public.$t CASCADE;"
58
+ done
59
+ echo ""
60
+ echo "==> agent: 上面这几条 DROP 请用 supabase MCP 跑(mcp__supabase__execute_sql),"
61
+ echo " 完了项目清理才算完成"
53
62
  else
54
- echo "ext.conf 未声明 SUPABASE_TABLES,跳过删表。如有表请手动确认清理。"
63
+ echo "ext.conf 未声明 SUPABASE_TABLES,无表要清理。"
55
64
  fi
56
65
 
57
66
  echo "✅ $APP_NAME 清理完成。"
@@ -0,0 +1,15 @@
1
+ {
2
+ "_comment": "preinstall ship 进 daemon defaults。跟 persona-app-builder / persona-developer 同一份配置——共享同一台阿里云自建 supabase(120.26.157.138)+ 同一把阿里云 RDS AK(共享 demo 凭证,跟 .secrets/aliyun.env 同性质,PR #808 决策前提)。daemon refreshDaemonManagedDirs 把这文件同步到 ~/.clawd*/personas/persona-bug-fixer/.mcp.json。cc CLI 在 persona dir 自动加载 → assistant 直接可调 mcp__supabase__*。",
3
+ "mcpServers": {
4
+ "supabase": {
5
+ "type": "stdio",
6
+ "command": "npx",
7
+ "args": [
8
+ "@aliyun-rds/supabase-mcp-server",
9
+ "--aliyun-ak", "LTAI5tSebp9pyBLk2nxF5wDg",
10
+ "--aliyun-sk", "DnkOBhZ9pLympYlwcLuQbsaGRIitwG",
11
+ "--aliyun-region", "cn-hangzhou"
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -91,32 +91,10 @@ done
91
91
 
92
92
  **ALWAYS**:增量提交可工作的代码 / 跟随项目模式 / 3 次失败停下重新评估 / 修完写回归测试 / **动手前自问"我有几成把握",< 70% 先加日志再说**
93
93
 
94
- ## 可选:按需配 MCP server
94
+ ## MCP server
95
95
 
96
- 这个 persona 没有强依赖任何 MCP server。如果修 bug 时需要操作 supabase / 其他数据源辅助复现或排查,按需在 **persona 目录**下创建 `.mcp.json`(注意是 persona 目录,不是项目目录,这样只对当前 persona 生效):
96
+ **已 preinstall:阿里云自建 supabase MCP**。daemon ship `.mcp.json` persona 目录,cc 启动时自动加载,assistant 直接可调 `mcp__supabase__*` 工具(list_tables / execute_sql / get_logs 等)操作 clawos 共享阿里云 supabase(`120.26.157.138`)—— bug 复现需要查 DB / 看日志时直接用。
97
97
 
98
- ```bash
99
- cat > ~/.clawd/personas/persona-bug-fixer/.mcp.json <<'EOF'
100
- {
101
- "mcpServers": {
102
- "supabase": {
103
- "command": "npx",
104
- "args": [
105
- "-y",
106
- "@supabase/mcp-server-supabase@latest",
107
- "--project-ref=<你的 project ref>"
108
- ],
109
- "env": {
110
- "SUPABASE_ACCESS_TOKEN": "<你的 access token>"
111
- }
112
- }
113
- }
114
- }
115
- EOF
116
- ```
117
-
118
- - `<你的 project ref>`:在 supabase dashboard 项目设置里找
119
- - `<你的 access token>`:去 https://supabase.com/dashboard/account/tokens 创建
120
- - 其他 MCP server 同理:参考各 server 自己的 npm package README
98
+ ⚠️ **`.mcp.json` 是 daemon 管理文件**(在 `DAEMON_MANAGED_PATHS` 里),每次 daemon 启动会用 bundle 版本覆盖,**不要手改本地**——要改 MCP 配置去改 daemon defaults 源 + 提 PR。
121
99
 
122
- **安全**:`.mcp.json` secret,**禁止** commit git / 上传到任何分享通道。persona 目录默认不在 git 仓库里,但如果老板把整个 `~/.clawd/` 同步到云盘/备份,注意排除 `.mcp.json`。
100
+ **要加别的 MCP server**(项目专属 / 个人用):在**项目目录**(不是 persona 目录)下加 `.mcp.json`,跟 persona 这份不冲突。或者扩 daemon defaults 给所有用户。
@@ -0,0 +1,15 @@
1
+ {
2
+ "_comment": "preinstall ship 进 daemon defaults。跟 persona-app-builder / persona-bug-fixer 同一份配置——共享同一台阿里云自建 supabase(120.26.157.138)+ 同一把阿里云 RDS AK(共享 demo 凭证,跟 .secrets/aliyun.env 同性质,PR #808 决策前提)。daemon refreshDaemonManagedDirs 把这文件同步到 ~/.clawd*/personas/persona-developer/.mcp.json。cc CLI 在 persona dir 自动加载 → assistant 直接可调 mcp__supabase__*。",
3
+ "mcpServers": {
4
+ "supabase": {
5
+ "type": "stdio",
6
+ "command": "npx",
7
+ "args": [
8
+ "@aliyun-rds/supabase-mcp-server",
9
+ "--aliyun-ak", "LTAI5tSebp9pyBLk2nxF5wDg",
10
+ "--aliyun-sk", "DnkOBhZ9pLympYlwcLuQbsaGRIitwG",
11
+ "--aliyun-region", "cn-hangzhou"
12
+ ]
13
+ }
14
+ }
15
+ }
@@ -95,32 +95,10 @@ done
95
95
 
96
96
  **ALWAYS**:增量提交可工作的代码 / 从既有实现学 / 3 次失败停下重新评估 / 错误处理快速失败 + 描述清楚 + 不静默吞异常
97
97
 
98
- ## 可选:按需配 MCP server
98
+ ## MCP server
99
99
 
100
- 这个 persona 没有强依赖任何 MCP server。如果老板的项目需要操作 supabase / 其他数据源,按需在 **persona 目录**下创建 `.mcp.json`(注意是 persona 目录,不是项目目录,这样只对当前 persona 生效):
100
+ **已 preinstall:阿里云自建 supabase MCP**。daemon ship `.mcp.json` persona 目录,cc 启动时自动加载,assistant 直接可调 `mcp__supabase__*` 工具(list_tables / execute_sql / apply_migration 等)操作 clawos 共享阿里云 supabase(`120.26.157.138`)。
101
101
 
102
- ```bash
103
- cat > ~/.clawd/personas/persona-developer/.mcp.json <<'EOF'
104
- {
105
- "mcpServers": {
106
- "supabase": {
107
- "command": "npx",
108
- "args": [
109
- "-y",
110
- "@supabase/mcp-server-supabase@latest",
111
- "--project-ref=<你的 project ref>"
112
- ],
113
- "env": {
114
- "SUPABASE_ACCESS_TOKEN": "<你的 access token>"
115
- }
116
- }
117
- }
118
- }
119
- EOF
120
- ```
121
-
122
- - `<你的 project ref>`:在 supabase dashboard 项目设置里找
123
- - `<你的 access token>`:去 https://supabase.com/dashboard/account/tokens 创建
124
- - 其他 MCP server 同理:参考各 server 自己的 npm package README
102
+ ⚠️ **`.mcp.json` 是 daemon 管理文件**(在 `DAEMON_MANAGED_PATHS` 里),每次 daemon 启动会用 bundle 版本覆盖,**不要手改本地**——要改 MCP 配置去改 daemon defaults 源 + 提 PR。
125
103
 
126
- **安全**:`.mcp.json` secret,**禁止** commit git / 上传到任何分享通道。persona 目录默认不在 git 仓库里,但如果老板把整个 `~/.clawd/` 同步到云盘/备份,注意排除 `.mcp.json`。
104
+ **要加别的 MCP server**(项目专属 / 个人用):在**项目目录**(不是 persona 目录)下加 `.mcp.json`,跟 persona 这份不冲突。或者扩 daemon defaults 给所有用户。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.124",
3
+ "version": "0.2.126-beta.252.5b08fb0",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",