@hienlh/ppm 0.8.7 → 0.8.8

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
@@ -3,8 +3,9 @@
3
3
  ## [0.8.7] - 2026-03-23
4
4
 
5
5
  ### Fixed
6
- - **Env var diagnostics in timeout/error messages**: When SDK hangs or returns `unknown` error, error message now guides user to check `ANTHROPIC_API_KEY`/`ANTHROPIC_BASE_URL` env vars (stale/invalid keys cause silent hang) with exact debug commands
7
- - **Shell env logging**: Log when shell environment has `ANTHROPIC_API_KEY`/`ANTHROPIC_BASE_URL` set helps diagnose SDK hangs in server logs
6
+ - **Auth env priority chain**: Properly resolve auth env vars with priority: PPM accounts > project `.env` > shell env. Previously project `.env` vars were neutralized (set empty) instead of parsed and used as overrides.
7
+ - **Env var diagnostics in timeout/error messages**: When SDK hangs or returns `unknown` error, error message guides user to check `ANTHROPIC_API_KEY`/`ANTHROPIC_BASE_URL` env vars with exact debug commands
8
+ - **Auth source logging**: Log which source each auth var comes from (project .env vs shell env) — helps diagnose SDK hangs
8
9
 
9
10
  ## [0.8.6] - 2026-03-23
10
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.8.7",
3
+ "version": "0.8.8",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -50,46 +50,67 @@ export class ClaudeAgentSdkProvider implements AIProvider {
50
50
  /** Fork source: ppmSessionId → sourceSessionId (used on first message to fork) */
51
51
  private forkSources = new Map<string, string>();
52
52
 
53
- /** Env vars to neutralize only if project .env contains them (prevents .env poisoning) */
54
- private readonly SENSITIVE_ENV_KEYS = ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
53
+ /** Auth-related env keyspriority: account > project .env > shell env */
54
+ private readonly AUTH_ENV_KEYS = ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
55
55
 
56
- private getProjectEnvOverrides(projectPath?: string): Record<string, string> {
56
+ /**
57
+ * Parse project .env file and extract auth-related values.
58
+ * Returns actual values (not neutralized) so they can override shell env.
59
+ */
60
+ private parseProjectEnv(projectPath?: string): Record<string, string> {
57
61
  if (!projectPath) return {};
58
62
  try {
59
63
  const envPath = resolve(projectPath, ".env");
60
64
  if (!existsSync(envPath)) return {};
61
65
  const content = readFileSync(envPath, "utf-8");
62
- const overrides: Record<string, string> = {};
63
- for (const key of this.SENSITIVE_ENV_KEYS) {
64
- if (content.includes(key)) {
65
- overrides[key] = "";
66
- console.log(`[sdk] Neutralizing ${key} from project .env (prevents poisoning)`);
66
+ const parsed: Record<string, string> = {};
67
+ for (const line of content.split("\n")) {
68
+ const trimmed = line.trim();
69
+ if (!trimmed || trimmed.startsWith("#")) continue;
70
+ const eqIdx = trimmed.indexOf("=");
71
+ if (eqIdx < 0) continue;
72
+ const key = trimmed.slice(0, eqIdx).trim();
73
+ if (!this.AUTH_ENV_KEYS.includes(key)) continue;
74
+ // Strip surrounding quotes
75
+ let val = trimmed.slice(eqIdx + 1).trim();
76
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
77
+ val = val.slice(1, -1);
67
78
  }
79
+ parsed[key] = val;
68
80
  }
69
- return overrides;
81
+ return parsed;
70
82
  } catch { return {}; }
71
83
  }
72
84
 
73
85
  /**
74
86
  * Build env for SDK query.
75
- * If account mode: detect token type and inject the correct env var.
76
- * - OAuth tokens (sk-ant-oat*) CLAUDE_CODE_OAUTH_TOKEN (SDK reads this for subscription auth)
77
- * - API keys (sk-ant-api* or other) → ANTHROPIC_API_KEY
78
- * Otherwise: pass through existing env (backward compatible).
87
+ * Priority: PPM accounts > project .env > shell env.
88
+ * - Account mode: inject account token, neutralize conflicting vars.
89
+ * TODO: support base_url from PPM AI settings.
90
+ * - No account: project .env overrides shell env for auth vars.
79
91
  */
80
92
  private buildQueryEnv(
81
93
  projectPath: string | undefined,
82
94
  account: { id: string; accessToken: string } | null,
83
95
  ): Record<string, string | undefined> {
84
- const base = { ...process.env, ...this.getProjectEnvOverrides(projectPath) };
85
- // Log if shell env has Anthropic vars helps diagnose hanging SDK
86
- for (const key of this.SENSITIVE_ENV_KEYS) {
87
- if (process.env[key]) {
88
- console.log(`[sdk] Shell env has ${key} set (length=${process.env[key]!.length})`);
96
+ const projectEnv = this.parseProjectEnv(projectPath);
97
+ // Merge: shell env project .env (project wins)
98
+ const base = { ...process.env, ...projectEnv };
99
+
100
+ // Log auth source for diagnostics
101
+ for (const key of this.AUTH_ENV_KEYS) {
102
+ if (projectEnv[key]) {
103
+ console.log(`[sdk] ${key} from project .env (length=${projectEnv[key].length})`);
104
+ } else if (process.env[key]) {
105
+ console.log(`[sdk] ${key} from shell env (length=${process.env[key]!.length})`);
89
106
  }
90
107
  }
108
+
91
109
  if (!account) return base;
110
+
111
+ // Account mode: account token takes highest priority
92
112
  const isOAuthToken = account.accessToken.startsWith("sk-ant-oat");
113
+ // TODO: support base_url from PPM AI provider settings
93
114
  if (isOAuthToken) {
94
115
  return {
95
116
  ...base,