@hienlh/ppm 0.8.74 → 0.8.76

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.76] - 2026-04-01
4
+
5
+ ### Fixed
6
+ - **SDK error messages**: 500/5xx server errors and 429 rate-limits now show correct user-facing messages instead of confusing "unknown API error" with debug instructions
7
+ - **Session title on resume**: Prioritize DB-stored title when resuming a chat session
8
+
9
+ ## [0.8.75] - 2026-04-01
10
+
11
+ ### Changed
12
+ - **Tunnel always enabled**: `ppm start` now always starts Cloudflare tunnel — `--share` flag deprecated (still accepted, no-op)
13
+
3
14
  ## [0.8.74] - 2026-04-01
4
15
 
5
16
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.8.74",
3
+ "version": "0.8.76",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -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", "Enable Cloudflare tunnel on boot")
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", "Share via public URL (Cloudflare tunnel)")
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) => {
@@ -632,13 +632,21 @@ export class ClaudeAgentSdkProvider implements AIProvider {
632
632
  // SDK assistant messages can carry an error field for auth/billing/rate-limit failures
633
633
  let assistantError = (msg as any).error as string | undefined;
634
634
 
635
- // SDK sometimes returns auth errors as text content without setting error field.
636
- // Detect 401 pattern in text: "Failed to authenticate. API Error: 401 ..."
637
- if (!assistantError) {
635
+ // SDK sometimes returns errors as text content without setting a specific error field.
636
+ // Detect known HTTP error patterns in text and reclassify accordingly.
637
+ if (!assistantError || assistantError === "unknown") {
638
638
  const textContent = this.extractAssistantText(msg);
639
- if (textContent && /API Error:\s*401\b.*authentication_error/i.test(textContent)) {
640
- assistantError = "authentication_failed";
641
- console.warn(`[sdk] session=${sessionId} detected 401 in assistant text content — treating as auth error`);
639
+ if (textContent) {
640
+ if (/API Error:\s*401\b.*authentication_error/i.test(textContent)) {
641
+ assistantError = "authentication_failed";
642
+ console.warn(`[sdk] session=${sessionId} detected 401 in assistant text content — treating as auth error`);
643
+ } else if (/API Error:\s*5\d{2}\b/i.test(textContent) || /internal server error/i.test(textContent)) {
644
+ assistantError = "server_error";
645
+ console.warn(`[sdk] session=${sessionId} detected 5xx in assistant text content — treating as server error`);
646
+ } else if (/API Error:\s*429\b/i.test(textContent) || /rate.?limit/i.test(textContent)) {
647
+ assistantError = "rate_limit";
648
+ console.warn(`[sdk] session=${sessionId} detected 429/rate-limit in assistant text content — treating as rate limit`);
649
+ }
642
650
  }
643
651
  }
644
652
 
@@ -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
  }
@@ -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(),