yaver-cli 1.99.246 → 1.99.248
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 +1 -1
- package/src/index.js +9 -0
- package/src/update-check.js +116 -0
package/package.json
CHANGED
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 };
|