@hienlh/ppm 0.7.29 → 0.7.31
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 +10 -0
- package/dist/web/assets/{chat-tab-B0UcLXFA.js → chat-tab-CSPEaVU7.js} +1 -1
- package/dist/web/assets/{code-editor-Bb-RxKRW.js → code-editor-CBufIEYp.js} +1 -1
- package/dist/web/assets/{database-viewer-B4pr_bwC.js → database-viewer-BMwU_QIa.js} +1 -1
- package/dist/web/assets/{diff-viewer-DuHuqbG4.js → diff-viewer-CAQGaToO.js} +1 -1
- package/dist/web/assets/{git-graph-BkTGWVMA.js → git-graph-lMDmYH4P.js} +1 -1
- package/dist/web/assets/{index-BWVej31S.js → index-DBeDeMZU.js} +4 -4
- package/dist/web/assets/keybindings-store-CQMiKDW3.js +1 -0
- package/dist/web/assets/{markdown-renderer-CyObkWZ-.js → markdown-renderer-BUIn5Ay7.js} +1 -1
- package/dist/web/assets/{postgres-viewer-CHVUVt7c.js → postgres-viewer-ByOqeLj-.js} +1 -1
- package/dist/web/assets/{settings-tab-C1Uj7t80.js → settings-tab-0HhY49gB.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-D1ohxjF9.js → sqlite-viewer-DDjKEx6w.js} +1 -1
- package/dist/web/assets/{terminal-tab-DGzY_K3A.js → terminal-tab-witLeJAI.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/restart.ts +15 -6
- package/src/web/components/layout/project-bar.tsx +1 -1
- package/dist/web/assets/keybindings-store-DoOYThSa.js +0 -1
- package/test-claude-oauth-v2.mjs +0 -165
- package/test-claude-oauth.mjs +0 -175
- package/test-verify-oat.mjs +0 -106
package/test-claude-oauth.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Test Claude OAuth flow — manual copy/paste approach
|
|
4
|
-
* Usage: bun test-claude-oauth.mjs
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import crypto from 'crypto'
|
|
8
|
-
import readline from 'readline'
|
|
9
|
-
import { execSync } from 'child_process'
|
|
10
|
-
|
|
11
|
-
const CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e'
|
|
12
|
-
const SCOPE = 'org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload'
|
|
13
|
-
|
|
14
|
-
// Redirect URIs to test (in order)
|
|
15
|
-
const REDIRECT_URI_OPTIONS = [
|
|
16
|
-
{ label: 'platform.claude.com (manual code page)', uri: 'https://platform.claude.com/oauth/code/callback' },
|
|
17
|
-
{ label: 'localhost:54545 (CLIProxyAPI style)', uri: 'http://localhost:54545/callback' },
|
|
18
|
-
{ label: 'localhost (no port)', uri: 'http://localhost/callback' },
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
// Token endpoints to try
|
|
22
|
-
const TOKEN_ENDPOINTS = [
|
|
23
|
-
'https://api.anthropic.com/v1/oauth/token',
|
|
24
|
-
'https://claude.ai/api/oauth/token',
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
// ── PKCE ────────────────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
function generatePKCE() {
|
|
30
|
-
// 96 bytes → 128 base64url chars (matches CLIProxyAPI exactly)
|
|
31
|
-
const verifierBytes = crypto.randomBytes(96)
|
|
32
|
-
const verifier = verifierBytes.toString('base64url')
|
|
33
|
-
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url')
|
|
34
|
-
const state = crypto.randomBytes(16).toString('base64url')
|
|
35
|
-
return { verifier, challenge, state }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ── CLI helpers ──────────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
function copyToClipboard(text) {
|
|
41
|
-
try {
|
|
42
|
-
execSync(`echo ${JSON.stringify(text)} | pbcopy`)
|
|
43
|
-
return true
|
|
44
|
-
} catch { return false }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function ask(rl, question) {
|
|
48
|
-
return new Promise(resolve => rl.question(question, resolve))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function parseCode(input) {
|
|
52
|
-
input = input.trim()
|
|
53
|
-
// Full URL: http://localhost/callback?code=XXX&state=YYY
|
|
54
|
-
try {
|
|
55
|
-
const url = new URL(input)
|
|
56
|
-
const code = url.searchParams.get('code')
|
|
57
|
-
if (code) return code.split('#')[0]
|
|
58
|
-
} catch {}
|
|
59
|
-
// code#state format (platform.claude.com)
|
|
60
|
-
if (input.includes('#')) return input.split('#')[0]
|
|
61
|
-
// Raw code
|
|
62
|
-
return input.split('?')[0].split('&')[0]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ── Token exchange ───────────────────────────────────────────────────────────
|
|
66
|
-
|
|
67
|
-
async function exchangeCode({ code, verifier, state, redirectUri }) {
|
|
68
|
-
const body = {
|
|
69
|
-
grant_type: 'authorization_code',
|
|
70
|
-
client_id: CLIENT_ID,
|
|
71
|
-
code,
|
|
72
|
-
redirect_uri: redirectUri,
|
|
73
|
-
code_verifier: verifier,
|
|
74
|
-
state,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
for (const endpoint of TOKEN_ENDPOINTS) {
|
|
78
|
-
process.stdout.write(` → POST ${endpoint} ... `)
|
|
79
|
-
const res = await fetch(endpoint, {
|
|
80
|
-
method: 'POST',
|
|
81
|
-
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
|
82
|
-
body: JSON.stringify(body),
|
|
83
|
-
})
|
|
84
|
-
const data = await res.json()
|
|
85
|
-
console.log(`HTTP ${res.status}`)
|
|
86
|
-
|
|
87
|
-
if (res.ok) return { endpoint, data }
|
|
88
|
-
console.log(` error: ${JSON.stringify(data?.error || data)}`)
|
|
89
|
-
}
|
|
90
|
-
return null
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
94
|
-
|
|
95
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
96
|
-
|
|
97
|
-
console.log('\n╔══════════════════════════════════════════╗')
|
|
98
|
-
console.log('║ Claude OAuth Test — PPM ║')
|
|
99
|
-
console.log('╚══════════════════════════════════════════╝\n')
|
|
100
|
-
|
|
101
|
-
// Pick redirect URI
|
|
102
|
-
console.log('Redirect URI options:')
|
|
103
|
-
REDIRECT_URI_OPTIONS.forEach((o, i) => console.log(` ${i + 1}. ${o.label}`))
|
|
104
|
-
const choice = parseInt(await ask(rl, '\nChọn (1-3) [default: 1]: ') || '1') - 1
|
|
105
|
-
const { uri: redirectUri, label } = REDIRECT_URI_OPTIONS[choice] || REDIRECT_URI_OPTIONS[0]
|
|
106
|
-
console.log(`✓ Using: ${redirectUri}\n`)
|
|
107
|
-
|
|
108
|
-
// Generate PKCE + URL
|
|
109
|
-
const { verifier, challenge, state } = generatePKCE()
|
|
110
|
-
console.log(`code_verifier length: ${verifier.length} chars`)
|
|
111
|
-
|
|
112
|
-
const params = new URLSearchParams({
|
|
113
|
-
client_id: CLIENT_ID,
|
|
114
|
-
response_type: 'code',
|
|
115
|
-
redirect_uri: redirectUri,
|
|
116
|
-
scope: SCOPE,
|
|
117
|
-
code_challenge: challenge,
|
|
118
|
-
code_challenge_method: 'S256',
|
|
119
|
-
state,
|
|
120
|
-
prompt: 'login',
|
|
121
|
-
})
|
|
122
|
-
// platform.claude.com redirect uses special `code=true` trigger
|
|
123
|
-
if (redirectUri.includes('platform.claude.com')) {
|
|
124
|
-
params.set('code', 'true')
|
|
125
|
-
}
|
|
126
|
-
const authUrl = `https://claude.ai/oauth/authorize?${params}`
|
|
127
|
-
|
|
128
|
-
console.log('\n┌─ Authorization URL ──────────────────────────────────────────────')
|
|
129
|
-
console.log(`│ ${authUrl}`)
|
|
130
|
-
console.log('└─────────────────────────────────────────────────────────────────\n')
|
|
131
|
-
|
|
132
|
-
const copied = copyToClipboard(authUrl)
|
|
133
|
-
if (copied) console.log('✓ Copied to clipboard (pbcopy)\n')
|
|
134
|
-
|
|
135
|
-
console.log('Mở URL trong browser → authorize → copy code/URL trả về\n')
|
|
136
|
-
if (label.includes('platform')) {
|
|
137
|
-
console.log(' platform.claude.com sẽ hiện code dạng: XXXX#STATE')
|
|
138
|
-
} else {
|
|
139
|
-
console.log(' Browser sẽ redirect về localhost (lỗi kết nối là bình thường)')
|
|
140
|
-
console.log(' Copy toàn bộ URL từ address bar: http://localhost.../callback?code=XXX')
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const input = await ask(rl, '\nPaste code/URL: ')
|
|
144
|
-
const code = parseCode(input)
|
|
145
|
-
if (!code) { console.error('❌ Không parse được code'); process.exit(1) }
|
|
146
|
-
console.log(`✓ code = ${code.slice(0, 20)}...\n`)
|
|
147
|
-
|
|
148
|
-
// Exchange
|
|
149
|
-
console.log('Thử exchange token:')
|
|
150
|
-
const result = await exchangeCode({ code, verifier, state, redirectUri })
|
|
151
|
-
|
|
152
|
-
if (result) {
|
|
153
|
-
const { endpoint, data } = result
|
|
154
|
-
console.log(`\n✅ Success! Endpoint: ${endpoint}`)
|
|
155
|
-
console.log('\n── Token info ──────────────────────────────────────')
|
|
156
|
-
console.log(` access_token: ${data.access_token?.slice(0, 30)}...`)
|
|
157
|
-
console.log(` refresh_token: ${data.refresh_token?.slice(0, 30)}...`)
|
|
158
|
-
console.log(` token_type: ${data.token_type}`)
|
|
159
|
-
console.log(` expires_in: ${data.expires_in}s`)
|
|
160
|
-
if (data.account) {
|
|
161
|
-
console.log(` email: ${data.account.email_address}`)
|
|
162
|
-
console.log(` account_uuid: ${data.account.uuid}`)
|
|
163
|
-
}
|
|
164
|
-
if (data.organization) {
|
|
165
|
-
console.log(` org_name: ${data.organization.name}`)
|
|
166
|
-
console.log(` org_uuid: ${data.organization.uuid}`)
|
|
167
|
-
}
|
|
168
|
-
console.log('────────────────────────────────────────────────────')
|
|
169
|
-
console.log('\nFull response:')
|
|
170
|
-
console.log(JSON.stringify(data, null, 2))
|
|
171
|
-
} else {
|
|
172
|
-
console.log('\n❌ Tất cả endpoints đều fail')
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
rl.close()
|
package/test-verify-oat.mjs
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Test verify sk-ant-oat token — see what APIs return
|
|
4
|
-
* Usage: bun test-verify-oat.mjs [TOKEN]
|
|
5
|
-
* If no TOKEN arg, reads TEST_OAUTH_TOKEN_1 from .env.test
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readFileSync } from "node:fs";
|
|
9
|
-
import { resolve, dirname } from "node:path";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
|
|
12
|
-
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
|
|
14
|
-
// Token from CLI arg or .env.test
|
|
15
|
-
let token = process.argv[2];
|
|
16
|
-
if (!token) {
|
|
17
|
-
const envPath = resolve(__dir, ".env.test");
|
|
18
|
-
const env = Object.fromEntries(
|
|
19
|
-
readFileSync(envPath, "utf-8")
|
|
20
|
-
.split("\n")
|
|
21
|
-
.filter((l) => l && !l.startsWith("#"))
|
|
22
|
-
.map((l) => l.split("=").map((s) => s.trim()))
|
|
23
|
-
.filter(([k, v]) => k && v),
|
|
24
|
-
);
|
|
25
|
-
token = env.TEST_OAUTH_TOKEN_1;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!token || token.startsWith("REPLACE")) {
|
|
29
|
-
console.error("Usage: bun test-verify-oat.mjs [TOKEN]");
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
console.log(`Token: ${token.slice(0, 30)}...`);
|
|
34
|
-
|
|
35
|
-
// ── Helper ──────────────────────────────────────────────
|
|
36
|
-
async function tryEndpoint({ label, url, method = "GET", headers = {}, body }) {
|
|
37
|
-
console.log(`\n── ${label} ──`);
|
|
38
|
-
try {
|
|
39
|
-
const opts = {
|
|
40
|
-
method,
|
|
41
|
-
headers: {
|
|
42
|
-
Accept: "application/json",
|
|
43
|
-
Authorization: `Bearer ${token}`,
|
|
44
|
-
...headers,
|
|
45
|
-
},
|
|
46
|
-
signal: AbortSignal.timeout(10_000),
|
|
47
|
-
};
|
|
48
|
-
if (body) opts.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
49
|
-
const res = await fetch(url, opts);
|
|
50
|
-
const text = await res.text();
|
|
51
|
-
console.log(`HTTP ${res.status}`);
|
|
52
|
-
try {
|
|
53
|
-
console.log(JSON.stringify(JSON.parse(text), null, 2));
|
|
54
|
-
} catch {
|
|
55
|
-
console.log(text.slice(0, 500));
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.log(`ERROR: ${e.message}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ── 1. claude auth status (CLI) ─────────────────────────
|
|
63
|
-
console.log("\n── claude auth status (CLI) ──");
|
|
64
|
-
try {
|
|
65
|
-
const proc = Bun.spawn(["claude", "auth", "status"], {
|
|
66
|
-
env: { ...process.env, CLAUDE_CODE_OAUTH_TOKEN: token, ANTHROPIC_API_KEY: "" },
|
|
67
|
-
stdout: "pipe",
|
|
68
|
-
stderr: "pipe",
|
|
69
|
-
});
|
|
70
|
-
const stdout = await new Response(proc.stdout).text();
|
|
71
|
-
const stderr = await new Response(proc.stderr).text();
|
|
72
|
-
await proc.exited;
|
|
73
|
-
console.log("stdout:", stdout);
|
|
74
|
-
if (stderr) console.log("stderr:", stderr);
|
|
75
|
-
} catch (e) {
|
|
76
|
-
console.log(`ERROR: ${e.message}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── 2. /api/oauth/usage ─────────────────────────────────
|
|
80
|
-
await tryEndpoint({
|
|
81
|
-
label: "GET /api/oauth/usage",
|
|
82
|
-
url: "https://api.anthropic.com/api/oauth/usage",
|
|
83
|
-
headers: { "anthropic-beta": "oauth-2025-04-20", "User-Agent": "ppm/1.0" },
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ── 3. /v1/organizations (org info) ─────────────────────
|
|
87
|
-
await tryEndpoint({
|
|
88
|
-
label: "GET /v1/organizations",
|
|
89
|
-
url: "https://api.anthropic.com/v1/organizations",
|
|
90
|
-
headers: { "anthropic-version": "2023-06-01" },
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// ── 4. /api/auth/session (claude.ai session) ────────────
|
|
94
|
-
await tryEndpoint({
|
|
95
|
-
label: "GET /api/auth/session (claude.ai)",
|
|
96
|
-
url: "https://claude.ai/api/auth/session",
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// ── 5. /v1/messages (minimal — test if token works) ─────
|
|
100
|
-
await tryEndpoint({
|
|
101
|
-
label: "POST /v1/messages (1 token test)",
|
|
102
|
-
url: "https://api.anthropic.com/v1/messages",
|
|
103
|
-
method: "POST",
|
|
104
|
-
headers: { "Content-Type": "application/json", "anthropic-version": "2023-06-01" },
|
|
105
|
-
body: { model: "claude-sonnet-4-20250514", max_tokens: 1, messages: [{ role: "user", content: "hi" }] },
|
|
106
|
-
});
|