@aion0/forge 0.9.6 → 0.9.7

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/RELEASE_NOTES.md CHANGED
@@ -1,8 +1,12 @@
1
- # Forge v0.9.6
1
+ # Forge v0.9.7
2
2
 
3
3
  Released: 2026-05-26
4
4
 
5
- ## Changes since v0.9.5
5
+ ## Changes since v0.9.6
6
6
 
7
+ ### Other
8
+ - fix(server): rebuild stale .next on every restart after upgrade
9
+ - marketplace: drop 'Recipes' option from Templates filter
7
10
 
8
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.5...v0.9.6
11
+
12
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.6...v0.9.7
@@ -43,6 +43,33 @@ function buildNext() {
43
43
  execSync('npx next build', { cwd: ROOT, stdio: 'inherit', env: { ...process.env } });
44
44
  }
45
45
 
46
+ /**
47
+ * Build if missing OR if the installed npm package version is newer than
48
+ * what produced the cached .next/. Without this version check, `forge server
49
+ * restart` (and any code path that doesn't go through the explicit rebuild
50
+ * block) would happily serve a stale .next built against the previous
51
+ * version — HTML referencing chunk hashes that no longer exist, → CSS/JS
52
+ * 500s in the browser. Local devs don't hit this because they always
53
+ * `forge server rebuild`; downstream users hitting `npm i -g + restart` do.
54
+ */
55
+ function ensureBuilt() {
56
+ const buildIdPath = join(ROOT, '.next', 'BUILD_ID');
57
+ const versionFile = join(ROOT, '.next', '.forge-version');
58
+ const pkgVersion = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8')).version;
59
+ const lastBuilt = existsSync(versionFile) ? readFileSync(versionFile, 'utf-8').trim() : '';
60
+
61
+ if (existsSync(buildIdPath) && lastBuilt === pkgVersion) return;
62
+
63
+ if (existsSync(buildIdPath)) {
64
+ console.log(`[forge] Upgrade detected (${lastBuilt || '(no marker)'} → v${pkgVersion}) — rebuilding .next/`);
65
+ execSync('rm -rf .next', { cwd: ROOT });
66
+ } else {
67
+ console.log(`[forge] Building v${pkgVersion}...`);
68
+ }
69
+ buildNext();
70
+ try { writeFileSync(versionFile, pkgVersion); } catch {}
71
+ }
72
+
46
73
  // ── Parse arguments ──
47
74
 
48
75
  function getArg(name) {
@@ -457,10 +484,7 @@ async function stopServer() {
457
484
 
458
485
  // ── Helper: start background server ──
459
486
  function startBackground() {
460
- if (!existsSync(join(ROOT, '.next', 'BUILD_ID'))) {
461
- console.log('[forge] Building...');
462
- buildNext();
463
- }
487
+ ensureBuilt();
464
488
 
465
489
  const logFd = openSync(LOG_FILE, 'a');
466
490
  const nextBin = join(ROOT, 'node_modules', '.bin', 'next');
@@ -519,20 +543,16 @@ if (isRestart) {
519
543
  }
520
544
 
521
545
  // ── Rebuild ──
522
- if (isRebuild || existsSync(join(ROOT, '.next', 'BUILD_ID'))) {
546
+ // Explicit `--rebuild` always wipes + rebuilds. Otherwise ensureBuilt()
547
+ // (called by every start path) handles upgrade detection itself.
548
+ if (isRebuild) {
523
549
  const pkgVersion = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8')).version;
524
- const versionFile = join(ROOT, '.next', '.forge-version');
525
- const lastBuiltVersion = existsSync(versionFile) ? readFileSync(versionFile, 'utf-8').trim() : '';
526
- if (isRebuild || lastBuiltVersion !== pkgVersion) {
527
- console.log(`[forge] Rebuilding (v${pkgVersion})...`);
528
- execSync('rm -rf .next', { cwd: ROOT });
529
- buildNext();
530
- writeFileSync(versionFile, pkgVersion);
531
- if (isRebuild) {
532
- console.log('[forge] Rebuild complete');
533
- process.exit(0);
534
- }
535
- }
550
+ console.log(`[forge] Rebuilding (v${pkgVersion})...`);
551
+ if (existsSync(join(ROOT, '.next'))) execSync('rm -rf .next', { cwd: ROOT });
552
+ buildNext();
553
+ try { writeFileSync(join(ROOT, '.next', '.forge-version'), pkgVersion); } catch {}
554
+ console.log('[forge] Rebuild complete');
555
+ process.exit(0);
536
556
  }
537
557
 
538
558
  // ── Background ──
@@ -557,10 +577,7 @@ if (isDev) {
557
577
  });
558
578
  child.on('exit', (code) => { stopServices(); process.exit(code || 0); });
559
579
  } else {
560
- if (!existsSync(join(ROOT, '.next', 'BUILD_ID'))) {
561
- console.log('[forge] Building...');
562
- buildNext();
563
- }
580
+ ensureBuilt();
564
581
  console.log(`[forge] Starting server (port ${webPort}, terminal ${terminalPort}, data ${DATA_DIR})`);
565
582
  startServices();
566
583
  const child = spawn('npx', ['next', 'start', '-p', String(webPort)], {
@@ -435,7 +435,6 @@ export default function SkillsPanel({ projectFilter }: { projectFilter?: string
435
435
  <option value="crafts">Crafts</option>
436
436
  </optgroup>
437
437
  <optgroup label="Templates">
438
- <option value="recipes">Recipes</option>
439
438
  <option value="pipelines">Pipelines</option>
440
439
  </optgroup>
441
440
  </select>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {