nothing-browser 0.0.17 → 0.0.19

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/piggy.js CHANGED
@@ -6870,6 +6870,14 @@ var BINARY_NAMES = {
6870
6870
  headful: "nothing-browser-headful"
6871
6871
  };
6872
6872
  function detectBinary(mode = "headless") {
6873
+ if (mode !== "headless" && mode !== "headful") {
6874
+ if (existsSync(mode)) {
6875
+ logger_default.success(`Binary found (custom path): ${mode}`);
6876
+ return mode;
6877
+ }
6878
+ logger_default.error(`❌ Binary not found at custom path: ${mode}`);
6879
+ return null;
6880
+ }
6873
6881
  const cwd = process.cwd();
6874
6882
  const name = BINARY_NAMES[mode];
6875
6883
  if (process.platform === "win32") {
@@ -7029,66 +7037,179 @@ import { connect } from "net";
7029
7037
  import { writeFileSync, mkdirSync } from "fs";
7030
7038
  import { dirname } from "path";
7031
7039
  import { platform as platform2 } from "os";
7032
- var SOCKET_PATH = platform2() === "win32" ? "\\\\.\\pipe\\piggy" : "/tmp/piggy";
7040
+ var DEFAULT_SOCKET_PATH = platform2() === "win32" ? "\\\\.\\pipe\\piggy" : "/tmp/piggy";
7041
+
7042
+ class SocketTransport {
7043
+ sock;
7044
+ constructor(sock) {
7045
+ this.sock = sock;
7046
+ }
7047
+ send(data) {
7048
+ this.sock.write(data);
7049
+ }
7050
+ on(event, handler) {
7051
+ this.sock.on(event, handler);
7052
+ }
7053
+ destroy() {
7054
+ this.sock.destroy();
7055
+ }
7056
+ }
7057
+
7058
+ class HttpTransport {
7059
+ host;
7060
+ key;
7061
+ dataHandlers = [];
7062
+ errorHandlers = [];
7063
+ closeHandlers = [];
7064
+ _destroyed = false;
7065
+ constructor(host, key) {
7066
+ this.host = host.replace(/\/$/, "");
7067
+ this.key = key;
7068
+ }
7069
+ on(event, handler) {
7070
+ if (event === "data")
7071
+ this.dataHandlers.push(handler);
7072
+ if (event === "error")
7073
+ this.errorHandlers.push(handler);
7074
+ if (event === "close")
7075
+ this.closeHandlers.push(handler);
7076
+ }
7077
+ send(data) {
7078
+ if (this._destroyed)
7079
+ return;
7080
+ fetch(this.host, {
7081
+ method: "POST",
7082
+ headers: {
7083
+ "Content-Type": "application/json",
7084
+ "X-Piggy-Key": this.key
7085
+ },
7086
+ body: data
7087
+ }).then(async (res) => {
7088
+ if (!res.ok) {
7089
+ const text2 = await res.text().catch(() => `HTTP ${res.status}`);
7090
+ this.errorHandlers.forEach((h) => h(new Error(`HTTP ${res.status}: ${text2}`)));
7091
+ return;
7092
+ }
7093
+ const text = await res.text();
7094
+ const lines = text.split(`
7095
+ `).filter((l) => l.trim());
7096
+ for (const line of lines) {
7097
+ this.dataHandlers.forEach((h) => h(line + `
7098
+ `));
7099
+ }
7100
+ }).catch((e) => {
7101
+ if (!this._destroyed) {
7102
+ this.errorHandlers.forEach((h) => h(e));
7103
+ }
7104
+ });
7105
+ }
7106
+ destroy() {
7107
+ this._destroyed = true;
7108
+ this.closeHandlers.forEach((h) => h());
7109
+ }
7110
+ }
7033
7111
 
7034
7112
  class PiggyClient {
7035
7113
  socketPath;
7036
- socket = null;
7114
+ httpHost = null;
7115
+ httpKey = null;
7116
+ transport = null;
7037
7117
  reqId = 0;
7038
7118
  pending = new Map;
7039
7119
  buf = "";
7040
- eventBuffer = "";
7041
7120
  eventHandlers = new Map;
7042
7121
  globalEventHandlers = new Map;
7043
- constructor(socketPath = SOCKET_PATH) {
7044
- this.socketPath = socketPath;
7122
+ constructor(arg) {
7123
+ if (arg && typeof arg === "object") {
7124
+ this.socketPath = "";
7125
+ this.httpHost = arg.host.replace(/\/$/, "");
7126
+ this.httpKey = arg.key;
7127
+ } else {
7128
+ this.socketPath = arg ?? DEFAULT_SOCKET_PATH;
7129
+ }
7045
7130
  this.eventHandlers.set("default", new Map);
7046
7131
  }
7047
7132
  connect() {
7133
+ if (this.httpHost)
7134
+ return this._connectHttp();
7135
+ return this._connectSocket();
7136
+ }
7137
+ _connectSocket() {
7048
7138
  return new Promise((resolve, reject) => {
7049
7139
  logger_default.info(`Connecting to socket: ${this.socketPath}`);
7050
7140
  const sock = connect(this.socketPath);
7051
7141
  sock.setEncoding("utf8");
7052
7142
  sock.on("connect", () => {
7053
- this.socket = sock;
7054
- logger_default.success("Connected to Piggy server");
7143
+ this.transport = new SocketTransport(sock);
7144
+ this._wireTransport();
7145
+ logger_default.success("Connected to Piggy server (socket)");
7055
7146
  resolve();
7056
7147
  });
7057
- sock.on("data", (chunk) => {
7058
- this.eventBuffer += chunk;
7059
- const lines = this.eventBuffer.split(`
7060
- `);
7061
- this.eventBuffer = lines.pop();
7062
- for (const line of lines) {
7063
- if (!line.trim())
7064
- continue;
7065
- try {
7066
- const msg = JSON.parse(line);
7067
- if (msg.type === "event") {
7068
- this.handleEvent(msg);
7069
- continue;
7070
- }
7071
- const p = this.pending.get(msg.id);
7072
- if (p) {
7073
- this.pending.delete(msg.id);
7074
- msg.ok ? p.resolve(msg.data) : p.reject(new Error(msg.data ?? "command failed"));
7075
- }
7076
- } catch {
7077
- logger_default.error(`Bad JSON from server: ${line}`);
7078
- }
7079
- }
7080
- });
7081
7148
  sock.on("error", (e) => {
7082
7149
  for (const p of this.pending.values())
7083
7150
  p.reject(e);
7084
7151
  this.pending.clear();
7085
7152
  reject(e);
7086
7153
  });
7087
- sock.on("close", () => {
7088
- for (const p of this.pending.values())
7089
- p.reject(new Error("Socket closed"));
7090
- this.pending.clear();
7154
+ });
7155
+ }
7156
+ async _connectHttp() {
7157
+ logger_default.info(`Connecting to Piggy server (HTTP): ${this.httpHost}`);
7158
+ try {
7159
+ const res = await fetch(this.httpHost, {
7160
+ method: "POST",
7161
+ headers: {
7162
+ "Content-Type": "application/json",
7163
+ "X-Piggy-Key": this.httpKey
7164
+ },
7165
+ body: "hello"
7091
7166
  });
7167
+ if (res.status === 401) {
7168
+ throw new Error("Unauthorized — invalid X-Piggy-Key");
7169
+ }
7170
+ this.transport = new HttpTransport(this.httpHost, this.httpKey);
7171
+ this._wireTransport();
7172
+ logger_default.success(`Connected to Piggy server (HTTP): ${this.httpHost}`);
7173
+ } catch (e) {
7174
+ throw new Error(`Failed to connect to Piggy HTTP server: ${e.message}`);
7175
+ }
7176
+ }
7177
+ _wireTransport() {
7178
+ if (!this.transport)
7179
+ return;
7180
+ this.transport.on("data", (chunk) => {
7181
+ this.buf += chunk;
7182
+ const lines = this.buf.split(`
7183
+ `);
7184
+ this.buf = lines.pop();
7185
+ for (const line of lines) {
7186
+ if (!line.trim())
7187
+ continue;
7188
+ try {
7189
+ const msg = JSON.parse(line);
7190
+ if (msg.type === "event") {
7191
+ this.handleEvent(msg);
7192
+ continue;
7193
+ }
7194
+ const p = this.pending.get(msg.id);
7195
+ if (p) {
7196
+ this.pending.delete(msg.id);
7197
+ msg.ok ? p.resolve(msg.data) : p.reject(new Error(msg.data ?? "command failed"));
7198
+ }
7199
+ } catch {
7200
+ logger_default.error(`Bad JSON from server: ${line}`);
7201
+ }
7202
+ }
7203
+ });
7204
+ this.transport.on("error", (e) => {
7205
+ for (const p of this.pending.values())
7206
+ p.reject(e);
7207
+ this.pending.clear();
7208
+ });
7209
+ this.transport.on("close", () => {
7210
+ for (const p of this.pending.values())
7211
+ p.reject(new Error("Connection closed"));
7212
+ this.pending.clear();
7092
7213
  });
7093
7214
  }
7094
7215
  handleEvent(event) {
@@ -7152,7 +7273,6 @@ class PiggyClient {
7152
7273
  } catch {}
7153
7274
  }
7154
7275
  }
7155
- return;
7156
7276
  }
7157
7277
  }
7158
7278
  onEvent(eventName, tabId, handler) {
@@ -7164,16 +7284,16 @@ class PiggyClient {
7164
7284
  return () => this.globalEventHandlers.get(key)?.delete(handler);
7165
7285
  }
7166
7286
  disconnect() {
7167
- this.socket?.destroy();
7168
- this.socket = null;
7287
+ this.transport?.destroy();
7288
+ this.transport = null;
7169
7289
  }
7170
7290
  send(cmd, payload = {}) {
7171
7291
  return new Promise((resolve, reject) => {
7172
- if (!this.socket)
7292
+ if (!this.transport)
7173
7293
  return reject(new Error("Not connected"));
7174
7294
  const id = String(++this.reqId);
7175
7295
  this.pending.set(id, { resolve, reject });
7176
- this.socket.write(JSON.stringify({ id, cmd, payload }) + `
7296
+ this.transport.send(JSON.stringify({ id, cmd, payload }) + `
7177
7297
  `);
7178
7298
  });
7179
7299
  }
@@ -7343,6 +7463,30 @@ class PiggyClient {
7343
7463
  async sessionImport(data, tabId = "default") {
7344
7464
  await this.send("session.import", { data, tabId });
7345
7465
  }
7466
+ async sessionWsSave(enabled = true) {
7467
+ await this.send("session.ws.save", { enabled });
7468
+ }
7469
+ async sessionPingsSave(enabled = true) {
7470
+ await this.send("session.pings.save", { enabled });
7471
+ }
7472
+ async sessionPaths() {
7473
+ return this.send("session.paths", {});
7474
+ }
7475
+ async sessionCookiesPath() {
7476
+ return this.send("session.cookies.path", {});
7477
+ }
7478
+ async sessionProfilePath() {
7479
+ return this.send("session.profile.path", {});
7480
+ }
7481
+ async sessionWsPath() {
7482
+ return this.send("session.ws.path", {});
7483
+ }
7484
+ async sessionPingsPath() {
7485
+ return this.send("session.pings.path", {});
7486
+ }
7487
+ async sessionReload() {
7488
+ await this.send("session.reload", {});
7489
+ }
7346
7490
  async exposeFunction(name, handler, tabId = "default") {
7347
7491
  if (!this.eventHandlers.has(tabId))
7348
7492
  this.eventHandlers.set(tabId, new Map);
@@ -7369,6 +7513,54 @@ class PiggyClient {
7369
7513
  this.eventHandlers.set(tabId, new Map);
7370
7514
  logger_default.info(`[${tabId}] cleared all exposed functions`);
7371
7515
  }
7516
+ async proxyLoad(path) {
7517
+ await this.send("proxy.load", { path });
7518
+ }
7519
+ async proxyFetch(url) {
7520
+ await this.send("proxy.fetch", { url });
7521
+ }
7522
+ async proxyOvpn(path) {
7523
+ await this.send("proxy.ovpn", { path });
7524
+ }
7525
+ async proxySet(opts) {
7526
+ await this.send("proxy.set", opts);
7527
+ }
7528
+ async proxyTest() {
7529
+ await this.send("proxy.test", {});
7530
+ }
7531
+ async proxyTestStop() {
7532
+ await this.send("proxy.test.stop", {});
7533
+ }
7534
+ async proxyNext() {
7535
+ await this.send("proxy.next", {});
7536
+ }
7537
+ async proxyDisable() {
7538
+ await this.send("proxy.disable", {});
7539
+ }
7540
+ async proxyEnable() {
7541
+ await this.send("proxy.enable", {});
7542
+ }
7543
+ async proxyCurrent() {
7544
+ return this.send("proxy.current", {});
7545
+ }
7546
+ async proxyStats() {
7547
+ return this.send("proxy.stats", {});
7548
+ }
7549
+ async proxyList(limit) {
7550
+ return this.send("proxy.list", limit !== undefined ? { limit } : {});
7551
+ }
7552
+ async proxyRotation(mode, interval) {
7553
+ await this.send("proxy.rotation", { mode, ...interval !== undefined ? { interval } : {} });
7554
+ }
7555
+ async proxyConfig(opts) {
7556
+ await this.send("proxy.config", opts);
7557
+ }
7558
+ async proxySave(path, filter = "all") {
7559
+ await this.send("proxy.save", { path, filter });
7560
+ }
7561
+ onProxyEvent(event, handler) {
7562
+ return this.onEvent(event, "*", handler);
7563
+ }
7372
7564
  }
7373
7565
 
7374
7566
  // node_modules/memoirist/dist/index.mjs
@@ -21803,12 +21995,12 @@ var _Elysia = class _Elysia2 {
21803
21995
  }), this) : (typeof this.server?.reload == "function" && this.server.reload(this.server || {}), this._handle = composeGeneralHandler(this), this);
21804
21996
  }
21805
21997
  get fetch() {
21806
- const fetch = this.config.aot ? composeGeneralHandler(this) : createDynamicHandler(this);
21998
+ const fetch2 = this.config.aot ? composeGeneralHandler(this) : createDynamicHandler(this);
21807
21999
  return Object.defineProperty(this, "fetch", {
21808
- value: fetch,
22000
+ value: fetch2,
21809
22001
  configurable: true,
21810
22002
  writable: true
21811
- }), fetch;
22003
+ }), fetch2;
21812
22004
  }
21813
22005
  get modules() {
21814
22006
  return this.promisedModules;
@@ -28949,6 +29141,14 @@ var piggy = {
28949
29141
  logger_default.info(`[piggy] launched — tab mode: "${_tabMode}", binary: "${binaryMode}"`);
28950
29142
  return piggy;
28951
29143
  },
29144
+ connect: async (opts) => {
29145
+ _tabMode = "tab";
29146
+ _client = new PiggyClient({ host: opts.host, key: opts.key });
29147
+ await _client.connect();
29148
+ setClient(_client);
29149
+ logger_default.info(`[piggy] connected (HTTP) → ${opts.host}`);
29150
+ return piggy;
29151
+ },
28952
29152
  register: async (name, url2, opts) => {
28953
29153
  if (!url2?.trim())
28954
29154
  throw new Error(`No URL for site "${name}"`);
@@ -28956,12 +29156,11 @@ var piggy = {
28956
29156
  const poolSize = opts?.pool ?? 0;
28957
29157
  if (_tabMode === "tab") {
28958
29158
  if (!_client)
28959
- throw new Error("No client. Call piggy.launch() first.");
29159
+ throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
28960
29160
  if (poolSize > 1) {
28961
29161
  const pool = new TabPool(_client, poolSize, url2, name);
28962
29162
  await pool.init();
28963
- const firstTabId = "default";
28964
- const siteObj = createSiteObject(name, url2, _client, firstTabId, pool);
29163
+ const siteObj = createSiteObject(name, url2, _client, "default", pool);
28965
29164
  _sites[name] = siteObj;
28966
29165
  piggy[name] = siteObj;
28967
29166
  logger_default.success(`[${name}] registered with pool of ${poolSize} tabs`);
@@ -28997,18 +29196,100 @@ var piggy = {
28997
29196
  },
28998
29197
  expose: async (name, handler, tabId = "default") => {
28999
29198
  if (!_client)
29000
- throw new Error("No client. Call piggy.launch() first.");
29199
+ throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
29001
29200
  await _client.exposeFunction(name, handler, tabId);
29002
29201
  logger_default.success(`[piggy] exposed global function: ${name}`);
29003
29202
  return piggy;
29004
29203
  },
29005
29204
  unexpose: async (name, tabId = "default") => {
29006
29205
  if (!_client)
29007
- throw new Error("No client. Call piggy.launch() first.");
29206
+ throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
29008
29207
  await _client.unexposeFunction(name, tabId);
29009
29208
  logger_default.info(`[piggy] unexposed function: ${name}`);
29010
29209
  return piggy;
29011
29210
  },
29211
+ proxy: {
29212
+ load: (path) => {
29213
+ if (!_client)
29214
+ throw new Error("No client");
29215
+ return _client.proxyLoad(path);
29216
+ },
29217
+ fetch: (url2) => {
29218
+ if (!_client)
29219
+ throw new Error("No client");
29220
+ return _client.proxyFetch(url2);
29221
+ },
29222
+ ovpn: (path) => {
29223
+ if (!_client)
29224
+ throw new Error("No client");
29225
+ return _client.proxyOvpn(path);
29226
+ },
29227
+ set: (opts) => {
29228
+ if (!_client)
29229
+ throw new Error("No client");
29230
+ return _client.proxySet(opts);
29231
+ },
29232
+ test: () => {
29233
+ if (!_client)
29234
+ throw new Error("No client");
29235
+ return _client.proxyTest();
29236
+ },
29237
+ testStop: () => {
29238
+ if (!_client)
29239
+ throw new Error("No client");
29240
+ return _client.proxyTestStop();
29241
+ },
29242
+ next: () => {
29243
+ if (!_client)
29244
+ throw new Error("No client");
29245
+ return _client.proxyNext();
29246
+ },
29247
+ disable: () => {
29248
+ if (!_client)
29249
+ throw new Error("No client");
29250
+ return _client.proxyDisable();
29251
+ },
29252
+ enable: () => {
29253
+ if (!_client)
29254
+ throw new Error("No client");
29255
+ return _client.proxyEnable();
29256
+ },
29257
+ current: () => {
29258
+ if (!_client)
29259
+ throw new Error("No client");
29260
+ return _client.proxyCurrent();
29261
+ },
29262
+ stats: () => {
29263
+ if (!_client)
29264
+ throw new Error("No client");
29265
+ return _client.proxyStats();
29266
+ },
29267
+ list: (limit) => {
29268
+ if (!_client)
29269
+ throw new Error("No client");
29270
+ return _client.proxyList(limit);
29271
+ },
29272
+ rotation: (mode, interval) => {
29273
+ if (!_client)
29274
+ throw new Error("No client");
29275
+ return _client.proxyRotation(mode, interval);
29276
+ },
29277
+ config: (opts) => {
29278
+ if (!_client)
29279
+ throw new Error("No client");
29280
+ return _client.proxyConfig(opts);
29281
+ },
29282
+ save: (path, filter) => {
29283
+ if (!_client)
29284
+ throw new Error("No client");
29285
+ return _client.proxySave(path, filter);
29286
+ },
29287
+ on: (event, handler) => {
29288
+ if (!_client)
29289
+ throw new Error("No client");
29290
+ return _client.onProxyEvent(event, handler);
29291
+ }
29292
+ },
29012
29293
  serve: (port, opts) => startServer(port, opts?.hostname, opts),
29013
29294
  stopServer,
29014
29295
  routes: () => Array.from(routeRegistry.entries()).map(([key, cfg]) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothing-browser",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Browser automation library powered by Nothing Browser",
5
5
  "homepage": "https://github.com/ernest-tech-house-co-operation/nothing-browser#readme",
6
6
  "repository": {