@portlens/cli 1.0.3

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.
@@ -0,0 +1,78 @@
1
+ import { EventEmitter } from "node:events";
2
+ /**
3
+ * Manages exponential-backoff reconnection with jitter.
4
+ *
5
+ * Usage:
6
+ * const mgr = new ReconnectionManager();
7
+ * await mgr.scheduleReconnect(() => openSocket(), onWaiting);
8
+ */
9
+ export declare class ReconnectionManager {
10
+ private attempts;
11
+ private readonly maxAttempts;
12
+ private readonly baseDelay;
13
+ private readonly maxDelay;
14
+ /**
15
+ * Compute the next delay with full-jitter:
16
+ * base = min(baseDelay × 2^attempts, maxDelay)
17
+ * result = base + base × 0.2 × random()
18
+ */
19
+ getDelay(): number;
20
+ /** Reset the attempt counter after a successful connection. */
21
+ reset(): void;
22
+ /** Returns true when the max retry budget has been exhausted. */
23
+ shouldGiveUp(): boolean;
24
+ /**
25
+ * Wait for the computed back-off delay, then call `connectFn`.
26
+ *
27
+ * @param connectFn Function that initiates a new WebSocket connection.
28
+ * @param onWaiting Optional callback fired just before sleeping.
29
+ * Receives `(delayMs, attempt, maxAttempts)` so callers
30
+ * can update the UI without coupling the manager to display code.
31
+ */
32
+ scheduleReconnect(connectFn: () => void, onWaiting?: (delayMs: number, attempt: number, maxAttempts: number) => void): Promise<void>;
33
+ }
34
+ export interface AgentOptions {
35
+ name: string;
36
+ desc: string;
37
+ password?: string;
38
+ relay: string;
39
+ noOpen: boolean;
40
+ /** JWT from ~/.portlens/config.json — forwarded to relay for userId resolution */
41
+ jwtToken?: string;
42
+ /** Skip automatic screenshot capture after connect */
43
+ noScreenshot?: boolean;
44
+ }
45
+ export declare class Agent extends EventEmitter {
46
+ private readonly port;
47
+ private readonly options;
48
+ private ws;
49
+ private pingTimer;
50
+ private closing;
51
+ private screenshotDone;
52
+ /** Timestamp of the last agent-initiated ping; null when no ping is in-flight. */
53
+ private pingTimestamp;
54
+ private readonly reconnectionManager;
55
+ readonly token: string;
56
+ constructor(port: number, options: AgentOptions);
57
+ connect(): void;
58
+ close(): void;
59
+ private _openSocket;
60
+ private _startPing;
61
+ private _stopPing;
62
+ private _handleMessage;
63
+ private _forwardRequest;
64
+ /** Convert the relay WebSocket URL to its HTTP equivalent. */
65
+ private _relayHttp;
66
+ /**
67
+ * Try to locate a system Chrome / Chromium executable.
68
+ * Returns the first path that exists on disk, or null.
69
+ */
70
+ private _findChrome;
71
+ /**
72
+ * Wait 2 s after connect, take a WebP screenshot of localhost:{port},
73
+ * and POST the base64 image to the relay for storage.
74
+ * All errors are caught and logged as warnings — never aborts the tunnel.
75
+ */
76
+ private _captureScreenshot;
77
+ }
78
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IAEtC;;;;OAIG;IACH,QAAQ,IAAI,MAAM;IAMlB,+DAA+D;IAC/D,KAAK,IAAI,IAAI;IAIb,iEAAiE;IACjE,YAAY,IAAI,OAAO;IAIvB;;;;;;;OAOG;IACG,iBAAiB,CACrB,SAAS,EAAE,MAAM,IAAI,EACrB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,GAC1E,OAAO,CAAC,IAAI,CAAC;CAkBjB;AAID,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAID,qBAAa,KAAM,SAAQ,YAAY;IAcnC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAE/B,kFAAkF;IAClF,OAAO,CAAC,aAAa,CAAuB;IAE5C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IAEjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAGJ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY;IAMxC,OAAO,IAAI,IAAI;IAKf,KAAK,IAAI,IAAI;IAWb,OAAO,CAAC,WAAW;IAyDnB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,eAAe;IAiEvB,8DAA8D;IAC9D,OAAO,CAAC,UAAU;IAMlB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA2BnB;;;;OAIG;YACW,kBAAkB;CAwEjC"}
package/dist/agent.js ADDED
@@ -0,0 +1,335 @@
1
+ import fs from "node:fs";
2
+ import http from "node:http";
3
+ import { EventEmitter } from "node:events";
4
+ import WebSocket from "ws";
5
+ import chalk from "chalk";
6
+ import { generateToken, hashPassword } from "@portlens/shared";
7
+ // ── Timing constants ──────────────────────────────────────────────────────────
8
+ /** How often the agent sends its own health-check ping to the relay. */
9
+ const PING_INTERVAL_MS = 30_000;
10
+ // ── ReconnectionManager ───────────────────────────────────────────────────────
11
+ /**
12
+ * Manages exponential-backoff reconnection with jitter.
13
+ *
14
+ * Usage:
15
+ * const mgr = new ReconnectionManager();
16
+ * await mgr.scheduleReconnect(() => openSocket(), onWaiting);
17
+ */
18
+ export class ReconnectionManager {
19
+ attempts = 0;
20
+ maxAttempts = 10;
21
+ baseDelay = 1_000; // 1 s
22
+ maxDelay = 30_000; // 30 s
23
+ /**
24
+ * Compute the next delay with full-jitter:
25
+ * base = min(baseDelay × 2^attempts, maxDelay)
26
+ * result = base + base × 0.2 × random()
27
+ */
28
+ getDelay() {
29
+ const base = Math.min(this.baseDelay * 2 ** this.attempts, this.maxDelay);
30
+ const jitter = base * 0.2 * Math.random();
31
+ return base + jitter;
32
+ }
33
+ /** Reset the attempt counter after a successful connection. */
34
+ reset() {
35
+ this.attempts = 0;
36
+ }
37
+ /** Returns true when the max retry budget has been exhausted. */
38
+ shouldGiveUp() {
39
+ return this.attempts >= this.maxAttempts;
40
+ }
41
+ /**
42
+ * Wait for the computed back-off delay, then call `connectFn`.
43
+ *
44
+ * @param connectFn Function that initiates a new WebSocket connection.
45
+ * @param onWaiting Optional callback fired just before sleeping.
46
+ * Receives `(delayMs, attempt, maxAttempts)` so callers
47
+ * can update the UI without coupling the manager to display code.
48
+ */
49
+ async scheduleReconnect(connectFn, onWaiting) {
50
+ if (this.shouldGiveUp()) {
51
+ console.error(chalk.red("\n Max reconnection attempts reached. " +
52
+ "Run `portlens` again to retry."));
53
+ process.exit(1);
54
+ }
55
+ const delay = this.getDelay();
56
+ onWaiting?.(delay, this.attempts, this.maxAttempts);
57
+ await new Promise((resolve) => setTimeout(resolve, delay));
58
+ this.attempts++;
59
+ connectFn();
60
+ }
61
+ }
62
+ // ── Agent ─────────────────────────────────────────────────────────────────────
63
+ export class Agent extends EventEmitter {
64
+ port;
65
+ options;
66
+ ws = null;
67
+ pingTimer = null;
68
+ closing = false;
69
+ screenshotDone = false;
70
+ /** Timestamp of the last agent-initiated ping; null when no ping is in-flight. */
71
+ pingTimestamp = null;
72
+ reconnectionManager = new ReconnectionManager();
73
+ token;
74
+ constructor(port, options) {
75
+ super();
76
+ this.port = port;
77
+ this.options = options;
78
+ this.token = generateToken(8);
79
+ }
80
+ connect() {
81
+ if (this.closing)
82
+ return;
83
+ this._openSocket();
84
+ }
85
+ close() {
86
+ this.closing = true;
87
+ this._stopPing();
88
+ if (this.ws) {
89
+ this.ws.removeAllListeners();
90
+ this.ws.close();
91
+ }
92
+ }
93
+ // ── Private ───────────────────────────────────────────────────────────────
94
+ _openSocket() {
95
+ const { relay, name, desc, password, jwtToken } = this.options;
96
+ const url = `${relay}/agent?token=${this.token}`;
97
+ const ws = new WebSocket(url);
98
+ this.ws = ws;
99
+ ws.on("open", () => {
100
+ // A successful open resets the back-off counter.
101
+ this.reconnectionManager.reset();
102
+ this.pingTimestamp = null;
103
+ const msg = {
104
+ type: "register",
105
+ token: this.token,
106
+ appName: name,
107
+ appDesc: desc,
108
+ ...(password ? { passwordHash: hashPassword(password) } : {}),
109
+ ...(jwtToken ? { jwtToken } : {}),
110
+ };
111
+ ws.send(JSON.stringify(msg));
112
+ this._startPing(ws);
113
+ /**
114
+ * Emit "connected" — two listeners in index.ts:
115
+ * - once("connected") → first-time box render
116
+ * - on("connected") → subsequent reconnect box updates
117
+ */
118
+ this.emit("connected");
119
+ // Take screenshot on first successful connect only (not on reconnects).
120
+ if (!this.options.noScreenshot && !this.screenshotDone) {
121
+ this.screenshotDone = true;
122
+ this._captureScreenshot().catch(() => { });
123
+ }
124
+ });
125
+ ws.on("message", (data) => this._handleMessage(data));
126
+ ws.on("close", () => {
127
+ this._stopPing();
128
+ if (!this.closing) {
129
+ void this.reconnectionManager.scheduleReconnect(() => { if (!this.closing)
130
+ this._openSocket(); }, (delayMs, attempt, maxAttempts) => {
131
+ this.emit("reconnecting", delayMs, attempt, maxAttempts);
132
+ });
133
+ }
134
+ });
135
+ ws.on("error", (err) => {
136
+ // "close" fires after "error" — let close handle reconnect.
137
+ console.error(chalk.red(`\n Error: ${err.message}`));
138
+ });
139
+ }
140
+ _startPing(ws) {
141
+ this._stopPing();
142
+ this.pingTimer = setInterval(() => {
143
+ if (ws.readyState !== WebSocket.OPEN)
144
+ return;
145
+ // Record send-time so we can compute RTT when the pong arrives.
146
+ this.pingTimestamp = Date.now();
147
+ ws.send(JSON.stringify({ type: "ping" }));
148
+ }, PING_INTERVAL_MS);
149
+ }
150
+ _stopPing() {
151
+ if (this.pingTimer) {
152
+ clearInterval(this.pingTimer);
153
+ this.pingTimer = null;
154
+ this.pingTimestamp = null;
155
+ }
156
+ }
157
+ _handleMessage(data) {
158
+ let msg;
159
+ try {
160
+ msg = JSON.parse(data.toString());
161
+ }
162
+ catch {
163
+ return;
164
+ }
165
+ // Relay-initiated ping — reply immediately.
166
+ if (msg.type === "ping") {
167
+ this.ws?.send(JSON.stringify({ type: "pong" }));
168
+ return;
169
+ }
170
+ // Pong in response to our own ping — measure RTT and emit.
171
+ if (msg.type === "pong") {
172
+ if (this.pingTimestamp !== null) {
173
+ const rtt = Date.now() - this.pingTimestamp;
174
+ this.pingTimestamp = null;
175
+ this.emit("rtt", rtt);
176
+ }
177
+ return;
178
+ }
179
+ if (msg.type === "request") {
180
+ this._forwardRequest(msg);
181
+ return;
182
+ }
183
+ if (msg.type === "error") {
184
+ console.error(chalk.red(`\n Relay error [${msg.code}]: ${msg.message}`));
185
+ }
186
+ }
187
+ _forwardRequest(msg) {
188
+ const { requestId, method, path, headers, body } = msg;
189
+ const reqBody = body ? Buffer.from(body, "base64") : undefined;
190
+ const options = {
191
+ hostname: "localhost",
192
+ port: this.port,
193
+ method,
194
+ path,
195
+ headers: {
196
+ ...headers,
197
+ host: `localhost:${this.port}`,
198
+ ...(reqBody ? { "content-length": String(reqBody.length) } : {}),
199
+ },
200
+ };
201
+ const respond = (statusCode, respHeaders, respBody) => {
202
+ const outMsg = {
203
+ type: "response",
204
+ requestId,
205
+ statusCode,
206
+ headers: respHeaders,
207
+ body: respBody.toString("base64"),
208
+ };
209
+ if (this.ws?.readyState === WebSocket.OPEN) {
210
+ this.ws.send(JSON.stringify(outMsg));
211
+ }
212
+ };
213
+ const req = http.request(options, (res) => {
214
+ const chunks = [];
215
+ res.on("data", (chunk) => chunks.push(chunk));
216
+ res.on("end", () => {
217
+ const respHeaders = {};
218
+ for (const [k, v] of Object.entries(res.headers)) {
219
+ if (typeof v === "string")
220
+ respHeaders[k] = v;
221
+ else if (Array.isArray(v))
222
+ respHeaders[k] = v.join(", ");
223
+ }
224
+ respond(res.statusCode ?? 500, respHeaders, Buffer.concat(chunks));
225
+ });
226
+ });
227
+ req.on("error", (err) => {
228
+ console.error(chalk.red(` Local request failed: ${err.message}`));
229
+ respond(502, { "content-type": "application/json" }, Buffer.from(JSON.stringify({ error: "Local server unreachable", detail: err.message })));
230
+ });
231
+ if (reqBody)
232
+ req.write(reqBody);
233
+ req.end();
234
+ }
235
+ // ── Screenshot capture ────────────────────────────────────────────────────
236
+ /** Convert the relay WebSocket URL to its HTTP equivalent. */
237
+ _relayHttp() {
238
+ return this.options.relay
239
+ .replace(/^wss:\/\//, "https://")
240
+ .replace(/^ws:\/\//, "http://");
241
+ }
242
+ /**
243
+ * Try to locate a system Chrome / Chromium executable.
244
+ * Returns the first path that exists on disk, or null.
245
+ */
246
+ _findChrome() {
247
+ const candidates = [
248
+ // macOS
249
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
250
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
251
+ "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
252
+ // Linux
253
+ "/usr/bin/google-chrome",
254
+ "/usr/bin/google-chrome-stable",
255
+ "/usr/bin/chromium",
256
+ "/usr/bin/chromium-browser",
257
+ "/snap/bin/chromium",
258
+ // Windows
259
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
260
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
261
+ ];
262
+ for (const p of candidates) {
263
+ try {
264
+ fs.accessSync(p, fs.constants.X_OK);
265
+ return p;
266
+ }
267
+ catch {
268
+ // not found / not executable — try next
269
+ }
270
+ }
271
+ return null;
272
+ }
273
+ /**
274
+ * Wait 2 s after connect, take a WebP screenshot of localhost:{port},
275
+ * and POST the base64 image to the relay for storage.
276
+ * All errors are caught and logged as warnings — never aborts the tunnel.
277
+ */
278
+ async _captureScreenshot() {
279
+ await new Promise((r) => setTimeout(r, 2_000));
280
+ if (this.closing)
281
+ return;
282
+ const executablePath = process.env["PUPPETEER_EXECUTABLE_PATH"] ?? this._findChrome() ?? undefined;
283
+ if (!executablePath) {
284
+ console.log(chalk.dim(" Screenshot skipped — no Chrome found. " +
285
+ "Set PUPPETEER_EXECUTABLE_PATH to enable."));
286
+ return;
287
+ }
288
+ let puppeteer;
289
+ try {
290
+ puppeteer = await import("puppeteer-core");
291
+ }
292
+ catch {
293
+ console.log(chalk.dim(" Screenshot skipped — puppeteer-core not available."));
294
+ return;
295
+ }
296
+ let browser = null;
297
+ try {
298
+ browser = await puppeteer.launch({
299
+ executablePath,
300
+ headless: true,
301
+ args: [
302
+ "--no-sandbox",
303
+ "--disable-setuid-sandbox",
304
+ "--disable-dev-shm-usage",
305
+ ],
306
+ });
307
+ const page = await browser.newPage();
308
+ await page.setViewport({ width: 1280, height: 800 });
309
+ await page.goto(`http://localhost:${this.port}`, {
310
+ waitUntil: "load",
311
+ timeout: 15_000,
312
+ });
313
+ const shot = await page.screenshot({ type: "webp", quality: 80 });
314
+ const imageBase64 = Buffer.from(shot).toString("base64");
315
+ const res = await fetch(`${this._relayHttp()}/session/${this.token}/screenshot`, {
316
+ method: "POST",
317
+ headers: { "content-type": "application/json" },
318
+ body: JSON.stringify({ imageBase64 }),
319
+ });
320
+ if (res.ok) {
321
+ console.log(chalk.dim(" Screenshot captured."));
322
+ }
323
+ else {
324
+ console.log(chalk.dim(` Screenshot upload failed (HTTP ${res.status}).`));
325
+ }
326
+ }
327
+ catch (err) {
328
+ console.log(chalk.yellow(` Screenshot warning: ${err instanceof Error ? err.message : String(err)}`));
329
+ }
330
+ finally {
331
+ await browser?.close();
332
+ }
333
+ }
334
+ }
335
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface AuthData {
2
+ token: string;
3
+ user: {
4
+ id: string;
5
+ email: string;
6
+ plan: "free" | "pro";
7
+ customSubdomain: string | null;
8
+ };
9
+ }
10
+ export declare const AUTH_FILE: string;
11
+ export declare function readAuth(): AuthData | null;
12
+ export declare function writeAuth(data: AuthData): void;
13
+ export declare function deleteAuth(): void;
14
+ //# sourceMappingURL=authStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../src/authStore.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACH;AAGD,eAAO,MAAM,SAAS,QAAmC,CAAC;AAE1D,wBAAgB,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAO1C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAS9C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAMjC"}
@@ -0,0 +1,34 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ const AUTH_DIR = path.join(os.homedir(), ".portlens");
5
+ export const AUTH_FILE = path.join(AUTH_DIR, "auth.json");
6
+ export function readAuth() {
7
+ try {
8
+ const raw = fs.readFileSync(AUTH_FILE, "utf8");
9
+ return JSON.parse(raw);
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function writeAuth(data) {
16
+ fs.mkdirSync(AUTH_DIR, { recursive: true });
17
+ fs.writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2) + "\n", "utf8");
18
+ // Restrict to owner read/write only (chmod 600)
19
+ try {
20
+ fs.chmodSync(AUTH_FILE, 0o600);
21
+ }
22
+ catch {
23
+ // chmod is a best-effort — Windows doesn't support POSIX mode bits
24
+ }
25
+ }
26
+ export function deleteAuth() {
27
+ try {
28
+ fs.unlinkSync(AUTH_FILE);
29
+ }
30
+ catch {
31
+ // Already gone — that's fine
32
+ }
33
+ }
34
+ //# sourceMappingURL=authStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authStore.js","sourceRoot":"","sources":["../src/authStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAYzB,MAAM,QAAQ,GAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE1D,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1E,gDAAgD;IAChD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;IACrE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface PortLensConfig {
2
+ relay: string;
3
+ defaultName: string;
4
+ defaultDesc: string;
5
+ }
6
+ /**
7
+ * Read ~/.portlens/config.json.
8
+ * If the file doesn't exist or is malformed, write defaults first then return them.
9
+ */
10
+ export declare function readConfig(): PortLensConfig;
11
+ export declare function writeConfig(config: PortLensConfig): void;
12
+ export declare function configFilePath(): string;
13
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAWD;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAQ3C;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAGxD;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
package/dist/config.js ADDED
@@ -0,0 +1,32 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ const CONFIG_DIR = path.join(os.homedir(), ".portlens");
5
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
6
+ const DEFAULTS = {
7
+ relay: "wss://relay.portlens.net",
8
+ defaultName: "My App",
9
+ defaultDesc: "",
10
+ };
11
+ /**
12
+ * Read ~/.portlens/config.json.
13
+ * If the file doesn't exist or is malformed, write defaults first then return them.
14
+ */
15
+ export function readConfig() {
16
+ try {
17
+ const raw = fs.readFileSync(CONFIG_FILE, "utf8");
18
+ return { ...DEFAULTS, ...JSON.parse(raw) };
19
+ }
20
+ catch {
21
+ writeConfig(DEFAULTS);
22
+ return { ...DEFAULTS };
23
+ }
24
+ }
25
+ export function writeConfig(config) {
26
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
27
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", "utf8");
28
+ }
29
+ export function configFilePath() {
30
+ return CONFIG_FILE;
31
+ }
32
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAQzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AACxD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,QAAQ,GAAmB;IAC/B,KAAK,EAAE,0BAA0B;IACjC,WAAW,EAAE,QAAQ;IACrB,WAAW,EAAE,EAAE;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACjD,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtB,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAsB;IAChD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,37 @@
1
+ export type TunnelStatus = "connected" | "reconnecting" | "disconnected";
2
+ export interface ReconnectInfo {
3
+ /** Milliseconds until the next attempt fires. */
4
+ delayMs: number;
5
+ /** Zero-based attempt index (0 = first retry). */
6
+ attempt: number;
7
+ maxAttempts: number;
8
+ }
9
+ export interface BoxOptions {
10
+ shareUrl: string;
11
+ localPort: number;
12
+ expiresAt: string | null;
13
+ status: TunnelStatus;
14
+ /**
15
+ * Most-recently measured ping round-trip time in milliseconds.
16
+ * Absent until the first ping/pong cycle completes (~30 s after connect).
17
+ */
18
+ rtt?: number;
19
+ /**
20
+ * Present while status === "reconnecting"; drives the countdown display
21
+ * in the Status row without adding extra lines below the box.
22
+ */
23
+ reconnectInfo?: ReconnectInfo;
24
+ }
25
+ /** Render the info box and return it as a string (ends with \n). */
26
+ export declare function renderBox(opts: BoxOptions): string;
27
+ /** Number of terminal lines the box occupies (used for cursor repositioning). */
28
+ export declare function boxLineCount(_opts: BoxOptions): number;
29
+ /** Print the box for the first time. */
30
+ export declare function printBox(opts: BoxOptions): void;
31
+ /**
32
+ * Overwrite the previously printed box in-place.
33
+ * Call this only when the box is the last thing printed to stdout
34
+ * (no other writes since the previous printBox / updateBox).
35
+ */
36
+ export declare function updateBox(opts: BoxOptions): void;
37
+ //# sourceMappingURL=display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,OAAO,EAAM,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,EAAM,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAG,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAK,YAAY,CAAC;IACxB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAqDD,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA+BlD;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAGtD;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAIhD"}