@lark-apaas/coding-templates 0.1.43-alpha.14 → 0.1.43-alpha.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-templates",
3
- "version": "0.1.43-alpha.14",
3
+ "version": "0.1.43-alpha.15",
4
4
  "description": "OpenClaw project templates for mclaw CLI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -10,9 +10,3 @@ build = ["npm", "run", "build"]
10
10
  pathPatterns = ["package.json", ".spark_project", ".spark/meta.json", ".gitignore"]
11
11
  [files.hidden]
12
12
  pathPatterns = [".config", ".git", "scripts", "node_modules", "dist", ".spark", ".agent", ".agents", "tmp", ".spark_project", ".playwright-cli"]
13
-
14
- [sandbox]
15
- # 临时兼容老沙箱硬编码探测的 3000 / 8001 端口。
16
- # 这两个端口由 scripts/mock-health.cjs 占位,Vite dev server 实际监听 CLIENT_DEV_PORT(默认 8080)。
17
- # 沙箱链路切到新协议后,可改回 [8080],并删掉 mock-health.cjs。
18
- healthCheck = [8001, 3000]
@@ -15,47 +15,30 @@
15
15
  ## 目录结构
16
16
 
17
17
  ```
18
- .
19
- ├── index.html # Vite 入口 HTML
20
- ├── public/ # 类别 ②:原样 serve(favicon 等,下方"静态资源"章节)
21
- │ └── favicon.svg
22
- ├── src/ # 业务源码
23
- │ ├── index.tsx # 入口(勿修改)
24
- │ ├── app.tsx # 路由配置(仅在 <Routes> 内增删 <Route>)
25
- │ ├── index.css # 全局样式 + 主题变量
26
- │ ├── assets/ # 类别 ①:vite 打包的资源(小图标 / 组件用图片)
27
- │ │ └── logo.svg
28
- │ ├── components/ # 基础 UI 组件(禁止存放业务组件)
29
- │ │ ├── layout.tsx # 全局布局容器(含 <Outlet />)
30
- │ │ └── ui/ # shadcn/ui 内置组件(勿修改)
31
- │ ├── pages/ # 页面模块(每个页面一个目录)
32
- │ │ ├── <PageName>/ # 页面目录示例
33
- │ │ │ ├── PageName.tsx # 页面入口文件与目录同名
34
- │ │ │ └── components/ # 页面专属组件
35
- │ │ └── NotFoundPage/
36
- │ │ └── NotFoundPage.tsx
37
- │ ├── hooks/ # 自定义 Hooks
38
- │ └── lib/ # 工具函数(cn() 等)
39
- └── shared/
40
- └── static/ # 类别 ③:业务 CDN 私有资源
41
- ├── data/ # 数据文件(JSON)
42
- └── images/ # 图片资源
18
+ src/
19
+ ├── index.tsx # 入口(勿修改)
20
+ ├── app.tsx # 路由配置(仅在 <Routes> 内增删 <Route>)
21
+ ├── index.css # 全局样式 + 主题变量
22
+ ├── components/ # 基础 UI 组件(禁止存放业务组件)
23
+ │ ├── layout.tsx # 全局布局容器(含 <Outlet />)
24
+ │ └── ui/ # shadcn/ui 内置组件(勿修改)
25
+ ├── pages/ # 页面模块(每个页面一个目录)
26
+ │ ├── <PageName>/ # 页面目录示例
27
+ │ │ ├── PageName.tsx # 页面入口文件与目录同名
28
+ │ │ └── components/ # 页面专属组件
29
+ │ └── NotFoundPage/
30
+ │ └── NotFoundPage.tsx
31
+ ├── hooks/ # 自定义 Hooks
32
+ └── lib/ # 工具函数(cn() 等)
33
+
34
+ shared/
35
+ └── static/ # 静态资源
36
+ ├── data/ # 数据文件(JSON)
37
+ └── images/ # 图片资源
43
38
  ```
44
39
 
45
40
  ---
46
41
 
47
- ## 静态资源放哪里?三类不同位置
48
-
49
- | 类别 | 位置 | 用法 | 谁处理 |
50
- |------|------|------|--------|
51
- | ① **代码 import 的资源**<br/>(小图标 / 按钮图)| `src/assets/` | `import logo from '@/assets/logo.svg'` | vite 打包:加 hash、压缩、可 inline base64 |
52
- | ② **原样 serve 的资源**<br/>(favicon / robots.txt)| `public/` | `<link href="/favicon.svg" />`(URL 直接引)| vite dev/build 都原样输出,不进 bundle |
53
- | ③ **业务 CDN 私有资源**<br/>(配置 json / 大图)| `shared/static/` | `import data from '@shared/static/foo.json'` | build.sh → `dist/output_static/` → 私有 tos(应用权限管控)|
54
-
55
- > **为什么叫 `shared/`?** 沿用全栈模板时代的目录名(当时 `client/` ↔ `server/` 共享类型)。jsPage 没 server,但 `shared/static/` 这个目录被 preset 和 build.sh 认住了,改名要联动改下游链路,所以保留。语义上把它当作"CDN 私有静态目录"理解即可。
56
-
57
- ---
58
-
59
42
  ## 模板初始状态
60
43
 
61
44
  - `app.tsx` 首页路由指向平台内置的 `<Welcome />` 组件
@@ -81,10 +64,8 @@
81
64
  | 页面专属组件 | `src/pages/<PageName>/components/` |
82
65
  | 自定义 Hooks | `src/hooks/` |
83
66
  | 工具函数 | `src/lib/` |
84
- | 代码内 import 的图标 | `src/assets/` |
85
- | 走 URL 引用的静态文件 | `public/` |
86
- | 走业务 CDN 的私有静态 | `shared/static/`(`data/` JSON / `images/` 图片)|
87
- | 共享类型定义 | `shared/types.ts` |
67
+ | 静态数据文件 | `shared/static/data/` |
68
+ | 静态图片 | `shared/static/images/` |
88
69
 
89
70
  ---
90
71
 
@@ -94,14 +75,10 @@
94
75
  // @/ 别名 → src/
95
76
  import { cn } from "@/lib/utils";
96
77
  import { useIsMobile } from "@/hooks/use-mobile";
97
- import logo from "@/assets/logo.svg";
98
78
 
99
- // @shared/ 别名 → shared/(走 CDN 私有 tos)
79
+ // @shared/ 别名 → shared/
100
80
  import heroImage from "@shared/static/images/hero.png";
101
81
  import configData from "@shared/static/config.json";
102
-
103
- // public/ 不能 import,只能 URL 直接引
104
- // <link rel="icon" href="/favicon.svg" />
105
82
  ```
106
83
 
107
84
  ---
@@ -1,19 +1,12 @@
1
1
  {
2
- "name": "{{projectName}}",
2
+ "name": "miaoda-vite-react-app",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "mclaw": {
6
- "stack": "vite-react",
7
- "stackVersion": "0.1.0",
8
- "features": [
9
- "client"
10
- ]
11
- },
12
5
  "scripts": {
13
- "dev": "node scripts/dev.cjs",
6
+ "dev": "node scripts/dev.mjs",
14
7
  "build": "bash scripts/build.sh",
15
8
  "typecheck": "tsc -p tsconfig.app.json",
16
- "lint": "npm run typecheck && npm run lint:eslint",
9
+ "lint": "concurrently npm:typecheck npm:lint:eslint",
17
10
  "lint:eslint": "eslint src"
18
11
  },
19
12
  "dependencies": {
@@ -44,7 +37,7 @@
44
37
  "@radix-ui/react-toggle": "^1.1.9",
45
38
  "@radix-ui/react-toggle-group": "^1.1.10",
46
39
  "@radix-ui/react-tooltip": "^1.1.18",
47
- "@lark-apaas/client-toolkit-lite": "1.1.7-alpha.16",
40
+ "@lark-apaas/client-toolkit-lite": "1.1.7-alpha.17",
48
41
  "@formkit/auto-animate": "^0.9.0",
49
42
  "@gsap/react": "^2.1.2",
50
43
  "framer-motion": "^12.38.0",
@@ -80,7 +73,7 @@
80
73
  "@types/react": "^19",
81
74
  "@types/react-dom": "^19",
82
75
  "@lark-apaas/coding-presets": "^1.0.0",
83
- "@lark-apaas/coding-vite-preset": "1.0.7-alpha.12",
76
+ "@lark-apaas/coding-vite-preset": "1.0.7-alpha.13",
84
77
  "concurrently": "^9",
85
78
  "eslint": "^9",
86
79
  "eslint-plugin-react-hooks": "^7",
@@ -1,38 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * jsPage 模板 dev launcher。
4
- *
5
- * 跟全栈 dev.js 对齐:每个子进程的 stdout/stderr 经 readline 逐行拼上
6
- * 时间戳 + [name] 前缀,同时写到 logs/<name>.std.log(沙箱外壳 dev-logs
7
- * middleware 监听的标准路径)和当前 stdout。
8
- *
9
- * jsPage 只需要 client(vite) + health 两个进程,没有 server。
10
- * 端口约定(保持跟 .spark_project healthCheck 一致):
11
- * - vite → 8001(CLIENT_DEV_PORT 可覆盖)
12
- * - mock-health → 3000(占位,老沙箱探针兼容用)
13
- *
14
- * 进程组与孤儿清理:
15
- * - 子进程 detached: true → 自己一个 PGID,cleanup 时 process.kill(-pid) 整组带走
16
- * - 监听 SIGINT/SIGTERM/SIGHUP/uncaughtException 触发 cleanup
17
- * - 启动前 killOrphansByPort 清理端口残留孤儿(应对沙箱保活重启)
18
- *
19
- * 触发重启场景:
20
- * - `pkill -f "npm run dev"` 杀 npm 进程 → dev.cjs 收 SIGHUP → cleanup
21
- * - 沙箱保活机制重启 npm run dev → 新 dev.cjs → killOrphansByPort 清理旧 vite/mock-health
3
+ * Dev launcher: 启动 vite dev server,把 stdout/stderr 加时间戳前缀
4
+ * 写到 logs/<name>.std.log + 当前 stdout;负责子进程组管理与端口孤儿清理。
22
5
  */
23
- 'use strict';
24
6
 
25
- const fs = require('fs');
26
- const path = require('path');
27
- const readline = require('readline');
28
- const { spawn, execSync } = require('child_process');
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import readline from 'node:readline';
10
+ import { spawn, execSync } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
29
12
 
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
30
14
  const ROOT = path.resolve(__dirname, '..');
31
15
  process.chdir(ROOT);
32
16
 
33
17
  const LOG_DIR = process.env.LOG_DIR || 'logs';
34
18
  const CLIENT_DEV_PORT = process.env.CLIENT_DEV_PORT || '8001';
35
- const HEALTH_PORT = '3000';
36
19
 
37
20
  fs.mkdirSync(LOG_DIR, { recursive: true });
38
21
 
@@ -50,11 +33,7 @@ function log(msg) {
50
33
  try { process.stdout.write(line); } catch {}
51
34
  }
52
35
 
53
- /**
54
- * 启动前清理端口占用的孤儿进程(沙箱保活重启时关键):
55
- * 之前 dev.cjs 被 pkill 杀掉后,子进程的 vite/mock-health 可能没被一起带走,
56
- * 保活机制重启新 dev.cjs 时发现端口被占,导致 vite/mock-health 起不来。
57
- */
36
+ /** 清理端口占用:lsof -ti:<port> | kill -9 */
58
37
  function killOrphansByPort(port) {
59
38
  try {
60
39
  const out = execSync(`lsof -ti:${port}`, { stdio: ['ignore', 'pipe', 'ignore'] })
@@ -66,13 +45,10 @@ function killOrphansByPort(port) {
66
45
  try {
67
46
  process.kill(Number(pid), 'SIGKILL');
68
47
  log(`killed orphan pid=${pid} on :${port}`);
69
- } catch {
70
- // 进程可能已退出
71
- }
48
+ } catch {}
72
49
  }
73
50
  return pids;
74
51
  } catch {
75
- // lsof 没找到任何占用,正常情况
76
52
  return [];
77
53
  }
78
54
  }
@@ -109,9 +85,7 @@ function startProcess({ name, command, args, logFileName }) {
109
85
  return child;
110
86
  }
111
87
 
112
- // 先清掉可能残留的孤儿进程,再启动新子进程
113
88
  killOrphansByPort(CLIENT_DEV_PORT);
114
- killOrphansByPort(HEALTH_PORT);
115
89
 
116
90
  startProcess({
117
91
  name: 'client',
@@ -120,13 +94,6 @@ startProcess({
120
94
  logFileName: 'client.std.log',
121
95
  });
122
96
 
123
- startProcess({
124
- name: 'health',
125
- command: 'node',
126
- args: ['scripts/mock-health.cjs'],
127
- // health 进程不单独写 log,透传到 stdout 就够
128
- });
129
-
130
97
  let stopping = false;
131
98
  function cleanup(signal) {
132
99
  if (stopping) return;
@@ -142,21 +109,17 @@ function cleanup(signal) {
142
109
  if (!child.pid) continue;
143
110
  try { process.kill(-child.pid, 'SIGKILL'); } catch {}
144
111
  }
145
- // 兜底再扫一次端口,确保孤儿被清理
146
112
  killOrphansByPort(CLIENT_DEV_PORT);
147
- killOrphansByPort(HEALTH_PORT);
148
113
  process.exit(0);
149
114
  }, 2000);
150
115
  }
151
116
 
152
117
  process.on('SIGINT', () => cleanup('SIGTERM'));
153
118
  process.on('SIGTERM', () => cleanup('SIGTERM'));
154
- // pkill -f "npm run dev" 把 npm 杀掉后,dev.cjs 会收到 SIGHUP(controlling
155
- // tty 关闭),Node 默认行为是直接退出不跑 handler。注册 SIGHUP handler 触发
156
- // cleanup,让子进程组跟着一起死,避免变孤儿
119
+ // pkill 杀父 npm 后会收 SIGHUP(controlling tty 关闭);
120
+ // Node 默认直接退出不跑 handler,注册 handler 触发 cleanup
157
121
  process.on('SIGHUP', () => cleanup('SIGTERM'));
158
122
 
159
- // 任意子进程退出就触发整体 cleanup,避免半残留
160
123
  Promise.race(
161
- managed.map(({ child }) => new Promise((r) => child.on('exit', r)))
124
+ managed.map(({ child }) => new Promise((r) => child.on('exit', r))),
162
125
  ).then(() => cleanup('SIGTERM'));
@@ -1,8 +0,0 @@
1
- {
2
- "appId": "app_xxxxxxxxxxxxxx",
3
- "template": "vite-react",
4
- "version": "0.1.0",
5
- "archType": 2,
6
- "features": ["client"],
7
- "appUrl": "https://bytedance.aiforce.cloud/app/app_xxxxxxxxxxxxxx"
8
- }
@@ -1,8 +0,0 @@
1
- {
2
- "appId": "app_xxxxxxxxxxxxxx",
3
- "template": "vite-react",
4
- "version": "0.1.0",
5
- "archType": 2,
6
- "features": ["client"],
7
- "appUrl": "https://bytedance.aiforce.cloud/app/app_xxxxxxxxxxxxxx"
8
- }
@@ -1,219 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 测沙箱(或本机)模板从 0 到"预览可访问"的耗时拆解。
3
- # 默认只测 dev 启动那段(不跑 npm install),符合沙箱里依赖已装好的常见场景。
4
- #
5
- # 用法:
6
- # bash scripts/measure-boot.sh # 默认:只测 dev 启动(warm)
7
- # bash scripts/measure-boot.sh --cold # 冷启动:清光 vite 预打包缓存
8
- # bash scripts/measure-boot.sh --with-install # 顺带跑一次 npm install 测装包耗时
9
- # bash scripts/measure-boot.sh --with-install --cold # 模拟首次冷启(清 node_modules + .vite)
10
- # bash scripts/measure-boot.sh --port 18001 # 临时换端口
11
- # bash scripts/measure-boot.sh --base /app/whatever # 临时换 CLIENT_BASE_PATH
12
- #
13
- # 兼容:--skip-install 等同于不传(默认就是跳过),保留作为 no-op 防止旧脚本/手势失败。
14
- #
15
- # 输出:每段耗时 + 总耗时,写到 ./logs/boot-timing.log(同时打到 stdout)
16
-
17
- set -uo pipefail
18
- T1=0; T2=0
19
- DEV_PID=0
20
-
21
- WITH_INSTALL=0
22
- COLD=0
23
- PORT=""
24
- BASE="/app/probe"
25
- LOG_DIR="./logs"
26
- LOG_FILE="$LOG_DIR/boot-timing.log"
27
-
28
- while [ $# -gt 0 ]; do
29
- case "$1" in
30
- --with-install) WITH_INSTALL=1; shift ;;
31
- --skip-install) shift ;; # no-op,兼容旧手势;默认就是跳过
32
- --cold) COLD=1; shift ;;
33
- --port) PORT="$2"; shift 2 ;;
34
- --base) BASE="$2"; shift 2 ;;
35
- *) echo "未知参数: $1"; exit 1 ;;
36
- esac
37
- done
38
-
39
- mkdir -p "$LOG_DIR"
40
- : > "$LOG_FILE"
41
-
42
- now_ms() {
43
- # GNU date 支持 %N,macOS 的 BSD date 不支持——优先用 python3 兜底,避免 sandbox 上 BSD 镜像出错。
44
- if command -v python3 >/dev/null 2>&1; then
45
- python3 -c 'import time; print(int(time.time()*1000))'
46
- else
47
- local raw="$(date +%s%3N)"
48
- case "$raw" in
49
- *N) echo $(($(date +%s) * 1000));; # date 不支持 %N,降级到秒级精度
50
- *) echo "$raw";;
51
- esac
52
- fi
53
- }
54
- log() {
55
- local msg="[$(date +%H:%M:%S)] $*"
56
- echo "$msg" | tee -a "$LOG_FILE"
57
- }
58
-
59
- # 自动从 package.json 提取 vite 端口
60
- if [ -z "$PORT" ]; then
61
- PORT=$(grep -oE -- '--port[[:space:]]+[0-9]+' package.json 2>/dev/null | head -1 | awk '{print $2}')
62
- PORT="${PORT:-8001}"
63
- fi
64
-
65
- export CLIENT_BASE_PATH="$BASE"
66
-
67
- log "=============================================="
68
- log "Boot timing — port=$PORT base=$BASE with-install=$WITH_INSTALL cold=$COLD"
69
- log "=============================================="
70
-
71
- # ── 冷启动:清光本地缓存(不计入耗时,T_TOTAL_START 之前完成)────────
72
- if [ "$COLD" = "1" ]; then
73
- log "[cold] 清缓存以模拟冷启动..."
74
- # vite 依赖预打包缓存(永远要清,热启动的最大区别就在这)
75
- if [ -d node_modules/.vite ]; then
76
- log " - rm -rf node_modules/.vite"
77
- rm -rf node_modules/.vite
78
- fi
79
- # 本脚本上轮日志
80
- if [ -d "$LOG_DIR" ]; then
81
- log " - 清 $LOG_DIR/dev.log(避免 grep 命中旧 ready 行)"
82
- : > "$LOG_DIR/dev.log" 2>/dev/null || true
83
- fi
84
- # 项目级 dist(一般不影响 dev,但顺手)
85
- [ -d dist ] && rm -rf dist
86
- # 默认保留 node_modules(典型冷启动对照:装好依赖、只清 .vite)
87
- # 仅 --with-install 模式才清 node_modules 触发 npm install 重装
88
- if [ "$WITH_INSTALL" = "1" ]; then
89
- if [ -d node_modules ]; then
90
- log " - rm -rf node_modules(--with-install 模式:清依赖以模拟首次冷启)"
91
- rm -rf node_modules
92
- fi
93
- # npm 全局缓存留着(不动 /tmp/.npmcache / ~/.npm),不然太慢且不真实
94
- else
95
- log " - 保留 node_modules(默认模式:只清 .vite 缓存)"
96
- fi
97
- log "[cold] 缓存清理完成"
98
- fi
99
-
100
- T_TOTAL_START=$(now_ms)
101
-
102
- # ── 阶段 1: npm install ──────────────────────────────────────────────
103
- if [ "$WITH_INSTALL" = "1" ]; then
104
- log "[1/4] npm install ..."
105
- T1=$(now_ms)
106
- npm install --silent > "$LOG_DIR/install.log" 2>&1
107
- RC=$?
108
- T2=$(now_ms)
109
- if [ $RC -ne 0 ]; then
110
- log " ❌ npm install 失败(exit=$RC,详见 $LOG_DIR/install.log)"
111
- exit 1
112
- fi
113
- log " ✅ 耗时 $((T2 - T1)) ms"
114
- else
115
- log "[1/4] 跳过 npm install(默认;用 --with-install 启用)"
116
- fi
117
-
118
- # ── 阶段 2: 启动 dev(进程拉起到出现 \"VITE ready\" 提示)──────────────
119
- log "[2/4] npm run dev — 等 vite ready 提示 ..."
120
-
121
- # 杀光占用目标端口的所有进程(不依赖进程名匹配,沙箱里 vite 可能被各种 wrapper 拉起)
122
- kill_port() {
123
- local p="$1"
124
- local pids
125
- pids=$(lsof -tiTCP:"$p" -sTCP:LISTEN 2>/dev/null | tr '\n' ' ')
126
- if [ -n "$pids" ]; then
127
- log " 清理 :$p 上的进程: $pids"
128
- # shellcheck disable=SC2086
129
- kill -9 $pids 2>/dev/null || true
130
- fi
131
- }
132
- kill_port "$PORT"
133
- kill_port 3000
134
- # 再保险一道:按名字
135
- pkill -f 'concurrently.*vite' 2>/dev/null || true
136
- pkill -f 'mock-health' 2>/dev/null || true
137
- sleep 1
138
-
139
- # 二次校验:端口必须真的空了
140
- if lsof -tiTCP:"$PORT" -sTCP:LISTEN >/dev/null 2>&1; then
141
- log " ❌ 清理后 :$PORT 仍被占用,请手动 kill 后重试"
142
- log " lsof -i :$PORT"
143
- exit 1
144
- fi
145
-
146
- # 截断 log 文件确保 grep 命中的是本次启动的输出(不是旧 log 残留)
147
- : > "$LOG_DIR/dev.log"
148
-
149
- T3=$(now_ms)
150
- nohup npm run dev > "$LOG_DIR/dev.log" 2>&1 &
151
- DEV_PID=$!
152
-
153
- # 等 vite ready 提示(最多 60s)
154
- T_READY=""
155
- for i in $(seq 1 600); do
156
- if grep -q 'VITE.*ready' "$LOG_DIR/dev.log" 2>/dev/null; then
157
- T_READY=$(now_ms)
158
- break
159
- fi
160
- # 短路:如果 dev 进程已经退出且没出现 ready,提早失败
161
- if ! kill -0 "$DEV_PID" 2>/dev/null; then
162
- log " ❌ dev 进程提前退出,看 $LOG_DIR/dev.log"
163
- tail -20 "$LOG_DIR/dev.log" | sed 's/^/ /' | tee -a "$LOG_FILE"
164
- exit 1
165
- fi
166
- sleep 0.1
167
- done
168
-
169
- if [ -z "$T_READY" ]; then
170
- log " ❌ 60s 内未看到 vite ready 提示,看 $LOG_DIR/dev.log"
171
- kill ${DEV_PID} 2>/dev/null
172
- exit 1
173
- fi
174
- log " ✅ 耗时 $((T_READY - T3)) ms"
175
-
176
- # ── 阶段 3: 端口可 TCP 连接 ──────────────────────────────────────────
177
- log "[3/4] 端口 :$PORT 接受 TCP 连接 ..."
178
- T4=$(now_ms)
179
- T_TCP=""
180
- for i in $(seq 1 100); do
181
- if (echo > /dev/tcp/127.0.0.1/$PORT) 2>/dev/null; then
182
- T_TCP=$(now_ms)
183
- break
184
- fi
185
- sleep 0.05
186
- done
187
- if [ -z "$T_TCP" ]; then
188
- log " ❌ 5s 内端口不可连接"
189
- kill ${DEV_PID} 2>/dev/null
190
- exit 1
191
- fi
192
- log " ✅ 耗时 $((T_TCP - T4)) ms(自 ready 后再 $((T_TCP - T_READY)) ms)"
193
-
194
- # ── 阶段 4: 首屏 HTML 200 ─────────────────────────────────────────────
195
- log "[4/4] curl 首屏(含 base normalize rewrite 路径)..."
196
- T5=$(now_ms)
197
- URL="http://127.0.0.1:$PORT${BASE}?probe=1"
198
- HTTP_CODE=$(curl -sS -o "$LOG_DIR/first.html" -w '%{http_code}' --max-time 10 "$URL" 2>&1)
199
- T6=$(now_ms)
200
- SIZE=$(wc -c < "$LOG_DIR/first.html" 2>/dev/null | tr -d ' ')
201
-
202
- log " URL=$URL"
203
- log " HTTP=$HTTP_CODE size=${SIZE}B 耗时 $((T6 - T5)) ms"
204
-
205
- if [ "$HTTP_CODE" != "200" ]; then
206
- log " ⚠️ 非 200,看 $LOG_DIR/first.html"
207
- fi
208
-
209
- # ── 汇总 ──────────────────────────────────────────────────────────────
210
- T_TOTAL_END=$(now_ms)
211
- log "=============================================="
212
- log "总耗时 $((T_TOTAL_END - T_TOTAL_START)) ms"
213
- log "dev 进程 PID=${DEV_PID}(kill 后退出:kill ${DEV_PID})"
214
- log " install: $([ $WITH_INSTALL = 1 ] && echo "$((T2 - T1)) ms" || echo skipped)"
215
- log " vite ready: $((T_READY - T3)) ms"
216
- log " tcp accept after ready: $((T_TCP - T_READY)) ms"
217
- log " first html curl: $((T6 - T5)) ms"
218
- log "=============================================="
219
- log "详细日志:$LOG_FILE / $LOG_DIR/dev.log"
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 沙箱预览态健康检查兜底:在 3000 端口起 HTTP listener。
4
- *
5
- * 老沙箱硬编码探测 3000 + 8001 判定"应用启动了"。jsPage 没有真实 Node server,
6
- * 8001 由 Vite dev server 真正占(预览流量也走 8001),3000 没有任何业务进程,
7
- * 只能 mock 一个 listener 让健康检查过。
8
- *
9
- * 等沙箱链路切到新协议(读 .spark_project [sandbox].healthCheck)后可删除。
10
- */
11
- const http = require('node:http');
12
-
13
- const PORTS = [3000];
14
-
15
- for (const port of PORTS) {
16
- const server = http.createServer((_req, res) => {
17
- res.writeHead(200, { 'content-type': 'text/plain' });
18
- res.end('ok');
19
- });
20
- server.on('error', (err) => {
21
- console.error(`[mock-health] :${port} listen error:`, err.message);
22
- if (err.code === 'EADDRINUSE') {
23
- // 端口已被占说明业务已起来,mock 退场即可
24
- console.warn(`[mock-health] :${port} 已被占用,跳过 mock`);
25
- return;
26
- }
27
- process.exitCode = 1;
28
- });
29
- server.listen(port, '0.0.0.0', () => {
30
- console.log(`[mock-health] listening on :${port}`);
31
- });
32
- }
@@ -1,4 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
- <rect width="64" height="64" rx="12" fill="currentColor" opacity="0.1"/>
3
- <path d="M20 32 L28 40 L44 24" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
4
- </svg>