vibeview 0.21.0 → 0.22.0
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/dist/{chunk-VVKF7GGL.js → chunk-SHS5PCSS.js} +68 -46
- package/dist/{chunk-KON2FOEC.js → chunk-UGCGJ6IN.js} +7 -0
- package/dist/cli.js +202 -117
- package/dist/{server-WWNWYD4Q.js → server-6IJ44HAX.js} +126 -11
- package/dist/{upload-app-W6NOSJPA.js → upload-app-LJH3PETS.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +14 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
loadConfig,
|
|
7
7
|
resolveAuth,
|
|
8
8
|
uploadAppBuild
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UGCGJ6IN.js";
|
|
10
10
|
|
|
11
11
|
// src/lib/dev-state.ts
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -528,10 +528,6 @@ function noAppsMessage(platform, hasBuildConfig) {
|
|
|
528
528
|
}
|
|
529
529
|
return `No ${platform} apps found. Run \`vibeview build --platform ${platform}\` to build and upload one (it will ask for your build command), or upload a build with \`vibeview upload-app\`.`;
|
|
530
530
|
}
|
|
531
|
-
function firstRunAppStrategy(apps, buildConfigured) {
|
|
532
|
-
if (!buildConfigured) return "picker";
|
|
533
|
-
return apps.length === 0 ? "build_now" : "ask";
|
|
534
|
-
}
|
|
535
531
|
function fillLaterHint(skipped, platform) {
|
|
536
532
|
if (!skipped.build && !skipped.metro) return "";
|
|
537
533
|
const lines = ["You can enable one-command builds later \u2014 add to vibeview.json:"];
|
|
@@ -1586,6 +1582,38 @@ function printOutcomeLine(build, multi) {
|
|
|
1586
1582
|
console.error(line);
|
|
1587
1583
|
}
|
|
1588
1584
|
|
|
1585
|
+
// src/lib/roku-push.ts
|
|
1586
|
+
function createRokuPush(opts) {
|
|
1587
|
+
const runBuild = opts.runBuild ?? runBuildForPlatform;
|
|
1588
|
+
return async () => {
|
|
1589
|
+
const started = Date.now();
|
|
1590
|
+
let result;
|
|
1591
|
+
try {
|
|
1592
|
+
result = await runBuild(
|
|
1593
|
+
opts.cwd,
|
|
1594
|
+
"roku",
|
|
1595
|
+
opts.build,
|
|
1596
|
+
{ token: opts.token, baseUrl: opts.baseUrl },
|
|
1597
|
+
opts.log
|
|
1598
|
+
);
|
|
1599
|
+
} catch (err) {
|
|
1600
|
+
const message = err.message;
|
|
1601
|
+
if (/artifact not found/i.test(message)) {
|
|
1602
|
+
throw new Error(
|
|
1603
|
+
`${message}
|
|
1604
|
+
your build command must produce a zip \u2014 use the package/zip step, not deploy`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
throw err;
|
|
1608
|
+
}
|
|
1609
|
+
if (!result.build_id) {
|
|
1610
|
+
throw new Error(`upload returned no build id for ${result.name ?? result.public_id}`);
|
|
1611
|
+
}
|
|
1612
|
+
await opts.api.installBuild(opts.sessionId, result.build_id, true);
|
|
1613
|
+
opts.log(`reloaded in ${((Date.now() - started) / 1e3).toFixed(1)}s`);
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1589
1617
|
// src/commands/dev.ts
|
|
1590
1618
|
import { spawn as spawn4 } from "child_process";
|
|
1591
1619
|
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
@@ -2015,38 +2043,6 @@ function watchProjectFiles(cwd, artifact, userIgnore = []) {
|
|
|
2015
2043
|
};
|
|
2016
2044
|
}
|
|
2017
2045
|
|
|
2018
|
-
// src/lib/roku-push.ts
|
|
2019
|
-
function createRokuPush(opts) {
|
|
2020
|
-
const runBuild = opts.runBuild ?? runBuildForPlatform;
|
|
2021
|
-
return async () => {
|
|
2022
|
-
const started = Date.now();
|
|
2023
|
-
let result;
|
|
2024
|
-
try {
|
|
2025
|
-
result = await runBuild(
|
|
2026
|
-
opts.cwd,
|
|
2027
|
-
"roku",
|
|
2028
|
-
opts.build,
|
|
2029
|
-
{ token: opts.token, baseUrl: opts.baseUrl },
|
|
2030
|
-
opts.log
|
|
2031
|
-
);
|
|
2032
|
-
} catch (err) {
|
|
2033
|
-
const message = err.message;
|
|
2034
|
-
if (/artifact not found/i.test(message)) {
|
|
2035
|
-
throw new Error(
|
|
2036
|
-
`${message}
|
|
2037
|
-
your build command must produce a zip \u2014 use the package/zip step, not deploy`
|
|
2038
|
-
);
|
|
2039
|
-
}
|
|
2040
|
-
throw err;
|
|
2041
|
-
}
|
|
2042
|
-
if (!result.build_id) {
|
|
2043
|
-
throw new Error(`upload returned no build id for ${result.name ?? result.public_id}`);
|
|
2044
|
-
}
|
|
2045
|
-
await opts.api.installBuild(opts.sessionId, result.build_id, true);
|
|
2046
|
-
opts.log(`reloaded in ${((Date.now() - started) / 1e3).toFixed(1)}s`);
|
|
2047
|
-
};
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
2046
|
// src/commands/dev.ts
|
|
2051
2047
|
function parseWatchSeconds(flag) {
|
|
2052
2048
|
if (flag === void 0 || flag === false) return void 0;
|
|
@@ -2134,7 +2130,13 @@ async function startDevSession(cfg, onEvent = () => {
|
|
|
2134
2130
|
}
|
|
2135
2131
|
if (cfg.open !== false) await openBrowser(pageUrl).catch(() => {
|
|
2136
2132
|
});
|
|
2137
|
-
const active = await pollUntilActive(
|
|
2133
|
+
const active = await pollUntilActive(
|
|
2134
|
+
api,
|
|
2135
|
+
sessionId,
|
|
2136
|
+
18e4,
|
|
2137
|
+
2e3,
|
|
2138
|
+
() => onEvent({ event: "installing", session_id: sessionId })
|
|
2139
|
+
);
|
|
2138
2140
|
for (const w of active.warnings ?? []) {
|
|
2139
2141
|
if (reportedWarnings.has(w)) continue;
|
|
2140
2142
|
reportedWarnings.add(w);
|
|
@@ -2192,13 +2194,18 @@ async function pollUntilTunnelReady(api, sessionId, timeoutMs, intervalMs = 2e3)
|
|
|
2192
2194
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
2193
2195
|
}
|
|
2194
2196
|
}
|
|
2195
|
-
async function pollUntilActive(api, sessionId, timeoutMs, intervalMs = 2e3) {
|
|
2197
|
+
async function pollUntilActive(api, sessionId, timeoutMs, intervalMs = 2e3, onInstallPending) {
|
|
2196
2198
|
const deadline = Date.now() + timeoutMs;
|
|
2199
|
+
let pendingReported = false;
|
|
2197
2200
|
for (; ; ) {
|
|
2198
|
-
const info = await api.
|
|
2201
|
+
const info = await api.touchSession(sessionId);
|
|
2199
2202
|
const blocking = findBlockingWarning(info);
|
|
2200
2203
|
if (blocking) throw new Error(blocking);
|
|
2201
|
-
if (info.status === "active"
|
|
2204
|
+
if (info.status === "active" && info.app_install_pending && !pendingReported) {
|
|
2205
|
+
pendingReported = true;
|
|
2206
|
+
onInstallPending?.();
|
|
2207
|
+
}
|
|
2208
|
+
if (info.status === "active" && !info.app_install_pending) return info;
|
|
2202
2209
|
if (info.status === "stopped" || info.status === "failed") {
|
|
2203
2210
|
throw new Error(`session ${info.status} before it became active`);
|
|
2204
2211
|
}
|
|
@@ -2250,6 +2257,12 @@ function validateAppChoice(requested, fromFlag, apps) {
|
|
|
2250
2257
|
if (apps.some((a) => a.public_id === requested)) return { appId: requested };
|
|
2251
2258
|
return fromFlag ? { staleFlag: requested } : { staleCached: requested };
|
|
2252
2259
|
}
|
|
2260
|
+
function buildAppStrategy(apps, buildFlag, json, isTTY) {
|
|
2261
|
+
if (buildFlag) return "build";
|
|
2262
|
+
if (apps.length === 0) return "build";
|
|
2263
|
+
if (json || !isTTY) return "pick";
|
|
2264
|
+
return "ask";
|
|
2265
|
+
}
|
|
2253
2266
|
function requireNonInteractive(json) {
|
|
2254
2267
|
if (!json) return;
|
|
2255
2268
|
emit(json, {
|
|
@@ -2325,9 +2338,10 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2325
2338
|
chalk3.yellow(`\u26A0 app "${choice.staleCached}" no longer exists \u2014 pick your debug build again`)
|
|
2326
2339
|
);
|
|
2327
2340
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2341
|
+
const buildConfig = firstRunBuild ?? entry?.build ?? null;
|
|
2342
|
+
if (!appId && buildConfig) {
|
|
2343
|
+
const strategy = buildAppStrategy(apps, Boolean(opts.build), opts.json, process.stdin.isTTY);
|
|
2344
|
+
let action = strategy === "ask" ? "pick" : strategy;
|
|
2331
2345
|
if (strategy === "ask") {
|
|
2332
2346
|
action = await select3({
|
|
2333
2347
|
message: "Run against which build?",
|
|
@@ -2343,7 +2357,7 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2343
2357
|
const result = await runBuildForPlatform(
|
|
2344
2358
|
process.cwd(),
|
|
2345
2359
|
platform,
|
|
2346
|
-
|
|
2360
|
+
buildConfig,
|
|
2347
2361
|
opts,
|
|
2348
2362
|
(l) => console.error(chalk3.dim(l))
|
|
2349
2363
|
);
|
|
@@ -2352,6 +2366,7 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2352
2366
|
} catch (err) {
|
|
2353
2367
|
const message = err.message;
|
|
2354
2368
|
console.error(chalk3.red(`error: ${message}`));
|
|
2369
|
+
emit(opts.json, { event: "error", message });
|
|
2355
2370
|
process.exit(1);
|
|
2356
2371
|
}
|
|
2357
2372
|
}
|
|
@@ -2583,6 +2598,10 @@ async function runDevInProcess(opts) {
|
|
|
2583
2598
|
} else if (evt.event === "tunnel_connected") {
|
|
2584
2599
|
everConnected = true;
|
|
2585
2600
|
console.error(chalk3.green("\u2713 tunnel connected \u2014 edit away, Fast Refresh is live"));
|
|
2601
|
+
} else if (evt.event === "installing") {
|
|
2602
|
+
console.error(
|
|
2603
|
+
chalk3.dim(" Putting the channel on the Roku \u2014 the link appears once it is up.")
|
|
2604
|
+
);
|
|
2586
2605
|
} else if (evt.event === "session_ended" && !stopping && handle) {
|
|
2587
2606
|
console.error(chalk3.red("tunnel closed by server \u2014 session ended"));
|
|
2588
2607
|
stopping = true;
|
|
@@ -2692,7 +2711,7 @@ async function runDevInProcess(opts) {
|
|
|
2692
2711
|
if (stopping) return;
|
|
2693
2712
|
let info = null;
|
|
2694
2713
|
try {
|
|
2695
|
-
info = await api.
|
|
2714
|
+
info = await api.touchSession(sessionId);
|
|
2696
2715
|
} catch {
|
|
2697
2716
|
}
|
|
2698
2717
|
const verdict = sessionLivenessVerdict(info);
|
|
@@ -3488,6 +3507,7 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
3488
3507
|
export {
|
|
3489
3508
|
readDevState,
|
|
3490
3509
|
clearDevState,
|
|
3510
|
+
DEV_PLATFORMS,
|
|
3491
3511
|
PLATFORM_NEEDS_METRO,
|
|
3492
3512
|
isDevPlatform,
|
|
3493
3513
|
loadDevConfig,
|
|
@@ -3501,7 +3521,9 @@ export {
|
|
|
3501
3521
|
newTransitionState,
|
|
3502
3522
|
printTransition,
|
|
3503
3523
|
printOutcomeLine,
|
|
3524
|
+
createRokuPush,
|
|
3504
3525
|
startDevSession,
|
|
3526
|
+
ROKU_LIVENESS_INTERVAL_MS,
|
|
3505
3527
|
PLATFORM_DEVICE,
|
|
3506
3528
|
dev,
|
|
3507
3529
|
AGENT_COMMANDS,
|
|
@@ -250,6 +250,13 @@ var ApiClient = class {
|
|
|
250
250
|
async getSession(sessionId) {
|
|
251
251
|
return this.requestRaw("GET", `/sessions/${sessionId}`);
|
|
252
252
|
}
|
|
253
|
+
/** Report presence on a session and read it back in one call. A Roku dev
|
|
254
|
+
* loop has no tunnel and no browser, so this is what tells the backend
|
|
255
|
+
* someone is still using the session between device commands. Same
|
|
256
|
+
* response shape as `getSession`. */
|
|
257
|
+
async touchSession(sessionId) {
|
|
258
|
+
return this.requestRaw("POST", `/sessions/${sessionId}/presence`);
|
|
259
|
+
}
|
|
253
260
|
async stopSession(sessionId) {
|
|
254
261
|
await this.requestRaw("DELETE", `/sessions/${sessionId}`);
|
|
255
262
|
}
|