@web-auto/webauto 0.1.6 → 0.1.8

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.
@@ -270,6 +270,7 @@ async function importConfigFromFile(filePath) {
270
270
  // src/main/core-daemon-manager.mts
271
271
  import { spawn } from "child_process";
272
272
  import path2 from "path";
273
+ import { existsSync as existsSync2 } from "fs";
273
274
  import { fileURLToPath } from "url";
274
275
  var REPO_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "../../../..");
275
276
  var CORE_HEALTH_URLS = ["http://127.0.0.1:7701/health", "http://127.0.0.1:7704/health"];
@@ -283,11 +284,33 @@ function resolveNodeBin() {
283
284
  if (explicit) return explicit;
284
285
  const npmNode = String(process.env.npm_node_execpath || "").trim();
285
286
  if (npmNode) return npmNode;
286
- return process.platform === "win32" ? "node.exe" : "node";
287
+ const fromPath = resolveOnPath(process.platform === "win32" ? ["node.exe", "node.cmd", "node"] : ["node"]);
288
+ if (fromPath) return fromPath;
289
+ return process.execPath;
287
290
  }
288
291
  function resolveNpxBin() {
292
+ const fromPath = resolveOnPath(
293
+ process.platform === "win32" ? ["npx.cmd", "npx.exe", "npx.bat", "npx.ps1"] : ["npx"]
294
+ );
295
+ if (fromPath) return fromPath;
289
296
  return process.platform === "win32" ? "npx.cmd" : "npx";
290
297
  }
298
+ function resolveOnPath(candidates) {
299
+ const pathEnv = process.env.PATH || process.env.Path || "";
300
+ const dirs = pathEnv.split(path2.delimiter).filter(Boolean);
301
+ for (const dir of dirs) {
302
+ for (const name of candidates) {
303
+ const full = path2.join(dir, name);
304
+ if (existsSync2(full)) return full;
305
+ }
306
+ }
307
+ return null;
308
+ }
309
+ function quoteCmdArg(value) {
310
+ if (!value) return '""';
311
+ if (!/[\s"]/u.test(value)) return value;
312
+ return `"${value.replace(/"/g, '""')}"`;
313
+ }
291
314
  async function checkHttpHealth(url) {
292
315
  try {
293
316
  const res = await fetch(url, { signal: AbortSignal.timeout(1e3) });
@@ -332,7 +355,18 @@ async function runNodeScript(scriptPath, timeoutMs) {
332
355
  }
333
356
  async function runCommand(command, args, timeoutMs) {
334
357
  return new Promise((resolve) => {
335
- const child = spawn(command, args, {
358
+ const lower = String(command || "").toLowerCase();
359
+ let spawnCommand2 = command;
360
+ let spawnArgs = args;
361
+ if (process.platform === "win32" && (lower.endsWith(".cmd") || lower.endsWith(".bat"))) {
362
+ spawnCommand2 = "cmd.exe";
363
+ const cmdLine = [quoteCmdArg(command), ...args.map(quoteCmdArg)].join(" ");
364
+ spawnArgs = ["/d", "/s", "/c", cmdLine];
365
+ } else if (process.platform === "win32" && lower.endsWith(".ps1")) {
366
+ spawnCommand2 = "powershell.exe";
367
+ spawnArgs = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", command, ...args];
368
+ }
369
+ const child = spawn(spawnCommand2, spawnArgs, {
336
370
  cwd: REPO_ROOT,
337
371
  stdio: "ignore",
338
372
  windowsHide: true,
@@ -365,7 +399,11 @@ async function startCoreDaemon() {
365
399
  console.error("[CoreDaemonManager] Failed to start unified API service");
366
400
  return false;
367
401
  }
368
- const startedBrowser = await runCommand(resolveNpxBin(), ["--yes", "@web-auto/camo", "init"], 4e4);
402
+ const startedBrowser = await runCommand(
403
+ resolveNpxBin(),
404
+ ["--yes", "--package=@web-auto/camo", "camo", "init"],
405
+ 4e4
406
+ );
369
407
  if (!startedBrowser) {
370
408
  console.error("[CoreDaemonManager] Failed to start camo browser backend");
371
409
  return false;
@@ -691,28 +729,26 @@ var StateBridge = class {
691
729
  var stateBridge = new StateBridge();
692
730
 
693
731
  // src/main/env-check.mts
694
- import { promisify } from "node:util";
695
- import { exec, spawnSync } from "node:child_process";
696
- import { existsSync as existsSync2 } from "node:fs";
732
+ import { spawnSync } from "node:child_process";
733
+ import { existsSync as existsSync3 } from "node:fs";
697
734
  import path4 from "node:path";
698
735
  import os3 from "node:os";
699
- var execAsync = promisify(exec);
700
736
  function resolveWebautoRoot() {
701
737
  const portableRoot = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || "").trim();
702
738
  return portableRoot ? path4.join(portableRoot, ".webauto") : path4.join(os3.homedir(), ".webauto");
703
739
  }
704
740
  function resolveNpxBin2() {
705
741
  if (process.platform !== "win32") return "npx";
706
- const resolved = resolveOnPath(["npx.cmd", "npx.exe", "npx.bat", "npx.ps1"]);
742
+ const resolved = resolveOnPath2(["npx.cmd", "npx.exe", "npx.bat", "npx.ps1"]);
707
743
  return resolved || "npx.cmd";
708
744
  }
709
- function resolveOnPath(candidates) {
745
+ function resolveOnPath2(candidates) {
710
746
  const pathEnv = process.env.PATH || process.env.Path || "";
711
747
  const dirs = pathEnv.split(path4.delimiter).filter(Boolean);
712
748
  for (const dir of dirs) {
713
749
  for (const name of candidates) {
714
750
  const full = path4.join(dir, name);
715
- if (existsSync2(full)) return full;
751
+ if (existsSync3(full)) return full;
716
752
  }
717
753
  }
718
754
  return null;
@@ -729,7 +765,7 @@ ${String(stderr || "")}`.trim();
729
765
  }
730
766
  return "unknown";
731
767
  }
732
- function quoteCmdArg(value) {
768
+ function quoteCmdArg2(value) {
733
769
  if (!value) return '""';
734
770
  if (!/[\s"]/u.test(value)) return value;
735
771
  return `"${value.replace(/"/g, '""')}"`;
@@ -739,7 +775,7 @@ function runVersionCheck(command, args, explicitPath) {
739
775
  const lower = String(command || "").toLowerCase();
740
776
  let ret;
741
777
  if (process.platform === "win32" && (lower.endsWith(".cmd") || lower.endsWith(".bat"))) {
742
- const cmdLine = [quoteCmdArg(command), ...args.map(quoteCmdArg)].join(" ");
778
+ const cmdLine = [quoteCmdArg2(command), ...args.map(quoteCmdArg2)].join(" ");
743
779
  ret = spawnSync("cmd.exe", ["/d", "/s", "/c", cmdLine], {
744
780
  encoding: "utf8",
745
781
  timeout: 8e3,
@@ -773,6 +809,14 @@ function runVersionCheck(command, args, explicitPath) {
773
809
  return { installed: false, error: String(err) };
774
810
  }
775
811
  }
812
+ function resolvePathFromOutput(stdout) {
813
+ const lines = String(stdout || "").split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
814
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
815
+ const line = lines[i];
816
+ if (line.startsWith("/") || /^[A-Z]:\\/i.test(line)) return line;
817
+ }
818
+ return "";
819
+ }
776
820
  async function checkCamoCli() {
777
821
  const camoCandidates = process.platform === "win32" ? ["camo.cmd", "camo.exe", "camo.bat", "camo.ps1"] : ["camo"];
778
822
  for (const candidate of camoCandidates) {
@@ -788,7 +832,7 @@ async function checkCamoCli() {
788
832
  for (const localRoot of localRoots) {
789
833
  for (const suffix of camoCandidates) {
790
834
  const candidate = path4.resolve(localRoot, suffix);
791
- if (!existsSync2(candidate)) continue;
835
+ if (!existsSync3(candidate)) continue;
792
836
  const ret = runVersionCheck(candidate, ["help"], candidate);
793
837
  if (ret.installed) return ret;
794
838
  }
@@ -813,51 +857,32 @@ async function checkServices() {
813
857
  return { unifiedApi, camoRuntime, searchGate };
814
858
  }
815
859
  async function checkFirefox() {
816
- try {
817
- const pythonBin = process.platform === "win32" ? "python" : "python3";
818
- const ret = spawnSync(pythonBin, ["-m", "camoufox", "path"], {
819
- encoding: "utf8",
820
- timeout: 8e3,
821
- windowsHide: true
822
- });
823
- if (ret.status === 0) {
824
- const lines = String(ret.stdout || "").split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
825
- for (let i = lines.length - 1; i >= 0; i -= 1) {
826
- const line = lines[i];
827
- if (line && (line.startsWith("/") || /^[A-Z]:\\/.test(line))) return { installed: true, path: line };
828
- }
829
- return { installed: true };
830
- }
831
- } catch {
832
- }
833
- const platform = process.platform;
834
- try {
835
- if (platform === "win32") {
836
- const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
837
- const programFilesX86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
838
- const localAppData = process.env.LOCALAPPDATA || path4.join(os3.homedir(), "AppData", "Local");
839
- const possiblePaths = [
840
- path4.join(programFiles, "Mozilla Firefox", "firefox.exe"),
841
- path4.join(programFilesX86, "Mozilla Firefox", "firefox.exe"),
842
- path4.join(localAppData, "Mozilla Firefox", "firefox.exe")
843
- ];
844
- for (const firefoxPath2 of possiblePaths) {
845
- if (existsSync2(firefoxPath2)) return { installed: true, path: firefoxPath2 };
846
- }
847
- return { installed: false };
860
+ const candidates = process.platform === "win32" ? [
861
+ { command: "python", args: ["-m", "camoufox", "path"] },
862
+ { command: "py", args: ["-3", "-m", "camoufox", "path"] },
863
+ { command: resolveNpxBin2(), args: ["--yes", "--package=camoufox", "camoufox", "path"] }
864
+ ] : [
865
+ { command: "python3", args: ["-m", "camoufox", "path"] },
866
+ { command: resolveNpxBin2(), args: ["--yes", "--package=camoufox", "camoufox", "path"] }
867
+ ];
868
+ for (const candidate of candidates) {
869
+ try {
870
+ const ret = spawnSync(candidate.command, candidate.args, {
871
+ encoding: "utf8",
872
+ timeout: 8e3,
873
+ windowsHide: true
874
+ });
875
+ if (ret.status !== 0) continue;
876
+ const resolvedPath = resolvePathFromOutput(String(ret.stdout || ""));
877
+ return resolvedPath ? { installed: true, path: resolvedPath } : { installed: true };
878
+ } catch {
848
879
  }
849
- const macBundle = "/Applications/Firefox.app/Contents/MacOS/firefox";
850
- if (platform === "darwin" && existsSync2(macBundle)) return { installed: true, path: macBundle };
851
- const { stdout } = await execAsync("which firefox", { timeout: 3e3 });
852
- const firefoxPath = String(stdout || "").trim();
853
- return firefoxPath ? { installed: true, path: firefoxPath } : { installed: false };
854
- } catch {
855
- return { installed: false };
856
880
  }
881
+ return { installed: false };
857
882
  }
858
883
  async function checkGeoIP() {
859
884
  const geoIpPath = path4.join(resolveWebautoRoot(), "geoip", "GeoLite2-City.mmdb");
860
- if (existsSync2(geoIpPath)) {
885
+ if (existsSync3(geoIpPath)) {
861
886
  return { installed: true, path: geoIpPath };
862
887
  }
863
888
  return { installed: false };
@@ -2258,6 +2283,7 @@ ipcMain2.handle("env:repairDeps", async (_evt, input) => {
2258
2283
  const wantGeoip = Boolean(input?.geoip);
2259
2284
  const wantReinstall = Boolean(input?.reinstall);
2260
2285
  const wantUninstall = Boolean(input?.uninstall);
2286
+ const wantEnsureBackend = Boolean(input?.ensureBackend);
2261
2287
  const result = { ok: true, core: null, install: null, env: null };
2262
2288
  if (wantCore) {
2263
2289
  const coreOk = await startCoreDaemon().catch(() => false);
@@ -2274,7 +2300,7 @@ ipcMain2.handle("env:repairDeps", async (_evt, input) => {
2274
2300
  else args.push("--install");
2275
2301
  if (wantBrowser) args.push("--download-browser");
2276
2302
  if (wantGeoip) args.push("--download-geoip");
2277
- if (!wantUninstall) args.push("--ensure-backend");
2303
+ if (!wantUninstall && wantEnsureBackend) args.push("--ensure-backend");
2278
2304
  const installRes = await runJson({
2279
2305
  title: "env repair deps",
2280
2306
  cwd: REPO_ROOT2,
@@ -1553,7 +1553,7 @@ function renderSetupWizard(root, ctx2) {
1553
1553
  <div class="env-item" id="env-camo" style="display:flex; align-items:center; justify-content:space-between; gap:8px;">
1554
1554
  <span style="display:flex; align-items:center; gap:8px; min-width:0;">
1555
1555
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
1556
- <span class="env-label">Camo CLI</span>
1556
+ <span class="env-label">Camo CLI (@web-auto/camo)</span>
1557
1557
  </span>
1558
1558
  <button id="repair-camo-btn" class="secondary" style="display:none; flex:0 0 auto;">\u4E00\u952E\u4FEE\u590D</button>
1559
1559
  </div>
@@ -1567,14 +1567,14 @@ function renderSetupWizard(root, ctx2) {
1567
1567
  <div class="env-item" id="env-browser" style="display:flex; align-items:center; justify-content:space-between; gap:8px;">
1568
1568
  <span style="display:flex; align-items:center; gap:8px; min-width:0;">
1569
1569
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
1570
- <span class="env-label">Camo Runtime\uFF08\u53EF\u9009\uFF09</span>
1570
+ <span class="env-label">Camo Runtime Service (7704\uFF0C\u53EF\u9009)</span>
1571
1571
  </span>
1572
1572
  <button id="repair-core2-btn" class="secondary" style="display:none; flex:0 0 auto;">\u4E00\u952E\u4FEE\u590D</button>
1573
1573
  </div>
1574
1574
  <div class="env-item" id="env-firefox" style="display:flex; align-items:center; justify-content:space-between; gap:8px;">
1575
1575
  <span style="display:flex; align-items:center; gap:8px; min-width:0;">
1576
1576
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
1577
- <span class="env-label">Camoufox Browser</span>
1577
+ <span class="env-label">Camoufox Runtime (python -m camoufox)</span>
1578
1578
  </span>
1579
1579
  <button id="repair-runtime-btn" class="secondary" style="display:none; flex:0 0 auto;">\u4E00\u952E\u4FEE\u590D</button>
1580
1580
  </div>
@@ -1706,7 +1706,7 @@ function renderSetupWizard(root, ctx2) {
1706
1706
  setupStatusText.textContent = "\u6B63\u5728\u62C9\u8D77\u6838\u5FC3\u670D\u52A1...";
1707
1707
  const res = await ctx2.api.envRepairDeps({ core: true }).catch((err) => ({ ok: false, error: err?.message || String(err) }));
1708
1708
  const ok = res?.ok !== false;
1709
- const detail = res?.error || (ok ? "" : "\u6838\u5FC3\u670D\u52A1\u542F\u52A8\u5931\u8D25");
1709
+ const detail = res?.error || res?.core?.error || res?.core?.services?.error || (ok ? "" : "\u6838\u5FC3\u670D\u52A1\u542F\u52A8\u5931\u8D25");
1710
1710
  return { ok, detail };
1711
1711
  }
1712
1712
  if (typeof ctx2.api?.envRepairCore === "function") {
@@ -1720,7 +1720,7 @@ function renderSetupWizard(root, ctx2) {
1720
1720
  }
1721
1721
  async function repairInstall({ browser, geoip, reinstall, uninstall }) {
1722
1722
  if (typeof ctx2.api?.envRepairDeps === "function") {
1723
- setupStatusText.textContent = reinstall ? "\u6B63\u5728\u5378\u8F7D\u5E76\u91CD\u88C5\u8D44\u6E90\uFF08Camoufox/GeoIP\uFF09..." : geoip && browser ? "\u6B63\u5728\u5B89\u88C5\u4F9D\u8D56\uFF08Camoufox/GeoIP\uFF09..." : geoip ? "\u6B63\u5728\u5B89\u88C5 GeoIP\uFF08\u53EF\u9009\uFF09..." : "\u6B63\u5728\u5B89\u88C5 Camoufox...";
1723
+ setupStatusText.textContent = reinstall ? "\u6B63\u5728\u5378\u8F7D\u5E76\u91CD\u88C5\u8D44\u6E90\uFF08Camoufox Runtime/GeoIP\uFF09..." : geoip && browser ? "\u6B63\u5728\u5B89\u88C5\u4F9D\u8D56\uFF08Camoufox Runtime/GeoIP\uFF09..." : geoip ? "\u6B63\u5728\u5B89\u88C5 GeoIP\uFF08\u53EF\u9009\uFF09..." : "\u6B63\u5728\u5B89\u88C5 Camoufox Runtime...";
1724
1724
  const res = await ctx2.api.envRepairDeps({
1725
1725
  browser: Boolean(browser),
1726
1726
  geoip: Boolean(geoip),
@@ -1728,18 +1728,17 @@ function renderSetupWizard(root, ctx2) {
1728
1728
  uninstall: Boolean(uninstall)
1729
1729
  }).catch((err) => ({ ok: false, error: err?.message || String(err) }));
1730
1730
  const ok = res?.ok !== false;
1731
- const detail = res?.error || (ok ? "" : "\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25");
1731
+ const detail = res?.error || res?.install?.error || res?.install?.stderr || res?.install?.stdout || res?.install?.json?.error || (ok ? "" : "\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25");
1732
1732
  return { ok, detail };
1733
1733
  }
1734
1734
  if (typeof ctx2.api?.cmdRunJson === "function") {
1735
- setupStatusText.textContent = reinstall ? "\u6B63\u5728\u5378\u8F7D\u5E76\u91CD\u88C5\u8D44\u6E90\uFF08Camoufox/GeoIP\uFF09..." : geoip && browser ? "\u6B63\u5728\u5B89\u88C5\u4F9D\u8D56\uFF08Camoufox/GeoIP\uFF09..." : geoip ? "\u6B63\u5728\u5B89\u88C5 GeoIP\uFF08\u53EF\u9009\uFF09..." : "\u6B63\u5728\u5B89\u88C5 Camoufox...";
1735
+ setupStatusText.textContent = reinstall ? "\u6B63\u5728\u5378\u8F7D\u5E76\u91CD\u88C5\u8D44\u6E90\uFF08Camoufox Runtime/GeoIP\uFF09..." : geoip && browser ? "\u6B63\u5728\u5B89\u88C5\u4F9D\u8D56\uFF08Camoufox Runtime/GeoIP\uFF09..." : geoip ? "\u6B63\u5728\u5B89\u88C5 GeoIP\uFF08\u53EF\u9009\uFF09..." : "\u6B63\u5728\u5B89\u88C5 Camoufox Runtime...";
1736
1736
  const script = ctx2.api.pathJoin("apps", "webauto", "entry", "xhs-install.mjs");
1737
1737
  const args = [script];
1738
1738
  if (reinstall) args.push("--reinstall");
1739
1739
  else if (uninstall) args.push("--uninstall");
1740
1740
  if (browser) args.push("--download-browser");
1741
1741
  if (geoip) args.push("--download-geoip");
1742
- if (!uninstall) args.push("--ensure-backend");
1743
1742
  const res = await ctx2.api.cmdRunJson({
1744
1743
  title: "setup auto repair",
1745
1744
  cwd: "",
@@ -1761,7 +1760,12 @@ function renderSetupWizard(root, ctx2) {
1761
1760
  if (!res.ok) ok = false;
1762
1761
  if (res.detail) detail = res.detail;
1763
1762
  }
1764
- if (missing.camo || missing.runtime) {
1763
+ if (missing.camo && !missing.core) {
1764
+ const res = await repairCoreServices();
1765
+ if (!res.ok) ok = false;
1766
+ if (res.detail) detail = res.detail;
1767
+ }
1768
+ if (missing.runtime) {
1765
1769
  const res = await repairInstall({ browser: true });
1766
1770
  if (!res.ok) ok = false;
1767
1771
  if (res.detail) detail = res.detail;
@@ -1837,7 +1841,7 @@ function renderSetupWizard(root, ctx2) {
1837
1841
  updateCompleteStatus();
1838
1842
  if (!envReady) {
1839
1843
  const missing = [];
1840
- if (!snapshot?.camo?.installed) missing.push("camo");
1844
+ if (!snapshot?.camo?.installed) missing.push("camo-cli");
1841
1845
  if (!snapshot?.services?.unifiedApi) missing.push("unified-api");
1842
1846
  if (!snapshot?.firefox?.installed) missing.push("camoufox-runtime");
1843
1847
  setupStatusText.textContent = `\u5B58\u5728\u5F85\u4FEE\u590D\u9879: ${missing.join(", ")}`;
@@ -2069,10 +2073,14 @@ function renderSetupWizard(root, ctx2) {
2069
2073
  const snapshot = await collectEnvironment();
2070
2074
  return await repairMissing(snapshot);
2071
2075
  });
2072
- envReinstallAllBtn.onclick = () => void runRepair("\u4E00\u952E\u5378\u8F7D\u91CD\u88C5\u8D44\u6E90", () => repairInstall({ browser: true, geoip: true, reinstall: true }));
2076
+ envReinstallAllBtn.onclick = () => void runRepair("\u4E00\u952E\u5378\u8F7D\u91CD\u88C5\u8D44\u6E90", () => (async () => {
2077
+ const core = await repairCoreServices();
2078
+ if (!core.ok) return core;
2079
+ return repairInstall({ browser: true, geoip: true, reinstall: true });
2080
+ })());
2073
2081
  repairCoreBtn.onclick = () => void runRepair("\u4FEE\u590D\u6838\u5FC3\u670D\u52A1", repairCoreServices);
2074
2082
  repairCore2Btn.onclick = () => void runRepair("\u4FEE\u590D\u6838\u5FC3\u670D\u52A1", repairCoreServices);
2075
- repairCamoBtn.onclick = () => void runRepair("\u4FEE\u590D Camoufox CLI/Runtime", () => repairInstall({ browser: true }));
2083
+ repairCamoBtn.onclick = () => void runRepair("\u4FEE\u590D Camo CLI", repairCoreServices);
2076
2084
  repairRuntimeBtn.onclick = () => void runRepair("\u4FEE\u590D Camoufox Runtime", () => repairInstall({ browser: true }));
2077
2085
  repairGeoipBtn.onclick = () => void runRepair("\u5B89\u88C5 GeoIP", () => repairInstall({ geoip: true }));
2078
2086
  addAccountBtn.onclick = addAccount;
@@ -3726,7 +3734,7 @@ function renderAccountManager(root, ctx2) {
3726
3734
  <div class="env-status-grid">
3727
3735
  <div class="env-item" id="env-camo">
3728
3736
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
3729
- <span>Camo CLI</span>
3737
+ <span>Camo CLI (@web-auto/camo)</span>
3730
3738
  </div>
3731
3739
  <div class="env-item" id="env-unified">
3732
3740
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
@@ -3734,11 +3742,11 @@ function renderAccountManager(root, ctx2) {
3734
3742
  </div>
3735
3743
  <div class="env-item" id="env-browser">
3736
3744
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
3737
- <span>Camo Runtime\uFF08\u53EF\u9009\uFF09</span>
3745
+ <span>Camo Runtime Service (7704\uFF0C\u53EF\u9009)</span>
3738
3746
  </div>
3739
3747
  <div class="env-item" id="env-firefox">
3740
3748
  <span class="icon" style="color: var(--text-4);">\u25CB</span>
3741
- <span>Camoufox Browser</span>
3749
+ <span>Camoufox Runtime (python -m camoufox)</span>
3742
3750
  </div>
3743
3751
  </div>
3744
3752
  <div class="btn-group" style="margin-top: var(--gap);">
@@ -57,6 +57,22 @@ function resolveNpxBin(platform = process.platform, pathEnv = process.env.PATH |
57
57
  return resolved || 'npx.cmd';
58
58
  }
59
59
 
60
+ function resolveNpmBin(platform = process.platform, pathEnv = process.env.PATH || process.env.Path || '') {
61
+ if (platform !== 'win32') return 'npm';
62
+ const resolved = resolveOnPath(
63
+ ['npm.cmd', 'npm.exe', 'npm.bat', 'npm.ps1'],
64
+ pathEnv,
65
+ ';',
66
+ );
67
+ return resolved || 'npm.cmd';
68
+ }
69
+
70
+ function runPackageCommand(packageName, commandArgs) {
71
+ const viaNpx = run(resolveNpxBin(), ['--yes', `--package=${packageName}`, ...commandArgs]);
72
+ if (viaNpx.status === 0) return viaNpx;
73
+ return run(resolveNpmBin(), ['exec', '--yes', `--package=${packageName}`, '--', ...commandArgs]);
74
+ }
75
+
60
76
  function resolveWebautoRoot() {
61
77
  const portableRoot = String(process.env.WEBAUTO_PORTABLE_ROOT || process.env.WEBAUTO_ROOT || '').trim();
62
78
  if (portableRoot) return path.join(portableRoot, '.webauto');
@@ -79,11 +95,13 @@ function checkCamoufoxInstalled() {
79
95
  const ret = run(candidate.cmd, candidate.args);
80
96
  if (ret.status === 0) return true;
81
97
  }
98
+ const npxRet = runPackageCommand('camoufox', ['camoufox', 'path']);
99
+ if (npxRet.status === 0) return true;
82
100
  return false;
83
101
  }
84
102
 
85
103
  function installCamoufox() {
86
- const ret = run(resolveNpxBin(), ['--yes', '--package=camoufox', 'camoufox', 'fetch']);
104
+ const ret = runPackageCommand('camoufox', ['camoufox', 'fetch']);
87
105
  return ret.status === 0;
88
106
  }
89
107
 
@@ -92,12 +110,12 @@ function checkGeoIPInstalled() {
92
110
  }
93
111
 
94
112
  function installGeoIP() {
95
- const ret = run(resolveNpxBin(), ['--yes', '--package=@web-auto/camo', 'camo', 'init', 'geoip']);
113
+ const ret = runPackageCommand('@web-auto/camo', ['camo', 'init', 'geoip']);
96
114
  return ret.status === 0;
97
115
  }
98
116
 
99
117
  function uninstallCamoufox() {
100
- const ret = run(resolveNpxBin(), ['--yes', '--package=camoufox', 'camoufox', 'remove']);
118
+ const ret = runPackageCommand('camoufox', ['camoufox', 'remove']);
101
119
  return ret.status === 0;
102
120
  }
103
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/webauto",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "webauto": "bin/webauto.mjs"