@bivy/bivy 0.17.3-staging.26 → 0.17.3-staging.28
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/bivy.mjs
CHANGED
|
@@ -2432,6 +2432,7 @@ async function cmdApp(args = []) {
|
|
|
2432
2432
|
bivy app remove <app-id> [--session <id>]
|
|
2433
2433
|
bivy app shot [app-id] [--widths 390,1280] [--themes light,dark] [--path /page] [--session <id>]
|
|
2434
2434
|
bivy app present [view] [--path /page] [--note "…"] [--session <id>]
|
|
2435
|
+
bivy app share [app-id] [--view <view>] [--session <id>]
|
|
2435
2436
|
bivy app run [--name <name>] [--restart-on-change] [--session <id>] -- <command> [args…]
|
|
2436
2437
|
|
|
2437
2438
|
Manifest: {"version":1,"name":"My app","views":[
|
|
@@ -2458,10 +2459,16 @@ run: bivy config set sessions.appScreenshots true
|
|
|
2458
2459
|
the chat with the app at phone width (and before/after), and it opens the live
|
|
2459
2460
|
preview. Use it when you finish a visible change, or when the user asks to see
|
|
2460
2461
|
it. [view] is an app or view name or ID (default: the one opened last).
|
|
2462
|
+
"share" mints a reusable link to one web view and prints its URL and expiry
|
|
2463
|
+
(24 hours, or until Revoke access in Apps, the app is removed or the machine
|
|
2464
|
+
restarts). [app-id] is an app ID or name, --view a view ID or name (default: the
|
|
2465
|
+
one opened last). Anyone who has the link can use the app, including a live
|
|
2466
|
+
server's backend, until it expires or is revoked: only post it where everyone
|
|
2467
|
+
who can read it may use the app.
|
|
2461
2468
|
Web previews require operator setup; see docs/apps.md.`);
|
|
2462
2469
|
return;
|
|
2463
2470
|
}
|
|
2464
|
-
if (!["publish", "list", "remove", "shot", "run", "present"].includes(action)) throw new Error("Unknown app command. Run bivy app --help.");
|
|
2471
|
+
if (!["publish", "list", "remove", "shot", "run", "present", "share"].includes(action)) throw new Error("Unknown app command. Run bivy app --help.");
|
|
2465
2472
|
// `run -- <command> [args…]`: publish a one-window desktop app without a manifest file.
|
|
2466
2473
|
let runManifest;
|
|
2467
2474
|
if (action === "run") {
|
|
@@ -2476,6 +2483,15 @@ Web previews require operator setup; see docs/apps.md.`);
|
|
|
2476
2483
|
runManifest = { version: 1, name: name.slice(0, 100), views: [{ kind: "display", name: "Window", command, args: commandArgs, ...(restartIndex >= 0 ? { restartOnChange: true } : {}) }] };
|
|
2477
2484
|
}
|
|
2478
2485
|
const shot = {};
|
|
2486
|
+
if (action === "share") {
|
|
2487
|
+
const i = rest.indexOf("--view");
|
|
2488
|
+
if (i >= 0) {
|
|
2489
|
+
const value = rest[i + 1];
|
|
2490
|
+
if (!value || value.startsWith("--")) throw new Error("--view requires a view ID or name.");
|
|
2491
|
+
shot.view = value;
|
|
2492
|
+
rest.splice(i, 2);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2479
2495
|
if (action === "shot" || action === "present") {
|
|
2480
2496
|
for (const flag of action === "shot" ? ["--widths", "--themes", "--path"] : ["--path", "--note"]) {
|
|
2481
2497
|
const i = rest.indexOf(flag);
|
|
@@ -2494,10 +2510,11 @@ Web previews require operator setup; see docs/apps.md.`);
|
|
|
2494
2510
|
const sessionId = resolveAttachSessionId({ sessionFlag: sessionIndex >= 0 ? rest[sessionIndex + 1] : undefined, env: process.env });
|
|
2495
2511
|
if (!sessionId) throw new Error("Set --session <id> or run inside a Bivy agent session.");
|
|
2496
2512
|
const positional = rest.filter((_, i) => i !== sessionIndex && (sessionIndex < 0 || i !== sessionIndex + 1));
|
|
2497
|
-
if (positional.some((a) => a.startsWith("-")) || (action === "shot" || action === "present" ? positional.length > 1 : positional.length !== (action === "list" || action === "run" ? 0 : 1))) throw new Error("Invalid arguments. Run bivy app --help.");
|
|
2513
|
+
if (positional.some((a) => a.startsWith("-")) || (action === "shot" || action === "present" || action === "share" ? positional.length > 1 : positional.length !== (action === "list" || action === "run" ? 0 : 1))) throw new Error("Invalid arguments. Run bivy app --help.");
|
|
2498
2514
|
const body = { sessionId, ...shot };
|
|
2499
2515
|
if (action === "shot" && positional[0]) body.appId = positional[0];
|
|
2500
2516
|
if (action === "present" && positional[0]) body.target = positional[0];
|
|
2517
|
+
if (action === "share" && positional[0]) body.appId = positional[0];
|
|
2501
2518
|
if (action === "publish") {
|
|
2502
2519
|
const file = path.resolve(positional[0]);
|
|
2503
2520
|
if (fs.statSync(file).size > 64 * 1024) throw new Error("App manifest exceeds 64 KiB.");
|
|
@@ -2519,6 +2536,12 @@ Web previews require operator setup; see docs/apps.md.`);
|
|
|
2519
2536
|
});
|
|
2520
2537
|
const result = await response.json();
|
|
2521
2538
|
if (!response.ok || result.error) throw new Error(result.error || `App command failed (${response.status}).`);
|
|
2539
|
+
if (action === "share") {
|
|
2540
|
+
const { type: _type, requestId: _requestId, ...link } = result;
|
|
2541
|
+
console.log(JSON.stringify({ ...link, expires: new Date(link.expiresAt).toISOString() }, null, 2));
|
|
2542
|
+
console.error("Anyone with this link can use the app, including its live backend, until it expires or you revoke it (Apps → Revoke access).");
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2522
2545
|
console.log(JSON.stringify(result, null, 2));
|
|
2523
2546
|
}
|
|
2524
2547
|
|
package/dist/apps/service.js
CHANGED
|
@@ -231,8 +231,11 @@ export class AppService {
|
|
|
231
231
|
return { ok: true };
|
|
232
232
|
}
|
|
233
233
|
/** A view by app or view ID or name; by default the one opened last, else the newest app's first. */
|
|
234
|
+
webViews(sessionId) {
|
|
235
|
+
return this.registry.list(sessionId).flatMap((app) => app.views.filter((view) => view.kind === "web").map((view) => this.registry.getView(view.id))).filter(Boolean);
|
|
236
|
+
}
|
|
234
237
|
pickView(sessionId, target) {
|
|
235
|
-
const views = this.
|
|
238
|
+
const views = this.webViews(sessionId);
|
|
236
239
|
if (!views.length)
|
|
237
240
|
throw new Error("This session has no web views to present. Publish one with bivy app publish, or preview a detected server.");
|
|
238
241
|
if (target) {
|
|
@@ -507,6 +510,25 @@ export class AppService {
|
|
|
507
510
|
throw new Error("Bivy's preview service is unavailable on this connection.");
|
|
508
511
|
return this.gateway.share(viewId);
|
|
509
512
|
}
|
|
513
|
+
/** `bivy app share`: mints a share link for a web view picked by app and/or
|
|
514
|
+
* view ID or name, like `present` (default: the one opened last). */
|
|
515
|
+
shareView(sessionId, input) {
|
|
516
|
+
let entry;
|
|
517
|
+
if (input.view) {
|
|
518
|
+
const wantedApp = input.app?.trim().toLowerCase();
|
|
519
|
+
const scope = this.webViews(sessionId).filter((candidate) => !wantedApp || candidate.app.id === wantedApp || candidate.app.name.toLowerCase() === wantedApp);
|
|
520
|
+
if (wantedApp && !scope.length)
|
|
521
|
+
throw new Error(`No app called "${input.app}" with web views in this session. Run bivy app list to see them.`);
|
|
522
|
+
const wanted = input.view.trim().toLowerCase();
|
|
523
|
+
const found = scope.find((candidate) => candidate.view.id === wanted) ?? scope.find((candidate) => candidate.view.name.toLowerCase() === wanted);
|
|
524
|
+
if (!found)
|
|
525
|
+
throw new Error(`No web view called "${input.view}"${input.app ? ` in "${input.app}"` : ""}. Run bivy app list to see them.`);
|
|
526
|
+
entry = found;
|
|
527
|
+
}
|
|
528
|
+
else
|
|
529
|
+
entry = this.pickView(sessionId, input.app);
|
|
530
|
+
return { ...this.share(sessionId, entry.app.id, entry.view.id), appId: entry.app.id, viewId: entry.view.id, app: entry.app.name, view: entry.view.name };
|
|
531
|
+
}
|
|
510
532
|
clearNotes(sessionId, appId, viewId) {
|
|
511
533
|
delete this.registry.requireView(sessionId, appId, viewId).notes;
|
|
512
534
|
return { ok: true };
|
|
@@ -29,7 +29,10 @@ export function createAppCommands(service, workspaceFor, published = () => { })
|
|
|
29
29
|
"apps.annotate": (msg) => service.annotate(String(msg.sessionId), msg),
|
|
30
30
|
"apps.clearNotes": (msg) => service.clearNotes(String(msg.sessionId), String(msg.appId), String(msg.viewId)),
|
|
31
31
|
"apps.logs": (msg) => service.logs(String(msg.sessionId), String(msg.appId), String(msg.viewId)),
|
|
32
|
-
|
|
32
|
+
// The app UI passes exact IDs; `bivy app share` passes an app and/or view by ID or name.
|
|
33
|
+
"apps.share": (msg) => typeof msg.viewId === "string" && typeof msg.appId === "string"
|
|
34
|
+
? service.share(String(msg.sessionId), msg.appId, msg.viewId)
|
|
35
|
+
: service.shareView(String(msg.sessionId), { app: typeof msg.appId === "string" ? msg.appId : undefined, view: typeof msg.view === "string" ? msg.view : undefined }),
|
|
33
36
|
"apps.revoke": (msg) => service.revoke(String(msg.sessionId), String(msg.appId), String(msg.viewId)),
|
|
34
37
|
"apps.remove": (msg) => { service.remove(String(msg.sessionId), String(msg.appId)); return { ok: true }; },
|
|
35
38
|
};
|
|
@@ -26,7 +26,7 @@ export const CLIENT_COMMAND_SCHEMAS = {
|
|
|
26
26
|
signals: Type.Optional(Type.Record(Type.String(), Type.Boolean())) }),
|
|
27
27
|
"apps.clearNotes": Type.Object({ ...request, ...session, appId: Type.String(), viewId: Type.String() }),
|
|
28
28
|
"apps.logs": Type.Object({ ...request, ...session, appId: Type.String(), viewId: Type.String() }),
|
|
29
|
-
"apps.share": Type.Object({ ...request, ...session, appId: Type.String(), viewId: Type.String() }),
|
|
29
|
+
"apps.share": Type.Object({ ...request, ...session, appId: Type.Optional(Type.String({ maxLength: 200 })), viewId: Type.Optional(Type.String()), view: Type.Optional(Type.String({ maxLength: 200 })) }),
|
|
30
30
|
"apps.revoke": Type.Object({ ...request, ...session, appId: Type.String(), viewId: Type.String() }),
|
|
31
31
|
"apps.remove": Type.Object({ ...request, ...session, appId: Type.String() }),
|
|
32
32
|
"credentials.native.preview": Type.Object({ ...request, label: Type.Optional(Type.String({ maxLength: 100 })) }),
|
package/package.json
CHANGED