yaver-cli 1.99.102 → 1.99.106

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.102",
3
+ "version": "1.99.106",
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": {
@@ -1,7 +1,90 @@
1
1
  const fs = require('fs');
2
+ const path = require('path');
3
+ const dgram = require('dgram');
4
+ const { execSync } = require('child_process');
2
5
  const { analyzeProject } = require('../analyzer');
3
6
  const { loadSDKManifest } = require('../sdk-manifest');
4
7
 
8
+ // commandExists checks PATH for an executable. Matches the helper used
9
+ // by postinstall.js — kept inline to avoid a new module dependency.
10
+ function commandExists(name) {
11
+ try {
12
+ execSync(`command -v ${name}`, { stdio: ['ignore', 'pipe', 'ignore'] });
13
+ return true;
14
+ } catch (_) {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ // stunPing sends one STUN binding request to the given host:port and
20
+ // resolves true if a response comes back within `timeoutMs`. Used to
21
+ // verify WebRTC ICE connectivity during `yaver doctor`. No external
22
+ // libraries required — STUN binding requests are 20 bytes.
23
+ function stunPing(host, port, timeoutMs = 1500) {
24
+ return new Promise((resolve) => {
25
+ const sock = dgram.createSocket('udp4');
26
+ let done = false;
27
+ const finish = (ok) => {
28
+ if (done) return;
29
+ done = true;
30
+ try { sock.close(); } catch (_) {}
31
+ resolve(ok);
32
+ };
33
+ const timer = setTimeout(() => finish(false), timeoutMs);
34
+ sock.on('message', () => {
35
+ clearTimeout(timer);
36
+ finish(true);
37
+ });
38
+ sock.on('error', () => {
39
+ clearTimeout(timer);
40
+ finish(false);
41
+ });
42
+ // STUN binding request (RFC 5389): 0x0001, length 0, magic cookie,
43
+ // 12-byte transaction ID. Total: 20 bytes.
44
+ const buf = Buffer.alloc(20);
45
+ buf.writeUInt16BE(0x0001, 0); // message type: binding request
46
+ buf.writeUInt16BE(0x0000, 2); // length
47
+ buf.writeUInt32BE(0x2112A442, 4); // magic cookie
48
+ for (let i = 0; i < 12; i++) buf[8 + i] = Math.floor(Math.random() * 256);
49
+ sock.send(buf, port, host, (err) => {
50
+ if (err) {
51
+ clearTimeout(timer);
52
+ finish(false);
53
+ }
54
+ });
55
+ });
56
+ }
57
+
58
+ // reportWebRTCReadiness prints a one-block summary of WebRTC bootstrap
59
+ // state: ffmpeg (mobile container frame encoding), Yaver agent binary
60
+ // (Pion server-side), and STUN reachability for ICE. Each line is
61
+ // independently actionable.
62
+ async function reportWebRTCReadiness() {
63
+ console.log('─── WebRTC + Streaming Readiness ──────────────\n');
64
+
65
+ const ffmpeg = commandExists('ffmpeg');
66
+ console.log(` ${ffmpeg ? '✅' : '❌'} ffmpeg ${ffmpeg ? 'installed' : 'missing — needed for screen capture transcode (auto-installed via `yaver install vibe-preview`)'}`);
67
+
68
+ const yaverBin = commandExists('yaver') || commandExists('yaver.exe');
69
+ console.log(` ${yaverBin ? '✅' : '❌'} yaver agent binary ${yaverBin ? 'on PATH (embeds Pion WebRTC server)' : 'not on PATH'}`);
70
+
71
+ // Best-effort STUN ping to Google's public STUN server. Confirms UDP
72
+ // egress works — without it, ICE candidate gathering fails and the
73
+ // WebRTC stream falls back to relay-jpeg-poll.
74
+ const stunHost = 'stun.l.google.com';
75
+ const stunPort = 19302;
76
+ process.stdout.write(` ⏳ STUN reachability (${stunHost}:${stunPort})…`);
77
+ const stunOk = await stunPing(stunHost, stunPort, 1500);
78
+ process.stdout.write('\r');
79
+ console.log(` ${stunOk ? '✅' : '⚠️ '} STUN ${stunOk ? 'reachable' : 'unreachable — ICE may fail; configure TURN on your relay (relay/deploy/coturn.conf, when added)'} (${stunHost}:${stunPort})`);
80
+
81
+ // Note about TURN. Yaver doesn't ship coturn yet (planned in the
82
+ // Phase 2 WebRTC plan). When the user has a managed relay running
83
+ // coturn, `yaver relay test` will surface its TURN port.
84
+ console.log(' ℹ️ TURN: configured per-relay. `yaver relay test` to verify your relay\'s TURN port.');
85
+ console.log('');
86
+ }
87
+
5
88
  async function doctor(options = {}) {
6
89
  if (!fs.existsSync('package.json')) {
7
90
  console.error('❌ No package.json found. Run this from your RN project root.');
@@ -77,6 +160,12 @@ async function doctor(options = {}) {
77
160
  }
78
161
  console.log('');
79
162
 
163
+ // WebRTC + streaming readiness — opt-out with --no-webrtc so CI
164
+ // doesn't pay the 1.5s STUN probe per run. Default on.
165
+ if (options.webrtc !== false) {
166
+ await reportWebRTCReadiness();
167
+ }
168
+
80
169
  // Summary
81
170
  const total = analysis.availableModules.length + analysis.missingModules.length;
82
171
  console.log(`─── Summary ────────────────────────────────────\n`);
@@ -70,6 +70,33 @@ function installMissingCodingRunners() {
70
70
  }
71
71
  }
72
72
 
73
+ // ensureLinuxHermescBuildDeps installs the apt packages required to
74
+ // compile hermesc from facebook/hermes sources on linux/arm64 (no
75
+ // upstream prebuilt exists for that arch). Best-effort; only runs as
76
+ // root. Mirrors ci/remote/install-hermesc.sh's dep list so the
77
+ // outcome matches the Hetzner test-box bootstrap path.
78
+ function ensureLinuxHermescBuildDeps() {
79
+ try {
80
+ if (process.platform !== "linux") return;
81
+ if (typeof process.geteuid !== "function" || process.geteuid() !== 0) {
82
+ log("linux/arm64 hermesc build needs cmake/ninja/clang/libicu-dev — re-run `npm install -g yaver-cli` as root to provision them, or install manually.");
83
+ return;
84
+ }
85
+ const required = ["git", "cmake", "ninja-build", "clang", "python3", "build-essential", "ca-certificates", "libicu-dev", "zlib1g-dev"];
86
+ if (!commandExists("apt-get")) {
87
+ log(`hermesc build deps missing (${required.join(", ")}) and no apt-get available. Install manually then re-run \`yaver install mobile\`.`);
88
+ return;
89
+ }
90
+ execSync("apt-get update -y", { stdio: ["ignore", "ignore", "ignore"] });
91
+ execSync(`DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ${required.join(" ")}`, {
92
+ stdio: "inherit",
93
+ });
94
+ log("Installed Linux hermesc build deps (cmake/ninja/clang/libicu-dev).");
95
+ } catch (error) {
96
+ log(`Skipping hermesc build deps install: ${error.message}`);
97
+ }
98
+ }
99
+
73
100
  function ensureLinuxRunnerSandboxPackages() {
74
101
  try {
75
102
  if (process.platform !== "linux") return;
@@ -229,7 +256,21 @@ async function main() {
229
256
  // Never blocks install — the bundler falls back to project-local RN
230
257
  // if this step skipped or failed.
231
258
  try {
232
- const hermescPath = await ensureHermesc({ quiet: true });
259
+ let hermescPath = await ensureHermesc({ quiet: true });
260
+ // linux/arm64 (and any other platform with no upstream prebuilt)
261
+ // falls through to build-from-source. Without this branch the
262
+ // first `yaver-push` on the box pays a 1–2 min CMake bill — bad UX
263
+ // on a fresh Hetzner ARM dev box. Install the toolchain (apt only,
264
+ // when running as root) and try the source build now so push is
265
+ // instant later. Never blocks npm install on failure.
266
+ if (!hermescPath && process.platform === "linux" && process.arch === "arm64") {
267
+ ensureLinuxHermescBuildDeps();
268
+ try {
269
+ hermescPath = await ensureHermesc({ quiet: true, allowBuildFromSource: true });
270
+ } catch (buildErr) {
271
+ log(`Source build of hermesc failed: ${buildErr.message}`);
272
+ }
273
+ }
233
274
  if (hermescPath) {
234
275
  log(`Hermes compiler ready at ${hermescPath}.`);
235
276
  } else {