@oya-ai/cli 1.0.103 → 1.0.105

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 (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +83 -40
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@oya-ai/cli"><img src="https://img.shields.io/npm/v/@oya-ai/cli?color=39ed35&label=@oya-ai/cli&logo=npm" alt="NPM Version"></a>
9
9
  <a href="https://github.com/OyadotAI/oya-browser/blob/main/packages/cli/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?color=39ed35" alt="License: MIT"></a>
10
- <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node Version"></a>
10
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg" alt="Node Version"></a>
11
11
  </p>
12
12
 
13
13
  ---
@@ -165,7 +165,7 @@ In automated pipelines (GitHub Actions, GitLab CI, Docker), use flags or environ
165
165
 
166
166
  ```bash
167
167
  export OYA_API_KEY="oya_live_..."
168
- export OYA_BASE_URL="https://browser.getoya.ai"
168
+ export OYA_BASE_URL="https://oyabrowser.com"
169
169
 
170
170
  # Non-interactive launch with JSON output
171
171
  oya start --provider browserbase --persona auto --json
package/dist/index.js CHANGED
@@ -472,7 +472,7 @@ async function locateRepo() {
472
472
 
473
473
  // src/install/llm.ts
474
474
  var LLM_PRESETS = {
475
- anthropic: { base: "https://api.anthropic.com/v1", model: "claude-sonnet-4-5", label: "Anthropic (Claude)" },
475
+ anthropic: { base: "https://api.anthropic.com/v1", model: "claude-sonnet-5", label: "Anthropic (Claude)" },
476
476
  openai: { base: "https://api.openai.com/v1", model: "gpt-4o-mini", label: "OpenAI" },
477
477
  gemini: {
478
478
  base: "https://generativelanguage.googleapis.com/v1beta/openai",
@@ -548,7 +548,7 @@ async function judge(res, spin, baseUrl, model) {
548
548
  return `${baseUrl} rejected that key (HTTP ${res.status}).`;
549
549
  }
550
550
  if (res.ok) await confirmModel(res, spin, model);
551
- else spin.fail(`could not verify (HTTP ${res.status}) \u2014 continuing anyway`);
551
+ else spin.fail(`could not verify (HTTP ${res.status}), continuing anyway`);
552
552
  return null;
553
553
  }
554
554
  async function confirmModel(res, spin, model) {
@@ -565,7 +565,7 @@ async function verifyLlm(baseUrl, key, model) {
565
565
  try {
566
566
  return await judge(await probe(baseUrl, key, model), spin, baseUrl, model);
567
567
  } catch (err) {
568
- spin.fail(`could not reach it (${err.message}) \u2014 continuing anyway`);
568
+ spin.fail(`could not reach it (${err.message}), continuing anyway`);
569
569
  return null;
570
570
  }
571
571
  }
@@ -644,7 +644,7 @@ function isPostgresUrl(v) {
644
644
  const url = new URL(v);
645
645
  if (!["postgres:", "postgresql:"].includes(url.protocol)) return "Must start with postgres://";
646
646
  } catch {
647
- return "That is not a connection string \u2014 postgres://user:pass@host:5432/db";
647
+ return "That is not a connection string, postgres://user:pass@host:5432/db";
648
648
  }
649
649
  }
650
650
  async function pick(preset, heading, question, options) {
@@ -667,7 +667,7 @@ async function askPostgres(s) {
667
667
  s.migrateUrl = s.secrets.DATABASE_URL;
668
668
  }
669
669
  function sqliteNotes() {
670
- note("SQLite means API-key auth only \u2014 email sign-in needs Supabase.");
670
+ note("SQLite means API-key auth only, email sign-in needs Supabase.");
671
671
  note("The dashboard still works: sign in with the API key printed at the end.");
672
672
  }
673
673
  async function askDatabase(s) {
@@ -710,7 +710,7 @@ function checkPublicUrl(v) {
710
710
  const url = new URL(v);
711
711
  if (!["http:", "https:"].includes(url.protocol)) return "Must be http:// or https://";
712
712
  } catch {
713
- return "That is not a URL \u2014 try http://localhost:3100";
713
+ return "That is not a URL, try http://localhost:3100";
714
714
  }
715
715
  }
716
716
  async function askPublicUrl(s) {
@@ -789,7 +789,7 @@ function envCheck(root) {
789
789
  const envPath = join2(root, ".env");
790
790
  if (!existsSync2(envPath)) return [];
791
791
  const mode = (statSync(envPath).mode & PERMISSION_BITS).toString(OCTAL);
792
- return [{ ok: true, label: ".env exists \u2014 keeping its OYA_PROFILE_SECRET and API keys", detail: `mode ${mode}` }];
792
+ return [{ ok: true, label: ".env exists, keeping its OYA_PROFILE_SECRET and API keys", detail: `mode ${mode}` }];
793
793
  }
794
794
  var psqlCheck = async () => ({
795
795
  ok: await has("psql"),
@@ -832,12 +832,12 @@ var SECRET_KEYS = /* @__PURE__ */ new Set([
832
832
  ]);
833
833
  var SECTIONS = [
834
834
  ["Server", ["PORT", "OYA_UI_MODE", "OYA_DATA_DIR"]],
835
- ["Tenant keys \u2014 each is an identity; add more from the dashboard", ["API_KEYS"]],
835
+ ["Tenant keys, each is an identity; add more from the dashboard", ["API_KEYS"]],
836
836
  ["Host operations: /metrics, drain, fleet provision", ["OYA_OPERATOR_TOKEN", "OYA_METRICS_TOKEN"]],
837
837
  ["Credentials at rest. Lose this and every stored cookie, proxy and TOTP seed is unreadable", ["OYA_PROFILE_SECRET"]],
838
838
  ["Database", ["SUPABASE_URL", "SUPABASE_SERVICE_KEY", "DATABASE_URL", "OYA_RECORDING_BUCKET"]],
839
839
  [
840
- "Agent LLM. A private or http base URL is operator-only: the dashboard and\n# POST /config/host reject one, on purpose \u2014 tenants can set that field too",
840
+ "Agent LLM. A private or http base URL is operator-only: the dashboard and\n# POST /config/host reject one, on purpose, tenants can set that field too",
841
841
  ["OPENAI_API_KEY", "OPENAI_BASE_URL", "CHAT_MODEL"]
842
842
  ],
843
843
  ["Browser fleet", ["OYA_BROWSER_PROVIDER", "OYA_FLEET_RUNTIME", "OYA_PUBLIC_WS_URL", "OYA_CDP_WS_URL"]],
@@ -883,7 +883,7 @@ function sectionLines(values, placed) {
883
883
  return lines;
884
884
  }
885
885
  function renderEnv(values, extra) {
886
- const lines = ["# Written by `oya install`. Re-run it, or edit by hand \u2014 both are fine.", ""];
886
+ const lines = ["# Written by `oya install`. Re-run it, or edit by hand, both are fine.", ""];
887
887
  const placed = /* @__PURE__ */ new Set();
888
888
  lines.push(...sectionLines(values, placed));
889
889
  const leftover = Object.keys(values).filter((k) => !placed.has(k) && values[k]);
@@ -1078,7 +1078,7 @@ var EGRESS = [
1078
1078
  // is what makes per-session egress policy and budgets enforceable.
1079
1079
  // `app: server` is the label k8s/base/server.yaml gives the control plane;
1080
1080
  // the empty namespaceSelector lets the browsers live in their own namespace.
1081
- // A control plane OUTSIDE the cluster is not matched by any pod selector —
1081
+ // A control plane OUTSIDE the cluster is not matched by any pod selector,
1082
1082
  // add an ipBlock rule here for that case, or the pods will resolve DNS and
1083
1083
  // then fail to enrol.
1084
1084
  { to: [{ namespaceSelector: {}, podSelector: { matchLabels: { app: "server" } } }] }
@@ -1106,7 +1106,7 @@ async function provisionK8sFleet(root, a) {
1106
1106
  const policyPath = savePolicy(root, fleet.namespace);
1107
1107
  await apply(networkPolicy(fleet.namespace, POLICY_NAME), root);
1108
1108
  console.log(` applied namespace ${fleet.namespace} and NetworkPolicy ${POLICY_NAME}`);
1109
- console.log(` policy written to ${policyPath} \u2014 review it; it is your egress boundary`);
1109
+ console.log(` policy written to ${policyPath}, review it; it is your egress boundary`);
1110
1110
  return pinned;
1111
1111
  }
1112
1112
 
@@ -1157,7 +1157,7 @@ function printPreflight(checks) {
1157
1157
  ${style.bold(" Preflight")}`);
1158
1158
  for (const c of checks) {
1159
1159
  const mark = c.ok ? icon.ok : c.fatal ? icon.fail : icon.warn;
1160
- console.log(` ${mark} ${c.label}${c.detail ? style.grey(` \u2014 ${c.detail}`) : ""}`);
1160
+ console.log(` ${mark} ${c.label}${c.detail ? style.grey(` , ${c.detail}`) : ""}`);
1161
1161
  }
1162
1162
  }
1163
1163
  var row = (k, v) => console.log(` ${style.grey(k.padEnd(PLAN_LABEL_WIDTH))}${v}`);
@@ -1194,7 +1194,7 @@ var ANSWERS_FILE = "oya-install.json";
1194
1194
  async function gather(configPath2) {
1195
1195
  const root = await locateRepo();
1196
1196
  const preset = configPath2 ? JSON.parse(readFileSync2(configPath2, "utf8")) : {};
1197
- banner("Oya Browser \u2014 self-host install", root);
1197
+ banner("Oya Browser, self-host install", root);
1198
1198
  if (configPath2) note(`replaying ${configPath2}`);
1199
1199
  steps(configPath2 ? 0 : INTERVIEW_STEPS);
1200
1200
  return { root, ...await interview(preset, !!configPath2) };
@@ -1234,7 +1234,7 @@ function writeFiles(p) {
1234
1234
  writeFileSync3(join5(p.root, ANSWERS_FILE), JSON.stringify(p.answers, null, JSON_INDENT) + "\n");
1235
1235
  success(`wrote ${p.envPath}`);
1236
1236
  if (p.existing.OYA_PROFILE_SECRET) return;
1237
- warn("OYA_PROFILE_SECRET was generated \u2014 back it up.");
1237
+ warn("OYA_PROFILE_SECRET was generated, back it up.");
1238
1238
  note("Without it, stored cookies, proxy credentials and TOTP seeds cannot be decrypted.");
1239
1239
  }
1240
1240
  async function provisionK8s(p) {
@@ -1268,7 +1268,7 @@ import { join as join6, dirname as dirname2 } from "path";
1268
1268
  // src/constants.ts
1269
1269
  var ARGV_SKIP = 2;
1270
1270
  var JSON_INDENT2 = 2;
1271
- var DEFAULT_BASE_URL = "https://browser.getoya.ai";
1271
+ var DEFAULT_BASE_URL = "https://oyabrowser.com";
1272
1272
  var CONFIG_FILE_MODE = 384;
1273
1273
  var LsColumns = {
1274
1274
  /** Provider name. */
@@ -1364,6 +1364,7 @@ async function cmdLogin(flags2) {
1364
1364
  save({ apiKey, baseUrl });
1365
1365
  console.log(`
1366
1366
  \u2705 Signed in to ${baseUrl}. Saved to ${configPath}`);
1367
+ console.log(" Scripts using @oya-ai/browser read this file too, so `new Oya()` needs no environment.");
1367
1368
  console.log(" Next: `oya init` to pick your model and browser provider.");
1368
1369
  }
1369
1370
 
@@ -1381,7 +1382,27 @@ var OyaError = class extends Error {
1381
1382
  this.body = body;
1382
1383
  }
1383
1384
  };
1384
- var DEFAULT_BASE_URL2 = "https://browser.getoya.ai";
1385
+ var node = () => globalThis.process;
1386
+ function builtin(name) {
1387
+ const get = node()?.getBuiltinModule;
1388
+ return typeof get === "function" ? get.call(node(), name) ?? null : null;
1389
+ }
1390
+ function configFile() {
1391
+ const os = builtin("node:os");
1392
+ const home = node()?.env?.OYA_CONFIG_HOME || (os ? `${os.homedir()}/.oya` : null);
1393
+ return home ? `${home}/config.json` : null;
1394
+ }
1395
+ function savedConfig() {
1396
+ try {
1397
+ const fs = builtin("node:fs");
1398
+ const file2 = configFile();
1399
+ if (!fs || !file2 || !fs.existsSync(file2)) return {};
1400
+ return JSON.parse(fs.readFileSync(file2, "utf8"));
1401
+ } catch {
1402
+ return {};
1403
+ }
1404
+ }
1405
+ var DEFAULT_BASE_URL2 = "https://oyabrowser.com";
1385
1406
  var DEFAULT_TIMEOUT_MS = 6e4;
1386
1407
  var START_TIMEOUT_MS = 12e4;
1387
1408
  var READY_TIMEOUT_MS2 = 12e4;
@@ -1427,10 +1448,31 @@ var Http = class {
1427
1448
  fetchImpl;
1428
1449
  /** Sends one request and returns the parsed answer, or throws an OyaError when it failed. */
1429
1450
  async request(method, path, body, timeoutMs = this.timeoutMs, headers = {}) {
1430
- const init = requestInit(method, body, timeoutMs, { Authorization: `Bearer ${this.apiKey}`, ...headers });
1431
- return readAnswer2(await this.fetchImpl(`${this.baseUrl}${path}`, init), `${method} ${path}`, this.baseUrl);
1451
+ const res = await this.send(`${this.baseUrl}${path}`, this.init(method, body, timeoutMs, headers), timeoutMs);
1452
+ return readAnswer2(res, `${method} ${path}`, this.baseUrl);
1453
+ }
1454
+ /** The fetch options for one call, with this client's key on them. */
1455
+ init(method, body, timeoutMs, headers) {
1456
+ return requestInit(method, body, timeoutMs, { Authorization: `Bearer ${this.apiKey}`, ...headers });
1457
+ }
1458
+ /**
1459
+ * fetch's own failures say only "fetch failed" or "The operation was
1460
+ * aborted", which leaves a reader guessing at the address, the port and
1461
+ * whether anything is listening. Say which it was.
1462
+ */
1463
+ async send(url, init, timeoutMs) {
1464
+ try {
1465
+ return await this.fetchImpl(url, init);
1466
+ } catch (err) {
1467
+ throw new OyaError(unreachable(this.baseUrl, timeoutMs, err), 0, { error: String(err?.message) });
1468
+ }
1432
1469
  }
1433
1470
  };
1471
+ function unreachable(baseUrl, timeoutMs, err) {
1472
+ const cause = err?.name === "TimeoutError" || /abort/i.test(String(err?.message));
1473
+ if (cause) return `No answer from ${baseUrl} within ${timeoutMs}ms. Is it reachable, and is the call this slow?`;
1474
+ return `Could not reach ${baseUrl}. Is the server running, and is OYA_BASE_URL right?`;
1475
+ }
1434
1476
  function requestInit(method, body, timeoutMs, headers) {
1435
1477
  const json = body === void 0 ? {} : { "Content-Type": "application/json" };
1436
1478
  const payload = body === void 0 ? void 0 : JSON.stringify(body);
@@ -1457,13 +1499,12 @@ function failure(call2, status, payload, baseUrl) {
1457
1499
  }
1458
1500
  var env = (name) => globalThis.process?.env?.[name];
1459
1501
  function createHttp(options) {
1460
- const apiKey = options.apiKey || env("OYA_API_KEY");
1461
- if (!apiKey) {
1462
- throw new Error("No API key. Pass { apiKey } or set OYA_API_KEY \u2014 run `oya login` to get one.");
1463
- }
1464
- const baseUrl = (options.baseUrl || env("OYA_BASE_URL") || DEFAULT_BASE_URL2).replace(/\/+$/, "");
1502
+ const saved = savedConfig();
1503
+ const apiKey = options.apiKey || env("OYA_API_KEY") || saved.apiKey;
1504
+ if (!apiKey) throw new Error("No API key. Pass { apiKey }, set OYA_API_KEY, or run `npx @oya-ai/cli login`.");
1505
+ const baseUrl = (options.baseUrl || env("OYA_BASE_URL") || saved.baseUrl || DEFAULT_BASE_URL2).replace(/\/+$/, "");
1465
1506
  const fetchImpl = options.fetch || globalThis.fetch;
1466
- if (!fetchImpl) throw new Error("No fetch available \u2014 pass { fetch } or use Node 18+.");
1507
+ if (!fetchImpl) throw new Error("No fetch available, pass { fetch } or use Node 18+.");
1467
1508
  return new Http(baseUrl, apiKey, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, fetchImpl.bind(globalThis));
1468
1509
  }
1469
1510
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -1557,10 +1598,8 @@ var Browser = class {
1557
1598
  constructor(http, info, autoCaptcha) {
1558
1599
  this.http = http;
1559
1600
  this.autoCaptcha = autoCaptcha;
1560
- this.id = info.id;
1561
- this.provider = info.provider;
1562
- this.persona = info.persona;
1563
- this.cdpUrl = info.cdpUrl;
1601
+ ({ id: this.id, provider: this.provider, persona: this.persona, cdpUrl: this.cdpUrl } = info);
1602
+ this.reused = info.reused === true;
1564
1603
  }
1565
1604
  http;
1566
1605
  autoCaptcha;
@@ -1572,6 +1611,8 @@ var Browser = class {
1572
1611
  persona;
1573
1612
  /** Point Playwright, Puppeteer or browser-use here. */
1574
1613
  cdpUrl;
1614
+ /** True when `start()` handed over a browser that was already running, such as the desktop app. */
1615
+ reused;
1575
1616
  /** Runs one browser command; a command that ran and failed throws. */
1576
1617
  async command(action, params = {}, timeoutMs) {
1577
1618
  const path = `/api/browsers/${this.id}/command`;
@@ -1723,7 +1764,7 @@ var Browser = class {
1723
1764
  /**
1724
1765
  * The SSE stream of JPEG frames, for embedding in your own UI. EventSource
1725
1766
  * cannot set headers, so the URL carries a connection ticket: single use,
1726
- * 60 seconds. Mint one per viewer — the first connection spends it.
1767
+ * 60 seconds. Mint one per viewer, the first connection spends it.
1727
1768
  */
1728
1769
  async liveStreamUrl() {
1729
1770
  const path = `/api/control/sessions/${encodeURIComponent(this.id)}/ticket`;
@@ -1759,13 +1800,14 @@ var Browser = class {
1759
1800
  * CDP session is handed back to its provider, a desktop browser disconnects.
1760
1801
  */
1761
1802
  stop() {
1803
+ if (this.reused) return Promise.resolve({ id: this.id, ok: true, reused: true });
1762
1804
  return this.http.request("POST", `/api/browsers/${this.id}/stop`, {}, STOP_TIMEOUT_MS);
1763
1805
  }
1764
1806
  /** `await using browser = await oya.browser.start()` stops it however the block exits, errors included. */
1765
1807
  async [Symbol.asyncDispose]() {
1766
1808
  await this.stop();
1767
1809
  }
1768
- /** @deprecated use stop() — close() only dropped the socket, and a cloud browser redialled. */
1810
+ /** @deprecated use stop(), close() only dropped the socket, and a cloud browser redialled. */
1769
1811
  async close() {
1770
1812
  await this.stop();
1771
1813
  }
@@ -1922,11 +1964,11 @@ var identityCalls = (http) => ({
1922
1964
  /** One persona. */
1923
1965
  get: (id) => http().request("GET", `/api/personas/${id}`),
1924
1966
  /**
1925
- * Create an identity. The device — platform, timezone, locale — is chosen
1967
+ * Create an identity. The device, platform, timezone, locale, is chosen
1926
1968
  * here and fixed for its life; `preview()` shows what a choice produces.
1927
1969
  */
1928
1970
  create: (options = {}) => http().request("POST", "/api/personas", options),
1929
- /** Name, concurrency cap and proxy hint. Never the device — clone for that. */
1971
+ /** Name, concurrency cap and proxy hint. Never the device, clone for that. */
1930
1972
  update: (id, changes) => http().request("PUT", `/api/personas/${id}`, changes),
1931
1973
  /** A new persona of the same kind of device: same choices, fresh identity, empty jar. */
1932
1974
  clone: (id, options = {}) => http().request("POST", `/api/personas/${id}/clone`, options)
@@ -2083,7 +2125,7 @@ async function targetBrowser(oya, flags2) {
2083
2125
 
2084
2126
  // src/commands/init.ts
2085
2127
  var PRESETS = {
2086
- anthropic: { name: "Anthropic", model: "claude-sonnet-4-5" },
2128
+ anthropic: { name: "Anthropic", model: "claude-sonnet-5" },
2087
2129
  openai: { name: "OpenAI", model: "gpt-4o-mini" },
2088
2130
  gemini: { name: "Gemini", model: "gemini-3.8-flash" },
2089
2131
  vertex: { name: "Gemini Enterprise", model: "gemini-2.5-flash" }
@@ -2137,7 +2179,7 @@ function signInHint(provider) {
2137
2179
  if (provider !== "oya-cloud" && provider !== "oya-selfhosted") return;
2138
2180
  console.log("\n\u2500\u2500 4. Sign in once, on your own machine \u2500\u2500");
2139
2181
  console.log(" Your remote browsers reuse the cookies from a desktop sign-in, so agents");
2140
- console.log(" arrive already logged in \u2014 as the same identity, from the same fingerprint.");
2182
+ console.log(" arrive already logged in, as the same identity, from the same fingerprint.");
2141
2183
  console.log(` Download the desktop browser: ${resolved().baseUrl}/downloads`);
2142
2184
  }
2143
2185
  async function interview2(oya) {
@@ -2219,7 +2261,7 @@ ${all.length} running.`);
2219
2261
  }
2220
2262
  function rmLine(x) {
2221
2263
  const removed = x.sandboxRemoved === true ? " (sandbox destroyed)" : "";
2222
- const note2 = x.sandboxRemoved === false ? " \u2014 sandbox NOT removed, check Oya Cloud" : removed;
2264
+ const note2 = x.sandboxRemoved === false ? ", sandbox NOT removed, check Oya Cloud" : removed;
2223
2265
  return `${x.ok ? "\u2705" : "\u2717"} ${x.id}${note2}${x.error ? ` ${x.error}` : ""}`;
2224
2266
  }
2225
2267
  async function cmdRm(args2, flags2) {
@@ -2230,7 +2272,7 @@ async function cmdRm(args2, flags2) {
2230
2272
  console.log(`stopped ${r.stopped}`);
2231
2273
  }
2232
2274
  function activityLine(a) {
2233
- const error = a.error ? ` \u2014 ${a.error}` : "";
2275
+ const error = a.error ? `, ${a.error}` : "";
2234
2276
  return ` ${a.ok ? "\xB7" : "\u2717"} ${a.action.padEnd(StatusColumns.ACTION)} ${a.summary}${error} ${a.ms}ms`;
2235
2277
  }
2236
2278
  function printStatus(s) {
@@ -2335,7 +2377,7 @@ function personaLine(p) {
2335
2377
  var list = async (oya, _rest, flags2) => {
2336
2378
  const all = await oya.personas.list();
2337
2379
  out(flags2, all, () => {
2338
- if (!all.length) return console.log("No personas yet \u2014 `oya personas new`.");
2380
+ if (!all.length) return console.log("No personas yet, `oya personas new`.");
2339
2381
  for (const p of all) console.log(personaLine(p));
2340
2382
  });
2341
2383
  };
@@ -2376,7 +2418,8 @@ async function cmdWhoami() {
2376
2418
 
2377
2419
  // src/commands/stealth.ts
2378
2420
  import { spawn as spawn4 } from "child_process";
2379
- var spawnHarness = (flags2) => spawn4("node", ["test-stealth.js", ...flags2.live ? ["--live"] : []], {
2421
+ var HARNESS = "tests/integration/stealth.test.js";
2422
+ var spawnHarness = (flags2) => spawn4("node", [HARNESS, ...flags2.live ? ["--live"] : []], {
2380
2423
  cwd: process.env.OYA_SERVER_DIR || "server",
2381
2424
  stdio: "inherit"
2382
2425
  });
@@ -2453,7 +2496,7 @@ async function runControl(command2, args2, flags2) {
2453
2496
  }
2454
2497
 
2455
2498
  // src/help.ts
2456
- var HELP = `oya \u2014 thousands of browsers, one API
2499
+ var HELP = `oya, thousands of browsers, one API
2457
2500
 
2458
2501
  oya install Stand up a self-hosted control plane
2459
2502
  [--dry-run] [--config oya-install.json]
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@oya-ai/cli",
3
- "version": "1.0.103",
3
+ "version": "1.0.105",
4
4
  "description": "Command line for the Oya browser control plane.",
5
5
  "license": "MIT",
6
- "homepage": "https://browser.getoya.ai",
6
+ "homepage": "https://oyabrowser.com",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/OyadotAI/oya-browser.git",
@@ -39,7 +39,7 @@
39
39
  "access": "public"
40
40
  },
41
41
  "devDependencies": {
42
- "@oya-ai/browser": "1.0.103",
42
+ "@oya-ai/browser": "1.0.105",
43
43
  "@types/node": "^20.19.43",
44
44
  "tsup": "^8.0.0",
45
45
  "typescript": "^5.4.0"