@hienlh/ppm 0.9.0-beta.6 → 0.9.0-beta.7
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,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.0-beta.7] - 2026-03-27
|
|
4
|
+
|
|
5
|
+
### Merged from main
|
|
6
|
+
- **Usage polling dedup**: Concurrent `pollOnce` calls share single in-flight fetch
|
|
7
|
+
- **429 cooldown floor**: Min 60s cooldown on 429 responses
|
|
8
|
+
- **Browser preview tests**: Unit tests (12) + integration test (6) for tunnel routes
|
|
9
|
+
|
|
3
10
|
## [0.9.0-beta.6] - 2026-03-27
|
|
4
11
|
|
|
5
12
|
### Merged from main
|
package/docs/project-roadmap.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PPM Project Roadmap
|
|
2
2
|
|
|
3
|
-
**Last Updated:** March
|
|
3
|
+
**Last Updated:** March 27, 2026
|
|
4
4
|
|
|
5
5
|
## Vision
|
|
6
6
|
|
|
@@ -56,19 +56,26 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
56
56
|
|
|
57
57
|
### v0.9.0 — "Open Platform" (Q2–Q3 2026)
|
|
58
58
|
|
|
59
|
-
**Theme:** Multi-provider AI + extension system.
|
|
59
|
+
**Theme:** Multi-provider AI (Claude + Cursor) + extension system. Ship a focused release, expand providers later.
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|---------|----------|-------------|
|
|
63
|
-
| **Multi-provider AI** | Critical | Refactor `ProviderInterface` for clean provider abstraction. Tiered support: Tier 1 (full agentic) = Claude Agent SDK; Tier 2 (chat + tools) = Gemini CLI, OpenAI Codex; Tier 3 (chat-only) = any OpenAI-compatible API. Clean base code for future Chinese providers (DeepSeek, Qwen). |
|
|
64
|
-
| **Extension architecture** | High | Dynamic extension loading system. Extensions = npm packages exporting skills + optional UI panels. First extension: extract DB viewer from core. Extension API: register routes, UI panels, sidebar tabs, skills. Config: `"extensions": ["@ppm/ext-database", "@ppm/ext-docker"]`. |
|
|
65
|
-
| **MCP Management** | Medium | UI to add/remove/configure MCP servers. Test connection. Per-project MCP overrides. Store in SQLite. Pass to Agent SDK via `mcpServers`. |
|
|
61
|
+
**Overall progress: ~40%** (1/3 features complete, merge all at 100%)
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
| Feature | Priority | Status | Description |
|
|
64
|
+
|---------|----------|--------|-------------|
|
|
65
|
+
| **Multi-provider AI** | Critical | ✅ Done | ProviderInterface, registry, Cursor CLI, CLI provider base, UI provider/model selector, permission mode selector, system prompt customization, comprehensive tests — all on beta branch. |
|
|
66
|
+
| **Extension architecture** | High | 🔴 0% | Dynamic extension loading. Extensions = npm packages. First extension: extract DB viewer from core. Extension API: register routes, UI panels, sidebar tabs, skills. Config: `"extensions": ["@ppm/ext-database", "@ppm/ext-docker"]`. |
|
|
67
|
+
| **MCP Management** | Medium | 🔴 0% | UI to add/remove/configure MCP servers. Test connection. Per-project MCP overrides. Store in SQLite. Pass to Agent SDK via `mcpServers`. |
|
|
68
|
+
|
|
69
|
+
**Multi-provider — v0.9 scope (reduced):**
|
|
68
70
|
- Tier 1 (full agentic): Claude Agent SDK — file edit, terminal, git, full autonomy
|
|
69
|
-
- Tier 2 (
|
|
70
|
-
-
|
|
71
|
-
|
|
71
|
+
- Tier 2 (agentic CLI): Cursor — agentic via its own tool system
|
|
72
|
+
- Provider interface is clean enough to add more providers later without refactor
|
|
73
|
+
|
|
74
|
+
**Deferred to v0.9.5+:**
|
|
75
|
+
- Gemini CLI (Tier 2)
|
|
76
|
+
- OpenAI Codex (Tier 2)
|
|
77
|
+
- Tier 3 (chat-only): Any OpenAI-compatible API
|
|
78
|
+
- Chinese providers (DeepSeek, Qwen) — v1.0+
|
|
72
79
|
|
|
73
80
|
**Extension architecture — design principles:**
|
|
74
81
|
- Extensions are npm packages: `ppm ext install @ppm/ext-database`
|
|
@@ -79,15 +86,27 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
79
86
|
|
|
80
87
|
---
|
|
81
88
|
|
|
82
|
-
### v0.10.0 — "
|
|
89
|
+
### v0.10.0 — "Enhanced Workflow" (Q3 2026)
|
|
83
90
|
|
|
84
|
-
**Theme:**
|
|
91
|
+
**Theme:** Chat UX upgrade + git workflow. High-impact, independent features that ship fast.
|
|
85
92
|
|
|
86
93
|
| Feature | Priority | Description |
|
|
87
94
|
|---------|----------|-------------|
|
|
95
|
+
| **Chat history graph** | High | Visual branching tree of chat sessions. Fork conversations, navigate history graph. Game-changer for AI chat UX. |
|
|
96
|
+
| **Worktree management** | Medium | UI to create/switch/delete git worktrees. Use different providers on different branches. Integrated with project switcher. |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### v0.11.0 — "Intelligence" (Q3–Q4 2026)
|
|
101
|
+
|
|
102
|
+
**Theme:** Event system + PPM's own AI layer. Hooks → Skills API → Clawbot dependency chain.
|
|
103
|
+
|
|
104
|
+
| Feature | Priority | Description |
|
|
105
|
+
|---------|----------|-------------|
|
|
106
|
+
| **Hooks system** | High | Event hooks for PPM lifecycle (file save, git commit, chat message, etc.). Foundation for Skills API and deeper extension integration. |
|
|
88
107
|
| **PPM Skills API** | High | Stable internal API for AI to control PPM: file.read/write/search, terminal.run, git.status/commit/diff, db.query, editor.open/goto, project.switch. Skills are the bridge between AI and PPM features. |
|
|
89
108
|
| **Built-in Clawbot** | High | Lightweight AI agent built into PPM using Anthropic Messages API (not Agent SDK). Uses Skills API + MCP tools. Instant response, no external CLI deps. For quick tasks: file search, code explanation, simple refactors. |
|
|
90
|
-
| **
|
|
109
|
+
| **More providers** | Medium | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
|
|
91
110
|
|
|
92
111
|
**Built-in Clawbot — why it matters:**
|
|
93
112
|
- Claude Agent SDK spawns subprocess — heavy, slow startup, requires CLI installed
|
|
@@ -106,6 +125,7 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
106
125
|
| **Self-hosted PPM Cloud** | High | Docker image of PPM Cloud for enterprise/team. Same codebase, self-hosted config flag. `docker-compose up` and it works. LDAP/SSO integration. |
|
|
107
126
|
| **PPM Marketplace** | High | Publish/install/update extensions. Browse community extensions. Revenue sharing for paid extensions. Clawbot can create extension → test → publish in minutes. |
|
|
108
127
|
| **Stability & hardening** | Critical | Security audit, performance optimization, comprehensive test coverage (>80%), documentation for contributors, CI/CD pipeline. |
|
|
128
|
+
| **Inline SQL** | Medium | Select text in Monaco → run as SQL. Connection picker in editor context menu. Results panel below editor. Leverages existing DB service. |
|
|
109
129
|
|
|
110
130
|
---
|
|
111
131
|
|
|
@@ -130,6 +150,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
|
|
|
130
150
|
| **Cross-platform binaries** | Distribution | Compile macOS/Linux/Windows binaries via `bun build --compile`. `npx ppm` without Bun. |
|
|
131
151
|
| **OLED dark mode** | UX | True black background for OLED screens. |
|
|
132
152
|
| **Collaborative editing** | Social | Real-time multi-user file editing with CRDT (yjs/automerge). |
|
|
153
|
+
| **Custom domain** | Cloud | Map custom domain to PPM Cloud tunnel URL. DNS CNAME + SSL via Let's Encrypt or Cloudflare. Access PPM at `code.yourdomain.com`. |
|
|
133
154
|
|
|
134
155
|
---
|
|
135
156
|
|
|
@@ -139,9 +160,10 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
|
|
|
139
160
|
|---------|-------|-------------|--------|
|
|
140
161
|
| **v0.7** | Multi-Account & Mobile | Account management, usage tracking, mobile UX | ✅ Current |
|
|
141
162
|
| **v0.8** | Always On | PPM Cloud, auto-start, AI chat enhancements | Q2 2026 |
|
|
142
|
-
| **v0.9** | Open Platform | Multi-provider
|
|
143
|
-
| **v0.10** |
|
|
144
|
-
| **
|
|
163
|
+
| **v0.9** | Open Platform | Multi-provider (Claude + Cursor), extension architecture, MCP | Q2–Q3 2026 |
|
|
164
|
+
| **v0.10** | Enhanced Workflow | Chat history graph, worktree management | Q3 2026 |
|
|
165
|
+
| **v0.11** | Intelligence | Hooks, Skills API, Clawbot, more providers (Gemini/Codex/Tier 3) | Q3–Q4 2026 |
|
|
166
|
+
| **v1.0** | Production Ready | Self-hosted Cloud, Marketplace, stability, inline SQL | Q4 2026 |
|
|
145
167
|
|
|
146
168
|
---
|
|
147
169
|
|
|
@@ -149,7 +171,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
|
|
|
149
171
|
|
|
150
172
|
1. **Own "phone to code"** — PPM wins on multi-device access. Don't chase Cursor/Windsurf feature parity.
|
|
151
173
|
2. **PPM Cloud stays razor-thin** — Device registry + tunnel URLs only. No code storage. No cloud execution.
|
|
152
|
-
3. **Multi-provider is tiered** —
|
|
174
|
+
3. **Multi-provider is tiered** — v0.9: Claude SDK (Tier 1) + Cursor (Tier 2). v0.11: Gemini, Codex, Tier 3. Clean interface for future providers.
|
|
153
175
|
4. **Extensions keep core lightweight** — Features are opt-in. DB viewer, future tools = extensions. Core stays fast.
|
|
154
176
|
5. **Clawbot enables the ecosystem** — Users create extensions with AI, publish to Marketplace. Zero-friction.
|
|
155
177
|
6. **Self-hosted first, always** — Cloud is optional convenience. PPM works 100% offline/local.
|
|
@@ -160,7 +182,7 @@ Features to pick from after v1.0. Will be reviewed and scheduled based on user f
|
|
|
160
182
|
|
|
161
183
|
| Item | Priority | Notes |
|
|
162
184
|
|------|----------|-------|
|
|
163
|
-
| Refactor ProviderInterface for multi-provider | High |
|
|
185
|
+
| ~~Refactor ProviderInterface for multi-provider~~ | ~~High~~ | ✅ Done on beta branch (v0.9.0-beta.5) |
|
|
164
186
|
| Simplify ChatService streaming | Medium | Reduce async generator complexity |
|
|
165
187
|
| Extract WebSocket common logic | Low | DRY for chat/terminal WS |
|
|
166
188
|
| Round-robin cursor bug in AccountSelector | Medium | Positional cursor not advancing correctly |
|
package/package.json
CHANGED
|
@@ -21,8 +21,8 @@ interface ActiveTunnel {
|
|
|
21
21
|
startedAt: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** Active tunnels keyed by port */
|
|
25
|
-
const activeTunnels = new Map<number, ActiveTunnel>();
|
|
24
|
+
/** Active tunnels keyed by port — exported for testing */
|
|
25
|
+
export const activeTunnels = new Map<number, ActiveTunnel>();
|
|
26
26
|
|
|
27
27
|
/** Start a tunnel for a localhost port */
|
|
28
28
|
browserPreviewRoutes.post("/tunnel", async (c) => {
|
|
@@ -51,6 +51,10 @@ let pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
51
51
|
|
|
52
52
|
// Per-token cooldown map: token prefix → earliest allowed fetch time
|
|
53
53
|
const tokenCooldowns = new Map<string, number>();
|
|
54
|
+
const MIN_COOLDOWN_MS = 60_000; // floor: at least 60s cooldown on 429
|
|
55
|
+
|
|
56
|
+
// Dedup: if a poll is already in-flight, reuse the same promise
|
|
57
|
+
let inflightPoll: Promise<void> | null = null;
|
|
54
58
|
|
|
55
59
|
// Legacy: Keychain token cache for users without accounts in DB
|
|
56
60
|
let tokenCache: { token: string; timestamp: number } | null = null;
|
|
@@ -113,9 +117,10 @@ async function fetchUsageForToken(token: string): Promise<ClaudeUsage> {
|
|
|
113
117
|
});
|
|
114
118
|
if (res.status === 429) {
|
|
115
119
|
const retryAfter = parseInt(res.headers.get("retry-after") ?? "60", 10);
|
|
120
|
+
const cooldownMs = Math.max(retryAfter * 1000, MIN_COOLDOWN_MS);
|
|
116
121
|
const cooldownKey = token.substring(0, 20);
|
|
117
|
-
tokenCooldowns.set(cooldownKey, Date.now() +
|
|
118
|
-
throw new Error(`Usage API 429 — cooldown ${
|
|
122
|
+
tokenCooldowns.set(cooldownKey, Date.now() + cooldownMs);
|
|
123
|
+
throw new Error(`Usage API 429 — cooldown ${Math.ceil(cooldownMs / 1000)}s`);
|
|
119
124
|
}
|
|
120
125
|
if (!res.ok) throw new Error(`Usage API returned ${res.status}`);
|
|
121
126
|
const raw = (await res.json()) as Record<string, any>;
|
|
@@ -228,7 +233,7 @@ async function fetchLegacySingleAccount(): Promise<void> {
|
|
|
228
233
|
} catch {}
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
async function
|
|
236
|
+
async function pollOnceInternal(): Promise<void> {
|
|
232
237
|
try {
|
|
233
238
|
const hasAccounts = accountService.list().length > 0;
|
|
234
239
|
if (hasAccounts) {
|
|
@@ -241,6 +246,13 @@ async function pollOnce(): Promise<void> {
|
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
|
|
249
|
+
/** Deduped: concurrent callers share a single in-flight fetch */
|
|
250
|
+
async function pollOnce(): Promise<void> {
|
|
251
|
+
if (inflightPoll) return inflightPoll;
|
|
252
|
+
inflightPoll = pollOnceInternal().finally(() => { inflightPoll = null; });
|
|
253
|
+
return inflightPoll;
|
|
254
|
+
}
|
|
255
|
+
|
|
244
256
|
// ---------------------------------------------------------------------------
|
|
245
257
|
// Public API
|
|
246
258
|
// ---------------------------------------------------------------------------
|