@evident-ai/cli 0.1.6 → 0.2.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/chunk-MWOWXSOP.js +120 -0
- package/dist/chunk-MWOWXSOP.js.map +1 -0
- package/dist/config-J7LPYFVS.js +30 -0
- package/dist/config-J7LPYFVS.js.map +1 -0
- package/dist/index.js +572 -147
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
clearCredentials,
|
|
4
|
+
getApiUrlConfig,
|
|
5
|
+
getCredentials,
|
|
6
|
+
getTunnelUrlConfig,
|
|
7
|
+
setCredentials,
|
|
8
|
+
setEnvironment
|
|
9
|
+
} from "./chunk-MWOWXSOP.js";
|
|
2
10
|
|
|
3
11
|
// src/index.ts
|
|
4
12
|
import { Command } from "commander";
|
|
@@ -8,79 +16,6 @@ import open from "open";
|
|
|
8
16
|
import ora from "ora";
|
|
9
17
|
import chalk2 from "chalk";
|
|
10
18
|
|
|
11
|
-
// src/lib/config.ts
|
|
12
|
-
import Conf from "conf";
|
|
13
|
-
import { homedir } from "os";
|
|
14
|
-
import { join } from "path";
|
|
15
|
-
var environmentPresets = {
|
|
16
|
-
local: {
|
|
17
|
-
apiUrl: "http://localhost:3000/v1",
|
|
18
|
-
tunnelUrl: "ws://localhost:8787"
|
|
19
|
-
},
|
|
20
|
-
dev: {
|
|
21
|
-
apiUrl: "https://api.dev.evident.run/v1",
|
|
22
|
-
tunnelUrl: "wss://tunnel.dev.evident.run"
|
|
23
|
-
},
|
|
24
|
-
production: {
|
|
25
|
-
// Production URLs also have aliases: api.evident.run, tunnel.evident.run
|
|
26
|
-
apiUrl: "https://api.production.evident.run/v1",
|
|
27
|
-
tunnelUrl: "wss://tunnel.production.evident.run"
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
var defaults = environmentPresets.production;
|
|
31
|
-
var currentEnvironment = "production";
|
|
32
|
-
function setEnvironment(env) {
|
|
33
|
-
currentEnvironment = env;
|
|
34
|
-
}
|
|
35
|
-
function getEnvironment() {
|
|
36
|
-
const envVar = process.env.EVIDENT_ENV;
|
|
37
|
-
if (envVar && environmentPresets[envVar]) {
|
|
38
|
-
return envVar;
|
|
39
|
-
}
|
|
40
|
-
return currentEnvironment;
|
|
41
|
-
}
|
|
42
|
-
function getEnvConfig() {
|
|
43
|
-
return environmentPresets[getEnvironment()];
|
|
44
|
-
}
|
|
45
|
-
function getApiUrl() {
|
|
46
|
-
return process.env.EVIDENT_API_URL ?? getEnvConfig().apiUrl;
|
|
47
|
-
}
|
|
48
|
-
function getTunnelUrl() {
|
|
49
|
-
return process.env.EVIDENT_TUNNEL_URL ?? getEnvConfig().tunnelUrl;
|
|
50
|
-
}
|
|
51
|
-
var config = new Conf({
|
|
52
|
-
projectName: "evident",
|
|
53
|
-
projectSuffix: "",
|
|
54
|
-
defaults
|
|
55
|
-
});
|
|
56
|
-
var credentials = new Conf({
|
|
57
|
-
projectName: "evident",
|
|
58
|
-
projectSuffix: "",
|
|
59
|
-
configName: "credentials",
|
|
60
|
-
defaults: {}
|
|
61
|
-
});
|
|
62
|
-
function getApiUrlConfig() {
|
|
63
|
-
return getApiUrl();
|
|
64
|
-
}
|
|
65
|
-
function getTunnelUrlConfig() {
|
|
66
|
-
return getTunnelUrl();
|
|
67
|
-
}
|
|
68
|
-
function getCredentials() {
|
|
69
|
-
return {
|
|
70
|
-
token: credentials.get("token"),
|
|
71
|
-
user: credentials.get("user"),
|
|
72
|
-
expiresAt: credentials.get("expiresAt")
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function setCredentials(creds) {
|
|
76
|
-
if (creds.token) credentials.set("token", creds.token);
|
|
77
|
-
if (creds.user) credentials.set("user", creds.user);
|
|
78
|
-
if (creds.expiresAt) credentials.set("expiresAt", creds.expiresAt);
|
|
79
|
-
}
|
|
80
|
-
function clearCredentials() {
|
|
81
|
-
credentials.clear();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
19
|
// src/lib/api.ts
|
|
85
20
|
var ApiClient = class {
|
|
86
21
|
baseUrl;
|
|
@@ -188,15 +123,15 @@ async function getKeytar() {
|
|
|
188
123
|
return null;
|
|
189
124
|
}
|
|
190
125
|
}
|
|
191
|
-
async function storeToken(
|
|
126
|
+
async function storeToken(credentials) {
|
|
192
127
|
const keytar = await getKeytar();
|
|
193
128
|
if (keytar) {
|
|
194
|
-
await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(
|
|
129
|
+
await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, JSON.stringify(credentials));
|
|
195
130
|
} else {
|
|
196
131
|
setCredentials({
|
|
197
|
-
token:
|
|
198
|
-
user:
|
|
199
|
-
expiresAt:
|
|
132
|
+
token: credentials.token,
|
|
133
|
+
user: credentials.user,
|
|
134
|
+
expiresAt: credentials.expiresAt
|
|
200
135
|
});
|
|
201
136
|
}
|
|
202
137
|
}
|
|
@@ -397,8 +332,8 @@ async function login(options) {
|
|
|
397
332
|
|
|
398
333
|
// src/commands/logout.ts
|
|
399
334
|
async function logout() {
|
|
400
|
-
const
|
|
401
|
-
if (!
|
|
335
|
+
const credentials = await getToken();
|
|
336
|
+
if (!credentials) {
|
|
402
337
|
printWarning("You are not logged in.");
|
|
403
338
|
return;
|
|
404
339
|
}
|
|
@@ -409,16 +344,16 @@ async function logout() {
|
|
|
409
344
|
// src/commands/whoami.ts
|
|
410
345
|
import chalk3 from "chalk";
|
|
411
346
|
async function whoami() {
|
|
412
|
-
const
|
|
413
|
-
if (!
|
|
347
|
+
const credentials = await getToken();
|
|
348
|
+
if (!credentials) {
|
|
414
349
|
printError("Not logged in. Run the `login` command to authenticate.");
|
|
415
350
|
process.exit(1);
|
|
416
351
|
}
|
|
417
352
|
blank();
|
|
418
|
-
console.log(keyValue("User", chalk3.bold(
|
|
419
|
-
console.log(keyValue("User ID",
|
|
420
|
-
if (
|
|
421
|
-
const expiresAt = new Date(
|
|
353
|
+
console.log(keyValue("User", chalk3.bold(credentials.user.email)));
|
|
354
|
+
console.log(keyValue("User ID", credentials.user.id));
|
|
355
|
+
if (credentials.expiresAt) {
|
|
356
|
+
const expiresAt = new Date(credentials.expiresAt);
|
|
422
357
|
const now = /* @__PURE__ */ new Date();
|
|
423
358
|
if (expiresAt < now) {
|
|
424
359
|
console.log(keyValue("Status", chalk3.red("Token expired")));
|
|
@@ -436,6 +371,8 @@ async function whoami() {
|
|
|
436
371
|
import WebSocket from "ws";
|
|
437
372
|
import chalk4 from "chalk";
|
|
438
373
|
import ora2 from "ora";
|
|
374
|
+
import { execSync, spawn } from "child_process";
|
|
375
|
+
import { select } from "@inquirer/prompts";
|
|
439
376
|
|
|
440
377
|
// src/lib/telemetry.ts
|
|
441
378
|
var CLI_VERSION = process.env.npm_package_version || "unknown";
|
|
@@ -479,8 +416,8 @@ async function flushEvents() {
|
|
|
479
416
|
flushTimeout = null;
|
|
480
417
|
}
|
|
481
418
|
try {
|
|
482
|
-
const
|
|
483
|
-
if (!
|
|
419
|
+
const credentials = await getToken();
|
|
420
|
+
if (!credentials) {
|
|
484
421
|
return;
|
|
485
422
|
}
|
|
486
423
|
const apiUrl = getApiUrlConfig();
|
|
@@ -491,7 +428,7 @@ async function flushEvents() {
|
|
|
491
428
|
method: "POST",
|
|
492
429
|
headers: {
|
|
493
430
|
"Content-Type": "application/json",
|
|
494
|
-
Authorization: `Bearer ${
|
|
431
|
+
Authorization: `Bearer ${credentials.token}`
|
|
495
432
|
},
|
|
496
433
|
body: JSON.stringify({
|
|
497
434
|
events,
|
|
@@ -551,6 +488,173 @@ var EventTypes = {
|
|
|
551
488
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
552
489
|
var BASE_RECONNECT_DELAY = 500;
|
|
553
490
|
var MAX_ACTIVITY_LOG_ENTRIES = 10;
|
|
491
|
+
var CHUNK_THRESHOLD = 512 * 1024;
|
|
492
|
+
var CHUNK_SIZE = 768 * 1024;
|
|
493
|
+
var OPENCODE_PORT_RANGE = [4096, 4097, 4098, 4099, 4100];
|
|
494
|
+
function getProcessCwd(pid) {
|
|
495
|
+
const platform = process.platform;
|
|
496
|
+
try {
|
|
497
|
+
if (platform === "darwin") {
|
|
498
|
+
const output = execSync(`lsof -a -p ${pid} -d cwd -Fn 2>/dev/null`, {
|
|
499
|
+
encoding: "utf-8",
|
|
500
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
501
|
+
}).trim();
|
|
502
|
+
const lines = output.split("\n");
|
|
503
|
+
for (const line of lines) {
|
|
504
|
+
if (line.startsWith("n") && !line.startsWith("n ")) {
|
|
505
|
+
return line.slice(1);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
} else if (platform === "linux") {
|
|
509
|
+
const output = execSync(`readlink /proc/${pid}/cwd 2>/dev/null`, {
|
|
510
|
+
encoding: "utf-8",
|
|
511
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
512
|
+
}).trim();
|
|
513
|
+
if (output) return output;
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
519
|
+
function isPortInUse(port) {
|
|
520
|
+
const platform = process.platform;
|
|
521
|
+
try {
|
|
522
|
+
if (platform === "darwin" || platform === "linux") {
|
|
523
|
+
execSync(`lsof -i :${port} -sTCP:LISTEN 2>/dev/null`, {
|
|
524
|
+
encoding: "utf-8",
|
|
525
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
526
|
+
});
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
} catch {
|
|
530
|
+
}
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
function findAvailablePort(startPort, maxAttempts = 10) {
|
|
534
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
535
|
+
const port = startPort + i;
|
|
536
|
+
if (!isPortInUse(port)) {
|
|
537
|
+
return port;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
function findOpenCodeProcesses() {
|
|
543
|
+
const instances = [];
|
|
544
|
+
try {
|
|
545
|
+
const platform = process.platform;
|
|
546
|
+
if (platform === "darwin" || platform === "linux") {
|
|
547
|
+
let pids = [];
|
|
548
|
+
try {
|
|
549
|
+
const pgrepOutput = execSync('pgrep -f "opencode serve|opencode-serve"', {
|
|
550
|
+
encoding: "utf-8",
|
|
551
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
552
|
+
}).trim();
|
|
553
|
+
if (pgrepOutput) {
|
|
554
|
+
pids = pgrepOutput.split("\n").map((p) => parseInt(p.trim(), 10)).filter((p) => !isNaN(p));
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
try {
|
|
558
|
+
const psOutput = execSync('ps aux | grep -E "opencode (serve|--port)" | grep -v grep', {
|
|
559
|
+
encoding: "utf-8",
|
|
560
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
561
|
+
}).trim();
|
|
562
|
+
if (psOutput) {
|
|
563
|
+
for (const line of psOutput.split("\n")) {
|
|
564
|
+
const parts = line.trim().split(/\s+/);
|
|
565
|
+
if (parts.length >= 2) {
|
|
566
|
+
const pid = parseInt(parts[1], 10);
|
|
567
|
+
if (!isNaN(pid)) pids.push(pid);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} catch {
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
for (const pid of pids) {
|
|
575
|
+
try {
|
|
576
|
+
const lsofOutput = execSync(`lsof -Pan -p ${pid} -i TCP -sTCP:LISTEN 2>/dev/null`, {
|
|
577
|
+
encoding: "utf-8",
|
|
578
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
579
|
+
}).trim();
|
|
580
|
+
for (const line of lsofOutput.split("\n")) {
|
|
581
|
+
const portMatch = line.match(/:(\d+)\s+\(LISTEN\)/);
|
|
582
|
+
if (portMatch) {
|
|
583
|
+
const port = parseInt(portMatch[1], 10);
|
|
584
|
+
if (!isNaN(port) && !instances.some((i) => i.port === port)) {
|
|
585
|
+
const cwd = getProcessCwd(pid);
|
|
586
|
+
instances.push({ pid, port, cwd });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
} catch {
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
} catch {
|
|
595
|
+
}
|
|
596
|
+
return instances;
|
|
597
|
+
}
|
|
598
|
+
async function scanPortsForOpenCode() {
|
|
599
|
+
const instances = [];
|
|
600
|
+
const checks = OPENCODE_PORT_RANGE.map(async (port) => {
|
|
601
|
+
const health = await checkOpenCodeHealth(port);
|
|
602
|
+
if (health.healthy) {
|
|
603
|
+
let pid = 0;
|
|
604
|
+
try {
|
|
605
|
+
const lsofOutput = execSync(`lsof -ti :${port} -sTCP:LISTEN 2>/dev/null`, {
|
|
606
|
+
encoding: "utf-8",
|
|
607
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
608
|
+
}).trim();
|
|
609
|
+
if (lsofOutput) {
|
|
610
|
+
pid = parseInt(lsofOutput.split("\n")[0], 10) || 0;
|
|
611
|
+
}
|
|
612
|
+
} catch {
|
|
613
|
+
}
|
|
614
|
+
const cwd = pid ? getProcessCwd(pid) : void 0;
|
|
615
|
+
return { pid, port, cwd, version: health.version };
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
});
|
|
619
|
+
const results = await Promise.all(checks);
|
|
620
|
+
for (const result of results) {
|
|
621
|
+
if (result) {
|
|
622
|
+
instances.push(result);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return instances;
|
|
626
|
+
}
|
|
627
|
+
async function checkOpenCodeHealth(port) {
|
|
628
|
+
try {
|
|
629
|
+
const response = await fetch(`http://localhost:${port}/health`, {
|
|
630
|
+
signal: AbortSignal.timeout(2e3)
|
|
631
|
+
// 2 second timeout
|
|
632
|
+
});
|
|
633
|
+
if (!response.ok) {
|
|
634
|
+
return { healthy: false, error: `HTTP ${response.status}` };
|
|
635
|
+
}
|
|
636
|
+
const data = await response.json().catch(() => ({}));
|
|
637
|
+
return { healthy: true, version: data.version };
|
|
638
|
+
} catch (error2) {
|
|
639
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
640
|
+
return { healthy: false, error: message };
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function findHealthyOpenCodeInstances() {
|
|
644
|
+
const processes = findOpenCodeProcesses();
|
|
645
|
+
const healthy = [];
|
|
646
|
+
for (const proc of processes) {
|
|
647
|
+
const health = await checkOpenCodeHealth(proc.port);
|
|
648
|
+
if (health.healthy) {
|
|
649
|
+
healthy.push({ ...proc, version: health.version });
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (healthy.length === 0) {
|
|
653
|
+
const scanned = await scanPortsForOpenCode();
|
|
654
|
+
return scanned;
|
|
655
|
+
}
|
|
656
|
+
return healthy;
|
|
657
|
+
}
|
|
554
658
|
function logActivity(state, entry) {
|
|
555
659
|
const fullEntry = {
|
|
556
660
|
...entry,
|
|
@@ -603,38 +707,76 @@ function colorizeStatus(status) {
|
|
|
603
707
|
}
|
|
604
708
|
return status.toString();
|
|
605
709
|
}
|
|
710
|
+
var ANSI = {
|
|
711
|
+
saveCursor: "\x1B[s",
|
|
712
|
+
restoreCursor: "\x1B[u",
|
|
713
|
+
clearToEnd: "\x1B[J",
|
|
714
|
+
moveTo: (row) => `\x1B[${row};1H`,
|
|
715
|
+
moveUp: (n) => `\x1B[${n}A`
|
|
716
|
+
};
|
|
717
|
+
var STATUS_DISPLAY_HEIGHT = 20;
|
|
606
718
|
function displayStatus(state) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
719
|
+
const lines = [];
|
|
720
|
+
lines.push(chalk4.bold("Evident Tunnel"));
|
|
721
|
+
lines.push(chalk4.dim("\u2500".repeat(60)));
|
|
722
|
+
lines.push("");
|
|
723
|
+
if (state.sandboxName) {
|
|
724
|
+
lines.push(` Sandbox: ${state.sandboxName}`);
|
|
725
|
+
}
|
|
726
|
+
lines.push(` ID: ${state.sandboxId ?? "Unknown"}`);
|
|
727
|
+
lines.push("");
|
|
611
728
|
if (state.connected) {
|
|
612
|
-
|
|
613
|
-
console.log(` Sandbox: ${state.sandboxId ?? "Unknown"}`);
|
|
614
|
-
if (state.sandboxName) {
|
|
615
|
-
console.log(` Name: ${state.sandboxName}`);
|
|
616
|
-
}
|
|
729
|
+
lines.push(` ${chalk4.green("\u25CF")} Tunnel: ${chalk4.green("Connected to Evident")}`);
|
|
617
730
|
} else {
|
|
618
|
-
console.log(` ${chalk4.yellow("\u25CB")} Status: ${chalk4.yellow("Reconnecting...")}`);
|
|
619
731
|
if (state.reconnectAttempt > 0) {
|
|
620
|
-
|
|
732
|
+
lines.push(
|
|
733
|
+
` ${chalk4.yellow("\u25CB")} Tunnel: ${chalk4.yellow(`Reconnecting... (attempt ${state.reconnectAttempt})`)}`
|
|
734
|
+
);
|
|
735
|
+
} else {
|
|
736
|
+
lines.push(` ${chalk4.yellow("\u25CB")} Tunnel: ${chalk4.yellow("Connecting...")}`);
|
|
621
737
|
}
|
|
622
738
|
}
|
|
623
|
-
|
|
739
|
+
if (state.opencodeConnected) {
|
|
740
|
+
const version = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
|
|
741
|
+
lines.push(` ${chalk4.green("\u25CF")} OpenCode: ${chalk4.green(`Running${version}`)}`);
|
|
742
|
+
} else {
|
|
743
|
+
lines.push(` ${chalk4.red("\u25CB")} OpenCode: ${chalk4.red("Not connected")}`);
|
|
744
|
+
}
|
|
745
|
+
lines.push("");
|
|
624
746
|
if (state.activityLog.length > 0) {
|
|
625
|
-
|
|
747
|
+
lines.push(chalk4.bold(" Activity:"));
|
|
626
748
|
for (const entry of state.activityLog) {
|
|
627
|
-
|
|
749
|
+
lines.push(formatActivityEntry(entry, state.verbose));
|
|
628
750
|
}
|
|
629
751
|
} else {
|
|
630
|
-
|
|
752
|
+
lines.push(chalk4.dim(" No activity yet. Waiting for requests..."));
|
|
631
753
|
}
|
|
632
|
-
|
|
633
|
-
|
|
754
|
+
lines.push("");
|
|
755
|
+
lines.push(chalk4.dim("\u2500".repeat(60)));
|
|
634
756
|
if (state.verbose) {
|
|
635
|
-
|
|
757
|
+
lines.push(chalk4.dim(" Verbose mode: ON (request/response bodies will be logged)"));
|
|
758
|
+
}
|
|
759
|
+
lines.push(chalk4.dim(" Press Ctrl+C to disconnect"));
|
|
760
|
+
while (lines.length < STATUS_DISPLAY_HEIGHT) {
|
|
761
|
+
lines.push("");
|
|
762
|
+
}
|
|
763
|
+
if (!state.displayInitialized) {
|
|
764
|
+
console.log("");
|
|
765
|
+
console.log(chalk4.dim("\u2550".repeat(60)));
|
|
766
|
+
console.log("");
|
|
767
|
+
for (const line of lines) {
|
|
768
|
+
console.log(line);
|
|
769
|
+
}
|
|
770
|
+
state.displayInitialized = true;
|
|
771
|
+
} else {
|
|
772
|
+
process.stdout.write(ANSI.moveUp(STATUS_DISPLAY_HEIGHT + 3));
|
|
773
|
+
console.log(chalk4.dim("\u2550".repeat(60)));
|
|
774
|
+
console.log("");
|
|
775
|
+
for (const line of lines) {
|
|
776
|
+
process.stdout.write("\x1B[2K");
|
|
777
|
+
console.log(line);
|
|
778
|
+
}
|
|
636
779
|
}
|
|
637
|
-
console.log(chalk4.dim(" Press Ctrl+C to disconnect"));
|
|
638
780
|
}
|
|
639
781
|
function displayError(_state, error2, details) {
|
|
640
782
|
blank();
|
|
@@ -715,13 +857,23 @@ async function forwardToOpenCode(port, request, requestId, state) {
|
|
|
715
857
|
});
|
|
716
858
|
let body;
|
|
717
859
|
const contentType = response.headers.get("Content-Type");
|
|
718
|
-
|
|
719
|
-
|
|
860
|
+
const text = await response.text();
|
|
861
|
+
if (!text || text.length === 0) {
|
|
862
|
+
body = null;
|
|
863
|
+
} else if (contentType?.includes("application/json")) {
|
|
864
|
+
try {
|
|
865
|
+
body = JSON.parse(text);
|
|
866
|
+
} catch {
|
|
867
|
+
body = text;
|
|
868
|
+
}
|
|
720
869
|
} else {
|
|
721
|
-
body =
|
|
870
|
+
body = text;
|
|
722
871
|
}
|
|
723
872
|
const durationMs = Date.now() - startTime;
|
|
724
873
|
state.pendingRequests.delete(requestId);
|
|
874
|
+
if (!state.opencodeConnected) {
|
|
875
|
+
state.opencodeConnected = true;
|
|
876
|
+
}
|
|
725
877
|
const lastEntry = state.activityLog[state.activityLog.length - 1];
|
|
726
878
|
if (lastEntry && lastEntry.requestId === requestId) {
|
|
727
879
|
lastEntry.type = "response";
|
|
@@ -762,6 +914,7 @@ async function forwardToOpenCode(port, request, requestId, state) {
|
|
|
762
914
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
763
915
|
const durationMs = Date.now() - startTime;
|
|
764
916
|
state.pendingRequests.delete(requestId);
|
|
917
|
+
state.opencodeConnected = false;
|
|
765
918
|
logActivity(state, {
|
|
766
919
|
type: "error",
|
|
767
920
|
method: request.method,
|
|
@@ -788,6 +941,123 @@ async function forwardToOpenCode(port, request, requestId, state) {
|
|
|
788
941
|
};
|
|
789
942
|
}
|
|
790
943
|
}
|
|
944
|
+
async function subscribeToOpenCodeEvents(port, subscriptionId, ws, state) {
|
|
945
|
+
const url = `http://localhost:${port}/event`;
|
|
946
|
+
logActivity(state, {
|
|
947
|
+
type: "info",
|
|
948
|
+
message: `Starting event subscription ${subscriptionId.slice(0, 8)}`
|
|
949
|
+
});
|
|
950
|
+
displayStatus(state);
|
|
951
|
+
const abortController = new AbortController();
|
|
952
|
+
state.activeEventSubscriptions.set(subscriptionId, abortController);
|
|
953
|
+
try {
|
|
954
|
+
const response = await fetch(url, {
|
|
955
|
+
headers: { Accept: "text/event-stream" },
|
|
956
|
+
signal: abortController.signal
|
|
957
|
+
});
|
|
958
|
+
if (!response.ok) {
|
|
959
|
+
throw new Error(`Failed to connect to OpenCode events: ${response.status}`);
|
|
960
|
+
}
|
|
961
|
+
if (!response.body) {
|
|
962
|
+
throw new Error("No response body");
|
|
963
|
+
}
|
|
964
|
+
const reader = response.body.getReader();
|
|
965
|
+
const decoder = new TextDecoder();
|
|
966
|
+
let buffer = "";
|
|
967
|
+
while (true) {
|
|
968
|
+
const { done, value } = await reader.read();
|
|
969
|
+
if (done) {
|
|
970
|
+
ws.send(JSON.stringify({ type: "event_end", id: subscriptionId }));
|
|
971
|
+
break;
|
|
972
|
+
}
|
|
973
|
+
buffer += decoder.decode(value, { stream: true });
|
|
974
|
+
const lines = buffer.split("\n");
|
|
975
|
+
buffer = lines.pop() || "";
|
|
976
|
+
for (const line of lines) {
|
|
977
|
+
if (line.startsWith("data: ")) {
|
|
978
|
+
try {
|
|
979
|
+
const event = JSON.parse(line.slice(6));
|
|
980
|
+
ws.send(JSON.stringify({ type: "event", id: subscriptionId, event }));
|
|
981
|
+
} catch {
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
} catch (error2) {
|
|
987
|
+
if (abortController.signal.aborted) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
991
|
+
logActivity(state, {
|
|
992
|
+
type: "error",
|
|
993
|
+
error: `Event subscription failed: ${message}`
|
|
994
|
+
});
|
|
995
|
+
displayStatus(state);
|
|
996
|
+
ws.send(JSON.stringify({ type: "event_error", id: subscriptionId, error: message }));
|
|
997
|
+
} finally {
|
|
998
|
+
state.activeEventSubscriptions.delete(subscriptionId);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
function cancelEventSubscription(subscriptionId, state) {
|
|
1002
|
+
const controller = state.activeEventSubscriptions.get(subscriptionId);
|
|
1003
|
+
if (controller) {
|
|
1004
|
+
controller.abort();
|
|
1005
|
+
state.activeEventSubscriptions.delete(subscriptionId);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function sendResponse(ws, requestId, response) {
|
|
1009
|
+
const bodyStr = JSON.stringify(response.body ?? null);
|
|
1010
|
+
const bodyBytes = Buffer.from(bodyStr, "utf-8");
|
|
1011
|
+
if (bodyBytes.length < CHUNK_THRESHOLD) {
|
|
1012
|
+
ws.send(
|
|
1013
|
+
JSON.stringify({
|
|
1014
|
+
type: "response",
|
|
1015
|
+
id: requestId,
|
|
1016
|
+
payload: response
|
|
1017
|
+
})
|
|
1018
|
+
);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
sendResponseAsChunks(ws, requestId, response, bodyBytes);
|
|
1022
|
+
}
|
|
1023
|
+
function sendResponseAsChunks(ws, requestId, response, bodyBytes) {
|
|
1024
|
+
const chunks = splitIntoChunks(bodyBytes, CHUNK_SIZE);
|
|
1025
|
+
ws.send(
|
|
1026
|
+
JSON.stringify({
|
|
1027
|
+
type: "response_start",
|
|
1028
|
+
id: requestId,
|
|
1029
|
+
total_chunks: chunks.length,
|
|
1030
|
+
total_size: bodyBytes.length,
|
|
1031
|
+
payload: {
|
|
1032
|
+
status: response.status,
|
|
1033
|
+
headers: response.headers
|
|
1034
|
+
}
|
|
1035
|
+
})
|
|
1036
|
+
);
|
|
1037
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1038
|
+
ws.send(
|
|
1039
|
+
JSON.stringify({
|
|
1040
|
+
type: "response_chunk",
|
|
1041
|
+
id: requestId,
|
|
1042
|
+
chunk_index: i,
|
|
1043
|
+
data: chunks[i].toString("base64")
|
|
1044
|
+
})
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
ws.send(
|
|
1048
|
+
JSON.stringify({
|
|
1049
|
+
type: "response_end",
|
|
1050
|
+
id: requestId
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
function splitIntoChunks(data, chunkSize) {
|
|
1055
|
+
const chunks = [];
|
|
1056
|
+
for (let i = 0; i < data.length; i += chunkSize) {
|
|
1057
|
+
chunks.push(data.subarray(i, i + chunkSize));
|
|
1058
|
+
}
|
|
1059
|
+
return chunks;
|
|
1060
|
+
}
|
|
791
1061
|
function getReconnectDelay(attempt) {
|
|
792
1062
|
const exponentialDelay = BASE_RECONNECT_DELAY * Math.pow(2, attempt);
|
|
793
1063
|
const jitter = Math.random() * 1e3;
|
|
@@ -882,13 +1152,17 @@ async function connect(token, sandboxId, port, state) {
|
|
|
882
1152
|
state.sandboxId ?? void 0
|
|
883
1153
|
);
|
|
884
1154
|
const response = await forwardToOpenCode(port, message.payload, message.id, state);
|
|
885
|
-
ws.
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1155
|
+
sendResponse(ws, message.id, response);
|
|
1156
|
+
}
|
|
1157
|
+
break;
|
|
1158
|
+
case "subscribe_events":
|
|
1159
|
+
if (message.id) {
|
|
1160
|
+
void subscribeToOpenCodeEvents(port, message.id, ws, state);
|
|
1161
|
+
}
|
|
1162
|
+
break;
|
|
1163
|
+
case "unsubscribe_events":
|
|
1164
|
+
if (message.id) {
|
|
1165
|
+
cancelEventSubscription(message.id, state);
|
|
892
1166
|
}
|
|
893
1167
|
break;
|
|
894
1168
|
}
|
|
@@ -973,34 +1247,40 @@ async function connect(token, sandboxId, port, state) {
|
|
|
973
1247
|
}
|
|
974
1248
|
async function tunnel(options) {
|
|
975
1249
|
const verbose = options.verbose ?? false;
|
|
976
|
-
const
|
|
977
|
-
if (!
|
|
1250
|
+
const credentials = await getToken();
|
|
1251
|
+
if (!credentials) {
|
|
1252
|
+
const cliName = (await import("./config-J7LPYFVS.js")).getCliName();
|
|
978
1253
|
telemetry.error(EventTypes.CLI_ERROR, "Not logged in", { command: "tunnel" });
|
|
979
|
-
printError(
|
|
1254
|
+
printError(`Not logged in. Run \`${cliName} login\` first.`);
|
|
980
1255
|
process.exit(1);
|
|
981
1256
|
}
|
|
982
1257
|
const port = options.port ?? 4096;
|
|
983
1258
|
const sandboxId = options.sandbox;
|
|
984
1259
|
if (!sandboxId) {
|
|
1260
|
+
const cliName = (await import("./config-J7LPYFVS.js")).getCliName();
|
|
985
1261
|
printError("--sandbox <id> is required");
|
|
986
1262
|
blank();
|
|
987
1263
|
console.log(chalk4.dim("To find your sandbox ID:"));
|
|
988
1264
|
console.log(chalk4.dim(" 1. Create a remote sandbox in the Evident web UI"));
|
|
989
1265
|
console.log(chalk4.dim(" 2. Copy the sandbox ID from the URL or settings"));
|
|
990
|
-
console.log(chalk4.dim(
|
|
1266
|
+
console.log(chalk4.dim(` 3. Run: ${cliName} tunnel --sandbox <id>`));
|
|
991
1267
|
blank();
|
|
992
1268
|
telemetry.error(EventTypes.CLI_ERROR, "Missing sandbox ID", { command: "tunnel" });
|
|
993
1269
|
process.exit(1);
|
|
994
1270
|
}
|
|
995
1271
|
const state = {
|
|
996
1272
|
connected: false,
|
|
1273
|
+
opencodeConnected: false,
|
|
1274
|
+
opencodeVersion: null,
|
|
997
1275
|
sandboxId,
|
|
998
1276
|
sandboxName: null,
|
|
999
1277
|
reconnectAttempt: 0,
|
|
1000
1278
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
1001
1279
|
activityLog: [],
|
|
1002
1280
|
pendingRequests: /* @__PURE__ */ new Map(),
|
|
1003
|
-
verbose
|
|
1281
|
+
verbose,
|
|
1282
|
+
displayInitialized: false,
|
|
1283
|
+
activeEventSubscriptions: /* @__PURE__ */ new Map()
|
|
1004
1284
|
};
|
|
1005
1285
|
telemetry.info(
|
|
1006
1286
|
EventTypes.CLI_COMMAND,
|
|
@@ -1022,7 +1302,7 @@ async function tunnel(options) {
|
|
|
1022
1302
|
message: "Validating sandbox..."
|
|
1023
1303
|
});
|
|
1024
1304
|
const validateSpinner = ora2("Validating sandbox...").start();
|
|
1025
|
-
const validation = await validateSandbox(
|
|
1305
|
+
const validation = await validateSandbox(credentials.token, sandboxId);
|
|
1026
1306
|
if (!validation.valid) {
|
|
1027
1307
|
validateSpinner.fail(`Sandbox validation failed: ${validation.error}`);
|
|
1028
1308
|
logActivity(state, {
|
|
@@ -1047,25 +1327,17 @@ async function tunnel(options) {
|
|
|
1047
1327
|
message: `Checking OpenCode on port ${port}...`
|
|
1048
1328
|
});
|
|
1049
1329
|
const opencodeSpinner = ora2("Checking OpenCode connection...").start();
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
sandboxId
|
|
1056
|
-
);
|
|
1057
|
-
const response = await fetch(`http://localhost:${port}/health`);
|
|
1058
|
-
if (!response.ok) {
|
|
1059
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
1060
|
-
}
|
|
1061
|
-
const healthData = await response.json().catch(() => ({}));
|
|
1062
|
-
const version = healthData.version ? ` (v${healthData.version})` : "";
|
|
1330
|
+
const healthCheck = await checkOpenCodeHealth(port);
|
|
1331
|
+
if (healthCheck.healthy) {
|
|
1332
|
+
state.opencodeConnected = true;
|
|
1333
|
+
state.opencodeVersion = healthCheck.version ?? null;
|
|
1334
|
+
const version = healthCheck.version ? ` (v${healthCheck.version})` : "";
|
|
1063
1335
|
telemetry.info(
|
|
1064
1336
|
EventTypes.OPENCODE_HEALTH_OK,
|
|
1065
1337
|
`OpenCode healthy on port ${port}`,
|
|
1066
1338
|
{
|
|
1067
1339
|
port,
|
|
1068
|
-
|
|
1340
|
+
version: healthCheck.version
|
|
1069
1341
|
},
|
|
1070
1342
|
sandboxId
|
|
1071
1343
|
);
|
|
@@ -1074,29 +1346,182 @@ async function tunnel(options) {
|
|
|
1074
1346
|
type: "info",
|
|
1075
1347
|
message: `OpenCode running on port ${port}${version}`
|
|
1076
1348
|
});
|
|
1077
|
-
}
|
|
1078
|
-
const errorMessage = error2 instanceof Error ? error2.message : "Unknown error";
|
|
1349
|
+
} else {
|
|
1079
1350
|
telemetry.warn(
|
|
1080
1351
|
EventTypes.OPENCODE_HEALTH_FAILED,
|
|
1081
|
-
`Could not connect to OpenCode: ${
|
|
1352
|
+
`Could not connect to OpenCode: ${healthCheck.error}`,
|
|
1082
1353
|
{
|
|
1083
1354
|
port,
|
|
1084
|
-
error:
|
|
1355
|
+
error: healthCheck.error
|
|
1085
1356
|
},
|
|
1086
1357
|
sandboxId
|
|
1087
1358
|
);
|
|
1088
1359
|
opencodeSpinner.warn(`Could not connect to OpenCode on port ${port}`);
|
|
1089
1360
|
logActivity(state, {
|
|
1090
1361
|
type: "error",
|
|
1091
|
-
error: `OpenCode not reachable on port ${port}: ${
|
|
1362
|
+
error: `OpenCode not reachable on port ${port}: ${healthCheck.error}`
|
|
1092
1363
|
});
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1364
|
+
const runningInstances = await findHealthyOpenCodeInstances();
|
|
1365
|
+
if (runningInstances.length > 0) {
|
|
1366
|
+
blank();
|
|
1367
|
+
console.log(chalk4.yellow("Found OpenCode running on different port(s):"));
|
|
1368
|
+
for (const instance of runningInstances) {
|
|
1369
|
+
const ver = instance.version ? ` (v${instance.version})` : "";
|
|
1370
|
+
const cwd = instance.cwd ? ` in ${instance.cwd}` : "";
|
|
1371
|
+
console.log(chalk4.dim(` \u2022 Port ${instance.port}${ver}${cwd}`));
|
|
1372
|
+
}
|
|
1373
|
+
blank();
|
|
1374
|
+
if (runningInstances.length === 1) {
|
|
1375
|
+
console.log(chalk4.yellow("Tip: Run with the correct port:"));
|
|
1376
|
+
console.log(
|
|
1377
|
+
chalk4.dim(
|
|
1378
|
+
` npx @evident-ai/cli@latest tunnel --sandbox ${sandboxId} --port ${runningInstances[0].port}`
|
|
1379
|
+
)
|
|
1380
|
+
);
|
|
1381
|
+
} else {
|
|
1382
|
+
console.log(chalk4.yellow("Tip: Specify which port to use:"));
|
|
1383
|
+
console.log(
|
|
1384
|
+
chalk4.dim(` npx @evident-ai/cli@latest tunnel --sandbox ${sandboxId} --port <PORT>`)
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
blank();
|
|
1388
|
+
} else {
|
|
1389
|
+
blank();
|
|
1390
|
+
const action = await select({
|
|
1391
|
+
message: "OpenCode is not running. What would you like to do?",
|
|
1392
|
+
choices: [
|
|
1393
|
+
{
|
|
1394
|
+
name: "Start OpenCode for me",
|
|
1395
|
+
value: "start",
|
|
1396
|
+
description: `Run 'opencode serve --port ${port}' in a new process`
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
name: "Show me the command to run",
|
|
1400
|
+
value: "manual",
|
|
1401
|
+
description: "Display instructions for starting OpenCode manually"
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
name: "Continue without OpenCode",
|
|
1405
|
+
value: "continue",
|
|
1406
|
+
description: "Connect the tunnel anyway (requests will fail until OpenCode starts)"
|
|
1407
|
+
}
|
|
1408
|
+
]
|
|
1409
|
+
});
|
|
1410
|
+
if (action === "start") {
|
|
1411
|
+
let actualPort = port;
|
|
1412
|
+
if (isPortInUse(port)) {
|
|
1413
|
+
console.log(chalk4.yellow(`
|
|
1414
|
+
Port ${port} is already in use by another process.`));
|
|
1415
|
+
const alternativePort = findAvailablePort(port + 1);
|
|
1416
|
+
if (alternativePort) {
|
|
1417
|
+
const useAlternative = await select({
|
|
1418
|
+
message: `Would you like to use port ${alternativePort} instead?`,
|
|
1419
|
+
choices: [
|
|
1420
|
+
{ name: `Yes, use port ${alternativePort}`, value: "yes" },
|
|
1421
|
+
{ name: "No, I will free up the port manually", value: "no" }
|
|
1422
|
+
]
|
|
1423
|
+
});
|
|
1424
|
+
if (useAlternative === "yes") {
|
|
1425
|
+
actualPort = alternativePort;
|
|
1426
|
+
} else {
|
|
1427
|
+
console.log(chalk4.dim(`
|
|
1428
|
+
Free up port ${port} and run the tunnel command again.`));
|
|
1429
|
+
blank();
|
|
1430
|
+
process.exit(1);
|
|
1431
|
+
}
|
|
1432
|
+
} else {
|
|
1433
|
+
console.log(
|
|
1434
|
+
chalk4.red(
|
|
1435
|
+
`Could not find an available port. Please free up port ${port} and try again.`
|
|
1436
|
+
)
|
|
1437
|
+
);
|
|
1438
|
+
blank();
|
|
1439
|
+
process.exit(1);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
const opencodeStartSpinner = ora2(`Starting OpenCode on port ${actualPort}...`).start();
|
|
1443
|
+
try {
|
|
1444
|
+
let command = "opencode";
|
|
1445
|
+
let args = ["serve", "--port", actualPort.toString()];
|
|
1446
|
+
try {
|
|
1447
|
+
execSync("which opencode", { stdio: "ignore" });
|
|
1448
|
+
} catch {
|
|
1449
|
+
command = "npx";
|
|
1450
|
+
args = ["opencode", "serve", "--port", actualPort.toString()];
|
|
1451
|
+
}
|
|
1452
|
+
const child = spawn(command, args, {
|
|
1453
|
+
detached: true,
|
|
1454
|
+
stdio: "ignore",
|
|
1455
|
+
cwd: process.cwd()
|
|
1456
|
+
// Start in current working directory
|
|
1457
|
+
});
|
|
1458
|
+
child.unref();
|
|
1459
|
+
const maxRetries = 10;
|
|
1460
|
+
const retryDelayMs = 1e3;
|
|
1461
|
+
let healthy = false;
|
|
1462
|
+
let version;
|
|
1463
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
1464
|
+
await sleep(retryDelayMs);
|
|
1465
|
+
const retryHealth = await checkOpenCodeHealth(actualPort);
|
|
1466
|
+
if (retryHealth.healthy) {
|
|
1467
|
+
healthy = true;
|
|
1468
|
+
version = retryHealth.version;
|
|
1469
|
+
break;
|
|
1470
|
+
}
|
|
1471
|
+
opencodeStartSpinner.text = `Starting OpenCode on port ${actualPort}... (${i + 1}/${maxRetries})`;
|
|
1472
|
+
}
|
|
1473
|
+
if (healthy) {
|
|
1474
|
+
state.opencodeConnected = true;
|
|
1475
|
+
state.opencodeVersion = version ?? null;
|
|
1476
|
+
const versionStr = version ? ` (v${version})` : "";
|
|
1477
|
+
opencodeStartSpinner.succeed(`OpenCode started on port ${actualPort}${versionStr}`);
|
|
1478
|
+
logActivity(state, {
|
|
1479
|
+
type: "info",
|
|
1480
|
+
message: `OpenCode started on port ${actualPort}${versionStr}`
|
|
1481
|
+
});
|
|
1482
|
+
} else {
|
|
1483
|
+
opencodeStartSpinner.warn(
|
|
1484
|
+
"OpenCode process started but not responding. Check if it started correctly."
|
|
1485
|
+
);
|
|
1486
|
+
logActivity(state, {
|
|
1487
|
+
type: "info",
|
|
1488
|
+
message: "OpenCode may still be starting..."
|
|
1489
|
+
});
|
|
1490
|
+
console.log(chalk4.dim("\nTip: Check for errors by running OpenCode manually:"));
|
|
1491
|
+
console.log(chalk4.dim(` opencode serve --port ${actualPort}`));
|
|
1492
|
+
blank();
|
|
1493
|
+
}
|
|
1494
|
+
} catch (error2) {
|
|
1495
|
+
const msg = error2 instanceof Error ? error2.message : "Unknown error";
|
|
1496
|
+
opencodeStartSpinner.fail(`Failed to start OpenCode: ${msg}`);
|
|
1497
|
+
logActivity(state, {
|
|
1498
|
+
type: "error",
|
|
1499
|
+
error: `Failed to start OpenCode: ${msg}`
|
|
1500
|
+
});
|
|
1501
|
+
console.log(chalk4.yellow("\nYou can try starting it manually:"));
|
|
1502
|
+
console.log(chalk4.dim(` opencode serve --port ${actualPort}`));
|
|
1503
|
+
blank();
|
|
1504
|
+
}
|
|
1505
|
+
} else if (action === "manual") {
|
|
1506
|
+
blank();
|
|
1507
|
+
console.log(chalk4.yellow("To start OpenCode, run one of these commands:"));
|
|
1508
|
+
blank();
|
|
1509
|
+
console.log(chalk4.dim(" # Start OpenCode in your project directory:"));
|
|
1510
|
+
console.log(chalk4.dim(` opencode serve --port ${port}`));
|
|
1511
|
+
blank();
|
|
1512
|
+
console.log(chalk4.dim(" # Or if you have OpenCode installed globally:"));
|
|
1513
|
+
console.log(chalk4.dim(` npx opencode serve --port ${port}`));
|
|
1514
|
+
blank();
|
|
1515
|
+
console.log(
|
|
1516
|
+
chalk4.dim("The tunnel will automatically forward requests once OpenCode is running.")
|
|
1517
|
+
);
|
|
1518
|
+
blank();
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1096
1521
|
}
|
|
1097
1522
|
while (true) {
|
|
1098
1523
|
try {
|
|
1099
|
-
await connect(
|
|
1524
|
+
await connect(credentials.token, sandboxId, port, state);
|
|
1100
1525
|
state.reconnectAttempt++;
|
|
1101
1526
|
const delay = getReconnectDelay(state.reconnectAttempt);
|
|
1102
1527
|
logActivity(state, {
|