@pentoshi/clai 0.2.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.
Files changed (102) hide show
  1. package/README.md +287 -0
  2. package/bin/clai.mjs +2 -0
  3. package/dist/agent/runner.d.ts +12 -0
  4. package/dist/agent/runner.js +249 -0
  5. package/dist/agent/runner.js.map +1 -0
  6. package/dist/commands/doctor.d.ts +1 -0
  7. package/dist/commands/doctor.js +29 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/providers.d.ts +13 -0
  10. package/dist/commands/providers.js +137 -0
  11. package/dist/commands/providers.js.map +1 -0
  12. package/dist/commands/update.d.ts +5 -0
  13. package/dist/commands/update.js +123 -0
  14. package/dist/commands/update.js.map +1 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +172 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/llm/anthropic.d.ts +2 -0
  19. package/dist/llm/anthropic.js +127 -0
  20. package/dist/llm/anthropic.js.map +1 -0
  21. package/dist/llm/gemini.d.ts +2 -0
  22. package/dist/llm/gemini.js +109 -0
  23. package/dist/llm/gemini.js.map +1 -0
  24. package/dist/llm/groq.d.ts +2 -0
  25. package/dist/llm/groq.js +49 -0
  26. package/dist/llm/groq.js.map +1 -0
  27. package/dist/llm/http.d.ts +35 -0
  28. package/dist/llm/http.js +112 -0
  29. package/dist/llm/http.js.map +1 -0
  30. package/dist/llm/ollama.d.ts +2 -0
  31. package/dist/llm/ollama.js +95 -0
  32. package/dist/llm/ollama.js.map +1 -0
  33. package/dist/llm/openai.d.ts +2 -0
  34. package/dist/llm/openai.js +49 -0
  35. package/dist/llm/openai.js.map +1 -0
  36. package/dist/llm/openrouter.d.ts +2 -0
  37. package/dist/llm/openrouter.js +55 -0
  38. package/dist/llm/openrouter.js.map +1 -0
  39. package/dist/llm/provider.d.ts +23 -0
  40. package/dist/llm/provider.js +58 -0
  41. package/dist/llm/provider.js.map +1 -0
  42. package/dist/llm/router.d.ts +8 -0
  43. package/dist/llm/router.js +103 -0
  44. package/dist/llm/router.js.map +1 -0
  45. package/dist/modes/agent.d.ts +17 -0
  46. package/dist/modes/agent.js +6 -0
  47. package/dist/modes/agent.js.map +1 -0
  48. package/dist/modes/ask.d.ts +8 -0
  49. package/dist/modes/ask.js +46 -0
  50. package/dist/modes/ask.js.map +1 -0
  51. package/dist/os/detect.d.ts +10 -0
  52. package/dist/os/detect.js +17 -0
  53. package/dist/os/detect.js.map +1 -0
  54. package/dist/os/pkgmgr.d.ts +6 -0
  55. package/dist/os/pkgmgr.js +32 -0
  56. package/dist/os/pkgmgr.js.map +1 -0
  57. package/dist/prompts/index.d.ts +2 -0
  58. package/dist/prompts/index.js +60 -0
  59. package/dist/prompts/index.js.map +1 -0
  60. package/dist/repl.d.ts +7 -0
  61. package/dist/repl.js +216 -0
  62. package/dist/repl.js.map +1 -0
  63. package/dist/safety/classifier.d.ts +8 -0
  64. package/dist/safety/classifier.js +118 -0
  65. package/dist/safety/classifier.js.map +1 -0
  66. package/dist/safety/patterns.d.ts +5 -0
  67. package/dist/safety/patterns.js +45 -0
  68. package/dist/safety/patterns.js.map +1 -0
  69. package/dist/store/config.d.ts +20 -0
  70. package/dist/store/config.js +46 -0
  71. package/dist/store/config.js.map +1 -0
  72. package/dist/store/history.d.ts +24 -0
  73. package/dist/store/history.js +145 -0
  74. package/dist/store/history.js.map +1 -0
  75. package/dist/store/keys.d.ts +10 -0
  76. package/dist/store/keys.js +115 -0
  77. package/dist/store/keys.js.map +1 -0
  78. package/dist/store/logs.d.ts +2 -0
  79. package/dist/store/logs.js +31 -0
  80. package/dist/store/logs.js.map +1 -0
  81. package/dist/store/project.d.ts +2 -0
  82. package/dist/store/project.js +14 -0
  83. package/dist/store/project.js.map +1 -0
  84. package/dist/tools/fs.d.ts +5 -0
  85. package/dist/tools/fs.js +82 -0
  86. package/dist/tools/fs.js.map +1 -0
  87. package/dist/tools/http.d.ts +6 -0
  88. package/dist/tools/http.js +14 -0
  89. package/dist/tools/http.js.map +1 -0
  90. package/dist/tools/registry.d.ts +5 -0
  91. package/dist/tools/registry.js +79 -0
  92. package/dist/tools/registry.js.map +1 -0
  93. package/dist/tools/shell.d.ts +7 -0
  94. package/dist/tools/shell.js +16 -0
  95. package/dist/tools/shell.js.map +1 -0
  96. package/dist/types.d.ts +40 -0
  97. package/dist/types.js +9 -0
  98. package/dist/types.js.map +1 -0
  99. package/dist/ui/banner.d.ts +12 -0
  100. package/dist/ui/banner.js +55 -0
  101. package/dist/ui/banner.js.map +1 -0
  102. package/package.json +66 -0
@@ -0,0 +1,109 @@
1
+ import { defaultModels, } from "./provider.js";
2
+ import { readJson } from "./http.js";
3
+ function geminiContents(messages) {
4
+ return messages
5
+ .filter((message) => message.role !== "system")
6
+ .map((message) => ({
7
+ role: message.role === "assistant" ? "model" : "user",
8
+ parts: [{ text: message.content }],
9
+ }));
10
+ }
11
+ function systemInstruction(messages) {
12
+ const system = messages.find((message) => message.role === "system");
13
+ return system ? { parts: [{ text: system.content }] } : undefined;
14
+ }
15
+ function geminiBody(request) {
16
+ return JSON.stringify({
17
+ systemInstruction: systemInstruction(request.messages),
18
+ contents: geminiContents(request.messages),
19
+ generationConfig: {
20
+ temperature: request.temperature ?? 0.2,
21
+ maxOutputTokens: request.maxTokens ?? 1_024,
22
+ },
23
+ });
24
+ }
25
+ export const geminiProvider = {
26
+ id: "gemini",
27
+ displayName: "Google Gemini",
28
+ defaultModel: defaultModels.gemini,
29
+ envVar: "GEMINI_API_KEY",
30
+ validateKey: (key) => /^AIza[0-9A-Za-z_-]{12,}$/.test(key),
31
+ async ping(auth) {
32
+ if (!auth.apiKey)
33
+ throw new Error("Gemini API key is required");
34
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(auth.apiKey)}`);
35
+ await readJson(response);
36
+ },
37
+ async complete(request, auth) {
38
+ if (!auth.apiKey)
39
+ throw new Error("Gemini API key is required");
40
+ const model = request.model ?? defaultModels.gemini;
41
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(auth.apiKey)}`, {
42
+ method: "POST",
43
+ signal: request.signal ?? null,
44
+ headers: { "content-type": "application/json" },
45
+ body: geminiBody(request),
46
+ });
47
+ const data = await readJson(response);
48
+ const text = data.candidates?.[0]?.content?.parts
49
+ ?.map((part) => part.text ?? "")
50
+ .join("")
51
+ .trim();
52
+ if (!text) {
53
+ throw new Error("Gemini returned no completion text");
54
+ }
55
+ return { text, provider: "gemini", model };
56
+ },
57
+ async stream(request, auth, onToken) {
58
+ if (!auth.apiKey)
59
+ throw new Error("Gemini API key is required");
60
+ const model = request.model ?? defaultModels.gemini;
61
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(auth.apiKey)}`, {
62
+ method: "POST",
63
+ signal: request.signal ?? null,
64
+ headers: { "content-type": "application/json" },
65
+ body: geminiBody(request),
66
+ });
67
+ if (!response.ok) {
68
+ await readJson(response);
69
+ }
70
+ if (!response.body) {
71
+ throw new Error("Gemini returned no stream body");
72
+ }
73
+ const decoder = new TextDecoder();
74
+ const reader = response.body.getReader();
75
+ let buffer = "";
76
+ let full = "";
77
+ while (true) {
78
+ const { done, value } = await reader.read();
79
+ if (done)
80
+ break;
81
+ buffer += decoder.decode(value, { stream: true });
82
+ const lines = buffer.split("\n");
83
+ buffer = lines.pop() ?? "";
84
+ for (const line of lines) {
85
+ const trimmed = line.trim();
86
+ if (!trimmed.startsWith("data:"))
87
+ continue;
88
+ const payload = trimmed.slice(5).trim();
89
+ if (payload === "[DONE]")
90
+ return { text: full, provider: "gemini", model };
91
+ try {
92
+ const parsed = JSON.parse(payload);
93
+ const token = parsed.candidates?.[0]?.content?.parts
94
+ ?.map((p) => p.text ?? "")
95
+ .join("");
96
+ if (token) {
97
+ full += token;
98
+ onToken(token);
99
+ }
100
+ }
101
+ catch {
102
+ // Ignore malformed keepalive lines.
103
+ }
104
+ }
105
+ }
106
+ return { text: full, provider: "gemini", model };
107
+ },
108
+ };
109
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/llm/gemini.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type LlmProvider } from "./provider.js";
2
+ export declare const groqProvider: LlmProvider;
@@ -0,0 +1,49 @@
1
+ import { defaultModels, } from "./provider.js";
2
+ import { openAiCompatibleComplete, openAiCompatiblePing, openAiCompatibleStream, } from "./http.js";
3
+ const baseUrl = "https://api.groq.com/openai/v1";
4
+ export const groqProvider = {
5
+ id: "groq",
6
+ displayName: "Groq",
7
+ defaultModel: defaultModels.groq,
8
+ envVar: "GROQ_API_KEY",
9
+ validateKey: (key) => /^gsk_[A-Za-z0-9_-]{8,}$/.test(key),
10
+ async ping(auth) {
11
+ if (!auth.apiKey)
12
+ throw new Error("Groq API key is required");
13
+ await openAiCompatiblePing(baseUrl, auth.apiKey);
14
+ },
15
+ async complete(request, auth) {
16
+ if (!auth.apiKey)
17
+ throw new Error("Groq API key is required");
18
+ const model = request.model ?? defaultModels.groq;
19
+ const text = await openAiCompatibleComplete({
20
+ provider: "Groq",
21
+ baseUrl,
22
+ apiKey: auth.apiKey,
23
+ model,
24
+ messages: request.messages,
25
+ maxTokens: request.maxTokens,
26
+ temperature: request.temperature,
27
+ signal: request.signal,
28
+ });
29
+ return { text, provider: "groq", model };
30
+ },
31
+ async stream(request, auth, onToken) {
32
+ if (!auth.apiKey)
33
+ throw new Error("Groq API key is required");
34
+ const model = request.model ?? defaultModels.groq;
35
+ const text = await openAiCompatibleStream({
36
+ provider: "Groq",
37
+ baseUrl,
38
+ apiKey: auth.apiKey,
39
+ model,
40
+ messages: request.messages,
41
+ maxTokens: request.maxTokens,
42
+ temperature: request.temperature,
43
+ signal: request.signal,
44
+ onToken,
45
+ });
46
+ return { text, provider: "groq", model };
47
+ },
48
+ };
49
+ //# sourceMappingURL=groq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/llm/groq.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,GAGd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,MAAM,OAAO,GAAG,gCAAgC,CAAC;AAEjD,MAAM,CAAC,MAAM,YAAY,GAAgB;IACvC,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,aAAa,CAAC,IAAI;IAChC,MAAM,EAAE,cAAc;IACtB,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,IAAkB;QAC3B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9D,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,CAAC,QAAQ,CACZ,OAA0B,EAC1B,IAAkB;QAElB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,IAAI,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC;YAC1C,QAAQ,EAAE,MAAM;YAChB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IACD,KAAK,CAAC,MAAM,CACV,OAA0B,EAC1B,IAAkB,EAClB,OAAgC;QAEhC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,IAAI,CAAC;QAClD,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;YACxC,QAAQ,EAAE,MAAM;YAChB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO;SACR,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;CACF,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { ChatMessage } from "../types.js";
2
+ export declare class ProviderError extends Error {
3
+ readonly status?: number | undefined;
4
+ readonly body?: string | undefined;
5
+ constructor(message: string, status?: number | undefined, body?: string | undefined);
6
+ }
7
+ export declare function readJson<T>(response: Response): Promise<T>;
8
+ export declare function toOpenAiMessages(messages: ChatMessage[]): Array<{
9
+ role: string;
10
+ content: string;
11
+ }>;
12
+ export declare function openAiCompatibleComplete(options: {
13
+ provider: string;
14
+ baseUrl: string;
15
+ apiKey: string;
16
+ model: string;
17
+ messages: ChatMessage[];
18
+ maxTokens?: number | undefined;
19
+ temperature?: number | undefined;
20
+ headers?: Record<string, string> | undefined;
21
+ signal?: AbortSignal | undefined;
22
+ }): Promise<string>;
23
+ export declare function openAiCompatibleStream(options: {
24
+ provider: string;
25
+ baseUrl: string;
26
+ apiKey: string;
27
+ model: string;
28
+ messages: ChatMessage[];
29
+ maxTokens?: number | undefined;
30
+ temperature?: number | undefined;
31
+ headers?: Record<string, string> | undefined;
32
+ signal?: AbortSignal | undefined;
33
+ onToken: (token: string) => void;
34
+ }): Promise<string>;
35
+ export declare function openAiCompatiblePing(baseUrl: string, apiKey: string, headers?: Record<string, string> | undefined): Promise<void>;
@@ -0,0 +1,112 @@
1
+ export class ProviderError extends Error {
2
+ status;
3
+ body;
4
+ constructor(message, status, body) {
5
+ super(message);
6
+ this.status = status;
7
+ this.body = body;
8
+ this.name = "ProviderError";
9
+ }
10
+ }
11
+ export async function readJson(response) {
12
+ const text = await response.text();
13
+ if (!response.ok) {
14
+ throw new ProviderError(`Provider request failed with HTTP ${response.status}`, response.status, text.slice(0, 1_000));
15
+ }
16
+ return JSON.parse(text);
17
+ }
18
+ export function toOpenAiMessages(messages) {
19
+ return messages.map((message) => ({
20
+ role: message.role === "tool" ? "user" : message.role,
21
+ content: message.content,
22
+ }));
23
+ }
24
+ export async function openAiCompatibleComplete(options) {
25
+ const response = await fetch(`${options.baseUrl}/chat/completions`, {
26
+ method: "POST",
27
+ signal: options.signal ?? null,
28
+ headers: {
29
+ "content-type": "application/json",
30
+ authorization: `Bearer ${options.apiKey}`,
31
+ ...options.headers,
32
+ },
33
+ body: JSON.stringify({
34
+ model: options.model,
35
+ messages: toOpenAiMessages(options.messages),
36
+ max_tokens: options.maxTokens ?? 1_024,
37
+ temperature: options.temperature ?? 0.2,
38
+ stream: false,
39
+ }),
40
+ });
41
+ const data = await readJson(response);
42
+ const text = data.choices?.[0]?.message?.content;
43
+ if (!text) {
44
+ throw new ProviderError(`${options.provider} returned no completion text`);
45
+ }
46
+ return text;
47
+ }
48
+ export async function openAiCompatibleStream(options) {
49
+ const response = await fetch(`${options.baseUrl}/chat/completions`, {
50
+ method: "POST",
51
+ signal: options.signal ?? null,
52
+ headers: {
53
+ "content-type": "application/json",
54
+ authorization: `Bearer ${options.apiKey}`,
55
+ ...options.headers,
56
+ },
57
+ body: JSON.stringify({
58
+ model: options.model,
59
+ messages: toOpenAiMessages(options.messages),
60
+ max_tokens: options.maxTokens ?? 1_024,
61
+ temperature: options.temperature ?? 0.2,
62
+ stream: true,
63
+ }),
64
+ });
65
+ if (!response.ok) {
66
+ await readJson(response);
67
+ }
68
+ if (!response.body)
69
+ throw new ProviderError(`${options.provider} returned no stream body`);
70
+ const decoder = new TextDecoder();
71
+ const reader = response.body.getReader();
72
+ let buffer = "";
73
+ let full = "";
74
+ while (true) {
75
+ const { done, value } = await reader.read();
76
+ if (done)
77
+ break;
78
+ buffer += decoder.decode(value, { stream: true });
79
+ const lines = buffer.split("\n");
80
+ buffer = lines.pop() ?? "";
81
+ for (const line of lines) {
82
+ const trimmed = line.trim();
83
+ if (!trimmed.startsWith("data:"))
84
+ continue;
85
+ const payload = trimmed.slice(5).trim();
86
+ if (payload === "[DONE]")
87
+ return full;
88
+ try {
89
+ const parsed = JSON.parse(payload);
90
+ const token = parsed.choices?.[0]?.delta?.content;
91
+ if (token) {
92
+ full += token;
93
+ options.onToken(token);
94
+ }
95
+ }
96
+ catch {
97
+ // Ignore malformed keepalive lines.
98
+ }
99
+ }
100
+ }
101
+ return full;
102
+ }
103
+ export async function openAiCompatiblePing(baseUrl, apiKey, headers) {
104
+ const response = await fetch(`${baseUrl}/models`, {
105
+ headers: {
106
+ authorization: `Bearer ${apiKey}`,
107
+ ...headers,
108
+ },
109
+ });
110
+ await readJson(response);
111
+ }
112
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/llm/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type LlmProvider } from "./provider.js";
2
+ export declare const ollamaProvider: LlmProvider;
@@ -0,0 +1,95 @@
1
+ import { defaultModels, } from "./provider.js";
2
+ import { readJson } from "./http.js";
3
+ function base(auth) {
4
+ return (auth.baseUrl ?? auth.apiKey ?? "http://localhost:11434").replace(/\/$/, "");
5
+ }
6
+ export const ollamaProvider = {
7
+ id: "ollama",
8
+ displayName: "Ollama",
9
+ defaultModel: defaultModels.ollama,
10
+ envVar: "OLLAMA_HOST",
11
+ validateKey: (key) => /^https?:\/\/.+/.test(key),
12
+ async ping(auth) {
13
+ const response = await fetch(`${base(auth)}/api/tags`);
14
+ await readJson(response);
15
+ },
16
+ async complete(request, auth) {
17
+ const model = request.model ?? defaultModels.ollama;
18
+ const response = await fetch(`${base(auth)}/api/chat`, {
19
+ method: "POST",
20
+ signal: request.signal ?? null,
21
+ headers: { "content-type": "application/json" },
22
+ body: JSON.stringify({
23
+ model,
24
+ messages: request.messages.map((message) => ({
25
+ role: message.role === "tool" ? "user" : message.role,
26
+ content: message.content,
27
+ })),
28
+ stream: false,
29
+ options: { temperature: request.temperature ?? 0.2 },
30
+ }),
31
+ });
32
+ const data = await readJson(response);
33
+ const text = data.message?.content?.trim();
34
+ if (!text) {
35
+ throw new Error("Ollama returned no completion text");
36
+ }
37
+ return { text, provider: "ollama", model };
38
+ },
39
+ async stream(request, auth, onToken) {
40
+ const model = request.model ?? defaultModels.ollama;
41
+ const response = await fetch(`${base(auth)}/api/chat`, {
42
+ method: "POST",
43
+ signal: request.signal ?? null,
44
+ headers: { "content-type": "application/json" },
45
+ body: JSON.stringify({
46
+ model,
47
+ messages: request.messages.map((message) => ({
48
+ role: message.role === "tool" ? "user" : message.role,
49
+ content: message.content,
50
+ })),
51
+ stream: true,
52
+ options: { temperature: request.temperature ?? 0.2 },
53
+ }),
54
+ });
55
+ if (!response.ok) {
56
+ await readJson(response);
57
+ }
58
+ if (!response.body) {
59
+ throw new Error("Ollama returned no stream body");
60
+ }
61
+ const decoder = new TextDecoder();
62
+ const reader = response.body.getReader();
63
+ let buffer = "";
64
+ let full = "";
65
+ while (true) {
66
+ const { done, value } = await reader.read();
67
+ if (done)
68
+ break;
69
+ buffer += decoder.decode(value, { stream: true });
70
+ const lines = buffer.split("\n");
71
+ buffer = lines.pop() ?? "";
72
+ for (const line of lines) {
73
+ const trimmed = line.trim();
74
+ if (!trimmed)
75
+ continue;
76
+ try {
77
+ const parsed = JSON.parse(trimmed);
78
+ const token = parsed.message?.content;
79
+ if (token) {
80
+ full += token;
81
+ onToken(token);
82
+ }
83
+ if (parsed.done) {
84
+ return { text: full, provider: "ollama", model };
85
+ }
86
+ }
87
+ catch {
88
+ // Ignore malformed lines.
89
+ }
90
+ }
91
+ }
92
+ return { text: full, provider: "ollama", model };
93
+ },
94
+ };
95
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/llm/ollama.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type LlmProvider } from "./provider.js";
2
+ export declare const openaiProvider: LlmProvider;
@@ -0,0 +1,49 @@
1
+ import { defaultModels, } from "./provider.js";
2
+ import { openAiCompatibleComplete, openAiCompatiblePing, openAiCompatibleStream, } from "./http.js";
3
+ const baseUrl = "https://api.openai.com/v1";
4
+ export const openaiProvider = {
5
+ id: "openai",
6
+ displayName: "OpenAI",
7
+ defaultModel: defaultModels.openai,
8
+ envVar: "OPENAI_API_KEY",
9
+ validateKey: (key) => /^sk-[A-Za-z0-9_-]{12,}$/.test(key),
10
+ async ping(auth) {
11
+ if (!auth.apiKey)
12
+ throw new Error("OpenAI API key is required");
13
+ await openAiCompatiblePing(baseUrl, auth.apiKey);
14
+ },
15
+ async complete(request, auth) {
16
+ if (!auth.apiKey)
17
+ throw new Error("OpenAI API key is required");
18
+ const model = request.model ?? defaultModels.openai;
19
+ const text = await openAiCompatibleComplete({
20
+ provider: "OpenAI",
21
+ baseUrl,
22
+ apiKey: auth.apiKey,
23
+ model,
24
+ messages: request.messages,
25
+ maxTokens: request.maxTokens,
26
+ temperature: request.temperature,
27
+ signal: request.signal,
28
+ });
29
+ return { text, provider: "openai", model };
30
+ },
31
+ async stream(request, auth, onToken) {
32
+ if (!auth.apiKey)
33
+ throw new Error("OpenAI API key is required");
34
+ const model = request.model ?? defaultModels.openai;
35
+ const text = await openAiCompatibleStream({
36
+ provider: "OpenAI",
37
+ baseUrl,
38
+ apiKey: auth.apiKey,
39
+ model,
40
+ messages: request.messages,
41
+ maxTokens: request.maxTokens,
42
+ temperature: request.temperature,
43
+ signal: request.signal,
44
+ onToken,
45
+ });
46
+ return { text, provider: "openai", model };
47
+ },
48
+ };
49
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/llm/openai.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,GAGd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAE5C,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,EAAE,EAAE,QAAQ;IACZ,WAAW,EAAE,QAAQ;IACrB,YAAY,EAAE,aAAa,CAAC,MAAM;IAClC,MAAM,EAAE,gBAAgB;IACxB,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC;IACjE,KAAK,CAAC,IAAI,CAAC,IAAkB;QAC3B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,CAAC,QAAQ,CACZ,OAA0B,EAC1B,IAAkB;QAElB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC;YAC1C,QAAQ,EAAE,QAAQ;YAClB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7C,CAAC;IACD,KAAK,CAAC,MAAM,CACV,OAA0B,EAC1B,IAAkB,EAClB,OAAgC;QAEhC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,MAAM,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;YACxC,QAAQ,EAAE,QAAQ;YAClB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO;SACR,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7C,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type LlmProvider } from "./provider.js";
2
+ export declare const openrouterProvider: LlmProvider;
@@ -0,0 +1,55 @@
1
+ import { defaultModels, } from "./provider.js";
2
+ import { openAiCompatibleComplete, openAiCompatiblePing, openAiCompatibleStream, } from "./http.js";
3
+ const baseUrl = "https://openrouter.ai/api/v1";
4
+ const headers = {
5
+ "HTTP-Referer": "https://github.com/clai/clai",
6
+ "X-Title": "clai",
7
+ };
8
+ export const openrouterProvider = {
9
+ id: "openrouter",
10
+ displayName: "OpenRouter",
11
+ defaultModel: defaultModels.openrouter,
12
+ envVar: "OPENROUTER_API_KEY",
13
+ validateKey: (key) => /^(sk-or-|or-)[A-Za-z0-9_-]{12,}$/.test(key),
14
+ async ping(auth) {
15
+ if (!auth.apiKey)
16
+ throw new Error("OpenRouter API key is required");
17
+ await openAiCompatiblePing(baseUrl, auth.apiKey, headers);
18
+ },
19
+ async complete(request, auth) {
20
+ if (!auth.apiKey)
21
+ throw new Error("OpenRouter API key is required");
22
+ const model = request.model ?? defaultModels.openrouter;
23
+ const text = await openAiCompatibleComplete({
24
+ provider: "OpenRouter",
25
+ baseUrl,
26
+ apiKey: auth.apiKey,
27
+ model,
28
+ messages: request.messages,
29
+ maxTokens: request.maxTokens,
30
+ temperature: request.temperature,
31
+ headers,
32
+ signal: request.signal,
33
+ });
34
+ return { text, provider: "openrouter", model };
35
+ },
36
+ async stream(request, auth, onToken) {
37
+ if (!auth.apiKey)
38
+ throw new Error("OpenRouter API key is required");
39
+ const model = request.model ?? defaultModels.openrouter;
40
+ const text = await openAiCompatibleStream({
41
+ provider: "OpenRouter",
42
+ baseUrl,
43
+ apiKey: auth.apiKey,
44
+ model,
45
+ messages: request.messages,
46
+ maxTokens: request.maxTokens,
47
+ temperature: request.temperature,
48
+ headers,
49
+ signal: request.signal,
50
+ onToken,
51
+ });
52
+ return { text, provider: "openrouter", model };
53
+ },
54
+ };
55
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/llm/openrouter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,GAGd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,MAAM,OAAO,GAAG,8BAA8B,CAAC;AAC/C,MAAM,OAAO,GAAG;IACd,cAAc,EAAE,8BAA8B;IAC9C,SAAS,EAAE,MAAM;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAgB;IAC7C,EAAE,EAAE,YAAY;IAChB,WAAW,EAAE,YAAY;IACzB,YAAY,EAAE,aAAa,CAAC,UAAU;IACtC,MAAM,EAAE,oBAAoB;IAC5B,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,kCAAkC,CAAC,IAAI,CAAC,GAAG,CAAC;IAC1E,KAAK,CAAC,IAAI,CAAC,IAAkB;QAC3B,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpE,MAAM,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,QAAQ,CACZ,OAA0B,EAC1B,IAAkB;QAElB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,UAAU,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC;YAC1C,QAAQ,EAAE,YAAY;YACtB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,OAAO;YACP,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;IACD,KAAK,CAAC,MAAM,CACV,OAA0B,EAC1B,IAAkB,EAClB,OAAgC;QAEhC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC,UAAU,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;YACxC,QAAQ,EAAE,YAAY;YACtB,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,OAAO;YACP,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO;SACR,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;CACF,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { CompletionRequest, CompletionResult, ProviderId } from "../types.js";
2
+ export interface LlmProvider {
3
+ id: ProviderId;
4
+ displayName: string;
5
+ defaultModel: string;
6
+ envVar?: string | undefined;
7
+ validateKey(key: string): boolean;
8
+ ping(options: ProviderAuth): Promise<void>;
9
+ complete(request: CompletionRequest, auth: ProviderAuth): Promise<CompletionResult>;
10
+ stream?(request: CompletionRequest, auth: ProviderAuth, onToken: (token: string) => void): Promise<CompletionResult>;
11
+ }
12
+ export interface ProviderAuth {
13
+ apiKey?: string | undefined;
14
+ baseUrl?: string | undefined;
15
+ }
16
+ export declare const providerAliases: Record<string, ProviderId>;
17
+ export declare const defaultModels: Record<ProviderId, string>;
18
+ export declare const envVars: Record<ProviderId, string | undefined>;
19
+ export declare function normalizeProvider(value: string): ProviderId | undefined;
20
+ export declare function assertProvider(value: string): ProviderId;
21
+ export declare function getDefaultModel(provider: ProviderId): string;
22
+ export declare function maskSecret(secret: string): string;
23
+ export declare function redactSecrets(value: string): string;
@@ -0,0 +1,58 @@
1
+ import { providerIds } from "../types.js";
2
+ export const providerAliases = {
3
+ groq: "groq",
4
+ gemini: "gemini",
5
+ google: "gemini",
6
+ openrouter: "openrouter",
7
+ openai: "openai",
8
+ anthropic: "anthropic",
9
+ claude: "anthropic",
10
+ ollama: "ollama",
11
+ local: "ollama",
12
+ };
13
+ export const defaultModels = {
14
+ groq: "llama-3.3-70b-versatile",
15
+ gemini: "gemini-2.0-flash",
16
+ openrouter: "meta-llama/llama-3.3-70b-instruct:free",
17
+ openai: "gpt-4o-mini",
18
+ anthropic: "claude-3-5-haiku-latest",
19
+ ollama: "llama3.1:8b",
20
+ };
21
+ export const envVars = {
22
+ groq: "GROQ_API_KEY",
23
+ gemini: "GEMINI_API_KEY",
24
+ openrouter: "OPENROUTER_API_KEY",
25
+ openai: "OPENAI_API_KEY",
26
+ anthropic: "ANTHROPIC_API_KEY",
27
+ ollama: "OLLAMA_HOST",
28
+ };
29
+ export function normalizeProvider(value) {
30
+ return providerAliases[value.trim().toLowerCase()];
31
+ }
32
+ export function assertProvider(value) {
33
+ const provider = normalizeProvider(value);
34
+ if (!provider) {
35
+ throw new Error(`Unsupported provider "${value}". Supported providers: ${providerIds.join(", ")}`);
36
+ }
37
+ return provider;
38
+ }
39
+ export function getDefaultModel(provider) {
40
+ return defaultModels[provider];
41
+ }
42
+ export function maskSecret(secret) {
43
+ if (secret.length <= 4) {
44
+ return "••••";
45
+ }
46
+ const knownPrefix = ["gsk_", "AIza", "sk-or-", "sk-ant-", "sk-"].find((prefix) => secret.startsWith(prefix)) ?? "";
47
+ const suffix = secret.slice(-4);
48
+ return `${knownPrefix}••••••${suffix}`;
49
+ }
50
+ export function redactSecrets(value) {
51
+ return value
52
+ .replace(/gsk_[A-Za-z0-9_-]+/g, "gsk_••••••")
53
+ .replace(/AIza[0-9A-Za-z_-]+/g, "AIza••••••")
54
+ .replace(/sk-[A-Za-z0-9_-]+/g, "sk-••••••")
55
+ .replace(/sk-or-[A-Za-z0-9_-]+/g, "sk-or-••••••")
56
+ .replace(/sk-ant-[A-Za-z0-9_-]+/g, "sk-ant-••••••");
57
+ }
58
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/llm/provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB1C,MAAM,CAAC,MAAM,eAAe,GAA+B;IACzD,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,QAAQ;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAA+B;IACvD,IAAI,EAAE,yBAAyB;IAC/B,MAAM,EAAE,kBAAkB;IAC1B,UAAU,EAAE,wCAAwC;IACpD,MAAM,EAAE,aAAa;IACrB,SAAS,EAAE,yBAAyB;IACpC,MAAM,EAAE,aAAa;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAA2C;IAC7D,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,gBAAgB;IACxB,UAAU,EAAE,oBAAoB;IAChC,MAAM,EAAE,gBAAgB;IACxB,SAAS,EAAE,mBAAmB;IAC9B,MAAM,EAAE,aAAa;CACtB,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,2BAA2B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAoB;IAClD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,WAAW,GACf,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAC3D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAC1B,IAAI,EAAE,CAAC;IACV,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,GAAG,WAAW,SAAS,MAAM,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,OAAO,CAAC,qBAAqB,EAAE,YAAY,CAAC;SAC5C,OAAO,CAAC,qBAAqB,EAAE,YAAY,CAAC;SAC5C,OAAO,CAAC,oBAAoB,EAAE,WAAW,CAAC;SAC1C,OAAO,CAAC,uBAAuB,EAAE,cAAc,CAAC;SAChD,OAAO,CAAC,wBAAwB,EAAE,eAAe,CAAC,CAAC;AACxD,CAAC"}