yaver-cli 1.99.245 → 1.99.247

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": "yaver-cli",
3
- "version": "1.99.245",
3
+ "version": "1.99.247",
4
4
  "mcpName": "io.github.kivanccakmak/yaver",
5
5
  "description": "Unified npm bootstrap for the Yaver agent, SDK injection, and local-first developer runtime",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -10,6 +10,7 @@ const { status } = require('./commands/status');
10
10
  const { feedback } = require('./commands/feedback');
11
11
  const { deploy, isLocalDeployToken } = require('./commands/deploy');
12
12
  const { run, isLocalRunToken } = require('./commands/run');
13
+ const { maybePromptUpdate } = require('./update-check');
13
14
 
14
15
  const PUSH_HELP = `
15
16
  yaver push — Push existing React Native projects to the Yaver mobile host
@@ -160,6 +161,14 @@ async function runUnified(args) {
160
161
  process.env.YAVER_NPM_PACKAGE = PACKAGE.name;
161
162
  process.env.YAVER_NPM_VERSION = PACKAGE.version;
162
163
 
164
+ // Codex-style update onboarder. Only on the interactive launches — the
165
+ // bare `yaver` shell and `yaver wrap`/`code` — so scripted commands
166
+ // (status/push/serve/mcp/CI) are never interrupted. maybePromptUpdate
167
+ // self-gates on TTY + an 8h throttle and fails open on any error.
168
+ if (!command || command === 'wrap' || command === 'code') {
169
+ await maybePromptUpdate(PACKAGE.name, PACKAGE.version);
170
+ }
171
+
163
172
  if (command === '--help' || command === '-h' || command === 'help') {
164
173
  console.log(UNIFIED_HELP);
165
174
  process.exit(0);
@@ -0,0 +1,116 @@
1
+ // Codex-style update onboarder for the yaver CLI.
2
+ //
3
+ // On interactive startup (the bare `yaver` shell or `yaver wrap`/`code`),
4
+ // checks npm for a newer yaver-cli and offers a one-keystroke upgrade —
5
+ // mirroring Codex's "✨ Update available! X -> Y" prompt. Everything here
6
+ // is best-effort and fail-open: a network blip, a non-TTY, CI, or any
7
+ // error must NEVER block or break the CLI.
8
+ //
9
+ // Throttled to once per CHECK_INTERVAL so the multi-pane/tmux workflow
10
+ // (yaver runs constantly) isn't nagged or hammering the npm registry.
11
+
12
+ const https = require('https');
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const path = require('path');
16
+ const readline = require('readline');
17
+ const { spawnSync } = require('child_process');
18
+
19
+ const STATE_DIR = path.join(os.homedir(), '.yaver');
20
+ const STATE_FILE = path.join(STATE_DIR, 'update-check.json');
21
+ const CHECK_INTERVAL_MS = 8 * 60 * 60 * 1000; // 8h
22
+ const FETCH_TIMEOUT_MS = 1500;
23
+ const RELEASES_URL = 'https://github.com/kivanccakmak/yaver.io/releases';
24
+
25
+ function readState() {
26
+ try { return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); } catch { return {}; }
27
+ }
28
+ function writeState(s) {
29
+ try { fs.mkdirSync(STATE_DIR, { recursive: true }); fs.writeFileSync(STATE_FILE, JSON.stringify(s)); } catch { /* ignore */ }
30
+ }
31
+
32
+ function parseVer(v) { return String(v || '').split('.').map((n) => parseInt(n, 10) || 0); }
33
+ function isNewer(latest, current) {
34
+ const a = parseVer(latest);
35
+ const b = parseVer(current);
36
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
37
+ const x = a[i] || 0;
38
+ const y = b[i] || 0;
39
+ if (x !== y) return x > y;
40
+ }
41
+ return false;
42
+ }
43
+
44
+ function fetchLatest(pkg) {
45
+ return new Promise((resolve) => {
46
+ let done = false;
47
+ const finish = (v) => { if (!done) { done = true; resolve(v); } };
48
+ try {
49
+ const req = https.get(`https://registry.npmjs.org/${pkg}/latest`, { timeout: FETCH_TIMEOUT_MS }, (res) => {
50
+ if (res.statusCode !== 200) { res.resume(); return finish(null); }
51
+ let body = '';
52
+ res.on('data', (c) => { body += c; });
53
+ res.on('end', () => { try { finish(JSON.parse(body).version); } catch { finish(null); } });
54
+ });
55
+ req.on('error', () => finish(null));
56
+ req.on('timeout', () => { req.destroy(); finish(null); });
57
+ } catch { finish(null); }
58
+ });
59
+ }
60
+
61
+ function ask(question) {
62
+ return new Promise((resolve) => {
63
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
64
+ rl.question(question, (ans) => { rl.close(); resolve((ans || '').trim()); });
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Check for a newer yaver-cli and, if found, show the interactive
70
+ * onboarder. Returns without doing anything when non-interactive,
71
+ * throttled, in CI, or on any error.
72
+ */
73
+ async function maybePromptUpdate(pkgName, currentVersion) {
74
+ try {
75
+ if (process.env.YAVER_NO_UPDATE_CHECK === '1') return;
76
+ if (process.env.CI) return;
77
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return;
78
+
79
+ const state = readState();
80
+ const now = Date.now();
81
+ if (state.lastCheck && now - state.lastCheck < CHECK_INTERVAL_MS) return;
82
+
83
+ const latest = await fetchLatest(pkgName);
84
+ writeState({ ...state, lastCheck: now });
85
+ if (!latest || !isNewer(latest, currentVersion)) return;
86
+ if (state.skipUntil === latest) return; // "skip until next version"
87
+
88
+ const bold = '\x1b[1m';
89
+ const green = '\x1b[32m';
90
+ const dim = '\x1b[2m';
91
+ const reset = '\x1b[0m';
92
+ process.stdout.write(`\n ${bold}✨ Update available!${reset} ${currentVersion} -> ${green}${latest}${reset}\n`);
93
+ process.stdout.write(` ${dim}Release notes: ${RELEASES_URL}${reset}\n\n`);
94
+ process.stdout.write(` 1. Update now ${dim}(runs npm install -g ${pkgName}@latest)${reset}\n`);
95
+ process.stdout.write(` 2. Skip\n`);
96
+ process.stdout.write(` 3. Skip until next version\n\n`);
97
+ const ans = await ask(' Choose [1]: ');
98
+ const choice = ans === '' ? '1' : ans;
99
+
100
+ if (choice === '2') return;
101
+ if (choice === '3') { writeState({ ...readState(), skipUntil: latest }); return; }
102
+
103
+ // Update now — same install path users bootstrap with.
104
+ process.stdout.write(`\n Updating ${pkgName} via npm...\n`);
105
+ const r = spawnSync('npm', ['install', '-g', `${pkgName}@latest`], { stdio: 'inherit' });
106
+ if (r.status === 0) {
107
+ process.stdout.write(`\n 🎉 Updated to ${latest}. Please re-run yaver.\n`);
108
+ process.exit(0);
109
+ }
110
+ process.stdout.write(`\n Update failed — run \`npm install -g ${pkgName}@latest\` manually, or pick Skip.\n`);
111
+ } catch {
112
+ // Never let the update check break the CLI.
113
+ }
114
+ }
115
+
116
+ module.exports = { maybePromptUpdate, isNewer };