@xbrowser/cli 1.23.0 → 1.24.0

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.
@@ -251,6 +251,7 @@ var CaptchaDetector = class {
251
251
  };
252
252
 
253
253
  // src/webhook.ts
254
+ import * as os from "os";
254
255
  var WebhookNotifier = class {
255
256
  url;
256
257
  constructor(url) {
@@ -264,11 +265,15 @@ var WebhookNotifier = class {
264
265
  */
265
266
  async notify(payload) {
266
267
  if (!this.url) return false;
268
+ if (this.isDrelEndpoint(this.url)) {
269
+ payload = { ...payload, previewUrl: this.lanify(payload.previewUrl) };
270
+ }
267
271
  try {
272
+ const body = this.isDrelEndpoint(this.url) ? JSON.stringify(this.toDrelPayload(payload)) : JSON.stringify(payload);
268
273
  const response = await fetch(this.url, {
269
274
  method: "POST",
270
275
  headers: { "Content-Type": "application/json" },
271
- body: JSON.stringify(payload),
276
+ body,
272
277
  signal: AbortSignal.timeout(5e3)
273
278
  });
274
279
  return response.ok;
@@ -276,6 +281,39 @@ var WebhookNotifier = class {
276
281
  return false;
277
282
  }
278
283
  }
284
+ /**
285
+ * previewUrl 里的 localhost/127.0.0.1 替换为本机局域网 IPv4——
286
+ * 推送目标是手机,localhost 在手机上不可达;同一 Wi-Fi 下局域网 IP 可达。
287
+ */
288
+ lanify(url) {
289
+ if (!url) return url;
290
+ if (!url.includes("localhost") && !url.includes("127.0.0.1")) return url;
291
+ const ips = Object.values(os.networkInterfaces()).flat().filter((i) => !!i && i.family === "IPv4" && !i.internal);
292
+ if (!ips.length) return url;
293
+ return url.replace("localhost", ips[0].address).replace("127.0.0.1", ips[0].address);
294
+ }
295
+ /** Drel 推送端点识别(api.drel.app/<token>) */
296
+ isDrelEndpoint(url) {
297
+ return url.includes("api.drel.app") || url.includes("drel.app");
298
+ }
299
+ toDrelPayload(payload) {
300
+ const eventLabel = {
301
+ "captcha-detected": "\u26A0\uFE0F \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801",
302
+ "captcha-resolved": "\u2705 \u9A8C\u8BC1\u7801\u5DF2\u89E3\u51B3",
303
+ "session-started": "\u25B6\uFE0F \u4F1A\u8BDD\u5F00\u59CB",
304
+ "session-ended": "\u23F9 \u4F1A\u8BDD\u7ED3\u675F"
305
+ };
306
+ const parts = [];
307
+ if (payload.reason) parts.push(payload.reason);
308
+ if (payload.targetUrl) parts.push(`\u76EE\u6807: ${payload.targetUrl}`);
309
+ if (payload.sessionId) parts.push(`\u4F1A\u8BDD: ${payload.sessionId}`);
310
+ if (payload.timeout) parts.push(`\u8D85\u65F6: ${payload.timeout}s`);
311
+ return {
312
+ title: payload.title || `${eventLabel[payload.event] || payload.event} \u2014 \u9700\u8981\u4F60\u4ECB\u5165`,
313
+ body: payload.body || parts.join("\n") || payload.event,
314
+ ...payload.previewUrl ? { url: payload.previewUrl } : {}
315
+ };
316
+ }
279
317
  };
280
318
 
281
319
  // src/human-interaction.ts
@@ -111,6 +111,7 @@ var CaptchaDetector = class {
111
111
  };
112
112
 
113
113
  // src/webhook.ts
114
+ import * as os from "os";
114
115
  var WebhookNotifier = class {
115
116
  url;
116
117
  constructor(url) {
@@ -124,11 +125,15 @@ var WebhookNotifier = class {
124
125
  */
125
126
  async notify(payload) {
126
127
  if (!this.url) return false;
128
+ if (this.isDrelEndpoint(this.url)) {
129
+ payload = { ...payload, previewUrl: this.lanify(payload.previewUrl) };
130
+ }
127
131
  try {
132
+ const body = this.isDrelEndpoint(this.url) ? JSON.stringify(this.toDrelPayload(payload)) : JSON.stringify(payload);
128
133
  const response = await fetch(this.url, {
129
134
  method: "POST",
130
135
  headers: { "Content-Type": "application/json" },
131
- body: JSON.stringify(payload),
136
+ body,
132
137
  signal: AbortSignal.timeout(5e3)
133
138
  });
134
139
  return response.ok;
@@ -136,6 +141,39 @@ var WebhookNotifier = class {
136
141
  return false;
137
142
  }
138
143
  }
144
+ /**
145
+ * previewUrl 里的 localhost/127.0.0.1 替换为本机局域网 IPv4——
146
+ * 推送目标是手机,localhost 在手机上不可达;同一 Wi-Fi 下局域网 IP 可达。
147
+ */
148
+ lanify(url) {
149
+ if (!url) return url;
150
+ if (!url.includes("localhost") && !url.includes("127.0.0.1")) return url;
151
+ const ips = Object.values(os.networkInterfaces()).flat().filter((i) => !!i && i.family === "IPv4" && !i.internal);
152
+ if (!ips.length) return url;
153
+ return url.replace("localhost", ips[0].address).replace("127.0.0.1", ips[0].address);
154
+ }
155
+ /** Drel 推送端点识别(api.drel.app/<token>) */
156
+ isDrelEndpoint(url) {
157
+ return url.includes("api.drel.app") || url.includes("drel.app");
158
+ }
159
+ toDrelPayload(payload) {
160
+ const eventLabel = {
161
+ "captcha-detected": "\u26A0\uFE0F \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801",
162
+ "captcha-resolved": "\u2705 \u9A8C\u8BC1\u7801\u5DF2\u89E3\u51B3",
163
+ "session-started": "\u25B6\uFE0F \u4F1A\u8BDD\u5F00\u59CB",
164
+ "session-ended": "\u23F9 \u4F1A\u8BDD\u7ED3\u675F"
165
+ };
166
+ const parts = [];
167
+ if (payload.reason) parts.push(payload.reason);
168
+ if (payload.targetUrl) parts.push(`\u76EE\u6807: ${payload.targetUrl}`);
169
+ if (payload.sessionId) parts.push(`\u4F1A\u8BDD: ${payload.sessionId}`);
170
+ if (payload.timeout) parts.push(`\u8D85\u65F6: ${payload.timeout}s`);
171
+ return {
172
+ title: payload.title || `${eventLabel[payload.event] || payload.event} \u2014 \u9700\u8981\u4F60\u4ECB\u5165`,
173
+ body: payload.body || parts.join("\n") || payload.event,
174
+ ...payload.previewUrl ? { url: payload.previewUrl } : {}
175
+ };
176
+ }
139
177
  };
140
178
 
141
179
  // src/human-interaction.ts
@@ -1,3 +1,7 @@
1
+ import {
2
+ getCaptchaConfig
3
+ } from "./chunk-ZTHE5RBZ.js";
4
+
1
5
  // src/human-interaction.ts
2
6
  import { execFile } from "child_process";
3
7
 
@@ -247,6 +251,7 @@ var CaptchaDetector = class {
247
251
  };
248
252
 
249
253
  // src/webhook.ts
254
+ import * as os from "os";
250
255
  var WebhookNotifier = class {
251
256
  url;
252
257
  constructor(url) {
@@ -260,11 +265,15 @@ var WebhookNotifier = class {
260
265
  */
261
266
  async notify(payload) {
262
267
  if (!this.url) return false;
268
+ if (this.isDrelEndpoint(this.url)) {
269
+ payload = { ...payload, previewUrl: this.lanify(payload.previewUrl) };
270
+ }
263
271
  try {
272
+ const body = this.isDrelEndpoint(this.url) ? JSON.stringify(this.toDrelPayload(payload)) : JSON.stringify(payload);
264
273
  const response = await fetch(this.url, {
265
274
  method: "POST",
266
275
  headers: { "Content-Type": "application/json" },
267
- body: JSON.stringify(payload),
276
+ body,
268
277
  signal: AbortSignal.timeout(5e3)
269
278
  });
270
279
  return response.ok;
@@ -272,95 +281,40 @@ var WebhookNotifier = class {
272
281
  return false;
273
282
  }
274
283
  }
275
- };
276
-
277
- // src/config.ts
278
- import { homedir, tmpdir } from "os";
279
- import { join } from "path";
280
- import { loadConfig as coreLoadConfig, saveConfig as coreSaveConfig } from "@dyyz1993/xcli-core";
281
- function getConfigSource() {
282
- return { configDir: join(homedir() || tmpdir(), ".xbrowser") };
283
- }
284
- function loadConfig() {
285
- return coreLoadConfig(getConfigSource());
286
- }
287
- function saveConfig(config) {
288
- coreSaveConfig(getConfigSource(), config);
289
- }
290
- function getConfigValue(key) {
291
- const parts = key.split(".");
292
- let obj = loadConfig();
293
- for (const part of parts) {
294
- if (obj && typeof obj === "object") {
295
- obj = obj[part];
296
- } else {
297
- return void 0;
298
- }
284
+ /**
285
+ * previewUrl 里的 localhost/127.0.0.1 替换为本机局域网 IPv4——
286
+ * 推送目标是手机,localhost 在手机上不可达;同一 Wi-Fi 下局域网 IP 可达。
287
+ */
288
+ lanify(url) {
289
+ if (!url) return url;
290
+ if (!url.includes("localhost") && !url.includes("127.0.0.1")) return url;
291
+ const ips = Object.values(os.networkInterfaces()).flat().filter((i) => !!i && i.family === "IPv4" && !i.internal);
292
+ if (!ips.length) return url;
293
+ return url.replace("localhost", ips[0].address).replace("127.0.0.1", ips[0].address);
299
294
  }
300
- return obj;
301
- }
302
- function setConfigValue(key, value) {
303
- const config = loadConfig();
304
- const parts = key.split(".");
305
- let obj = config;
306
- for (let i = 0; i < parts.length - 1; i++) {
307
- if (!obj[parts[i]] || typeof obj[parts[i]] !== "object") {
308
- obj[parts[i]] = {};
309
- }
310
- obj = obj[parts[i]];
295
+ /** Drel 推送端点识别(api.drel.app/<token>) */
296
+ isDrelEndpoint(url) {
297
+ return url.includes("api.drel.app") || url.includes("drel.app");
311
298
  }
312
- obj[parts[parts.length - 1]] = value;
313
- saveConfig(config);
314
- }
315
- var DEFAULT_MARKETPLACE_URL = "https://marketplace.xbrowser.dev";
316
- var NPM_REGISTRY_URL = "https://registry.npmjs.org";
317
- var NPM_SCOPE = "@xbrowser/";
318
- function getMarketplaceUrl() {
319
- return process.env.XBROWSER_MARKETPLACE_URL || getConfigValue("marketplaceUrl") || DEFAULT_MARKETPLACE_URL;
320
- }
321
- function resolveNpmPackageName(name) {
322
- if (name.startsWith("@")) return name;
323
- return `${NPM_SCOPE}${name}`;
324
- }
325
- var NPM_NAME_ALIASES = {
326
- "1688": "alibaba-1688"
327
- };
328
- function generateNpmCandidates(name) {
329
- if (name.startsWith("@")) return [name];
330
- const resolved = NPM_NAME_ALIASES[name] ?? name;
331
- return [
332
- `${NPM_SCOPE}xbrowser-plugin-${resolved}`,
333
- `${NPM_SCOPE}${resolved}`,
334
- `xbrowser-plugin-${resolved}`
335
- ];
336
- }
337
- async function resolveNpmPackageWithFallback(name) {
338
- const candidates = generateNpmCandidates(name);
339
- for (const candidate of candidates) {
340
- try {
341
- const encoded = encodeURIComponent(candidate);
342
- const res = await fetch(`${NPM_REGISTRY_URL}/${encoded}`);
343
- if (res.ok) return candidate;
344
- } catch {
345
- continue;
346
- }
299
+ toDrelPayload(payload) {
300
+ const eventLabel = {
301
+ "captcha-detected": "\u26A0\uFE0F \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801",
302
+ "captcha-resolved": "\u2705 \u9A8C\u8BC1\u7801\u5DF2\u89E3\u51B3",
303
+ "session-started": "\u25B6\uFE0F \u4F1A\u8BDD\u5F00\u59CB",
304
+ "session-ended": "\u23F9 \u4F1A\u8BDD\u7ED3\u675F"
305
+ };
306
+ const parts = [];
307
+ if (payload.reason) parts.push(payload.reason);
308
+ if (payload.targetUrl) parts.push(`\u76EE\u6807: ${payload.targetUrl}`);
309
+ if (payload.sessionId) parts.push(`\u4F1A\u8BDD: ${payload.sessionId}`);
310
+ if (payload.timeout) parts.push(`\u8D85\u65F6: ${payload.timeout}s`);
311
+ return {
312
+ title: payload.title || `${eventLabel[payload.event] || payload.event} \u2014 \u9700\u8981\u4F60\u4ECB\u5165`,
313
+ body: payload.body || parts.join("\n") || payload.event,
314
+ ...payload.previewUrl ? { url: payload.previewUrl } : {}
315
+ };
347
316
  }
348
- return resolveNpmPackageName(name);
349
- }
350
- function getCaptchaConfig() {
351
- const config = loadConfig();
352
- return {
353
- notifyUrl: process.env.XBROWSER_NOTIFY_URL || config.captcha?.notifyUrl,
354
- autoOpen: process.env.XBROWSER_AUTO_OPEN === "true" || config.captcha?.autoOpen === true,
355
- timeout: parseInt(process.env.XBROWSER_CAPTCHA_TIMEOUT || "") || config.captcha?.timeout || 120,
356
- previewPort: parseInt(process.env.XBROWSER_PREVIEW_PORT || "") || config.preview?.port || 9223
357
- };
358
- }
359
- function resolveScreenshotsDir() {
360
- const configured = getConfigValue("screenshots.dir");
361
- if (typeof configured === "string" && configured.trim()) return configured.trim();
362
- return join(tmpdir(), "xbrowser", "screenshots");
363
- }
317
+ };
364
318
 
365
319
  // src/human-interaction.ts
366
320
  var HumanInteractionManager = class {
@@ -510,15 +464,6 @@ var HumanInteractionManager = class {
510
464
  };
511
465
 
512
466
  export {
513
- loadConfig,
514
- getConfigValue,
515
- setConfigValue,
516
- NPM_REGISTRY_URL,
517
- NPM_SCOPE,
518
- getMarketplaceUrl,
519
- resolveNpmPackageWithFallback,
520
- getCaptchaConfig,
521
- resolveScreenshotsDir,
522
467
  ScreencastCapturer,
523
468
  CaptchaDetector,
524
469
  WebhookNotifier,