@hienlh/ppm 0.8.38 → 0.8.39

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.39] - 2026-03-24
4
+
5
+ ### Fixed
6
+ - **Binary frontend missing**: Compiled binary now ships with `web/` assets in archive (`.tar.gz`/`.zip`). Server looks for `web/` next to binary when running in compiled mode.
7
+ - **Windows install**: Fix TLS 1.2, SSL revocation, and download hang issues in PowerShell installer
8
+
3
9
  ## [0.8.38] - 2026-03-24
4
10
 
5
11
  ### Fixed
package/README.md CHANGED
@@ -12,11 +12,17 @@ A mobile-first web IDE with AI chat, terminal, git, database tools, and file exp
12
12
 
13
13
  ### Binary (no dependencies)
14
14
 
15
+ **macOS / Linux:**
15
16
  ```bash
16
17
  curl -fsSL https://raw.githubusercontent.com/hienlh/ppm/main/scripts/install.sh | sh
17
18
  ```
18
19
 
19
- This downloads the latest binary to `~/.ppm/bin/ppm`, adds it to your PATH, and shows next steps. To upgrade, run the same command again.
20
+ **Windows (PowerShell):**
21
+ ```powershell
22
+ irm https://raw.githubusercontent.com/hienlh/ppm/main/scripts/install.ps1 | iex
23
+ ```
24
+
25
+ Downloads the latest binary, adds to PATH, and shows next steps. To upgrade, run the same command again.
20
26
 
21
27
  ### Via Bun
22
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.8.38",
3
+ "version": "0.8.39",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -0,0 +1,116 @@
1
+ $ErrorActionPreference = "Stop"
2
+ $ProgressPreference = "SilentlyContinue"
3
+ # PowerShell 5.1 defaults to TLS 1.0 — GitHub requires TLS 1.2
4
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
5
+
6
+ $Repo = "hienlh/ppm"
7
+ $InstallDir = if ($env:PPM_INSTALL_DIR) { $env:PPM_INSTALL_DIR } else { "$env:USERPROFILE\.ppm\bin" }
8
+ $Artifact = "ppm-windows-x64.exe"
9
+
10
+ Write-Host "Detected: windows/x64"
11
+
12
+ # Check current version
13
+ $Current = ""
14
+ if (Test-Path "$InstallDir\ppm.exe") {
15
+ try { $Current = & "$InstallDir\ppm.exe" --version 2>$null } catch {}
16
+ }
17
+
18
+ # Get latest release
19
+ Write-Host "Fetching latest release..."
20
+ $Release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
21
+ $Tag = $Release.tag_name
22
+ if (-not $Tag) { Write-Host "Failed to fetch latest release"; exit 1 }
23
+ $Latest = $Tag -replace "^v", ""
24
+
25
+ # Check if upgrade needed
26
+ if ($Current -eq $Latest) {
27
+ Write-Host "Already up to date: v$Current"
28
+ exit 0
29
+ }
30
+ if ($Current) {
31
+ Write-Host "Upgrading: v$Current -> v$Latest"
32
+ } else {
33
+ Write-Host "Installing: v$Latest"
34
+ }
35
+
36
+ # Download and extract archive
37
+ $Archive = "ppm-windows-x64"
38
+ $Url = "https://github.com/$Repo/releases/download/$Tag/${Archive}.zip"
39
+ Write-Host "Downloading ${Archive}.zip..."
40
+ New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
41
+ $TmpZip = "$env:TEMP\ppm-install.zip"
42
+ & curl.exe -fSL# --ssl-no-revoke -o $TmpZip $Url
43
+ if ($LASTEXITCODE -ne 0) {
44
+ Write-Host "Download failed. Binary may not be available for this version."
45
+ Write-Host "Try installing via: bunx @hienlh/ppm start"
46
+ exit 1
47
+ }
48
+ # Extract ppm.exe + web/ into install dir
49
+ $TmpDir = "$env:TEMP\ppm-extract"
50
+ Remove-Item $TmpDir -Recurse -ErrorAction SilentlyContinue
51
+ Expand-Archive -Path $TmpZip -DestinationPath $TmpDir -Force
52
+ # Move contents from nested folder to install dir
53
+ $Nested = Get-ChildItem $TmpDir | Select-Object -First 1
54
+ Copy-Item "$($Nested.FullName)\*" -Destination $InstallDir -Recurse -Force
55
+ Remove-Item $TmpZip, $TmpDir -Recurse -ErrorAction SilentlyContinue
56
+
57
+ # Show changelog
58
+ Write-Host ""
59
+ Write-Host "========== Changelog =========="
60
+ try {
61
+ $Changelog = Invoke-RestMethod "https://raw.githubusercontent.com/$Repo/$Tag/CHANGELOG.md" -ErrorAction Stop
62
+ if ($Current) {
63
+ $Print = $false
64
+ foreach ($Line in $Changelog -split "`n") {
65
+ if ($Line -match "^## \[(.+?)\]") {
66
+ if ($Matches[1] -eq $Current) { break }
67
+ $Print = $true
68
+ }
69
+ if ($Print) { Write-Host $Line }
70
+ }
71
+ } else {
72
+ $Count = 0
73
+ foreach ($Line in $Changelog -split "`n") {
74
+ if ($Line -match "^## \[") { $Count++ }
75
+ if ($Count -gt 1) { break }
76
+ if ($Count -eq 1) { Write-Host $Line }
77
+ }
78
+ }
79
+ } catch {
80
+ Write-Host "(changelog unavailable)"
81
+ }
82
+ Write-Host "================================"
83
+
84
+ Write-Host ""
85
+ if ($Current) {
86
+ Write-Host "Upgraded ppm v$Current -> v$Latest"
87
+ } else {
88
+ Write-Host "Installed ppm v$Latest to $InstallDir\ppm.exe"
89
+ }
90
+
91
+ # Add to PATH
92
+ $UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
93
+ if ($UserPath -notlike "*$InstallDir*") {
94
+ [Environment]::SetEnvironmentVariable("Path", "$InstallDir;$UserPath", "User")
95
+ $env:Path = "$InstallDir;$env:Path"
96
+ Write-Host "Added to PATH. Restart your terminal to use ppm."
97
+ }
98
+
99
+ # Getting started (fresh install)
100
+ if (-not $Current) {
101
+ Write-Host ""
102
+ Write-Host "========== Getting Started =========="
103
+ Write-Host "1. Restart your terminal"
104
+ Write-Host "2. Run the setup wizard:"
105
+ Write-Host " ppm init"
106
+ Write-Host "3. Start the server:"
107
+ Write-Host " ppm start"
108
+ Write-Host "4. Open in browser:"
109
+ Write-Host " ppm open"
110
+ Write-Host ""
111
+ Write-Host "For remote access (public URL via Cloudflare tunnel):"
112
+ Write-Host " ppm start --share"
113
+ Write-Host ""
114
+ Write-Host "Docs: https://github.com/$Repo#readme"
115
+ Write-Host "====================================="
116
+ }
@@ -49,19 +49,15 @@ else
49
49
  echo "Installing: v${LATEST}"
50
50
  fi
51
51
 
52
- # Check if binary exists in release
53
- URL="https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}"
54
- HTTP_CODE=$(curl -sL -o /dev/null -w "%{http_code}" "$URL")
55
- if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "302" ]; then
56
- echo "Binary not available for ${os}/${arch} in ${TAG} (HTTP ${HTTP_CODE})"
57
- echo "Try installing via: bunx @hienlh/ppm start"
58
- exit 1
59
- fi
60
-
61
- # Download binary
62
- echo "Downloading ${ARTIFACT}..."
52
+ # Download and extract archive
53
+ URL="https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}.tar.gz"
54
+ echo "Downloading ${ARTIFACT}.tar.gz..."
63
55
  mkdir -p "$INSTALL_DIR"
64
- curl -fSL# -o "${INSTALL_DIR}/ppm" "$URL"
56
+ TMPFILE=$(mktemp)
57
+ curl -fSL# -o "$TMPFILE" "$URL"
58
+ # Extract binary + web/ into install dir
59
+ tar -xzf "$TMPFILE" -C "$INSTALL_DIR"
60
+ rm -f "$TMPFILE"
65
61
  chmod +x "${INSTALL_DIR}/ppm"
66
62
 
67
63
  # Show changelog
@@ -71,7 +67,6 @@ CHANGELOG_URL="https://raw.githubusercontent.com/${REPO}/${TAG}/CHANGELOG.md"
71
67
  CHANGELOG=$(curl -fsSL "$CHANGELOG_URL" 2>/dev/null || true)
72
68
  if [ -n "$CHANGELOG" ]; then
73
69
  if [ -n "$CURRENT" ]; then
74
- # Upgrade: show entries between current and latest
75
70
  echo "$CHANGELOG" | awk -v cur="$CURRENT" '
76
71
  /^## \[/ {
77
72
  ver = $0; gsub(/.*\[/, "", ver); gsub(/\].*/, "", ver)
@@ -81,7 +76,6 @@ if [ -n "$CHANGELOG" ]; then
81
76
  printing { print }
82
77
  '
83
78
  else
84
- # Fresh install: show latest entry only
85
79
  echo "$CHANGELOG" | awk '
86
80
  /^## \[/ { count++; if (count > 1) exit }
87
81
  count == 1 { print }
@@ -104,7 +98,6 @@ PATH_LINE="export PATH=\"${INSTALL_DIR}:\$PATH\""
104
98
  case ":$PATH:" in
105
99
  *":${INSTALL_DIR}:"*) ;;
106
100
  *)
107
- # Detect shell profile
108
101
  PROFILE=""
109
102
  if [ -n "$ZSH_VERSION" ] || [ "$(basename "$SHELL")" = "zsh" ]; then
110
103
  PROFILE="$HOME/.zshrc"
@@ -36,21 +36,48 @@ for entry in "${TARGETS[@]}"; do
36
36
  bun build src/index.ts --compile --target="$target" --outfile="dist/$artifact"
37
37
  done
38
38
 
39
- # 3. Create tag if not exists
39
+ # 3. Package binaries with web assets
40
+ echo "[3/5] Packaging..."
41
+ rm -f dist/ppm-*.tar.gz dist/ppm-*.zip
42
+
43
+ for entry in "${TARGETS[@]}"; do
44
+ artifact="${entry##*:}"
45
+ if [[ "$artifact" == *.exe ]]; then
46
+ # Windows: zip
47
+ name="${artifact%.exe}"
48
+ mkdir -p "dist/$name"
49
+ cp "dist/$artifact" "dist/$name/ppm.exe"
50
+ cp -r dist/web "dist/$name/web"
51
+ (cd dist && zip -qr "${name}.zip" "$name")
52
+ rm -rf "dist/$name"
53
+ echo " -> ${name}.zip"
54
+ else
55
+ # Unix: tar.gz
56
+ mkdir -p "dist/$artifact-pkg"
57
+ cp "dist/$artifact" "dist/$artifact-pkg/ppm"
58
+ cp -r dist/web "dist/$artifact-pkg/web"
59
+ tar -czf "dist/${artifact}.tar.gz" -C "dist/$artifact-pkg" .
60
+ rm -rf "dist/$artifact-pkg"
61
+ echo " -> ${artifact}.tar.gz"
62
+ fi
63
+ done
64
+
65
+ # 4. Create tag if not exists
40
66
  if git rev-parse "$TAG" >/dev/null 2>&1; then
41
- echo "[3/4] Tag $TAG already exists, skipping"
67
+ echo "[4/5] Tag $TAG already exists, skipping"
42
68
  else
43
- echo "[3/4] Creating tag $TAG..."
69
+ echo "[4/5] Creating tag $TAG..."
44
70
  git tag "$TAG"
45
71
  git push origin "$TAG"
46
72
  fi
47
73
 
48
- # 4. Create or update release
49
- echo "[4/4] Uploading to GitHub release..."
74
+ # 5. Create or update release
75
+ echo "[5/5] Uploading to GitHub release..."
76
+ ARCHIVES=(dist/ppm-*.tar.gz dist/ppm-*.zip)
50
77
  if gh release view "$TAG" >/dev/null 2>&1; then
51
- gh release upload "$TAG" dist/ppm-* --clobber
78
+ gh release upload "$TAG" ${ARCHIVES[@]} --clobber
52
79
  else
53
- gh release create "$TAG" dist/ppm-* --title "$TAG" --generate-notes
80
+ gh release create "$TAG" ${ARCHIVES[@]} --title "$TAG" --generate-notes
54
81
  fi
55
82
 
56
83
  echo "=== Done: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/releases/tag/$TAG ==="
@@ -1,10 +1,15 @@
1
1
  import { Hono } from "hono";
2
2
  import { existsSync } from "node:fs";
3
- import { resolve, join, extname } from "node:path";
3
+ import { resolve, join, extname, dirname } from "node:path";
4
+ import { isCompiledBinary } from "../../services/autostart-generator.ts";
4
5
 
5
6
  export const staticRoutes = new Hono();
6
7
 
7
- const DIST_DIR = resolve(import.meta.dir, "../../../dist/web");
8
+ // Compiled binary: look for web/ next to the binary itself
9
+ // Dev mode: resolve relative to source file
10
+ const DIST_DIR = isCompiledBinary()
11
+ ? resolve(dirname(process.execPath), "web")
12
+ : resolve(import.meta.dir, "../../../dist/web");
8
13
 
9
14
  /** MIME types for common static assets */
10
15
  const MIME_TYPES: Record<string, string> = {