unbrowse 3.8.0 → 4.0.1

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/cli.js CHANGED
@@ -31,7 +31,7 @@ var __promiseAll = (args) => Promise.all(args);
31
31
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
32
32
 
33
33
  // ../../src/build-info.generated.ts
34
- var BUILD_RELEASE_VERSION = "3.8.0", BUILD_GIT_SHA = "81ae16d04a8c", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiMy44LjAiLCJnaXRfc2hhIjoiODFhZTE2ZDA0YThjIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUA4MWFlMTZkMDRhOGMiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTI1VDAxOjAyOjAwLjEwNloifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "rPotXSdWbu75TR3RgjPFf-9Gdc9qwUidjazsSGRYR8Y", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
34
+ var BUILD_RELEASE_VERSION = "4.0.1", BUILD_GIT_SHA = "1f3097d82cad", BUILD_CODE_HASH = "5d9ebf619c61", BUILD_RELEASE_MANIFEST_BASE64 = "eyJzY2hlbWFfdmVyc2lvbiI6MSwicmVsZWFzZV92ZXJzaW9uIjoiNC4wLjEiLCJnaXRfc2hhIjoiMWYzMDk3ZDgyY2FkIiwiY29kZV9oYXNoIjoiNWQ5ZWJmNjE5YzYxIiwidHJhY2VfdmVyc2lvbiI6IjVkOWViZjYxOWM2MUAxZjMwOTdkODJjYWQiLCJpc3N1ZWRfYXQiOiIyMDI2LTA0LTI1VDAzOjE0OjAyLjU2MVoifQ", BUILD_RELEASE_MANIFEST_SIGNATURE = "Csq--Ymsq3ZSoGooWl9PUup0wzkTbB5uzhEiij1mjdY", BUILD_DEFAULT_BACKEND_URL = "https://beta-api.unbrowse.ai";
35
35
 
36
36
  // ../../src/version.ts
37
37
  import { createHash } from "crypto";
@@ -530,11 +530,6 @@ function getRegistrableDomain(hostname2) {
530
530
  }
531
531
  return parts.slice(-2).join(".");
532
532
  }
533
- function isDomainMatch(cookieDomain, targetDomain) {
534
- const c = cookieDomain.replace(/^\./, "").toLowerCase();
535
- const t = targetDomain.replace(/^\./, "").toLowerCase();
536
- return t === c || t.endsWith("." + c);
537
- }
538
533
  var CC_TLDS, GEO_TLD_SUFFIXES;
539
534
  var init_domain = __esm(() => {
540
535
  CC_TLDS = new Set([
@@ -640,7 +635,6 @@ var init_logger = __esm(() => {
640
635
  // ../../src/kuri/client.ts
641
636
  import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
642
637
  import { existsSync as existsSync9 } from "node:fs";
643
- import net from "node:net";
644
638
  import path5 from "node:path";
645
639
  function createBrokerState(port = KURI_DEFAULT_PORT) {
646
640
  return {
@@ -653,39 +647,6 @@ function createBrokerState(port = KURI_DEFAULT_PORT) {
653
647
  requestedPort: port
654
648
  };
655
649
  }
656
- function brokerCacheKey(port) {
657
- return port === undefined ? "default" : `port:${port}`;
658
- }
659
- function rememberBrokerClient(client, state) {
660
- brokerClients.set(brokerCacheKey(state.requestedPort), client);
661
- brokerClients.set(brokerCacheKey(state.port), client);
662
- }
663
- function forgetBrokerClient(state) {
664
- brokerClients.delete(brokerCacheKey(state.requestedPort));
665
- brokerClients.delete(brokerCacheKey(state.port));
666
- }
667
- function envFlag(value) {
668
- return value === "1" || value?.toLowerCase() === "true";
669
- }
670
- function falseyEnv(value) {
671
- if (!value)
672
- return false;
673
- const normalized = value.trim().toLowerCase();
674
- return normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off";
675
- }
676
- function resolveKuriLaunchConfig(env = process.env) {
677
- const explicitHeadless = env.KURI_HEADLESS ?? env.HEADLESS;
678
- const headless = explicitHeadless !== undefined ? envFlag(explicitHeadless) : process.platform === "linux" && !env.DISPLAY;
679
- const cleanRoom = envFlag(env.UNBROWSE_LOCAL_ONLY) || envFlag(env.KURI_CLEAN_ROOM);
680
- const browserCookieOptOut = falseyEnv(env.UNBROWSE_IMPORT_BROWSER_COOKIES);
681
- const explicitAttach = envFlag(env.KURI_ATTACH_EXISTING_CHROME ?? env.UNBROWSE_ATTACH_EXISTING_CHROME);
682
- const disableCdpAttach = envFlag(env.KURI_DISABLE_CDP_ATTACH);
683
- const canAttachToExistingChrome = !headless && !disableCdpAttach && !cleanRoom;
684
- return {
685
- headless,
686
- attachToExistingChrome: canAttachToExistingChrome && (explicitAttach || browserCookieOptOut)
687
- };
688
- }
689
650
  function kuriBinaryName() {
690
651
  return process.platform === "win32" ? "kuri.exe" : "kuri";
691
652
  }
@@ -744,679 +705,13 @@ function getKuriBinaryCandidates() {
744
705
  addCandidate(candidates, resolveBinaryOnPath("kuri"));
745
706
  return candidates;
746
707
  }
747
- async function discoverCdpPort(state) {
748
- const portsToTry = [9222, 9223, 9224, 9225];
749
- for (const port of portsToTry) {
750
- try {
751
- const res = await fetch(`http://127.0.0.1:${port}/json/version`, {
752
- signal: AbortSignal.timeout(500)
753
- });
754
- if (res.ok) {
755
- state.cdpPort = port;
756
- kuriCdpPort = port;
757
- log("kuri", `found Chrome CDP on port ${port}`);
758
- return;
759
- }
760
- } catch {}
761
- }
762
- log("kuri", "could not discover CDP port — tab discovery may fail");
763
- }
764
- async function findFreeCdpPort() {
765
- for (let port = 9222;port < 9230; port++) {
766
- try {
767
- await fetch(`http://127.0.0.1:${port}/json/version`, {
768
- signal: AbortSignal.timeout(300)
769
- });
770
- } catch {
771
- return port;
772
- }
773
- }
774
- return 9222;
775
- }
776
- async function isKuriHealthyOnPort(port) {
777
- try {
778
- const health = await fetch(`http://127.0.0.1:${port}/health`, {
779
- signal: AbortSignal.timeout(1000)
780
- });
781
- return health.ok;
782
- } catch {
783
- return false;
784
- }
785
- }
786
- async function isChromeCdpAvailable(port) {
787
- try {
788
- const res = await fetch(`http://127.0.0.1:${port}/json/version`, {
789
- signal: AbortSignal.timeout(1000)
790
- });
791
- return res.ok;
792
- } catch {
793
- return false;
794
- }
795
- }
796
- async function listRegisteredTabs(state) {
797
- try {
798
- const tabs = await kuriGet(state, "/tabs");
799
- if (!Array.isArray(tabs))
800
- return [];
801
- return tabs.filter((tab) => typeof tab?.id === "string").map((tab) => ({ id: tab.id, url: tab.url ?? "", title: tab.title }));
802
- } catch {
803
- return [];
804
- }
805
- }
806
- async function waitForChromeCdpReady(state, timeoutMs = KURI_CDP_READY_TIMEOUT_MS) {
807
- if (typeof state.cdpPort !== "number")
808
- return false;
809
- const deadline = Date.now() + timeoutMs;
810
- while (Date.now() < deadline) {
811
- if (await isChromeCdpAvailable(state.cdpPort))
812
- return true;
813
- await new Promise((resolve) => setTimeout(resolve, KURI_CDP_POLL_INTERVAL_MS));
814
- }
815
- return false;
816
- }
817
- function shouldReuseManagedChrome(launchConfig, state, managedChromeAvailable) {
818
- return !launchConfig.attachToExistingChrome && state.managedChrome === true && typeof state.cdpPort === "number" && managedChromeAvailable;
819
- }
820
- async function isTcpPortOpen(port, timeoutMs = 400) {
821
- return await new Promise((resolve) => {
822
- const socket = net.createConnection({ host: "127.0.0.1", port });
823
- const finish = (open) => {
824
- socket.removeAllListeners();
825
- socket.destroy();
826
- resolve(open);
827
- };
828
- socket.setTimeout(timeoutMs);
829
- socket.once("connect", () => finish(true));
830
- socket.once("timeout", () => finish(false));
831
- socket.once("error", () => finish(false));
832
- });
833
- }
834
- async function resolveKuriPort(preferredPort, deps = {}) {
835
- const isHealthyPort = deps.isHealthyPort ?? isKuriHealthyOnPort;
836
- const isPortOpen = deps.isPortOpen ?? isTcpPortOpen;
837
- const searchLimit = deps.searchLimit ?? KURI_PORT_SEARCH_LIMIT;
838
- if (await isHealthyPort(preferredPort))
839
- return preferredPort;
840
- if (!await isPortOpen(preferredPort))
841
- return preferredPort;
842
- for (let candidate = preferredPort + 1;candidate <= preferredPort + searchLimit; candidate++) {
843
- if (await isHealthyPort(candidate))
844
- return candidate;
845
- if (!await isPortOpen(candidate))
846
- return candidate;
847
- }
848
- return preferredPort;
849
- }
850
- async function ensureUserChromeRunning(state) {
851
- for (const port2 of [9222, 9223, 9224, 9225]) {
852
- try {
853
- const res = await fetch(`http://127.0.0.1:${port2}/json/version`, { signal: AbortSignal.timeout(500) });
854
- if (res.ok) {
855
- state.cdpPort = port2;
856
- return;
857
- }
858
- } catch {}
859
- }
860
- const chromePaths = {
861
- darwin: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
862
- linux: "google-chrome",
863
- win32: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
864
- };
865
- const chromeBin = chromePaths[process.platform];
866
- if (!chromeBin)
867
- return;
868
- const port = await findFreeCdpPort();
869
- state.cdpPort = port;
870
- log("kuri", `launching user Chrome with CDP on port ${port}`);
871
- try {
872
- const child = spawn2(chromeBin, [
873
- `--remote-debugging-port=${port}`,
874
- "--no-first-run",
875
- "--no-default-browser-check"
876
- ], {
877
- stdio: "ignore",
878
- detached: true
879
- });
880
- child.unref();
881
- const deadline = Date.now() + 8000;
882
- while (Date.now() < deadline) {
883
- try {
884
- const res = await fetch(`http://127.0.0.1:${port}/json/version`, { signal: AbortSignal.timeout(500) });
885
- if (res.ok) {
886
- log("kuri", `user Chrome ready with CDP on port ${port}`);
887
- return;
888
- }
889
- } catch {}
890
- await new Promise((r) => setTimeout(r, 300));
891
- }
892
- log("kuri", "user Chrome launched but CDP not responding — Kuri will launch managed Chrome");
893
- state.cdpPort = null;
894
- } catch (err) {
895
- log("kuri", `failed to launch user Chrome: ${err instanceof Error ? err.message : err}`);
896
- state.cdpPort = null;
897
- }
898
- }
899
- async function terminateBrokerOnPort(port) {
900
- try {
901
- const output = execFileSync2("lsof", ["-tiTCP:" + String(port), "-sTCP:LISTEN"], {
902
- encoding: "utf8",
903
- stdio: ["ignore", "pipe", "ignore"]
904
- });
905
- const pids = output.split(/\r?\n/).map((line) => Number(line.trim())).filter((pid) => Number.isInteger(pid) && pid > 0);
906
- for (const pid of pids) {
907
- try {
908
- process.kill(pid, "SIGTERM");
909
- } catch {}
910
- }
911
- if (pids.length > 0) {
912
- await new Promise((resolve) => setTimeout(resolve, 500));
913
- }
914
- } catch {}
915
- }
916
- async function reuseHealthyBrokerIfPossible(state, launchConfig, deps = {}) {
917
- const isHealthyPort = deps.isHealthyPort ?? isKuriHealthyOnPort;
918
- if (!await isHealthyPort(state.port))
919
- return false;
920
- log("kuri", `already running on port ${state.port}`);
921
- state.ready = true;
922
- const cdpAvailable = deps.isChromeCdpAvailable ?? isChromeCdpAvailable;
923
- const discover = deps.discoverCdpPort ?? discoverCdpPort;
924
- await discover(state);
925
- if (!state.cdpPort && launchConfig.attachToExistingChrome) {
926
- const ensureChrome = deps.ensureUserChromeRunning ?? ensureUserChromeRunning;
927
- await ensureChrome(state);
928
- }
929
- if (typeof state.cdpPort === "number" && !await cdpAvailable(state.cdpPort)) {
930
- state.cdpPort = null;
931
- }
932
- const syncTabs = deps.ensureTabsDiscovered ?? ensureTabsDiscovered;
933
- await syncTabs(state).catch(() => {});
934
- const tabs = await (deps.listTabs ?? listRegisteredTabs)(state).catch(() => []);
935
- if (state.cdpPort) {
936
- return true;
937
- }
938
- if (tabs.length > 0) {
939
- log("kuri", `healthy broker on port ${state.port} has stale registered tabs but no CDP; recycling startup path`);
940
- } else {
941
- log("kuri", `healthy broker on port ${state.port} has no CDP or tabs; recycling startup path`);
942
- }
943
- state.ready = false;
944
- if (state.process) {
945
- state.process.kill("SIGTERM");
946
- await waitForChildExit(state.process);
947
- state.process = null;
948
- } else {
949
- const terminate = deps.terminateBrokerOnPort ?? terminateBrokerOnPort;
950
- await terminate(state.port);
951
- }
952
- return false;
953
- }
954
- function kuriUrl(state, path6, params) {
955
- const base = `http://127.0.0.1:${state.port}${path6}`;
956
- if (!params || Object.keys(params).length === 0)
957
- return base;
958
- const parts = Object.entries(params).map(([k, v]) => `${k}=${v.replace(/#/g, "%23").replace(/&/g, "%26").replace(/\+/g, "%2B")}`);
959
- return `${base}?${parts.join("&")}`;
960
- }
961
- function shouldRetryKuriTransportError(error) {
962
- const message = error instanceof Error ? error.message : String(error ?? "");
963
- return /fetch failed|econnrefused|connection refused|socket hang up|other side closed|transport closed/i.test(message);
964
- }
965
- async function kuriGet(state, path6, params, retryOnTransportFailure = true) {
966
- const url = kuriUrl(state, path6, params);
967
- const controller = new AbortController;
968
- const timeout = setTimeout(() => controller.abort(), KURI_REQUEST_TIMEOUT_MS);
969
- try {
970
- const res = await fetch(url, { signal: controller.signal });
971
- const text = await res.text();
972
- try {
973
- return JSON.parse(text);
974
- } catch {
975
- return text;
976
- }
977
- } catch (error) {
978
- if (retryOnTransportFailure && path6 !== "/health" && shouldRetryKuriTransportError(error)) {
979
- log("kuri", `transport failed for ${path6}; restarting Kuri and retrying once on port ${state.port}`);
980
- await stopOn(state);
981
- await startOn(state, state.requestedPort || state.port);
982
- return await kuriGet(state, path6, params, false);
983
- }
984
- throw error;
985
- } finally {
986
- clearTimeout(timeout);
987
- }
988
- }
989
708
  function findKuriBinary() {
990
709
  if (process.env.KURI_BIN)
991
710
  return process.env.KURI_BIN;
992
711
  const candidates = getKuriBinaryCandidates();
993
712
  return candidates.find((candidate) => existsSync9(candidate)) ?? candidates[0] ?? kuriBinaryName();
994
713
  }
995
- async function waitForChildExit(child, timeoutMs = 2000) {
996
- if (!child)
997
- return;
998
- if (child.exitCode !== null || child.killed)
999
- return;
1000
- await new Promise((resolve) => {
1001
- const timer = setTimeout(resolve, timeoutMs);
1002
- child.once("exit", () => {
1003
- clearTimeout(timer);
1004
- resolve();
1005
- });
1006
- });
1007
- }
1008
- async function startOn(state, port) {
1009
- const requestedPort = port ?? Number(process.env.KURI_PORT || KURI_DEFAULT_PORT);
1010
- const externalPort = process.env.KURI_EXTERNAL_PORT;
1011
- if (externalPort && !state.ready) {
1012
- const ep = Number(externalPort);
1013
- if (await isKuriHealthyOnPort(ep)) {
1014
- state.port = ep;
1015
- state.requestedPort = ep;
1016
- state.ready = true;
1017
- state.managedChrome = false;
1018
- const cdpUrl = process.env.CDP_URL;
1019
- if (cdpUrl) {
1020
- const m = cdpUrl.match(/:(\d+)/);
1021
- if (m)
1022
- state.cdpPort = Number(m[1]);
1023
- }
1024
- log("kuri", `using external Kuri broker on port ${ep}${state.cdpPort ? ` (CDP port ${state.cdpPort})` : ""}`);
1025
- return;
1026
- }
1027
- log("kuri", `external Kuri on port ${ep} not healthy — falling through to normal start`);
1028
- }
1029
- if (state.ready) {
1030
- const activePort = state.port || requestedPort;
1031
- if (await isKuriHealthyOnPort(activePort))
1032
- return;
1033
- log("kuri", `cached ready state stale on port ${activePort}; restarting`);
1034
- state.ready = false;
1035
- state.process = null;
1036
- state.startPromise = null;
1037
- }
1038
- if (state.startPromise)
1039
- return state.startPromise;
1040
- const startPromise = (async () => {
1041
- const launchConfig = resolveKuriLaunchConfig();
1042
- state.requestedPort = requestedPort;
1043
- state.port = await resolveKuriPort(requestedPort);
1044
- const existingClient = brokerClients.get(brokerCacheKey(requestedPort));
1045
- if (existingClient)
1046
- rememberBrokerClient(existingClient, state);
1047
- if (state.port !== requestedPort) {
1048
- log("kuri", `preferred port ${requestedPort} is occupied but unhealthy; falling back to ${state.port}`);
1049
- }
1050
- if (await reuseHealthyBrokerIfPossible(state, launchConfig))
1051
- return;
1052
- const binary = findKuriBinary();
1053
- log("kuri", `starting: ${binary} on port ${state.port}`);
1054
- if (!existsSync9(binary)) {
1055
- throw new Error(`Kuri binary not found at ${binary}`);
1056
- }
1057
- const reusableManagedChrome = shouldReuseManagedChrome(launchConfig, state, typeof state.cdpPort === "number" && await isChromeCdpAvailable(state.cdpPort));
1058
- if (launchConfig.attachToExistingChrome) {
1059
- await discoverCdpPort(state);
1060
- state.managedChrome = false;
1061
- } else if (reusableManagedChrome) {
1062
- log("kuri", `reconnecting to surviving managed Chrome on port ${state.cdpPort}`);
1063
- } else {
1064
- state.cdpPort = null;
1065
- state.managedChrome = false;
1066
- }
1067
- const env = {
1068
- ...process.env,
1069
- PORT: String(state.port),
1070
- HOST: "127.0.0.1",
1071
- HEADLESS: launchConfig.headless ? "true" : "false"
1072
- };
1073
- if (state.cdpPort && (launchConfig.attachToExistingChrome || reusableManagedChrome)) {
1074
- env.CDP_URL = `ws://127.0.0.1:${state.cdpPort}`;
1075
- log("kuri", reusableManagedChrome ? `connecting to surviving managed Chrome on port ${state.cdpPort}` : `connecting to existing Chrome on port ${state.cdpPort}`);
1076
- } else if (launchConfig.headless) {
1077
- log("kuri", "starting in headless mode with managed Chrome");
1078
- } else {
1079
- log("kuri", "no existing Chrome found — Kuri will launch managed Chrome");
1080
- }
1081
- const maxAttempts = KURI_SPAWN_RETRIES + 1;
1082
- for (let attempt = 1;attempt <= maxAttempts; attempt++) {
1083
- if (attempt > 1) {
1084
- log("kuri", `spawn retry ${attempt}/${maxAttempts} after ${KURI_SPAWN_RETRY_DELAY_MS}ms`);
1085
- await new Promise((r) => setTimeout(r, KURI_SPAWN_RETRY_DELAY_MS));
1086
- }
1087
- let exitedBeforeReady = false;
1088
- state.process = spawn2(binary, [], {
1089
- env,
1090
- stdio: ["ignore", "pipe", "pipe"]
1091
- });
1092
- const childPid = state.process.pid;
1093
- log("kuri", `spawned pid ${childPid ?? "unknown"} on broker port ${state.port}`);
1094
- state.process.stderr?.on("data", (chunk) => {
1095
- const line = chunk.toString().trim();
1096
- if (line)
1097
- log("kuri", `[stderr] ${line}`);
1098
- const cdpMatch = line.match(/CDP port:\s*(\d+)/);
1099
- if (cdpMatch) {
1100
- state.cdpPort = parseInt(cdpMatch[1], 10);
1101
- kuriCdpPort = state.cdpPort;
1102
- log("kuri", `discovered CDP port: ${state.cdpPort}`);
1103
- }
1104
- if (/launched Chrome \(pid=\d+\) on CDP port/i.test(line) || /launching managed Chrome instance/i.test(line)) {
1105
- state.managedChrome = true;
1106
- }
1107
- });
1108
- state.process.on("exit", (code, signal) => {
1109
- if (!state.ready)
1110
- exitedBeforeReady = true;
1111
- log("kuri", `process exited pid=${childPid ?? "unknown"} code=${code === null ? "null" : code} signal=${signal ?? "none"} broker_port=${state.port} cdp_port=${state.cdpPort ?? "unknown"}`);
1112
- state.ready = false;
1113
- state.process = null;
1114
- forgetBrokerClient(state);
1115
- });
1116
- const deadline = Date.now() + KURI_STARTUP_TIMEOUT_MS;
1117
- while (Date.now() < deadline) {
1118
- if (exitedBeforeReady)
1119
- break;
1120
- try {
1121
- const res = await fetch(`http://127.0.0.1:${state.port}/health`, {
1122
- signal: AbortSignal.timeout(500)
1123
- });
1124
- if (res.ok) {
1125
- state.ready = true;
1126
- log("kuri", `ready on port ${state.port}`);
1127
- await new Promise((r) => setTimeout(r, 300));
1128
- if (!state.cdpPort)
1129
- await discoverCdpPort(state);
1130
- await waitForChromeCdpReady(state).catch(() => false);
1131
- await ensureTabsDiscovered(state);
1132
- return;
1133
- }
1134
- } catch {}
1135
- await new Promise((r) => setTimeout(r, 200));
1136
- }
1137
- if (state.ready)
1138
- return;
1139
- if (state.process) {
1140
- state.process.kill();
1141
- await waitForChildExit(state.process);
1142
- }
1143
- try {
1144
- execFileSync2("pkill", ["-f", `remote-debugging-port=${state.cdpPort ?? 9222}`], { stdio: "ignore" });
1145
- await new Promise((r) => setTimeout(r, 1000));
1146
- } catch {}
1147
- }
1148
- throw new Error(`Kuri failed to start after ${maxAttempts} attempts`);
1149
- })();
1150
- state.startPromise = startPromise.finally(() => {
1151
- if (state.startPromise === startPromise) {
1152
- state.startPromise = null;
1153
- }
1154
- });
1155
- return state.startPromise;
1156
- }
1157
- async function stopOn(state) {
1158
- if (state.startPromise) {
1159
- await state.startPromise.catch(() => {});
1160
- }
1161
- if (state.process) {
1162
- state.process.kill("SIGTERM");
1163
- state.process = null;
1164
- }
1165
- state.ready = false;
1166
- state.cdpPort = null;
1167
- kuriCdpPort = null;
1168
- state.managedChrome = false;
1169
- state.startPromise = null;
1170
- forgetBrokerClient(state);
1171
- }
1172
- async function getDefaultTabOn(state) {
1173
- await ensureTabsDiscovered(state);
1174
- try {
1175
- const tabs = await kuriGet(state, "/tabs");
1176
- if (Array.isArray(tabs) && tabs.length > 0)
1177
- return tabs[0].id;
1178
- } catch {}
1179
- const cdpTabId = await createTabViaChromeCdp("about:blank", state);
1180
- if (cdpTabId)
1181
- return cdpTabId;
1182
- throw new Error("No tabs available and failed to create one");
1183
- }
1184
- async function start(port, state = defaultBrokerState) {
1185
- return startOn(state, port);
1186
- }
1187
- async function stop(state = defaultBrokerState) {
1188
- return stopOn(state);
1189
- }
1190
- async function getDefaultTab(state = defaultBrokerState) {
1191
- return getDefaultTabOn(state);
1192
- }
1193
- async function ensureTabsDiscovered(state) {
1194
- try {
1195
- if (state.cdpPort)
1196
- await waitForChromeCdpReady(state).catch(() => false);
1197
- const params = {};
1198
- if (state.cdpPort)
1199
- params.cdp_url = `ws://127.0.0.1:${state.cdpPort}`;
1200
- await kuriGet(state, "/discover", params);
1201
- } catch {}
1202
- }
1203
- async function waitForTabRegistration(state, tabId, timeoutMs = 2000) {
1204
- const deadline = Date.now() + timeoutMs;
1205
- while (Date.now() < deadline) {
1206
- await ensureTabsDiscovered(state);
1207
- try {
1208
- const tabs = await kuriGet(state, "/tabs");
1209
- if (Array.isArray(tabs) && tabs.some((tab) => tab?.id === tabId))
1210
- return;
1211
- } catch {}
1212
- await new Promise((resolve) => setTimeout(resolve, 100));
1213
- }
1214
- }
1215
- async function createTabViaChromeCdp(url = "about:blank", state = defaultBrokerState) {
1216
- if (!state.cdpPort)
1217
- return "";
1218
- for (let attempt = 0;attempt < KURI_TAB_CREATE_RETRIES; attempt += 1) {
1219
- await waitForChromeCdpReady(state).catch(() => false);
1220
- try {
1221
- const res = await fetch(`http://127.0.0.1:${state.cdpPort}/json/new?${url}`, {
1222
- method: "PUT",
1223
- signal: AbortSignal.timeout(5000)
1224
- });
1225
- const target = await res.json();
1226
- const tabId = target?.id ?? target?.targetId ?? "";
1227
- if (tabId) {
1228
- log("kuri", `created new Chrome tab: ${tabId}`);
1229
- await new Promise((resolve) => setTimeout(resolve, 300));
1230
- await ensureTabsDiscovered(state).catch(() => {});
1231
- await waitForTabRegistration(state, tabId).catch(() => {});
1232
- return tabId;
1233
- }
1234
- } catch (err) {
1235
- if (attempt === KURI_TAB_CREATE_RETRIES - 1) {
1236
- log("kuri", `Chrome tab creation failed: ${err instanceof Error ? err.message : err}`);
1237
- return "";
1238
- }
1239
- }
1240
- await new Promise((resolve) => setTimeout(resolve, KURI_CDP_POLL_INTERVAL_MS));
1241
- }
1242
- return "";
1243
- }
1244
- async function navigate(tabId, url, state = defaultBrokerState) {
1245
- await kuriGet(state, "/navigate", { tab_id: tabId, url });
1246
- }
1247
- async function evaluate(tabId, expression, state = defaultBrokerState) {
1248
- let raw;
1249
- if (expression.length > 2000) {
1250
- const url = kuriUrl(state, "/evaluate", { tab_id: tabId, expression });
1251
- const controller = new AbortController;
1252
- const timeout = setTimeout(() => controller.abort(), KURI_REQUEST_TIMEOUT_MS);
1253
- try {
1254
- const res = await fetch(url, {
1255
- method: "POST",
1256
- headers: { "Content-Type": "text/plain" },
1257
- body: expression,
1258
- signal: controller.signal
1259
- });
1260
- const text = await res.text();
1261
- try {
1262
- raw = JSON.parse(text);
1263
- } catch {
1264
- raw = text;
1265
- }
1266
- } finally {
1267
- clearTimeout(timeout);
1268
- }
1269
- } else {
1270
- raw = await kuriGet(state, "/evaluate", { tab_id: tabId, expression });
1271
- }
1272
- const inner = raw?.result?.result;
1273
- if (!inner)
1274
- return raw;
1275
- if (inner.type === "undefined")
1276
- return;
1277
- if ("value" in inner)
1278
- return inner.value;
1279
- return inner.description ?? raw;
1280
- }
1281
- async function getCookies(tabId, state = defaultBrokerState) {
1282
- const raw = await kuriGet(state, "/cookies", { tab_id: tabId });
1283
- return raw?.result?.cookies ?? [];
1284
- }
1285
- async function setCookieViaCDP(wsUrl, cookie) {
1286
- return new Promise((resolve) => {
1287
- const timer = setTimeout(() => {
1288
- resolve(false);
1289
- }, 3000);
1290
- try {
1291
- const ws = new (__require("ws"))(wsUrl);
1292
- ws.on("open", () => {
1293
- ws.send(JSON.stringify({
1294
- id: 1,
1295
- method: "Network.setCookie",
1296
- params: {
1297
- ...cookie,
1298
- url: `https://${cookie.domain.replace(/^\./, "")}/`
1299
- }
1300
- }));
1301
- });
1302
- ws.on("message", (data) => {
1303
- clearTimeout(timer);
1304
- try {
1305
- const msg = JSON.parse(data.toString());
1306
- if (msg.id === 1) {
1307
- ws.close();
1308
- resolve(msg.result?.success ?? false);
1309
- }
1310
- } catch {
1311
- ws.close();
1312
- resolve(false);
1313
- }
1314
- });
1315
- ws.on("error", () => {
1316
- clearTimeout(timer);
1317
- resolve(false);
1318
- });
1319
- } catch {
1320
- clearTimeout(timer);
1321
- resolve(false);
1322
- }
1323
- });
1324
- }
1325
- async function resolveCdpDebuggerUrlForTab(tabId) {
1326
- const portsToTry = Array.from(new Set([kuriCdpPort, 9222, 9223, 9224, 9225].filter((port) => typeof port === "number")));
1327
- for (const port of portsToTry) {
1328
- try {
1329
- const res = await fetch(`http://127.0.0.1:${port}/json`, { signal: AbortSignal.timeout(1000) });
1330
- if (!res.ok)
1331
- continue;
1332
- const pages = await res.json();
1333
- const page = pages.find((candidate) => candidate.id === tabId);
1334
- if (page?.webSocketDebuggerUrl) {
1335
- if (kuriCdpPort !== port) {
1336
- kuriCdpPort = port;
1337
- log("kuri", `updated CDP port from tab discovery: ${port}`);
1338
- }
1339
- return page.webSocketDebuggerUrl;
1340
- }
1341
- } catch {}
1342
- }
1343
- return null;
1344
- }
1345
- async function setCookie(tabId, cookie, state = defaultBrokerState) {
1346
- const value = cookie.value.replace(/^"|"$/g, "");
1347
- if (cookie.secure || cookie.httpOnly) {
1348
- try {
1349
- const debuggerUrl = await resolveCdpDebuggerUrlForTab(tabId);
1350
- if (debuggerUrl) {
1351
- const success = await setCookieViaCDP(debuggerUrl, {
1352
- name: cookie.name,
1353
- value,
1354
- domain: cookie.domain,
1355
- path: cookie.path || "/",
1356
- secure: cookie.secure ?? false,
1357
- httpOnly: cookie.httpOnly ?? false,
1358
- sameSite: cookie.sameSite || "Lax",
1359
- ...cookie.expires && cookie.expires > 0 ? { expires: cookie.expires } : {}
1360
- });
1361
- if (success)
1362
- return;
1363
- log("kuri", `CDP cookie set failed for ${cookie.name} on ${cookie.domain}; falling back to /cookies`);
1364
- } else {
1365
- log("kuri", `no CDP websocket for tab ${tabId}; falling back to /cookies for secure cookie ${cookie.name}`);
1366
- }
1367
- } catch {}
1368
- }
1369
- await kuriGet(state, "/cookies", {
1370
- tab_id: tabId,
1371
- name: cookie.name,
1372
- value,
1373
- domain: cookie.domain,
1374
- ...cookie.path ? { path: cookie.path } : {}
1375
- });
1376
- }
1377
- async function networkEnable(tabId, state = defaultBrokerState) {
1378
- await kuriGet(state, "/network", { tab_id: tabId, mode: "enable" });
1379
- }
1380
- async function getCurrentUrl(tabId, state = defaultBrokerState) {
1381
- const result = await evaluate(tabId, "window.location.href", state);
1382
- return typeof result === "string" ? result : "";
1383
- }
1384
- async function action(tabId, actionType, ref, value, state = defaultBrokerState) {
1385
- const params = { tab_id: tabId, action: actionType, ref };
1386
- if (value !== undefined)
1387
- params.value = value;
1388
- return kuriGet(state, "/action", params);
1389
- }
1390
- async function click(tabId, ref, state = defaultBrokerState) {
1391
- await scrollIntoView(tabId, ref, state);
1392
- return action(tabId, "click", ref, undefined, state);
1393
- }
1394
- async function fill(tabId, ref, value, state = defaultBrokerState) {
1395
- await click(tabId, ref, state);
1396
- const result = await action(tabId, "fill", ref, value, state);
1397
- const currentValue = await evaluate(tabId, `(() => {
1398
- const active = document.activeElement;
1399
- return active && "value" in active ? active.value : undefined;
1400
- })()`, state);
1401
- if (currentValue !== value) {
1402
- return evaluate(tabId, `(function() {
1403
- const active = document.activeElement;
1404
- if (!active || !("value" in active)) return false;
1405
- active.value = ${JSON.stringify(value)};
1406
- active.dispatchEvent(new Event("input", { bubbles: true }));
1407
- active.dispatchEvent(new Event("change", { bubbles: true }));
1408
- return active.value;
1409
- })()`, state);
1410
- }
1411
- return result;
1412
- }
1413
- async function scrollIntoView(tabId, ref, state = defaultBrokerState) {
1414
- return kuriGet(state, "/scrollintoview", { tab_id: tabId, ref });
1415
- }
1416
- async function authProfileSave(tabId, name, state = defaultBrokerState) {
1417
- return kuriGet(state, "/auth/profile/save", { tab_id: tabId, name });
1418
- }
1419
- var KURI_DEFAULT_PORT = 7700, KURI_STARTUP_TIMEOUT_MS = 60000, KURI_REQUEST_TIMEOUT_MS = 30000, KURI_SPAWN_RETRIES = 3, KURI_SPAWN_RETRY_DELAY_MS = 1000, KURI_PORT_SEARCH_LIMIT = 10, KURI_CDP_READY_TIMEOUT_MS = 5000, KURI_CDP_POLL_INTERVAL_MS = 200, KURI_TAB_CREATE_RETRIES = 5, kuriCdpPort = null, defaultBrokerState, brokerClients;
714
+ var KURI_DEFAULT_PORT = 7700, defaultBrokerState, brokerClients;
1420
715
  var init_client2 = __esm(() => {
1421
716
  init_logger();
1422
717
  init_paths();
@@ -1721,210 +1016,6 @@ var init_token_resolver = __esm(() => {
1721
1016
  init_token_sources();
1722
1017
  });
1723
1018
 
1724
- // ../../src/auth/agent-mail.ts
1725
- var exports_agent_mail = {};
1726
- __export(exports_agent_mail, {
1727
- waitForVerificationEmail: () => waitForVerificationEmail,
1728
- tryAgentMailAuth: () => tryAgentMailAuth,
1729
- isAgentMailAvailable: () => isAgentMailAvailable,
1730
- getOrCreateSiteInbox: () => getOrCreateSiteInbox,
1731
- extractVerificationLink: () => extractVerificationLink,
1732
- extractOtpFromEmail: () => extractOtpFromEmail,
1733
- autonomousEmailLogin: () => autonomousEmailLogin
1734
- });
1735
- import { AgentMailClient } from "agentmail";
1736
- function getClient() {
1737
- if (_client)
1738
- return _client;
1739
- const apiKey = process.env.AGENTMAIL_API_KEY;
1740
- if (!apiKey)
1741
- throw new Error("AGENTMAIL_API_KEY not set — cannot use agent mail");
1742
- _client = new AgentMailClient({ apiKey });
1743
- return _client;
1744
- }
1745
- async function getStoredInbox(domain) {
1746
- const key = `${VAULT_PREFIX}${getRegistrableDomain(domain)}`;
1747
- const raw = await getCredential(key);
1748
- if (!raw)
1749
- return null;
1750
- try {
1751
- return JSON.parse(raw);
1752
- } catch {
1753
- return null;
1754
- }
1755
- }
1756
- async function storeInbox(domain, inbox) {
1757
- const key = `${VAULT_PREFIX}${getRegistrableDomain(domain)}`;
1758
- await storeCredential(key, JSON.stringify(inbox));
1759
- log("agent-mail", `stored inbox ${inbox.email} for ${domain}`);
1760
- }
1761
- async function getOrCreateSiteInbox(domain) {
1762
- const stored = await getStoredInbox(domain);
1763
- if (stored) {
1764
- log("agent-mail", `reusing stored inbox ${stored.email} for ${domain}`);
1765
- return { inboxId: stored.inboxId, email: stored.email };
1766
- }
1767
- const client = getClient();
1768
- const safeDomain = getRegistrableDomain(domain).replace(/[^a-z0-9-]/gi, "-").toLowerCase();
1769
- const username = `unbrowse-${safeDomain}`;
1770
- const clientId = `unbrowse-login-${safeDomain}`;
1771
- try {
1772
- const inbox = await client.inboxes.create({
1773
- username,
1774
- domain: "agentmail.to",
1775
- displayName: `Unbrowse Agent - ${domain}`,
1776
- clientId
1777
- });
1778
- const result = { inboxId: inbox.inboxId, email: inbox.email };
1779
- await storeInbox(domain, { ...result, domain, createdAt: new Date().toISOString() });
1780
- log("agent-mail", `created inbox ${inbox.email} for ${domain}`);
1781
- return result;
1782
- } catch (err) {
1783
- log("agent-mail", `create failed (likely exists), listing to find match: ${err instanceof Error ? err.message : err}`);
1784
- try {
1785
- const list = await client.inboxes.list({ limit: 100 });
1786
- const match = list.inboxes.find((i) => i.clientId === clientId || i.email === `${username}@agentmail.to`);
1787
- if (match) {
1788
- const result = { inboxId: match.inboxId, email: match.email };
1789
- await storeInbox(domain, { ...result, domain, createdAt: new Date().toISOString() });
1790
- return result;
1791
- }
1792
- } catch {}
1793
- throw new Error(`Failed to create or find AgentMail inbox for ${domain}`);
1794
- }
1795
- }
1796
- async function waitForVerificationEmail(inboxId, fromDomain, timeoutMs = DEFAULT_TIMEOUT_MS) {
1797
- const client = getClient();
1798
- const start2 = Date.now();
1799
- const normalizedDomain = fromDomain.toLowerCase();
1800
- log("agent-mail", `polling inbox ${inboxId} for email from *@${normalizedDomain} (timeout: ${timeoutMs}ms)`);
1801
- while (Date.now() - start2 < timeoutMs) {
1802
- try {
1803
- const response = await client.inboxes.messages.list(inboxId, {
1804
- limit: 10,
1805
- labels: ["received"]
1806
- });
1807
- for (const item of response.messages ?? []) {
1808
- const msg = await client.inboxes.messages.get(inboxId, item.messageId);
1809
- const from = typeof msg.from === "string" ? msg.from : (msg.from ?? []).join(", ");
1810
- if (from.toLowerCase().includes(normalizedDomain)) {
1811
- log("agent-mail", `found verification email from ${from}: "${msg.subject}"`);
1812
- return {
1813
- messageId: msg.messageId,
1814
- threadId: msg.threadId,
1815
- subject: msg.subject ?? "",
1816
- from,
1817
- text: msg.extractedText ?? msg.text ?? "",
1818
- html: msg.extractedHtml ?? msg.html ?? "",
1819
- receivedAt: msg.createdAt
1820
- };
1821
- }
1822
- }
1823
- } catch (err) {
1824
- log("agent-mail", `poll error: ${err instanceof Error ? err.message : err}`);
1825
- }
1826
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1827
- }
1828
- log("agent-mail", `timeout waiting for email from ${normalizedDomain}`);
1829
- return null;
1830
- }
1831
- function extractOtpFromEmail(text) {
1832
- const codePhrase = text.match(/(?:verification|confirm|security|login|one[- ]time|otp)\s*(?:code|pin|number)[:\s]+(\w{4,8})/i);
1833
- if (codePhrase)
1834
- return codePhrase[1];
1835
- const codeIs = text.match(/(?:code|otp|pin)[:\s]+(\w{4,8})/i);
1836
- if (codeIs)
1837
- return codeIs[1];
1838
- const six = text.match(/\b(\d{6})\b/);
1839
- if (six)
1840
- return six[1];
1841
- const four = text.match(/\b(\d{4})\b/);
1842
- if (four)
1843
- return four[1];
1844
- return null;
1845
- }
1846
- function extractVerificationLink(text, html) {
1847
- const htmlLink = html.match(/href="(https?:\/\/[^"]*(?:verify|confirm|activate|magic|token|auth|callback|validate|approve|email)[^"]*)"/i);
1848
- if (htmlLink)
1849
- return htmlLink[1];
1850
- const textLink = text.match(/(https?:\/\/\S*(?:verify|confirm|activate|magic|token|auth|callback|validate|approve|email)\S*)/i);
1851
- if (textLink)
1852
- return textLink[1];
1853
- const anyHtmlLink = html.match(/href="(https?:\/\/[^"]+)"/);
1854
- if (anyHtmlLink)
1855
- return anyHtmlLink[1];
1856
- const anyTextLink = text.match(/(https?:\/\/\S+)/);
1857
- if (anyTextLink)
1858
- return anyTextLink[1];
1859
- return null;
1860
- }
1861
- async function autonomousEmailLogin(domain) {
1862
- const inbox = await getOrCreateSiteInbox(domain);
1863
- const client = getClient();
1864
- log("agent-mail", `session ready for ${domain} — email: ${inbox.email}`);
1865
- return {
1866
- email: inbox.email,
1867
- inboxId: inbox.inboxId,
1868
- domain,
1869
- waitForOtp: async (timeoutMs = DEFAULT_TIMEOUT_MS) => {
1870
- const email = await waitForVerificationEmail(inbox.inboxId, domain, timeoutMs);
1871
- if (!email)
1872
- return null;
1873
- const otp = extractOtpFromEmail(email.text);
1874
- if (otp)
1875
- log("agent-mail", `extracted OTP: ${otp} from ${email.subject}`);
1876
- return otp;
1877
- },
1878
- waitForLink: async (timeoutMs = DEFAULT_TIMEOUT_MS) => {
1879
- const email = await waitForVerificationEmail(inbox.inboxId, domain, timeoutMs);
1880
- if (!email)
1881
- return null;
1882
- const link = extractVerificationLink(email.text, email.html);
1883
- if (link)
1884
- log("agent-mail", `extracted verification link from ${email.subject}`);
1885
- return link;
1886
- },
1887
- waitForEmail: (timeoutMs = DEFAULT_TIMEOUT_MS) => waitForVerificationEmail(inbox.inboxId, domain, timeoutMs),
1888
- sendEmail: async (to, subject, body) => {
1889
- const result = await client.inboxes.messages.send(inbox.inboxId, {
1890
- to: [to],
1891
- subject,
1892
- text: body
1893
- });
1894
- log("agent-mail", `sent email to ${to}: "${subject}" (${result.messageId})`);
1895
- return { messageId: result.messageId, threadId: result.threadId };
1896
- },
1897
- replyTo: async (messageId, body) => {
1898
- const result = await client.inboxes.messages.reply(inbox.inboxId, messageId, {
1899
- text: body
1900
- });
1901
- log("agent-mail", `replied to ${messageId} (${result.messageId})`);
1902
- return { messageId: result.messageId, threadId: result.threadId };
1903
- }
1904
- };
1905
- }
1906
- function isAgentMailAvailable() {
1907
- return Boolean(process.env.AGENTMAIL_API_KEY);
1908
- }
1909
- async function tryAgentMailAuth(domain) {
1910
- if (!isAgentMailAvailable()) {
1911
- log("agent-mail", `skipping — AGENTMAIL_API_KEY not set`);
1912
- return null;
1913
- }
1914
- try {
1915
- return await autonomousEmailLogin(domain);
1916
- } catch (err) {
1917
- log("agent-mail", `auth failed for ${domain}: ${err instanceof Error ? err.message : err}`);
1918
- return null;
1919
- }
1920
- }
1921
- var POLL_INTERVAL_MS = 3000, DEFAULT_TIMEOUT_MS = 90000, _client = null, VAULT_PREFIX = "agentmail:";
1922
- var init_agent_mail = __esm(async () => {
1923
- init_logger();
1924
- init_domain();
1925
- await init_vault();
1926
- });
1927
-
1928
1019
  // ../../src/auth/browser-cookies.ts
1929
1020
  var exports_browser_cookies = {};
1930
1021
  __export(exports_browser_cookies, {
@@ -2292,74 +1383,6 @@ var init_browser_cookies = __esm(() => {
2292
1383
  });
2293
1384
 
2294
1385
  // ../../src/auth/index.ts
2295
- function formatAuthError(error) {
2296
- if (error instanceof Error && error.message)
2297
- return error.message;
2298
- if (typeof error === "string")
2299
- return error;
2300
- try {
2301
- return JSON.stringify(error);
2302
- } catch {
2303
- return String(error);
2304
- }
2305
- }
2306
- function normalizeAuthDomain(domain) {
2307
- return domain.toLowerCase().replace(/^www\./, "");
2308
- }
2309
- function shouldImportBrowserCookies() {
2310
- const raw = process.env.UNBROWSE_IMPORT_BROWSER_COOKIES?.trim();
2311
- if (!raw)
2312
- return true;
2313
- return !DISABLE_BROWSER_COOKIE_IMPORT.test(raw);
2314
- }
2315
- function isLikelyAuthenticatedCookie(targetDomain, cookie) {
2316
- const cookieName = cookie.name.toLowerCase();
2317
- const registrableDomain = getRegistrableDomain(normalizeAuthDomain(targetDomain));
2318
- const requiredCookieNames = DOMAIN_AUTH_COOKIE_NAMES[registrableDomain];
2319
- if (requiredCookieNames)
2320
- return requiredCookieNames.includes(cookieName);
2321
- return GENERIC_AUTH_COOKIE_NAMES.test(cookieName);
2322
- }
2323
- function getAuthenticatedCookiesForDomain(targetDomain, cookies) {
2324
- return cookies.filter((cookie) => isDomainMatch(cookie.domain, targetDomain) && isLikelyAuthenticatedCookie(targetDomain, cookie));
2325
- }
2326
- async function importBrowserCookiesIntoTab(tabId, domain) {
2327
- if (!shouldImportBrowserCookies())
2328
- return 0;
2329
- try {
2330
- const { extractBrowserCookies: extractBrowserCookies2, findBestBrowserSession: findBestBrowserSession2 } = await Promise.resolve().then(() => (init_browser_cookies(), exports_browser_cookies));
2331
- const bestSession = findBestBrowserSession2(domain);
2332
- const cookies = bestSession ? bestSession.cookies : extractBrowserCookies2(domain).cookies;
2333
- let imported = 0;
2334
- for (const cookie of cookies) {
2335
- try {
2336
- await setCookie(tabId, cookie);
2337
- imported += 1;
2338
- } catch (error) {
2339
- log("auth", `browser_cookie_import_failed domain=${normalizeAuthDomain(domain)} tab_id=${tabId} cookie=${cookie.name} error=${formatAuthError(error)}`);
2340
- }
2341
- }
2342
- if (imported > 0) {
2343
- log("auth", `browser_cookie_imported domain=${normalizeAuthDomain(domain)} tab_id=${tabId} count=${imported}`);
2344
- }
2345
- return imported;
2346
- } catch (error) {
2347
- log("auth", `browser_cookie_extract_failed domain=${normalizeAuthDomain(domain)} tab_id=${tabId} error=${formatAuthError(error)}`);
2348
- return 0;
2349
- }
2350
- }
2351
- async function saveAuthProfileBestEffort(tabId, domain, context = "auth") {
2352
- const profileName = normalizeAuthDomain(domain);
2353
- if (!profileName || profileName === "unknown")
2354
- return false;
2355
- try {
2356
- await authProfileSave(tabId, profileName);
2357
- return true;
2358
- } catch (error) {
2359
- log("auth", `${context} auth_profile_failed op=save domain=${profileName} tab_id=${tabId} error=${formatAuthError(error)}`);
2360
- return false;
2361
- }
2362
- }
2363
1386
  async function extractBrowserAuth(domain, opts) {
2364
1387
  const { extractBrowserCookies: extractBrowserCookies2 } = await Promise.resolve().then(() => (init_browser_cookies(), exports_browser_cookies));
2365
1388
  const result = extractBrowserCookies2(domain, opts);
@@ -2446,301 +1469,12 @@ async function getAuthCookies(domain) {
2446
1469
  }
2447
1470
  return null;
2448
1471
  }
2449
- var DISABLE_BROWSER_COOKIE_IMPORT, GENERIC_AUTH_COOKIE_NAMES, DOMAIN_AUTH_COOKIE_NAMES;
2450
1472
  var init_auth = __esm(async () => {
2451
1473
  init_client2();
2452
1474
  init_domain();
2453
1475
  init_logger();
2454
1476
  init_supervisor();
2455
- await __promiseAll([
2456
- init_vault(),
2457
- init_agent_mail()
2458
- ]);
2459
- DISABLE_BROWSER_COOKIE_IMPORT = /^(0|false|no|off)$/i;
2460
- GENERIC_AUTH_COOKIE_NAMES = /(^|[_\-.])(sess(?:ion)?(?:id)?|auth|token|csrf|xsrf|jwt|sid|sso|remember|logged[_-]?in|connect\.sid)([_\-.]|$)/i;
2461
- DOMAIN_AUTH_COOKIE_NAMES = {
2462
- "linkedin.com": ["li_at"]
2463
- };
2464
- });
2465
-
2466
- // ../../src/auth/email-provider.ts
2467
- class AgentMailProvider {
2468
- name = "agentmail";
2469
- get configured() {
2470
- return Boolean(process.env.AGENTMAIL_API_KEY);
2471
- }
2472
- async getAddress(domain) {
2473
- const { getOrCreateSiteInbox: getOrCreateSiteInbox2 } = await init_agent_mail().then(() => exports_agent_mail);
2474
- const inbox = await getOrCreateSiteInbox2(domain);
2475
- return inbox.email;
2476
- }
2477
- async send(to, subject, body) {
2478
- const { autonomousEmailLogin: autonomousEmailLogin2 } = await init_agent_mail().then(() => exports_agent_mail);
2479
- const session = await autonomousEmailLogin2("general");
2480
- const result = await session.sendEmail(to, subject, body);
2481
- return { messageId: result.messageId };
2482
- }
2483
- async waitForMessage(fromDomain, timeoutMs = 90000) {
2484
- const { autonomousEmailLogin: autonomousEmailLogin2, waitForVerificationEmail: waitForVerificationEmail2 } = await init_agent_mail().then(() => exports_agent_mail);
2485
- const session = await autonomousEmailLogin2(fromDomain);
2486
- const email = await waitForVerificationEmail2(session.inboxId, fromDomain, timeoutMs);
2487
- return email;
2488
- }
2489
- }
2490
-
2491
- class GmailProvider {
2492
- name = "gmail";
2493
- get configured() {
2494
- try {
2495
- const { existsSync: existsSync12 } = __require("fs");
2496
- const { homedir: homedir7 } = __require("os");
2497
- const { join: join9 } = __require("path");
2498
- return existsSync12(join9(homedir7(), ".config", "gcloud", "application_default_credentials.json"));
2499
- } catch {
2500
- return false;
2501
- }
2502
- }
2503
- async getAddress(_domain) {
2504
- const { execSync: execSync3 } = __require("child_process");
2505
- try {
2506
- const email = execSync3("gcloud config get-value account 2>/dev/null", { encoding: "utf-8" }).trim();
2507
- if (email && email.includes("@"))
2508
- return email;
2509
- } catch {}
2510
- throw new Error("Gmail not configured — run: gcloud auth login");
2511
- }
2512
- async send(_to, _subject, _body) {
2513
- throw new Error("Gmail send not yet wired — use AgentMail or the gws-gmail-send skill directly");
2514
- }
2515
- async waitForMessage(_fromDomain, _timeoutMs = 90000) {
2516
- throw new Error("Gmail inbox polling not yet wired — use AgentMail or the gws-gmail skill directly");
2517
- }
2518
- }
2519
- function getEmailProvider(purpose = "general") {
2520
- if (purpose === "register") {
2521
- const agentmail = providers.find((p) => p.name === "agentmail" && p.configured);
2522
- if (agentmail)
2523
- return agentmail;
2524
- }
2525
- if (purpose === "login") {
2526
- const gmail = providers.find((p) => p.name === "gmail" && p.configured);
2527
- if (gmail)
2528
- return gmail;
2529
- }
2530
- return providers.find((p) => p.configured) ?? null;
2531
- }
2532
- var providers;
2533
- var init_email_provider = __esm(() => {
2534
- providers = [
2535
- new GmailProvider,
2536
- new AgentMailProvider
2537
- ];
2538
- });
2539
-
2540
- // ../../src/auth/autonomous-login.ts
2541
- async function detectEmailInput(tabId) {
2542
- const ref = await evaluate(tabId, `(() => {
2543
- // Priority order: type=email, name/id containing email, placeholder containing email
2544
- const byType = document.querySelector('input[type="email"]');
2545
- if (byType) return byType.getAttribute("data-ref") || "input[type=email]";
2546
-
2547
- const byName = document.querySelector('input[name*="email" i], input[id*="email" i]');
2548
- if (byName) return byName.getAttribute("data-ref") || 'input[name*="email" i]';
2549
-
2550
- const byPlaceholder = document.querySelector('input[placeholder*="email" i]');
2551
- if (byPlaceholder) return byPlaceholder.getAttribute("data-ref") || 'input[placeholder*="email" i]';
2552
-
2553
- // Generic text input on a login page (single input + submit button pattern)
2554
- const inputs = document.querySelectorAll('input[type="text"], input:not([type])');
2555
- const submit = document.querySelector('button[type="submit"], input[type="submit"], button:not([type])');
2556
- if (inputs.length === 1 && submit) {
2557
- return inputs[0].getAttribute("data-ref") || 'input[type="text"]';
2558
- }
2559
-
2560
- return null;
2561
- })()`);
2562
- return typeof ref === "string" ? ref : null;
2563
- }
2564
- async function detectOtpInput(tabId) {
2565
- const ref = await evaluate(tabId, `(() => {
2566
- // OTP-specific inputs
2567
- const byAutocomplete = document.querySelector('input[autocomplete="one-time-code"]');
2568
- if (byAutocomplete) return byAutocomplete.getAttribute("data-ref") || 'input[autocomplete="one-time-code"]';
2569
-
2570
- // Name/id/placeholder containing otp, code, verification, pin
2571
- const byName = document.querySelector(
2572
- 'input[name*="otp" i], input[name*="code" i], input[name*="verification" i], input[name*="pin" i], ' +
2573
- 'input[id*="otp" i], input[id*="code" i], input[id*="verification" i], ' +
2574
- 'input[placeholder*="code" i], input[placeholder*="verification" i], input[placeholder*="otp" i]'
2575
- );
2576
- if (byName) return byName.getAttribute("data-ref") || 'input[name*="code" i]';
2577
-
2578
- // Numeric input with maxlength 4-8 (common OTP pattern)
2579
- const numericInputs = document.querySelectorAll('input[type="number"], input[type="tel"], input[inputmode="numeric"]');
2580
- for (const el of numericInputs) {
2581
- const ml = parseInt(el.getAttribute("maxlength") || "0");
2582
- if (ml >= 4 && ml <= 8) return el.getAttribute("data-ref") || 'input[inputmode="numeric"]';
2583
- }
2584
-
2585
- // Single short text input that appeared after email submit
2586
- const textInputs = document.querySelectorAll('input[type="text"], input:not([type])');
2587
- for (const el of textInputs) {
2588
- const ml = parseInt(el.getAttribute("maxlength") || "0");
2589
- if (ml >= 4 && ml <= 8) return el.getAttribute("data-ref") || 'input[type="text"]';
2590
- }
2591
-
2592
- return null;
2593
- })()`);
2594
- return typeof ref === "string" ? ref : null;
2595
- }
2596
- async function detectSubmitButton(tabId) {
2597
- const ref = await evaluate(tabId, `(() => {
2598
- const submit = document.querySelector(
2599
- 'button[type="submit"], input[type="submit"], ' +
2600
- 'button:not([type]):not([aria-label*="close" i]):not([aria-label*="cancel" i])'
2601
- );
2602
- if (submit) return submit.getAttribute("data-ref") || 'button[type="submit"]';
2603
-
2604
- // Look for buttons with login/register/continue/verify text
2605
- const buttons = document.querySelectorAll('button, a[role="button"]');
2606
- for (const btn of buttons) {
2607
- const text = btn.textContent?.toLowerCase() || "";
2608
- if (/logs*in|signs*in|register|signs*up|continue|submit|verify|next/i.test(text)) {
2609
- return btn.getAttribute("data-ref") || null;
2610
- }
2611
- }
2612
- return null;
2613
- })()`);
2614
- return typeof ref === "string" ? ref : null;
2615
- }
2616
- async function checkLoginSuccess(tabId, domain) {
2617
- const cookies = await getCookies(tabId);
2618
- const domainCookies = cookies.filter((c) => isDomainMatch(c.domain, domain));
2619
- const authCookies = getAuthenticatedCookiesForDomain(domain, domainCookies);
2620
- return authCookies.length > 0;
2621
- }
2622
- async function autonomousLogin(loginUrl, domain) {
2623
- const start2 = Date.now();
2624
- const targetDomain = domain ?? (() => {
2625
- try {
2626
- return new URL(loginUrl).hostname.replace(/^www\./, "");
2627
- } catch {
2628
- return loginUrl;
2629
- }
2630
- })();
2631
- const elapsed = () => Date.now() - start2;
2632
- const emailProvider = getEmailProvider("register");
2633
- if (!emailProvider) {
2634
- return { success: false, method: "failed", domain: targetDomain, cookies_stored: 0, error: "No email provider configured (set AGENTMAIL_API_KEY or configure Gmail via gcloud)", duration_ms: elapsed() };
2635
- }
2636
- log("autonomous-login", `starting for ${targetDomain} — url: ${loginUrl}`);
2637
- let tabId;
2638
- try {
2639
- await start();
2640
- tabId = await getDefaultTab();
2641
- await networkEnable(tabId);
2642
- } catch (err) {
2643
- return { success: false, method: "failed", domain: targetDomain, cookies_stored: 0, error: `Kuri start failed: ${err}`, duration_ms: elapsed() };
2644
- }
2645
- try {
2646
- await navigate(tabId, loginUrl);
2647
- await new Promise((r) => setTimeout(r, 2000));
2648
- let emailRef = await detectEmailInput(tabId);
2649
- if (!emailRef) {
2650
- const regUrl = loginUrl.replace(/login|signin|sign-in/i, "register").replace(/register/i, "signup");
2651
- if (regUrl !== loginUrl) {
2652
- await navigate(tabId, regUrl);
2653
- await new Promise((r) => setTimeout(r, 2000));
2654
- emailRef = await detectEmailInput(tabId);
2655
- }
2656
- }
2657
- if (!emailRef) {
2658
- log("autonomous-login", `no email input found on ${loginUrl}`);
2659
- return { success: false, method: "failed", domain: targetDomain, cookies_stored: 0, error: "no email input field detected", duration_ms: elapsed() };
2660
- }
2661
- let agentEmail;
2662
- try {
2663
- agentEmail = await emailProvider.getAddress(targetDomain);
2664
- } catch (err) {
2665
- return { success: false, method: "failed", domain: targetDomain, cookies_stored: 0, error: `Email provider (${emailProvider.name}) failed: ${err}`, duration_ms: elapsed() };
2666
- }
2667
- log("autonomous-login", `filling email: ${agentEmail} (via ${emailProvider.name}) into ${emailRef}`);
2668
- await fill(tabId, emailRef, agentEmail);
2669
- const submitRef = await detectSubmitButton(tabId);
2670
- if (submitRef) {
2671
- await click(tabId, submitRef);
2672
- } else {
2673
- await evaluate(tabId, `document.querySelector('${emailRef}')?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))`);
2674
- }
2675
- await new Promise((r) => setTimeout(r, POST_SUBMIT_SETTLE_MS));
2676
- const otpRef = await detectOtpInput(tabId);
2677
- if (otpRef) {
2678
- log("autonomous-login", `OTP input detected (${otpRef}), waiting for verification email via ${emailProvider.name}...`);
2679
- const verificationEmail = await emailProvider.waitForMessage(targetDomain, OTP_FILL_TIMEOUT_MS);
2680
- const otp = verificationEmail ? extractOtpFromEmail(verificationEmail.text) : null;
2681
- if (!otp) {
2682
- return { success: false, method: "failed", domain: targetDomain, email: agentEmail, cookies_stored: 0, error: "OTP email not received within timeout", duration_ms: elapsed() };
2683
- }
2684
- log("autonomous-login", `filling OTP: ${otp}`);
2685
- await fill(tabId, otpRef, otp);
2686
- const otpSubmitRef = await detectSubmitButton(tabId);
2687
- if (otpSubmitRef) {
2688
- await click(tabId, otpSubmitRef);
2689
- }
2690
- await new Promise((r) => setTimeout(r, POST_SUBMIT_SETTLE_MS));
2691
- } else {
2692
- log("autonomous-login", `no OTP input, trying magic link flow via ${emailProvider.name}...`);
2693
- const verificationEmail = await emailProvider.waitForMessage(targetDomain, OTP_FILL_TIMEOUT_MS);
2694
- const link = verificationEmail ? extractVerificationLink(verificationEmail.text, verificationEmail.html) : null;
2695
- if (link) {
2696
- log("autonomous-login", `navigating to magic link`);
2697
- await navigate(tabId, link);
2698
- await new Promise((r) => setTimeout(r, POST_SUBMIT_SETTLE_MS));
2699
- } else {
2700
- log("autonomous-login", `no magic link received, checking if already authenticated...`);
2701
- }
2702
- }
2703
- const success = await checkLoginSuccess(tabId, targetDomain);
2704
- if (success) {
2705
- const cookies = await getCookies(tabId);
2706
- const domainCookies = cookies.filter((c) => isDomainMatch(c.domain, targetDomain));
2707
- const storableCookies = domainCookies.map((c) => ({
2708
- name: c.name,
2709
- value: c.value,
2710
- domain: c.domain,
2711
- path: c.path,
2712
- secure: c.secure,
2713
- httpOnly: c.httpOnly,
2714
- sameSite: c.sameSite,
2715
- expires: c.expires
2716
- }));
2717
- const vaultKey = `auth:${getRegistrableDomain(targetDomain)}`;
2718
- await storeCredential(vaultKey, JSON.stringify({ cookies: storableCookies }));
2719
- await saveAuthProfileBestEffort(tabId, targetDomain, "autonomous_login");
2720
- log("autonomous-login", `login succeeded for ${targetDomain} — stored ${storableCookies.length} cookies (${elapsed()}ms)`);
2721
- return {
2722
- success: true,
2723
- method: otpRef ? "agent-mail-otp" : "agent-mail-link",
2724
- domain: targetDomain,
2725
- email: agentEmail,
2726
- cookies_stored: storableCookies.length,
2727
- duration_ms: elapsed()
2728
- };
2729
- }
2730
- return { success: false, method: "failed", domain: targetDomain, email: agentEmail, cookies_stored: 0, error: "login flow completed but no auth cookies detected", duration_ms: elapsed() };
2731
- } finally {}
2732
- }
2733
- var OTP_FILL_TIMEOUT_MS = 120000, POST_SUBMIT_SETTLE_MS = 3000;
2734
- var init_autonomous_login = __esm(async () => {
2735
- init_client2();
2736
- init_logger();
2737
- init_email_provider();
2738
- init_domain();
2739
- await __promiseAll([
2740
- init_agent_mail(),
2741
- init_auth(),
2742
- init_vault()
2743
- ]);
1477
+ await init_vault();
2744
1478
  });
2745
1479
 
2746
1480
  // ../../src/auth/runtime.ts
@@ -2802,18 +1536,6 @@ class LocalAuthRuntime {
2802
1536
  return true;
2803
1537
  }
2804
1538
  } catch {}
2805
- const url = loginUrl ?? `https://${domain}/login`;
2806
- try {
2807
- const result = await autonomousLogin(url, domain);
2808
- if (result.success) {
2809
- this.setSession(domain, "autonomous-login", 3600000);
2810
- log("auth-runtime", `loginIfNeeded resolved via autonomous login for ${domain} (${result.method}, ${result.duration_ms}ms)`);
2811
- return true;
2812
- }
2813
- log("auth-runtime", `autonomous login failed for ${domain}: ${result.error}`);
2814
- } catch (err) {
2815
- log("auth-runtime", `autonomous login error for ${domain}: ${err instanceof Error ? err.message : err}`);
2816
- }
2817
1539
  return false;
2818
1540
  }
2819
1541
  setSession(domain, token, ttlMs = 3600000) {
@@ -2823,10 +1545,7 @@ class LocalAuthRuntime {
2823
1545
  var authRuntime;
2824
1546
  var init_runtime = __esm(async () => {
2825
1547
  init_logger();
2826
- await __promiseAll([
2827
- init_auth(),
2828
- init_autonomous_login()
2829
- ]);
1548
+ await init_auth();
2830
1549
  authRuntime = new LocalAuthRuntime;
2831
1550
  });
2832
1551
 
@@ -3032,7 +1751,6 @@ var init_execution = __esm(async () => {
3032
1751
  init_vault(),
3033
1752
  init_auth(),
3034
1753
  init_runtime(),
3035
- init_autonomous_login(),
3036
1754
  init_indexer(),
3037
1755
  init_orchestrator()
3038
1756
  ]);
@@ -3358,186 +2076,6 @@ function checkWalletConfigured2() {
3358
2076
  }
3359
2077
  var init_wallet2 = () => {};
3360
2078
 
3361
- // ../../src/auth/bootstrap-agentmail.ts
3362
- var exports_bootstrap_agentmail = {};
3363
- __export(exports_bootstrap_agentmail, {
3364
- bootstrapAgentMailKey: () => bootstrapAgentMailKey
3365
- });
3366
- import os6 from "node:os";
3367
- import fs2 from "node:fs";
3368
- import path9 from "node:path";
3369
- async function bootstrapAgentMailKey() {
3370
- log("bootstrap-agentmail", "starting — opening console.agentmail.to");
3371
- try {
3372
- await start();
3373
- const tabId = await getDefaultTab();
3374
- await networkEnable(tabId);
3375
- await importBrowserCookiesIntoTab(tabId, "agentmail.to");
3376
- await importBrowserCookiesIntoTab(tabId, "clerk.agentmail.to");
3377
- await importBrowserCookiesIntoTab(tabId, "github.com");
3378
- await navigate(tabId, CONSOLE_URL);
3379
- await new Promise((r) => setTimeout(r, SETTLE_MS));
3380
- let currentUrl = await getCurrentUrl(tabId);
3381
- let onDashboard = typeof currentUrl === "string" && currentUrl.includes("/dashboard");
3382
- if (!onDashboard) {
3383
- log("bootstrap-agentmail", "not logged in — looking for social OAuth");
3384
- const clickResult = await evaluate(tabId, `(() => {
3385
- // Clerk's social button class pattern
3386
- const socialBtns = document.querySelectorAll('.cl-socialButtonsBlockButton, [data-provider]');
3387
- for (const btn of socialBtns) {
3388
- const text = (btn.textContent || '').toLowerCase();
3389
- const provider = btn.getAttribute('data-provider') || '';
3390
- // Match any social provider — Google, GitHub, etc.
3391
- if (text || provider) {
3392
- btn.click();
3393
- return 'clicked:' + (provider || text.slice(0, 20));
3394
- }
3395
- }
3396
- // Fallback: look for any button with a known OAuth provider name
3397
- const btns = document.querySelectorAll('button, a');
3398
- for (const btn of btns) {
3399
- const text = (btn.textContent || '').toLowerCase();
3400
- if (text.includes('google') || text.includes('github') || text.includes('continue with')) {
3401
- btn.click();
3402
- return 'clicked:' + text.slice(0, 30);
3403
- }
3404
- }
3405
- return 'not_found';
3406
- })()`);
3407
- const clickStr = typeof clickResult === "string" ? clickResult : String(clickResult);
3408
- log("bootstrap-agentmail", `social button result: ${clickStr}`);
3409
- if (clickStr.startsWith("clicked")) {
3410
- log("bootstrap-agentmail", "clicked social OAuth — waiting for redirect");
3411
- const start2 = Date.now();
3412
- while (Date.now() - start2 < AUTH_TIMEOUT_MS) {
3413
- await new Promise((r) => setTimeout(r, 2000));
3414
- currentUrl = await getCurrentUrl(tabId);
3415
- if (typeof currentUrl === "string" && currentUrl.includes("/dashboard")) {
3416
- onDashboard = true;
3417
- log("bootstrap-agentmail", "OAuth completed — on dashboard");
3418
- break;
3419
- }
3420
- }
3421
- }
3422
- if (!onDashboard) {
3423
- log("bootstrap-agentmail", "could not auto-login — manual setup needed");
3424
- const pageInfo = await evaluate(tabId, `JSON.stringify({
3425
- url: window.location.href,
3426
- title: document.title,
3427
- visible_buttons: Array.from(document.querySelectorAll('button,a'))
3428
- .map(e => (e.textContent || '').trim().slice(0, 30))
3429
- .filter(t => t)
3430
- .slice(0, 10),
3431
- })`).catch(() => "{}");
3432
- return {
3433
- success: false,
3434
- method: "manual",
3435
- error: `Could not auto-login to AgentMail console. Page state: ${pageInfo}. Sign up at https://console.agentmail.to and create an API key manually, then: export AGENTMAIL_API_KEY=<key>`
3436
- };
3437
- }
3438
- }
3439
- log("bootstrap-agentmail", "on dashboard — navigating to API keys");
3440
- await navigate(tabId, `${DASHBOARD_URL}/api-keys`);
3441
- await new Promise((r) => setTimeout(r, SETTLE_MS));
3442
- const createResult = await evaluate(tabId, `(() => {
3443
- const btns = document.querySelectorAll('button, a');
3444
- for (const btn of btns) {
3445
- const text = (btn.textContent || '').toLowerCase();
3446
- if (text.includes('create') && (text.includes('key') || text.includes('api'))) {
3447
- btn.click();
3448
- return 'clicked';
3449
- }
3450
- }
3451
- return 'not_found';
3452
- })()`);
3453
- if (createResult === "clicked") {
3454
- await new Promise((r) => setTimeout(r, 2000));
3455
- await evaluate(tabId, `(() => {
3456
- const input = document.querySelector('input[placeholder*="name" i], input[name*="name" i], input[type="text"]');
3457
- if (input) {
3458
- input.value = 'unbrowse-agent';
3459
- input.dispatchEvent(new Event('input', { bubbles: true }));
3460
- input.dispatchEvent(new Event('change', { bubbles: true }));
3461
- }
3462
- })()`);
3463
- await evaluate(tabId, `(() => {
3464
- const btns = document.querySelectorAll('button');
3465
- for (const btn of btns) {
3466
- const text = (btn.textContent || '').toLowerCase();
3467
- if (text.includes('create') || text.includes('confirm') || text.includes('generate')) {
3468
- btn.click();
3469
- return 'clicked';
3470
- }
3471
- }
3472
- })()`);
3473
- await new Promise((r) => setTimeout(r, 2000));
3474
- }
3475
- const apiKey = await evaluate(tabId, `(() => {
3476
- // Look for the key in common patterns
3477
- // 1. Input/textarea with the key value
3478
- const inputs = document.querySelectorAll('input[readonly], input[type="text"], textarea, code, pre');
3479
- for (const el of inputs) {
3480
- const val = el.value || el.textContent || '';
3481
- // AgentMail keys typically start with 'am_' or are long alphanumeric strings
3482
- if (/^(am_|sk_|key_)/.test(val) || (val.length > 30 && /^[a-zA-Z0-9_-]+$/.test(val))) {
3483
- return val.trim();
3484
- }
3485
- }
3486
- // 2. Look in the page text for key patterns
3487
- const text = document.body.innerText;
3488
- const match = text.match(/(am_[a-zA-Z0-9_-]{20,}|sk_[a-zA-Z0-9_-]{20,})/);
3489
- if (match) return match[1];
3490
- return null;
3491
- })()`);
3492
- if (apiKey && typeof apiKey === "string" && apiKey.length > 10) {
3493
- log("bootstrap-agentmail", `extracted API key: ${apiKey.substring(0, 8)}...`);
3494
- await storeCredential("agentmail:api_key", apiKey);
3495
- persistApiKeyToShell(apiKey);
3496
- process.env.AGENTMAIL_API_KEY = apiKey;
3497
- return {
3498
- success: true,
3499
- api_key: apiKey,
3500
- method: onDashboard ? "existing-session" : "github-oauth"
3501
- };
3502
- }
3503
- return {
3504
- success: false,
3505
- error: "Reached dashboard but could not extract API key. Create one manually at https://console.agentmail.to/dashboard/api-keys"
3506
- };
3507
- } finally {
3508
- try {
3509
- await stop();
3510
- } catch {}
3511
- }
3512
- }
3513
- function persistApiKeyToShell(apiKey) {
3514
- const shell = process.env.SHELL ?? "/bin/zsh";
3515
- const rcFile = shell.includes("zsh") ? path9.join(os6.homedir(), ".zshrc") : path9.join(os6.homedir(), ".bashrc");
3516
- try {
3517
- const content = fs2.existsSync(rcFile) ? fs2.readFileSync(rcFile, "utf-8") : "";
3518
- if (content.includes("AGENTMAIL_API_KEY")) {
3519
- log("bootstrap-agentmail", `${rcFile} already has AGENTMAIL_API_KEY — not overwriting`);
3520
- return;
3521
- }
3522
- fs2.appendFileSync(rcFile, `
3523
- # AgentMail API key (added by unbrowse setup)
3524
- export AGENTMAIL_API_KEY="${apiKey}"
3525
- `);
3526
- log("bootstrap-agentmail", `wrote AGENTMAIL_API_KEY to ${rcFile}`);
3527
- } catch (err) {
3528
- log("bootstrap-agentmail", `failed to write to ${rcFile}: ${err}`);
3529
- }
3530
- }
3531
- var CONSOLE_URL = "https://console.agentmail.to", DASHBOARD_URL = "https://console.agentmail.to/dashboard", SETTLE_MS = 3000, AUTH_TIMEOUT_MS = 120000;
3532
- var init_bootstrap_agentmail = __esm(async () => {
3533
- init_client2();
3534
- init_logger();
3535
- await __promiseAll([
3536
- init_vault(),
3537
- init_auth()
3538
- ]);
3539
- });
3540
-
3541
2079
  // ../../src/version.ts
3542
2080
  var exports_version = {};
3543
2081
  __export(exports_version, {
@@ -3663,210 +2201,6 @@ var init_version2 = __esm(() => {
3663
2201
  RELEASE_MANIFEST_SIGNATURE2 = BUILD_RELEASE_MANIFEST_SIGNATURE?.trim() || "";
3664
2202
  });
3665
2203
 
3666
- // ../../src/auth/agent-mail.ts
3667
- var exports_agent_mail2 = {};
3668
- __export(exports_agent_mail2, {
3669
- waitForVerificationEmail: () => waitForVerificationEmail2,
3670
- tryAgentMailAuth: () => tryAgentMailAuth2,
3671
- isAgentMailAvailable: () => isAgentMailAvailable2,
3672
- getOrCreateSiteInbox: () => getOrCreateSiteInbox2,
3673
- extractVerificationLink: () => extractVerificationLink2,
3674
- extractOtpFromEmail: () => extractOtpFromEmail2,
3675
- autonomousEmailLogin: () => autonomousEmailLogin2
3676
- });
3677
- import { AgentMailClient as AgentMailClient2 } from "agentmail";
3678
- function getClient2() {
3679
- if (_client2)
3680
- return _client2;
3681
- const apiKey = process.env.AGENTMAIL_API_KEY;
3682
- if (!apiKey)
3683
- throw new Error("AGENTMAIL_API_KEY not set — cannot use agent mail");
3684
- _client2 = new AgentMailClient2({ apiKey });
3685
- return _client2;
3686
- }
3687
- async function getStoredInbox2(domain) {
3688
- const key = `${VAULT_PREFIX2}${getRegistrableDomain(domain)}`;
3689
- const raw = await getCredential(key);
3690
- if (!raw)
3691
- return null;
3692
- try {
3693
- return JSON.parse(raw);
3694
- } catch {
3695
- return null;
3696
- }
3697
- }
3698
- async function storeInbox2(domain, inbox) {
3699
- const key = `${VAULT_PREFIX2}${getRegistrableDomain(domain)}`;
3700
- await storeCredential(key, JSON.stringify(inbox));
3701
- log("agent-mail", `stored inbox ${inbox.email} for ${domain}`);
3702
- }
3703
- async function getOrCreateSiteInbox2(domain) {
3704
- const stored = await getStoredInbox2(domain);
3705
- if (stored) {
3706
- log("agent-mail", `reusing stored inbox ${stored.email} for ${domain}`);
3707
- return { inboxId: stored.inboxId, email: stored.email };
3708
- }
3709
- const client = getClient2();
3710
- const safeDomain = getRegistrableDomain(domain).replace(/[^a-z0-9-]/gi, "-").toLowerCase();
3711
- const username = `unbrowse-${safeDomain}`;
3712
- const clientId = `unbrowse-login-${safeDomain}`;
3713
- try {
3714
- const inbox = await client.inboxes.create({
3715
- username,
3716
- domain: "agentmail.to",
3717
- displayName: `Unbrowse Agent - ${domain}`,
3718
- clientId
3719
- });
3720
- const result = { inboxId: inbox.inboxId, email: inbox.email };
3721
- await storeInbox2(domain, { ...result, domain, createdAt: new Date().toISOString() });
3722
- log("agent-mail", `created inbox ${inbox.email} for ${domain}`);
3723
- return result;
3724
- } catch (err) {
3725
- log("agent-mail", `create failed (likely exists), listing to find match: ${err instanceof Error ? err.message : err}`);
3726
- try {
3727
- const list = await client.inboxes.list({ limit: 100 });
3728
- const match = list.inboxes.find((i) => i.clientId === clientId || i.email === `${username}@agentmail.to`);
3729
- if (match) {
3730
- const result = { inboxId: match.inboxId, email: match.email };
3731
- await storeInbox2(domain, { ...result, domain, createdAt: new Date().toISOString() });
3732
- return result;
3733
- }
3734
- } catch {}
3735
- throw new Error(`Failed to create or find AgentMail inbox for ${domain}`);
3736
- }
3737
- }
3738
- async function waitForVerificationEmail2(inboxId, fromDomain, timeoutMs = DEFAULT_TIMEOUT_MS2) {
3739
- const client = getClient2();
3740
- const start2 = Date.now();
3741
- const normalizedDomain = fromDomain.toLowerCase();
3742
- log("agent-mail", `polling inbox ${inboxId} for email from *@${normalizedDomain} (timeout: ${timeoutMs}ms)`);
3743
- while (Date.now() - start2 < timeoutMs) {
3744
- try {
3745
- const response = await client.inboxes.messages.list(inboxId, {
3746
- limit: 10,
3747
- labels: ["received"]
3748
- });
3749
- for (const item of response.messages ?? []) {
3750
- const msg = await client.inboxes.messages.get(inboxId, item.messageId);
3751
- const from = typeof msg.from === "string" ? msg.from : (msg.from ?? []).join(", ");
3752
- if (from.toLowerCase().includes(normalizedDomain)) {
3753
- log("agent-mail", `found verification email from ${from}: "${msg.subject}"`);
3754
- return {
3755
- messageId: msg.messageId,
3756
- threadId: msg.threadId,
3757
- subject: msg.subject ?? "",
3758
- from,
3759
- text: msg.extractedText ?? msg.text ?? "",
3760
- html: msg.extractedHtml ?? msg.html ?? "",
3761
- receivedAt: msg.createdAt
3762
- };
3763
- }
3764
- }
3765
- } catch (err) {
3766
- log("agent-mail", `poll error: ${err instanceof Error ? err.message : err}`);
3767
- }
3768
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS2));
3769
- }
3770
- log("agent-mail", `timeout waiting for email from ${normalizedDomain}`);
3771
- return null;
3772
- }
3773
- function extractOtpFromEmail2(text) {
3774
- const codePhrase = text.match(/(?:verification|confirm|security|login|one[- ]time|otp)\s*(?:code|pin|number)[:\s]+(\w{4,8})/i);
3775
- if (codePhrase)
3776
- return codePhrase[1];
3777
- const codeIs = text.match(/(?:code|otp|pin)[:\s]+(\w{4,8})/i);
3778
- if (codeIs)
3779
- return codeIs[1];
3780
- const six = text.match(/\b(\d{6})\b/);
3781
- if (six)
3782
- return six[1];
3783
- const four = text.match(/\b(\d{4})\b/);
3784
- if (four)
3785
- return four[1];
3786
- return null;
3787
- }
3788
- function extractVerificationLink2(text, html) {
3789
- const htmlLink = html.match(/href="(https?:\/\/[^"]*(?:verify|confirm|activate|magic|token|auth|callback|validate|approve|email)[^"]*)"/i);
3790
- if (htmlLink)
3791
- return htmlLink[1];
3792
- const textLink = text.match(/(https?:\/\/\S*(?:verify|confirm|activate|magic|token|auth|callback|validate|approve|email)\S*)/i);
3793
- if (textLink)
3794
- return textLink[1];
3795
- const anyHtmlLink = html.match(/href="(https?:\/\/[^"]+)"/);
3796
- if (anyHtmlLink)
3797
- return anyHtmlLink[1];
3798
- const anyTextLink = text.match(/(https?:\/\/\S+)/);
3799
- if (anyTextLink)
3800
- return anyTextLink[1];
3801
- return null;
3802
- }
3803
- async function autonomousEmailLogin2(domain) {
3804
- const inbox = await getOrCreateSiteInbox2(domain);
3805
- const client = getClient2();
3806
- log("agent-mail", `session ready for ${domain} — email: ${inbox.email}`);
3807
- return {
3808
- email: inbox.email,
3809
- inboxId: inbox.inboxId,
3810
- domain,
3811
- waitForOtp: async (timeoutMs = DEFAULT_TIMEOUT_MS2) => {
3812
- const email = await waitForVerificationEmail2(inbox.inboxId, domain, timeoutMs);
3813
- if (!email)
3814
- return null;
3815
- const otp = extractOtpFromEmail2(email.text);
3816
- if (otp)
3817
- log("agent-mail", `extracted OTP: ${otp} from ${email.subject}`);
3818
- return otp;
3819
- },
3820
- waitForLink: async (timeoutMs = DEFAULT_TIMEOUT_MS2) => {
3821
- const email = await waitForVerificationEmail2(inbox.inboxId, domain, timeoutMs);
3822
- if (!email)
3823
- return null;
3824
- const link = extractVerificationLink2(email.text, email.html);
3825
- if (link)
3826
- log("agent-mail", `extracted verification link from ${email.subject}`);
3827
- return link;
3828
- },
3829
- waitForEmail: (timeoutMs = DEFAULT_TIMEOUT_MS2) => waitForVerificationEmail2(inbox.inboxId, domain, timeoutMs),
3830
- sendEmail: async (to, subject, body) => {
3831
- const result = await client.inboxes.messages.send(inbox.inboxId, {
3832
- to: [to],
3833
- subject,
3834
- text: body
3835
- });
3836
- log("agent-mail", `sent email to ${to}: "${subject}" (${result.messageId})`);
3837
- return { messageId: result.messageId, threadId: result.threadId };
3838
- },
3839
- replyTo: async (messageId, body) => {
3840
- const result = await client.inboxes.messages.reply(inbox.inboxId, messageId, {
3841
- text: body
3842
- });
3843
- log("agent-mail", `replied to ${messageId} (${result.messageId})`);
3844
- return { messageId: result.messageId, threadId: result.threadId };
3845
- }
3846
- };
3847
- }
3848
- function isAgentMailAvailable2() {
3849
- return Boolean(process.env.AGENTMAIL_API_KEY);
3850
- }
3851
- async function tryAgentMailAuth2(domain) {
3852
- if (!isAgentMailAvailable2()) {
3853
- log("agent-mail", `skipping — AGENTMAIL_API_KEY not set`);
3854
- return null;
3855
- }
3856
- try {
3857
- return await autonomousEmailLogin2(domain);
3858
- } catch (err) {
3859
- log("agent-mail", `auth failed for ${domain}: ${err instanceof Error ? err.message : err}`);
3860
- return null;
3861
- }
3862
- }
3863
- var POLL_INTERVAL_MS2 = 3000, DEFAULT_TIMEOUT_MS2 = 90000, _client2 = null, VAULT_PREFIX2 = "agentmail:";
3864
- var init_agent_mail2 = __esm(async () => {
3865
- init_logger();
3866
- init_domain();
3867
- await init_vault();
3868
- });
3869
-
3870
2204
  // ../../src/auth/browser-cookies.ts
3871
2205
  var exports_browser_cookies2 = {};
3872
2206
  __export(exports_browser_cookies2, {
@@ -5907,11 +4241,7 @@ async function runSetup(options) {
5907
4241
  browser_engine: browser,
5908
4242
  opencode: writeOpenCodeCommand(options?.opencode ?? "auto", cwd),
5909
4243
  update_hints: configureUpdateHintHooks(import.meta.url, installSource),
5910
- wallet,
5911
- agent_mail: {
5912
- configured: Boolean(process.env.AGENTMAIL_API_KEY),
5913
- message: process.env.AGENTMAIL_API_KEY ? "AgentMail configured — autonomous email login enabled." : "AgentMail not configured. Set AGENTMAIL_API_KEY to enable autonomous email login/registration. Get a key at https://agentmail.to"
5914
- }
4244
+ wallet
5915
4245
  };
5916
4246
  }
5917
4247
 
@@ -6115,8 +4445,8 @@ function parseArgs(argv) {
6115
4445
  }
6116
4446
  return { command, args: positional, flags };
6117
4447
  }
6118
- async function api2(method, path10, body) {
6119
- let target = `${BASE_URL}${path10}`;
4448
+ async function api2(method, path9, body) {
4449
+ let target = `${BASE_URL}${path9}`;
6120
4450
  let requestBody = body;
6121
4451
  if (method === "GET" && body && typeof body === "object") {
6122
4452
  const params = new URLSearchParams;
@@ -6446,8 +4776,8 @@ async function cmdResolve(flags) {
6446
4776
  throw error;
6447
4777
  }
6448
4778
  }
6449
- function drillPath(data, path10) {
6450
- const segments = path10.split(/\./).flatMap((s) => {
4779
+ function drillPath(data, path9) {
4780
+ const segments = path9.split(/\./).flatMap((s) => {
6451
4781
  const m = s.match(/^(.+)\[\]$/);
6452
4782
  return m ? [m[1], "[]"] : [s];
6453
4783
  });
@@ -6474,9 +4804,9 @@ function drillPath(data, path10) {
6474
4804
  }
6475
4805
  return values;
6476
4806
  }
6477
- function resolveDotPath(obj, path10) {
4807
+ function resolveDotPath(obj, path9) {
6478
4808
  let cur = obj;
6479
- for (const key of path10.split(".")) {
4809
+ for (const key of path9.split(".")) {
6480
4810
  if (cur == null || typeof cur !== "object")
6481
4811
  return;
6482
4812
  cur = cur[key];
@@ -6493,8 +4823,8 @@ function applyExtract(items, extractSpec) {
6493
4823
  return items.map((item) => {
6494
4824
  const row = {};
6495
4825
  let hasValue = false;
6496
- for (const { alias, path: path10 } of fields) {
6497
- const val = resolveDotPath(item, path10);
4826
+ for (const { alias, path: path9 } of fields) {
4827
+ const val = resolveDotPath(item, path9);
6498
4828
  row[alias] = val ?? null;
6499
4829
  if (val != null)
6500
4830
  hasValue = true;
@@ -6823,11 +5153,6 @@ async function cmdSetup(flags) {
6823
5153
  properties: { command: "setup" }
6824
5154
  });
6825
5155
  info("Running setup checks");
6826
- try {
6827
- await ensureRegistered({ promptForEmail: false, exitOnFailure: false });
6828
- } catch (err) {
6829
- info(`[setup] background registration issue: ${err instanceof Error ? err.message : err}`);
6830
- }
6831
5156
  const report = await runSetup({
6832
5157
  cwd: process.cwd(),
6833
5158
  opencode: normalizeSetupScope(flags.opencode),
@@ -6856,21 +5181,6 @@ async function cmdSetup(flags) {
6856
5181
  info("Set up a wallet to start earning:");
6857
5182
  info(" npx @crossmint/lobster-cli setup");
6858
5183
  }
6859
- if (!report.agent_mail.configured) {
6860
- info("AgentMail not configured \u2014 attempting to bootstrap via console.agentmail.to...");
6861
- try {
6862
- const { bootstrapAgentMailKey: bootstrapAgentMailKey2 } = await init_bootstrap_agentmail().then(() => exports_bootstrap_agentmail);
6863
- const result = await bootstrapAgentMailKey2();
6864
- if (result.success) {
6865
- info(`AgentMail API key obtained (${result.method}) \u2014 autonomous email login enabled`);
6866
- report.agent_mail.configured = true;
6867
- } else {
6868
- info(`AgentMail bootstrap: ${result.error}`);
6869
- }
6870
- } catch (err) {
6871
- info(`AgentMail bootstrap failed: ${err instanceof Error ? err.message : err}`);
6872
- }
6873
- }
6874
5184
  const hasGcloud = (() => {
6875
5185
  try {
6876
5186
  const { existsSync: existsSync19 } = __require("fs");
@@ -6881,16 +5191,8 @@ async function cmdSetup(flags) {
6881
5191
  return false;
6882
5192
  }
6883
5193
  })();
6884
- const emailProviders = [];
6885
- if (hasGcloud)
6886
- emailProviders.push("Gmail (via GWS)");
6887
- if (report.agent_mail.configured)
6888
- emailProviders.push("AgentMail");
6889
- if (emailProviders.length > 0) {
6890
- info(`Email providers: ${emailProviders.join(", ")} \u2014 autonomous login enabled`);
6891
- } else {
6892
- info("No email provider configured \u2014 agents can't auto-register on gated sites.");
6893
- info("Options: export AGENTMAIL_API_KEY=<key> (https://agentmail.to) or gcloud auth login (Gmail)");
5194
+ if (hasGcloud) {
5195
+ info("Email provider: Gmail (via GWS) \u2014 autonomous login enabled");
6894
5196
  }
6895
5197
  await recordInstallTelemetryEvent("setup", {
6896
5198
  hostType,
@@ -6918,9 +5220,6 @@ async function cmdSetup(flags) {
6918
5220
  process.exit(1);
6919
5221
  return;
6920
5222
  }
6921
- if (!getApiKey()) {
6922
- await ensureRegistered({ promptForEmail: true });
6923
- }
6924
5223
  try {
6925
5224
  await ensureLocalServer(BASE_URL, false, import.meta.url);
6926
5225
  report.server = { started: true, base_url: BASE_URL };
@@ -7034,7 +5333,8 @@ var CLI_REFERENCE = {
7034
5333
  { name: "flywheel", usage: "[--json] [--pretty]", desc: "Flywheel pulse: funnel, credits, index health, economics, conversions" },
7035
5334
  { name: "earnings", usage: "[--json]", desc: "Show your credit balance, earnings from indexing, and spending" },
7036
5335
  { name: "corpus-test", usage: "--url <url> [--id <id>] [--retries N]", desc: "Capture a single URL with retry logic; keeps best result across N attempts" },
7037
- { name: "corpus-run", usage: "--corpus <file> --out <file> [--retries N]", desc: "Run corpus-test over all cases in a corpus JSON file and write a comparable snapshot" }
5336
+ { name: "corpus-run", usage: "--corpus <file> --out <file> [--retries N]", desc: "Run corpus-test over all cases in a corpus JSON file and write a comparable snapshot" },
5337
+ { name: "register", usage: "[--no-prompt]", desc: "Optional: register an API key to publish skills, check earnings, and access backend analytics" }
7038
5338
  ],
7039
5339
  globalFlags: [
7040
5340
  { flag: "--pretty", desc: "Indented JSON output" },
@@ -7627,76 +5927,17 @@ async function cmdSync(flags) {
7627
5927
  async function cmdClose(flags) {
7628
5928
  output(await api2("POST", "/v1/browse/close", typeof flags.session === "string" ? { session_id: flags.session } : undefined), false);
7629
5929
  }
7630
- async function cmdLoginAuto(args, flags) {
7631
- const urlOrDomain = args[0] ?? flags.url ?? flags.domain;
7632
- if (!urlOrDomain)
7633
- return die("usage: unbrowse login-auto <domain-or-url> [--wait-otp | --wait-link | --send-to <email>]");
7634
- const domain = (() => {
7635
- try {
7636
- return new URL(urlOrDomain.startsWith("http") ? urlOrDomain : `https://${urlOrDomain}`).hostname.replace(/^www\./, "");
7637
- } catch {
7638
- return urlOrDomain;
7639
- }
7640
- })();
7641
- const { autonomousEmailLogin: autonomousEmailLogin3, isAgentMailAvailable: isAgentMailAvailable3 } = await init_agent_mail2().then(() => exports_agent_mail2);
7642
- if (!isAgentMailAvailable3()) {
7643
- info(`[login-auto] AGENTMAIL_API_KEY not set \u2014 attempting auto-bootstrap via console.agentmail.to`);
7644
- try {
7645
- const { bootstrapAgentMailKey: bootstrapAgentMailKey2 } = await init_bootstrap_agentmail().then(() => exports_bootstrap_agentmail);
7646
- const bootstrap = await bootstrapAgentMailKey2();
7647
- if (bootstrap.success && bootstrap.api_key) {
7648
- process.env.AGENTMAIL_API_KEY = bootstrap.api_key;
7649
- info(`[login-auto] bootstrapped AgentMail key via ${bootstrap.method ?? "browser"}`);
7650
- } else {
7651
- return die(`AGENTMAIL_API_KEY not set and bootstrap failed: ${bootstrap.error ?? "unknown"}. Get a key at https://agentmail.to and run: export AGENTMAIL_API_KEY=<key>`);
7652
- }
7653
- } catch (err) {
7654
- const msg = err instanceof Error ? err.message : String(err);
7655
- return die(`AGENTMAIL_API_KEY not set and bootstrap errored: ${msg}. Get a key at https://agentmail.to and run: export AGENTMAIL_API_KEY=<key>`);
7656
- }
7657
- }
7658
- info(`[login-auto] creating agent email for ${domain}...`);
7659
- const session = await autonomousEmailLogin3(domain);
7660
- info(`[login-auto] email: ${session.email}`);
7661
- const sendTo = flags["send-to"];
7662
- if (sendTo) {
7663
- const subject = flags.subject ?? `Verify ${domain}`;
7664
- const body = flags.body ?? `This is an automated verification email from Unbrowse agent for ${domain}.`;
7665
- info(`[login-auto] sending email to ${sendTo}...`);
7666
- const result = await session.sendEmail(sendTo, subject, body);
7667
- output({ email: session.email, sent_to: sendTo, message_id: result.messageId, domain });
5930
+ async function cmdRegister(flags) {
5931
+ if (getApiKey()) {
5932
+ info("Already registered. API key loaded from env or ~/.unbrowse/config.json");
7668
5933
  return;
7669
5934
  }
7670
- if (flags["wait-otp"]) {
7671
- const timeout = Number(flags.timeout) || 90000;
7672
- info(`[login-auto] waiting for OTP from ${domain} (${Math.round(timeout / 1000)}s timeout)...`);
7673
- const otp = await session.waitForOtp(timeout);
7674
- if (otp) {
7675
- info(`[login-auto] OTP: ${otp}`);
7676
- output({ email: session.email, otp, domain });
7677
- } else {
7678
- info(`[login-auto] no OTP received`);
7679
- output({ email: session.email, otp: null, domain, error: "timeout" });
7680
- }
7681
- return;
7682
- }
7683
- if (flags["wait-link"]) {
7684
- const timeout = Number(flags.timeout) || 90000;
7685
- info(`[login-auto] waiting for verification link from ${domain} (${Math.round(timeout / 1000)}s timeout)...`);
7686
- const link = await session.waitForLink(timeout);
7687
- if (link) {
7688
- info(`[login-auto] link: ${link}`);
7689
- output({ email: session.email, link, domain });
7690
- } else {
7691
- info(`[login-auto] no verification link received`);
7692
- output({ email: session.email, link: null, domain, error: "timeout" });
7693
- }
7694
- return;
5935
+ await ensureRegistered({ promptForEmail: !flags["no-prompt"], exitOnFailure: false });
5936
+ if (getApiKey()) {
5937
+ info("Registration complete. You can now publish skills and check earnings.");
5938
+ } else {
5939
+ info("Registration skipped or failed. Unbrowse still works locally \u2014 publish/earnings are disabled.");
7695
5940
  }
7696
- info(`[login-auto] use this email to register/login on ${domain}`);
7697
- info(`[login-auto] then: unbrowse login-auto ${domain} --wait-otp`);
7698
- info(`[login-auto] or: unbrowse login-auto ${domain} --wait-link`);
7699
- output({ email: session.email, inbox_id: session.inboxId, domain });
7700
5941
  }
7701
5942
  async function cmdSessionsScan(flags) {
7702
5943
  const domain = flags.domain;
@@ -7997,8 +6238,8 @@ async function main() {
7997
6238
  return cmdEarnings(flags);
7998
6239
  if (command === "sessions-scan")
7999
6240
  return cmdSessionsScan(flags);
8000
- if (command === "login-auto")
8001
- return cmdLoginAuto(args, flags);
6241
+ if (command === "register")
6242
+ return cmdRegister(flags);
8002
6243
  const KNOWN_COMMANDS = new Set([
8003
6244
  "health",
8004
6245
  "mcp",
@@ -8051,7 +6292,7 @@ async function main() {
8051
6292
  "corpus-run",
8052
6293
  "sessions-scan",
8053
6294
  "cache-clear",
8054
- "login-auto"
6295
+ "register"
8055
6296
  ]);
8056
6297
  if (!KNOWN_COMMANDS.has(command)) {
8057
6298
  const pack = findSitePack(command);
@@ -8161,8 +6402,8 @@ async function main() {
8161
6402
  return cmdCorpusRun(flags);
8162
6403
  case "sessions-scan":
8163
6404
  return cmdSessionsScan(flags);
8164
- case "login-auto":
8165
- return cmdLoginAuto(args, flags);
6405
+ case "register":
6406
+ return cmdRegister(flags);
8166
6407
  default:
8167
6408
  info(`Unknown command: ${command}`);
8168
6409
  printHelp();