@hienlh/ppm 0.8.6 → 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 +7 -0
- package/package.json +1 -1
- package/src/providers/claude-agent-sdk.ts +42 -15
- package/src/server/ws/chat.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.7] - 2026-03-23
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
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
|
|
9
|
+
|
|
3
10
|
## [0.8.6] - 2026-03-23
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/package.json
CHANGED
|
@@ -50,40 +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
|
-
/**
|
|
54
|
-
private readonly
|
|
53
|
+
/** Auth-related env keys — priority: account > project .env > shell env */
|
|
54
|
+
private readonly AUTH_ENV_KEYS = ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
|
|
55
55
|
|
|
56
|
-
|
|
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
|
|
63
|
-
for (const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
81
|
+
return parsed;
|
|
70
82
|
} catch { return {}; }
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
/**
|
|
74
86
|
* Build env for SDK query.
|
|
75
|
-
*
|
|
76
|
-
* -
|
|
77
|
-
*
|
|
78
|
-
*
|
|
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
|
|
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})`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
85
109
|
if (!account) return base;
|
|
110
|
+
|
|
111
|
+
// Account mode: account token takes highest priority
|
|
86
112
|
const isOAuthToken = account.accessToken.startsWith("sk-ant-oat");
|
|
113
|
+
// TODO: support base_url from PPM AI provider settings
|
|
87
114
|
if (isOAuthToken) {
|
|
88
115
|
return {
|
|
89
116
|
...base,
|
|
@@ -813,7 +840,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
813
840
|
rate_limit: "Rate limited by the API. Please wait and try again.",
|
|
814
841
|
invalid_request: "Invalid request sent to the API.",
|
|
815
842
|
server_error: "Anthropic API server error. Try again shortly.",
|
|
816
|
-
unknown: `API error in project "${effectiveCwd}". Debug:
|
|
843
|
+
unknown: `API error in project "${effectiveCwd}". Debug:\n1. Run: \`cd ${effectiveCwd} && claude -p "hi"\`\n2. Check env: \`echo $ANTHROPIC_API_KEY $ANTHROPIC_BASE_URL\` — stale/invalid keys cause this\n3. Try: \`ANTHROPIC_API_KEY="" ANTHROPIC_BASE_URL="" claude -p "hi"\`\n4. Refresh auth: \`claude login\``,
|
|
817
844
|
};
|
|
818
845
|
const hint = errorHints[assistantError] ?? `API error: ${assistantError}`;
|
|
819
846
|
yield { type: "error", message: hint };
|
package/src/server/ws/chat.ts
CHANGED
|
@@ -138,7 +138,7 @@ async function runStreamLoop(sessionId: string, providerId: string, content: str
|
|
|
138
138
|
const debugCmd = projectPath ? `cd ${projectPath} && claude -p "hi"` : `claude -p "hi"`;
|
|
139
139
|
safeSend(sessionId, {
|
|
140
140
|
type: "error",
|
|
141
|
-
message: `Claude SDK timed out after ${elapsed}s for project "${projectPath || "(no project)"}".${wslHint}\n\nDebug steps:\n1. Run
|
|
141
|
+
message: `Claude SDK timed out after ${elapsed}s for project "${projectPath || "(no project)"}".${wslHint}\n\nDebug steps:\n1. Run: \`${debugCmd}\` — if it also hangs, the issue is your Claude CLI environment\n2. Check env vars: \`echo $ANTHROPIC_API_KEY $ANTHROPIC_BASE_URL\` — stale/invalid keys cause silent hang\n3. Try with env cleared: \`ANTHROPIC_API_KEY="" ANTHROPIC_BASE_URL="" ${debugCmd}\`\n4. Check hooks/MCP: \`cat ${projectPath}/.claude/settings.local.json\`\n5. Refresh auth: \`claude login\``,
|
|
142
142
|
});
|
|
143
143
|
abortController.abort();
|
|
144
144
|
return;
|