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

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-template-nestjs-react-fullstack",
3
- "version": "0.1.27-alpha.20260824122815",
3
+ "version": "0.1.28-alpha.20260824161528",
4
4
  "description": "NestJS + React fullstack template for miaoda coding",
5
5
  "type": "module",
6
6
  "files": [
@@ -30,7 +30,7 @@
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@1.1.62-alpha.20260824155956 sync --disable-gen-openapi"
34
34
  },
35
35
  "dependencies": {
36
36
  "@lark-apaas/fullstack-nestjs-core": "^1.1.59",
@@ -63,7 +63,7 @@
63
63
  "@heroicons/react": "^2.2.0",
64
64
  "@hookform/resolvers": "^5.2.2",
65
65
  "@lark-apaas/client-toolkit": "^1.2.63",
66
- "@lark-apaas/coding-preset-vite-react": "1.0.19-alpha.20260824121536",
66
+ "@lark-apaas/coding-preset-vite-react": "1.0.19-alpha.20260824155954",
67
67
  "@lark-apaas/fullstack-presets": "^1.1.0",
68
68
  "@nestjs/cli": "^10.4.9",
69
69
  "@nestjs/testing": "^10.4.20",
@@ -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,16 +50,11 @@ 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
  }
@@ -204,33 +68,21 @@ const STDOUT_MAX_INFLIGHT = 1000;
204
68
  function writeOutput(msg) {
205
69
  // File first and synchronously — read-logs reads this file; it must never be gated
206
70
  // by the terminal consumer.
207
- try {
208
- fs.writeSync(devStdLogFd, msg);
209
- } catch {}
71
+ try { fs.writeSync(devStdLogFd, msg); } catch {}
210
72
  // stdout mirror via async fs.write: if process.stdout is a pty/pipe whose consumer
211
73
  // stalls, the block happens on the libuv threadpool, NOT the event loop — so the
212
74
  // synchronous log-FILE writes above (and subsequent readline callbacks) keep running.
213
75
  // Drop overflow when too many writes are already pending (best-effort mirror).
214
- if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) {
215
- return;
216
- }
76
+ if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) { return; }
217
77
  _stdoutInFlight++;
218
- try {
219
- fs.write(1, msg, () => {
220
- _stdoutInFlight--;
221
- });
222
- } catch {
223
- _stdoutInFlight--;
224
- }
78
+ try { fs.write(1, msg, () => { _stdoutInFlight--; }); } catch { _stdoutInFlight--; }
225
79
  }
226
80
 
227
81
  /** Structured event log → terminal + dev.std.log + dev.log */
228
82
  function logEvent(level, name, message) {
229
83
  const msg = `[${timestamp()}] [${level}] [${name}] ${message}\n`;
230
84
  writeOutput(msg);
231
- try {
232
- fs.writeSync(devLogFd, msg);
233
- } catch {}
85
+ try { fs.writeSync(devLogFd, msg); } catch {}
234
86
  }
235
87
 
236
88
  // ── Process group management ──────────────────────────────────────────────────
@@ -242,16 +94,11 @@ function killProcessGroup(pid, signal) {
242
94
 
243
95
  function killOrphansByPort(port) {
244
96
  try {
245
- const pids = execSync(`lsof -ti :${port}`, {
246
- encoding: 'utf8',
247
- timeout: 5000,
248
- }).trim();
97
+ const pids = execSync(`lsof -ti :${port}`, { encoding: 'utf8', timeout: 5000 }).trim();
249
98
  if (pids) {
250
99
  const pidList = pids.split('\n').filter(Boolean);
251
100
  for (const p of pidList) {
252
- try {
253
- process.kill(parseInt(p, 10), 'SIGKILL');
254
- } catch {}
101
+ try { process.kill(parseInt(p, 10), 'SIGKILL'); } catch {}
255
102
  }
256
103
  return pidList;
257
104
  }
@@ -264,7 +111,7 @@ let stopping = false;
264
111
  const managedProcesses = []; // { name, pid, child }
265
112
 
266
113
  function sleep(ms) {
267
- return new Promise(resolve => setTimeout(resolve, ms));
114
+ return new Promise((resolve) => setTimeout(resolve, ms));
268
115
  }
269
116
 
270
117
  /**
@@ -294,23 +141,14 @@ function startProcess({ name, command, args, cleanupPort }) {
294
141
  entry.child = child;
295
142
 
296
143
  const startTime = Date.now();
297
- logEvent(
298
- 'INFO',
299
- name,
300
- `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`
301
- );
144
+ logEvent('INFO', name, `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`);
302
145
 
303
146
  // 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 => {
147
+ const pipeLines = (stream) => {
148
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
149
+ rl.on('line', (line) => {
310
150
  const msg = `[${timestamp()}] [${name}] ${line}\n`;
311
- try {
312
- fs.writeSync(logFd, msg);
313
- } catch {}
151
+ try { fs.writeSync(logFd, msg); } catch {}
314
152
  writeOutput(msg);
315
153
  });
316
154
  };
@@ -321,8 +159,8 @@ function startProcess({ name, command, args, cleanupPort }) {
321
159
  // NOTE: must use 'exit', not 'close'. With shell:true, grandchild processes
322
160
  // (e.g. nest's server) inherit stdout pipes. 'close' won't fire until ALL
323
161
  // 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));
162
+ const exitCode = await new Promise((resolve) => {
163
+ child.on('exit', (code) => resolve(code ?? 1));
326
164
  child.on('error', () => resolve(1));
327
165
  });
328
166
 
@@ -339,11 +177,7 @@ function startProcess({ name, command, args, cleanupPort }) {
339
177
  if (cleanupPort) {
340
178
  const orphans = killOrphansByPort(cleanupPort);
341
179
  if (orphans.length > 0) {
342
- logEvent(
343
- 'WARN',
344
- name,
345
- `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`
346
- );
180
+ logEvent('WARN', name, `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`);
347
181
  await sleep(500);
348
182
  }
349
183
  }
@@ -353,38 +187,21 @@ function startProcess({ name, command, args, cleanupPort }) {
353
187
  const runDuration = (Date.now() - startTime) / 1000;
354
188
  if (runDuration >= 60) {
355
189
  restartCount = 0;
356
- logEvent(
357
- 'INFO',
358
- name,
359
- `Ran for ${Math.round(runDuration)}s, resetting restart counter`
360
- );
190
+ logEvent('INFO', name, `Ran for ${Math.round(runDuration)}s, resetting restart counter`);
361
191
  } else {
362
192
  restartCount++;
363
193
  }
364
194
  if (restartCount >= MAX_RESTART_COUNT) {
365
- logEvent(
366
- 'ERROR',
367
- name,
368
- `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`
369
- );
195
+ logEvent('ERROR', name, `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`);
370
196
  break;
371
197
  }
372
198
 
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
- );
199
+ const delay = Math.min(RESTART_DELAY * (1 << Math.max(0, restartCount - 1)), MAX_DELAY);
200
+ logEvent('WARN', name, `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`);
382
201
  await sleep(delay * 1000);
383
202
  }
384
203
 
385
- try {
386
- fs.closeSync(logFd);
387
- } catch {}
204
+ try { fs.closeSync(logFd); } catch {}
388
205
  };
389
206
 
390
207
  return run();
@@ -414,11 +231,7 @@ async function cleanup() {
414
231
  // Force kill any remaining
415
232
  for (const entry of managedProcesses) {
416
233
  if (entry.pid) {
417
- logEvent(
418
- 'WARN',
419
- 'main',
420
- `Force killing process group (PGID: ${entry.pid})`
421
- );
234
+ logEvent('WARN', 'main', `Force killing process group (PGID: ${entry.pid})`);
422
235
  killProcessGroup(entry.pid, 'SIGKILL');
423
236
  }
424
237
  }
@@ -429,12 +242,8 @@ async function cleanup() {
429
242
 
430
243
  logEvent('INFO', 'main', 'All processes stopped');
431
244
 
432
- try {
433
- fs.closeSync(devStdLogFd);
434
- } catch {}
435
- try {
436
- fs.closeSync(devLogFd);
437
- } catch {}
245
+ try { fs.closeSync(devStdLogFd); } catch {}
246
+ try { fs.closeSync(devLogFd); } catch {}
438
247
 
439
248
  process.exit(0);
440
249
  }
@@ -443,99 +252,16 @@ process.on('SIGTERM', cleanup);
443
252
  process.on('SIGINT', cleanup);
444
253
  process.on('SIGHUP', cleanup);
445
254
 
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 便于单测。
255
+ // 定向保留 dist/ 里能复用的部分, 替代之前无脑 rm -rf dist/。
256
+ // 收益: 保留 dist/server 让 nest --watch 从增量编译起点起 (省 8-13s 冷启动),
257
+ // routes JSON 也保留 (build 会覆盖, 无 stale 风险)。dist/client/index.html 按
258
+ // preset 打的 <meta name="miaoda-html-mode"> marker 判定:
259
+ // marker=dev → 保留 dist/client/ 整个 (dev cache hit)
260
+ // marker=prod → agent 跑过 build:client 污染, 定向清 index.html + assets/
261
+ // 具体逻辑抽到 lib/preserve-dev-cache.cjs 便于单测。
262
+ // 降级 escape hatch: FORCE_CLEAN_DIST=true 走原全清路径 (marker 逻辑出 bug 时用)。
453
263
  const { preserveDevCache } = require('./lib/preserve-dev-cache.cjs');
454
264
 
455
- function prepareTypeScriptBuildState() {
456
- const startedAtEpochMs = Date.now();
457
- const enabled =
458
- STARTUP_EXPERIMENT_ENABLED &&
459
- process.env.MIAODA_TYPESCRIPT_INCREMENTAL_CACHE_ENABLED === 'true';
460
- 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) {
475
- 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
- }
523
- }
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
- }
538
-
539
265
  // ── Main ──────────────────────────────────────────────────────────────────────
540
266
  async function main() {
541
267
  logEvent('INFO', 'main', '========== Dev session started ==========');
@@ -544,97 +270,29 @@ async function main() {
544
270
  log: (level, msg) => logEvent(level, 'main', msg),
545
271
  });
546
272
 
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();
273
+ // Initialize action plugins
274
+ writeOutput('\n🔌 Initializing action plugins...\n');
275
+ try {
276
+ execSync('fullstack-cli action-plugin init', { cwd: PROJECT_ROOT, stdio: 'inherit' });
277
+ writeOutput('✅ Action plugins initialized\n\n');
278
+ } catch {
279
+ writeOutput('⚠️ Action plugin initialization failed, continuing anyway...\n\n');
601
280
  }
602
281
 
603
282
  // 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
- }
283
+ const serverPromise = startProcess({
284
+ name: 'server',
285
+ command: 'npm',
286
+ args: ['run', 'dev:server'],
287
+ cleanupPort: SERVER_PORT,
288
+ });
289
+
290
+ const clientPromise = startProcess({
291
+ name: 'client',
292
+ command: 'npm',
293
+ args: ['run', 'dev:client'],
294
+ cleanupPort: CLIENT_DEV_PORT,
295
+ });
638
296
 
639
297
  writeOutput(`📋 Dev processes running. Press Ctrl+C to stop.\n`);
640
298
  writeOutput(`📄 Logs: ${devStdLogPath}\n\n`);
@@ -647,7 +305,7 @@ async function main() {
647
305
  }
648
306
  }
649
307
 
650
- main().catch(err => {
308
+ main().catch((err) => {
651
309
  console.error('Fatal error:', err);
652
310
  process.exit(1);
653
311
  });
@@ -1,20 +1,14 @@
1
1
  #!/usr/bin/env bash
2
- # `npm run dev` 入口;按 MIAODA_DEP_CACHE_DIR 是否非空判断运行环境(与 miaoda-cli
3
- # isSandboxEnv() 同口径):
4
- # - 非空(沙箱平台注入的依赖缓存目录)→ 直接跑 dev.js
2
+ # `npm run dev` 入口;按 SANDBOX_ID 是否非空判断运行环境:
3
+ # - SANDBOX_ID 非空(沙箱平台注入应用所属沙箱 ID)→ 直接跑 dev.js
5
4
  # (保活 / restart loop / 文件日志 —— 沙箱生产形态)。脚本同步由平台 pod 启动阶段做过,
6
5
  # dev 入口不再额外 `npm run upgrade`。
7
- # - 否则(本地)→ 走 miaoda app sync 兜底 + 跑 dev-local.js:纯 stdout、崩了就崩、Agent 友好。
8
- # 显式想跑本地路径可用 `npm run dev:local`(绕过本判断)。
9
- #
10
- # 不用 SANDBOX_ID:它由 sandbox_console 在绑定沙箱时才写入 .force/environment/env,预热池实例
11
- # 在绑定前拿不到,会被误判成本地。保留为附加条件只为兼容,不影响判定结果。
6
+ # - 否则(本地)→ 走 miaoda app sync 兜底 + 跑 dev-local.sh:纯 stdout、崩了就崩、Agent 友好。
7
+ # 显式想跑本地路径可用 `npm run dev:local`(绕过 SANDBOX_ID 判断)。
12
8
  set -euo pipefail
13
9
  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
10
 
17
- if [ -n "${MIAODA_DEP_CACHE_DIR:-}" ] || [ -n "${SANDBOX_ID:-}" ]; then
11
+ if [ -n "${SANDBOX_ID:-}" ]; then
18
12
  exec node "$SCRIPT_DIR/dev.js" "$@"
19
13
  fi
20
14
 
@@ -24,7 +18,7 @@ if [ ! -f "$SCRIPT_DIR/dev-local.js" ]; then
24
18
  fi
25
19
 
26
20
  # 本地启动前先跑一次 miaoda app sync:同步 platform-controlled 内容 + 升 @lark-apaas/* 到
27
- # latest + 迁移老 npm scripts。沙箱不走这里(上面的沙箱分支已经 exec return)。
21
+ # latest + 迁移老 npm scripts。沙箱不走这里(SANDBOX_ID 分支已经 exec return)。
28
22
  npx -y @lark-apaas/miaoda-cli@latest app sync || echo "[dev] miaoda app sync 失败,按现状继续" >&2
29
23
 
30
24
  exec node "$SCRIPT_DIR/dev-local.js" "$@"