@oya-ai/cli 1.0.61 → 1.0.64

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.
Files changed (2) hide show
  1. package/dist/index.js +33 -13
  2. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -168,12 +168,32 @@ var Browser = class {
168
168
  return res.text;
169
169
  }
170
170
  /**
171
- * Watch it work: an SSE stream of JPEG frames. EventSource cannot set
172
- * headers, so the key travels as a query parameter — treat the URL itself as
173
- * a credential.
171
+ * Watch it work: the console, opened on this browser.
172
+ *
173
+ * This used to return the raw frame stream with the project's API key in the
174
+ * query string — a permanent credential in browser history, Referer headers
175
+ * and every proxy log on the way, and a URL that renders as a wall of
176
+ * text/event-stream if a person actually opens it. It is the console deep
177
+ * link now, the same one the server hands back from `completeMfa()`, and it
178
+ * carries no credential at all.
179
+ *
180
+ * For the frames themselves, use `liveStreamUrl()`.
174
181
  */
175
182
  liveViewUrl() {
176
- return `${this.http.baseUrl}/api/live/${this.id}?key=${encodeURIComponent(this.http.apiKey)}`;
183
+ return `${this.http.baseUrl}/dashboard/?browser=${encodeURIComponent(this.id)}`;
184
+ }
185
+ /**
186
+ * The SSE stream of JPEG frames, for embedding in your own UI. EventSource
187
+ * cannot set headers, so the URL carries a connection ticket: single use,
188
+ * 60 seconds. Mint one per viewer — the first connection spends it.
189
+ */
190
+ async liveStreamUrl() {
191
+ const { ticket } = await this.http.request(
192
+ "POST",
193
+ `/api/control/sessions/${encodeURIComponent(this.id)}/ticket`,
194
+ {}
195
+ );
196
+ return `${this.http.baseUrl}/api/live/${this.id}?ticket=${encodeURIComponent(ticket)}`;
177
197
  }
178
198
  /** Counters, health and the last 50 things this browser did. */
179
199
  status() {
@@ -957,7 +977,7 @@ async function preflight(root, a) {
957
977
  const daemon = await capture("docker", ["info", "--format", "{{.ID}}"]);
958
978
  checks.push({ ok: !!daemon, label: "docker daemon reachable", detail: daemon ? "ok" : "not running", fatal: true });
959
979
  const compose = await capture("docker", ["compose", "version", "--short"]);
960
- checks.push({ ok: atLeast(compose, 2, 24), label: "docker compose \u2265 2.24", detail: compose || "not found", fatal: !compose });
980
+ checks.push({ ok: atLeast(compose, 2, 24), label: "docker compose \u2265 2.24", detail: compose || "not found", fatal: true });
961
981
  }
962
982
  if (a.host !== "docker") {
963
983
  checks.push({ ok: atLeast(process.versions.node, 22, 13), label: "node \u2265 22.13 (node:sqlite)", detail: process.versions.node, fatal: true });
@@ -1035,15 +1055,16 @@ function buildEnv(a, secrets, existing) {
1035
1055
  ];
1036
1056
  return { values: v, extra, apiKey };
1037
1057
  }
1058
+ var PROBE_IMAGE = "alpine@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce";
1038
1059
  async function priorState() {
1039
1060
  const volumes = await capture("docker", ["volume", "ls", "-q", "--filter", "name=oya-data"]) || "";
1040
1061
  for (const name of volumes.split("\n").map((v) => v.trim()).filter(Boolean)) {
1041
- const listing = await capture("docker", ["run", "--rm", "-v", `${name}:/d`, "alpine", "ls", "/d"]);
1062
+ const listing = await capture("docker", ["run", "--rm", "-v", `${name}:/d`, PROBE_IMAGE, "ls", "/d"]);
1042
1063
  if (listing === null || /cookies\.json|personas\.json|profiles|\.secret/.test(listing)) return name;
1043
1064
  }
1044
1065
  return null;
1045
1066
  }
1046
- async function resolveKekConflict(volume) {
1067
+ async function resolveKekConflict(volume, root) {
1047
1068
  warn(`${volume} already holds encrypted data from an earlier install.`);
1048
1069
  note("Cookies, proxy credentials and TOTP seeds in it were sealed with that");
1049
1070
  note("install's OYA_PROFILE_SECRET. A new secret cannot open them, and the");
@@ -1062,10 +1083,9 @@ async function resolveKekConflict(volume) {
1062
1083
  if (!await confirm(`Delete ${volume} and everything stored in it?`, false)) {
1063
1084
  throw new InputError("Stopped without changing anything.");
1064
1085
  }
1065
- await run("docker", ["compose", "down", "-v"], { cwd: process.cwd(), quiet: true }).catch(() => {
1066
- });
1067
- await run("docker", ["volume", "rm", "-f", volume], { cwd: process.cwd(), quiet: true }).catch(() => {
1086
+ await run("docker", ["compose", "down", "-v"], { cwd: root, quiet: true }).catch(() => {
1068
1087
  });
1088
+ await run("docker", ["volume", "rm", "-f", volume], { cwd: root, quiet: true });
1069
1089
  success(`removed ${volume}`);
1070
1090
  return null;
1071
1091
  }
@@ -1189,7 +1209,7 @@ ${style.bold(" Preflight")}`);
1189
1209
  if (!existing.OYA_PROFILE_SECRET && answers.host === "docker" && !dryRun) {
1190
1210
  const volume = await priorState();
1191
1211
  if (volume) {
1192
- const reused = await resolveKekConflict(volume);
1212
+ const reused = await resolveKekConflict(volume, root);
1193
1213
  if (reused) existing.OYA_PROFILE_SECRET = reused;
1194
1214
  }
1195
1215
  }
@@ -1572,8 +1592,8 @@ async function cmdPersonas(args2, flags2) {
1572
1592
  async function cmdOpen(flags2) {
1573
1593
  const browser = await targetBrowser(client(flags2), flags2);
1574
1594
  const url = browser.liveViewUrl();
1575
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1576
- spawn2(opener, [url], { detached: true, stdio: "ignore" }).unref();
1595
+ const [opener, args2] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
1596
+ spawn2(opener, args2, { detached: true, stdio: "ignore" }).unref();
1577
1597
  console.log(`Opening ${browser.id}`);
1578
1598
  }
1579
1599
  async function cmdConfig(args2, flags2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oya-ai/cli",
3
- "version": "1.0.61",
3
+ "version": "1.0.64",
4
4
  "description": "Command line for the Oya browser control plane.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://browser.getoya.ai",
@@ -39,9 +39,10 @@
39
39
  "access": "public"
40
40
  },
41
41
  "devDependencies": {
42
- "@oya-ai/browser": "1.0.61",
42
+ "@oya-ai/browser": "1.0.64",
43
43
  "@types/node": "^20.19.43",
44
44
  "tsup": "^8.0.0",
45
45
  "typescript": "^5.4.0"
46
- }
46
+ },
47
+ "author": "mk@getoya.ai"
47
48
  }