@coze-arch/cli 0.1.5 → 0.1.8-alpha.6e99f9

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 (45) hide show
  1. package/docs/deploy.md +12 -1
  2. package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +3 -3
  3. package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +11 -3
  4. package/lib/__templates__/expo/.cozeproj/scripts/prepare-node-modules.sh +382 -0
  5. package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +3 -3
  6. package/lib/__templates__/expo/client/components/Screen.tsx +65 -25
  7. package/lib/__templates__/expo/client/heroui/components/toast/toast.tsx +6 -2
  8. package/lib/__templates__/expo/client/heroui/helpers/internal/hooks/use-controllable-state.ts +1 -1
  9. package/lib/__templates__/nextjs/eslint.config.mjs +11 -5
  10. package/lib/__templates__/nextjs/next.config.ts +10 -0
  11. package/lib/__templates__/nextjs/package.json +2 -5
  12. package/lib/__templates__/nextjs/pnpm-lock.yaml +14 -945
  13. package/lib/__templates__/nextjs/scripts/build.ps1 +1 -1
  14. package/lib/__templates__/nextjs/scripts/build.sh +2 -2
  15. package/lib/__templates__/nextjs/scripts/dev.ps1 +2 -2
  16. package/lib/__templates__/nextjs/scripts/dev.sh +93 -10
  17. package/lib/__templates__/nextjs/scripts/prepare-next-dev-output.sh +122 -0
  18. package/lib/__templates__/nextjs/scripts/prepare-node-modules.sh +382 -0
  19. package/lib/__templates__/nextjs/scripts/prepare.sh +1 -4
  20. package/lib/__templates__/nextjs/src/app/layout.tsx +0 -4
  21. package/lib/__templates__/nextjs/src/server.ts +1 -1
  22. package/lib/__templates__/nuxt-vue/scripts/build.sh +1 -1
  23. package/lib/__templates__/nuxt-vue/scripts/dev.sh +11 -3
  24. package/lib/__templates__/nuxt-vue/scripts/prepare-node-modules.sh +382 -0
  25. package/lib/__templates__/nuxt-vue/scripts/prepare.sh +1 -4
  26. package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +5 -1
  27. package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +4 -0
  28. package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +3 -0
  29. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.ps1 +4 -2
  30. package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +29 -7
  31. package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +4 -0
  32. package/lib/__templates__/taro/.cozeproj/scripts/pack.ps1 +3 -1
  33. package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +8 -0
  34. package/lib/__templates__/taro/.cozeproj/scripts/prepare-node-modules.sh +382 -0
  35. package/lib/__templates__/taro/.cozeproj/scripts/validate.ps1 +3 -1
  36. package/lib/__templates__/taro/.cozeproj/scripts/validate.sh +4 -0
  37. package/lib/__templates__/taro/_gitignore +1 -0
  38. package/lib/__templates__/vite/scripts/build.sh +1 -1
  39. package/lib/__templates__/vite/scripts/dev.ps1 +2 -2
  40. package/lib/__templates__/vite/scripts/dev.sh +11 -3
  41. package/lib/__templates__/vite/scripts/prepare-node-modules.sh +382 -0
  42. package/lib/__templates__/vite/scripts/prepare.sh +1 -4
  43. package/lib/cli.js +147 -16
  44. package/package.json +2 -2
  45. package/lib/__templates__/nextjs/.babelrc +0 -15
@@ -0,0 +1,382 @@
1
+ #!/usr/bin/env bash
2
+ # Install dependencies on local disk and expose them to the source tree by symlink.
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
6
+ if [ "$(basename "$(dirname "$SCRIPT_DIR")")" = ".cozeproj" ]; then
7
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
8
+ else
9
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
10
+ fi
11
+
12
+ command -v pnpm >/dev/null 2>&1 || {
13
+ echo "[node_modules] pnpm is required" >&2
14
+ exit 1
15
+ }
16
+
17
+ DRIVE_ROOT="${COZE_DRIVE_ROOT:-/Coze/Drive}"
18
+ if [ -d "$DRIVE_ROOT" ]; then
19
+ DRIVE_ROOT="$(cd "$DRIVE_ROOT" && pwd -P)"
20
+ else
21
+ DRIVE_ROOT="${DRIVE_ROOT%/}"
22
+ fi
23
+ case "$PROJECT_ROOT" in
24
+ "$DRIVE_ROOT"|"$DRIVE_ROOT"/*) ;;
25
+ *)
26
+ (cd "$PROJECT_ROOT" && pnpm install "$@")
27
+ exit 0
28
+ ;;
29
+ esac
30
+
31
+ NM_ROOT="/tmp/nm"
32
+ PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
33
+ mkdir -p "$NM_ROOT"
34
+ WORK_DIR="$NM_ROOT/$PROJECT_ID"
35
+ case "$WORK_DIR" in
36
+ "$DRIVE_ROOT"|"$DRIVE_ROOT"/*)
37
+ echo "[node_modules] local dependency directory must not be on Coze Drive: $WORK_DIR" >&2
38
+ exit 1
39
+ ;;
40
+ esac
41
+
42
+ LOCK_DIR="$NM_ROOT/$PROJECT_ID.lock"
43
+ LOCK_TIMEOUT_SECONDS="${COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS:-120}"
44
+ case "$LOCK_TIMEOUT_SECONDS" in
45
+ ''|*[!0-9]*|0)
46
+ echo "[node_modules] COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS must be a positive integer" >&2
47
+ exit 1
48
+ ;;
49
+ esac
50
+
51
+ lock_owner_is_alive() {
52
+ local owner_pid=""
53
+ [ -f "$LOCK_DIR/pid" ] || return 1
54
+ IFS= read -r owner_pid < "$LOCK_DIR/pid" || return 1
55
+ case "$owner_pid" in
56
+ ''|*[!0-9]*) return 1 ;;
57
+ esac
58
+ kill -0 "$owner_pid" 2>/dev/null
59
+ }
60
+
61
+ release_lock() {
62
+ local owner_pid=""
63
+ [ -f "$LOCK_DIR/pid" ] || return 0
64
+ IFS= read -r owner_pid < "$LOCK_DIR/pid" || true
65
+ if [ "$owner_pid" = "$$" ]; then
66
+ rm -f "$LOCK_DIR/pid"
67
+ rmdir "$LOCK_DIR" 2>/dev/null || true
68
+ fi
69
+ }
70
+
71
+ lock_started_at="$(date +%s)"
72
+ missing_owner_checks=0
73
+ while ! mkdir "$LOCK_DIR" 2>/dev/null; do
74
+ if lock_owner_is_alive; then
75
+ missing_owner_checks=0
76
+ else
77
+ missing_owner_checks=$((missing_owner_checks + 1))
78
+ if [ "$missing_owner_checks" -ge 2 ]; then
79
+ stale_lock_dir="$LOCK_DIR.stale.$$"
80
+ if mv "$LOCK_DIR" "$stale_lock_dir" 2>/dev/null; then
81
+ echo "[node_modules] Recovering stale dependency preparation lock."
82
+ rm -rf "$stale_lock_dir"
83
+ fi
84
+ missing_owner_checks=0
85
+ continue
86
+ fi
87
+ fi
88
+
89
+ lock_now="$(date +%s)"
90
+ if [ $((lock_now - lock_started_at)) -ge "$LOCK_TIMEOUT_SECONDS" ]; then
91
+ echo "[node_modules] Timed out waiting ${LOCK_TIMEOUT_SECONDS}s for dependency preparation lock: $LOCK_DIR" >&2
92
+ exit 1
93
+ fi
94
+ echo "[node_modules] Waiting for another dependency preparation for this project..."
95
+ sleep 1
96
+ done
97
+ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
+ trap release_lock EXIT
99
+
100
+ mkdir -p "$WORK_DIR"
101
+ INSTALL_STATE_FILE="$WORK_DIR/.install-state"
102
+
103
+ # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
104
+ # and framework dev servers two paths for the same source tree, which breaks
105
+ # file watching and on-demand compilation. Keep only managed local outputs.
106
+ cleanup_legacy_workspace() {
107
+ local workspace_dir="$1" entry name
108
+ if [ -L "$workspace_dir" ] || { [ -e "$workspace_dir" ] && [ ! -d "$workspace_dir" ]; }; then
109
+ rm -rf "$workspace_dir"
110
+ return
111
+ fi
112
+ if [ ! -d "$workspace_dir" ]; then
113
+ return 0
114
+ fi
115
+
116
+ while IFS= read -r -d '' entry; do
117
+ name="$(basename "$entry")"
118
+ [ "$name" = "node_modules" ] || rm -rf "$entry"
119
+ done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
120
+ }
121
+
122
+ # caches is reserved for the sibling prepare scripts of this project. They keep
123
+ # regenerable framework output there (the Next helper points .next/dev into it), and that
124
+ # output has to outlive an install, so this cleanup must not read it as leftover.
125
+ cleanup_legacy_shadow_project() {
126
+ local entry name
127
+ while IFS= read -r -d '' entry; do
128
+ name="$(basename "$entry")"
129
+ case "$name" in
130
+ node_modules|client|server|caches|.install-state) continue ;;
131
+ esac
132
+ rm -rf "$entry"
133
+ done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
134
+
135
+ for workspace in client server; do
136
+ cleanup_legacy_workspace "$WORK_DIR/$workspace"
137
+ done
138
+ }
139
+
140
+ link_install_entry() {
141
+ local source_path="$1" target_path="$2"
142
+ rm -rf "$target_path"
143
+ if [ -e "$source_path" ]; then
144
+ ln -s "$source_path" "$target_path"
145
+ fi
146
+ }
147
+
148
+ hash_install_input() {
149
+ if command -v sha256sum >/dev/null 2>&1; then
150
+ sha256sum | awk '{print $1}'
151
+ elif command -v shasum >/dev/null 2>&1; then
152
+ shasum -a 256 | awk '{print $1}'
153
+ else
154
+ cksum | awk '{print $1 "-" $2}'
155
+ fi
156
+ }
157
+
158
+ append_file_to_install_fingerprint() {
159
+ local relative_path="$1"
160
+ local source_path="$PROJECT_ROOT/$relative_path"
161
+ printf 'path=%s\n' "$relative_path"
162
+ if [ -f "$source_path" ]; then
163
+ cat "$source_path"
164
+ else
165
+ printf '<missing>\n'
166
+ fi
167
+ }
168
+
169
+ append_directory_to_install_fingerprint() {
170
+ local relative_path="$1"
171
+ local source_path="$PROJECT_ROOT/$relative_path"
172
+ local file
173
+ printf 'path=%s\n' "$relative_path"
174
+ if [ ! -d "$source_path" ]; then
175
+ printf '<missing>\n'
176
+ return
177
+ fi
178
+
179
+ while IFS= read -r file; do
180
+ printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
+ cat "$file"
182
+ done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
+ }
184
+
185
+ # Everything that can change the resolved dependency tree. The leading version marker
186
+ # invalidates every stored state when this scheme itself changes.
187
+ calculate_install_fingerprint() {
188
+ local filename workspace argument
189
+ {
190
+ printf 'prepare-node-modules-install-state=1\n'
191
+ printf 'node-version=%s\n' "$(node --version)"
192
+ printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
+ printf 'node-env=%s\n' "${NODE_ENV:-}"
194
+ printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
+ printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
+ printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
+ for argument in "$@"; do
198
+ printf 'install-argument=%s\n' "$argument"
199
+ done
200
+ for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
+ append_file_to_install_fingerprint "$filename"
202
+ done
203
+ for workspace in client server; do
204
+ append_file_to_install_fingerprint "$workspace/package.json"
205
+ done
206
+ append_directory_to_install_fingerprint patches
207
+ } | hash_install_input
208
+ }
209
+
210
+ install_requires_refresh() {
211
+ local argument
212
+ for argument in "$@"; do
213
+ case "$argument" in
214
+ --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
+ esac
216
+ done
217
+ return 1
218
+ }
219
+
220
+ # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
+ # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
+ # there is no install to guard when the tree is already in place.
223
+ package_has_install_lifecycle() {
224
+ local package_path="$1"
225
+ node - "$package_path" <<'NODE'
226
+ const fs = require('node:fs');
227
+ const [packagePath] = process.argv.slice(2);
228
+
229
+ try {
230
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
+ const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
+ ? packageJson.scripts
233
+ : {};
234
+ const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
+ name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
+ );
237
+
238
+ process.exit(hasLifecycle ? 0 : 1);
239
+ } catch {
240
+ process.exit(0);
241
+ }
242
+ NODE
243
+ }
244
+
245
+ install_reuse_is_safe() {
246
+ local workspace package_path
247
+ for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
+ if [ -e "$package_path" ]; then
249
+ echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
+ return 1
251
+ fi
252
+ done
253
+
254
+ if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
+ echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
+ return 1
257
+ fi
258
+
259
+ for workspace in . client server; do
260
+ package_path="$PROJECT_ROOT/$workspace/package.json"
261
+ [ -f "$package_path" ] || continue
262
+ if package_has_install_lifecycle "$package_path"; then
263
+ echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
+ return 1
265
+ fi
266
+ done
267
+ return 0
268
+ }
269
+
270
+ dependencies_are_ready() {
271
+ local workspace
272
+ [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
+ for workspace in client server; do
274
+ if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
+ [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
+ return 1
277
+ fi
278
+ done
279
+ return 0
280
+ }
281
+
282
+ # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
+ # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
+ install_can_be_reused() {
285
+ local installed_fingerprint=""
286
+
287
+ [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
+ install_requires_refresh "$@" && return 1
289
+ [ -f "$INSTALL_STATE_FILE" ] || return 1
290
+ IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
+ dependencies_are_ready || return 1
292
+ install_reuse_is_safe || return 1
293
+ [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
+ return 0
295
+ }
296
+
297
+ write_install_state() {
298
+ local fingerprint="$1" temporary_state
299
+ temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
+ printf '%s\n' "$fingerprint" > "$temporary_state"
301
+ mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
+ }
303
+
304
+ copy_install_metadata() {
305
+ local filename source_file target_file workspace
306
+ for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
307
+ source_file="$PROJECT_ROOT/$filename"
308
+ target_file="$WORK_DIR/$filename"
309
+ rm -f "$target_file"
310
+ if [ -f "$source_file" ]; then
311
+ cp "$source_file" "$target_file"
312
+ fi
313
+ done
314
+
315
+ link_install_entry "$PROJECT_ROOT/package.json" "$WORK_DIR/package.json"
316
+ link_install_entry "$PROJECT_ROOT/patches" "$WORK_DIR/patches"
317
+ link_install_entry "$PROJECT_ROOT/scripts" "$WORK_DIR/scripts"
318
+
319
+ for workspace in client server; do
320
+ if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
321
+ mkdir -p "$WORK_DIR/$workspace"
322
+ link_install_entry "$PROJECT_ROOT/$workspace/package.json" "$WORK_DIR/$workspace/package.json"
323
+ link_install_entry "$PROJECT_ROOT/$workspace/scripts" "$WORK_DIR/$workspace/scripts"
324
+ else
325
+ rm -rf "$WORK_DIR/$workspace"
326
+ fi
327
+ done
328
+ }
329
+
330
+ link_node_modules() {
331
+ local source_dir="$1" target_dir="$2"
332
+ local current=""
333
+ mkdir -p "$target_dir"
334
+ if [ -L "$source_dir/node_modules" ]; then
335
+ current="$(readlink "$source_dir/node_modules")"
336
+ case "$current" in
337
+ "$NM_ROOT"/*/node_modules|"$NM_ROOT"/*/*/node_modules) ;;
338
+ *)
339
+ echo "[node_modules] refusing to replace unmanaged symlink: $source_dir/node_modules" >&2
340
+ exit 1
341
+ ;;
342
+ esac
343
+ rm "$source_dir/node_modules"
344
+ elif [ -e "$source_dir/node_modules" ]; then
345
+ rm -rf "$source_dir/node_modules"
346
+ fi
347
+ ln -s "$target_dir" "$source_dir/node_modules"
348
+ }
349
+
350
+ cleanup_legacy_shadow_project
351
+ copy_install_metadata
352
+
353
+ if install_can_be_reused "$@"; then
354
+ echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
+ else
356
+ # A stale state file must not survive a failed install.
357
+ rm -f "$INSTALL_STATE_FILE"
358
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
359
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
+ exit 1
361
+ fi
362
+
363
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
+ chmod 0644 "$temporary_lockfile"
367
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
+ fi
369
+
370
+ # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
+ # now stands. Otherwise the next run would see a changed input and reinstall.
372
+ write_install_state "$(calculate_install_fingerprint "$@")"
373
+ fi
374
+
375
+ link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
376
+ for workspace in client server; do
377
+ if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
378
+ link_node_modules "$PROJECT_ROOT/$workspace" "$WORK_DIR/$workspace/node_modules"
379
+ fi
380
+ done
381
+
382
+ echo "[node_modules] Dependencies ready"
@@ -6,7 +6,4 @@ COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
6
6
  cd "${COZE_WORKSPACE_PATH}"
7
7
 
8
8
  echo "Installing dependencies..."
9
- pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
10
- if command -v coze-dev > /dev/null 2>&1 && coze-dev check-bins --help > /dev/null 2>&1; then
11
- coze-dev check-bins --fix
12
- fi
9
+ bash "$COZE_WORKSPACE_PATH/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
@@ -1,5 +1,4 @@
1
1
  import type { Metadata } from 'next';
2
- import { Inspector } from 'react-dev-inspector';
3
2
  import './globals.css';
4
3
 
5
4
  export const metadata: Metadata = {
@@ -61,12 +60,9 @@ export default function RootLayout({
61
60
  }: Readonly<{
62
61
  children: React.ReactNode;
63
62
  }>) {
64
- const isDev = process.env.COZE_PROJECT_ENV === 'DEV';
65
-
66
63
  return (
67
64
  <html lang="en">
68
65
  <body className={`antialiased`}>
69
- {isDev && <Inspector />}
70
66
  {children}
71
67
  </body>
72
68
  </html>
@@ -7,7 +7,7 @@ const hostname = process.env.HOSTNAME || 'localhost';
7
7
  const port = parseInt(process.env.PORT || '<%= port %>', 10);
8
8
 
9
9
  // Create Next.js app
10
- const app = next({ dev, hostname, port });
10
+ const app = next({ dev, hostname, port, webpack: true });
11
11
  const handle = app.getRequestHandler();
12
12
 
13
13
  app.prepare().then(() => {
@@ -6,7 +6,7 @@ COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
6
6
  cd "${COZE_WORKSPACE_PATH}"
7
7
 
8
8
  echo "Installing dependencies..."
9
- pnpm install --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
9
+ bash "$COZE_WORKSPACE_PATH/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
10
10
 
11
11
  echo "Building the Nuxt.js project..."
12
12
  pnpm nuxt build
@@ -65,11 +65,15 @@ LOG_FILE="${LOG_DIR}/server.log"
65
65
  PID_FILE="${LOG_DIR}/server.pid"
66
66
 
67
67
  # detached 出去的进程没人负责回收,超过这个时长就自己退出,避免端口与内存长期泄露。
68
- MAX_RUNTIME_SECONDS=1200
68
+ MAX_RUNTIME_SECONDS=3600
69
+
70
+ timeout_watchdog_enabled() {
71
+ [[ -z "${COZE_EVAL:-}" && -z "${COZE_PROJECT_TYPE:-}" ]]
72
+ }
69
73
 
70
74
  # 真正被 detach 的是这层 bash wrapper:它是进程组 leader,组内 watchdog 到点回收整组
71
75
  # (wrapper -> pnpm -> nuxt -> node);被包的进程自己先退出时也顺手清空进程组,不留残余。
72
- RUN_WITH_TIMEOUT='
76
+ RUN_WITH_TIMEOUT="$(declare -f timeout_watchdog_enabled)"'
73
77
  timeout_seconds=$1
74
78
  shift
75
79
 
@@ -77,6 +81,7 @@ shift
77
81
  child_pid=$!
78
82
 
79
83
  # 先忽略 TERM,才能在向整组发 TERM(自己也在组里)之后存活下来补一发 KILL。
84
+ if timeout_watchdog_enabled; then
80
85
  ( trap "" TERM
81
86
  sleep "${timeout_seconds}"
82
87
  echo "[dev] 后台进程运行超过 ${timeout_seconds}s,回收进程组 $$。"
@@ -84,6 +89,7 @@ child_pid=$!
84
89
  sleep 5
85
90
  kill -KILL -- "-$$" 2>/dev/null || true
86
91
  ) &
92
+ fi
87
93
 
88
94
  wait "${child_pid}"
89
95
  kill -KILL -- "-$$" 2>/dev/null || true
@@ -262,7 +268,9 @@ if [[ "${ready_status}" -ne 0 ]]; then
262
268
  fi
263
269
 
264
270
  echo "Dev server started (PID: ${server_pid}), listening on port ${DEPLOY_RUN_PORT}."
265
- echo "Auto stop after ${MAX_RUNTIME_SECONDS}s."
271
+ if timeout_watchdog_enabled; then
272
+ echo "Auto stop after ${MAX_RUNTIME_SECONDS}s."
273
+ fi
266
274
  echo "Log file: ${LOG_FILE}"
267
275
  echo "PID file: ${PID_FILE}"
268
276
  <% } %>