@clawos-dev/clawd 0.2.520 → 0.2.522

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 (28) hide show
  1. package/dist/app-builder-plugin/mcp-server.cjs +65 -19
  2. package/dist/cli.cjs +20 -23
  3. package/dist/deploy-kit/scripts/ensure-credentials.spec.ts +139 -18
  4. package/dist/deploy-kit/scripts/new-extension.sh +6 -2
  5. package/dist/deploy-kit/scripts/publish.sh +32 -9
  6. package/dist/deploy-kit/scripts/publish.spec.ts +72 -0
  7. package/dist/deploy-kit/scripts/remove-extension.sh +36 -9
  8. package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/SKILL.md +20 -4
  9. package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/collect-files.mjs +2 -2
  10. package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/list-projects.mjs +4 -1
  11. package/dist/persona-defaults/persona-app-builder/.claude/skills/app-builder-projects/scripts/render-sql.mjs +64 -0
  12. package/dist/persona-defaults/persona-app-builder/CLAUDE.md +19 -14
  13. package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +1 -1
  14. package/dist/persona-defaults/persona-app-builder/extension-kit/contract/s.yaml.tmpl +8 -3
  15. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/ext.conf +3 -1
  16. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.env.example +4 -0
  17. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.fcignore +1 -0
  18. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/db/schema.sql +15 -0
  19. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +3 -1
  20. package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/table-name.ts +19 -0
  21. package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/SKILL.md +20 -4
  22. package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/collect-files.mjs +2 -2
  23. package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/list-projects.mjs +4 -1
  24. package/dist/persona-defaults/persona-dataclaw-builder/.claude/skills/app-builder-projects/scripts/render-sql.mjs +64 -0
  25. package/dist/persona-defaults/persona-dataclaw-builder/CLAUDE.md +4 -2
  26. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/README.md +1 -1
  27. package/dist/persona-defaults/persona-dataclaw-builder/extension-kit/contract/s.yaml.tmpl +15 -6
  28. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // 把项目目录里该进仓库的文件收成 GitHub MCP `push_files` 要的 `[{ path, content }]`。
3
3
  //
4
- // 过滤规则 = 项目根 `.gitignore`(模板那份:node_modules/ dist/ .env .publish.log s.yaml server/bootstrap .s/ …)
4
+ // 过滤规则 = 项目根 `.gitignore`(模板那份:node_modules/ dist/ .env .publish.log s.yaml s.*.yaml server/bootstrap .s/ …)
5
5
  // + 永远排掉 `.git` 和 `.clawd-project.json`(插件的记账文件:publish / record-push 都会改它,进了仓库或哈希就会
6
- // 「每推一次都变一次」,而且里面的 prodUrl / pushedCommit 也不是源码)。**不要**在这里另写一份排除表:publish.sh 渲进项目目录的 s.yaml 含 supabase key,
6
+ // 「每推一次都变一次」,而且里面的 deployments / pushedCommit 也不是源码)。**不要**在这里另写一份排除表:publish.sh 渲进项目目录的 s.int.yaml / s.prod.yaml 含 supabase key,
7
7
  // 它进不进仓库只该由 .gitignore 一处决定。
8
8
  // 二进制(前 8KB 含 NUL)和超过 512KB 的文件跳过并列进 `skipped`——push_files 只收文本,
9
9
  // 而且每个字节都要经模型上下文走一遍;agent 看到 skipped 里有该进仓库的东西要自己判断。
@@ -24,7 +24,10 @@ for (const dirName of names) {
24
24
  try {
25
25
  const value = JSON.parse(await readFile(join(projectsRoot, dirName, '.clawd-project.json'), 'utf8'))
26
26
  if (value?.name !== dirName || typeof value.createdAt !== 'string') continue
27
- projects.push({ ...value, projectDir: join(projectsRoot, dirName) })
27
+ // 双环境后发布记录在 deployments.{integration,prod};prodUrl 保留作兼容字段 = prod 那份的 url。
28
+ // 插件还没读写过的老文件顶层就有 prodUrl,原样带出。
29
+ const prodUrl = value.deployments?.prod?.url
30
+ projects.push({ ...value, ...(prodUrl ? { prodUrl } : {}), projectDir: join(projectsRoot, dirName) })
28
31
  } catch {
29
32
  // User-owned directories without valid app-builder metadata are not projects.
30
33
  }
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // 把项目的 server/db/schema.sql 渲成某个环境的 SQL 打到 stdout:`__TABLE_PREFIX__` → 该环境的表前缀。
3
+ // 表结构只有这一份源,integration / prod 各渲一次再喂给 supabase MCP 的 execute_sql——两套表结构
4
+ // 不会漂移。前缀口径与 publish.sh / remove-extension.sh 相同:
5
+ // integration → <APP_NAME>_<SLUG>_int_ prod → <APP_NAME>_<SLUG>_
6
+ // ext.conf 没 SLUG 的老项目退成 <APP_NAME>_int_ / <APP_NAME>_。
7
+ import { readFile } from 'node:fs/promises'
8
+ import { join } from 'node:path'
9
+
10
+ const ENVS = ['integration', 'prod']
11
+ const PLACEHOLDER = '__TABLE_PREFIX__'
12
+
13
+ function arg(name) {
14
+ const i = process.argv.indexOf(name)
15
+ return i >= 0 ? process.argv[i + 1] : undefined
16
+ }
17
+
18
+ const projectDir = arg('--project')
19
+ const env = arg('--env')
20
+ if (!projectDir || !env || !ENVS.includes(env)) {
21
+ process.stderr.write('usage: render-sql.mjs --project <projectDir> --env integration|prod\n')
22
+ process.exit(2)
23
+ }
24
+
25
+ function fail(msg) {
26
+ process.stderr.write(`${msg}\n`)
27
+ process.exit(1)
28
+ }
29
+
30
+ /** ext.conf 是 bash 赋值文件;这里只认 `KEY=value` / `KEY="value"` 单行,够 APP_NAME / SLUG 用。 */
31
+ function readConf(text) {
32
+ const out = {}
33
+ for (const line of text.split('\n')) {
34
+ const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line.trim())
35
+ if (!m) continue
36
+ out[m[1]] = m[2].replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1')
37
+ }
38
+ return out
39
+ }
40
+
41
+ const confPath = join(projectDir, 'ext.conf')
42
+ const schemaPath = join(projectDir, 'server', 'db', 'schema.sql')
43
+
44
+ let conf
45
+ try {
46
+ conf = readConf(await readFile(confPath, 'utf8'))
47
+ } catch (error) {
48
+ fail(`读不到 ${confPath}:${error?.message ?? error}`)
49
+ }
50
+ if (!conf.APP_NAME) fail(`${confPath} 里没有 APP_NAME,算不出表前缀`)
51
+
52
+ let schema
53
+ try {
54
+ schema = await readFile(schemaPath, 'utf8')
55
+ } catch (error) {
56
+ fail(`读不到 ${schemaPath}:${error?.message ?? error}(表结构应写在 server/db/schema.sql,表名用 ${PLACEHOLDER}visits 这种占位)`)
57
+ }
58
+ if (!schema.includes(PLACEHOLDER)) {
59
+ fail(`${schemaPath} 里没有 ${PLACEHOLDER}:表名不要写死,写成 ${PLACEHOLDER}<表名>,两个环境才能各渲各的`)
60
+ }
61
+
62
+ const base = conf.SLUG ? `${conf.APP_NAME}_${conf.SLUG}_` : `${conf.APP_NAME}_`
63
+ const prefix = env === 'integration' ? `${base}int_` : base
64
+ process.stdout.write(schema.replaceAll(PLACEHOLDER, prefix))
@@ -8,7 +8,7 @@
8
8
 
9
9
  一个 app = 当前用户工作区下的 `projects/<name>/`,配一个 GitHub 组织 `ottin4ttc` 下的私有仓库(`<name>-<slug>`),仓库由你经 **`github` MCP** 建和提交,本机不需要 `gh`、项目里不需要 git。会话不绑项目:一个会话里可以改多个 app,隔几天新开会话改旧 app 用 skill 的 `list-projects.mjs` 找目录。没有右栏预览、没有 dev server 概念,验证靠发布后的公网链接。
10
10
 
11
- 宿主侧 **`clawd-app-builder` MCP** 只有 3 个 tool:`createProject({name})` / `publish({name})` / `removeProject({name})`。没有 `listProjects`(走脚本),没有任何 `appBuilder:*` RPC。
11
+ 宿主侧 **`clawd-app-builder` MCP** 只有 3 个 tool:`createProject({name})` / `publish({name, env})` / `removeProject({name})`。没有 `listProjects`(走脚本),没有任何 `appBuilder:*` RPC。
12
12
 
13
13
  ### 新建
14
14
 
@@ -27,7 +27,7 @@
27
27
  ```
28
28
  key 只存这里:dev 由 dotenv 加载、发布 FC 时 pre-deploy 自动从这读出注入 FC(`.env` 被 `.fcignore` / `.gitignore` 排除,不进部署包、`collect-files.mjs` 也不会把它推上 GitHub)。老板要换 key → 重写本文件
29
29
  7. 按需求改数据集 / 端点 / 展示;每完成一段按 skill 推一次:`collect-files.mjs` → `push_files` → `record-push.mjs`。不调 `gh`、不 `git push`
30
- 8. `publish({ name })` 拿到 `prodUrl`,回复里给老板可点的链接;老板看链接提意见,回到第 7 步
30
+ 8. 先 `publish({ name, env: "integration" })` 拿到测试链接给老板看;老板点头再 `publish({ name, env: "prod" })` 给正式链接。老板看链接提意见,回到第 7 步
31
31
 
32
32
  ### 改旧页面
33
33
 
@@ -35,6 +35,8 @@
35
35
 
36
36
  ### 发布
37
37
 
38
+ **上线两步走**:先 `publish({ name, env: "integration" })`,把测试链接给老板;老板确认没问题后再 `publish({ name, env: "prod" })`。prod 只接受 integration 发过的同一个 commit,tool 会机械拒绝(`env` 必填,没有默认值、没有绕过开关)。本 persona 不用 Supabase,表两套那条与你无关。
39
+
38
40
  **只走 `publish` tool,不手跑 `publish.sh`**(凭据只在 tool 进程的 env 里)。发布前先把改动推到仓库并 `record-push.mjs`——`publish` 会重算文件树哈希跟记账的比,有没推的改动直接拒。失败时读项目目录下的 `.publish.log`,修好 → 推 → 再 `publish`。本地想先看一眼:`cd server && pnpm dev` + `cd web && pnpm dev`(`/api` 代理到 3000),看完停掉。
39
41
 
40
42
  ### 删页面
@@ -40,7 +40,7 @@ PR="$HOME/.clawd/personas/persona-dataclaw-builder"; DK="$HOME/.clawd/deploy-kit
40
40
  # 1) scaffold(daemon 已自动跑;手动签名: new-extension.sh <name> <模板源目录> <目标目录>)
41
41
  # 2) 改 web/ 和 server/ 业务逻辑(只读查 dataclaw 公网 API,不接 Supabase 写库)
42
42
  # 3) 一键发布到 FC,输出公网地址(apikey 在建页时已写进生成 app 的 server/.env,无需额外配置)
43
- "$DK/scripts/publish.sh" <projDir> "$PR"
43
+ "$DK/scripts/publish.sh" <projDir> "$PR" integration # 第 3 参 env:integration | prod(必填)
44
44
  # 4) 不要了就一键清理 FC 资源
45
45
  "$DK/scripts/remove-extension.sh" <projDir> "$PR"
46
46
  ```
@@ -1,7 +1,14 @@
1
1
  # s.yaml 模板 —— publish.sh 用 sed 替换 __占位符__ 后生成实际 s.yaml
2
2
  # 框架无关:command 固定走 ./bootstrap,入口由 FC_ENTRY 环境变量决定
3
+ #
4
+ # 占位符约定:
5
+ # __APP_NAME__ = 用户给的项目名(描述/可读用)
6
+ # __DEPLOY_NAME__ = APP_NAME-<4位 slug>[-int],FC functionName / s 项目名 / 资源 key 走这个;
7
+ # integration 环境带 -int 标签,prod 不带
8
+ # __APP_ENV__ = integration | prod
9
+ # __TABLE_PREFIX__ = APP_NAME_<slug>_[int_](本 persona 不用 supabase,随共享 publish.sh 一起注入)
3
10
  edition: 3.0.0
4
- name: __APP_NAME__-app
11
+ name: __DEPLOY_NAME__-app
5
12
  # clawd 自己的 Serverless Devs profile,写在 deploy-kit 私有 s-home 里(见 scripts/ensure-credentials.sh)。
6
13
  # 刻意不用 default —— 那是用户自己账号的 profile,不该被 clawd 读也不该被 clawd 写。
7
14
  access: clawd-fc
@@ -10,12 +17,12 @@ vars:
10
17
  region: __REGION__
11
18
 
12
19
  resources:
13
- __APP_NAME__:
20
+ __DEPLOY_NAME__:
14
21
  component: fc3
15
22
  props:
16
23
  region: ${vars.region}
17
- functionName: __APP_NAME__
18
- description: __APP_NAME__ extension (FC serverless)
24
+ functionName: __DEPLOY_NAME__
25
+ description: __APP_NAME__ extension (__APP_ENV__)
19
26
  runtime: __FC_RUNTIME__
20
27
  logConfig: auto
21
28
  layers:
@@ -35,6 +42,8 @@ resources:
35
42
  APP_ENTRY: __FC_ENTRY__
36
43
  DATACLAW_SERVICE_URL: __DATACLAW_SERVICE_URL__
37
44
  DATACLAW_API_KEY: __DATACLAW_API_KEY__
45
+ APP_ENV: __APP_ENV__
46
+ TABLE_PREFIX: __TABLE_PREFIX__
38
47
  triggers:
39
48
  - triggerName: httpTrigger
40
49
  triggerType: http
@@ -43,7 +52,7 @@ resources:
43
52
  methods: [GET, POST, PUT, DELETE, HEAD]
44
53
 
45
54
  # 自定义域名:否则 fcapp.run 默认 URL 强制下载网页(防滥用),浏览器无法渲染
46
- __APP_NAME__Domain:
55
+ __DEPLOY_NAME__Domain:
47
56
  component: fc3-domain
48
57
  props:
49
58
  region: ${vars.region}
@@ -52,5 +61,5 @@ resources:
52
61
  routeConfig:
53
62
  routes:
54
63
  - path: /*
55
- functionName: __APP_NAME__
64
+ functionName: __DEPLOY_NAME__
56
65
  qualifier: LATEST
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.520",
3
+ "version": "0.2.522",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",