@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.
- package/README.md +2 -2
- package/dist/index.js +83 -40
- 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%
|
|
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://
|
|
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-
|
|
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})
|
|
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})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}
|
|
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(`
|
|
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
|
|
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
|
|
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://
|
|
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
|
|
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
|
|
1431
|
-
return readAnswer2(
|
|
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
|
|
1461
|
-
|
|
1462
|
-
|
|
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
|
|
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
|
|
1561
|
-
this.
|
|
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
|
|
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()
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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 ? "
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
3
|
+
"version": "1.0.105",
|
|
4
4
|
"description": "Command line for the Oya browser control plane.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"homepage": "https://
|
|
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.
|
|
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"
|