@hienlh/ppm 0.8.73 → 0.8.75
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/CHANGELOG.md +8 -2
- package/package.json +1 -1
- package/src/cli/commands/autostart.ts +1 -1
- package/src/index.ts +1 -1
- package/src/server/index.ts +3 -0
- package/src/services/cloud-ws.service.ts +23 -15
- package/src/services/supervisor.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.8.
|
|
3
|
+
## [0.8.75] - 2026-04-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **Tunnel always enabled**: `ppm start` now always starts Cloudflare tunnel — `--share` flag deprecated (still accepted, no-op)
|
|
7
|
+
|
|
8
|
+
## [0.8.74] - 2026-04-01
|
|
4
9
|
|
|
5
10
|
### Fixed
|
|
6
|
-
- **Cloud WS reconnect loop**: Stale WebSocket closure handlers from replaced connections no longer reset module state
|
|
11
|
+
- **Cloud WS reconnect loop**: Stale WebSocket closure handlers from replaced connections no longer reset module state
|
|
12
|
+
- **Cloud WS auth race**: Delay heartbeat/queue flush 500ms after auth to let server complete async DB auth — prevents 4002 rejection
|
|
7
13
|
|
|
8
14
|
## [0.8.72] - 2026-03-31
|
|
9
15
|
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export function registerAutoStartCommands(program: Command): void {
|
|
|
10
10
|
.command("enable")
|
|
11
11
|
.description("Register PPM to start automatically on boot")
|
|
12
12
|
.option("-p, --port <port>", "Override port")
|
|
13
|
-
.option("-s, --share", "
|
|
13
|
+
.option("-s, --share", "(deprecated) Tunnel is now always enabled")
|
|
14
14
|
.option("-c, --config <path>", "Config file path")
|
|
15
15
|
.option("--profile <name>", "DB profile name")
|
|
16
16
|
.action(async (options) => {
|
package/src/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ program
|
|
|
16
16
|
.command("start")
|
|
17
17
|
.description("Start the PPM server (background by default)")
|
|
18
18
|
.option("-p, --port <port>", "Port to listen on")
|
|
19
|
-
.option("-s, --share", "
|
|
19
|
+
.option("-s, --share", "(deprecated) Tunnel is now always enabled")
|
|
20
20
|
.option("-c, --config <path>", "Path to config file (YAML import into DB)")
|
|
21
21
|
.option("--profile <name>", "DB profile name (e.g. 'dev' → ppm.dev.db)")
|
|
22
22
|
.action(async (options) => {
|
package/src/server/index.ts
CHANGED
|
@@ -164,6 +164,9 @@ export async function startServer(options: {
|
|
|
164
164
|
config?: string;
|
|
165
165
|
profile?: string;
|
|
166
166
|
}) {
|
|
167
|
+
// Tunnel always enabled — cloudflared shares the server publicly
|
|
168
|
+
options.share = true;
|
|
169
|
+
|
|
167
170
|
// Load config
|
|
168
171
|
configService.load(options.config);
|
|
169
172
|
const port = parseInt(options.port ?? String(configService.get("port")), 10);
|
|
@@ -18,6 +18,7 @@ interface HeartbeatMsg extends WsMessage {
|
|
|
18
18
|
tunnelUrl: string | null;
|
|
19
19
|
state: string;
|
|
20
20
|
appVersion: string;
|
|
21
|
+
availableVersion: string | null;
|
|
21
22
|
serverPid: number | null;
|
|
22
23
|
uptime: number;
|
|
23
24
|
}
|
|
@@ -140,8 +141,10 @@ function doConnect(): void {
|
|
|
140
141
|
sock.onopen = () => {
|
|
141
142
|
if (ws !== sock) return; // stale — newer connection replaced us
|
|
142
143
|
reconnecting = false;
|
|
144
|
+
reconnectAttempt = 0;
|
|
143
145
|
log("INFO", "Cloud WS connected, sending auth");
|
|
144
146
|
|
|
147
|
+
// Send auth as first message — server must process this before any other msg
|
|
145
148
|
sock.send(JSON.stringify({
|
|
146
149
|
type: "auth",
|
|
147
150
|
deviceId,
|
|
@@ -150,23 +153,28 @@ function doConnect(): void {
|
|
|
150
153
|
version: 1,
|
|
151
154
|
}));
|
|
152
155
|
|
|
153
|
-
connected
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
156
|
+
// Delay setting connected + sending heartbeat to let server process auth.
|
|
157
|
+
// Server's authenticateDevice() is async (DB lookup), so messages sent
|
|
158
|
+
// immediately after auth arrive before authenticated=true → 4002 reject.
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
if (ws !== sock) return; // replaced during delay
|
|
161
|
+
connected = true;
|
|
162
|
+
|
|
163
|
+
// Flush queued messages
|
|
164
|
+
while (outboundQueue.length > 0 && connected) {
|
|
165
|
+
const msg = outboundQueue.shift()!;
|
|
166
|
+
sock.send(JSON.stringify(msg));
|
|
167
|
+
}
|
|
161
168
|
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
// Send immediate heartbeat
|
|
170
|
+
if (getHeartbeatData) send(getHeartbeatData());
|
|
164
171
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
// Start periodic heartbeat
|
|
173
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
174
|
+
heartbeatTimer = setInterval(() => {
|
|
175
|
+
if (getHeartbeatData && connected) send(getHeartbeatData());
|
|
176
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
177
|
+
}, 500); // 500ms for DB auth round-trip
|
|
170
178
|
};
|
|
171
179
|
|
|
172
180
|
sock.onmessage = (event) => {
|
|
@@ -456,6 +456,7 @@ async function connectCloud(opts: { port: number }, serverArgs: string[], logFd:
|
|
|
456
456
|
tunnelUrl,
|
|
457
457
|
state: supervisorState,
|
|
458
458
|
appVersion: VERSION,
|
|
459
|
+
availableVersion: (readStatus().availableVersion as string) || null,
|
|
459
460
|
serverPid: serverChild?.pid ?? null,
|
|
460
461
|
uptime: Math.floor((Date.now() - startTime) / 1000),
|
|
461
462
|
timestamp: new Date().toISOString(),
|