@coze-arch/cli 0.0.36-alpha.9ee522 → 0.0.36-alpha.af24fb

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 (109) hide show
  1. package/README.md +65 -114
  2. package/docs/deploy.md +2 -1
  3. package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +3 -2
  4. package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +1 -1
  5. package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +1 -1
  6. package/lib/__templates__/expo/client/metro.config.js +3 -1
  7. package/lib/__templates__/expo/pnpm-lock.yaml +5 -5
  8. package/lib/__templates__/expo/server/package.json +2 -2
  9. package/lib/__templates__/expo/server/src/index.ts +1 -1
  10. package/lib/__templates__/expo/template.config.js +11 -2
  11. package/lib/__templates__/native-static/.coze +2 -0
  12. package/lib/__templates__/native-static/scripts/serve.ps1 +7 -0
  13. package/lib/__templates__/nextjs/.coze +5 -0
  14. package/lib/__templates__/nextjs/package.json +1 -1
  15. package/lib/__templates__/nextjs/pnpm-lock.yaml +5 -5
  16. package/lib/__templates__/nextjs/scripts/build.ps1 +16 -0
  17. package/lib/__templates__/nextjs/scripts/dev.ps1 +63 -0
  18. package/lib/__templates__/nextjs/scripts/dev.sh +81 -9
  19. package/lib/__templates__/nextjs/scripts/prepare.ps1 +18 -0
  20. package/lib/__templates__/nextjs/scripts/start.ps1 +11 -0
  21. package/lib/__templates__/nextjs/scripts/validate.ps1 +8 -0
  22. package/lib/__templates__/nextjs/template.config.js +1 -1
  23. package/lib/__templates__/nuxt-vue/.coze +5 -0
  24. package/lib/__templates__/nuxt-vue/package.json +1 -1
  25. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +5 -5
  26. package/lib/__templates__/nuxt-vue/scripts/build.ps1 +12 -0
  27. package/lib/__templates__/nuxt-vue/scripts/dev.ps1 +63 -0
  28. package/lib/__templates__/nuxt-vue/scripts/dev.sh +82 -1
  29. package/lib/__templates__/nuxt-vue/scripts/prepare.ps1 +12 -0
  30. package/lib/__templates__/nuxt-vue/scripts/start.ps1 +11 -0
  31. package/lib/__templates__/nuxt-vue/scripts/validate.ps1 +8 -0
  32. package/lib/__templates__/phaser/.coze +5 -0
  33. package/lib/__templates__/phaser/README.md +8 -0
  34. package/lib/__templates__/phaser/scripts/build.ps1 +12 -0
  35. package/lib/__templates__/phaser/scripts/dev.ps1 +49 -0
  36. package/lib/__templates__/phaser/scripts/prepare.ps1 +8 -0
  37. package/lib/__templates__/phaser/scripts/start.ps1 +49 -0
  38. package/lib/__templates__/phaser/scripts/validate.ps1 +7 -0
  39. package/lib/__templates__/phaser/src/global.d.ts +3 -0
  40. package/lib/__templates__/phaser/src/scene/BootScene.ts +4 -0
  41. package/lib/__templates__/phaser/src/scene/DevLoadingScene.ts +3 -0
  42. package/lib/__templates__/phaser/src/utils/mark-editable.ts +8 -1
  43. package/lib/__templates__/phaser/vite.config.ts +1 -1
  44. package/lib/__templates__/phaser-static/.coze +15 -0
  45. package/lib/__templates__/phaser-static/README.md +40 -0
  46. package/lib/__templates__/phaser-static/_gitignore +6 -0
  47. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
  48. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
  49. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
  50. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
  51. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
  52. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
  53. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
  54. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
  55. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
  56. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
  57. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
  58. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
  59. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
  60. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
  61. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
  62. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
  63. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
  64. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
  65. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
  66. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
  67. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
  68. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
  69. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
  70. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
  71. package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/manifest.json +11 -0
  72. package/lib/__templates__/phaser-static/debug-runtime/flow-graph.json +20 -0
  73. package/lib/__templates__/phaser-static/index.html +17 -0
  74. package/lib/__templates__/phaser-static/scripts/serve.ps1 +7 -0
  75. package/lib/__templates__/phaser-static/src/assets.js +15 -0
  76. package/lib/__templates__/phaser-static/src/main.js +26 -0
  77. package/lib/__templates__/phaser-static/src/phaser.js +7 -0
  78. package/lib/__templates__/phaser-static/src/scene/BootScene.js +205 -0
  79. package/lib/__templates__/phaser-static/src/scene/DevLoadingScene.js +67 -0
  80. package/lib/__templates__/phaser-static/src/style.css +28 -0
  81. package/lib/__templates__/phaser-static/src/utils/asset-loader.js +267 -0
  82. package/lib/__templates__/phaser-static/src/utils/index.js +2 -0
  83. package/lib/__templates__/phaser-static/src/utils/mark-editable.js +3 -0
  84. package/lib/__templates__/phaser-static/template.config.js +35 -0
  85. package/lib/__templates__/pi-agent/package.json +1 -1
  86. package/lib/__templates__/pi-agent/pnpm-lock.yaml +5 -5
  87. package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +1 -1
  88. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +144 -68
  89. package/lib/__templates__/taro/AGENTS.md +11 -7
  90. package/lib/__templates__/taro/README.md +4 -4
  91. package/lib/__templates__/taro/config/index.ts +10 -2
  92. package/lib/__templates__/taro/pnpm-lock.yaml +5 -5
  93. package/lib/__templates__/taro/server/package.json +1 -1
  94. package/lib/__templates__/taro/server/src/main.ts +8 -2
  95. package/lib/__templates__/templates.json +26 -1
  96. package/lib/__templates__/vite/.coze +5 -0
  97. package/lib/__templates__/vite/package.json +1 -1
  98. package/lib/__templates__/vite/pnpm-lock.yaml +5 -5
  99. package/lib/__templates__/vite/scripts/build.ps1 +16 -0
  100. package/lib/__templates__/vite/scripts/dev.ps1 +63 -0
  101. package/lib/__templates__/vite/scripts/dev.sh +82 -1
  102. package/lib/__templates__/vite/scripts/prepare.ps1 +18 -0
  103. package/lib/__templates__/vite/scripts/start.ps1 +11 -0
  104. package/lib/__templates__/vite/scripts/validate.ps1 +8 -0
  105. package/lib/__templates__/vite/template.config.js +1 -1
  106. package/lib/cli.js +288 -212
  107. package/lib/deploy/package-project.js +22 -4
  108. package/lib/deploy/package-project.py +18 -3
  109. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export * from './asset-loader.js';
2
+ export * from './mark-editable.js';
@@ -0,0 +1,3 @@
1
+ export const markEditable = (locator, gameObject, options) =>
2
+ window.PhaserBridge?.markEditable?.(locator, gameObject, options) ??
3
+ gameObject;
@@ -0,0 +1,35 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+ export const paramsSchema = {
10
+ type: 'object',
11
+ properties: {
12
+ appName: {
13
+ type: 'string',
14
+ minLength: 1,
15
+ pattern: '^[a-z0-9-]+$',
16
+ description:
17
+ 'Application name (lowercase, alphanumeric and hyphens only)',
18
+ },
19
+ },
20
+ required: [],
21
+ additionalProperties: false,
22
+ };
23
+
24
+ const config = {
25
+ description:
26
+ 'Phaser Static(纯静态 2D 游戏):coze-dev init ${COZE_WORKSPACE_PATH} --template phaser-static\n' +
27
+ '- 适用:浏览器原生 JavaScript ESM 的 Phaser 3 游戏。\n' +
28
+ '- Phaser 与 Phaser Bridge 从 CDN 加载;无需 npm、Vite 或 TypeScript。',
29
+ paramsSchema,
30
+ defaultParams: {
31
+ appName: 'phaser-static-game',
32
+ },
33
+ };
34
+
35
+ export default config;
@@ -22,7 +22,7 @@
22
22
  "@mariozechner/pi-ai": "^0.63.2",
23
23
  "@mariozechner/pi-coding-agent": "^0.63.2",
24
24
  "@sinclair/typebox": "^0.34.48",
25
- "coze-coding-dev-sdk": "^0.7.17",
25
+ "coze-coding-dev-sdk": "^0.7.25-beta.8",
26
26
  "@radix-ui/react-select": "^2.2.6",
27
27
  "@radix-ui/react-slot": "^1.2.4",
28
28
  "@streamdown/code": "^1.1.1",
@@ -36,8 +36,8 @@ importers:
36
36
  specifier: ^2.1.1
37
37
  version: 2.1.1
38
38
  coze-coding-dev-sdk:
39
- specifier: ^0.7.17
40
- version: 0.7.20(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
39
+ specifier: ^0.7.25-beta.8
40
+ version: 0.7.25-beta.8(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
41
41
  express:
42
42
  specifier: ^4.21.2
43
43
  version: 4.22.1
@@ -2085,8 +2085,8 @@ packages:
2085
2085
  cose-base@2.2.0:
2086
2086
  resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
2087
2087
 
2088
- coze-coding-dev-sdk@0.7.20:
2089
- resolution: {integrity: sha512-f3UeZcYB/udyWrlFrsOViM47PCW+Nk0I4FcXEGzcjEfaQsa1CPBpf/y5UbBM6GIZ1v+lnuh/MiBlAaALFeMTqw==}
2088
+ coze-coding-dev-sdk@0.7.25-beta.8:
2089
+ resolution: {integrity: sha512-RSKlXCO0W/OPDjx9D9JICn2cGeOJw3iG+y0Pr1AhAf7YdAjjNbBZoG7qfg0Zg2SqgaVN2qSLQFmKPbhNq9ZWSg==}
2090
2090
  engines: {node: '>=18.0.0'}
2091
2091
  hasBin: true
2092
2092
 
@@ -6120,7 +6120,7 @@ snapshots:
6120
6120
  dependencies:
6121
6121
  layout-base: 2.0.1
6122
6122
 
6123
- coze-coding-dev-sdk@0.7.20(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0):
6123
+ coze-coding-dev-sdk@0.7.25-beta.8(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0):
6124
6124
  dependencies:
6125
6125
  '@langchain/core': 1.1.40(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
6126
6126
  '@langchain/openai': 1.4.3(@langchain/core@1.1.40(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(ws@8.20.0)
@@ -4,7 +4,7 @@ set -Eeuo pipefail
4
4
  start_service() {
5
5
  cd "${COZE_WORKSPACE_PATH}/server/dist"
6
6
 
7
- local port="${DEPLOY_RUN_PORT:-3000}"
7
+ local port="${DEPLOY_RUN_PORT:-<%= serverPort %>}"
8
8
  echo "Starting Static File Server on port ${port} for deploy..."
9
9
 
10
10
  node ./main.js -p "${port}"
@@ -2,14 +2,16 @@
2
2
  echo "⚙️ dev_run.sh 开始运行"
3
3
  set -Eeuo pipefail
4
4
 
5
+ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
6
+ COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-${ROOT_DIR}}"
5
7
  cd "${COZE_WORKSPACE_PATH}"
6
8
 
7
9
  # ---------------------------------------------------------
8
- # PID 文件,用于追踪上一次启动的进程树
10
+ # 项目级日志目录
9
11
  # ---------------------------------------------------------
10
- PID_FILE="/tmp/coze-dev-run.pid"
11
- LOG_DIR="/tmp/coze-logs"
12
+ LOG_DIR="${COZE_WORKSPACE_PATH}/logs"
12
13
  LOG_FILE="${LOG_DIR}/dev.log"
14
+ PID_FILE="${LOG_DIR}/dev.pid"
13
15
  DEV_PID=""
14
16
 
15
17
  # ---------------------------------------------------------
@@ -28,64 +30,136 @@ kill_process_tree() {
28
30
  fi
29
31
  }
30
32
 
33
+ # coze-daemon 会在 runtime shell 退出时清理原进程组。
34
+ # 通过 Node detached spawn 创建独立 session/进程组,并将 stdio 直接写入日志。
35
+ spawn_detached() {
36
+ local cwd="$1"
37
+ local log_file="$2"
38
+ shift 2
39
+
40
+ node - "$cwd" "$log_file" "$@" <<'NODE'
41
+ const fs = require('node:fs');
42
+ const { spawn } = require('node:child_process');
43
+
44
+ const [cwd, logFile, command, ...args] = process.argv.slice(2);
45
+ if (!cwd || !logFile || !command) {
46
+ throw new Error('spawn_detached 缺少 cwd、log_file 或 command');
47
+ }
48
+
49
+ const logFd = fs.openSync(logFile, 'a');
50
+ try {
51
+ const child = spawn(command, args, {
52
+ cwd,
53
+ detached: true,
54
+ env: process.env,
55
+ stdio: ['ignore', logFd, logFd],
56
+ });
57
+ child.unref();
58
+ process.stdout.write(String(child.pid));
59
+ } finally {
60
+ fs.closeSync(logFd);
61
+ }
62
+ NODE
63
+ }
64
+
65
+ stop_detached() {
66
+ local pid="${1:-}"
67
+ if [[ -z "${pid}" ]]; then
68
+ return
69
+ fi
70
+
71
+ kill -TERM -- "-${pid}" 2>/dev/null || kill -TERM "${pid}" 2>/dev/null || true
72
+ sleep 1
73
+ kill -KILL -- "-${pid}" 2>/dev/null || true
74
+ }
75
+
76
+ process_belongs_to_workspace() {
77
+ local pid=$1
78
+ local cwd=""
79
+
80
+ if [[ -L "/proc/${pid}/cwd" ]]; then
81
+ cwd=$(readlink "/proc/${pid}/cwd" 2>/dev/null || true)
82
+ elif command -v lsof >/dev/null 2>&1; then
83
+ cwd=$(lsof -a -p "${pid}" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -1)
84
+ fi
85
+
86
+ case "${cwd}" in
87
+ "${COZE_WORKSPACE_PATH}"|"${COZE_WORKSPACE_PATH}"/*) return 0 ;;
88
+ *) return 1 ;;
89
+ esac
90
+ }
91
+
92
+ process_group_belongs_to_workspace() {
93
+ local pgid=$1
94
+ local pids
95
+ local found=0
96
+
97
+ pids=$(ps -axo pid=,pgid= 2>/dev/null | awk -v pgid="${pgid}" '$2 == pgid {print $1}')
98
+ for pid in ${pids}; do
99
+ found=1
100
+ if ! process_belongs_to_workspace "${pid}"; then
101
+ return 1
102
+ fi
103
+ done
104
+
105
+ [[ "${found}" -eq 1 ]]
106
+ }
107
+
108
+ # 优先用 ss,无结果或缺少 ss 时回落到 lsof;两者都不可用则显式告警,避免静默误判为端口空闲
109
+ list_port_pids() {
110
+ local port=$1
111
+ local pids=""
112
+ local has_tool=0
113
+
114
+ if command -v ss >/dev/null 2>&1; then
115
+ has_tool=1
116
+ pids=$(ss -H -lntp 2>/dev/null | awk -v port="${port}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u | paste -sd' ' - || true)
117
+ fi
118
+ if [[ -z "${pids}" ]] && command -v lsof >/dev/null 2>&1; then
119
+ has_tool=1
120
+ pids=$(lsof -t -iTCP:"${port}" -sTCP:LISTEN 2>/dev/null | sort -u | paste -sd' ' - || true)
121
+ fi
122
+ if [[ "${has_tool}" -eq 0 ]]; then
123
+ echo "Warning: neither ss nor lsof available, cannot inspect port ${port}." >&2
124
+ fi
125
+
126
+ echo "${pids}"
127
+ }
128
+
31
129
  kill_port_if_listening() {
32
130
  local port=$1
33
131
  local pids
34
- pids=$(ss -H -lntp 2>/dev/null | awk -v port="${port}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u | paste -sd' ' - || true)
132
+ pids=$(list_port_pids "${port}")
35
133
  if [[ -z "${pids}" ]]; then
36
134
  echo "Port ${port} is free."
37
135
  return
38
136
  fi
39
- echo "Port ${port} in use by PIDs: ${pids}"
137
+ echo "Port ${port} in use by PIDs: ${pids} (SIGKILL)"
40
138
  for pid in ${pids}; do
41
139
  kill_process_tree "${pid}"
42
140
  done
43
141
  sleep 1
44
- pids=$(ss -H -lntp 2>/dev/null | awk -v port="${port}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u | paste -sd' ' - || true)
142
+ pids=$(list_port_pids "${port}")
45
143
  if [[ -n "${pids}" ]]; then
46
- echo "Warning: port ${port} still busy after cleanup, PIDs: ${pids}"
144
+ echo "Warning: port ${port} still busy after SIGKILL, PIDs: ${pids}"
47
145
  else
48
146
  echo "Port ${port} cleared."
49
147
  fi
50
148
  }
51
149
 
52
- # ---------------------------------------------------------
53
- # 1. 清理上一次运行残留的整棵进程树
54
- # ---------------------------------------------------------
150
+ # 只清理当前项目上次记录的 detached 进程。
55
151
  cleanup_previous_run() {
56
- # 1a. 通过 PID 文件清理上次的进程树
57
- if [[ -f "${PID_FILE}" ]]; then
58
- local old_pid
59
- old_pid=$(cat "${PID_FILE}" 2>/dev/null || true)
60
- if [[ -n "${old_pid}" ]] && kill -0 "${old_pid}" 2>/dev/null; then
61
- echo "🧹 Killing previous dev process tree (root PID: ${old_pid})..."
62
- kill_process_tree "${old_pid}"
63
- fi
64
- rm -f "${PID_FILE}"
152
+ if [[ ! -f "${PID_FILE}" ]]; then
153
+ return
65
154
  fi
66
155
 
67
- # 1b. 兜底:按特征匹配清理所有残留的相关进程(排除自身)
68
- echo "🧹 Cleaning up any orphaned dev processes..."
69
- local patterns=(
70
- "pnpm dev"
71
- "concurrently.*dev:web.*dev:server"
72
- "nest start --watch"
73
- "taro build --type h5 --watch"
74
- "node --enable-source-maps.*/workspace/projects/server/dist/main"
75
- "esbuild --service.*--ping"
76
- )
77
- for pattern in "${patterns[@]}"; do
78
- local pids
79
- pids=$(pgrep -f "${pattern}" 2>/dev/null || true)
80
- for pid in ${pids}; do
81
- # 不杀自己和自己的父进程链
82
- if [[ "${pid}" != "$$" ]] && [[ "${pid}" != "${PPID}" ]]; then
83
- echo " Killing orphan PID ${pid} matching '${pattern}'"
84
- kill -9 "${pid}" 2>/dev/null || true
85
- fi
86
- done
87
- done
88
- sleep 1
156
+ local old_pid
157
+ old_pid=$(cat "${PID_FILE}" 2>/dev/null || true)
158
+ if [[ "${old_pid}" =~ ^[1-9][0-9]*$ ]] && process_group_belongs_to_workspace "${old_pid}"; then
159
+ echo "🧹 Killing previous dev process group (PGID: ${old_pid})..."
160
+ stop_detached "${old_pid}"
161
+ fi
162
+ rm -f "${PID_FILE}"
89
163
  }
90
164
 
91
165
  # ---------------------------------------------------------
@@ -99,32 +173,22 @@ echo "✅ Dependencies installed successfully!"
99
173
  # ---------------------------------------------------------
100
174
  # 3. 清理旧进程 + 端口
101
175
  # ---------------------------------------------------------
102
- SERVER_PORT=3000
176
+ # DEPLOY_RUN_PORT 是平台注入的对外端口,项目内统一使用 PORT。
177
+ PORT="${DEPLOY_RUN_PORT:-${PORT:-<%= port %>}}"
178
+ SERVER_PORT="${SERVER_PORT:-<%= serverPort %>}"
179
+ export PORT
180
+ export SERVER_PORT
103
181
 
182
+ mkdir -p "${LOG_DIR}"
104
183
  cleanup_previous_run
105
184
 
106
- echo "Clearing port ${DEPLOY_RUN_PORT} (web) before start."
107
- kill_port_if_listening "${DEPLOY_RUN_PORT}"
185
+ echo "Clearing port ${PORT} (web) before start."
186
+ kill_port_if_listening "${PORT}"
108
187
  echo "Clearing port ${SERVER_PORT} (server) before start."
109
188
  kill_port_if_listening "${SERVER_PORT}"
110
189
 
111
190
  # ---------------------------------------------------------
112
- # 4. 退出时自动清理子进程(信号 trap)
113
- # ---------------------------------------------------------
114
- cleanup_on_exit() {
115
- local exit_code=$?
116
- trap - EXIT INT TERM HUP
117
- if [[ -n "${DEV_PID}" ]] && kill -0 "${DEV_PID}" 2>/dev/null; then
118
- echo "🛑 dev_run.sh exiting, cleaning up child processes..."
119
- kill_process_tree "${DEV_PID}"
120
- fi
121
- rm -f "${PID_FILE}"
122
- exit "${exit_code}"
123
- }
124
- trap cleanup_on_exit EXIT INT TERM HUP
125
-
126
- # ---------------------------------------------------------
127
- # 5. 启动服务
191
+ # 4. 启动服务
128
192
  # ---------------------------------------------------------
129
193
  start_service() {
130
194
  cd "${COZE_WORKSPACE_PATH}"
@@ -140,18 +204,30 @@ start_service() {
140
204
  # 启动 Taro H5 和 NestJS Server
141
205
  echo "Starting Taro H5 Dev Server and NestJS Server..."
142
206
 
143
- export PORT=${DEPLOY_RUN_PORT}
144
- mkdir -p "${LOG_DIR}"
145
207
  : > "${LOG_FILE}"
146
208
 
147
- # 后台启动以便记录真实 pnpm PID,但脚本持续 wait,保持外层 CLI 生命周期有效。
148
- "${PNPM_BIN}" dev > >(tee -a "${LOG_FILE}") 2>&1 &
149
- DEV_PID=$!
209
+ DEV_PID="$(spawn_detached \
210
+ "${COZE_WORKSPACE_PATH}" \
211
+ "${LOG_FILE}" \
212
+ "${PNPM_BIN}" dev)"
213
+ if [[ -z "${DEV_PID}" ]]; then
214
+ echo "❌ 无法获取 dev 后台进程 PID"
215
+ return 1
216
+ fi
150
217
  echo "${DEV_PID}" > "${PID_FILE}"
151
- echo "📝 Dev process started with PID: ${DEV_PID} (saved to ${PID_FILE})"
152
218
 
153
- wait "${DEV_PID}"
219
+ sleep 1
220
+ if ! kill -0 "${DEV_PID}" 2>/dev/null; then
221
+ echo "❌ Dev service failed to start. See ${LOG_FILE}." >&2
222
+ tail -n 20 "${LOG_FILE}" >&2 || true
223
+ rm -f "${PID_FILE}"
224
+ return 1
225
+ fi
226
+
227
+ echo "📝 Dev process started with PID: ${DEV_PID}"
228
+ echo "Log file: ${LOG_FILE}"
229
+ echo "PID file: ${PID_FILE}"
154
230
  }
155
231
 
156
- echo "Starting HTTP services on port ${DEPLOY_RUN_PORT} (web) and ${SERVER_PORT} (server)..."
232
+ echo "Starting HTTP services on port ${PORT} (web) and ${SERVER_PORT} (server)..."
157
233
  start_service
@@ -361,20 +361,24 @@ if (isExternalUrl) {
361
361
 
362
362
  **工作原理**:
363
363
 
364
- 1. **开发环境(H5)**:使用 `/api/xxx` 相对路径,Vite 的 proxy 会自动将其代理到 `http://localhost:3000/api/xxx`
364
+ 1. **开发环境(H5)**:使用 `/api/xxx` 相对路径,Vite 的 proxy 会自动将其代理到后端;后端端口读取 `SERVER_PORT`,未设置时使用模板默认端口 `<%= serverPort %>`
365
365
  2. **生产环境**:如果配置了 `PROJECT_DOMAIN` 环境变量,Network 会自动拼接为 `${PROJECT_DOMAIN}/api/xxx`
366
366
  3. **小程序端**:同样会根据 `PROJECT_DOMAIN` 配置使用正确的域名
367
367
 
368
368
  **Vite 代理配置**(已在项目中配置,无需修改):
369
369
 
370
370
  ```typescript
371
- // vite.config.ts
371
+ // config/index.ts
372
+ const serverPort = Number(process.env.SERVER_PORT) || <%= serverPort %>
373
+
372
374
  export default {
373
- server: {
374
- proxy: {
375
- '/api': {
376
- target: 'http://localhost:3000',
377
- changeOrigin: true
375
+ h5: {
376
+ devServer: {
377
+ proxy: {
378
+ '/api': {
379
+ target: `http://localhost:${serverPort}`,
380
+ changeOrigin: true
381
+ }
378
382
  }
379
383
  }
380
384
  }
@@ -63,8 +63,8 @@ pnpm install
63
63
  pnpm dev
64
64
  ```
65
65
 
66
- - 前端地址:http://localhost:5000
67
- - 后端地址:http://localhost:3000
66
+ - 前端地址:http://localhost:<%= port %>
67
+ - 后端地址:http://localhost:<%= serverPort %>
68
68
 
69
69
  单独启动:
70
70
 
@@ -560,7 +560,7 @@ async function getLocation(): Promise<Taro.getLocation.SuccessCallbackResult> {
560
560
  ### 开发命令
561
561
 
562
562
  ```sh
563
- pnpm dev:server // 启动开发服务 (热重载, 默认端口 3000)
563
+ pnpm dev:server // 启动开发服务 (热重载, 默认端口 <%= serverPort %>,可用 SERVER_PORT 环境变量覆盖)
564
564
  pnpm build:server // 构建生产版本
565
565
  ```
566
566
 
@@ -586,7 +586,7 @@ npx nest g service modules/order
586
586
 
587
587
  ```sh
588
588
  ## 服务端口
589
- PORT=3000
589
+ SERVER_PORT=<%= serverPort %>
590
590
 
591
591
  ## 微信小程序配置
592
592
  WX_APP_ID=你的AppID
@@ -12,6 +12,14 @@ import pkg from '../package.json';
12
12
 
13
13
  dotenv.config({ path: path.resolve(__dirname, '../.env.local') });
14
14
 
15
+ const resolvePort = (value: string | undefined, fallback: number): number => {
16
+ const port = Number(value);
17
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : fallback;
18
+ };
19
+
20
+ const h5Port = resolvePort(process.env.PORT, <%= port %>);
21
+ const serverPort = resolvePort(process.env.SERVER_PORT, <%= serverPort %>);
22
+
15
23
  const generateTTProjectConfig = (outputRoot: string) => {
16
24
  const config = {
17
25
  miniprogramRoot: './',
@@ -182,12 +190,12 @@ export default defineConfig<'vite'>(async (merge, _env) => {
182
190
  mode: 'hash',
183
191
  },
184
192
  devServer: {
185
- port: <%= port %>,
193
+ port: h5Port,
186
194
  host: '0.0.0.0',
187
195
  open: false,
188
196
  proxy: {
189
197
  '/api': {
190
- target: 'http://localhost:<%= serverPort %>',
198
+ target: `http://localhost:${serverPort}`,
191
199
  changeOrigin: true,
192
200
  },
193
201
  },
@@ -204,8 +204,8 @@ importers:
204
204
  specifier: 2.95.3
205
205
  version: 2.95.3
206
206
  coze-coding-dev-sdk:
207
- specifier: ^0.7.25-beta.4
208
- version: 0.7.25-beta.4(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0)
207
+ specifier: ^0.7.25-beta.8
208
+ version: 0.7.25-beta.8(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0)
209
209
  dotenv:
210
210
  specifier: ^17.2.3
211
211
  version: 17.2.3
@@ -5416,8 +5416,8 @@ packages:
5416
5416
  typescript:
5417
5417
  optional: true
5418
5418
 
5419
- coze-coding-dev-sdk@0.7.25-beta.4:
5420
- resolution: {integrity: sha512-+PCoJ8yhsAIlx3YT898bCkB2gx5EMBunJMP7/kI/Jq/8dj7SDU9EUBZ2Z8ZJocSaPBT9T6dVvFHBsDLZh4FN/Q==}
5419
+ coze-coding-dev-sdk@0.7.25-beta.8:
5420
+ resolution: {integrity: sha512-RSKlXCO0W/OPDjx9D9JICn2cGeOJw3iG+y0Pr1AhAf7YdAjjNbBZoG7qfg0Zg2SqgaVN2qSLQFmKPbhNq9ZWSg==}
5421
5421
  engines: {node: '>=18.0.0'}
5422
5422
  hasBin: true
5423
5423
 
@@ -17159,7 +17159,7 @@ snapshots:
17159
17159
  optionalDependencies:
17160
17160
  typescript: 5.9.3
17161
17161
 
17162
- coze-coding-dev-sdk@0.7.25-beta.4(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0):
17162
+ coze-coding-dev-sdk@0.7.25-beta.8(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0):
17163
17163
  dependencies:
17164
17164
  '@langchain/core': 1.1.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5))
17165
17165
  '@langchain/openai': 1.2.3(@langchain/core@1.1.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5)))(ws@8.19.0)
@@ -17,7 +17,7 @@
17
17
  "@nestjs/core": "^10.4.15",
18
18
  "@nestjs/platform-express": "^10.4.15",
19
19
  "@supabase/supabase-js": "2.95.3",
20
- "coze-coding-dev-sdk": "^0.7.25-beta.4",
20
+ "coze-coding-dev-sdk": "^0.7.25-beta.8",
21
21
  "dotenv": "^17.2.3",
22
22
  "drizzle-kit": "^0.31.8",
23
23
  "drizzle-orm": "^0.45.1",
@@ -12,6 +12,12 @@ function parsePort(): number {
12
12
  return port;
13
13
  }
14
14
  }
15
+
16
+ const envPort = parseInt(process.env.SERVER_PORT || '', 10);
17
+ if (!isNaN(envPort) && envPort > 0 && envPort < 65536) {
18
+ return envPort;
19
+ }
20
+
15
21
  return <%= serverPort %>;
16
22
  }
17
23
 
@@ -38,12 +44,12 @@ async function bootstrap() {
38
44
  console.log(`Server running on http://localhost:${port}`);
39
45
  } catch (err) {
40
46
  if (err.code === 'EADDRINUSE') {
41
- console.error(`❌ 端口 \({port} 被占用! 请运行 'npx kill-port \){port}' 然后重试。`);
47
+ console.error(`❌ 端口 ${port} 被占用,请设置其他 SERVER_PORT 后重试。`);
42
48
  process.exit(1);
43
49
  } else {
44
50
  throw err;
45
51
  }
46
52
  }
47
- console.log(`Application is running on: http://localhost:<%= serverPort %>`);
53
+ console.log(`Application is running on: http://localhost:${port}`);
48
54
  }
49
55
  bootstrap();
@@ -16,10 +16,17 @@
16
16
  },
17
17
  "port": {
18
18
  "type": "number",
19
- "default": 8081,
19
+ "default": 5000,
20
20
  "minimum": 1024,
21
21
  "maximum": 65535,
22
22
  "description": "Metro bundler port"
23
+ },
24
+ "serverPort": {
25
+ "type": "number",
26
+ "default": 9091,
27
+ "minimum": 1024,
28
+ "maximum": 65535,
29
+ "description": "Express backend server port"
23
30
  }
24
31
  },
25
32
  "required": [],
@@ -119,6 +126,24 @@
119
126
  "additionalProperties": false
120
127
  }
121
128
  },
129
+ {
130
+ "name": "phaser-static",
131
+ "description": "Phaser Static(纯静态 2D 游戏):coze-dev init ${COZE_WORKSPACE_PATH} --template phaser-static\n- 适用:浏览器原生 JavaScript ESM 的 Phaser 3 游戏。\n- Phaser 与 Phaser Bridge 从 CDN 加载;无需 npm、Vite 或 TypeScript。",
132
+ "location": "./phaser-static",
133
+ "paramsSchema": {
134
+ "type": "object",
135
+ "properties": {
136
+ "appName": {
137
+ "type": "string",
138
+ "minLength": 1,
139
+ "pattern": "^[a-z0-9-]+$",
140
+ "description": "Application name (lowercase, alphanumeric and hyphens only)"
141
+ }
142
+ },
143
+ "required": [],
144
+ "additionalProperties": false
145
+ }
146
+ },
122
147
  {
123
148
  "name": "pi-agent",
124
149
  "description": "Pi Agent:`coze-dev init ${COZE_WORKSPACE_PATH} --template pi-agent`\n- 适用:基于 pi-agent-core 的 AI Agent 应用\n- 支持飞书、微信等多渠道接入\n- 内置 Dashboard 管理面板\n- 使用 TypeScript + Express + Vite",
@@ -4,11 +4,16 @@ project_type = "web"
4
4
 
5
5
  [dev]
6
6
  build = ["bash", "./scripts/prepare.sh"]
7
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/prepare.ps1"]
7
8
  run = ["bash", "./scripts/dev.sh"]
9
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/dev.ps1"]
8
10
  validate = ["bash", "./scripts/validate.sh"]
11
+ validate_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/validate.ps1"]
9
12
  deps = ["git"] # -> apt install git
10
13
 
11
14
  [deploy]
12
15
  build = ["bash","./scripts/build.sh"]
16
+ build_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/build.ps1"]
13
17
  run = ["bash","./scripts/start.sh"]
18
+ run_win = ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "./scripts/start.ps1"]
14
19
  deps = ["git"] # -> apt install git
@@ -22,7 +22,7 @@
22
22
  "@types/express": "^5.0.0",
23
23
  "@types/node": "^22.10.5",
24
24
  "autoprefixer": "^10.4.20",
25
- "coze-coding-dev-sdk": "^0.7.16",
25
+ "coze-coding-dev-sdk": "^0.7.25-beta.8",
26
26
  "esbuild": "^0.24.2",
27
27
  "eslint": "^9",
28
28
  "eslint-plugin-import": "^2.32.0",
@@ -32,8 +32,8 @@ importers:
32
32
  specifier: ^10.4.20
33
33
  version: 10.4.23(postcss@8.5.6)
34
34
  coze-coding-dev-sdk:
35
- specifier: ^0.7.16
36
- version: 0.7.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0)
35
+ specifier: ^0.7.25-beta.8
36
+ version: 0.7.25-beta.8(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0)
37
37
  esbuild:
38
38
  specifier: ^0.27.2
39
39
  version: 0.27.2
@@ -910,8 +910,8 @@ packages:
910
910
  typescript:
911
911
  optional: true
912
912
 
913
- coze-coding-dev-sdk@0.7.16:
914
- resolution: {integrity: sha512-B7gRPBa+sfrZL8ur0+F/92SOgZgFaWD/fOnCN2DjthnHSdmCYhAJWCfqzG2oO2vwTiYCXkKUuTBZ3MJLMj886g==}
913
+ coze-coding-dev-sdk@0.7.25-beta.8:
914
+ resolution: {integrity: sha512-RSKlXCO0W/OPDjx9D9JICn2cGeOJw3iG+y0Pr1AhAf7YdAjjNbBZoG7qfg0Zg2SqgaVN2qSLQFmKPbhNq9ZWSg==}
915
915
  engines: {node: '>=18.0.0'}
916
916
  hasBin: true
917
917
 
@@ -3281,7 +3281,7 @@ snapshots:
3281
3281
  optionalDependencies:
3282
3282
  typescript: 5.9.3
3283
3283
 
3284
- coze-coding-dev-sdk@0.7.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0):
3284
+ coze-coding-dev-sdk@0.7.25-beta.8(openai@6.16.0(ws@8.19.0)(zod@4.3.5))(ws@8.19.0):
3285
3285
  dependencies:
3286
3286
  '@langchain/core': 1.1.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5))
3287
3287
  '@langchain/openai': 1.2.3(@langchain/core@1.1.16(openai@6.16.0(ws@8.19.0)(zod@4.3.5)))(ws@8.19.0)
@@ -0,0 +1,16 @@
1
+ $ErrorActionPreference = "Stop"
2
+
3
+ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (Get-Location).Path }
4
+ Set-Location $workspace
5
+
6
+ Write-Host "Installing dependencies..."
7
+ & pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
8
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
9
+
10
+ Write-Host "Building frontend with Vite..."
11
+ & pnpm vite build
12
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
13
+
14
+ Write-Host "Bundling server with tsup..."
15
+ & pnpm tsup server/server.ts --format cjs --platform node --target node20 --outDir dist-server --no-splitting --no-minify --external vite
16
+ exit $LASTEXITCODE