helixevo 0.2.13 → 0.2.14

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.
@@ -0,0 +1,15 @@
1
+ import { NextResponse } from 'next/server'
2
+
3
+ export const dynamic = 'force-dynamic'
4
+
5
+ // Exit code 75 signals the parent CLI to auto-restart the dashboard
6
+ const RESTART_EXIT_CODE = 75
7
+
8
+ export async function POST() {
9
+ // Send response first, then exit after a brief delay
10
+ setTimeout(() => {
11
+ process.exit(RESTART_EXIT_CODE)
12
+ }, 500)
13
+
14
+ return NextResponse.json({ restarting: true })
15
+ }
@@ -1,31 +1,80 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { execSync } from 'child_process'
3
+ import { existsSync, cpSync, readdirSync, rmSync, writeFileSync, mkdirSync } from 'fs'
4
+ import { join } from 'path'
5
+ import { homedir } from 'os'
3
6
 
4
7
  export const dynamic = 'force-dynamic'
5
8
 
9
+ const HELIX_DASHBOARD_DIR = join(homedir(), '.helix', 'dashboard')
10
+
11
+ function findNewDashboardSource(): string | null {
12
+ const candidates: string[] = []
13
+
14
+ // From npm global prefix
15
+ try {
16
+ const globalPrefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim()
17
+ candidates.push(join(globalPrefix, 'lib', 'node_modules', 'helixevo', 'dashboard'))
18
+ candidates.push(join(globalPrefix, 'node_modules', 'helixevo', 'dashboard'))
19
+ } catch {}
20
+
21
+ for (const dir of candidates) {
22
+ if (existsSync(join(dir, 'package.json'))) {
23
+ return dir
24
+ }
25
+ }
26
+ return null
27
+ }
28
+
29
+ function copyDashboardFiles(sourceDir: string, newVersion: string): void {
30
+ mkdirSync(HELIX_DASHBOARD_DIR, { recursive: true })
31
+
32
+ // Copy source files (exclude node_modules, .next, package-lock.json)
33
+ const items = readdirSync(sourceDir, { withFileTypes: true })
34
+ for (const item of items) {
35
+ if (item.name === 'node_modules' || item.name === '.next' || item.name === 'package-lock.json') continue
36
+ const src = join(sourceDir, item.name)
37
+ const dest = join(HELIX_DASHBOARD_DIR, item.name)
38
+ cpSync(src, dest, { recursive: true })
39
+ }
40
+
41
+ // Clear .next cache so Next.js recompiles from new source
42
+ const nextCache = join(HELIX_DASHBOARD_DIR, '.next')
43
+ if (existsSync(nextCache)) rmSync(nextCache, { recursive: true })
44
+
45
+ // Write version marker
46
+ writeFileSync(join(HELIX_DASHBOARD_DIR, '.helixevo-version'), newVersion)
47
+ }
48
+
6
49
  export async function POST() {
7
50
  try {
8
- // Run the upgrade command
51
+ // 1. Install the new package
9
52
  const output = execSync('npm install -g helixevo@latest 2>&1', {
10
53
  encoding: 'utf-8',
11
- timeout: 120000, // 2 minute timeout
54
+ timeout: 120000,
12
55
  env: { ...process.env },
13
56
  })
14
57
 
15
- // Get the new version
58
+ // 2. Get the new version
16
59
  let newVersion = ''
17
60
  try {
18
61
  newVersion = execSync('helixevo --version 2>/dev/null', { encoding: 'utf-8' }).trim()
19
62
  } catch {
20
- // Try parsing from output
21
63
  const match = output.match(/helixevo@(\d+\.\d+\.\d+)/)
22
64
  newVersion = match?.[1] ?? 'unknown'
23
65
  }
24
66
 
67
+ // 3. Copy new dashboard files to ~/.helix/dashboard
68
+ const newSource = findNewDashboardSource()
69
+ if (newSource) {
70
+ copyDashboardFiles(newSource, newVersion)
71
+ }
72
+
25
73
  return NextResponse.json({
26
74
  success: true,
27
75
  version: newVersion,
28
- output: output.slice(-500), // last 500 chars
76
+ dashboardUpdated: !!newSource,
77
+ output: output.slice(-500),
29
78
  })
30
79
  } catch (err: unknown) {
31
80
  const message = err instanceof Error ? err.message : String(err)
@@ -56,10 +56,24 @@ export function UpdateBanner({ currentVersion }: { currentVersion: string }) {
56
56
  if (data.success) {
57
57
  setState('success')
58
58
  setNewVersion(data.version)
59
- // Reload after a brief delay to show success state
60
- setTimeout(() => {
61
- window.location.reload()
62
- }, 2000)
59
+ // Trigger dashboard restart the CLI will auto-relaunch
60
+ setTimeout(async () => {
61
+ try {
62
+ await fetch('/api/restart', { method: 'POST' })
63
+ } catch {}
64
+ // Poll until the new server is ready, then reload
65
+ const poll = setInterval(async () => {
66
+ try {
67
+ const res = await fetch('/', { cache: 'no-store' })
68
+ if (res.ok) {
69
+ clearInterval(poll)
70
+ window.location.reload()
71
+ }
72
+ } catch {}
73
+ }, 1500)
74
+ // Stop polling after 60s
75
+ setTimeout(() => clearInterval(poll), 60000)
76
+ }, 1500)
63
77
  } else {
64
78
  setState('error')
65
79
  setErrorMsg(data.error ?? 'Update failed')
package/dist/cli.js CHANGED
@@ -12378,20 +12378,45 @@ async function dashboardCommand() {
12378
12378
  ensureSkillGraph();
12379
12379
  console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${VERSION} at http://localhost:3847
12380
12380
  `);
12381
+ launchDashboard(dir, true);
12382
+ }
12383
+ var RESTART_EXIT_CODE = 75;
12384
+ function launchDashboard(dir, openBrowser) {
12385
+ let currentVersion = VERSION;
12386
+ try {
12387
+ currentVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
12388
+ } catch {}
12381
12389
  const child = spawn2("npx", ["next", "dev", "--port", "3847"], {
12382
12390
  cwd: dir,
12383
12391
  stdio: "inherit",
12384
- env: { ...process.env, HELIXEVO_VERSION: VERSION }
12392
+ env: { ...process.env, HELIXEVO_VERSION: currentVersion }
12385
12393
  });
12386
- setTimeout(() => {
12387
- try {
12388
- const platform = process.platform;
12389
- const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
12390
- execSync2(`${cmd} http://localhost:3847`, { stdio: "ignore" });
12391
- } catch {}
12392
- }, 3000);
12394
+ if (openBrowser) {
12395
+ setTimeout(() => {
12396
+ try {
12397
+ const platform = process.platform;
12398
+ const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
12399
+ execSync2(`${cmd} http://localhost:3847`, { stdio: "ignore" });
12400
+ } catch {}
12401
+ }, 3000);
12402
+ }
12393
12403
  child.on("close", (code) => {
12394
- process.exit(code ?? 0);
12404
+ if (code === RESTART_EXIT_CODE) {
12405
+ console.log(`
12406
+ \uD83D\uDD04 Restarting dashboard after update...
12407
+ `);
12408
+ const newDir = prepareDashboard() ?? dir;
12409
+ ensureSkillGraph();
12410
+ let updatedVersion = currentVersion;
12411
+ try {
12412
+ updatedVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || currentVersion;
12413
+ } catch {}
12414
+ console.log(` \uD83C\uDF10 HelixEvo Dashboard v${updatedVersion} at http://localhost:3847
12415
+ `);
12416
+ launchDashboard(newDir, false);
12417
+ } else {
12418
+ process.exit(code ?? 0);
12419
+ }
12395
12420
  });
12396
12421
  }
12397
12422
  function prepareDashboard() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixevo",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "Self-evolving skill ecosystem for AI agents. Skills and projects co-evolve through multi-judge evaluation and a Pareto frontier.",
5
5
  "type": "module",
6
6
  "bin": {