@lark-apaas/coding-template-nestjs-react-fullstack 0.1.27-alpha.20260824122815 → 0.1.27

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.
@@ -30,13 +30,13 @@
30
30
  "lint": "node ./scripts/lint.js",
31
31
  "format": "prettier --write \"{server,client,test}/**/*.{js,ts,tsx,json,md}\"",
32
32
  "precommit": "node scripts/hooks/run-precommit.js",
33
- "upgrade": "npx -y @lark-apaas/fullstack-cli@1.1.62-alpha.20260824121535 sync --disable-gen-openapi"
33
+ "upgrade": "npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi"
34
34
  },
35
35
  "dependencies": {
36
36
  "@lark-apaas/fullstack-nestjs-core": "^1.1.59",
37
37
  "@nestjs/axios": "^4.0.1",
38
- "@nestjs/cache-manager": "^3.1.3",
39
38
  "@nestjs/common": "^10.4.20",
39
+ "@nestjs/cache-manager": "^3.1.3",
40
40
  "@nestjs/config": "^3.3.0",
41
41
  "@nestjs/core": "^10.4.20",
42
42
  "@nestjs/platform-express": "^10.4.20",
@@ -62,9 +62,9 @@
62
62
  "@headlessui/react": "^2.2.9",
63
63
  "@heroicons/react": "^2.2.0",
64
64
  "@hookform/resolvers": "^5.2.2",
65
- "@lark-apaas/client-toolkit": "^1.2.63",
66
- "@lark-apaas/coding-preset-vite-react": "1.0.19-alpha.20260824121536",
65
+ "@lark-apaas/client-toolkit": "^1.2.65",
67
66
  "@lark-apaas/fullstack-presets": "^1.1.0",
67
+ "@lark-apaas/fullstack-vite-preset": "^1.0.35",
68
68
  "@nestjs/cli": "^10.4.9",
69
69
  "@nestjs/testing": "^10.4.20",
70
70
  "@radix-ui/react-accordion": "^1.2.12",
@@ -181,7 +181,7 @@
181
181
  "typescript-eslint": "^8.44.0",
182
182
  "uuid": "11.0.5",
183
183
  "vaul": "^1.1.2",
184
- "vite": "^8",
184
+ "vite": "^7.3.1",
185
185
  "xlsx": "^0.18.5",
186
186
  "xlsx-js-style": "^1.2.0",
187
187
  "zod": "^3.25.76",
@@ -89,18 +89,6 @@ cp "$ROOT_DIR/dist/client/"*.html "$OUT_DIR/dist/client/" || true
89
89
  # 拷贝 run.sh 文件
90
90
  cp "$ROOT_DIR/scripts/run.sh" "$OUT_DIR/"
91
91
 
92
- # 拷贝 shared/static → OUT_DIR/shared/static
93
- # Nest server 生产从 process.cwd() + shared/static 读 (nestjs-core static.controller.ts:100),
94
- # 生产 cwd = $OUT_DIR (dist/server), 所以目标是 dist/server/shared/static。
95
- # coding-preset 的 static-assets plugin 之前 closeBundle 拷贝, 现在删除后由 build.sh 显式做。
96
- # 排除 .ts/.tsx/.js/.jsx: 避免 shared/static/ 里若有开发脚本被误传到生产。
97
- if [ -d "$ROOT_DIR/shared/static" ]; then
98
- mkdir -p "$OUT_DIR/shared/static"
99
- rsync -a --exclude='*.ts' --exclude='*.tsx' --exclude='*.js' --exclude='*.jsx' \
100
- "$ROOT_DIR/shared/static/" "$OUT_DIR/shared/static/"
101
- echo " Static → dist/server/shared/static/"
102
- fi
103
-
104
92
  # 清理无用文件
105
93
  rm -rf "$ROOT_DIR/dist/scripts"
106
94
  rm -rf "$ROOT_DIR/dist/tsconfig.node.tsbuildinfo"
@@ -9,9 +9,6 @@ const readline = require('readline');
9
9
  // ── Project root ──────────────────────────────────────────────────────────────
10
10
  const PROJECT_ROOT = path.resolve(__dirname, '..');
11
11
  process.chdir(PROJECT_ROOT);
12
- // Runtime scripts may be image-owned and therefore live outside the workspace.
13
- // Keep the project identity explicit for validation and all supervised children.
14
- process.env.MIAODA_WORKSPACE_ROOT ||= PROJECT_ROOT;
15
12
 
16
13
  // ── Load .env ─────────────────────────────────────────────────────────────────
17
14
  function loadEnv() {
@@ -34,143 +31,15 @@ loadEnv();
34
31
 
35
32
  // ── Configuration ─────────────────────────────────────────────────────────────
36
33
  const LOG_DIR = process.env.LOG_DIR || 'logs';
37
- const MAX_RESTART_COUNT =
38
- process.env.MAX_RESTART_COUNT != null && process.env.MAX_RESTART_COUNT !== ''
39
- ? parseInt(process.env.MAX_RESTART_COUNT, 10)
40
- : Infinity;
34
+ const MAX_RESTART_COUNT = process.env.MAX_RESTART_COUNT != null && process.env.MAX_RESTART_COUNT !== ''
35
+ ? parseInt(process.env.MAX_RESTART_COUNT, 10)
36
+ : Infinity;
41
37
  const RESTART_DELAY = parseInt(process.env.RESTART_DELAY, 10) || 2;
42
38
  const MAX_DELAY = 8;
43
39
  const SERVER_PORT = process.env.SERVER_PORT || '3000';
44
40
  const CLIENT_DEV_PORT = process.env.CLIENT_DEV_PORT || '8080';
45
- const WORKSPACE_READY_FILE = path.resolve(
46
- process.env.MIAODA_WORKSPACE_READY_FILE || '/tmp/event/WORKSPACE_READY'
47
- );
48
- const ACTION_PLUGIN_READY_FILE = path.resolve(
49
- process.env.MIAODA_ACTION_PLUGIN_READY_FILE ||
50
- '/tmp/event/MIAODA_ACTION_PLUGIN_READY.json'
51
- );
52
- const NEST_ORCHESTRATOR_OWNER_FILE = path.resolve(
53
- process.env.MIAODA_NEST_ORCHESTRATOR_OWNER_FILE ||
54
- '/tmp/event/MIAODA_NEST_ORCHESTRATOR_OWNER.json'
55
- );
56
- let STARTUP_EXPERIMENT_ENABLED =
57
- process.env.MIAODA_STARTUP_EXPERIMENT_ENABLED === 'true';
58
- const STARTUP_RUNTIME_SCRIPTS_ROOT = path.resolve(
59
- process.env.MIAODA_CACHE_RUNTIME_SCRIPTS_ROOT ||
60
- path.join(PROJECT_ROOT, 'scripts')
61
- );
62
-
63
- // When the image launched a bundle orchestrator before this project process
64
- // existed, it owns the Nest lifecycle and records itself here. dev.js then
65
- // follows that owner instead of competing for the public server port.
66
- function readExternalNestOrchestratorOwner() {
67
- try {
68
- const owner = JSON.parse(
69
- fs.readFileSync(NEST_ORCHESTRATOR_OWNER_FILE, 'utf8')
70
- );
71
- const pid = Number(owner.pid);
72
- if (
73
- owner.schemaVersion !== 1 ||
74
- owner.mode !== (process.env.MIAODA_NEST_START_MODE || 'workspace') ||
75
- !Number.isSafeInteger(pid) ||
76
- pid <= 1 ||
77
- pid === process.pid
78
- ) {
79
- return undefined;
80
- }
81
- process.kill(pid, 0);
82
- return { pid, mode: owner.mode };
83
- } catch {
84
- return undefined;
85
- }
86
- }
87
-
88
- function workspaceDependenciesReady() {
89
- try {
90
- return (
91
- fs.statSync(WORKSPACE_READY_FILE).isFile() &&
92
- fs.statSync(path.join(PROJECT_ROOT, 'node_modules')).isDirectory()
93
- );
94
- } catch {
95
- return false;
96
- }
97
- }
98
-
99
- async function waitForWorkspaceDependencies() {
100
- while (!stopping && !workspaceDependenciesReady()) await sleep(100);
101
- if (stopping) throw new Error('MIAODA_ACTION_PLUGIN_STARTUP_STOPPING');
102
- }
103
-
104
- function publishActionPluginReady(status, startedAtEpochMs) {
105
- const atEpochMs = Date.now();
106
- let restoreEpochMs = process.env.MIAODA_PLATFORM_ENV_NOT_BEFORE_MS || '';
107
- if (!restoreEpochMs) {
108
- try {
109
- const dependencyReceipt = JSON.parse(
110
- fs.readFileSync(
111
- path.join(PROJECT_ROOT, '.miaoda-runtime', 'dependencies-ready.json'),
112
- 'utf8'
113
- )
114
- );
115
- restoreEpochMs = String(dependencyReceipt.restoreEpochMs || '');
116
- } catch {}
117
- }
118
- if (restoreEpochMs) {
119
- process.env.MIAODA_PLATFORM_ENV_NOT_BEFORE_MS = restoreEpochMs;
120
- }
121
- const marker = {
122
- schemaVersion: 1,
123
- restoreEpochMs,
124
- status,
125
- atEpochMs,
126
- };
127
- fs.mkdirSync(path.dirname(ACTION_PLUGIN_READY_FILE), { recursive: true });
128
- const temporaryFile = `${ACTION_PLUGIN_READY_FILE}.${process.pid}.tmp`;
129
- fs.writeFileSync(temporaryFile, `${JSON.stringify(marker)}\n`, {
130
- mode: 0o600,
131
- });
132
- fs.renameSync(temporaryFile, ACTION_PLUGIN_READY_FILE);
133
- process.env.MIAODA_NEST_SKIP_ACTION_PLUGIN_INIT = 'true';
134
- writeOutput(
135
- `${JSON.stringify({
136
- event: 'miaoda_workspace_runtime_phase',
137
- runtime: 'shared',
138
- mode: process.env.MIAODA_NEST_START_MODE || 'workspace',
139
- phase: 'action_plugin_init',
140
- phaseStartedAtEpochMs: startedAtEpochMs,
141
- atEpochMs,
142
- durationMs: atEpochMs - startedAtEpochMs,
143
- status,
144
- readyFile: ACTION_PLUGIN_READY_FILE,
145
- })}\n`
146
- );
147
- }
148
-
149
- function initializeActionPlugins(publishReady = false) {
150
- const startedAtEpochMs = Date.now();
151
- let status = 'success';
152
- writeOutput('\n🔌 Initializing action plugins...\n');
153
- try {
154
- execSync('fullstack-cli action-plugin init', {
155
- cwd: PROJECT_ROOT,
156
- stdio: 'inherit',
157
- });
158
- writeOutput('✅ Action plugins initialized\n\n');
159
- } catch {
160
- status = 'failed-continued';
161
- writeOutput(
162
- '⚠️ Action plugin initialization failed, continuing anyway...\n\n'
163
- );
164
- }
165
- if (publishReady) publishActionPluginReady(status, startedAtEpochMs);
166
- }
167
41
 
168
42
  fs.mkdirSync(LOG_DIR, { recursive: true });
169
- process.once('exit', () => {
170
- try {
171
- fs.rmSync(ACTION_PLUGIN_READY_FILE, { force: true });
172
- } catch {}
173
- });
174
43
 
175
44
  // ── Logging infrastructure ────────────────────────────────────────────────────
176
45
  const devStdLogPath = path.join(LOG_DIR, 'dev.std.log');
@@ -181,56 +50,26 @@ const devLogFd = fs.openSync(devLogPath, 'a');
181
50
  function timestamp() {
182
51
  const now = new Date();
183
52
  return (
184
- now.getFullYear() +
185
- '-' +
186
- String(now.getMonth() + 1).padStart(2, '0') +
187
- '-' +
188
- String(now.getDate()).padStart(2, '0') +
189
- ' ' +
190
- String(now.getHours()).padStart(2, '0') +
191
- ':' +
192
- String(now.getMinutes()).padStart(2, '0') +
193
- ':' +
53
+ now.getFullYear() + '-' +
54
+ String(now.getMonth() + 1).padStart(2, '0') + '-' +
55
+ String(now.getDate()).padStart(2, '0') + ' ' +
56
+ String(now.getHours()).padStart(2, '0') + ':' +
57
+ String(now.getMinutes()).padStart(2, '0') + ':' +
194
58
  String(now.getSeconds()).padStart(2, '0')
195
59
  );
196
60
  }
197
61
 
198
- // Cap pending async stdout writes so a stalled pty consumer can't grow memory unbounded.
199
- // Each pending fs.write retains ~1.5 KB (msg + libuv/V8 wrappers); 1000 ≈ ~1.5 MB ceiling.
200
- let _stdoutInFlight = 0;
201
- const STDOUT_MAX_INFLIGHT = 1000;
202
-
203
- /** Write to dev.std.log (sync, guaranteed) + mirror to terminal (async, non-blocking) */
62
+ /** Write to terminal + dev.std.log */
204
63
  function writeOutput(msg) {
205
- // File first and synchronously — read-logs reads this file; it must never be gated
206
- // by the terminal consumer.
207
- try {
208
- fs.writeSync(devStdLogFd, msg);
209
- } catch {}
210
- // stdout mirror via async fs.write: if process.stdout is a pty/pipe whose consumer
211
- // stalls, the block happens on the libuv threadpool, NOT the event loop — so the
212
- // synchronous log-FILE writes above (and subsequent readline callbacks) keep running.
213
- // Drop overflow when too many writes are already pending (best-effort mirror).
214
- if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) {
215
- return;
216
- }
217
- _stdoutInFlight++;
218
- try {
219
- fs.write(1, msg, () => {
220
- _stdoutInFlight--;
221
- });
222
- } catch {
223
- _stdoutInFlight--;
224
- }
64
+ try { process.stdout.write(msg); } catch {}
65
+ try { fs.writeSync(devStdLogFd, msg); } catch {}
225
66
  }
226
67
 
227
68
  /** Structured event log → terminal + dev.std.log + dev.log */
228
69
  function logEvent(level, name, message) {
229
70
  const msg = `[${timestamp()}] [${level}] [${name}] ${message}\n`;
230
71
  writeOutput(msg);
231
- try {
232
- fs.writeSync(devLogFd, msg);
233
- } catch {}
72
+ try { fs.writeSync(devLogFd, msg); } catch {}
234
73
  }
235
74
 
236
75
  // ── Process group management ──────────────────────────────────────────────────
@@ -242,16 +81,11 @@ function killProcessGroup(pid, signal) {
242
81
 
243
82
  function killOrphansByPort(port) {
244
83
  try {
245
- const pids = execSync(`lsof -ti :${port}`, {
246
- encoding: 'utf8',
247
- timeout: 5000,
248
- }).trim();
84
+ const pids = execSync(`lsof -ti :${port}`, { encoding: 'utf8', timeout: 5000 }).trim();
249
85
  if (pids) {
250
86
  const pidList = pids.split('\n').filter(Boolean);
251
87
  for (const p of pidList) {
252
- try {
253
- process.kill(parseInt(p, 10), 'SIGKILL');
254
- } catch {}
88
+ try { process.kill(parseInt(p, 10), 'SIGKILL'); } catch {}
255
89
  }
256
90
  return pidList;
257
91
  }
@@ -264,7 +98,7 @@ let stopping = false;
264
98
  const managedProcesses = []; // { name, pid, child }
265
99
 
266
100
  function sleep(ms) {
267
- return new Promise(resolve => setTimeout(resolve, ms));
101
+ return new Promise((resolve) => setTimeout(resolve, ms));
268
102
  }
269
103
 
270
104
  /**
@@ -294,23 +128,14 @@ function startProcess({ name, command, args, cleanupPort }) {
294
128
  entry.child = child;
295
129
 
296
130
  const startTime = Date.now();
297
- logEvent(
298
- 'INFO',
299
- name,
300
- `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`
301
- );
131
+ logEvent('INFO', name, `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`);
302
132
 
303
133
  // Pipe stdout and stderr through readline for timestamped logging
304
- const pipeLines = stream => {
305
- const rl = readline.createInterface({
306
- input: stream,
307
- crlfDelay: Infinity,
308
- });
309
- rl.on('line', line => {
134
+ const pipeLines = (stream) => {
135
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
136
+ rl.on('line', (line) => {
310
137
  const msg = `[${timestamp()}] [${name}] ${line}\n`;
311
- try {
312
- fs.writeSync(logFd, msg);
313
- } catch {}
138
+ try { fs.writeSync(logFd, msg); } catch {}
314
139
  writeOutput(msg);
315
140
  });
316
141
  };
@@ -321,8 +146,8 @@ function startProcess({ name, command, args, cleanupPort }) {
321
146
  // NOTE: must use 'exit', not 'close'. With shell:true, grandchild processes
322
147
  // (e.g. nest's server) inherit stdout pipes. 'close' won't fire until ALL
323
148
  // pipe holders exit, causing dev.js to hang when npm/nest dies but server survives.
324
- const exitCode = await new Promise(resolve => {
325
- child.on('exit', code => resolve(code ?? 1));
149
+ const exitCode = await new Promise((resolve) => {
150
+ child.on('exit', (code) => resolve(code ?? 1));
326
151
  child.on('error', () => resolve(1));
327
152
  });
328
153
 
@@ -339,11 +164,7 @@ function startProcess({ name, command, args, cleanupPort }) {
339
164
  if (cleanupPort) {
340
165
  const orphans = killOrphansByPort(cleanupPort);
341
166
  if (orphans.length > 0) {
342
- logEvent(
343
- 'WARN',
344
- name,
345
- `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`
346
- );
167
+ logEvent('WARN', name, `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`);
347
168
  await sleep(500);
348
169
  }
349
170
  }
@@ -353,38 +174,21 @@ function startProcess({ name, command, args, cleanupPort }) {
353
174
  const runDuration = (Date.now() - startTime) / 1000;
354
175
  if (runDuration >= 60) {
355
176
  restartCount = 0;
356
- logEvent(
357
- 'INFO',
358
- name,
359
- `Ran for ${Math.round(runDuration)}s, resetting restart counter`
360
- );
177
+ logEvent('INFO', name, `Ran for ${Math.round(runDuration)}s, resetting restart counter`);
361
178
  } else {
362
179
  restartCount++;
363
180
  }
364
181
  if (restartCount >= MAX_RESTART_COUNT) {
365
- logEvent(
366
- 'ERROR',
367
- name,
368
- `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`
369
- );
182
+ logEvent('ERROR', name, `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`);
370
183
  break;
371
184
  }
372
185
 
373
- const delay = Math.min(
374
- RESTART_DELAY * (1 << Math.max(0, restartCount - 1)),
375
- MAX_DELAY
376
- );
377
- logEvent(
378
- 'WARN',
379
- name,
380
- `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`
381
- );
186
+ const delay = Math.min(RESTART_DELAY * (1 << Math.max(0, restartCount - 1)), MAX_DELAY);
187
+ logEvent('WARN', name, `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`);
382
188
  await sleep(delay * 1000);
383
189
  }
384
190
 
385
- try {
386
- fs.closeSync(logFd);
387
- } catch {}
191
+ try { fs.closeSync(logFd); } catch {}
388
192
  };
389
193
 
390
194
  return run();
@@ -414,11 +218,7 @@ async function cleanup() {
414
218
  // Force kill any remaining
415
219
  for (const entry of managedProcesses) {
416
220
  if (entry.pid) {
417
- logEvent(
418
- 'WARN',
419
- 'main',
420
- `Force killing process group (PGID: ${entry.pid})`
421
- );
221
+ logEvent('WARN', 'main', `Force killing process group (PGID: ${entry.pid})`);
422
222
  killProcessGroup(entry.pid, 'SIGKILL');
423
223
  }
424
224
  }
@@ -429,12 +229,8 @@ async function cleanup() {
429
229
 
430
230
  logEvent('INFO', 'main', 'All processes stopped');
431
231
 
432
- try {
433
- fs.closeSync(devStdLogFd);
434
- } catch {}
435
- try {
436
- fs.closeSync(devLogFd);
437
- } catch {}
232
+ try { fs.closeSync(devStdLogFd); } catch {}
233
+ try { fs.closeSync(devLogFd); } catch {}
438
234
 
439
235
  process.exit(0);
440
236
  }
@@ -443,198 +239,44 @@ process.on('SIGTERM', cleanup);
443
239
  process.on('SIGINT', cleanup);
444
240
  process.on('SIGHUP', cleanup);
445
241
 
446
- // dist 缓存策略:
447
- // 沙箱首次启动 保留 dist/, 让 nest --watch 复用上次编译产物
448
- // 后续启动 (agent 主动触发 npm run dev) → 全清 dist/, 让模型面对新鲜产物避免 stale
449
- // 依赖升级 (lockfile hash 变) → 同后续启动 (清 dist), log 里点出 "deps changed"
450
- // FORCE_CLEAN_DIST=true → escape hatch, 强清
451
- // marker 放 os.tmpdir()/.miaoda-dev-boot (跟沙箱容器 lifecycle 同, 沙箱重启会清空,
452
- // 语义对齐"沙箱首次启动"); 具体逻辑抽到 lib/preserve-dev-cache.cjs 便于单测。
453
- const { preserveDevCache } = require('./lib/preserve-dev-cache.cjs');
454
-
455
- function prepareTypeScriptBuildState() {
456
- const startedAtEpochMs = Date.now();
457
- const enabled =
458
- STARTUP_EXPERIMENT_ENABLED &&
459
- process.env.MIAODA_TYPESCRIPT_INCREMENTAL_CACHE_ENABLED === 'true';
242
+ // Stale dist makes nest --watch skip missing files; watcher won't self-heal.
243
+ function cleanStaleDist() {
460
244
  const distPath = path.join(PROJECT_ROOT, 'dist');
461
- const rootBuildInfoPath = path.join(
462
- PROJECT_ROOT,
463
- 'tsconfig.node.tsbuildinfo'
464
- );
465
- const distBuildInfoPath = path.join(distPath, 'tsconfig.node.tsbuildinfo');
466
- const candidateDistExists = fs.existsSync(distPath);
467
- const candidateBuildInfoPath = fs.existsSync(distBuildInfoPath)
468
- ? distBuildInfoPath
469
- : fs.existsSync(rootBuildInfoPath)
470
- ? rootBuildInfoPath
471
- : '';
472
- const candidateAvailable =
473
- enabled && candidateDistExists && Boolean(candidateBuildInfoPath);
474
- if (!candidateAvailable) {
245
+ if (fs.existsSync(distPath)) {
475
246
  fs.rmSync(distPath, { recursive: true, force: true });
476
- fs.rmSync(rootBuildInfoPath, { force: true });
477
- }
478
- const atEpochMs = Date.now();
479
- writeOutput(
480
- `${JSON.stringify({
481
- event: 'miaoda_typescript_incremental_cache',
482
- phase: 'consume',
483
- enabled,
484
- status: candidateAvailable ? 'preserved' : enabled ? 'miss' : 'disabled',
485
- candidateDistExists,
486
- candidateTsbuildinfoExists: Boolean(candidateBuildInfoPath),
487
- tsbuildinfoPath: candidateBuildInfoPath || null,
488
- distExists: fs.existsSync(distPath),
489
- tsbuildinfoExists:
490
- fs.existsSync(distBuildInfoPath) || fs.existsSync(rootBuildInfoPath),
491
- phaseStartedAtEpochMs: startedAtEpochMs,
492
- atEpochMs,
493
- durationMs: atEpochMs - startedAtEpochMs,
494
- })}\n`
495
- );
496
- }
497
-
498
- async function followExternalNestOrchestratorOrStartLocal(
499
- serverStartupRuntimeScript
500
- ) {
501
- const owner = readExternalNestOrchestratorOwner();
502
- if (!owner) {
503
- return startProcess({
504
- name: 'server',
505
- command: process.execPath,
506
- args: [serverStartupRuntimeScript],
507
- cleanupPort: SERVER_PORT,
508
- });
509
- }
510
-
511
- logEvent(
512
- 'INFO',
513
- 'server',
514
- `Following image-owned Nest orchestrator pid=${owner.pid} mode=${owner.mode}`
515
- );
516
- while (!stopping) {
517
- try {
518
- process.kill(owner.pid, 0);
519
- await sleep(200);
520
- } catch {
521
- break;
522
- }
247
+ logEvent('INFO', 'main', 'Cleaned dist/ to force full rebuild');
523
248
  }
524
- if (stopping) return 0;
525
-
526
- logEvent(
527
- 'WARN',
528
- 'server',
529
- `Image-owned Nest orchestrator pid=${owner.pid} exited; starting local supervisor`
530
- );
531
- return startProcess({
532
- name: 'server',
533
- command: process.execPath,
534
- args: [serverStartupRuntimeScript],
535
- cleanupPort: SERVER_PORT,
536
- });
537
249
  }
538
250
 
539
251
  // ── Main ──────────────────────────────────────────────────────────────────────
540
252
  async function main() {
541
253
  logEvent('INFO', 'main', '========== Dev session started ==========');
542
254
 
543
- preserveDevCache(PROJECT_ROOT, {
544
- log: (level, msg) => logEvent(level, 'main', msg),
545
- });
255
+ cleanStaleDist();
546
256
 
547
- if (process.env.MIAODA_HMR_ROUTING_MODE === 'same-origin') {
548
- delete process.env.MIAODA_WS_HOST;
549
- logEvent(
550
- 'INFO',
551
- 'main',
552
- 'Using same-origin HMR routing; ignored the dedicated MIAODA_WS_HOST'
553
- );
554
- }
555
-
556
- const serverStartupRuntimeScript = path.join(
557
- STARTUP_RUNTIME_SCRIPTS_ROOT,
558
- 'server-startup-runtime.mjs'
559
- );
560
- const workspaceClientRuntimeScript = path.join(
561
- STARTUP_RUNTIME_SCRIPTS_ROOT,
562
- 'workspace-client-runtime.mjs'
563
- );
564
- const viteRuntimeConfigScript = path.join(
565
- PROJECT_ROOT,
566
- 'scripts',
567
- 'vite-runtime.config.mjs'
568
- );
569
- if (STARTUP_EXPERIMENT_ENABLED) {
570
- try {
571
- for (const requiredPath of [
572
- serverStartupRuntimeScript,
573
- workspaceClientRuntimeScript,
574
- viteRuntimeConfigScript,
575
- ]) {
576
- if (!fs.existsSync(requiredPath)) {
577
- throw new Error(
578
- `MIAODA_STARTUP_EXPERIMENT_INPUT_MISSING: ${requiredPath}`
579
- );
580
- }
581
- }
582
- } catch (error) {
583
- STARTUP_EXPERIMENT_ENABLED = false;
584
- process.env.MIAODA_STARTUP_EXPERIMENT_ENABLED = 'false';
585
- logEvent(
586
- 'WARN',
587
- 'main',
588
- `Startup experiment unavailable; using original services: ${error.message}`
589
- );
590
- }
591
- }
592
-
593
- if (STARTUP_EXPERIMENT_ENABLED) {
594
- fs.rmSync(ACTION_PLUGIN_READY_FILE, { force: true });
595
- await waitForWorkspaceDependencies();
596
- prepareTypeScriptBuildState();
597
- initializeActionPlugins(true);
598
- } else {
599
- prepareTypeScriptBuildState();
600
- initializeActionPlugins();
257
+ // Initialize action plugins
258
+ writeOutput('\n🔌 Initializing action plugins...\n');
259
+ try {
260
+ execSync('fullstack-cli action-plugin init', { cwd: PROJECT_ROOT, stdio: 'inherit' });
261
+ writeOutput('✅ Action plugins initialized\n\n');
262
+ } catch {
263
+ writeOutput('⚠️ Action plugin initialization failed, continuing anyway...\n\n');
601
264
  }
602
265
 
603
266
  // Start server and client
604
- let serverPromise;
605
- let clientPromise;
606
- if (STARTUP_EXPERIMENT_ENABLED) {
607
- logEvent(
608
- 'INFO',
609
- 'main',
610
- `Starting normal workspace Vite with Nest mode ${
611
- process.env.MIAODA_NEST_START_MODE || 'workspace'
612
- }`
613
- );
614
- serverPromise = followExternalNestOrchestratorOrStartLocal(
615
- serverStartupRuntimeScript
616
- );
617
- clientPromise = startProcess({
618
- name: 'client',
619
- command: process.execPath,
620
- args: [workspaceClientRuntimeScript],
621
- cleanupPort: CLIENT_DEV_PORT,
622
- });
623
- } else {
624
- serverPromise = startProcess({
625
- name: 'server',
626
- command: 'npm',
627
- args: ['run', 'dev:server'],
628
- cleanupPort: SERVER_PORT,
629
- });
630
-
631
- clientPromise = startProcess({
632
- name: 'client',
633
- command: 'npm',
634
- args: ['run', 'dev:client'],
635
- cleanupPort: CLIENT_DEV_PORT,
636
- });
637
- }
267
+ const serverPromise = startProcess({
268
+ name: 'server',
269
+ command: 'npm',
270
+ args: ['run', 'dev:server'],
271
+ cleanupPort: SERVER_PORT,
272
+ });
273
+
274
+ const clientPromise = startProcess({
275
+ name: 'client',
276
+ command: 'npm',
277
+ args: ['run', 'dev:client'],
278
+ cleanupPort: CLIENT_DEV_PORT,
279
+ });
638
280
 
639
281
  writeOutput(`📋 Dev processes running. Press Ctrl+C to stop.\n`);
640
282
  writeOutput(`📄 Logs: ${devStdLogPath}\n\n`);
@@ -647,7 +289,7 @@ async function main() {
647
289
  }
648
290
  }
649
291
 
650
- main().catch(err => {
292
+ main().catch((err) => {
651
293
  console.error('Fatal error:', err);
652
294
  process.exit(1);
653
295
  });
@@ -11,8 +11,6 @@
11
11
  # 在绑定前拿不到,会被误判成本地。保留为附加条件只为兼容,不影响判定结果。
12
12
  set -euo pipefail
13
13
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
14
- export MIAODA_CACHE_RUNTIME_SCRIPTS_ROOT="${MIAODA_CACHE_RUNTIME_SCRIPTS_ROOT:-$SCRIPT_DIR}"
15
- export MIAODA_WORKSPACE_ROOT="${MIAODA_WORKSPACE_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
16
14
 
17
15
  if [ -n "${MIAODA_DEP_CACHE_DIR:-}" ] || [ -n "${SANDBOX_ID:-}" ]; then
18
16
  exec node "$SCRIPT_DIR/dev.js" "$@"
@@ -1,5 +1,5 @@
1
1
  import path from 'path';
2
- import { defineConfig } from '@lark-apaas/coding-preset-vite-react';
2
+ import { defineConfig } from '@lark-apaas/fullstack-vite-preset';
3
3
 
4
4
  export default defineConfig({
5
5
  resolve: {