@agentcamo/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import chalk from "chalk";
5
+ import { createSession, buildProxyUrl, deleteSession } from "./session.js";
6
+ import { SessionTUI } from "./tui.js";
7
+ import { JsonEmitter } from "./json-output.js";
8
+ import { registerCleanup, setSession, setDockerContainer } from "./cleanup.js";
9
+ import { swapCountry } from "./geo-swap.js";
10
+ import { dockerPreflight, startSidecar, waitForHealth, verifyExitIpViaSocks, CONTAINER_NAME, } from "./docker.js";
11
+ import { verifyExitIp } from "./ip-verify.js";
12
+ import { BANNER } from "./banner.js";
13
+ const argv = await yargs(hideBin(process.argv))
14
+ .scriptName("agentcamo")
15
+ .usage("$0 [options]")
16
+ .option("mode", {
17
+ type: "string",
18
+ choices: ["residential", "mobile", "socks5", "wireguard"],
19
+ default: "residential",
20
+ describe: "Proxy mode",
21
+ })
22
+ .option("chain", {
23
+ type: "string",
24
+ choices: ["evm", "solana"],
25
+ describe: "Payment chain (auto-detects from env)",
26
+ })
27
+ .option("country", {
28
+ type: "string",
29
+ default: "us",
30
+ describe: "ISO 3166-1 alpha-2 country code",
31
+ })
32
+ .option("json", {
33
+ type: "boolean",
34
+ default: false,
35
+ describe: "Machine-readable JSONL output",
36
+ })
37
+ .option("verbose", {
38
+ type: "boolean",
39
+ default: false,
40
+ describe: "Show full response bodies",
41
+ })
42
+ .fail((msg, err) => {
43
+ process.stderr.write(`ERROR: ${msg || err?.message}\n`);
44
+ process.exit(1);
45
+ })
46
+ .strict()
47
+ .parse();
48
+ const EVM_KEY = process.env.AGENT_PRIVATE_KEY;
49
+ const SOL_KEY = process.env.SOLANA_PRIVATE_KEY;
50
+ const chain = argv.chain ??
51
+ (SOL_KEY && !EVM_KEY ? "solana" : EVM_KEY && !SOL_KEY ? "evm" : "evm");
52
+ if (!argv.chain && EVM_KEY && SOL_KEY) {
53
+ process.stderr.write(chalk.yellow("Warning: Both AGENT_PRIVATE_KEY and SOLANA_PRIVATE_KEY are set. " +
54
+ `Defaulting to ${chain}. Use --chain to specify.\n`));
55
+ }
56
+ const privateKey = chain === "evm" ? EVM_KEY : SOL_KEY;
57
+ if (!privateKey) {
58
+ const envVar = chain === "evm" ? "AGENT_PRIVATE_KEY" : "SOLANA_PRIVATE_KEY";
59
+ process.stderr.write(chalk.red(`ERROR: ${envVar} environment variable is required for ${chain} chain\n`));
60
+ process.exit(1);
61
+ }
62
+ const mode = argv.mode;
63
+ const jsonMode = argv.json;
64
+ registerCleanup();
65
+ // --- Docker preflight for wireguard mode ---
66
+ if (mode === "wireguard") {
67
+ try {
68
+ dockerPreflight();
69
+ }
70
+ catch (err) {
71
+ const msg = err instanceof Error ? err.message : String(err);
72
+ process.stderr.write(chalk.red(msg + "\n"));
73
+ process.exit(1);
74
+ }
75
+ }
76
+ // --- Banner ---
77
+ if (!jsonMode && process.stderr.isTTY) {
78
+ process.stderr.write(BANNER + "\n\n");
79
+ }
80
+ // --- Create session ---
81
+ const tui = jsonMode || !process.stderr.isTTY ? null : new SessionTUI();
82
+ const json = jsonMode ? new JsonEmitter() : null;
83
+ if (tui) {
84
+ tui.setStatus("Creating session...");
85
+ tui.start();
86
+ }
87
+ if (json) {
88
+ json.emit("status", {
89
+ message: `Creating ${mode} session in ${argv.country.toUpperCase()}...`,
90
+ });
91
+ }
92
+ let sessionResponse;
93
+ try {
94
+ sessionResponse = await createSession({
95
+ mode,
96
+ country: argv.country,
97
+ chain,
98
+ privateKey,
99
+ });
100
+ }
101
+ catch (err) {
102
+ const msg = err instanceof Error ? err.message : String(err);
103
+ if (tui) {
104
+ tui.setError(msg);
105
+ tui.stop();
106
+ }
107
+ if (json) {
108
+ json.emit("error", { message: msg });
109
+ }
110
+ process.exit(1);
111
+ }
112
+ const proxyUrl = buildProxyUrl(sessionResponse, mode);
113
+ // Use the server's ttl_remaining to compute expiry relative to our local clock.
114
+ // This avoids relying on server-absolute expiresAt which may differ due to clock skew.
115
+ const adjustedExpiresAt = new Date(Date.now() + sessionResponse.ttl_remaining * 1000);
116
+ const state = {
117
+ sessionId: sessionResponse.sessionId,
118
+ token: sessionResponse.token,
119
+ country: sessionResponse.country,
120
+ mode,
121
+ exitIp: null,
122
+ expiresAt: adjustedExpiresAt,
123
+ ttl: sessionResponse.ttl,
124
+ startedAt: Date.now(),
125
+ proxyUrl,
126
+ response: sessionResponse,
127
+ };
128
+ setSession(state.sessionId, state.token);
129
+ if (tui) {
130
+ tui.setSession(state);
131
+ tui.setStatus("Verifying exit IP...");
132
+ }
133
+ if (json) {
134
+ json.emit("session_created", {
135
+ sessionId: state.sessionId,
136
+ country: state.country,
137
+ mode: state.mode,
138
+ expiresAt: sessionResponse.expiresAt,
139
+ ttl: state.ttl,
140
+ proxyUrl: state.proxyUrl,
141
+ });
142
+ }
143
+ if (argv.verbose) {
144
+ const redacted = { ...sessionResponse };
145
+ if ("token" in redacted)
146
+ redacted.token = "[REDACTED]";
147
+ if ("proxyAuth" in redacted)
148
+ redacted.proxyAuth = "[REDACTED]";
149
+ if (sessionResponse.mode === "wireguard") {
150
+ redacted.wireguard_config = {
151
+ ...sessionResponse.wireguard_config,
152
+ client_private_key: "[REDACTED]",
153
+ };
154
+ }
155
+ if (sessionResponse.mode === "socks5") {
156
+ redacted.socks5 = {
157
+ ...sessionResponse.socks5,
158
+ password: "[REDACTED]",
159
+ };
160
+ }
161
+ process.stderr.write(chalk.dim(JSON.stringify(redacted, null, 2) + "\n"));
162
+ }
163
+ // --- Verify exit IP ---
164
+ // Fly.io requires TLS on port 8080 (handlers = ["tls"]), so plain curl -x http:// fails.
165
+ // Use Node.js tls.connect + CONNECT tunnel (same pattern as scripts/kayak-arbitrage.ts).
166
+ try {
167
+ let exitIp;
168
+ if (mode === "wireguard" && sessionResponse.mode === "wireguard") {
169
+ // Start Docker sidecar
170
+ if (tui)
171
+ tui.setStatus("Starting WireGuard sidecar...");
172
+ const wgConfigB64 = Buffer.from(JSON.stringify(sessionResponse.wireguard_config)).toString("base64");
173
+ startSidecar(wgConfigB64);
174
+ setDockerContainer(CONTAINER_NAME);
175
+ if (tui)
176
+ tui.setStatus("Waiting for tunnel...");
177
+ await waitForHealth();
178
+ if (tui)
179
+ tui.setStatus("Verifying exit IP through tunnel...");
180
+ exitIp = verifyExitIpViaSocks();
181
+ }
182
+ else {
183
+ exitIp = await verifyExitIp(state, sessionResponse);
184
+ }
185
+ state.exitIp = exitIp;
186
+ if (tui) {
187
+ tui.setExitIp(exitIp);
188
+ tui.setStatus("");
189
+ }
190
+ if (json) {
191
+ json.emit("ip_verified", { exitIp });
192
+ }
193
+ }
194
+ catch {
195
+ if (tui)
196
+ tui.setStatus("Exit IP verification failed");
197
+ if (json)
198
+ json.emit("error", { message: "Exit IP verification failed" });
199
+ }
200
+ // --- Enable inline geo-swap input ---
201
+ if (tui) {
202
+ tui.onSwap(async (newCountry) => {
203
+ const result = await swapCountry(state.sessionId, state.token, newCountry);
204
+ state.country = result.country;
205
+ // Re-verify exit IP after swap — use SOCKS for WireGuard mode
206
+ try {
207
+ const newIp = state.mode === "wireguard"
208
+ ? verifyExitIpViaSocks()
209
+ : await verifyExitIp(state, sessionResponse);
210
+ state.exitIp = newIp;
211
+ return {
212
+ country: result.country,
213
+ exitIp: newIp,
214
+ cooldownExpiresAt: result.cooldownExpiresAt,
215
+ };
216
+ }
217
+ catch {
218
+ return {
219
+ country: result.country,
220
+ cooldownExpiresAt: result.cooldownExpiresAt,
221
+ };
222
+ }
223
+ });
224
+ tui.enableInput();
225
+ }
226
+ // --- Session timer (for --json mode expiry events) ---
227
+ if (json) {
228
+ let expiringEmitted = false;
229
+ const expiryCheckInterval = setInterval(() => {
230
+ const remaining = Math.max(0, (state.expiresAt.getTime() - Date.now()) / 1000);
231
+ if (remaining <= 30 && !expiringEmitted) {
232
+ expiringEmitted = true;
233
+ json.emit("session_expiring", { ttl_remaining: Math.floor(remaining) });
234
+ }
235
+ if (remaining <= 0) {
236
+ json.emit("session_expired", {});
237
+ clearInterval(expiryCheckInterval);
238
+ deleteSession(state.sessionId, state.token).finally(() => process.exit(0));
239
+ }
240
+ }, 1000);
241
+ }
242
+ // --- TUI keeps running until session expires or Ctrl+C ---
243
+ if (tui) {
244
+ // TUI render loop also auto-stops; this watcher handles cleanup (deleteSession)
245
+ await new Promise((resolve) => {
246
+ const check = setInterval(() => {
247
+ const remaining = (state.expiresAt.getTime() - Date.now()) / 1000;
248
+ if (remaining <= 0) {
249
+ clearInterval(check);
250
+ tui.setStatus("Session expired");
251
+ tui.stop();
252
+ process.stderr.write(chalk.dim("\nSession expired. Cleaning up...\n"));
253
+ deleteSession(state.sessionId, state.token).finally(() => {
254
+ resolve();
255
+ });
256
+ }
257
+ }, 1000);
258
+ });
259
+ }
260
+ // --- JSON mode: keep process alive until expiry ---
261
+ if (json) {
262
+ await new Promise(() => {
263
+ // Process stays alive via the expiryCheckInterval above
264
+ // Exits when session expires or SIGINT
265
+ });
266
+ }
267
+ // --- Headless mode (non-TTY, no --json): print proxy URL and keep alive ---
268
+ if (!tui && !json) {
269
+ process.stdout.write(proxyUrl + "\n");
270
+ await new Promise((resolve) => {
271
+ const check = setInterval(() => {
272
+ const remaining = (state.expiresAt.getTime() - Date.now()) / 1000;
273
+ if (remaining <= 0) {
274
+ clearInterval(check);
275
+ deleteSession(state.sessionId, state.token).finally(() => resolve());
276
+ }
277
+ }, 1000);
278
+ });
279
+ }
280
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ declare const CONTAINER_NAME = "agentcamo-tunnel";
2
+ declare const SOCKS_PORT = 1080;
3
+ export declare function dockerPreflight(): void;
4
+ export declare function startSidecar(wgConfigB64: string): string;
5
+ export declare function waitForHealth(maxWaitSecs?: number): Promise<void>;
6
+ export declare function verifyExitIpViaSocks(): string;
7
+ export { CONTAINER_NAME, SOCKS_PORT };
package/dist/docker.js ADDED
@@ -0,0 +1,106 @@
1
+ import { execSync } from "child_process";
2
+ import { writeFileSync, unlinkSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+ import { randomUUID } from "crypto";
6
+ const CONTAINER_NAME = "agentcamo-tunnel";
7
+ const IMAGE = "agentcamo/wg-sidecar:1";
8
+ const SOCKS_PORT = 1080; // host port
9
+ const HEALTH_PORT = 8081; // host port
10
+ const CONTAINER_SOCKS_PORT = 1080; // fixed internal port
11
+ const CONTAINER_HEALTH_PORT = 8081; // fixed internal port
12
+ export function dockerPreflight() {
13
+ try {
14
+ execSync("docker info >/dev/null 2>&1", { stdio: "pipe", timeout: 5000 });
15
+ }
16
+ catch {
17
+ throw new Error("Docker is required for wireguard mode but is not running. " +
18
+ "Start Docker and try again.");
19
+ }
20
+ // Check port conflict
21
+ try {
22
+ execSync(`lsof -i :${SOCKS_PORT} -sTCP:LISTEN >/dev/null 2>&1`, {
23
+ stdio: "pipe",
24
+ timeout: 3000,
25
+ });
26
+ throw new Error(`Port ${SOCKS_PORT} is already in use. ` +
27
+ `Stop the existing service or use a different mode.`);
28
+ }
29
+ catch (err) {
30
+ // lsof exits non-zero when port is NOT in use — that's what we want
31
+ if (err instanceof Error && err.message.includes("already in use"))
32
+ throw err;
33
+ }
34
+ }
35
+ export function startSidecar(wgConfigB64) {
36
+ // Remove stale container if exists
37
+ try {
38
+ execSync(`docker rm -f ${CONTAINER_NAME} 2>/dev/null`, { stdio: "pipe" });
39
+ }
40
+ catch {
41
+ /* none running */
42
+ }
43
+ // Write config to a temp env-file instead of passing via -e flag,
44
+ // which would expose the WireGuard private key in `ps aux` output.
45
+ const envFile = join(tmpdir(), `agentcamo-${randomUUID()}.env`);
46
+ try {
47
+ writeFileSync(envFile, `AGENT_CAMO_WG_CONFIG=${wgConfigB64}\n`, {
48
+ mode: 0o600,
49
+ });
50
+ const cmd = [
51
+ "docker run -d",
52
+ `--name ${CONTAINER_NAME}`,
53
+ `--env-file ${envFile}`,
54
+ `-p ${SOCKS_PORT}:${CONTAINER_SOCKS_PORT}`,
55
+ `-p ${HEALTH_PORT}:${CONTAINER_HEALTH_PORT}`,
56
+ IMAGE,
57
+ ].join(" ");
58
+ return execSync(cmd, { encoding: "utf-8", timeout: 30_000 }).trim();
59
+ }
60
+ finally {
61
+ try {
62
+ unlinkSync(envFile);
63
+ }
64
+ catch {
65
+ /* best-effort cleanup */
66
+ }
67
+ }
68
+ }
69
+ export async function waitForHealth(maxWaitSecs = 20) {
70
+ for (let i = 1; i <= maxWaitSecs; i++) {
71
+ try {
72
+ execSync(`curl -sf http://localhost:${HEALTH_PORT}/readyz`, {
73
+ stdio: "pipe",
74
+ timeout: 3000,
75
+ });
76
+ return;
77
+ }
78
+ catch {
79
+ await new Promise((r) => setTimeout(r, 1000));
80
+ }
81
+ }
82
+ // Dump container logs for debugging
83
+ let logs = "";
84
+ try {
85
+ logs = execSync(`docker logs ${CONTAINER_NAME} 2>&1`, {
86
+ encoding: "utf-8",
87
+ timeout: 5000,
88
+ });
89
+ }
90
+ catch {
91
+ /* best effort */
92
+ }
93
+ // Check if WG handshake succeeded despite health check failure
94
+ if (logs.includes("Received handshake response")) {
95
+ // Known issue: published image uses ICMP CheckAlive which fails through TCP-only tunnel
96
+ // But SOCKS5 TCP traffic works fine
97
+ return;
98
+ }
99
+ throw new Error(`Sidecar not ready after ${maxWaitSecs}s.\n` +
100
+ (logs ? `Container logs:\n${logs.slice(0, 500)}` : "No logs available."));
101
+ }
102
+ export function verifyExitIpViaSocks() {
103
+ return execSync(`curl -sf --max-time 10 --socks5-hostname localhost:${SOCKS_PORT} https://api.ipify.org`, { encoding: "utf-8", timeout: 15_000 }).trim();
104
+ }
105
+ export { CONTAINER_NAME, SOCKS_PORT };
106
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../src/docker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { LocationSwapResponse } from "./types.js";
2
+ export declare function swapCountry(sessionId: string, token: string, newCountry: string): Promise<LocationSwapResponse>;
@@ -0,0 +1,25 @@
1
+ import { API_URL } from "./session.js";
2
+ export async function swapCountry(sessionId, token, newCountry) {
3
+ const res = await fetch(`${API_URL}/sessions/${sessionId}/location`, {
4
+ method: "POST",
5
+ headers: {
6
+ "Content-Type": "application/json",
7
+ Authorization: `Bearer ${token}`,
8
+ },
9
+ body: JSON.stringify({ countryCode: newCountry }),
10
+ });
11
+ if (res.status === 429) {
12
+ const body = (await res.json());
13
+ const expiresAt = body.cooldownExpiresAt ?? "unknown";
14
+ throw new Error(`Rate limited: geo-swap cooldown active until ${expiresAt}`);
15
+ }
16
+ if (res.status === 400) {
17
+ throw new Error(`Invalid country code: ${newCountry}`);
18
+ }
19
+ if (!res.ok) {
20
+ const body = await res.text();
21
+ throw new Error(`Geo-swap failed (${res.status}): ${body}`);
22
+ }
23
+ return (await res.json());
24
+ }
25
+ //# sourceMappingURL=geo-swap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo-swap.js","sourceRoot":"","sources":["../src/geo-swap.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,KAAa,EACb,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,aAAa,SAAS,WAAW,EAAE;QACnE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAAC;IAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,IAAI,SAAS,CAAC;QACtD,MAAM,IAAI,KAAK,CACb,gDAAgD,SAAS,EAAE,CAC5D,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;AACpD,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { SessionState, CreateSessionResponse } from "./types.js";
2
+ /**
3
+ * Verify exit IP via HTTP forward proxy through the TLS tunnel.
4
+ *
5
+ * Fly.io requires TLS on proxy port 8080 (handlers = ["tls"]),
6
+ * so we tls.connect first, then send a plain HTTP forward proxy
7
+ * request with an absolute URI (http://api.ipify.org/?format=text).
8
+ *
9
+ * This avoids the CONNECT+inner-TLS path which requires BD's CA cert
10
+ * (BD residential/mobile zones MITM TLS on CONNECT tunnels).
11
+ */
12
+ export declare function verifyExitIp(state: SessionState, _response: CreateSessionResponse): Promise<string>;
@@ -0,0 +1,58 @@
1
+ import * as tls from "node:tls";
2
+ import * as nodeHttp from "node:http";
3
+ import { API_URL } from "./session.js";
4
+ const API_HOST = new URL(API_URL).hostname;
5
+ /**
6
+ * Verify exit IP via HTTP forward proxy through the TLS tunnel.
7
+ *
8
+ * Fly.io requires TLS on proxy port 8080 (handlers = ["tls"]),
9
+ * so we tls.connect first, then send a plain HTTP forward proxy
10
+ * request with an absolute URI (http://api.ipify.org/?format=text).
11
+ *
12
+ * This avoids the CONNECT+inner-TLS path which requires BD's CA cert
13
+ * (BD residential/mobile zones MITM TLS on CONNECT tunnels).
14
+ */
15
+ export function verifyExitIp(state, _response) {
16
+ return new Promise((resolve, reject) => {
17
+ const proxyAuth = Buffer.from(`${state.sessionId}:${state.token}`).toString("base64");
18
+ const timer = setTimeout(() => {
19
+ tlsSocket.destroy();
20
+ reject(new Error("IP verify timeout (15s)"));
21
+ }, 15_000);
22
+ const tlsSocket = tls.connect({ host: API_HOST, port: 8080, servername: API_HOST }, () => {
23
+ const req = nodeHttp.request({
24
+ createConnection: () => tlsSocket,
25
+ path: "http://api.ipify.org/?format=text",
26
+ method: "GET",
27
+ headers: {
28
+ "Proxy-Authorization": `Basic ${proxyAuth}`,
29
+ Host: "api.ipify.org",
30
+ },
31
+ }, (res) => {
32
+ let body = "";
33
+ res.on("data", (chunk) => {
34
+ body += chunk.toString();
35
+ });
36
+ res.on("end", () => {
37
+ clearTimeout(timer);
38
+ tlsSocket.destroy();
39
+ if (res.statusCode !== 200) {
40
+ reject(new Error(`IP verify failed: HTTP ${res.statusCode} — ${body.trim()}`));
41
+ return;
42
+ }
43
+ resolve(body.trim());
44
+ });
45
+ });
46
+ req.on("error", (err) => {
47
+ clearTimeout(timer);
48
+ reject(err);
49
+ });
50
+ req.end();
51
+ });
52
+ tlsSocket.on("error", (err) => {
53
+ clearTimeout(timer);
54
+ reject(err);
55
+ });
56
+ });
57
+ }
58
+ //# sourceMappingURL=ip-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip-verify.js","sourceRoot":"","sources":["../src/ip-verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,QAAQ,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;AAE3C;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,SAAgC;IAEhC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,CACzE,QAAQ,CACT,CAAC;QAEF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,SAAS,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAC/C,CAAC,EAAE,MAAM,CAAC,CAAC;QAEX,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAC3B,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EACpD,GAAG,EAAE;YACH,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAC1B;gBACE,gBAAgB,EAAE,GAAG,EAAE,CACrB,SAAiD;gBACnD,IAAI,EAAE,mCAAmC;gBACzC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,qBAAqB,EAAE,SAAS,SAAS,EAAE;oBAC3C,IAAI,EAAE,eAAe;iBACtB;aACF,EACD,CAAC,GAAG,EAAE,EAAE;gBACN,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC/B,IAAI,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC3B,CAAC,CAAC,CAAC;gBACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,SAAS,CAAC,OAAO,EAAE,CAAC;oBACpB,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;wBAC3B,MAAM,CACJ,IAAI,KAAK,CACP,0BAA0B,GAAG,CAAC,UAAU,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,CAC5D,CACF,CAAC;wBACF,OAAO;oBACT,CAAC;oBACD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvB,CAAC,CAAC,CAAC;YACL,CAAC,CACF,CAAC;YACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACtB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC,CACF,CAAC;QAEF,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC5B,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare class JsonEmitter {
2
+ emit(event: string, data: Record<string, unknown>): void;
3
+ }
@@ -0,0 +1,11 @@
1
+ export class JsonEmitter {
2
+ emit(event, data) {
3
+ const line = JSON.stringify({
4
+ event,
5
+ ts: new Date().toISOString(),
6
+ ...data,
7
+ });
8
+ process.stdout.write(line + "\n");
9
+ }
10
+ }
11
+ //# sourceMappingURL=json-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-output.js","sourceRoot":"","sources":["../src/json-output.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IACtB,IAAI,CAAC,KAAa,EAAE,IAA6B;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,KAAK;YACL,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,GAAG,IAAI;SACR,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACpC,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { CliMode, Chain, CreateSessionResponse } from "./types.js";
2
+ export declare const API_URL: string;
3
+ export declare function createSession(opts: {
4
+ mode: CliMode;
5
+ country: string;
6
+ chain: Chain;
7
+ privateKey: string;
8
+ }): Promise<CreateSessionResponse>;
9
+ export declare function buildRequestBody(mode: CliMode, country: string): Record<string, string>;
10
+ export declare function buildProxyUrl(response: CreateSessionResponse, mode: CliMode): string;
11
+ export declare function deleteSession(sessionId: string, token: string): Promise<void>;