pinggy 0.3.4 → 0.3.6
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/README.md +1 -1
- package/dist/chunk-65R2GMKQ.js +2101 -0
- package/dist/index.cjs +1814 -1362
- package/dist/index.d.cts +616 -0
- package/dist/index.d.ts +616 -0
- package/dist/index.js +38 -55
- package/dist/{main-CZY6GID4.js → main-2QDG7PWL.js} +229 -1726
- package/package.json +3 -4
- package/.github/workflows/npm-publish-github-packages.yml +0 -34
- package/.github/workflows/publish-binaries.yml +0 -223
- package/Makefile +0 -4
- package/caxa_build.js +0 -24
- package/dist/chunk-T5ESYDJY.js +0 -121
- package/ent.plist +0 -14
- package/jest.config.js +0 -19
- package/src/_tests_/build_config.test.ts +0 -91
- package/src/cli/buildConfig.ts +0 -475
- package/src/cli/defaults.ts +0 -20
- package/src/cli/extendedOptions.ts +0 -153
- package/src/cli/help.ts +0 -43
- package/src/cli/options.ts +0 -50
- package/src/cli/starCli.ts +0 -229
- package/src/index.ts +0 -30
- package/src/logger.ts +0 -138
- package/src/main.ts +0 -87
- package/src/remote_management/handler.ts +0 -244
- package/src/remote_management/remoteManagement.ts +0 -226
- package/src/remote_management/remote_schema.ts +0 -176
- package/src/remote_management/websocket_handlers.ts +0 -180
- package/src/tui/blessed/TunnelTui.ts +0 -340
- package/src/tui/blessed/components/DisplayUpdaters.ts +0 -189
- package/src/tui/blessed/components/KeyBindings.ts +0 -236
- package/src/tui/blessed/components/Modals.ts +0 -302
- package/src/tui/blessed/components/UIComponents.ts +0 -306
- package/src/tui/blessed/components/index.ts +0 -4
- package/src/tui/blessed/config.ts +0 -53
- package/src/tui/blessed/headerFetcher.ts +0 -42
- package/src/tui/blessed/index.ts +0 -2
- package/src/tui/blessed/qrCodeGenerator.ts +0 -20
- package/src/tui/blessed/webDebuggerConnection.ts +0 -128
- package/src/tui/ink/asciArt.ts +0 -7
- package/src/tui/ink/hooks/useQrCodes.ts +0 -27
- package/src/tui/ink/hooks/useReqResHeaders.ts +0 -27
- package/src/tui/ink/hooks/useTerminalSize.ts +0 -26
- package/src/tui/ink/hooks/useTerminalStats.ts +0 -24
- package/src/tui/ink/hooks/useWebDebugger.ts +0 -98
- package/src/tui/ink/index.tsx +0 -243
- package/src/tui/ink/layout/Borders.tsx +0 -15
- package/src/tui/ink/layout/Container.tsx +0 -15
- package/src/tui/ink/sections/DebuggerDetailModal.tsx +0 -53
- package/src/tui/ink/sections/KeyBindings.tsx +0 -58
- package/src/tui/ink/sections/QrCodeSection.tsx +0 -28
- package/src/tui/ink/sections/StatsSection.tsx +0 -20
- package/src/tui/ink/sections/URLsSection.tsx +0 -53
- package/src/tui/ink/utils/utils.ts +0 -35
- package/src/tui/spinner/spinner.ts +0 -64
- package/src/tunnel_manager/TunnelManager.ts +0 -1212
- package/src/types.ts +0 -255
- package/src/utils/FileServer.ts +0 -112
- package/src/utils/detect_vc_redist_on_windows.ts +0 -167
- package/src/utils/getFreePort.ts +0 -41
- package/src/utils/htmlTemplates.ts +0 -146
- package/src/utils/parseArgs.ts +0 -79
- package/src/utils/printer.ts +0 -81
- package/src/utils/util.ts +0 -18
- package/src/workers/file_serve_worker.ts +0 -33
- package/tsconfig.json +0 -17
- package/tsup.config.ts +0 -12
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
import blessed from "blessed";
|
|
2
|
-
import { FinalConfig } from "../../../types.js";
|
|
3
|
-
import { asciiArtPinggyLogo } from "../../ink/asciArt.js";
|
|
4
|
-
|
|
5
|
-
export const MIN_WIDTH_WARNING = 60;
|
|
6
|
-
export const SIMPLE_LAYOUT_THRESHOLD = 80;
|
|
7
|
-
|
|
8
|
-
export interface UIElements {
|
|
9
|
-
mainContainer: blessed.Widgets.BoxElement;
|
|
10
|
-
logoBox?: blessed.Widgets.BoxElement;
|
|
11
|
-
contentBox?: blessed.Widgets.BoxElement;
|
|
12
|
-
urlsBox: blessed.Widgets.BoxElement;
|
|
13
|
-
statsBox: blessed.Widgets.BoxElement;
|
|
14
|
-
requestsBox?: blessed.Widgets.BoxElement;
|
|
15
|
-
qrCodeBox?: blessed.Widgets.BoxElement;
|
|
16
|
-
footerBox: blessed.Widgets.BoxElement;
|
|
17
|
-
warningBox?: blessed.Widgets.BoxElement;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Colorizes text with a gradient of colors
|
|
22
|
-
*/
|
|
23
|
-
export function colorizeGradient(text: string): string {
|
|
24
|
-
const colors = ["red", "yellow", "green", "cyan", "blue", "magenta"];
|
|
25
|
-
const lines = text.split("\n");
|
|
26
|
-
return lines
|
|
27
|
-
.map((line, i) => {
|
|
28
|
-
const color = colors[i % colors.length];
|
|
29
|
-
return `{${color}-fg}${line}{/${color}-fg}`;
|
|
30
|
-
})
|
|
31
|
-
.join("\n");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Creates the warning UI when terminal is too narrow
|
|
36
|
-
*/
|
|
37
|
-
export function createWarningUI(screen: blessed.Widgets.Screen): blessed.Widgets.BoxElement {
|
|
38
|
-
return blessed.box({
|
|
39
|
-
parent: screen,
|
|
40
|
-
top: "center",
|
|
41
|
-
left: "center",
|
|
42
|
-
width: "80%",
|
|
43
|
-
height: 5,
|
|
44
|
-
content: `{red-fg}{bold}Terminal is too narrow to show TUI (${screen.width} cols).{/bold}{/red-fg}\n{yellow-fg}Please resize your terminal to at least ${MIN_WIDTH_WARNING} columns for proper display.{/yellow-fg}`,
|
|
45
|
-
tags: true,
|
|
46
|
-
align: "center",
|
|
47
|
-
valign: "middle",
|
|
48
|
-
style: {
|
|
49
|
-
fg: 'red'
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Creates the full UI layout for wider terminals
|
|
56
|
-
*/
|
|
57
|
-
export function createFullUI(
|
|
58
|
-
screen: blessed.Widgets.Screen,
|
|
59
|
-
urls: string[],
|
|
60
|
-
greet: string,
|
|
61
|
-
tunnelConfig?: FinalConfig
|
|
62
|
-
): UIElements {
|
|
63
|
-
// Main container
|
|
64
|
-
const mainContainer = blessed.box({
|
|
65
|
-
parent: screen,
|
|
66
|
-
top: 0,
|
|
67
|
-
left: 0,
|
|
68
|
-
width: "100%",
|
|
69
|
-
height: "100%",
|
|
70
|
-
padding: 1,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Logo
|
|
74
|
-
const logoBox = blessed.box({
|
|
75
|
-
parent: mainContainer,
|
|
76
|
-
top: 0,
|
|
77
|
-
left: 0,
|
|
78
|
-
width: "100%",
|
|
79
|
-
height: 7,
|
|
80
|
-
content: colorizeGradient(asciiArtPinggyLogo),
|
|
81
|
-
tags: true,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Content box with border
|
|
85
|
-
const contentBox = blessed.box({
|
|
86
|
-
parent: mainContainer,
|
|
87
|
-
top: 8,
|
|
88
|
-
left: 0,
|
|
89
|
-
width: "100%-2",
|
|
90
|
-
height: "100%-10",
|
|
91
|
-
padding: 0,
|
|
92
|
-
border: {
|
|
93
|
-
type: "line",
|
|
94
|
-
},
|
|
95
|
-
style: {
|
|
96
|
-
border: {
|
|
97
|
-
fg: "green",
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Greet message
|
|
104
|
-
let greetHeight = 0;
|
|
105
|
-
if (greet) {
|
|
106
|
-
const greetBox = blessed.box({
|
|
107
|
-
parent: contentBox,
|
|
108
|
-
top: 0,
|
|
109
|
-
left: "center",
|
|
110
|
-
width: "60%",
|
|
111
|
-
height: 4,
|
|
112
|
-
content: `{bold}${greet}{/bold}`,
|
|
113
|
-
tags: true,
|
|
114
|
-
align: "center",
|
|
115
|
-
style: {
|
|
116
|
-
fg: 'green',
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
greetHeight = 4;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Upper section: URLs + Stats
|
|
123
|
-
const upperSectionTop = greetHeight > 0 ? greetHeight : 0;
|
|
124
|
-
|
|
125
|
-
const upperSection = blessed.box({
|
|
126
|
-
parent: contentBox,
|
|
127
|
-
top: upperSectionTop,
|
|
128
|
-
left: 0,
|
|
129
|
-
width: "100%-2",
|
|
130
|
-
height: 10,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// URLs section
|
|
134
|
-
const urlsBox = blessed.box({
|
|
135
|
-
parent: upperSection,
|
|
136
|
-
top: 0,
|
|
137
|
-
left: 0,
|
|
138
|
-
width: "48%",
|
|
139
|
-
height: "100%",
|
|
140
|
-
padding: { left: 1, right: 1 },
|
|
141
|
-
tags: true,
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// Stats section
|
|
147
|
-
const statsBox = blessed.box({
|
|
148
|
-
parent: upperSection,
|
|
149
|
-
top: 0,
|
|
150
|
-
right: 0,
|
|
151
|
-
left: "65%",
|
|
152
|
-
width: "35%",
|
|
153
|
-
height: "100%",
|
|
154
|
-
padding: { left: 1, right: 1 },
|
|
155
|
-
tags: true,
|
|
156
|
-
align: "left",
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Lower section: Requests + QR Code
|
|
160
|
-
const lowerSectionTop = greetHeight + 11;
|
|
161
|
-
const lowerSection = blessed.box({
|
|
162
|
-
parent: contentBox,
|
|
163
|
-
top: lowerSectionTop,
|
|
164
|
-
left: 0,
|
|
165
|
-
right: 0,
|
|
166
|
-
bottom: 2,
|
|
167
|
-
width: "100%-2",
|
|
168
|
-
height: `100%-${lowerSectionTop + 6}`,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const isQrCodeRequested = tunnelConfig?.qrCode || false;
|
|
172
|
-
|
|
173
|
-
// Requests section
|
|
174
|
-
const requestsBox = blessed.box({
|
|
175
|
-
parent: lowerSection,
|
|
176
|
-
top: 0,
|
|
177
|
-
left: 0,
|
|
178
|
-
width: isQrCodeRequested ? "60%" : "80%",
|
|
179
|
-
height: "80%",
|
|
180
|
-
padding: { left: 1, right: 1 },
|
|
181
|
-
tags: true,
|
|
182
|
-
scrollable: true,
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// QR Code section
|
|
187
|
-
let qrCodeBox: blessed.Widgets.BoxElement | undefined;
|
|
188
|
-
if (isQrCodeRequested) {
|
|
189
|
-
qrCodeBox = blessed.box({
|
|
190
|
-
parent: lowerSection,
|
|
191
|
-
top: 0,
|
|
192
|
-
right: 0,
|
|
193
|
-
width: "40%",
|
|
194
|
-
height: "100%",
|
|
195
|
-
tags: true,
|
|
196
|
-
padding: { left: 1, right: 1 },
|
|
197
|
-
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Footer
|
|
202
|
-
const footerBox = blessed.box({
|
|
203
|
-
parent: contentBox,
|
|
204
|
-
bottom: 0,
|
|
205
|
-
left: "center",
|
|
206
|
-
width: "shrink",
|
|
207
|
-
height: 1,
|
|
208
|
-
content: "Press Ctrl+C to stop the tunnel. Or press h for key bindings.",
|
|
209
|
-
tags: true,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
mainContainer,
|
|
214
|
-
logoBox,
|
|
215
|
-
contentBox,
|
|
216
|
-
urlsBox,
|
|
217
|
-
statsBox,
|
|
218
|
-
requestsBox,
|
|
219
|
-
qrCodeBox,
|
|
220
|
-
footerBox,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Creates a simple UI layout for narrower terminals
|
|
226
|
-
*/
|
|
227
|
-
export function createSimpleUI(
|
|
228
|
-
screen: blessed.Widgets.Screen,
|
|
229
|
-
urls: string[],
|
|
230
|
-
greet: string
|
|
231
|
-
): UIElements {
|
|
232
|
-
const mainContainer = blessed.box({
|
|
233
|
-
parent: screen,
|
|
234
|
-
top: 0,
|
|
235
|
-
left: 0,
|
|
236
|
-
width: "100%",
|
|
237
|
-
height: "100%",
|
|
238
|
-
padding: { left: 1, right: 1 },
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
let currentTop = 0;
|
|
242
|
-
|
|
243
|
-
// Greet message
|
|
244
|
-
if (greet) {
|
|
245
|
-
blessed.box({
|
|
246
|
-
parent: mainContainer,
|
|
247
|
-
top: currentTop,
|
|
248
|
-
left: "center",
|
|
249
|
-
width: "90%",
|
|
250
|
-
height: "shrink",
|
|
251
|
-
content: `{bold}${greet}{/bold}`,
|
|
252
|
-
tags: true,
|
|
253
|
-
align: "center",
|
|
254
|
-
style: {
|
|
255
|
-
fg: 'green'
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const lines = Math.ceil(greet.length / ((screen.width as number) * 0.9));
|
|
260
|
-
currentTop += Math.max(lines, 1) + 1;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// URLs section
|
|
264
|
-
const urlsBox = blessed.box({
|
|
265
|
-
parent: mainContainer,
|
|
266
|
-
top: currentTop,
|
|
267
|
-
left: 0,
|
|
268
|
-
width: "100%",
|
|
269
|
-
height: urls.length + 2,
|
|
270
|
-
tags: true,
|
|
271
|
-
});
|
|
272
|
-
currentTop += urls.length + 3;
|
|
273
|
-
|
|
274
|
-
// Stats section
|
|
275
|
-
const statsBox = blessed.box({
|
|
276
|
-
parent: mainContainer,
|
|
277
|
-
top: currentTop,
|
|
278
|
-
left: 0,
|
|
279
|
-
width: "100%",
|
|
280
|
-
height: 8,
|
|
281
|
-
tags: true,
|
|
282
|
-
});
|
|
283
|
-
currentTop += 9;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
// Footer
|
|
287
|
-
const footerBox = blessed.box({
|
|
288
|
-
parent: mainContainer,
|
|
289
|
-
bottom: 0,
|
|
290
|
-
left: "center",
|
|
291
|
-
width: "shrink",
|
|
292
|
-
height: 1,
|
|
293
|
-
content: "Press Ctrl+C to stop the tunnel.",
|
|
294
|
-
tags: true,
|
|
295
|
-
style: {
|
|
296
|
-
fg: 'white',
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
mainContainer,
|
|
302
|
-
urlsBox,
|
|
303
|
-
statsBox,
|
|
304
|
-
footerBox,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createFullUI, createSimpleUI, createWarningUI, UIElements, MIN_WIDTH_WARNING, SIMPLE_LAYOUT_THRESHOLD, colorizeGradient } from './UIComponents.js';
|
|
2
|
-
export { updateUrlsDisplay, updateStatsDisplay, updateRequestsDisplay, updateQrCodeDisplay } from './DisplayUpdaters.js';
|
|
3
|
-
export { showDetailModal, closeDetailModal, showKeyBindingsModal, closeKeyBindingsModal, ModalManager } from './Modals.js';
|
|
4
|
-
export { setupKeyBindings, KeyBindingsState, KeyBindingsCallbacks } from './KeyBindings.js';
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TUI Configuration Settings
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface TuiConfig {
|
|
6
|
-
/**
|
|
7
|
-
* Maximum number of request/response pairs to keep in memory.
|
|
8
|
-
* Older requests will be removed when this limit is exceeded.
|
|
9
|
-
* Default: 100
|
|
10
|
-
*/
|
|
11
|
-
maxRequestPairs: number;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Number of visible request items to display in the requests box.
|
|
15
|
-
* Default: 10
|
|
16
|
-
*/
|
|
17
|
-
visibleRequestCount: number;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Margin from the viewport edge when auto-scrolling to keep selector visible.
|
|
21
|
-
* Default: 2
|
|
22
|
-
*/
|
|
23
|
-
viewportScrollMargin: number;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Inactivity timeout in milliseconds to auto-unselect the selected row and adjust viewport to latest request.
|
|
27
|
-
* Default: 10000 (10 seconds)
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
inactivityHttpSelectorTimeoutMs?: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Default TUI configuration values
|
|
35
|
-
*/
|
|
36
|
-
export const defaultTuiConfig: TuiConfig = {
|
|
37
|
-
maxRequestPairs: 100,
|
|
38
|
-
visibleRequestCount: 10,
|
|
39
|
-
viewportScrollMargin: 2,
|
|
40
|
-
inactivityHttpSelectorTimeoutMs: 10000,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Get the current TUI configuration.
|
|
45
|
-
*/
|
|
46
|
-
export function getTuiConfig(): TuiConfig {
|
|
47
|
-
return {
|
|
48
|
-
maxRequestPairs: defaultTuiConfig.maxRequestPairs,
|
|
49
|
-
visibleRequestCount:defaultTuiConfig.visibleRequestCount,
|
|
50
|
-
viewportScrollMargin: defaultTuiConfig.viewportScrollMargin,
|
|
51
|
-
inactivityHttpSelectorTimeoutMs: defaultTuiConfig.inactivityHttpSelectorTimeoutMs,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { logger } from "../../logger.js";
|
|
2
|
-
|
|
3
|
-
export interface HeadersResult {
|
|
4
|
-
req: string;
|
|
5
|
-
res: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Fetch request/response headers from the web debugger
|
|
10
|
-
*/
|
|
11
|
-
export async function fetchReqResHeaders(
|
|
12
|
-
baseUrl: string,
|
|
13
|
-
key: number,
|
|
14
|
-
signal?: AbortSignal
|
|
15
|
-
): Promise<HeadersResult> {
|
|
16
|
-
if (!baseUrl) {
|
|
17
|
-
return { req: "", res: "" };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const [reqRes, resRes] = await Promise.all([
|
|
22
|
-
fetch(`http://${baseUrl}/introspec/getrawrequestheader`, {
|
|
23
|
-
headers: { "X-Introspec-Key": key.toString() },
|
|
24
|
-
signal,
|
|
25
|
-
}),
|
|
26
|
-
fetch(`http://${baseUrl}/introspec/getrawresponseheader`, {
|
|
27
|
-
headers: { "X-Introspec-Key": key.toString() },
|
|
28
|
-
signal,
|
|
29
|
-
}),
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
const [req, res] = await Promise.all([reqRes.text(), resRes.text()]);
|
|
33
|
-
return { req, res };
|
|
34
|
-
} catch (err: any) {
|
|
35
|
-
// Re-throw abort errors so caller can handle cancellation
|
|
36
|
-
if (err?.name === 'AbortError') {
|
|
37
|
-
throw err;
|
|
38
|
-
}
|
|
39
|
-
logger.error("Error fetching headers:", err.message || err);
|
|
40
|
-
throw err;
|
|
41
|
-
}
|
|
42
|
-
}
|
package/src/tui/blessed/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import QRCode from "qrcode";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generate QR codes for a list of URLs
|
|
5
|
-
*/
|
|
6
|
-
export async function createQrCodes(urls: string[]): Promise<string[]> {
|
|
7
|
-
const codes: string[] = [];
|
|
8
|
-
|
|
9
|
-
for (const url of urls) {
|
|
10
|
-
const qr = await QRCode.toString(url, {
|
|
11
|
-
type: "terminal",
|
|
12
|
-
small: true,
|
|
13
|
-
margin: 0,
|
|
14
|
-
errorCorrectionLevel: "L",
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
codes.push(qr);
|
|
18
|
-
}
|
|
19
|
-
return codes;
|
|
20
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import WebSocket from "ws";
|
|
2
|
-
import { ReqResPair, WebDebuggerSocketRequest } from "../../types.js";
|
|
3
|
-
import { logger } from "../../logger.js";
|
|
4
|
-
import { getTuiConfig } from "./config.js";
|
|
5
|
-
|
|
6
|
-
export interface WebDebuggerConnection {
|
|
7
|
-
close: () => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* We are using a WebSocket connection to the web debugger endpoint
|
|
12
|
-
* to receive real-time HTTP request and response data.
|
|
13
|
-
* @param webDebuggerUrl
|
|
14
|
-
* @param onUpdate
|
|
15
|
-
* @returns
|
|
16
|
-
*/
|
|
17
|
-
export function createWebDebuggerConnection(
|
|
18
|
-
webDebuggerUrl: string,
|
|
19
|
-
onUpdate: (pairs: ReqResPair[]) => void
|
|
20
|
-
): WebDebuggerConnection {
|
|
21
|
-
const pairs = new Map<number, ReqResPair>();
|
|
22
|
-
const pairKeys: number[] = [];
|
|
23
|
-
let socket: WebSocket | null = null;
|
|
24
|
-
let reconnectTimeout: NodeJS.Timeout | null = null;
|
|
25
|
-
let isStopped = false;
|
|
26
|
-
|
|
27
|
-
const config = getTuiConfig();
|
|
28
|
-
const maxPairs = config.maxRequestPairs;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Trim pairs to keep only the latest maxPairs entries
|
|
32
|
-
const trimPairs = () => {
|
|
33
|
-
while (pairKeys.length > maxPairs) {
|
|
34
|
-
const oldestKey = pairKeys.shift();
|
|
35
|
-
if (oldestKey !== undefined) {
|
|
36
|
-
pairs.delete(oldestKey);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Add or update a pair and track its key for ordering
|
|
43
|
-
const upsertPair = (key: number, pair: ReqResPair) => {
|
|
44
|
-
if (!pairs.has(key)) {
|
|
45
|
-
pairKeys.push(key);
|
|
46
|
-
}
|
|
47
|
-
pairs.set(key, pair);
|
|
48
|
-
trimPairs();
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const connect = () => {
|
|
52
|
-
const ws = new WebSocket(`ws://${webDebuggerUrl}/introspec/websocket`);
|
|
53
|
-
socket = ws;
|
|
54
|
-
|
|
55
|
-
ws.on("open", () => {
|
|
56
|
-
logger.info("Web debugger connected.");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
ws.on("message", (data) => {
|
|
60
|
-
try {
|
|
61
|
-
const raw = data.toString();
|
|
62
|
-
const parsed = JSON.parse(raw);
|
|
63
|
-
const msg = {
|
|
64
|
-
Req: parsed.req,
|
|
65
|
-
Res: parsed.res,
|
|
66
|
-
} as Partial<WebDebuggerSocketRequest>;
|
|
67
|
-
|
|
68
|
-
if (msg.Req) {
|
|
69
|
-
const { key } = msg.Req;
|
|
70
|
-
const existing = pairs.get(key) as ReqResPair | undefined;
|
|
71
|
-
const merged: ReqResPair = {
|
|
72
|
-
request: msg.Req,
|
|
73
|
-
response: existing?.response,
|
|
74
|
-
} as ReqResPair;
|
|
75
|
-
upsertPair(key, merged);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (msg.Res) {
|
|
79
|
-
const { key } = msg.Res;
|
|
80
|
-
const existing = pairs.get(key) as ReqResPair | undefined;
|
|
81
|
-
const merged: ReqResPair = {
|
|
82
|
-
request: existing?.request ?? ({} as any),
|
|
83
|
-
response: msg.Res,
|
|
84
|
-
} as ReqResPair;
|
|
85
|
-
upsertPair(key, merged);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Notify listener with reversed array (latest first)
|
|
89
|
-
const reversedPairs: ReqResPair[] = [];
|
|
90
|
-
for (let i = pairKeys.length - 1; i >= 0; i--) {
|
|
91
|
-
const key = pairKeys[i];
|
|
92
|
-
const pair = pairs.get(key);
|
|
93
|
-
if (pair) {
|
|
94
|
-
reversedPairs.push(pair);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
onUpdate(reversedPairs);
|
|
98
|
-
} catch (err: any) {
|
|
99
|
-
logger.error("Error parsing WebSocket message:", err.message || err);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
ws.on("close", () => {
|
|
104
|
-
logger.warn("Web debugger disconnected. Reconnecting in 5s...");
|
|
105
|
-
if (!isStopped) {
|
|
106
|
-
reconnectTimeout = setTimeout(connect, 5000);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
ws.on("error", (err) => {
|
|
111
|
-
logger.error(`WebSocket error: ${err.message}`);
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
connect();
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
close: () => {
|
|
119
|
-
isStopped = true;
|
|
120
|
-
if (socket) {
|
|
121
|
-
socket.close();
|
|
122
|
-
}
|
|
123
|
-
if (reconnectTimeout) {
|
|
124
|
-
clearTimeout(reconnectTimeout);
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
}
|
package/src/tui/ink/asciArt.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export const asciiArtPinggyLogo = `
|
|
2
|
-
██████╗ ██╗███╗ ██╗ ██████╗ ██████╗██╗ ██╗
|
|
3
|
-
██╔══██╗██║████╗ ██║██╔════╝ ██╔════╝╚██╗ ██╔╝
|
|
4
|
-
██████╔╝██║██╔██╗ ██║██║ ███╗██║ ███╗╚████╔╝
|
|
5
|
-
██╔═══╝ ██║██║╚██╗██║██║ ██║██║ ██║ ╚██╔╝
|
|
6
|
-
██║ ██║██║ ╚████║╚██████╔╝╚██████╔╝ ██║
|
|
7
|
-
╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ `;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import qrcode from "qrcode-terminal";
|
|
3
|
-
|
|
4
|
-
export function useQrCodes(urls: string[], isQrCodeRequested: boolean) {
|
|
5
|
-
const [qrCodes, setQrCodes] = useState<string[]>([]);
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (!isQrCodeRequested || urls.length === 0) return;
|
|
9
|
-
|
|
10
|
-
const generateAll = async () => {
|
|
11
|
-
const codes: string[] = [];
|
|
12
|
-
for (const url of urls) {
|
|
13
|
-
await new Promise<void>((resolve) => {
|
|
14
|
-
qrcode.generate(url, { small: true }, (qr) => {
|
|
15
|
-
codes.push(qr);
|
|
16
|
-
resolve();
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
setQrCodes(codes);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
generateAll();
|
|
24
|
-
}, [urls, isQrCodeRequested]);
|
|
25
|
-
|
|
26
|
-
return qrCodes;
|
|
27
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { logger } from "../../../logger.js";
|
|
3
|
-
|
|
4
|
-
export function useReqResHeaders(baseUrl?: string) {
|
|
5
|
-
const [headers, setHeaders] = useState<{ req: string; res: string } | null>(null);
|
|
6
|
-
|
|
7
|
-
async function fetchHeaders(key: number) {
|
|
8
|
-
if (!baseUrl) return;
|
|
9
|
-
try {
|
|
10
|
-
const [reqRes, resRes] = await Promise.all([
|
|
11
|
-
fetch(`http://${baseUrl}/introspec/getrawrequestheader`, {
|
|
12
|
-
headers: { "X-Introspec-Key": key.toString() },
|
|
13
|
-
}),
|
|
14
|
-
fetch(`http://${baseUrl}/introspec/getrawresponseheader`, {
|
|
15
|
-
headers: { "X-Introspec-Key": key.toString() },
|
|
16
|
-
}),
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
const [req, res] = await Promise.all([reqRes.text(), resRes.text()]);
|
|
20
|
-
setHeaders({ req, res });
|
|
21
|
-
} catch (err: any) {
|
|
22
|
-
logger.error("Error fetching headers:", err.message || err);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return { headers, fetchHeaders, clear: () => setHeaders(null) };
|
|
27
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
const TERMINAL_PADDING_X = 1;
|
|
4
|
-
|
|
5
|
-
export function useTerminalSize(): { columns: number; rows: number } {
|
|
6
|
-
const [size, setSize] = useState({
|
|
7
|
-
columns: (process.stdout.columns || 60) - TERMINAL_PADDING_X,
|
|
8
|
-
rows: process.stdout.rows || 20,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
function updateSize() {
|
|
13
|
-
setSize({
|
|
14
|
-
columns: (process.stdout.columns || 60) - TERMINAL_PADDING_X,
|
|
15
|
-
rows: process.stdout.rows || 20,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
process.stdout.on('resize', updateSize);
|
|
20
|
-
return () => {
|
|
21
|
-
process.stdout.off('resize', updateSize);
|
|
22
|
-
};
|
|
23
|
-
}, []);
|
|
24
|
-
|
|
25
|
-
return size;
|
|
26
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import { TunnelUsageType } from "@pinggy/pinggy";
|
|
3
|
-
|
|
4
|
-
export function useTunnelStats() {
|
|
5
|
-
const [stats, setStats] = useState<TunnelUsageType>({
|
|
6
|
-
elapsedTime: 0,
|
|
7
|
-
numLiveConnections: 0,
|
|
8
|
-
numTotalConnections: 0,
|
|
9
|
-
numTotalReqBytes: 0,
|
|
10
|
-
numTotalResBytes: 0,
|
|
11
|
-
numTotalTxBytes: 0,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
globalThis.__PINGGY_TUNNEL_STATS__ = (newStats: TunnelUsageType) => {
|
|
16
|
-
setStats({ ...newStats });
|
|
17
|
-
};
|
|
18
|
-
return () => {
|
|
19
|
-
delete globalThis.__PINGGY_TUNNEL_STATS__;
|
|
20
|
-
};
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
return stats;
|
|
24
|
-
}
|