Skip to content

Commit 9560b67

Browse files
VKrishna04Copilot
andcommitted
docs: update GitHub App setup instructions and clarify secret naming conventions
Co-authored-by: Copilot <copilot@github.com> Signed-off-by: Krishna GSVV <krishnagsvv@gmail.com>
1 parent 5d57512 commit 9560b67

21 files changed

Lines changed: 528 additions & 265 deletions

File tree

.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ GITHUB_APP_CLIENT_SECRET=
2626
# Webhook secret used to verify incoming GitHub webhooks
2727
GITHUB_APP_WEBHOOK_SECRET=
2828

29+
# NOTE: GitHub Actions disallows repository secret names beginning with `GITHUB_`.
30+
# When adding these values as GitHub repository secrets (for CI), use the
31+
# `CODELEDGER_GH_*` names instead. Example mapping:
32+
# CODELEDGER_GH_APP_ID -> (value of GITHUB_APP_ID)
33+
# CODELEDGER_GH_APP_PRIVATE_KEY -> (contents of GITHUB_APP_PRIVATE_KEY file)
34+
# CODELEDGER_GH_APP_CLIENT_ID -> (value of GITHUB_APP_CLIENT_ID)
35+
# CODELEDGER_GH_APP_CLIENT_SECRET -> (value of GITHUB_APP_CLIENT_SECRET)
36+
# CODELEDGER_GH_APP_WEBHOOK_SECRET -> (value of GITHUB_APP_WEBHOOK_SECRET)
37+
2938
# Application secrets
3039
# Secret used to sign session cookies / JWTs (choose a long random string)
3140
SESSION_SECRET=

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ If you are a developer compiling the extension from source and do not want to us
5757
1. Go to **GitHub Developer Settings** -> **GitHub Apps** -> **New GitHub App**.
5858
2. **GitHub App name:** `CodeLedger Dev` (or similar).
5959
3. **Homepage URL:** (Optional, your GitHub profile/repo URL).
60-
4. **Callback URL:** If using a web-backend like Cloudflare Workers, use `https://<your-worker-url>.workers.dev/auth/github/callback`. For Chrome Extension direct flows, use `https://<extension-id>.chromiumapp.org/`.
60+
4. **Callback URL:** If using a web-backend like Cloudflare Workers, use `https://<your-worker-url>.workers.dev/api/auth/github/callback`. For Chrome Extension direct flows, use `https://<extension-id>.chromiumapp.org/`.
6161
5. **Webhook:** Disable Webhook (Active: false).
6262
6. **Permissions:**
6363
* **Repository Permissions:**
@@ -76,7 +76,7 @@ sequenceDiagram
7676
participant GitHub as GitHub OAuth API
7777
7878
User->>Extension: Clicks "Connect GitHub"
79-
Extension->>CFWorker: Opens popup to /auth/github
79+
Extension->>CFWorker: Opens popup to /api/auth/github
8080
CFWorker->>GitHub: Redirects user to GitHub Authorize URI
8181
GitHub-->>User: Prompts for permission (repo scope)
8282
User->>GitHub: Approves access

docs/ARCHITECTURE.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ graph TD
1111
Ext[Browser Extension] -->|Observes| LeetCode[LeetCode]
1212
Ext -->|Observes| GFG[GeeksForGeeks]
1313
Ext -->|Observes| CF[Codeforces]
14-
14+
1515
Ext -->|Event: problem:solved| EventBus[Event Bus]
1616
EventBus --> BG[Background Service Worker]
17-
17+
1818
BG --> Storage[Local chrome.storage]
1919
BG -->|AI Review| LLM[Gemini/OpenAI/Ollama]
2020
BG -->|GraphQL/REST| Git[GitHub API - Tree Commit]
@@ -41,7 +41,7 @@ sequenceDiagram
4141
```
4242

4343
**Explanation:**
44-
Upon installation or startup, the Service Worker initializes `BasePlatformHandler`, `BaseGitHandler`, and `BaseAIHandler` instances. This design means that adding a new platform (like HackerRank) only requires writing a single isolated class and registering it.
44+
Upon installation or startup, the Service Worker initializes `BasePlatformHandler`, `BaseGitHandler`, and `BaseAIHandler` instances. This design means that adding a new platform (like HackerRank) only requires writing a single isolated class and registering it.
4545

4646
## 3. GitHub Secure OAuth Flow
4747

@@ -51,11 +51,11 @@ Since MV3 service workers and cross-origin isolation make OAuth tricky without h
5151
sequenceDiagram
5252
participant User
5353
participant Ext as CodeLedger
54-
participant CF as Cloudflare Worker (/auth/github)
54+
participant CF as Cloudflare Worker (/api/auth/github)
5555
participant GH as GitHub OAuth
56-
56+
5757
User->>Ext: Clicks Login
58-
Ext->>CF: Popup window to /auth/github
58+
Ext->>CF: Popup window to /api/auth/github
5959
CF->>GH: Redirect to authorization UI
6060
User->>GH: Approves
6161
GH->>CF: Callback with ?code=XXX
@@ -76,16 +76,16 @@ CodeLedger uses the GitHub Git Data APIs (`/git/trees`, `/git/commits`, `/git/re
7676
sequenceDiagram
7777
participant BG as Background Worker
7878
participant GH as GitHub API (Trees)
79-
79+
8080
BG->>GH: GET /git/refs/heads/main
8181
GH-->>BG: Return base commit SHA
82-
82+
8383
BG->>GH: POST /git/trees (Files & prev SHA)
8484
GH-->>BG: Return new tree SHA
85-
85+
8686
BG->>GH: POST /git/commits (Tree SHA & Parent SHA)
8787
GH-->>BG: Return new commit SHA
88-
88+
8989
BG->>GH: PATCH /git/refs/heads/main (New commit SHA)
9090
GH-->>BG: Branch Updated
9191
```

docs/GITHUB_APP_SETUP.md

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ This document provides recommended values and a manifest to create the GitHub Ap
66

77
**Homepage URL**: https://codeledger.vkrishna04.me
88

9-
**Callback URL (OAuth / redirect)**: https://api.codeledger.vkrishna04.me/auth/github/callback
9+
**Callback URL (OAuth / redirect)**: https://codeledger.vkrishna04.me/api/auth/github/callback
1010

11-
**Webhook URL**: https://api.codeledger.vkrishna04.me/webhook/github
11+
**Webhook URL**: https://codeledger.vkrishna04.me/api/webhook/github
1212

1313
**Webhook secret**: generate a strong random secret and store it as an environment variable in your Cloudflare Worker (e.g. `GITHUB_APP_WEBHOOK_SECRET`).
1414

@@ -49,8 +49,8 @@ Example GitHub App manifest (use via GitHub App creation flow):
4949
{
5050
"name": "Code Ledger",
5151
"url": "https://codeledger.vkrishna04.me",
52-
"hook_attributes": { "url": "https://api.codeledger.vkrishna04.me/webhook/github" },
53-
"redirect_url": "https://api.codeledger.vkrishna04.me/auth/github/callback",
52+
"hook_attributes": { "url": "https://codeledger.vkrishna04.me/api/webhook/github" },
53+
"redirect_url": "https://codeledger.vkrishna04.me/api/auth/github/callback",
5454
"public": false,
5555
"default_permissions": {
5656
"metadata": "read",
@@ -87,7 +87,7 @@ Manual Cloudflare Worker deployment (via Cloudflare dashboard):
8787
2. In the Cloudflare dashboard, create a new Worker, choose "Upload a script" or use the online editor and paste `worker/src/index.js`.
8888
3. In Worker settings, add the environment variables listed above under "Variables" (or "Secrets/Environment").
8989
4. If your Worker serves static assets (worker/public), configure a KV or Pages deployment, or copy the static assets into the Worker script's `serveStatic` root.
90-
5. Test the endpoints: `/auth/github` should redirect to GitHub, `/auth/github/callback` should return a small HTML page that posts the token back to the opener window.
90+
5. Test the endpoints: `/api/auth/github` should redirect to GitHub, `/api/auth/github/callback` should return a small HTML page that posts the token back to the opener window.
9191

9292
Notes on the two auth modes:
9393

@@ -97,21 +97,42 @@ Notes on the two auth modes:
9797

9898
Deploying the worker from this repository (recommended GitHub Actions flow):
9999

100-
- Add repository secrets: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_ID`, `GITHUB_APP_WEBHOOK_SECRET`.
101-
- Create a GitHub Actions workflow that runs `wrangler publish` (or `wrangler deploy`) using those secrets.
100+
- Repository secrets required by CI (use these exact names):
101+
- `CF_API_TOKEN` — Cloudflare API token with permissions to manage Workers, KV, and Routes.
102+
- `CF_ACCOUNT_ID` — Your Cloudflare account ID (32-char string).
103+
- `CANONICAL_KV_ID` — Workers KV namespace id used for `CANONICAL_MAP` binding.
104+
- `CANONICAL_UPLOAD_TOKEN` — Admin token used by the Worker to accept canonical-map uploads.
105+
- `SESSION_SECRET` — Long random signing secret for session/JWTs.
106+
- `CODELEDGER_GH_APP_PRIVATE_KEY` — GitHub App private key (PEM contents).
107+
- `CODELEDGER_GH_APP_ID` — GitHub App numeric ID.
108+
- `CODELEDGER_GH_APP_CLIENT_ID` — (optional) OAuth client id if using OAuth flow.
109+
- `CODELEDGER_GH_APP_CLIENT_SECRET` — (optional) OAuth client secret if using OAuth flow.
110+
- `CODELEDGER_GH_APP_WEBHOOK_SECRET` — Webhook secret to validate GitHub webhooks.
102111

103-
Quick local steps to publish the worker (you must have `wrangler` installed):
112+
> Note: GitHub Actions disallows repository secret names that begin with `GITHUB_`. That is why the CI and repo are configured to use the `CODELEDGER_GH_*` mapping instead of `GITHUB_*`.
113+
114+
- The included workflow (`.github/workflows/deploy-worker.yml`) will:
115+
1. Generate a runtime `worker/wrangler.toml` using `CF_ACCOUNT_ID` and `CANONICAL_KV_ID`.
116+
2. Upload the runtime secrets listed above to Cloudflare (via `wrangler secret put`).
117+
3. Publish the Worker to the route `https://codeledger.vkrishna04.me/*` (landing + API).
118+
119+
Quick local steps to run a smoke test (no CI required):
104120

105121
```powershell
106122
cd worker
107-
wrangler login
108-
wrangler publish
123+
npm ci
124+
# Start a local dev server for the Worker (requires Wrangler installed)
125+
npx wrangler dev --local
109126
```
110127

128+
Notes:
129+
- If you prefer CI-based deployment, ensure `CF_API_TOKEN` and all required secrets are set in the repository before triggering the workflow — otherwise the workflow will fail early during validation.
130+
- If you want me to trigger the workflow now, I can do that (I will check whether `CF_API_TOKEN` exists in repo secrets first). Alternatively I can run `wrangler dev` locally for a smoke test instead.
131+
111132
If you want, I can:
112133

113134
- Add a `docs` page with a sample `worker/.env.example` and the minimal `worker/src/index.js` auth routes wired to the App settings.
114-
- Prepare a GitHub Actions workflow that deploys the worker using `CLOUDFLARE_API_TOKEN`.
135+
- Prepare or run the GitHub Actions workflow that deploys the worker using `CF_API_TOKEN`.
115136

116137
---
117138
File references:

docs/OPENAPI.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ info:
66

77
# Servers: update these to match `CONSTANTS.URLS` in src/core/constants.js.
88
servers:
9-
- url: https://api.codeledger.vkrishna04.me
9+
- url: https://codeledger.vkrishna04.me/api
1010
description: Production Cloudflare OAuth Bridge (AUTH_WORKER)
1111
- url: https://codeledger.vkrishna04.me
1212
description: Project landing / public docs

prompt.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ React + Vite + Tailwind, driven entirely by a `settings.json` in the repo. Dynam
2828
| License | Apache 2.0 |
2929
| Author | VKrishna04 |
3030
| Domain | `codeledger.vkrishna04.me` |
31-
| Auth worker | `https://api.codeledger.vkrishna04.me` |
31+
| Auth worker | `https://codeledger.vkrishna04.me/api` |
3232
| Telemetry | `https://counter.vkrishna04.me` (CFlair-Counter, deployed) |
3333
| Canonical map | `https://raw.githubusercontent.com/vkrishna04/codeledger/main/data/canonical-map.json` |
3434

@@ -414,7 +414,7 @@ export const CONSTANTS = Object.freeze({
414414
// ── External URLs (all user-overridable via Settings > Advanced) ──
415415
URLS: {
416416
LANDING: 'https://codeledger.vkrishna04.me',
417-
AUTH_WORKER: 'https://api.codeledger.vkrishna04.me',
417+
AUTH_WORKER: 'https://codeledger.vkrishna04.me/api',
418418
TELEMETRY: 'https://counter.vkrishna04.me',
419419
CANONICAL_MAP_RAW: 'https://raw.githubusercontent.com/vkrishna04/codeledger/main/data/canonical-map.json',
420420
CANONICAL_MAP_SCHEMA: 'https://raw.githubusercontent.com/vkrishna04/codeledger/main/data/schema/canonical-map.schema.json',

src/core/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ export const CONSTANTS = Object.freeze({
104104
supportsLiveFetch: true,
105105
keyRequired: false,
106106
},
107+
openrouter: {
108+
id: "openrouter",
109+
name: "OpenRouter",
110+
endpoint: "https://api.openrouter.ai/v1",
111+
modelsEndpoint: "https://api.openrouter.ai/v1/models",
112+
defaultModel: "openrouter/gpt-4o-mini",
113+
supportsLiveFetch: true,
114+
keyRequired: true,
115+
},
107116
},
108117

109118
AI_DEFAULT_PRIMARY: "gemini",

src/core/model-fetch.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ export async function fetchModelsForProvider(providerId) {
126126
// ignore
127127
}
128128
}
129+
130+
if (providerId === "openrouter") {
131+
const key = await getFirstKeyForProvider("openrouter");
132+
if (!key) return [];
133+
const me = provider.modelsEndpoint;
134+
const ep = me ? me.replace(/\/$/, "") : `${epFor()}/models`;
135+
try {
136+
const res = await fetch(ep, {
137+
headers: { Authorization: `Bearer ${key}` },
138+
});
139+
if (res.ok) {
140+
const data = await res.json();
141+
const orModels = (data.data || data.models || []).map((m) => ({
142+
id: m.id || m.name,
143+
label: `${provider.name}: ${m.name || m.id}`,
144+
group: provider.name,
145+
}));
146+
models.push(...orModels);
147+
}
148+
} catch (e) {
149+
// ignore
150+
}
151+
}
129152
} catch (e) {
130153
// best-effort
131154
}
@@ -219,6 +242,22 @@ export async function testAIKey(providerId, key) {
219242
}
220243
}
221244

245+
if (providerId === "openrouter") {
246+
const ep = me
247+
? me.replace(/\/$/, "")
248+
: `${(provider.endpoint || "").replace(/\/$/, "")}/models`;
249+
try {
250+
const res = await fetch(ep, {
251+
headers: { Authorization: `Bearer ${key}` },
252+
});
253+
if (res.ok) return { ok: true };
254+
const text = await res.text();
255+
return { ok: false, error: `Status ${res.status}: ${text}` };
256+
} catch (e) {
257+
return { ok: false, error: e.message };
258+
}
259+
}
260+
222261
return { ok: false, error: "Provider does not support key testing" };
223262
} catch (e) {
224263
return { ok: false, error: e.message };

src/handlers/ai/claude/index.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,47 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { BaseAIHandler } from '../../_base/BaseAIHandler.js';
7-
import { APIKeyPool } from '../../../core/api-key-pool.js';
8-
import { Storage } from '../../../core/storage.js';
9-
import { CONSTANTS } from '../../../core/constants.js';
6+
import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
7+
import { APIKeyPool } from "../../../core/api-key-pool.js";
8+
import { Storage } from "../../../core/storage.js";
9+
import { CONSTANTS } from "../../../core/constants.js";
1010

1111
export class ClaudeHandler extends BaseAIHandler {
1212
constructor() {
13-
super('claude', 'Anthropic Claude');
14-
this.keyPool = new APIKeyPool('claude');
13+
super("claude", "Anthropic Claude");
14+
this.keyPool = new APIKeyPool("claude");
1515
}
1616

1717
async review(code, problemContext) {
1818
const key = await this.keyPool.getNextKey();
19-
if (!key) throw new Error('No Claude API key available.');
19+
if (!key) throw new Error("No Claude API key available.");
2020

2121
const settings = await Storage.getSettings();
22-
const model = settings.aiModel || CONSTANTS.AI_PROVIDERS.claude.defaultModel;
22+
const model =
23+
settings.claude_model ||
24+
settings.aiModel ||
25+
CONSTANTS.AI_PROVIDERS.claude.defaultModel;
2326

2427
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
2528

2629
try {
27-
const res = await fetch(`${CONSTANTS.AI_PROVIDERS.claude.endpoint}/messages`, {
28-
method: 'POST',
29-
headers: {
30-
'Content-Type': 'application/json',
31-
'x-api-key': key,
32-
'anthropic-version': '2023-06-01',
33-
'anthropic-dangerously-allow-browser': 'true' // Caution: requires bg proxy or this header depending on exact extension env
30+
const res = await fetch(
31+
`${CONSTANTS.AI_PROVIDERS.claude.endpoint}/messages`,
32+
{
33+
method: "POST",
34+
headers: {
35+
"Content-Type": "application/json",
36+
"x-api-key": key,
37+
"anthropic-version": "2023-06-01",
38+
"anthropic-dangerously-allow-browser": "true", // Caution: requires bg proxy or this header depending on exact extension env
39+
},
40+
body: JSON.stringify({
41+
model,
42+
max_tokens: 1024,
43+
messages: [{ role: "user", content: prompt }],
44+
}),
3445
},
35-
body: JSON.stringify({
36-
model,
37-
max_tokens: 1024,
38-
messages: [{ role: 'user', content: prompt }]
39-
})
40-
});
46+
);
4147

4248
if (!res.ok) {
4349
if (res.status === 429) this.keyPool.markFailed(key);
@@ -47,7 +53,7 @@ export class ClaudeHandler extends BaseAIHandler {
4753
const data = await res.json();
4854
return data.content[0].text;
4955
} catch (err) {
50-
this.dbg.error('Claude review failed', err);
56+
this.dbg.error("Claude review failed", err);
5157
throw err;
5258
}
5359
}

0 commit comments

Comments
 (0)