@coze-arch/cli 0.0.36-alpha.3ab612 → 0.0.36-alpha.4e6503
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/README.md +104 -68
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +19 -80
- package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +1 -1
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +1 -1
- package/lib/__templates__/expo/client/metro.config.js +1 -3
- package/lib/__templates__/expo/pnpm-lock.yaml +5 -5
- package/lib/__templates__/expo/server/package.json +2 -2
- package/lib/__templates__/expo/server/src/index.ts +1 -1
- package/lib/__templates__/expo/template.config.js +2 -11
- package/lib/__templates__/native-static/.coze +0 -2
- package/lib/__templates__/nextjs/.coze +0 -5
- package/lib/__templates__/nextjs/package.json +1 -1
- package/lib/__templates__/nextjs/pnpm-lock.yaml +5 -5
- package/lib/__templates__/nextjs/scripts/dev.sh +7 -205
- package/lib/__templates__/nextjs/template.config.js +2 -4
- package/lib/__templates__/nuxt-vue/.coze +0 -5
- package/lib/__templates__/nuxt-vue/package.json +1 -1
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +5 -5
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +1 -82
- package/lib/__templates__/pi-agent/package.json +1 -1
- package/lib/__templates__/pi-agent/pnpm-lock.yaml +5 -5
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +1 -1
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +68 -150
- package/lib/__templates__/taro/AGENTS.md +7 -11
- package/lib/__templates__/taro/README.md +4 -4
- package/lib/__templates__/taro/config/index.ts +2 -10
- package/lib/__templates__/taro/pnpm-lock.yaml +5 -5
- package/lib/__templates__/taro/server/package.json +1 -1
- package/lib/__templates__/taro/server/src/main.ts +2 -8
- package/lib/__templates__/templates.json +1 -51
- package/lib/__templates__/vite/package.json +1 -1
- package/lib/__templates__/vite/pnpm-lock.yaml +5 -5
- package/lib/__templates__/vite/scripts/build.ps1 +1 -1
- package/lib/__templates__/vite/scripts/dev.ps1 +65 -9
- package/lib/__templates__/vite/scripts/dev.sh +1 -82
- package/lib/__templates__/vite/scripts/start.ps1 +8 -2
- package/lib/__templates__/vite/template.config.js +2 -4
- package/lib/cli.js +204 -3590
- package/package.json +3 -5
- package/docs/deploy.md +0 -179
- package/lib/__templates__/native-static/scripts/serve.ps1 +0 -7
- package/lib/__templates__/nextjs/scripts/build.ps1 +0 -16
- package/lib/__templates__/nextjs/scripts/dev.ps1 +0 -63
- package/lib/__templates__/nextjs/scripts/prepare.ps1 +0 -18
- package/lib/__templates__/nextjs/scripts/start.ps1 +0 -11
- package/lib/__templates__/nextjs/scripts/validate.ps1 +0 -8
- package/lib/__templates__/nuxt-vue/scripts/build.ps1 +0 -12
- package/lib/__templates__/nuxt-vue/scripts/dev.ps1 +0 -63
- package/lib/__templates__/nuxt-vue/scripts/prepare.ps1 +0 -12
- package/lib/__templates__/nuxt-vue/scripts/start.ps1 +0 -11
- package/lib/__templates__/nuxt-vue/scripts/validate.ps1 +0 -8
- package/lib/__templates__/phaser/.coze +0 -18
- package/lib/__templates__/phaser/README.md +0 -45
- package/lib/__templates__/phaser/_gitignore +0 -23
- package/lib/__templates__/phaser/_npmrc +0 -14
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/manifest.json +0 -11
- package/lib/__templates__/phaser/debug-runtime/flow-graph.json +0 -20
- package/lib/__templates__/phaser/index.html +0 -12
- package/lib/__templates__/phaser/package.json +0 -31
- package/lib/__templates__/phaser/pnpm-lock.yaml +0 -661
- package/lib/__templates__/phaser/scripts/build.ps1 +0 -12
- package/lib/__templates__/phaser/scripts/build.sh +0 -8
- package/lib/__templates__/phaser/scripts/dev.ps1 +0 -49
- package/lib/__templates__/phaser/scripts/dev.sh +0 -82
- package/lib/__templates__/phaser/scripts/prepare.ps1 +0 -8
- package/lib/__templates__/phaser/scripts/prepare.sh +0 -7
- package/lib/__templates__/phaser/scripts/spawn-detached.cjs +0 -29
- package/lib/__templates__/phaser/scripts/start.ps1 +0 -49
- package/lib/__templates__/phaser/scripts/start.sh +0 -82
- package/lib/__templates__/phaser/scripts/validate.ps1 +0 -7
- package/lib/__templates__/phaser/scripts/validate.sh +0 -7
- package/lib/__templates__/phaser/src/assets.ts +0 -15
- package/lib/__templates__/phaser/src/global.d.ts +0 -39
- package/lib/__templates__/phaser/src/main.ts +0 -28
- package/lib/__templates__/phaser/src/scene/BootScene.ts +0 -213
- package/lib/__templates__/phaser/src/scene/DevLoadingScene.ts +0 -69
- package/lib/__templates__/phaser/src/style.css +0 -28
- package/lib/__templates__/phaser/src/types.ts +0 -32
- package/lib/__templates__/phaser/src/utils/asset-loader.ts +0 -319
- package/lib/__templates__/phaser/src/utils/index.ts +0 -2
- package/lib/__templates__/phaser/src/utils/mark-editable.ts +0 -13
- package/lib/__templates__/phaser/template.config.js +0 -43
- package/lib/__templates__/phaser/tsconfig.json +0 -23
- package/lib/__templates__/phaser/vite.config.ts +0 -96
- package/lib/__templates__/phaser-static/.coze +0 -15
- package/lib/__templates__/phaser-static/README.md +0 -40
- package/lib/__templates__/phaser-static/_gitignore +0 -6
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/manifest.json +0 -11
- package/lib/__templates__/phaser-static/debug-runtime/flow-graph.json +0 -20
- package/lib/__templates__/phaser-static/index.html +0 -17
- package/lib/__templates__/phaser-static/scripts/serve.ps1 +0 -7
- package/lib/__templates__/phaser-static/src/assets.js +0 -15
- package/lib/__templates__/phaser-static/src/main.js +0 -26
- package/lib/__templates__/phaser-static/src/phaser.js +0 -7
- package/lib/__templates__/phaser-static/src/scene/BootScene.js +0 -205
- package/lib/__templates__/phaser-static/src/scene/DevLoadingScene.js +0 -67
- package/lib/__templates__/phaser-static/src/style.css +0 -28
- package/lib/__templates__/phaser-static/src/utils/asset-loader.js +0 -267
- package/lib/__templates__/phaser-static/src/utils/index.js +0 -2
- package/lib/__templates__/phaser-static/src/utils/mark-editable.js +0 -3
- package/lib/__templates__/phaser-static/template.config.js +0 -35
- package/lib/deploy/package-project.js +0 -244
- package/lib/deploy/package-project.py +0 -183
|
@@ -2,17 +2,12 @@
|
|
|
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}}"
|
|
7
5
|
cd "${COZE_WORKSPACE_PATH}"
|
|
8
6
|
|
|
9
7
|
# ---------------------------------------------------------
|
|
10
|
-
#
|
|
8
|
+
# PID 文件,用于追踪上一次启动的进程树
|
|
11
9
|
# ---------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
LOG_FILE="${LOG_DIR}/dev.log"
|
|
14
|
-
PID_FILE="${LOG_DIR}/dev.pid"
|
|
15
|
-
DEV_PID=""
|
|
10
|
+
PID_FILE="/tmp/coze-dev-run.pid"
|
|
16
11
|
|
|
17
12
|
# ---------------------------------------------------------
|
|
18
13
|
# 工具函数
|
|
@@ -30,165 +25,99 @@ kill_process_tree() {
|
|
|
30
25
|
fi
|
|
31
26
|
}
|
|
32
27
|
|
|
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
|
-
|
|
129
28
|
kill_port_if_listening() {
|
|
130
29
|
local port=$1
|
|
131
30
|
local pids
|
|
132
|
-
pids=$(
|
|
31
|
+
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)
|
|
133
32
|
if [[ -z "${pids}" ]]; then
|
|
134
33
|
echo "Port ${port} is free."
|
|
135
34
|
return
|
|
136
35
|
fi
|
|
137
|
-
echo "Port ${port} in use by PIDs: ${pids}
|
|
36
|
+
echo "Port ${port} in use by PIDs: ${pids}"
|
|
138
37
|
for pid in ${pids}; do
|
|
139
38
|
kill_process_tree "${pid}"
|
|
140
39
|
done
|
|
141
40
|
sleep 1
|
|
142
|
-
pids=$(
|
|
41
|
+
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)
|
|
143
42
|
if [[ -n "${pids}" ]]; then
|
|
144
|
-
echo "Warning: port ${port} still busy after
|
|
43
|
+
echo "Warning: port ${port} still busy after cleanup, PIDs: ${pids}"
|
|
145
44
|
else
|
|
146
45
|
echo "Port ${port} cleared."
|
|
147
46
|
fi
|
|
148
47
|
}
|
|
149
48
|
|
|
150
|
-
#
|
|
49
|
+
# ---------------------------------------------------------
|
|
50
|
+
# 1. 清理上一次运行残留的整棵进程树
|
|
51
|
+
# ---------------------------------------------------------
|
|
151
52
|
cleanup_previous_run() {
|
|
152
|
-
|
|
153
|
-
|
|
53
|
+
# 1a. 通过 PID 文件清理上次的进程树
|
|
54
|
+
if [[ -f "${PID_FILE}" ]]; then
|
|
55
|
+
local old_pid
|
|
56
|
+
old_pid=$(cat "${PID_FILE}" 2>/dev/null || true)
|
|
57
|
+
if [[ -n "${old_pid}" ]] && kill -0 "${old_pid}" 2>/dev/null; then
|
|
58
|
+
echo "🧹 Killing previous dev process tree (root PID: ${old_pid})..."
|
|
59
|
+
kill_process_tree "${old_pid}"
|
|
60
|
+
fi
|
|
61
|
+
rm -f "${PID_FILE}"
|
|
154
62
|
fi
|
|
155
63
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
64
|
+
# 1b. 兜底:按特征匹配清理所有残留的相关进程(排除自身)
|
|
65
|
+
echo "🧹 Cleaning up any orphaned dev processes..."
|
|
66
|
+
local patterns=(
|
|
67
|
+
"pnpm dev"
|
|
68
|
+
"concurrently.*dev:web.*dev:server"
|
|
69
|
+
"nest start --watch"
|
|
70
|
+
"taro build --type h5 --watch"
|
|
71
|
+
"node --enable-source-maps.*/workspace/projects/server/dist/main"
|
|
72
|
+
"esbuild --service.*--ping"
|
|
73
|
+
)
|
|
74
|
+
for pattern in "${patterns[@]}"; do
|
|
75
|
+
local pids
|
|
76
|
+
pids=$(pgrep -f "${pattern}" 2>/dev/null || true)
|
|
77
|
+
for pid in ${pids}; do
|
|
78
|
+
# 不杀自己和自己的父进程链
|
|
79
|
+
if [[ "${pid}" != "$$" ]] && [[ "${pid}" != "${PPID}" ]]; then
|
|
80
|
+
echo " Killing orphan PID ${pid} matching '${pattern}'"
|
|
81
|
+
kill -9 "${pid}" 2>/dev/null || true
|
|
82
|
+
fi
|
|
83
|
+
done
|
|
84
|
+
done
|
|
85
|
+
sleep 1
|
|
163
86
|
}
|
|
164
87
|
|
|
165
88
|
# ---------------------------------------------------------
|
|
166
89
|
# 2. 安装依赖
|
|
167
90
|
# ---------------------------------------------------------
|
|
168
91
|
echo "📦 Installing dependencies..."
|
|
169
|
-
|
|
170
|
-
"${PNPM_BIN}" install
|
|
92
|
+
pnpm install
|
|
171
93
|
echo "✅ Dependencies installed successfully!"
|
|
172
94
|
|
|
173
95
|
# ---------------------------------------------------------
|
|
174
96
|
# 3. 清理旧进程 + 端口
|
|
175
97
|
# ---------------------------------------------------------
|
|
176
|
-
|
|
177
|
-
PORT="${DEPLOY_RUN_PORT:-${PORT:-<%= port %>}}"
|
|
178
|
-
SERVER_PORT="${SERVER_PORT:-<%= serverPort %>}"
|
|
179
|
-
export PORT
|
|
180
|
-
export SERVER_PORT
|
|
98
|
+
SERVER_PORT=3000
|
|
181
99
|
|
|
182
|
-
mkdir -p "${LOG_DIR}"
|
|
183
100
|
cleanup_previous_run
|
|
184
101
|
|
|
185
|
-
echo "Clearing port ${
|
|
186
|
-
kill_port_if_listening "${
|
|
102
|
+
echo "Clearing port ${DEPLOY_RUN_PORT} (web) before start."
|
|
103
|
+
kill_port_if_listening "${DEPLOY_RUN_PORT}"
|
|
187
104
|
echo "Clearing port ${SERVER_PORT} (server) before start."
|
|
188
105
|
kill_port_if_listening "${SERVER_PORT}"
|
|
189
106
|
|
|
190
107
|
# ---------------------------------------------------------
|
|
191
|
-
# 4.
|
|
108
|
+
# 4. 退出时自动清理子进程(信号 trap)
|
|
109
|
+
# ---------------------------------------------------------
|
|
110
|
+
cleanup_on_exit() {
|
|
111
|
+
echo "🛑 dev_run.sh exiting, cleaning up child processes..."
|
|
112
|
+
# 杀掉当前脚本的所有子进程
|
|
113
|
+
kill -- -$$ 2>/dev/null || true
|
|
114
|
+
rm -f "${PID_FILE}"
|
|
115
|
+
exit 0
|
|
116
|
+
}
|
|
117
|
+
trap cleanup_on_exit EXIT INT TERM HUP
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------
|
|
120
|
+
# 5. 启动服务
|
|
192
121
|
# ---------------------------------------------------------
|
|
193
122
|
start_service() {
|
|
194
123
|
cd "${COZE_WORKSPACE_PATH}"
|
|
@@ -204,30 +133,19 @@ start_service() {
|
|
|
204
133
|
# 启动 Taro H5 和 NestJS Server
|
|
205
134
|
echo "Starting Taro H5 Dev Server and NestJS Server..."
|
|
206
135
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|
217
|
-
echo "${DEV_PID}" > "${PID_FILE}"
|
|
136
|
+
export PORT=${DEPLOY_RUN_PORT}
|
|
137
|
+
rm -f /tmp/coze-logs/dev.log
|
|
138
|
+
mkdir -p /tmp/coze-logs
|
|
218
139
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return 1
|
|
225
|
-
fi
|
|
140
|
+
# 后台启动并记录 PID
|
|
141
|
+
pnpm dev 2>&1 | tee /tmp/coze-logs/dev.log &
|
|
142
|
+
local dev_pid=$!
|
|
143
|
+
echo "${dev_pid}" > "${PID_FILE}"
|
|
144
|
+
echo "📝 Dev process started with PID: ${dev_pid} (saved to ${PID_FILE})"
|
|
226
145
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
echo "PID file: ${PID_FILE}"
|
|
146
|
+
# 前台等待,保证 trap 能正常捕获信号
|
|
147
|
+
wait "${dev_pid}" || true
|
|
230
148
|
}
|
|
231
149
|
|
|
232
|
-
echo "Starting HTTP services on port ${
|
|
150
|
+
echo "Starting HTTP services on port ${DEPLOY_RUN_PORT} (web) and ${SERVER_PORT} (server)..."
|
|
233
151
|
start_service
|
|
@@ -361,24 +361,20 @@ if (isExternalUrl) {
|
|
|
361
361
|
|
|
362
362
|
**工作原理**:
|
|
363
363
|
|
|
364
|
-
1. **开发环境(H5)**:使用 `/api/xxx` 相对路径,Vite 的 proxy
|
|
364
|
+
1. **开发环境(H5)**:使用 `/api/xxx` 相对路径,Vite 的 proxy 会自动将其代理到 `http://localhost:3000/api/xxx`
|
|
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
|
-
// config
|
|
372
|
-
const serverPort = Number(process.env.SERVER_PORT) || <%= serverPort %>
|
|
373
|
-
|
|
371
|
+
// vite.config.ts
|
|
374
372
|
export default {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
'
|
|
379
|
-
|
|
380
|
-
changeOrigin: true
|
|
381
|
-
}
|
|
373
|
+
server: {
|
|
374
|
+
proxy: {
|
|
375
|
+
'/api': {
|
|
376
|
+
target: 'http://localhost:3000',
|
|
377
|
+
changeOrigin: true
|
|
382
378
|
}
|
|
383
379
|
}
|
|
384
380
|
}
|
|
@@ -63,8 +63,8 @@ pnpm install
|
|
|
63
63
|
pnpm dev
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
- 前端地址:http://localhost
|
|
67
|
-
- 后端地址:http://localhost
|
|
66
|
+
- 前端地址:http://localhost:5000
|
|
67
|
+
- 后端地址:http://localhost:3000
|
|
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 // 启动开发服务 (热重载, 默认端口
|
|
563
|
+
pnpm dev:server // 启动开发服务 (热重载, 默认端口 3000)
|
|
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
|
-
|
|
589
|
+
PORT=3000
|
|
590
590
|
|
|
591
591
|
## 微信小程序配置
|
|
592
592
|
WX_APP_ID=你的AppID
|
|
@@ -12,14 +12,6 @@ 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
|
-
|
|
23
15
|
const generateTTProjectConfig = (outputRoot: string) => {
|
|
24
16
|
const config = {
|
|
25
17
|
miniprogramRoot: './',
|
|
@@ -190,12 +182,12 @@ export default defineConfig<'vite'>(async (merge, _env) => {
|
|
|
190
182
|
mode: 'hash',
|
|
191
183
|
},
|
|
192
184
|
devServer: {
|
|
193
|
-
port:
|
|
185
|
+
port: <%= port %>,
|
|
194
186
|
host: '0.0.0.0',
|
|
195
187
|
open: false,
|
|
196
188
|
proxy: {
|
|
197
189
|
'/api': {
|
|
198
|
-
target:
|
|
190
|
+
target: 'http://localhost:<%= serverPort %>',
|
|
199
191
|
changeOrigin: true,
|
|
200
192
|
},
|
|
201
193
|
},
|
|
@@ -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.
|
|
208
|
-
version: 0.7.
|
|
207
|
+
specifier: ^0.7.24
|
|
208
|
+
version: 0.7.24(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.
|
|
5420
|
-
resolution: {integrity: sha512-
|
|
5419
|
+
coze-coding-dev-sdk@0.7.24:
|
|
5420
|
+
resolution: {integrity: sha512-oL9Tg5PwxP6iMixonzzMO4aBjOKWcFUMVM+iz5dPyaqQkG84HP1y9DqK5DJVmtdfJuYdAW7pzOatDqLvgvZiXg==}
|
|
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.
|
|
17162
|
+
coze-coding-dev-sdk@0.7.24(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.
|
|
20
|
+
"coze-coding-dev-sdk": "^0.7.24",
|
|
21
21
|
"dotenv": "^17.2.3",
|
|
22
22
|
"drizzle-kit": "^0.31.8",
|
|
23
23
|
"drizzle-orm": "^0.45.1",
|
|
@@ -12,12 +12,6 @@ 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
|
-
|
|
21
15
|
return <%= serverPort %>;
|
|
22
16
|
}
|
|
23
17
|
|
|
@@ -44,12 +38,12 @@ async function bootstrap() {
|
|
|
44
38
|
console.log(`Server running on http://localhost:${port}`);
|
|
45
39
|
} catch (err) {
|
|
46
40
|
if (err.code === 'EADDRINUSE') {
|
|
47
|
-
console.error(`❌ 端口
|
|
41
|
+
console.error(`❌ 端口 \({port} 被占用! 请运行 'npx kill-port \){port}' 然后重试。`);
|
|
48
42
|
process.exit(1);
|
|
49
43
|
} else {
|
|
50
44
|
throw err;
|
|
51
45
|
}
|
|
52
46
|
}
|
|
53
|
-
console.log(`Application is running on: http://localhost
|
|
47
|
+
console.log(`Application is running on: http://localhost:<%= serverPort %>`);
|
|
54
48
|
}
|
|
55
49
|
bootstrap();
|
|
@@ -16,17 +16,10 @@
|
|
|
16
16
|
},
|
|
17
17
|
"port": {
|
|
18
18
|
"type": "number",
|
|
19
|
-
"default":
|
|
19
|
+
"default": 8081,
|
|
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"
|
|
30
23
|
}
|
|
31
24
|
},
|
|
32
25
|
"required": [],
|
|
@@ -101,49 +94,6 @@
|
|
|
101
94
|
"additionalProperties": false
|
|
102
95
|
}
|
|
103
96
|
},
|
|
104
|
-
{
|
|
105
|
-
"name": "phaser",
|
|
106
|
-
"description": "Phaser(2D 游戏):`coze-dev init ${COZE_WORKSPACE_PATH} --template phaser`\n- 适用:基于 Phaser 3、Vite 和 TypeScript 的 2D Web 游戏。",
|
|
107
|
-
"location": "./phaser",
|
|
108
|
-
"paramsSchema": {
|
|
109
|
-
"type": "object",
|
|
110
|
-
"properties": {
|
|
111
|
-
"appName": {
|
|
112
|
-
"type": "string",
|
|
113
|
-
"minLength": 1,
|
|
114
|
-
"pattern": "^[a-z0-9-]+$",
|
|
115
|
-
"description": "Application name (lowercase, alphanumeric and hyphens only)"
|
|
116
|
-
},
|
|
117
|
-
"port": {
|
|
118
|
-
"type": "number",
|
|
119
|
-
"default": 5000,
|
|
120
|
-
"minimum": 1024,
|
|
121
|
-
"maximum": 65535,
|
|
122
|
-
"description": "Development and preview server port"
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
"required": [],
|
|
126
|
-
"additionalProperties": false
|
|
127
|
-
}
|
|
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
|
-
},
|
|
147
97
|
{
|
|
148
98
|
"name": "pi-agent",
|
|
149
99
|
"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",
|
|
@@ -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.
|
|
36
|
-
version: 0.7.
|
|
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)
|
|
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.
|
|
914
|
-
resolution: {integrity: sha512-
|
|
913
|
+
coze-coding-dev-sdk@0.7.16:
|
|
914
|
+
resolution: {integrity: sha512-B7gRPBa+sfrZL8ur0+F/92SOgZgFaWD/fOnCN2DjthnHSdmCYhAJWCfqzG2oO2vwTiYCXkKUuTBZ3MJLMj886g==}
|
|
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.
|
|
3284
|
+
coze-coding-dev-sdk@0.7.16(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)
|
|
@@ -4,7 +4,7 @@ $workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { (
|
|
|
4
4
|
Set-Location $workspace
|
|
5
5
|
|
|
6
6
|
Write-Host "Installing dependencies..."
|
|
7
|
-
& pnpm install --
|
|
7
|
+
& pnpm install --frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
|
|
8
8
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
9
9
|
|
|
10
10
|
Write-Host "Building frontend with Vite..."
|
|
@@ -7,9 +7,9 @@ function Get-WorkspacePath {
|
|
|
7
7
|
return (Get-Location).Path
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
function Stop-ProcessTree([int] $
|
|
11
|
-
if ($
|
|
12
|
-
& taskkill.exe /PID $
|
|
10
|
+
function Stop-ProcessTree([int] $targetProcessId) {
|
|
11
|
+
if ($targetProcessId -gt 0 -and (Get-Process -Id $targetProcessId -ErrorAction SilentlyContinue)) {
|
|
12
|
+
& taskkill.exe /PID $targetProcessId /T /F *> $null
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -20,11 +20,40 @@ function Stop-ListeningProcess([int] $port) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function Test-PortListening([int] $port) {
|
|
24
|
+
return $null -ne (Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function Wait-PortState([int] $port, [bool] $listening, [int] $timeoutSeconds) {
|
|
28
|
+
$deadline = (Get-Date).AddSeconds($timeoutSeconds)
|
|
29
|
+
do {
|
|
30
|
+
if ((Test-PortListening $port) -eq $listening) {
|
|
31
|
+
return $true
|
|
32
|
+
}
|
|
33
|
+
Start-Sleep -Milliseconds 250
|
|
34
|
+
} while ((Get-Date) -lt $deadline)
|
|
35
|
+
return $false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Get-Port {
|
|
39
|
+
$rawPort = if ($env:DEPLOY_RUN_PORT) { $env:DEPLOY_RUN_PORT } elseif ($env:PORT) { $env:PORT } else { "<%= port %>" }
|
|
40
|
+
$parsedPort = 0
|
|
41
|
+
if (-not [int]::TryParse($rawPort, [ref] $parsedPort) -or $parsedPort -lt 1 -or $parsedPort -gt 65535) {
|
|
42
|
+
throw "Invalid port '$rawPort'. Expected an integer between 1 and 65535."
|
|
43
|
+
}
|
|
44
|
+
return $parsedPort
|
|
45
|
+
}
|
|
46
|
+
|
|
23
47
|
$workspace = Get-WorkspacePath
|
|
24
|
-
$
|
|
25
|
-
|
|
48
|
+
if (-not (Test-Path -LiteralPath $workspace -PathType Container)) {
|
|
49
|
+
throw "Workspace directory does not exist: $workspace"
|
|
50
|
+
}
|
|
51
|
+
$port = Get-Port
|
|
26
52
|
Set-Location $workspace
|
|
27
53
|
Stop-ListeningProcess $port
|
|
54
|
+
if (-not (Wait-PortState $port $false 10)) {
|
|
55
|
+
throw "Port $port is still in use after stopping the previous process."
|
|
56
|
+
}
|
|
28
57
|
|
|
29
58
|
<% if (process.env.NODE_ENV === 'test') { %>
|
|
30
59
|
$env:PORT = "$port"
|
|
@@ -38,7 +67,10 @@ $pidFile = Join-Path $logDirectory "server.pid"
|
|
|
38
67
|
New-Item -ItemType Directory -Force -Path $logDirectory | Out-Null
|
|
39
68
|
|
|
40
69
|
if (Test-Path $pidFile) {
|
|
41
|
-
|
|
70
|
+
$previousPid = 0
|
|
71
|
+
if ([int]::TryParse((Get-Content $pidFile -Raw).Trim(), [ref] $previousPid)) {
|
|
72
|
+
Stop-ProcessTree $previousPid
|
|
73
|
+
}
|
|
42
74
|
Remove-Item $pidFile -Force
|
|
43
75
|
}
|
|
44
76
|
|
|
@@ -51,13 +83,37 @@ try {
|
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
$process.Id | Set-Content $pidFile
|
|
54
|
-
|
|
55
|
-
if ($
|
|
86
|
+
$processStartTime = $process.StartTime.ToUniversalTime().Ticks
|
|
87
|
+
if (-not (Wait-PortState $port $true 30)) {
|
|
56
88
|
Get-Content $errorLogFile -Tail 20 -ErrorAction SilentlyContinue
|
|
89
|
+
Stop-ProcessTree $process.Id
|
|
57
90
|
Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
|
|
58
|
-
|
|
91
|
+
throw "Dev server did not listen on port $port within 30 seconds."
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# 后台服务最长运行 20 分钟。回收前校验启动时间,避免 PID 复用后误杀无关进程。
|
|
95
|
+
$pidFileBase64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($pidFile))
|
|
96
|
+
$watchdogScript = @'
|
|
97
|
+
$targetProcessId = __PROCESS_ID__
|
|
98
|
+
$expectedStartTime = __START_TIME__
|
|
99
|
+
$pidFile = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('__PID_FILE__'))
|
|
100
|
+
Start-Sleep -Seconds 1200
|
|
101
|
+
$target = Get-Process -Id $targetProcessId -ErrorAction SilentlyContinue
|
|
102
|
+
if ($target -and $target.StartTime.ToUniversalTime().Ticks -eq $expectedStartTime) {
|
|
103
|
+
& taskkill.exe /PID $targetProcessId /T /F *> $null
|
|
104
|
+
}
|
|
105
|
+
$recordedPid = 0
|
|
106
|
+
if ((Test-Path -LiteralPath $pidFile) -and [int]::TryParse((Get-Content -LiteralPath $pidFile -Raw).Trim(), [ref] $recordedPid) -and $recordedPid -eq $targetProcessId) {
|
|
107
|
+
Remove-Item -LiteralPath $pidFile -Force -ErrorAction SilentlyContinue
|
|
59
108
|
}
|
|
109
|
+
'@
|
|
110
|
+
$watchdogScript = $watchdogScript.Replace('__PROCESS_ID__', "$($process.Id)").Replace('__START_TIME__', "$processStartTime").Replace('__PID_FILE__', $pidFileBase64)
|
|
111
|
+
$encodedWatchdog = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($watchdogScript))
|
|
112
|
+
Start-Process -FilePath "powershell.exe" -WindowStyle Hidden -ArgumentList @(
|
|
113
|
+
"-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", $encodedWatchdog
|
|
114
|
+
) | Out-Null
|
|
60
115
|
|
|
61
116
|
Write-Host "Dev server started (PID: $($process.Id))."
|
|
117
|
+
Write-Host "Auto stop after 1200 seconds."
|
|
62
118
|
Write-Host "Log file: $logFile"
|
|
63
119
|
<% } %>
|