pinggy 0.4.8 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+ // src/utils/configDir.ts
2
+ import os from "os";
3
+ import path from "path";
4
+ import fs from "fs";
5
+ function getPinggyConfigDir() {
6
+ const platform = os.platform();
7
+ let baseDir;
8
+ if (platform === "win32") {
9
+ baseDir = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
10
+ } else {
11
+ baseDir = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
12
+ }
13
+ return path.join(baseDir, "pinggy");
14
+ }
15
+ function getTunnelConfigDir() {
16
+ return path.join(getPinggyConfigDir(), "tunnels");
17
+ }
18
+ function ensureTunnelConfigDir() {
19
+ const dir = getTunnelConfigDir();
20
+ fs.mkdirSync(dir, { recursive: true });
21
+ return dir;
22
+ }
23
+ function getDaemonInfoPath() {
24
+ return path.join(getPinggyConfigDir(), "daemon.json");
25
+ }
26
+ function getDaemonConfigPath() {
27
+ return path.join(getPinggyConfigDir(), "daemon-config.json");
28
+ }
29
+ function getPinggyLogDir() {
30
+ const platform = os.platform();
31
+ if (platform === "win32") {
32
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
33
+ return path.join(localAppData, "Pinggy-CLI", "Logs");
34
+ }
35
+ if (platform === "darwin") {
36
+ return path.join(os.homedir(), "Library", "Logs", "Pinggy-CLI");
37
+ }
38
+ const stateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state");
39
+ return path.join(stateHome, "pinggy-cli", "logs");
40
+ }
41
+ function ensurePinggyLogDir() {
42
+ const dir = getPinggyLogDir();
43
+ fs.mkdirSync(dir, { recursive: true });
44
+ return dir;
45
+ }
46
+ function getTunnelLogDir() {
47
+ return path.join(getPinggyLogDir(), "tunnels");
48
+ }
49
+ function getTunnelLogPath(tunnelId, origin, name) {
50
+ const dir = getTunnelLogDir();
51
+ if (name) {
52
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
53
+ return path.join(dir, `${origin}__${sanitized}.log`);
54
+ }
55
+ return path.join(dir, `${origin}__${tunnelId}.log`);
56
+ }
57
+ function getDaemonLogPath() {
58
+ return path.join(getPinggyLogDir(), "daemon.log");
59
+ }
60
+ function ensurePinggyConfigDir() {
61
+ const dir = getPinggyConfigDir();
62
+ fs.mkdirSync(dir, { recursive: true });
63
+ return dir;
64
+ }
65
+
66
+ export {
67
+ getPinggyConfigDir,
68
+ getTunnelConfigDir,
69
+ ensureTunnelConfigDir,
70
+ getDaemonInfoPath,
71
+ getDaemonConfigPath,
72
+ ensurePinggyLogDir,
73
+ getTunnelLogDir,
74
+ getTunnelLogPath,
75
+ getDaemonLogPath,
76
+ ensurePinggyConfigDir
77
+ };
@@ -0,0 +1,269 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-7G6SJEEA.js";
4
+ import {
5
+ ensureTunnelConfigDir,
6
+ getTunnelConfigDir
7
+ } from "./chunk-GBYF2H4H.js";
8
+
9
+ // src/cli/configStore.ts
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import pico from "picocolors";
13
+ function buildFilename(name, configId) {
14
+ return `${name}_${configId}.json`;
15
+ }
16
+ function sanitizeName(name) {
17
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
18
+ }
19
+ var Subcommand = {
20
+ Config: "config",
21
+ Start: "start",
22
+ Stop: "stop",
23
+ Ps: "ps",
24
+ Attach: "attach",
25
+ Daemon: "daemon",
26
+ DaemonAlias: "d",
27
+ Logs: "logs",
28
+ Log: "log",
29
+ Restart: "restart"
30
+ };
31
+ var ConfigVerb = {
32
+ List: "list",
33
+ Ls: "ls",
34
+ Show: "show",
35
+ Save: "save",
36
+ Update: "update",
37
+ Delete: "delete",
38
+ Auto: "auto",
39
+ Noauto: "noauto"
40
+ };
41
+ var SUBCOMMANDS = Object.values(Subcommand);
42
+ var CONFIG_VERBS = Object.values(ConfigVerb);
43
+ var RESERVED_NAMES = /* @__PURE__ */ new Set([...SUBCOMMANDS, ...CONFIG_VERBS]);
44
+ function validateName(name) {
45
+ if (!name || name.trim().length === 0) {
46
+ return new Error("Tunnel name cannot be empty.");
47
+ }
48
+ if (name.length > 128) {
49
+ return new Error("Tunnel name cannot exceed 128 characters.");
50
+ }
51
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
52
+ return new Error("Tunnel name can only contain alphanumeric characters, hyphens, and underscores.");
53
+ }
54
+ if (RESERVED_NAMES.has(name.toLowerCase())) {
55
+ return new Error(`"${name}" is a reserved subcommand name. Use a different name.`);
56
+ }
57
+ return null;
58
+ }
59
+ function readConfigFile(filePath) {
60
+ try {
61
+ const data = fs.readFileSync(filePath, { encoding: "utf-8" });
62
+ return JSON.parse(data);
63
+ } catch (err) {
64
+ logger.warn(`Failed to read config file ${filePath}:`, err);
65
+ return null;
66
+ }
67
+ }
68
+ function writeConfigFile(filePath, config) {
69
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2), { encoding: "utf-8" });
70
+ }
71
+ function listSavedConfigs() {
72
+ const dir = getTunnelConfigDir();
73
+ if (!fs.existsSync(dir)) {
74
+ return [];
75
+ }
76
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
77
+ const configs = [];
78
+ for (const file of files) {
79
+ const config = readConfigFile(path.join(dir, file));
80
+ if (config && config.name && config.configId) {
81
+ configs.push(config);
82
+ }
83
+ }
84
+ return configs;
85
+ }
86
+ function findConfigFile(nameOrId) {
87
+ const dir = getTunnelConfigDir();
88
+ if (!fs.existsSync(dir)) return null;
89
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
90
+ const sanitized = sanitizeName(nameOrId);
91
+ const nameMatch = files.find((f) => f.startsWith(sanitized + "_"));
92
+ if (nameMatch) {
93
+ const filePath = path.join(dir, nameMatch);
94
+ const config = readConfigFile(filePath);
95
+ if (config && config.name === nameOrId) return { filePath, config };
96
+ }
97
+ const idCandidates = files.filter((f) => {
98
+ const withoutExt = f.replace(/\.json$/, "");
99
+ const lastUnderscore = withoutExt.indexOf("_");
100
+ if (lastUnderscore === -1) return false;
101
+ const idPart = withoutExt.slice(lastUnderscore + 1);
102
+ return idPart.startsWith(nameOrId);
103
+ });
104
+ if (idCandidates.length === 1) {
105
+ const filePath = path.join(dir, idCandidates[0]);
106
+ const config = readConfigFile(filePath);
107
+ if (config) return { filePath, config };
108
+ }
109
+ return null;
110
+ }
111
+ function findConfigByName(name) {
112
+ const resolved = findConfigFile(name);
113
+ return resolved?.config.name === name ? resolved.config : null;
114
+ }
115
+ function findConfig(nameOrId) {
116
+ return findConfigFile(nameOrId)?.config ?? null;
117
+ }
118
+ function saveConfig(name, configId, tunnelConfig, autoStart = false) {
119
+ const nameErr = validateName(name);
120
+ if (nameErr) {
121
+ throw nameErr;
122
+ }
123
+ const existing = findConfigByName(name);
124
+ if (existing) {
125
+ throw new Error(
126
+ `A tunnel config with the name "${name}" already exists (configId: ${existing.configId}). Please use a different name.`
127
+ );
128
+ }
129
+ const dir = ensureTunnelConfigDir();
130
+ const now = (/* @__PURE__ */ new Date()).toISOString();
131
+ const saved = {
132
+ name,
133
+ configId,
134
+ autoStart,
135
+ createdAt: now,
136
+ updatedAt: now,
137
+ tunnelConfig
138
+ };
139
+ const filename = buildFilename(sanitizeName(name), configId);
140
+ const filePath = path.join(dir, filename);
141
+ fs.writeFileSync(filePath, JSON.stringify(saved, null, 2), { encoding: "utf-8" });
142
+ logger.info(`Config "${name}" saved to ${filePath}`);
143
+ return saved;
144
+ }
145
+ function loadConfigByName(name) {
146
+ return findConfigByName(name);
147
+ }
148
+ function deleteConfig(nameOrId) {
149
+ const resolved = findConfigFile(nameOrId);
150
+ if (!resolved) return null;
151
+ fs.unlinkSync(resolved.filePath);
152
+ logger.info(`Config "${resolved.config.name}" deleted.`);
153
+ return resolved.config.name;
154
+ }
155
+ function updateConfigAutoStart(nameOrId, autoStart) {
156
+ const resolved = findConfigFile(nameOrId);
157
+ if (!resolved) return null;
158
+ resolved.config.autoStart = autoStart;
159
+ resolved.config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
160
+ writeConfigFile(resolved.filePath, resolved.config);
161
+ logger.info(`Config "${resolved.config.name}" auto-start set to ${autoStart}`);
162
+ return resolved.config;
163
+ }
164
+ function updateTunnelConfig(nameOrId, tunnelConfig) {
165
+ const resolved = findConfigFile(nameOrId);
166
+ if (!resolved) return null;
167
+ resolved.config.tunnelConfig = tunnelConfig;
168
+ resolved.config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
169
+ writeConfigFile(resolved.filePath, resolved.config);
170
+ logger.info(`Config "${resolved.config.name}" tunnel configuration updated`);
171
+ return resolved.config;
172
+ }
173
+ function getAutoStartConfigs() {
174
+ return listSavedConfigs().filter((c) => c.autoStart);
175
+ }
176
+ function printConfigList() {
177
+ const configs = listSavedConfigs();
178
+ if (configs.length === 0) {
179
+ console.log(pico.yellow("No saved tunnel configs found."));
180
+ console.log(pico.gray(`Config directory: ${getTunnelConfigDir()}`));
181
+ return;
182
+ }
183
+ const nameW = 20;
184
+ const idW = 12;
185
+ const typeW = 8;
186
+ const fwdW = 25;
187
+ const serverW = 22;
188
+ const autoW = 10;
189
+ const header = pico.bold("Name".padEnd(nameW)) + pico.bold("Config ID".padEnd(idW)) + pico.bold("Type".padEnd(typeW)) + pico.bold("Forwarding".padEnd(fwdW)) + pico.bold("Server".padEnd(serverW)) + pico.bold("Auto-start".padEnd(autoW));
190
+ console.log("\n" + header);
191
+ console.log(pico.gray("\u2500".repeat(nameW + idW + typeW + fwdW + serverW + autoW)));
192
+ for (const c of configs) {
193
+ const tc = c.tunnelConfig;
194
+ const forwarding = Array.isArray(tc.forwarding) ? tc.forwarding[0]?.address : String(tc.forwarding || "");
195
+ const type = (Array.isArray(tc.forwarding) ? tc.forwarding[0]?.type : void 0) || "http";
196
+ const server = tc.serverAddress || "a.pinggy.io";
197
+ const line = pico.cyanBright(c.name.padEnd(nameW)) + pico.gray(c.configId.slice(0, 8).padEnd(idW)) + type.padEnd(typeW) + forwarding.slice(0, fwdW - 2).padEnd(fwdW) + server.slice(0, serverW - 2).padEnd(serverW) + (c.autoStart ? pico.green("yes") : pico.gray("no")).padEnd(autoW);
198
+ console.log(line);
199
+ }
200
+ console.log();
201
+ }
202
+ function printConfigDetail(config) {
203
+ console.log(pico.bold(`
204
+ Tunnel Config: ${pico.cyanBright(config.name)}`));
205
+ console.log(pico.gray("\u2500".repeat(40)));
206
+ console.log(` Config ID: ${config.configId}`);
207
+ console.log(` Auto-start: ${config.autoStart ? pico.green("yes") : pico.gray("no")}`);
208
+ console.log(` Created: ${config.createdAt}`);
209
+ console.log(` Updated: ${config.updatedAt}`);
210
+ console.log(pico.gray("\u2500".repeat(40)));
211
+ console.log(` Server: ${config.tunnelConfig.serverAddress || "a.pinggy.io"}`);
212
+ console.log(` Token: ${config.tunnelConfig.token ? "***" + config.tunnelConfig.token.slice(-4) : "(none)"}`);
213
+ const fwd = config.tunnelConfig.forwarding;
214
+ if (Array.isArray(fwd)) {
215
+ const defaultFwds = [];
216
+ const customFwds = [];
217
+ for (const f of fwd) {
218
+ if (typeof f === "string") {
219
+ defaultFwds.push(f);
220
+ } else if (f.listenAddress) {
221
+ customFwds.push(f);
222
+ } else {
223
+ defaultFwds.push(f);
224
+ }
225
+ }
226
+ for (const f of defaultFwds) {
227
+ const addr = typeof f === "string" ? f : `${f.address} (${f.type || "http"})`;
228
+ console.log(` Forwarding: ${addr}`);
229
+ if (config.tunnelConfig.webDebugger) {
230
+ console.log(` Debugger: ${config.tunnelConfig.webDebugger}`);
231
+ }
232
+ }
233
+ if (customFwds.length > 0) {
234
+ console.log(pico.gray("\u2500".repeat(40)));
235
+ console.log(pico.bold(" Domain Mappings:"));
236
+ for (const f of customFwds) {
237
+ if (typeof f === "string") continue;
238
+ const domain = f.listenAddress;
239
+ const target = f.address;
240
+ const type = f.type || "http";
241
+ console.log(` ${pico.cyanBright(domain)} \u2192 ${target} (${type})`);
242
+ }
243
+ }
244
+ } else if (fwd) {
245
+ console.log(` Forwarding: ${fwd}`);
246
+ }
247
+ console.log();
248
+ }
249
+
250
+ export {
251
+ sanitizeName,
252
+ Subcommand,
253
+ ConfigVerb,
254
+ SUBCOMMANDS,
255
+ CONFIG_VERBS,
256
+ RESERVED_NAMES,
257
+ validateName,
258
+ listSavedConfigs,
259
+ findConfigByName,
260
+ findConfig,
261
+ saveConfig,
262
+ loadConfigByName,
263
+ deleteConfig,
264
+ updateConfigAutoStart,
265
+ updateTunnelConfig,
266
+ getAutoStartConfigs,
267
+ printConfigList,
268
+ printConfigDetail
269
+ };
@@ -0,0 +1,36 @@
1
+ import {
2
+ ensurePinggyConfigDir,
3
+ getDaemonConfigPath
4
+ } from "./chunk-GBYF2H4H.js";
5
+
6
+ // src/daemon/lifecycle/daemonConfig.ts
7
+ import fs from "fs";
8
+ function readDaemonConfig() {
9
+ try {
10
+ const p = getDaemonConfigPath();
11
+ if (!fs.existsSync(p)) return null;
12
+ const raw = fs.readFileSync(p, "utf-8");
13
+ const parsed = JSON.parse(raw);
14
+ if (typeof parsed !== "object" || parsed === null) return null;
15
+ return parsed;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+ function writeDaemonConfig(partial) {
21
+ try {
22
+ ensurePinggyConfigDir();
23
+ const current = readDaemonConfig() ?? {};
24
+ const next = { ...current, ...partial };
25
+ const p = getDaemonConfigPath();
26
+ const tmp = p + ".tmp";
27
+ fs.writeFileSync(tmp, JSON.stringify(next, null, 2), "utf-8");
28
+ fs.renameSync(tmp, p);
29
+ } catch {
30
+ }
31
+ }
32
+
33
+ export {
34
+ readDaemonConfig,
35
+ writeDaemonConfig
36
+ };
@@ -0,0 +1,10 @@
1
+ // src/workers/fileServerMessages.ts
2
+ var FileServerMessage = {
3
+ Started: "started",
4
+ Warning: "warning",
5
+ Error: "error"
6
+ };
7
+
8
+ export {
9
+ FileServerMessage
10
+ };