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
package/src/types.ts
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import { PinggyOptions, TunnelUsageType } from "@pinggy/pinggy";
|
|
2
|
-
|
|
3
|
-
// Local representation of additional forwarding
|
|
4
|
-
export interface AdditionalForwarding {
|
|
5
|
-
localDomain: string;
|
|
6
|
-
localPort: number;
|
|
7
|
-
remoteDomain?: string;
|
|
8
|
-
remotePort?: number;
|
|
9
|
-
protocol?: 'http' | 'tcp' | 'udp' | 'tls';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export interface TunnelStatus {
|
|
14
|
-
tunnelid: string,
|
|
15
|
-
remoteurls: string[],
|
|
16
|
-
tunnelconfig: PinggyOptions,
|
|
17
|
-
status: Status,
|
|
18
|
-
stats: TunnelUsageType
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Enum for TunnelStateType
|
|
24
|
-
export enum TunnelStateType {
|
|
25
|
-
New = "idle",
|
|
26
|
-
Starting = "starting",
|
|
27
|
-
Running = "running",
|
|
28
|
-
Live = "live",
|
|
29
|
-
Closed = "closed",
|
|
30
|
-
Exited = "exited"
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Enum for TunnelErrorCodeType
|
|
34
|
-
export enum TunnelErrorCodeType {
|
|
35
|
-
NonResponsive = "non_responsive",
|
|
36
|
-
FailedToConnect = "failed_to_connect",
|
|
37
|
-
ErrorInAdditionalForwarding = "additional_forwarding_error",
|
|
38
|
-
WebdebuggerError = "webdebugger_error",
|
|
39
|
-
NoError = ""
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Enum for TunnelWarningCode
|
|
43
|
-
export enum TunnelWarningCode {
|
|
44
|
-
InvalidTunnelServePath = "INVALID_TUNNEL_SERVE_PATH",
|
|
45
|
-
UnknownWarning = "UNKNOWN_WARNING"
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Interface for Warning
|
|
49
|
-
export interface Warning {
|
|
50
|
-
code: TunnelWarningCode;
|
|
51
|
-
message: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Main Status interface
|
|
57
|
-
export interface Status {
|
|
58
|
-
state: TunnelStateType;
|
|
59
|
-
errorcode: TunnelErrorCodeType;
|
|
60
|
-
errormsg: string;
|
|
61
|
-
createdtimestamp: string;
|
|
62
|
-
starttimestamp: string;
|
|
63
|
-
endtimestamp: string;
|
|
64
|
-
warnings: Warning[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export type FinalConfig = (PinggyOptions & { configid: string }) & {
|
|
68
|
-
tunnelType: string[];
|
|
69
|
-
conf?: string;
|
|
70
|
-
saveconf?: string;
|
|
71
|
-
serve?: string;
|
|
72
|
-
remoteManagement?: string;
|
|
73
|
-
additionalForwarding?: AdditionalForwarding[];
|
|
74
|
-
manage?: string;
|
|
75
|
-
version?: boolean;
|
|
76
|
-
NoTUI?: boolean;
|
|
77
|
-
qrCode?: boolean;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export type ErrorCodeType =
|
|
81
|
-
| "INVALID_REQUEST_METHOD"
|
|
82
|
-
| "COULD_NOT_READ_BODY"
|
|
83
|
-
| "INTERNAL_SERVER_ERROR"
|
|
84
|
-
| "INVALID_DATA_FORMAT"
|
|
85
|
-
| "ERROR_STARTING_TUNNEL"
|
|
86
|
-
| "TUNNEL_WITH_ID_OR_CONFIG_ID_NOT_FOUND"
|
|
87
|
-
| "TUNNEL_WITH_ID_OR_CONFIG_ID_ALREADY_RUNNING"
|
|
88
|
-
| "WEBSOCKET_UPGRADE_FAILED"
|
|
89
|
-
| "REMOTE_MANAGEMENT_ALREADY_RUNNING"
|
|
90
|
-
| "REMOTE_MANAGEMENT_NOT_RUNNING"
|
|
91
|
-
| "REMOTE_MANAGEMENT_DESERIALIZATION_FAILED";
|
|
92
|
-
|
|
93
|
-
export const ErrorCode: Record<string, ErrorCodeType> = {
|
|
94
|
-
InvalidRequestMethodError: "INVALID_REQUEST_METHOD",
|
|
95
|
-
InvalidRequestBodyError: "COULD_NOT_READ_BODY",
|
|
96
|
-
InternalServerError: "INTERNAL_SERVER_ERROR",
|
|
97
|
-
InvalidBodyFormatError: "INVALID_DATA_FORMAT",
|
|
98
|
-
ErrorStartingTunnel: "ERROR_STARTING_TUNNEL",
|
|
99
|
-
TunnelNotFound: "TUNNEL_WITH_ID_OR_CONFIG_ID_NOT_FOUND",
|
|
100
|
-
TunnelAlreadyRunningError: "TUNNEL_WITH_ID_OR_CONFIG_ID_ALREADY_RUNNING",
|
|
101
|
-
WebsocketUpgradeFailError: "WEBSOCKET_UPGRADE_FAILED",
|
|
102
|
-
RemoteManagementAlreadyRunning: "REMOTE_MANAGEMENT_ALREADY_RUNNING",
|
|
103
|
-
RemoteManagementNotRunning: "REMOTE_MANAGEMENT_NOT_RUNNING",
|
|
104
|
-
RemoteManagementDeserializationFailed: "REMOTE_MANAGEMENT_DESERIALIZATION_FAILED",
|
|
105
|
-
} as const;
|
|
106
|
-
|
|
107
|
-
export interface ErrorResponse {
|
|
108
|
-
code: ErrorCodeType;
|
|
109
|
-
message: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function isErrorResponse(obj: unknown): obj is ErrorResponse {
|
|
113
|
-
return (
|
|
114
|
-
typeof obj === 'object' &&
|
|
115
|
-
obj !== null &&
|
|
116
|
-
'code' in obj &&
|
|
117
|
-
'message' in obj &&
|
|
118
|
-
typeof (obj as ErrorResponse).message === 'string' &&
|
|
119
|
-
Object.values(ErrorCode).includes((obj as ErrorResponse).code)
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function newErrorResponse(errorResponse: ErrorResponse): ErrorResponse;
|
|
124
|
-
export function newErrorResponse(code: ErrorCodeType, message: string): ErrorResponse;
|
|
125
|
-
export function newErrorResponse(codeOrError: ErrorCodeType | ErrorResponse, message?: string): ErrorResponse {
|
|
126
|
-
if (typeof codeOrError === 'object') {
|
|
127
|
-
return codeOrError;
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
code: codeOrError,
|
|
131
|
-
message: message!
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
export interface ResponseObj {
|
|
137
|
-
response: Uint8Array;
|
|
138
|
-
requestid: string;
|
|
139
|
-
command: string;
|
|
140
|
-
error: boolean;
|
|
141
|
-
errorresponse: ErrorResponse;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function NewResponseObject(data: unknown): ResponseObj {
|
|
145
|
-
const encoder = new TextEncoder();
|
|
146
|
-
const bytes = encoder.encode(JSON.stringify(data));
|
|
147
|
-
return {
|
|
148
|
-
response: bytes,
|
|
149
|
-
requestid: "",
|
|
150
|
-
command: "",
|
|
151
|
-
error: false,
|
|
152
|
-
errorresponse: {} as ErrorResponse,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function NewErrorResponseObject(errorResponse: ErrorResponse): ResponseObj {
|
|
157
|
-
return {
|
|
158
|
-
response: new Uint8Array(),
|
|
159
|
-
requestid: "",
|
|
160
|
-
command: "",
|
|
161
|
-
error: true,
|
|
162
|
-
errorresponse: errorResponse,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function newStatus(
|
|
167
|
-
tunnelState: TunnelStateType,
|
|
168
|
-
errorCode: TunnelErrorCodeType,
|
|
169
|
-
errorMsg: string,
|
|
170
|
-
): Status {
|
|
171
|
-
let assignedState = tunnelState;
|
|
172
|
-
if (tunnelState === TunnelStateType.Live) {
|
|
173
|
-
assignedState = TunnelStateType.Running;
|
|
174
|
-
} else if (tunnelState === TunnelStateType.New) {
|
|
175
|
-
assignedState = TunnelStateType.New;
|
|
176
|
-
} else if (tunnelState === TunnelStateType.Closed) {
|
|
177
|
-
assignedState = TunnelStateType.Exited;
|
|
178
|
-
}
|
|
179
|
-
const now = new Date().toISOString();
|
|
180
|
-
return {
|
|
181
|
-
state: assignedState,
|
|
182
|
-
errorcode: errorCode,
|
|
183
|
-
errormsg: errorMsg,
|
|
184
|
-
createdtimestamp: now,
|
|
185
|
-
starttimestamp: now,
|
|
186
|
-
endtimestamp: now,
|
|
187
|
-
warnings: []
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function newStats(): TunnelUsageType {
|
|
192
|
-
return {
|
|
193
|
-
numLiveConnections: 0,
|
|
194
|
-
numTotalConnections: 0,
|
|
195
|
-
numTotalReqBytes: 0,
|
|
196
|
-
numTotalResBytes: 0,
|
|
197
|
-
numTotalTxBytes: 0,
|
|
198
|
-
elapsedTime: 0,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export interface Request {
|
|
203
|
-
key: number;
|
|
204
|
-
method: string;
|
|
205
|
-
uri: string;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export interface Response {
|
|
209
|
-
key: number;
|
|
210
|
-
status: string;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export interface ReqResPair {
|
|
214
|
-
request: Request;
|
|
215
|
-
response: Response;
|
|
216
|
-
reqHeaders: string;
|
|
217
|
-
resHeaders: string;
|
|
218
|
-
headersLoaded: boolean;
|
|
219
|
-
}
|
|
220
|
-
export interface StatsAll {
|
|
221
|
-
activeConn: number;
|
|
222
|
-
numRequests: number;
|
|
223
|
-
numResponses: number;
|
|
224
|
-
reqBytes: number;
|
|
225
|
-
resBytes: number;
|
|
226
|
-
totalConn: number;
|
|
227
|
-
}
|
|
228
|
-
export interface WebDebuggerSocketRequest {
|
|
229
|
-
Req: Request;
|
|
230
|
-
Res: Response;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export type RemoteManagementStatusType =
|
|
234
|
-
| "CONNECTING"
|
|
235
|
-
| "DISCONNECTING"
|
|
236
|
-
| "RECONNECTING"
|
|
237
|
-
| "RUNNING"
|
|
238
|
-
| "NOT_RUNNING"
|
|
239
|
-
| "ERROR";
|
|
240
|
-
|
|
241
|
-
export const RemoteManagementStatus: Record<string, RemoteManagementStatusType> = {
|
|
242
|
-
Connecting: "CONNECTING",
|
|
243
|
-
Disconnecting: "DISCONNECTING",
|
|
244
|
-
Reconnecting: "RECONNECTING",
|
|
245
|
-
Running: "RUNNING",
|
|
246
|
-
NotRunning: "NOT_RUNNING",
|
|
247
|
-
Error: "ERROR",
|
|
248
|
-
} as const;
|
|
249
|
-
|
|
250
|
-
export interface RemoteManagementState {
|
|
251
|
-
status: RemoteManagementStatusType;
|
|
252
|
-
errorMessage: string;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
package/src/utils/FileServer.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { createServer } from "http";
|
|
2
|
-
import { readFile, readdir, stat } from "fs/promises";
|
|
3
|
-
import { existsSync } from "fs";
|
|
4
|
-
import { extname, join, resolve, relative } from "path";
|
|
5
|
-
import { URL } from "url";
|
|
6
|
-
import mime from "mime";
|
|
7
|
-
import { logger } from "../logger.js";
|
|
8
|
-
import { directoryListingHtml, invalidPathErrorHtml } from "./htmlTemplates.js";
|
|
9
|
-
|
|
10
|
-
export class FileServerError extends Error {
|
|
11
|
-
code: string;
|
|
12
|
-
constructor(message: string, code: string) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.name = "FileServerError";
|
|
15
|
-
this.code = code;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function startFileServer(dirPath: string, port = 8080) {
|
|
20
|
-
let invalidPathError: FileServerError | null = null;
|
|
21
|
-
const root = resolve(dirPath);
|
|
22
|
-
logger.debug("Starting file server with root:", root, "on port:", port);
|
|
23
|
-
|
|
24
|
-
if (!existsSync(root)) {
|
|
25
|
-
logger.debug("Invalid root path for file server:", root);
|
|
26
|
-
invalidPathError = new FileServerError(`The path ${dirPath} does not exist. Please check the path and try again.`, "INVALID_TUNNEL_SERVE_PATH");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const server = createServer(async (req, res) => {
|
|
31
|
-
try {
|
|
32
|
-
// If invalid root, show an HTML error page
|
|
33
|
-
if (invalidPathError) {
|
|
34
|
-
const html = invalidPathErrorHtml(invalidPathError);
|
|
35
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
36
|
-
res.end(html);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const reqUrl = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
40
|
-
let filePath = join(root, decodeURIComponent(reqUrl.pathname));
|
|
41
|
-
|
|
42
|
-
let stats;
|
|
43
|
-
try {
|
|
44
|
-
stats = await stat(filePath);
|
|
45
|
-
} catch {
|
|
46
|
-
res.statusCode = 404;
|
|
47
|
-
res.end("404 Not Found");
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (stats.isDirectory() && !reqUrl.pathname.endsWith("/")) {
|
|
52
|
-
res.writeHead(301, { Location: reqUrl.pathname + "/" });
|
|
53
|
-
res.end();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Directory handling
|
|
58
|
-
if (stats.isDirectory()) {
|
|
59
|
-
const indexPath = join(filePath, "index.html");
|
|
60
|
-
if (existsSync(indexPath)) {
|
|
61
|
-
filePath = indexPath;
|
|
62
|
-
} else {
|
|
63
|
-
// No index.html — show directory listing
|
|
64
|
-
const items = await readdir(filePath, { withFileTypes: true });
|
|
65
|
-
|
|
66
|
-
const list = items
|
|
67
|
-
.map((item) => {
|
|
68
|
-
// Get the display name
|
|
69
|
-
const name = item.name + (item.isDirectory() ? "/" : "");
|
|
70
|
-
// Construct the current base URL
|
|
71
|
-
const base = new URL(reqUrl.pathname, `http://${req.headers.host}`);
|
|
72
|
-
// Create the full URL
|
|
73
|
-
const hrefUrl = new URL(encodeURIComponent(name), base);
|
|
74
|
-
return `<li><a href="${hrefUrl.pathname}">${name}</a></li>`;
|
|
75
|
-
})
|
|
76
|
-
.join("");
|
|
77
|
-
|
|
78
|
-
const relativePath = relative(root, filePath) || "/";
|
|
79
|
-
const html = directoryListingHtml(relativePath, list);
|
|
80
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
81
|
-
res.end(html);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Normal file serving
|
|
87
|
-
const content = await readFile(filePath);
|
|
88
|
-
const type = mime.getType(extname(filePath)) || "application/octet-stream";
|
|
89
|
-
res.writeHead(200, { "Content-Type": type });
|
|
90
|
-
res.end(content);
|
|
91
|
-
} catch (err: any) {
|
|
92
|
-
logger.debug("Error in handling request", err)
|
|
93
|
-
res.statusCode = 500;
|
|
94
|
-
res.end(`Internal Server Error: ${err.message}`);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
await new Promise<void>((resolve, reject) => {
|
|
99
|
-
server.listen(port, () => {
|
|
100
|
-
resolve();
|
|
101
|
-
});
|
|
102
|
-
server.on("error", (err) => {
|
|
103
|
-
logger.debug("Error starting file server", err);
|
|
104
|
-
reject(err);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
hasInvalidPath: !!invalidPathError,
|
|
110
|
-
error: invalidPathError ? { message: invalidPathError.message, code: invalidPathError.code } : null
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { exec, execSync } from "child_process";
|
|
4
|
-
import os from "os";
|
|
5
|
-
import CLIPrinter from "./printer.js";
|
|
6
|
-
import { promisify } from "util";
|
|
7
|
-
|
|
8
|
-
const execAsync = promisify(exec);
|
|
9
|
-
|
|
10
|
-
const DLLS = ["vcruntime140.dll", "vcruntime140_1.dll", "msvcp140.dll"];
|
|
11
|
-
|
|
12
|
-
const PATHS = ["C:\\Windows\\System32", "C:\\Windows\\SysWOW64"];
|
|
13
|
-
|
|
14
|
-
// Registry keys for different VC++ Redistributable versions
|
|
15
|
-
const REGISTRY_KEYS = [
|
|
16
|
-
"HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
|
|
17
|
-
"HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X86",
|
|
18
|
-
"HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
|
|
19
|
-
"HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X86",
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check if VC++ Redistributable DLLs exist
|
|
24
|
-
*/
|
|
25
|
-
function checkDLLs() {
|
|
26
|
-
return DLLS.every((dll) =>
|
|
27
|
-
PATHS.some((p) => {
|
|
28
|
-
try {
|
|
29
|
-
return fs.existsSync(path.join(p, dll));
|
|
30
|
-
} catch {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}),
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Check Windows Registry for VC++ Redistributable installation
|
|
39
|
-
*/
|
|
40
|
-
function checkRegistry() {
|
|
41
|
-
if (os.platform() !== "win32") return false;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
for (const key of REGISTRY_KEYS) {
|
|
45
|
-
const cmd = `reg query "${key}" /v Installed 2>nul`;
|
|
46
|
-
const result = execSync(cmd, { encoding: "utf8" });
|
|
47
|
-
|
|
48
|
-
if (result.includes("0x1")) {
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
// Registry check failed, fall back to DLL check
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function getVCRedistVersion() {
|
|
60
|
-
if (os.platform() !== "win32") return null;
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
for (const key of REGISTRY_KEYS) {
|
|
64
|
-
const cmd = `reg query "${key}" /v Version 2>nul`;
|
|
65
|
-
const result = execSync(cmd, { encoding: "utf8" });
|
|
66
|
-
|
|
67
|
-
const match = result.match(/Version\s+REG_SZ\s+(\S+)/);
|
|
68
|
-
if (match) {
|
|
69
|
-
return match[1];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
// Ignore errors
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Main detection function
|
|
81
|
-
*/
|
|
82
|
-
export function hasVCRedist() {
|
|
83
|
-
if (os.platform() !== "win32") {
|
|
84
|
-
return true; // Not Windows, assume OK
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (checkRegistry()) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (checkDLLs()) {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function getVCRedistStatus() {
|
|
99
|
-
if (os.platform() !== "win32") {
|
|
100
|
-
return {
|
|
101
|
-
required: false,
|
|
102
|
-
installed: true,
|
|
103
|
-
version: null,
|
|
104
|
-
method: "non-windows",
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const registryInstalled = checkRegistry();
|
|
109
|
-
const dllsPresent = checkDLLs();
|
|
110
|
-
const version = getVCRedistVersion();
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
required: true,
|
|
114
|
-
installed: registryInstalled || dllsPresent,
|
|
115
|
-
version,
|
|
116
|
-
registryCheck: registryInstalled,
|
|
117
|
-
dllCheck: dllsPresent,
|
|
118
|
-
method: registryInstalled ? "registry" : dllsPresent ? "dll" : "none",
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Open download page in browser
|
|
124
|
-
*/
|
|
125
|
-
export async function openDownloadPage() {
|
|
126
|
-
if (process.platform !== "win32") {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const url =
|
|
130
|
-
"https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170";
|
|
131
|
-
|
|
132
|
-
// Use cmd.exe explicitly for better compatibility
|
|
133
|
-
const command = `cmd.exe /c start "" "${url}"`;
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
await execAsync(command);
|
|
137
|
-
CLIPrinter.info("\nOpening Microsoft download page in your browser...");
|
|
138
|
-
CLIPrinter.info(
|
|
139
|
-
"Please install the Visual C++ Runtime and restart this application.\n",
|
|
140
|
-
);
|
|
141
|
-
} catch (err) {
|
|
142
|
-
CLIPrinter.info("\nUnable to open your browser automatically.");
|
|
143
|
-
CLIPrinter.info(
|
|
144
|
-
"Please visit the following page to download the runtime:\n",
|
|
145
|
-
);
|
|
146
|
-
CLIPrinter.info(url + "\n");
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Get error message
|
|
153
|
-
*/
|
|
154
|
-
export function getVCRedistMessage() {
|
|
155
|
-
const status = getVCRedistStatus();
|
|
156
|
-
|
|
157
|
-
if (!status.required || status.installed) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
error: true,
|
|
163
|
-
message:
|
|
164
|
-
"Missing Microsoft Visual C++ Runtime. This application requires the Microsoft Visual C++ Runtime to run on Windows.\n" +
|
|
165
|
-
"Please download and install it using the link below, then restart this application.\n",
|
|
166
|
-
};
|
|
167
|
-
}
|
package/src/utils/getFreePort.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import net from "net";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get a free TCP port.
|
|
5
|
-
* If `providedPort` is available, returns that.
|
|
6
|
-
* Otherwise, returns a randomly assigned free port from the OS.
|
|
7
|
-
*/
|
|
8
|
-
export function getFreePort(webDebugger: string): Promise<number> {
|
|
9
|
-
return new Promise((resolve, reject) => {
|
|
10
|
-
// Try provided port first
|
|
11
|
-
const tryPort = (portToTry: number) => {
|
|
12
|
-
const server = net.createServer();
|
|
13
|
-
|
|
14
|
-
server.unref();
|
|
15
|
-
|
|
16
|
-
server.on("error", (err) => {
|
|
17
|
-
// If provided port failed, try random port (0)
|
|
18
|
-
if (portToTry !== 0) {
|
|
19
|
-
tryPort(0);
|
|
20
|
-
} else {
|
|
21
|
-
reject(err);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
server.listen(portToTry, () => {
|
|
26
|
-
const address: net.AddressInfo = server.address() as net.AddressInfo;
|
|
27
|
-
const port = address ? address.port : 0;
|
|
28
|
-
server.close(() => resolve(port));
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
let providedPort = 0;
|
|
32
|
-
if (webDebugger && webDebugger.includes(":")) {
|
|
33
|
-
const portPart = webDebugger.split(":")[1];
|
|
34
|
-
const parsed = parseInt(portPart, 10);
|
|
35
|
-
if (!isNaN(parsed) && parsed > 0 && parsed < 65536) {
|
|
36
|
-
providedPort = parsed;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
tryPort(providedPort);
|
|
40
|
-
});
|
|
41
|
-
}
|