pinggy 0.3.0 → 0.3.2
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/.github/workflows/publish-binaries.yml +50 -54
- package/Makefile +2 -2
- package/dist/index.cjs +1060 -902
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1039 -129
- package/ent.plist +14 -0
- package/package.json +31 -14
- package/scripts/pre_pkg_processing.js +74 -0
- package/src/cli/buildConfig.ts +10 -13
- package/src/cli/{starCli.tsx → starCli.ts} +51 -80
- package/src/index.ts +0 -6
- package/src/remote_management/remoteManagement.ts +0 -3
- package/src/remote_management/remote_schema.ts +2 -2
- package/src/tui/blessed/TunnelTui.ts +298 -0
- package/src/tui/blessed/components/DisplayUpdaters.ts +118 -0
- package/src/tui/blessed/components/KeyBindings.ts +134 -0
- package/src/tui/blessed/components/Modals.ts +216 -0
- package/src/tui/blessed/components/UIComponents.ts +306 -0
- package/src/tui/blessed/components/index.ts +4 -0
- package/src/tui/blessed/headerFetcher.ts +35 -0
- package/src/tui/blessed/index.ts +2 -0
- package/src/tui/blessed/qrCodeGenerator.ts +20 -0
- package/src/tui/blessed/webDebuggerConnection.ts +100 -0
- package/src/tui/{hooks → ink/hooks}/useReqResHeaders.ts +1 -1
- package/src/tui/{hooks → ink/hooks}/useWebDebugger.ts +2 -2
- package/src/tui/{index.tsx → ink/index.tsx} +12 -2
- package/src/tui/spinner/spinner.ts +64 -0
- package/src/tunnel_manager/TunnelManager.ts +9 -10
- package/src/types.ts +1 -1
- package/src/utils/printer.ts +15 -21
- package/src/utils/util.ts +5 -0
- package/tsconfig.json +1 -1
- package/dist/tui-AZUFY7T2.js +0 -584
- package/src/utils/esmOnlyPackageLoader.ts +0 -29
- /package/src/tui/{asciArt.ts → ink/asciArt.ts} +0 -0
- /package/src/tui/{hooks → ink/hooks}/useQrCodes.ts +0 -0
- /package/src/tui/{hooks → ink/hooks}/useTerminalSize.ts +0 -0
- /package/src/tui/{hooks → ink/hooks}/useTerminalStats.ts +0 -0
- /package/src/tui/{layout → ink/layout}/Borders.tsx +0 -0
- /package/src/tui/{layout → ink/layout}/Container.tsx +0 -0
- /package/src/tui/{sections → ink/sections}/DebuggerDetailModal.tsx +0 -0
- /package/src/tui/{sections → ink/sections}/KeyBindings.tsx +0 -0
- /package/src/tui/{sections → ink/sections}/QrCodeSection.tsx +0 -0
- /package/src/tui/{sections → ink/sections}/StatsSection.tsx +0 -0
- /package/src/tui/{sections → ink/sections}/URLsSection.tsx +0 -0
- /package/src/tui/{utils → ink/utils}/utils.ts +0 -0
package/ent.plist
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- Entitlement for mac os signing since allow-unsigned-executable-memory is required -->
|
|
2
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
3
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
4
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
5
|
+
<plist version="1.0">
|
|
6
|
+
<dict>
|
|
7
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
8
|
+
<true/>
|
|
9
|
+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
10
|
+
<true/>
|
|
11
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
12
|
+
<true/>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinggy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Create secure, shareable tunnels to your localhost and manage them from the command line. ",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"module": ".dist/index.js",
|
|
9
9
|
"types": ".dist/index.d.ts",
|
|
10
10
|
"bin": {
|
|
11
|
-
"pinggy": "dist/index.
|
|
11
|
+
"pinggy": "dist/index.cjs"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build:tsc": "tsc",
|
|
@@ -18,7 +18,19 @@
|
|
|
18
18
|
"bump": "node scripts/bumpVersion.js --bump && npm install",
|
|
19
19
|
"bump:minor": "node scripts/bumpVersion.js --bump --minor && npm install",
|
|
20
20
|
"bump:major": "node scripts/bumpVersion.js --bump --major && npm install",
|
|
21
|
-
"dev": "npm link @pinggy/pinggy && npm run build && npm link "
|
|
21
|
+
"dev": "npm link @pinggy/pinggy && npm run build && npm link ",
|
|
22
|
+
"pre-pkg": "node scripts/pre_pkg_processing.js remove",
|
|
23
|
+
"post-pkg": "node scripts/pre_pkg_processing.js restore",
|
|
24
|
+
"pack:linux-x64": "npm run pre-pkg && pkg . --targets node20-linux-x64 --output out/pinggy-linux-x64 && npm run post-pkg",
|
|
25
|
+
"pack:linux-arm64": "npm run pre-pkg && pkg . --targets node20-linux-arm64 --output out/pinggy-linux-arm64 && npm run post-pkg",
|
|
26
|
+
"pack:linux": "npm run pack:linux-x64 && npm run pack:linux-arm64",
|
|
27
|
+
"pack:win-x64": "npm run pre-pkg && pkg . --targets node20-win-x64 --output out/pinggy-win-x64.exe && npm run post-pkg",
|
|
28
|
+
"pack:win-arm64": "npm run pre-pkg && pkg . --targets node20-win-arm64 --output out/pinggy-win-arm64.exe && npm run post-pkg",
|
|
29
|
+
"pack:windows": "npm run pack:win-x64 && npm run pack:win-arm64",
|
|
30
|
+
"pack:macos-x64": "npm run pre-pkg && pkg . --targets node20-macos-x64 --output out/pinggy-macos-x64 && npm run post-pkg",
|
|
31
|
+
"pack:macos-arm64": "npm run pre-pkg && pkg . --targets node20-macos-arm64 --output out/pinggy-macos-arm64 && npm run post-pkg",
|
|
32
|
+
"pack:macos": "npm run pack:macos-x64 && npm run pack:macos-arm64",
|
|
33
|
+
"pack:all": "npm run pack:linux-x64 && npm run pack:linux-arm64 && npm run pack:win-x64 && npm run pack:macos-x64 && npm run pack:macos-arm64"
|
|
22
34
|
},
|
|
23
35
|
"exports": {
|
|
24
36
|
".": {
|
|
@@ -27,30 +39,35 @@
|
|
|
27
39
|
"require": "./dist/index.cjs"
|
|
28
40
|
}
|
|
29
41
|
},
|
|
42
|
+
"pkg": {
|
|
43
|
+
"scripts": [
|
|
44
|
+
"dist/index.cjs"
|
|
45
|
+
],
|
|
46
|
+
"assets": [
|
|
47
|
+
"node_modules/@pinggy/pinggy/**/*",
|
|
48
|
+
"node_modules/blessed/lib/**/*",
|
|
49
|
+
"node_modules/blessed/usr/**/*"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
30
52
|
"dependencies": {
|
|
31
|
-
"@pinggy/pinggy": "^0.3.
|
|
32
|
-
"
|
|
53
|
+
"@pinggy/pinggy": "^0.3.2",
|
|
54
|
+
"blessed": "^0.1.81",
|
|
33
55
|
"clipboardy": "^5.0.0",
|
|
34
|
-
"fullscreen-ink": "^0.1.0",
|
|
35
|
-
"ink": "^6.3.1",
|
|
36
|
-
"ink-big-text": "^2.0.0",
|
|
37
|
-
"ink-gradient": "^3.0.0",
|
|
38
56
|
"mime": "^4.1.0",
|
|
39
|
-
"
|
|
40
|
-
"qrcode
|
|
41
|
-
"react": "^19.1.1",
|
|
42
|
-
"uuid": "^12.0.0",
|
|
57
|
+
"picocolors": "^1.1.1",
|
|
58
|
+
"qrcode": "^1.5.4",
|
|
43
59
|
"winston": "^3.17.0",
|
|
44
60
|
"ws": "^8.18.0",
|
|
45
61
|
"zod": "^4.1.5"
|
|
46
62
|
},
|
|
47
63
|
"devDependencies": {
|
|
48
64
|
"@appthreat/caxa": "^1.0.13",
|
|
65
|
+
"@types/blessed": "^0.1.25",
|
|
49
66
|
"@types/jest": "^30.0.0",
|
|
50
67
|
"@types/node": "^24.3.0",
|
|
51
68
|
"@types/qrcode-terminal": "^0.12.2",
|
|
52
|
-
"@types/react": "^19.2.0",
|
|
53
69
|
"@types/ws": "^8.18.1",
|
|
70
|
+
"@yao-pkg/pkg": "^6.11.0",
|
|
54
71
|
"jest": "^30.1.2",
|
|
55
72
|
"ts-jest": "^29.4.1",
|
|
56
73
|
"ts-node": "^10.9.2",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script to temporarily remove "type": "module" from package.json before running pkg
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
14
|
+
const backupPath = join(__dirname, '..', 'package.json.backup');
|
|
15
|
+
|
|
16
|
+
function main() {
|
|
17
|
+
const action = process.argv[2] || 'remove';
|
|
18
|
+
|
|
19
|
+
if (action === 'remove') {
|
|
20
|
+
removeTypeModule();
|
|
21
|
+
} else if (action === 'restore') {
|
|
22
|
+
restoreTypeModule();
|
|
23
|
+
} else {
|
|
24
|
+
console.error('Usage: node pre-pkg.js [remove|restore]');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function removeTypeModule() {
|
|
30
|
+
try {
|
|
31
|
+
// Read package.json
|
|
32
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
33
|
+
|
|
34
|
+
// Create backup if type module exists
|
|
35
|
+
if (packageJson.type === 'module') {
|
|
36
|
+
writeFileSync(backupPath, JSON.stringify(packageJson, null, 2));
|
|
37
|
+
console.log('Backup created at package.json.backup');
|
|
38
|
+
|
|
39
|
+
// Remove type module
|
|
40
|
+
delete packageJson.type;
|
|
41
|
+
|
|
42
|
+
// Write back to package.json
|
|
43
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
44
|
+
console.log('Removed "type": "module" from package.json');
|
|
45
|
+
} else {
|
|
46
|
+
console.log('No "type": "module" found in package.json');
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Error removing type module:', error.message);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function restoreTypeModule() {
|
|
55
|
+
try {
|
|
56
|
+
if (existsSync(backupPath)) {
|
|
57
|
+
// Restore from backup
|
|
58
|
+
const backupContent = readFileSync(backupPath, 'utf8');
|
|
59
|
+
writeFileSync(packageJsonPath, backupContent);
|
|
60
|
+
|
|
61
|
+
// Remove backup file
|
|
62
|
+
unlinkSync(backupPath);
|
|
63
|
+
|
|
64
|
+
console.log('Restored package.json from backup');
|
|
65
|
+
} else {
|
|
66
|
+
console.log('No backup file found, nothing to restore');
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Error restoring package.json:', error.message);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main();
|
package/src/cli/buildConfig.ts
CHANGED
|
@@ -4,11 +4,10 @@ import { logger } from "../logger.js";
|
|
|
4
4
|
import { FinalConfig, Forwarding } from "../types.js";
|
|
5
5
|
import { ParsedValues } from "../utils/parseArgs.js";
|
|
6
6
|
import { cliOptions } from "./options.js";
|
|
7
|
-
import { isValidPort } from "../utils/util.js";
|
|
7
|
+
import { getRandomId, isValidPort } from "../utils/util.js";
|
|
8
8
|
import { TunnelType } from "@pinggy/pinggy";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path from "path";
|
|
11
|
-
import { getUuid } from "../utils/esmOnlyPackageLoader.js";
|
|
12
11
|
|
|
13
12
|
const domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
14
13
|
|
|
@@ -188,7 +187,7 @@ function parseForwarding(forwarding: string): Forwarding | Error {
|
|
|
188
187
|
|
|
189
188
|
function parseReverseTunnelAddr(finalConfig: FinalConfig, values: ParsedValues<typeof cliOptions>): Error | null {
|
|
190
189
|
const reverseTunnel = values.R;
|
|
191
|
-
if ((!Array.isArray(reverseTunnel) || reverseTunnel.length === 0) && !values.localport) {
|
|
190
|
+
if ((!Array.isArray(reverseTunnel) || reverseTunnel.length === 0) && !values.localport && !finalConfig.forwarding) {
|
|
192
191
|
return new Error("local port not specified. Please use '-h' option for help.");
|
|
193
192
|
}
|
|
194
193
|
|
|
@@ -314,9 +313,6 @@ export async function buildFinalConfig(values: ParsedValues<typeof cliOptions>,
|
|
|
314
313
|
let saveconf = isSaveConfOption(values);
|
|
315
314
|
|
|
316
315
|
const configFromFile = loadJsonConfig(values);
|
|
317
|
-
if (configFromFile !== null) {
|
|
318
|
-
finalConfig = { ...configFromFile };
|
|
319
|
-
}
|
|
320
316
|
|
|
321
317
|
const userParse = parseUsers(positionals, values.token);
|
|
322
318
|
token = userParse.token;
|
|
@@ -329,13 +325,14 @@ export async function buildFinalConfig(values: ParsedValues<typeof cliOptions>,
|
|
|
329
325
|
const initialTunnel = (type || values.type) as TunnelType;
|
|
330
326
|
finalConfig = {
|
|
331
327
|
...defaultOptions,
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
328
|
+
...(configFromFile || {}), // Apply loaded config on top of defaults
|
|
329
|
+
configid: getRandomId(),
|
|
330
|
+
token: token || (configFromFile?.token || (typeof values.token === 'string' ? values.token : '')),
|
|
331
|
+
serverAddress: server || (configFromFile?.serverAddress || defaultOptions.serverAddress),
|
|
332
|
+
tunnelType: initialTunnel ? [initialTunnel] : (configFromFile?.tunnelType || [TunnelType.Http]),
|
|
333
|
+
NoTUI: values.notui || (configFromFile?.NoTUI || false),
|
|
334
|
+
qrCode: qrCode || (configFromFile?.qrCode || false),
|
|
335
|
+
autoReconnect: values.autoreconnect || (configFromFile?.autoReconnect || false),
|
|
339
336
|
};
|
|
340
337
|
|
|
341
338
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import CLIPrinter from "../utils/printer.js";
|
|
2
|
-
import { TunnelManager } from "../tunnel_manager/TunnelManager.js";
|
|
3
|
-
import { loadChalk } from "../utils/esmOnlyPackageLoader.js";
|
|
2
|
+
import { ManagedTunnel, TunnelManager } from "../tunnel_manager/TunnelManager.js";
|
|
4
3
|
import { FinalConfig } from "../types.js";
|
|
5
4
|
import { getFreePort } from "../utils/getFreePort.js";
|
|
6
5
|
import { logger } from "../logger.js";
|
|
6
|
+
import pico from "picocolors";
|
|
7
|
+
import { TunnelTui } from "../tui/blessed/index.js"
|
|
7
8
|
|
|
8
9
|
interface TunnelData {
|
|
9
10
|
urls: string[] | null;
|
|
@@ -17,12 +18,7 @@ const TunnelData: TunnelData = {
|
|
|
17
18
|
usage: null,
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
let activeTui:
|
|
21
|
-
instance?: { unmount?: () => void };
|
|
22
|
-
start: () => Promise<void>;
|
|
23
|
-
waitUntilExit: () => Promise<void>;
|
|
24
|
-
} | null = null;
|
|
25
|
-
|
|
21
|
+
let activeTui: any = null; // TunnelTui type - loaded dynamically
|
|
26
22
|
|
|
27
23
|
let disconnectState: {
|
|
28
24
|
disconnected: boolean;
|
|
@@ -30,72 +26,47 @@ let disconnectState: {
|
|
|
30
26
|
messages?: string[];
|
|
31
27
|
} | null = null;
|
|
32
28
|
|
|
33
|
-
let updateDisconnectState: ((state: typeof disconnectState) => void) | null = null;
|
|
34
|
-
|
|
35
29
|
declare global {
|
|
36
30
|
var __PINGGY_TUNNEL_STATS__: ((stats: any) => void) | undefined;
|
|
37
31
|
}
|
|
38
32
|
|
|
39
|
-
async function launchTui(finalConfig: FinalConfig, urls: string[] | null, greet: string | null) {
|
|
33
|
+
async function launchTui(finalConfig: FinalConfig, urls: string[] | null, greet: string | null, tunnel: ManagedTunnel) {
|
|
40
34
|
try {
|
|
41
|
-
const { withFullScreen } = await import("fullscreen-ink");
|
|
42
|
-
const { default: TunnelTui } = await import("../tui/index.js");
|
|
43
|
-
const React = await import("react");
|
|
44
35
|
const isTTYEnabled = process.stdin.isTTY;
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
if (!isTTYEnabled) {
|
|
38
|
+
CLIPrinter.warn("Unable to initiate the TUI: your terminal does not support the required input mode.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<TunnelTui
|
|
58
|
-
urls={urls ?? []}
|
|
59
|
-
greet={greet ?? ""}
|
|
60
|
-
tunnelConfig={finalConfig}
|
|
61
|
-
disconnectInfo={disconnectInfo}
|
|
62
|
-
/>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const tui = withFullScreen(
|
|
67
|
-
<TunnelTuiWrapper
|
|
68
|
-
finalConfig={finalConfig}
|
|
69
|
-
urls={urls}
|
|
70
|
-
greet={greet}
|
|
71
|
-
/>
|
|
72
|
-
);
|
|
42
|
+
|
|
43
|
+
const tui = new TunnelTui({
|
|
44
|
+
urls: urls ?? [],
|
|
45
|
+
greet: greet ?? "",
|
|
46
|
+
tunnelConfig: finalConfig,
|
|
47
|
+
disconnectInfo: null,
|
|
48
|
+
tunnelInstance: tunnel,
|
|
49
|
+
});
|
|
73
50
|
|
|
74
51
|
activeTui = tui;
|
|
75
52
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
activeTui = null;
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
CLIPrinter.warn("Unable to initiate the TUI: your terminal does not support the required input mode.");
|
|
53
|
+
try {
|
|
54
|
+
tui.start();
|
|
55
|
+
await tui.waitUntilExit();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
logger.warn("TUI error", e);
|
|
58
|
+
} finally {
|
|
59
|
+
activeTui = null;
|
|
87
60
|
}
|
|
88
61
|
} catch (e) {
|
|
89
62
|
logger.warn("Failed to (re-)initiate TUI", e);
|
|
90
63
|
}
|
|
91
|
-
}
|
|
64
|
+
}
|
|
92
65
|
|
|
93
66
|
|
|
94
67
|
|
|
95
68
|
export async function startCli(finalConfig: FinalConfig, manager: TunnelManager) {
|
|
96
69
|
|
|
97
|
-
await CLIPrinter.ensureDeps();
|
|
98
|
-
const chalk = await loadChalk();
|
|
99
70
|
|
|
100
71
|
if (!finalConfig.NoTUI && finalConfig.webDebugger === "") {
|
|
101
72
|
// Need a webdebugger port
|
|
@@ -128,46 +99,46 @@ export async function startCli(finalConfig: FinalConfig, manager: TunnelManager)
|
|
|
128
99
|
|
|
129
100
|
|
|
130
101
|
await manager.startTunnel(tunnel.tunnelid);
|
|
131
|
-
CLIPrinter.stopSpinnerSuccess("Connected to Pinggy");
|
|
132
|
-
CLIPrinter.success(
|
|
133
|
-
CLIPrinter.print(
|
|
102
|
+
CLIPrinter.stopSpinnerSuccess(" Connected to Pinggy");
|
|
103
|
+
CLIPrinter.success(pico.bold("Tunnel established!"));
|
|
104
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
134
105
|
|
|
135
106
|
TunnelData.urls = await manager.getTunnelUrls(tunnel.tunnelid);
|
|
136
107
|
TunnelData.greet = await manager.getTunnelGreetMessage(tunnel.tunnelid);
|
|
137
108
|
|
|
138
|
-
CLIPrinter.info(
|
|
109
|
+
CLIPrinter.info(pico.cyanBright("Remote URLs:"));
|
|
139
110
|
(TunnelData.urls ?? []).forEach((url: string) =>
|
|
140
|
-
CLIPrinter.print(" " +
|
|
111
|
+
CLIPrinter.print(" " + pico.magentaBright(url))
|
|
141
112
|
);
|
|
142
|
-
CLIPrinter.print(
|
|
113
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
143
114
|
|
|
144
115
|
|
|
145
116
|
if (TunnelData.greet?.includes("not authenticated")) {
|
|
146
117
|
// show unauthenticated warning
|
|
147
|
-
CLIPrinter.warn(
|
|
118
|
+
CLIPrinter.warn(pico.yellowBright(TunnelData.greet));
|
|
148
119
|
} else if (TunnelData.greet?.includes("authenticated as")) {
|
|
149
120
|
// extract email
|
|
150
121
|
const emailMatch = /authenticated as (.+)/.exec(TunnelData.greet);
|
|
151
122
|
if (emailMatch) {
|
|
152
123
|
const email = emailMatch[1];
|
|
153
|
-
CLIPrinter.info(
|
|
124
|
+
CLIPrinter.info(pico.cyanBright("Authenticated as: " + email));
|
|
154
125
|
}
|
|
155
126
|
}
|
|
156
127
|
|
|
157
|
-
CLIPrinter.print(
|
|
158
|
-
CLIPrinter.print(
|
|
128
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
129
|
+
CLIPrinter.print(pico.gray("\nPress Ctrl+C to stop the tunnel.\n"));
|
|
159
130
|
|
|
160
131
|
manager.registerDisconnectListener(tunnel.tunnelid, async (tunnelId, error, messages) => {
|
|
161
|
-
if (activeTui
|
|
132
|
+
if (activeTui) {
|
|
162
133
|
disconnectState = {
|
|
163
134
|
disconnected: true,
|
|
164
135
|
error: error,
|
|
165
136
|
messages: messages
|
|
166
137
|
};
|
|
167
|
-
|
|
138
|
+
activeTui.updateDisconnectInfo(disconnectState);
|
|
168
139
|
|
|
169
140
|
try {
|
|
170
|
-
// Wait for
|
|
141
|
+
// Wait for Blessed TUI to fully exit
|
|
171
142
|
await activeTui.waitUntilExit();
|
|
172
143
|
} catch (e) {
|
|
173
144
|
logger.warn("Failed to wait for TUI exit", e);
|
|
@@ -175,10 +146,10 @@ export async function startCli(finalConfig: FinalConfig, manager: TunnelManager)
|
|
|
175
146
|
activeTui = null;
|
|
176
147
|
CLIPrinter.warn(`Error in tunnel:`);
|
|
177
148
|
messages?.forEach(function (m) {
|
|
178
|
-
CLIPrinter.
|
|
149
|
+
CLIPrinter.warnTxt(m)
|
|
179
150
|
});
|
|
180
151
|
|
|
181
|
-
// Exit ONLY after
|
|
152
|
+
// Exit ONLY after blessed has restored the terminal
|
|
182
153
|
// On disconnect only exit if autoReconnect is false otherwise retry will not work
|
|
183
154
|
if (!finalConfig.autoReconnect) {
|
|
184
155
|
process.exit(0);
|
|
@@ -210,34 +181,34 @@ export async function startCli(finalConfig: FinalConfig, manager: TunnelManager)
|
|
|
210
181
|
// ignore
|
|
211
182
|
}
|
|
212
183
|
|
|
213
|
-
CLIPrinter.success(
|
|
214
|
-
CLIPrinter.print(
|
|
184
|
+
CLIPrinter.success(pico.bold("Tunnel re-established!"));
|
|
185
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
215
186
|
|
|
216
187
|
TunnelData.urls = urls;
|
|
217
188
|
TunnelData.greet = await manager.getTunnelGreetMessage(tunnel.tunnelid);
|
|
218
189
|
|
|
219
|
-
CLIPrinter.info(
|
|
190
|
+
CLIPrinter.info(pico.cyanBright("Remote URLs:"));
|
|
220
191
|
(TunnelData.urls ?? []).forEach((url: string) =>
|
|
221
|
-
CLIPrinter.print(" " +
|
|
192
|
+
CLIPrinter.print(" " + pico.magentaBright(url))
|
|
222
193
|
);
|
|
223
|
-
CLIPrinter.print(
|
|
194
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
224
195
|
|
|
225
196
|
if (TunnelData.greet?.includes("not authenticated")) {
|
|
226
|
-
CLIPrinter.warn(
|
|
197
|
+
CLIPrinter.warn(pico.yellowBright(TunnelData.greet));
|
|
227
198
|
} else if (TunnelData.greet?.includes("authenticated as")) {
|
|
228
199
|
const emailMatch = /authenticated as (.+)/.exec(TunnelData.greet);
|
|
229
200
|
if (emailMatch) {
|
|
230
201
|
const email = emailMatch[1];
|
|
231
|
-
CLIPrinter.info(
|
|
202
|
+
CLIPrinter.info(pico.cyanBright("Authenticated as: " + email));
|
|
232
203
|
}
|
|
233
204
|
}
|
|
234
205
|
|
|
235
|
-
CLIPrinter.print(
|
|
236
|
-
CLIPrinter.print(
|
|
206
|
+
CLIPrinter.print(pico.gray("───────────────────────────────"));
|
|
207
|
+
CLIPrinter.print(pico.gray("\nPress Ctrl+C to stop the tunnel.\n"));
|
|
237
208
|
|
|
238
209
|
// If the TUI was enabled previously, re-create and start it
|
|
239
210
|
if (!finalConfig.NoTUI) {
|
|
240
|
-
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet);
|
|
211
|
+
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet, tunnel);
|
|
241
212
|
}
|
|
242
213
|
});
|
|
243
214
|
} catch (e) {
|
|
@@ -245,7 +216,7 @@ export async function startCli(finalConfig: FinalConfig, manager: TunnelManager)
|
|
|
245
216
|
}
|
|
246
217
|
|
|
247
218
|
if (!finalConfig.NoTUI) {
|
|
248
|
-
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet);
|
|
219
|
+
await launchTui(finalConfig, TunnelData.urls, TunnelData.greet,tunnel);
|
|
249
220
|
}
|
|
250
221
|
|
|
251
222
|
|
package/src/index.ts
CHANGED
|
@@ -15,16 +15,11 @@ import { argv } from 'process';
|
|
|
15
15
|
import { realpathSync } from 'fs';
|
|
16
16
|
import { enablePackageLogging } from "./logger.js"
|
|
17
17
|
import { getRemoteManagementState, initiateRemoteManagement, closeRemoteManagement } from "./remote_management/remoteManagement.js";
|
|
18
|
-
import { loadChalk } from "./utils/esmOnlyPackageLoader.js";
|
|
19
|
-
|
|
20
18
|
|
|
21
19
|
export { TunnelManager, TunnelOperations, TunnelResponse, enablePackageLogging, getRemoteManagementState, initiateRemoteManagement, closeRemoteManagement };
|
|
22
20
|
|
|
23
|
-
|
|
24
21
|
async function main() {
|
|
25
22
|
try {
|
|
26
|
-
await CLIPrinter.ensureDeps();
|
|
27
|
-
await loadChalk();
|
|
28
23
|
// Parse arguments from the command line
|
|
29
24
|
const { values, positionals, hasAnyArgs } = parseCliArgs(cliOptions);
|
|
30
25
|
|
|
@@ -65,7 +60,6 @@ async function main() {
|
|
|
65
60
|
logger.debug("Building final config from CLI values and positionals", { values, positionals });
|
|
66
61
|
const finalConfig = await buildFinalConfig(values, positionals);
|
|
67
62
|
logger.debug("Final configuration built", finalConfig);
|
|
68
|
-
|
|
69
63
|
await startCli(finalConfig, manager);
|
|
70
64
|
|
|
71
65
|
} catch (error) {
|
|
@@ -2,7 +2,6 @@ import WebSocket from "ws";
|
|
|
2
2
|
import { logger } from "../logger.js";
|
|
3
3
|
import { handleConnectionStatusMessage, WebSocketCommandHandler, WebSocketRequest } from "./websocket_handlers.js";
|
|
4
4
|
import CLIPrinter from "../utils/printer.js";
|
|
5
|
-
import { loadChalk } from "../utils/esmOnlyPackageLoader.js";
|
|
6
5
|
import { RemoteManagementState, RemoteManagementStatus } from "../types.js";
|
|
7
6
|
|
|
8
7
|
const RECONNECT_SLEEP_MS = 5000; // 5 seconds
|
|
@@ -70,8 +69,6 @@ export async function parseRemoteManagement(values: RemoteManagementValues): Pro
|
|
|
70
69
|
* - Keep running until closed or SIGINT
|
|
71
70
|
*/
|
|
72
71
|
export async function initiateRemoteManagement(token: string, manage?: string): Promise<RemoteManagementState> {
|
|
73
|
-
await CLIPrinter.ensureDeps();
|
|
74
|
-
await loadChalk();
|
|
75
72
|
|
|
76
73
|
if (!token || token.trim().length === 0) {
|
|
77
74
|
throw new Error("Remote management token is required (use --remote-management <TOKEN>)");
|
|
@@ -25,7 +25,7 @@ export const TunnelConfigSchema = z
|
|
|
25
25
|
autoreconnect: z.boolean(),
|
|
26
26
|
basicauth: z.array(z.object({ username: z.string(), password: z.string() })).nullable(),
|
|
27
27
|
bearerauth: z.string().nullable(),
|
|
28
|
-
configid: z.string()
|
|
28
|
+
configid: z.string(),
|
|
29
29
|
configname: z.string(),
|
|
30
30
|
greetmsg: z.string().optional(),
|
|
31
31
|
force: z.boolean(),
|
|
@@ -83,7 +83,7 @@ export const TunnelConfigSchema = z
|
|
|
83
83
|
*/
|
|
84
84
|
|
|
85
85
|
export const StartSchema = z.object({
|
|
86
|
-
tunnelID: z.string().
|
|
86
|
+
tunnelID: z.string().nullable().optional(),
|
|
87
87
|
tunnelConfig: TunnelConfigSchema,
|
|
88
88
|
});
|
|
89
89
|
|