@thejeetsingh/kalcode 2.0.0 → 2.1.0
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/README.md +0 -4
- package/api/health.ts +3 -5
- package/api/v1/chat/completions.ts +2 -18
- package/package.json +1 -1
- package/src/api/client.ts +15 -20
- package/src/constants.ts +4 -1
- package/src/index.ts +3 -3
- package/src/proxy/server.ts +0 -18
- package/tsconfig.json +2 -1
- package/vercel.json +1 -0
package/README.md
CHANGED
|
@@ -62,7 +62,6 @@ Run the backend proxy with your NVIDIA key on a server you control:
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
export NVIDIA_NIM_KEY="your-real-provider-key"
|
|
65
|
-
export KALCODE_PROXY_TOKEN="choose-a-long-random-token"
|
|
66
65
|
export PORT=8787
|
|
67
66
|
bun run proxy
|
|
68
67
|
```
|
|
@@ -71,7 +70,6 @@ Client users run `kalcode` with proxy settings (no NVIDIA key needed on client):
|
|
|
71
70
|
|
|
72
71
|
```bash
|
|
73
72
|
export KALCODE_PROXY_URL="https://your-proxy-domain"
|
|
74
|
-
export KALCODE_PROXY_TOKEN="same-token-as-server"
|
|
75
73
|
kalcode
|
|
76
74
|
```
|
|
77
75
|
|
|
@@ -103,13 +101,11 @@ This repo now includes Vercel serverless endpoints:
|
|
|
103
101
|
### 2) Set Vercel environment variables
|
|
104
102
|
|
|
105
103
|
- `NVIDIA_NIM_KEY` = your provider key (server-only)
|
|
106
|
-
- `KALCODE_PROXY_TOKEN` = long random token for client auth
|
|
107
104
|
|
|
108
105
|
### 3) Configure clients
|
|
109
106
|
|
|
110
107
|
```bash
|
|
111
108
|
export KALCODE_PROXY_URL="https://<your-vercel-project>.vercel.app"
|
|
112
|
-
export KALCODE_PROXY_TOKEN="<same-token-as-vercel-env>"
|
|
113
109
|
kalcode
|
|
114
110
|
```
|
|
115
111
|
|
package/api/health.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const config = { runtime: "edge" };
|
|
2
2
|
|
|
3
|
-
export default
|
|
3
|
+
export default function handler(_req: Request): Response {
|
|
4
4
|
return new Response(JSON.stringify({ ok: true }), {
|
|
5
5
|
status: 200,
|
|
6
|
-
headers: {
|
|
7
|
-
"Content-Type": "application/json",
|
|
8
|
-
},
|
|
6
|
+
headers: { "Content-Type": "application/json" },
|
|
9
7
|
});
|
|
10
8
|
}
|
|
@@ -1,41 +1,25 @@
|
|
|
1
1
|
const API_URL = "https://integrate.api.nvidia.com/v1/chat/completions";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const config = { runtime: "edge" };
|
|
4
4
|
|
|
5
5
|
function json(status: number, payload: Record<string, unknown>): Response {
|
|
6
6
|
return new Response(JSON.stringify(payload), {
|
|
7
7
|
status,
|
|
8
|
-
headers: {
|
|
9
|
-
"Content-Type": "application/json",
|
|
10
|
-
},
|
|
8
|
+
headers: { "Content-Type": "application/json" },
|
|
11
9
|
});
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
function getBearerToken(req: Request): string {
|
|
15
|
-
const auth = req.headers.get("authorization") || "";
|
|
16
|
-
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
17
|
-
return m?.[1]?.trim() || "";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
12
|
export default async function handler(req: Request): Promise<Response> {
|
|
21
13
|
if (req.method !== "POST") {
|
|
22
14
|
return json(405, { error: { message: "Method not allowed." } });
|
|
23
15
|
}
|
|
24
16
|
|
|
25
17
|
const serverNimKey = process.env.NVIDIA_NIM_KEY || "";
|
|
26
|
-
const requiredProxyToken = process.env.KALCODE_PROXY_TOKEN || "";
|
|
27
18
|
|
|
28
19
|
if (!serverNimKey) {
|
|
29
20
|
return json(500, { error: { message: "Server missing NVIDIA_NIM_KEY." } });
|
|
30
21
|
}
|
|
31
22
|
|
|
32
|
-
if (requiredProxyToken) {
|
|
33
|
-
const incoming = getBearerToken(req);
|
|
34
|
-
if (!incoming || incoming !== requiredProxyToken) {
|
|
35
|
-
return json(401, { error: { message: "Unauthorized." } });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
23
|
const body = await req.text();
|
|
40
24
|
const upstream = await fetch(API_URL, {
|
|
41
25
|
method: "POST",
|
package/package.json
CHANGED
package/src/api/client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { API_URL, RATE_LIMIT_MAX, RATE_LIMIT_WINDOW, MAX_RETRIES, RETRY_BASE_DELAY } from "../constants.js";
|
|
1
|
+
import { API_URL, DEFAULT_PROXY_URL, RATE_LIMIT_MAX, RATE_LIMIT_WINDOW, MAX_RETRIES, RETRY_BASE_DELAY } from "../constants.js";
|
|
2
2
|
import type { Message, StreamDelta, ToolDefinition } from "../types.js";
|
|
3
3
|
import { parseSSEStream } from "./stream-parser.js";
|
|
4
4
|
|
|
@@ -28,27 +28,18 @@ export async function* streamChat(
|
|
|
28
28
|
tools?: ToolDefinition[],
|
|
29
29
|
onRetry?: (attempt: number, waitSec: number) => void
|
|
30
30
|
): AsyncGenerator<StreamDelta> {
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const explicitProxy = (process.env.KALCODE_PROXY_URL || "").trim();
|
|
32
|
+
// If no API key and no explicit proxy, auto-use the public Vercel proxy
|
|
33
|
+
const proxyUrl = explicitProxy || (!apiKey ? DEFAULT_PROXY_URL : "");
|
|
33
34
|
const targetUrl = proxyUrl
|
|
34
35
|
? `${proxyUrl.replace(/\/+$/, "")}/v1/chat/completions`
|
|
35
36
|
: API_URL;
|
|
36
|
-
const authToken =
|
|
37
|
+
const authToken = apiKey;
|
|
37
38
|
|
|
38
|
-
if (proxyUrl && !
|
|
39
|
+
if (!proxyUrl && !authToken) {
|
|
39
40
|
yield {
|
|
40
41
|
type: "error",
|
|
41
|
-
error: "
|
|
42
|
-
};
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!authToken) {
|
|
47
|
-
yield {
|
|
48
|
-
type: "error",
|
|
49
|
-
error: proxyUrl
|
|
50
|
-
? "Missing proxy auth token."
|
|
51
|
-
: "Missing NVIDIA API key.",
|
|
42
|
+
error: "Missing NVIDIA API key.",
|
|
52
43
|
};
|
|
53
44
|
return;
|
|
54
45
|
}
|
|
@@ -71,10 +62,14 @@ export async function* streamChat(
|
|
|
71
62
|
|
|
72
63
|
const response = await fetch(targetUrl, {
|
|
73
64
|
method: "POST",
|
|
74
|
-
headers:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
headers: proxyUrl
|
|
66
|
+
? {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
Authorization: `Bearer ${authToken}`,
|
|
72
|
+
},
|
|
78
73
|
body: JSON.stringify(body),
|
|
79
74
|
});
|
|
80
75
|
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
export const VERSION = "2.
|
|
1
|
+
export const VERSION = "2.1.0";
|
|
2
2
|
|
|
3
3
|
export const API_URL = "https://integrate.api.nvidia.com/v1/chat/completions";
|
|
4
4
|
|
|
5
|
+
/** Public proxy — used automatically when no API key is set */
|
|
6
|
+
export const DEFAULT_PROXY_URL = "https://kalcode.vercel.app";
|
|
7
|
+
|
|
5
8
|
export const DEFAULT_MODEL = "qwen/qwen3-coder-480b-a35b-instruct";
|
|
6
9
|
|
|
7
10
|
export const AVAILABLE_MODELS: { id: string; name: string; params: string }[] = [
|
package/src/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ const REPL_COMMANDS: { cmd: string; desc: string }[] = [
|
|
|
41
41
|
|
|
42
42
|
export async function main(): Promise<void> {
|
|
43
43
|
const { values, positionals } = parseArgs({
|
|
44
|
-
args:
|
|
44
|
+
args: process.argv.slice(2),
|
|
45
45
|
options: {
|
|
46
46
|
help: { type: "boolean", short: "h" },
|
|
47
47
|
version: { type: "boolean", short: "v" },
|
|
@@ -88,9 +88,9 @@ export async function main(): Promise<void> {
|
|
|
88
88
|
if (values["auto-accept"]) setPermissionLevel("auto");
|
|
89
89
|
if (values.ask) setAskMode(true);
|
|
90
90
|
const proxyUrl = (process.env.KALCODE_PROXY_URL || "").trim();
|
|
91
|
-
const proxyMode = proxyUrl.length > 0;
|
|
91
|
+
const proxyMode = proxyUrl.length > 0 || !config.apiKey; // auto-proxy when no key
|
|
92
92
|
|
|
93
|
-
// API key check
|
|
93
|
+
// API key check — skip when proxy will be used (explicit or auto-fallback)
|
|
94
94
|
if (!proxyMode && !config.apiKey) {
|
|
95
95
|
console.log("");
|
|
96
96
|
console.log(chalk.yellow(" No API key found."));
|
package/src/proxy/server.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { API_URL } from "../constants.js";
|
|
|
2
2
|
|
|
3
3
|
const PORT = parseInt(process.env.PORT || "8787", 10);
|
|
4
4
|
const HOST = process.env.HOST || "0.0.0.0";
|
|
5
|
-
const PROXY_TOKEN = process.env.KALCODE_PROXY_TOKEN || "";
|
|
6
5
|
const NVIDIA_NIM_KEY = process.env.NVIDIA_NIM_KEY || "";
|
|
7
6
|
const RATE_LIMIT_PER_MIN = Math.max(
|
|
8
7
|
1,
|
|
@@ -42,12 +41,6 @@ function isRateLimited(clientId: string): boolean {
|
|
|
42
41
|
return false;
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
function getBearerToken(req: Request): string {
|
|
46
|
-
const auth = req.headers.get("authorization") || "";
|
|
47
|
-
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
48
|
-
return m?.[1]?.trim() || "";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
44
|
async function proxyCompletions(req: Request): Promise<Response> {
|
|
52
45
|
if (!NVIDIA_NIM_KEY) {
|
|
53
46
|
return json(500, {
|
|
@@ -57,17 +50,6 @@ async function proxyCompletions(req: Request): Promise<Response> {
|
|
|
57
50
|
});
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
if (PROXY_TOKEN) {
|
|
61
|
-
const incomingToken = getBearerToken(req);
|
|
62
|
-
if (!incomingToken || incomingToken !== PROXY_TOKEN) {
|
|
63
|
-
return json(401, {
|
|
64
|
-
error: {
|
|
65
|
-
message: "Unauthorized. Invalid proxy token.",
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
53
|
const clientId = getClientId(req);
|
|
72
54
|
if (isRateLimited(clientId)) {
|
|
73
55
|
return json(429, {
|
package/tsconfig.json
CHANGED