-
Notifications
You must be signed in to change notification settings - Fork 31
feat: implement API for AI interactions and authentication: #244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # Server | ||
| PORT=4000 | ||
| SUPERCODE_JSON_BODY_LIMIT=10mb | ||
|
|
||
| # Auth (Better-Auth) | ||
| BETTER_AUTH_URL=http://localhost:4000 | ||
| CLIENT_URL=http://localhost:3000 | ||
|
|
||
| # Database (terminal DB) | ||
| DATABASE_URL=postgresql://user:password@localhost:5432/supercode_terminal | ||
|
|
||
| # GitHub OAuth | ||
| GITHUB_CLIENT_ID= | ||
| GITHUB_CLIENT_SECRET= | ||
|
|
||
| # Provider API Keys | ||
| GOOGLE_GENERATIVE_AI_API_KEY= | ||
| OPENROUTER_API_KEY= | ||
| OPENROUTER_MODEL=openai/gpt-oss-120b:free | ||
| MINIMAX_API_KEY= | ||
| MINIMAX_MAX_TOKENS=4096 | ||
| NVIDIA_API_KEY= | ||
| NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1 | ||
| NVIDIA_MODEL=minimaxai/minimax-m3 | ||
| NVIDIA_MAX_TOKENS=8192 | ||
| NVIDIA_TEMPERATURE=1 | ||
| NVIDIA_TOP_P=0.95 | ||
| MERGE_DEV_API_KEY= | ||
| ORCAROUTER_API_KEY= | ||
| CONCENTRATEAI_API_KEY= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,28 @@ | |
| "name": "api", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "echo 'api app scaffold pending'", | ||
| "build": "echo 'api build scaffold pending'", | ||
| "dev": "bun --watch src/index.ts", | ||
| "build": "bun build --target bun --outdir dist src/index.ts", | ||
| "start": "bun src/index.ts", | ||
| "lint": "echo 'api lint scaffold pending'", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Replace the no-op lint script.
🤖 Prompt for AI Agents |
||
| "typecheck": "echo 'api typecheck scaffold pending'" | ||
| "typecheck": "tsc --noEmit" | ||
| }, | ||
| "dependencies": { | ||
| "@ai-sdk/google": "^3.0.80", | ||
| "@ai-sdk/openai-compatible": "^2.0.48", | ||
| "@hono/zod-validator": "^0.5.0", | ||
| "@openrouter/ai-sdk-provider": "^2.9.0", | ||
| "@super/db-terminal": "workspace:*", | ||
| "ai": "^7.0.37", | ||
| "better-auth": "^1.6.23", | ||
| "hono": "^4.7.0", | ||
| "vercel-minimax-ai-provider": "^0.0.2", | ||
| "zod": "^3.25.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/bun": "^1.3.14", | ||
| "typescript": "^5.7.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { Hono } from "hono" | ||
| import { cors } from "hono/cors" | ||
| import { logger } from "hono/logger" | ||
| import { bodyLimit } from "hono/body-limit" | ||
| import { healthRoute } from "./routes/health" | ||
| import { authRoute } from "./routes/auth" | ||
| import { aiRoute } from "./routes/ai" | ||
| import type { AppEnv } from "./types" | ||
|
|
||
| export function createApp() { | ||
| const app = new Hono<AppEnv>().basePath("/api") | ||
|
|
||
| app.use("*", logger()) | ||
| app.use("*", cors()) | ||
| app.use("*", bodyLimit({ | ||
| maxSize: 10 * 1024 * 1024, | ||
| })) | ||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Connect the documented body-limit configuration to runtime behavior. The environment variable is declared but ignored by the application’s hardcoded limit.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| app.onError((err, c) => { | ||
| console.error("[API Error]", err) | ||
| return c.json({ error: "Internal server error" }, 500) | ||
| }) | ||
|
|
||
| app.route("/", healthRoute) | ||
| app.route("/", authRoute) | ||
| app.route("/", aiRoute) | ||
|
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- files ---\n'
git ls-files | rg '(^|/)(app|auth).*\.(ts|tsx|js)$|auth' || true
printf '\n--- app.ts outline ---\n'
ast-grep outline apps/api/src/app.ts --view expanded || true
printf '\n--- app.ts ---\n'
cat -n apps/api/src/app.ts
printf '\n--- auth.ts outline ---\n'
ast-grep outline apps/api/src/lib/auth.ts --view expanded || true
printf '\n--- auth.ts ---\n'
cat -n apps/api/src/lib/auth.ts
printf '\n--- route files matching auth/health/ai ---\n'
git ls-files apps/api/src | rg '(^|/)(routes|lib|index|auth|health|ai)\.(ts|js)$' || true
printf '\n--- route usages ---\n'
rg -n "healthRoute|authRoute|aiRoute|basePath|betterAuth|auth/me|sign-out|callback/github|`@auth`|/api/auth|toAuth" apps/api/src -S || trueRepository: yashdev9274/supercli Length of output: 249 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- files ---'
git ls-files | rg '(^|/)(app|auth).*\.(ts|tsx|js)$|auth' || true
printf '%s\n' '\n--- app.ts outline ---'
ast-grep outline apps/api/src/app.ts --view expanded || true
printf '%s\n' '\n--- app.ts ---'
cat -n apps/api/src/app.ts
printf '%s\n' '\n--- auth.ts outline ---'
ast-grep outline apps/api/src/lib/auth.ts --view expanded || true
printf '%s\n' '\n--- auth.ts ---'
cat -n apps/api/src/lib/auth.ts
printf '%s\n' '\n--- route files matching auth/health/ai ---'
git ls-files apps/api/src | rg '(^|/)(routes|lib|index|auth|health|ai)\.(ts|js)$' || true
printf '%s\n' '\n--- route usages ---'
rg -n "healthRoute|authRoute|aiRoute|basePath|betterAuth|auth/me|sign-out|callback/github|`@auth`|/api/auth|toAuth" apps/api/src -S || trueRepository: yashdev9274/supercli Length of output: 22917 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- apps/api/src/routes/auth.ts ---'
cat -n apps/api/src/routes/auth.ts
printf '%s\n' '\n--- apps/api/src/middleware/auth.ts ---'
cat -n apps/api/src/middleware/auth.ts
printf '%s\n' '\n--- apps/api package.json/auth deps ---'
if [ -f apps/api/package.json ]; then cat -n apps/api/package.json | sed -n '1,220p'; fi
printf '%s\n' '\n--- root/package lock snippets for hono/better-auth ---'
if [ -f package.json ]; then rg -n '"hono"|"better-auth"' package.json apps packages -S || true; fiRepository: yashdev9274/supercli Length of output: 10194 🌐 Web query:
💡 Result: In Better Auth, the handler function acts as the unified endpoint for all authentication-related requests (GET and POST) [1][2]. When integrating with Hono, you mount this handler to a specific path using Hono's.on method [3][4]. Mounting the Handler The standard approach is to use a wildcard path to ensure all authentication sub-routes are captured [2][4]. It is recommended to use the single asterisk () wildcard rather than double, as the latter can cause routing conflicts in some Hono configurations [5]. Example implementation: const app = new Hono; app.on(["POST", "GET"], "/api/auth/", (c) => { return auth.handler(c.req.raw); }); Configuration and basePath The path you provide in app.on must align with the basePath defined in your Better Auth configuration [6]. By default, Better Auth uses /api/auth as its base path [6]. If you change the basePath in your auth options, you must update the mounting path in your Hono application to match [6]. Key Considerations 1. CORS Middleware: If your frontend is on a different domain, register the Hono CORS middleware before the auth route to ensure preflight requests (OPTIONS) are handled correctly [2][4]. 2. Session Middleware: You can define a global Hono middleware that calls auth.api.getSession to populate the Hono context (c.set) with user and session data, making it available to other routes [2][4]. 3. Client Configuration: When using the Hono RPC client ( Citations:
Mount the Better Auth handler.
🤖 Prompt for AI Agents |
||
|
|
||
| return app | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { serve } from "bun" | ||
| import { createApp } from "./app" | ||
|
|
||
| const PORT = Number(process.env.PORT) || 4000 | ||
|
|
||
| serve({ | ||
| fetch: createApp().fetch, | ||
| port: PORT, | ||
| development: { | ||
| hmr: true, | ||
| }, | ||
| }) | ||
|
|
||
| console.log(`[API] Server running on http://localhost:${PORT}`) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { betterAuth } from "better-auth" | ||
| import { prismaAdapter } from "better-auth/adapters/prisma" | ||
| import prisma from "./prisma" | ||
|
|
||
| const serverUrl = process.env.BETTER_AUTH_URL || "http://localhost:4000" | ||
| const clientUrl = process.env.CLIENT_URL || "http://localhost:3000" | ||
| const isProduction = serverUrl.startsWith("https://") | ||
|
|
||
| export const auth = betterAuth({ | ||
| database: prismaAdapter(prisma, { provider: "postgresql" }), | ||
| baseURL: serverUrl, | ||
| basePath: "/api/auth", | ||
| trustedOrigins: [clientUrl, serverUrl], | ||
| socialProviders: { | ||
| github: { | ||
| clientId: process.env.GITHUB_CLIENT_ID as string, | ||
| clientSecret: process.env.GITHUB_CLIENT_SECRET as string, | ||
|
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)(auth\.ts|\.env\.example|package\.json|pnpm-lock\.yaml)$' || true
echo "== auth outline =="
if [ -f apps/api/src/lib/auth.ts ]; then
ast-grep outline apps/api/src/lib/auth.ts || true
wc -l apps/api/src/lib/auth.ts
sed -n '1,220p' apps/api/src/lib/auth.ts | cat -n
fi
echo "== env references =="
rg -n "GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|github|GITHUB|CLIENT_URL|SERVER_URL|better-auth" apps/api .env.example .env* -S 2>/dev/null || trueRepository: yashdev9274/supercli Length of output: 2899 🌐 Web query:
💡 Result: In Better Auth, managing OAuth redirect URIs involves correctly configuring your environment and authentication settings to bridge the gap between your server (where the auth logic runs) and your client (where the user interacts with your application) [1]. Key Configuration Concepts baseURL: This is the root URL of your authentication server [2]. You should always set this explicitly in your Better Auth configuration or via the environment variable BETTER_AUTH_URL [3]. Relying on automatic request inference is not recommended for production environments [3]. redirectURI: By default, Better Auth constructs the redirect URI using the baseURL and a standard callback path (e.g., /api/auth/callback/github) [4][5]. You can override this by providing a specific redirectURI in the provider configuration [2][3], but it must match exactly what you have registered in your GitHub OAuth App settings. Handling Client vs. Server URLs A common issue occurs when your server runs on one port (e.g., 8787) and your client on another (e.g., 3000) [1]. Better Auth might default to the server's URL, leading to redirect mismatches [1]. To resolve this: 1. Trusted Origins: Add your client's URL to the trustedOrigins array in your auth configuration to allow cross-origin communication [1]. export const auth = betterAuth({ baseURL: "http://your-server-url", trustedOrigins: ["http://localhost:3000"], }); 2. Callback URL Parameter: When initiating the sign-in process on the client, pass the callbackURL parameter to specify where the user should be sent after the OAuth flow completes [1]. await authClient.signIn.social({ provider: "github", callbackURL: "http://localhost:3000/dashboard", }); 3. OAuth Proxy Plugin: For complex deployments (e.g., preview environments or multi-host setups), use the OAuth Proxy plugin [6]. This allows you to register only your production callback URL with GitHub while the plugin handles routing requests from other environments through your production server [6]. Always ensure that the redirect URL registered in your GitHub Developer Settings matches the callback endpoint constructed by Better Auth [7][6]. If you are using the Generic OAuth plugin, remember that the redirectURI must include the:providerId placeholder [5][8]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== root env example =="
if [ -f .env.example ]; then
cat -n .env.example | sed -n '1,180p'
fi
echo "== apps/api env example =="
if [ -f apps/api/.env.example ]; then
cat -n apps/api/.env.example | sed -n '1,180p'
fi
echo "== runtime probe process.env assertion behavior =="
node - <<'JS'
const env = { GITHUB_CLIENT_ID: undefined, GITHUB_CLIENT_SECRET: undefined }
function register(input) {
return {
clientId: input.GITHUB_CLIENT_ID,
clientSecret: input.GITHUB_CLIENT_SECRET,
authEnvIsNull: input.GITHUB_CLIENT_ID === null,
authEnvIsUndefined: input.GITHUB_CLIENT_ID === undefined,
authEnvEmptyString: input.GITHUB_CLIENT_ID === "",
}
}
console.log(JSON.stringify(register(env), null, 2))
JS
echo "== better-auth package metadata/source availability =="
python3 - <<'PY'
import json
from pathlib import Path
root = Path("apps/api")
for p in ["package.json", "pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]:
path = root / p
print(f"{path}: exists={path.exists()}")
PYRepository: yashdev9274/supercli Length of output: 1958 Validate GitHub credentials before registering the provider.
🤖 Prompt for AI Agents |
||
| ...(isProduction ? { redirectURI: `${clientUrl}/api/auth/callback/github` } : {}), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== candidate files =="
git ls-files | rg '(^|/)apps/api/src/lib/auth\.ts$|auth|better-auth|callback|route|routes|middleware' || true
echo "== auth.ts =="
if [ -f apps/api/src/lib/auth.ts ]; then
cat -n apps/api/src/lib/auth.ts
fi
echo "== surrounding auth references =="
rg -n "CLIENT_URL|clientUrl|baseURL|basePath|redirectURI|redirectUrl|github|callbacks|better-auth|originPath|handler|auth" apps -S || trueRepository: yashdev9274/supercli Length of output: 50376 🌐 Web query:
💡 Result: In Better Auth, the Citations:
🌐 Web query:
💡 Result: In Better Auth, the callback URL (or redirect URI) must follow a specific pattern for the library to correctly identify and handle the OAuth provider. The standard pattern for the callback URL is: {baseURL}/api/auth/callback/github [1][2] Key Configuration Details: 1. Requirement for Provider Identification: Better Auth internally registers callback routes using the pattern /callback/:id [3]. If the callback URL does not include the provider name (e.g., /callback/github), Better Auth will be unable to identify which provider is handling the request, typically resulting in a 404 error [3]. 2. Setting the baseURL: It is recommended to configure the baseURL in your Better Auth instance [3][1]. When set correctly, Better Auth will automatically construct the appropriate callback URL for each provider, avoiding common redirect_uri_mismatch errors [1]. 3. GitHub App Configuration: You must register this exact URL in your GitHub Developer Portal settings [4][1]. - For local development: http://localhost:3000/api/auth/callback/github [4] - For production: Replace with your actual domain, such as https://example.com/api/auth/callback/github [4][1] 4. Custom redirectURI: While you can manually specify a redirectURI in the socialProviders configuration, it must still match the path expected by Better Auth (/callback/github) [3][5][6]. The redirectURI option is used by the provider for the redirect after authorization, but it does not change Better Auth's internal routing requirements [3]. Example Configuration: export const auth = betterAuth({ baseURL: process.env.BETTER_AUTH_URL, // e.g., "https://example.com/api/auth" [3][1] socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, }, }, }); In summary, ensure the URL registered in your GitHub app settings matches {baseURL}/api/auth/callback/github exactly [3][1]. Citations:
Use the API callback URL for GitHub OAuth.
🤖 Prompt for AI Agents |
||
| }, | ||
| }, | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| export interface ModelPricing { | ||
| input: number | ||
| output: number | ||
| cacheRead: number | ||
| cacheWrite: number | ||
| } | ||
|
|
||
| const MODEL_PRICING: Record<string, ModelPricing> = { | ||
| "gemini-3-flash-preview": { input: 1, output: 4, cacheRead: 0.25, cacheWrite: 1 }, | ||
| "gemini-3-pro-preview": { input: 1.25, output: 10, cacheRead: 0.315, cacheWrite: 1.25 }, | ||
| "minimax-m3": { input: 0.15, output: 0.85, cacheRead: 0.015, cacheWrite: 0.15 }, | ||
| "nvidia/minimax-m3": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, | ||
| } | ||
|
|
||
| export function lookupPricing(model: string): ModelPricing { | ||
| return ( | ||
| MODEL_PRICING[model] ?? { | ||
| input: 0, | ||
| output: 0, | ||
| cacheRead: 0, | ||
| cacheWrite: 0, | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| export function computeCost( | ||
| model: string, | ||
| inputTokens: number, | ||
| outputTokens: number, | ||
| cachedInputTokens: number | ||
| ): number { | ||
| const p = lookupPricing(model) | ||
| const regularInput = inputTokens - cachedInputTokens | ||
| const cost = | ||
| (regularInput * p.input + cachedInputTokens * p.cacheRead + outputTokens * p.output) / 1_000_000 | ||
| return Math.round(cost * 1_000_000) / 1_000_000 | ||
| } | ||
|
Comment on lines
+1
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n "from \"\.\./lib/pricing\"|from \"\./pricing\"|lib/pricing" --type=tsRepository: yashdev9274/supercli Length of output: 158 🏁 Script executed: #!/bin/bash
set -u
echo "Repository files matching pricing.ts/index.ts:"
git ls-files | rg '(^|/)pricing\.ts$|(^|/)index\.ts$' || true
echo
echo "Pricing module exports/import/usages:"
rg -n "lookupPricing|computeCost|ModelPricing|MODEL_PRICING|from \"\.\./lib/pricing\"|from \"\./pricing\"|lib/pricing" --type=ts .
echo
echo "apps/api/src/lib/pricing.ts:"
cat -n apps/api/src/lib/pricing.ts | sed -n '1,120p'
echo
echo "apps/api/src/providers/index.ts (provider pricing area):"
if [ -f apps/api/src/providers/index.ts ]; then
wc -l apps/api/src/providers/index.ts
sed -n '1,130p' apps/api/src/providers/index.ts | cat -n
fiRepository: yashdev9274/supercli Length of output: 15336 Fix the
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| import prisma from "@super/db-terminal" | ||
| export default prisma |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,47 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import prisma from "./prisma" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DAILY_TOKEN_LIMIT = 128_000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DAILY_QUERY_LIMIT = 20 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function checkDailyTokenBudget(userId: string): Promise<{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allowed: boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| used: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limit: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remaining: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queriesUsed: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queriesRemaining: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const today = new Date() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| today.setHours(0, 0, 0, 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tomorrow = new Date(today) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tomorrow.setDate(tomorrow.getDate() + 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const events = await prisma.usageEvent.findMany({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createdAt: { gte: today, lt: tomorrow }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| select: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputTokens: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputTokens: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cachedInputTokens: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n "checkDailyTokenBudget" --type=ts
rg -n "model UsageEvent" -A 15 --type-add 'prisma:*.prisma' --type prismaRepository: yashdev9274/supercli Length of output: 158 🏁 Script executed: #!/bin/bash
set -u
printf 'tracked files matching token-budget/checkDailyTokenBudget/UsageEvent:\n'
git ls-files | rg -i 'token-budget|checkDailyToken|usage|prisma|schema' || true
printf '\nAll occurrences of checkDailyTokenBudget / UsageEvent in repo:\n'
rg -n "checkDailyTokenBudget|UsageEvent|usageEvent" . || true
printf '\nCandidate file snippets:\n'
fd -a 'token-budget\.(ts|tsx|js|jsx)$|schema\.prisma$' . | while read -r f; do
printf '\n--- %s ---\n' "$f"
wc -l "$f"
sed -n '1,220p' "$f" | cat -n
doneRepository: yashdev9274/supercli Length of output: 29096 Scope this daily budget check to the passed
🐛 Proposed fix const events = await prisma.usageEvent.findMany({
where: {
+ userId,
createdAt: { gte: today, lt: tomorrow },
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const totalTokens = events.reduce( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (sum, e) => sum + e.inputTokens + e.outputTokens + (e.cachedInputTokens ?? 0), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const queriesUsed = events.length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const remaining = DAILY_TOKEN_LIMIT - totalTokens | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const queriesRemaining = DAILY_QUERY_LIMIT - queriesUsed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allowed: remaining > 0 && queriesRemaining > 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| used: totalTokens, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| limit: DAILY_TOKEN_LIMIT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remaining: Math.max(0, remaining), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queriesUsed, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queriesRemaining: Math.max(0, queriesRemaining), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,27 @@ | ||||||||||||||||||||||||||||||||||||||
| import prisma from "./prisma" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export async function recordUsage(opts: { | ||||||||||||||||||||||||||||||||||||||
| provider: string | ||||||||||||||||||||||||||||||||||||||
| model: string | ||||||||||||||||||||||||||||||||||||||
| inputTokens: number | ||||||||||||||||||||||||||||||||||||||
| outputTokens: number | ||||||||||||||||||||||||||||||||||||||
| cachedInputTokens?: number | ||||||||||||||||||||||||||||||||||||||
| costUsd: number | ||||||||||||||||||||||||||||||||||||||
| durationMs: number | ||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Add explicit return type.
-export async function recordUsage(opts: {
+export async function recordUsage(opts: {
provider: string
model: string
inputTokens: number
outputTokens: number
cachedInputTokens?: number
costUsd: number
durationMs: number
-}) {
+}): Promise<void> {📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| await prisma.usageEvent.create({ | ||||||||||||||||||||||||||||||||||||||
| data: { | ||||||||||||||||||||||||||||||||||||||
| provider: opts.provider, | ||||||||||||||||||||||||||||||||||||||
| model: opts.model, | ||||||||||||||||||||||||||||||||||||||
| inputTokens: opts.inputTokens, | ||||||||||||||||||||||||||||||||||||||
| outputTokens: opts.outputTokens, | ||||||||||||||||||||||||||||||||||||||
| cachedInputTokens: opts.cachedInputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||
| costUsd: opts.costUsd, | ||||||||||||||||||||||||||||||||||||||
| durationMs: opts.durationMs, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||
| console.error("[track-usage] Failed to record usage event:", e) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { createMiddleware } from "hono/factory" | ||
| import prisma from "../lib/prisma" | ||
| import type { AppEnv } from "../types" | ||
|
|
||
| export const authenticate = createMiddleware<AppEnv>(async (c, next) => { | ||
| const authHeader = c.req.header("Authorization") | ||
| if (!authHeader?.startsWith("Bearer ")) { | ||
| return c.json({ error: "Unauthorized" }, 401) | ||
| } | ||
|
|
||
| const token = authHeader.slice(7) | ||
| try { | ||
| const session = await prisma.session.findUnique({ | ||
| where: { token }, | ||
| include: { user: true }, | ||
| }) | ||
|
|
||
| if (!session || session.expiresAt < new Date()) { | ||
| return c.json({ error: "Unauthorized" }, 401) | ||
| } | ||
|
|
||
| c.set("user", session.user) | ||
| c.set("session", session) | ||
| await next() | ||
| } catch { | ||
| return c.json({ error: "Unauthorized" }, 401) | ||
|
Comment on lines
+12
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Do not convert downstream failures into The 🤖 Prompt for AI Agents |
||
| } | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Run the artifact produced by
build.buildemitsdist/index.js, butstartrunssrc/index.ts. This means production does not use the built artifact and deployments containing onlydistcannot start.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents