vibelet 0.1.16 → 0.1.17

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/bin/vibelet.mjs CHANGED
@@ -25,20 +25,102 @@ const stdoutLogPath = join(logDir, 'daemon.stdout.log');
25
25
  const stderrLogPath = join(logDir, 'daemon.stderr.log');
26
26
  const pidFilePath = join(vibeletDir, 'daemon.pid');
27
27
  const relayConfigPath = join(vibeletDir, 'relay.json');
28
+ const updateCheckPath = join(vibeletDir, 'update-check.json');
28
29
  const OFFICIAL_SITE_URL = 'https://vibelet.icu';
30
+ const UPDATE_CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
29
31
 
30
32
  let officialSitePrinted = false;
33
+ let updateMessage = '';
31
34
 
32
35
  function printOfficialSite() {
33
36
  if (officialSitePrinted) return;
34
37
  officialSitePrinted = true;
35
38
  try {
39
+ if (updateMessage) writeSync(2, `\n${updateMessage}\n`);
36
40
  writeSync(1, `\nOfficial site: ${OFFICIAL_SITE_URL}\n`);
37
41
  } catch {
38
42
  // Best-effort branding footer; ignore broken pipes and closed stdio.
39
43
  }
40
44
  }
41
45
 
46
+ // ─── Update check ──────────────────────────────────────────────────────────────
47
+
48
+ function readUpdateCheck() {
49
+ try {
50
+ return JSON.parse(readFileSync(updateCheckPath, 'utf8'));
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ function writeUpdateCheck(data) {
57
+ try {
58
+ mkdirSync(vibeletDir, { recursive: true });
59
+ writeFileSync(updateCheckPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
60
+ } catch {
61
+ // Best-effort; failing to persist is fine.
62
+ }
63
+ }
64
+
65
+ function compareVersions(a, b) {
66
+ const pa = a.split('.').map(Number);
67
+ const pb = b.split('.').map(Number);
68
+ for (let i = 0; i < 3; i++) {
69
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
70
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ function checkForUpdateFromCache() {
76
+ const cached = readUpdateCheck();
77
+ if (!cached?.latestVersion) return;
78
+ if (compareVersions(packageJson.version, cached.latestVersion) < 0) {
79
+ updateMessage =
80
+ `\x1b[33m╭───────────────────────────────────────────╮\x1b[0m\n` +
81
+ `\x1b[33m│\x1b[0m Update available: \x1b[90m${packageJson.version}\x1b[0m → \x1b[32m${cached.latestVersion}\x1b[0m${' '.repeat(Math.max(0, 14 - packageJson.version.length - cached.latestVersion.length))}\x1b[33m│\x1b[0m\n` +
82
+ `\x1b[33m│\x1b[0m Run \x1b[36mnpx @vibelet/cli@latest\x1b[0m to upgrade \x1b[33m│\x1b[0m\n` +
83
+ `\x1b[33m╰───────────────────────────────────────────╯\x1b[0m`;
84
+ }
85
+ }
86
+
87
+ function fetchLatestVersionInBackground() {
88
+ const cached = readUpdateCheck();
89
+ const now = Date.now();
90
+ if (cached?.checkedAt && now - cached.checkedAt < UPDATE_CHECK_INTERVAL_MS) {
91
+ return; // Checked recently; skip.
92
+ }
93
+
94
+ // Fire-and-forget: spawn a detached process to query the registry so we
95
+ // never block the CLI. Results are read on the *next* invocation.
96
+ const script = `
97
+ const https = await import('node:https');
98
+ const fs = await import('node:fs');
99
+ const url = 'https://registry.npmjs.org/@vibelet/cli/latest';
100
+ https.get(url, { headers: { 'Accept': 'application/json' }, timeout: 8000 }, (res) => {
101
+ let data = '';
102
+ res.on('data', (c) => data += c);
103
+ res.on('end', () => {
104
+ try {
105
+ const version = JSON.parse(data).version;
106
+ if (version) {
107
+ fs.writeFileSync(${JSON.stringify(updateCheckPath)}, JSON.stringify({
108
+ latestVersion: version,
109
+ checkedAt: Date.now(),
110
+ }, null, 2) + '\\n');
111
+ }
112
+ } catch {}
113
+ });
114
+ }).on('error', () => {});
115
+ `;
116
+
117
+ const child = spawn(process.execPath, ['--input-type=module', '-e', script], {
118
+ detached: true,
119
+ stdio: 'ignore',
120
+ });
121
+ child.unref();
122
+ }
123
+
42
124
  // ─── Helpers ────────────────────────────────────────────────────────────────────
43
125
 
44
126
  function fail(message, details) {
@@ -563,6 +645,10 @@ function clearRelayConfig() {
563
645
  }
564
646
 
565
647
  async function main() {
648
+ // Update check: read cached result (sync, instant) and spawn background fetch.
649
+ checkForUpdateFromCache();
650
+ fetchLatestVersionInBackground();
651
+
566
652
  const relayArg = parseRelayArg();
567
653
  const hostArg = parseNamedArg('host', '100.x.x.x');
568
654
  const fallbackHostsArg = parseNamedArg('fallback-hosts', '100.x.x.x,192.168.1.x');