create-forge-team 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.
@@ -0,0 +1,108 @@
1
+ // src/memory/openrouter-client.ts
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ var OPENROUTER_ENDPOINT = "https://openrouter.ai/api/v1/chat/completions";
6
+ var DEFAULT_MODEL = "deepseek/deepseek-v3.2-20251201";
7
+ var FALLBACK_MODEL = "google/gemini-2.5-flash";
8
+ var MAX_RETRIES = 3;
9
+ var BASE_DELAY_MS = 1e3;
10
+ function loadConfig() {
11
+ const configPath = join(homedir(), ".claude", "forge", "config.json");
12
+ if (!existsSync(configPath)) return null;
13
+ try {
14
+ return JSON.parse(readFileSync(configPath, "utf-8"));
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+ function getApiKey() {
20
+ if (process.env.OPENROUTER_API_KEY) {
21
+ return process.env.OPENROUTER_API_KEY;
22
+ }
23
+ const config = loadConfig();
24
+ return config?.memoryAgent?.openrouterApiKey || null;
25
+ }
26
+ function getModel() {
27
+ const config = loadConfig();
28
+ return config?.memoryAgent?.model || DEFAULT_MODEL;
29
+ }
30
+ function isMemoryAgentConfigured() {
31
+ const apiKey = getApiKey();
32
+ if (!apiKey) return false;
33
+ const config = loadConfig();
34
+ return config?.memoryAgent?.enabled !== false;
35
+ }
36
+ async function callOpenRouter(messages, options) {
37
+ const apiKey = getApiKey();
38
+ if (!apiKey) {
39
+ throw new Error(
40
+ "OpenRouter API key not configured. Run 'forge memory setup' or set OPENROUTER_API_KEY."
41
+ );
42
+ }
43
+ const primaryModel = options?.model || getModel();
44
+ const models = [primaryModel, FALLBACK_MODEL];
45
+ for (const model of models) {
46
+ try {
47
+ return await callWithRetry(apiKey, messages, model, options);
48
+ } catch (err) {
49
+ const errMsg = err instanceof Error ? err.message : String(err);
50
+ if (errMsg.includes("401") || errMsg.includes("403")) {
51
+ throw err;
52
+ }
53
+ if (model === models[models.length - 1]) {
54
+ throw err;
55
+ }
56
+ }
57
+ }
58
+ throw new Error("All models failed");
59
+ }
60
+ async function callWithRetry(apiKey, messages, model, options) {
61
+ let lastError = null;
62
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
63
+ try {
64
+ const response = await fetch(OPENROUTER_ENDPOINT, {
65
+ method: "POST",
66
+ headers: {
67
+ "Authorization": `Bearer ${apiKey}`,
68
+ "Content-Type": "application/json",
69
+ "X-Title": "Forge Memory Agent"
70
+ },
71
+ body: JSON.stringify({
72
+ model,
73
+ messages,
74
+ max_tokens: options?.maxTokens || 4096,
75
+ temperature: options?.temperature ?? 0.3
76
+ })
77
+ });
78
+ if (!response.ok) {
79
+ const body = await response.text();
80
+ throw new Error(`OpenRouter ${response.status}: ${body}`);
81
+ }
82
+ const data = await response.json();
83
+ if (data.error) {
84
+ throw new Error(`OpenRouter error: ${data.error.message}`);
85
+ }
86
+ const content = data.choices?.[0]?.message?.content;
87
+ if (!content) {
88
+ throw new Error("Empty response from OpenRouter");
89
+ }
90
+ return content;
91
+ } catch (err) {
92
+ lastError = err instanceof Error ? err : new Error(String(err));
93
+ if (lastError.message.includes("401") || lastError.message.includes("403")) {
94
+ throw lastError;
95
+ }
96
+ if (attempt < MAX_RETRIES - 1) {
97
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt);
98
+ await new Promise((resolve) => setTimeout(resolve, delay));
99
+ }
100
+ }
101
+ }
102
+ throw lastError || new Error("Failed after retries");
103
+ }
104
+
105
+ export {
106
+ isMemoryAgentConfigured,
107
+ callOpenRouter
108
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node