teleton 0.8.2 → 0.8.4
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 +230 -294
- package/dist/bootstrap-NNEI3Z5H.js +126 -0
- package/dist/{chunk-HEDJCLA6.js → chunk-35MX4ZUI.js} +2 -122
- package/dist/{chunk-57URFK6M.js → chunk-5LOHRZYY.js} +64 -15
- package/dist/{chunk-YOSUPUAJ.js → chunk-6OOHHJ4N.js} +1 -174
- package/dist/{chunk-W25Z7CM6.js → chunk-ALKAAG4O.js} +3 -3
- package/dist/chunk-CUE4UZXR.js +129 -0
- package/dist/{chunk-XBSCYMKM.js → chunk-G7PCW63M.js} +14 -14
- package/dist/{chunk-VYKW7FMV.js → chunk-GHMXWAXI.js} +1 -1
- package/dist/chunk-JROBTXWY.js +908 -0
- package/dist/{chunk-J73TA3UM.js → chunk-LVTKJQ7O.js} +7 -5
- package/dist/{chunk-GGXJLMOH.js → chunk-LZQOX6YY.js} +283 -1061
- package/dist/{chunk-7YKSXOQQ.js → chunk-NH2CNRKJ.js} +131 -241
- package/dist/chunk-NVKBBTI6.js +128 -0
- package/dist/{chunk-2IZU3REP.js → chunk-UMUONAD6.js} +143 -220
- package/dist/chunk-WTDAICGT.js +175 -0
- package/dist/{chunk-55SKE6YH.js → chunk-XDZDOKIF.js} +1 -1
- package/dist/cli/index.js +41 -24
- package/dist/{client-YOOHI776.js → client-5KD25NOP.js} +4 -3
- package/dist/index.js +14 -10
- package/dist/local-IHKJFQJS.js +9 -0
- package/dist/{memory-Q6EWGK2S.js → memory-QMJRM3XJ.js} +5 -3
- package/dist/{memory-hook-WUXJNVT5.js → memory-hook-VUNWZ3NY.js} +5 -4
- package/dist/{migrate-WFU6COBN.js → migrate-5VBAP52B.js} +3 -2
- package/dist/{server-YODFBZKG.js → server-AJCOURH7.js} +13 -10
- package/dist/{server-GYZXKIKU.js → server-WWGVDFPW.js} +38 -13
- package/dist/{setup-server-IZBUOJRU.js → setup-server-VDY64CWW.js} +5 -3
- package/dist/{store-7M4XV6M5.js → store-BY7S6IFN.js} +4 -3
- package/dist/{tool-index-NYH57UWP.js → tool-index-FTERJSZK.js} +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentLifecycle
|
|
3
|
+
} from "./chunk-NVKBBTI6.js";
|
|
4
|
+
import {
|
|
5
|
+
configExists,
|
|
6
|
+
ensureWorkspace,
|
|
7
|
+
getDefaultConfigPath
|
|
8
|
+
} from "./chunk-NH2CNRKJ.js";
|
|
9
|
+
import "./chunk-C4NKJT2Z.js";
|
|
10
|
+
import "./chunk-6OOHHJ4N.js";
|
|
11
|
+
import {
|
|
12
|
+
SHUTDOWN_TIMEOUT_MS
|
|
13
|
+
} from "./chunk-R4YSJ4EY.js";
|
|
14
|
+
import "./chunk-EYWNOHMJ.js";
|
|
15
|
+
import {
|
|
16
|
+
createLogger
|
|
17
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
18
|
+
import "./chunk-3RG5ZIWI.js";
|
|
19
|
+
|
|
20
|
+
// src/api/bootstrap.ts
|
|
21
|
+
var log = createLogger("Bootstrap");
|
|
22
|
+
async function startApiOnly(options) {
|
|
23
|
+
await ensureWorkspace({ ensureTemplates: false, silent: false });
|
|
24
|
+
const configPath = options.config ?? getDefaultConfigPath();
|
|
25
|
+
const lifecycle = new AgentLifecycle();
|
|
26
|
+
const deps = {
|
|
27
|
+
agent: null,
|
|
28
|
+
bridge: null,
|
|
29
|
+
memory: null,
|
|
30
|
+
toolRegistry: null,
|
|
31
|
+
plugins: null,
|
|
32
|
+
mcpServers: null,
|
|
33
|
+
config: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
port: 7777,
|
|
36
|
+
host: "127.0.0.1",
|
|
37
|
+
cors_origins: [],
|
|
38
|
+
log_requests: false
|
|
39
|
+
},
|
|
40
|
+
configPath,
|
|
41
|
+
lifecycle,
|
|
42
|
+
marketplace: null,
|
|
43
|
+
userHookEvaluator: null
|
|
44
|
+
};
|
|
45
|
+
const { ApiServer } = await import("./server-WWGVDFPW.js");
|
|
46
|
+
const apiConfig = {
|
|
47
|
+
enabled: true,
|
|
48
|
+
port: parseInt(options.apiPort || process.env.TELETON_API_PORT || "7778"),
|
|
49
|
+
key_hash: "",
|
|
50
|
+
allowed_ips: []
|
|
51
|
+
};
|
|
52
|
+
const server = new ApiServer(deps, apiConfig);
|
|
53
|
+
let appInstance = null;
|
|
54
|
+
lifecycle.registerCallbacks(
|
|
55
|
+
// startFn — called when POST /v1/agent/start fires
|
|
56
|
+
async () => {
|
|
57
|
+
if (!configExists(configPath)) {
|
|
58
|
+
throw new Error("Configuration not found. Complete setup via /v1/setup endpoints first.");
|
|
59
|
+
}
|
|
60
|
+
const { TeletonApp } = await import("./index.js");
|
|
61
|
+
appInstance = new TeletonApp(configPath);
|
|
62
|
+
await appInstance.startAgentSubsystems();
|
|
63
|
+
server.updateDeps({
|
|
64
|
+
agent: appInstance.getAgent(),
|
|
65
|
+
bridge: appInstance.getBridge(),
|
|
66
|
+
memory: appInstance.getMemory(),
|
|
67
|
+
toolRegistry: appInstance.getToolRegistry(),
|
|
68
|
+
plugins: appInstance.getPlugins(),
|
|
69
|
+
config: appInstance.getWebuiConfig()
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
// stopFn — called when POST /v1/agent/stop fires
|
|
73
|
+
async () => {
|
|
74
|
+
if (appInstance) {
|
|
75
|
+
await appInstance.stopAgentSubsystems();
|
|
76
|
+
appInstance = null;
|
|
77
|
+
server.updateDeps({
|
|
78
|
+
agent: null,
|
|
79
|
+
bridge: null,
|
|
80
|
+
memory: null,
|
|
81
|
+
toolRegistry: null,
|
|
82
|
+
plugins: null
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
await server.start();
|
|
88
|
+
if (process.env.TELETON_JSON_CREDENTIALS === "true") {
|
|
89
|
+
const creds = server.getCredentials();
|
|
90
|
+
process.stdout.write(JSON.stringify(creds) + "\n");
|
|
91
|
+
}
|
|
92
|
+
log.info("API-only mode: complete setup via /v1/setup endpoints, then POST /v1/agent/start");
|
|
93
|
+
let shutdownInProgress = false;
|
|
94
|
+
const gracefulShutdown = async () => {
|
|
95
|
+
if (shutdownInProgress) return;
|
|
96
|
+
shutdownInProgress = true;
|
|
97
|
+
const forceExit = setTimeout(() => {
|
|
98
|
+
log.error("Shutdown timed out, forcing exit");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
101
|
+
forceExit.unref();
|
|
102
|
+
try {
|
|
103
|
+
if (lifecycle.getState() === "running") {
|
|
104
|
+
await lifecycle.stop();
|
|
105
|
+
}
|
|
106
|
+
await server.stop();
|
|
107
|
+
} finally {
|
|
108
|
+
clearTimeout(forceExit);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
process.on("SIGINT", gracefulShutdown);
|
|
113
|
+
process.on("SIGTERM", gracefulShutdown);
|
|
114
|
+
process.on("unhandledRejection", (reason) => {
|
|
115
|
+
log.error({ err: reason }, "Unhandled rejection");
|
|
116
|
+
});
|
|
117
|
+
process.on("uncaughtException", (error) => {
|
|
118
|
+
log.error({ err: error }, "Uncaught exception");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
|
121
|
+
await new Promise(() => {
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
startApiOnly
|
|
126
|
+
};
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
fetchWithTimeout
|
|
13
13
|
} from "./chunk-XQUHC3JZ.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
15
|
+
LocalEmbeddingProvider
|
|
16
|
+
} from "./chunk-CUE4UZXR.js";
|
|
17
17
|
import {
|
|
18
18
|
createLogger
|
|
19
19
|
} from "./chunk-NQ6FZKCE.js";
|
|
@@ -88,125 +88,6 @@ var AnthropicEmbeddingProvider = class {
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
// src/memory/embeddings/local.ts
|
|
92
|
-
import { pipeline, env } from "@huggingface/transformers";
|
|
93
|
-
import { join, dirname } from "path";
|
|
94
|
-
import { mkdirSync, writeFileSync, renameSync, statSync, unlinkSync } from "fs";
|
|
95
|
-
var log = createLogger("Memory");
|
|
96
|
-
var modelCacheDir = join(TELETON_ROOT, "models");
|
|
97
|
-
try {
|
|
98
|
-
mkdirSync(modelCacheDir, { recursive: true });
|
|
99
|
-
} catch {
|
|
100
|
-
}
|
|
101
|
-
env.cacheDir = modelCacheDir;
|
|
102
|
-
var MIN_FILE_SIZES = { "onnx/model.onnx": 1e6 };
|
|
103
|
-
function isCacheFileValid(filePath, fileName) {
|
|
104
|
-
try {
|
|
105
|
-
return statSync(filePath).size >= (MIN_FILE_SIZES[fileName] ?? 1);
|
|
106
|
-
} catch {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
async function ensureModelCached(model) {
|
|
111
|
-
const files = ["config.json", "tokenizer_config.json", "tokenizer.json", "onnx/model.onnx"];
|
|
112
|
-
const baseUrl = `https://huggingface.co/${model}/resolve/main`;
|
|
113
|
-
for (const file of files) {
|
|
114
|
-
const localPath = join(modelCacheDir, model, file);
|
|
115
|
-
if (isCacheFileValid(localPath, file)) continue;
|
|
116
|
-
try {
|
|
117
|
-
unlinkSync(localPath);
|
|
118
|
-
} catch {
|
|
119
|
-
}
|
|
120
|
-
log.info(`Downloading ${model}/${file}...`);
|
|
121
|
-
mkdirSync(dirname(localPath), { recursive: true });
|
|
122
|
-
const res = await fetch(`${baseUrl}/${file}`, { redirect: "follow" });
|
|
123
|
-
if (!res.ok) {
|
|
124
|
-
throw new Error(`Failed to download ${model}/${file}: ${res.status} ${res.statusText}`);
|
|
125
|
-
}
|
|
126
|
-
const buffer = Buffer.from(await res.arrayBuffer());
|
|
127
|
-
const tmpPath = localPath + ".tmp";
|
|
128
|
-
writeFileSync(tmpPath, buffer);
|
|
129
|
-
renameSync(tmpPath, localPath);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
var extractorPromise = null;
|
|
133
|
-
function getExtractor(model) {
|
|
134
|
-
if (!extractorPromise) {
|
|
135
|
-
log.info(`Loading local embedding model: ${model} (cache: ${modelCacheDir})`);
|
|
136
|
-
extractorPromise = pipeline("feature-extraction", model, {
|
|
137
|
-
dtype: "fp32",
|
|
138
|
-
// Explicit cache_dir to avoid any env race condition
|
|
139
|
-
cache_dir: modelCacheDir,
|
|
140
|
-
// Prevent pthread_setaffinity_np EINVAL on VPS/containers with restricted CPU sets.
|
|
141
|
-
// ONNX Runtime skips thread affinity when thread counts are explicit.
|
|
142
|
-
session_options: { intraOpNumThreads: 1, interOpNumThreads: 1 }
|
|
143
|
-
}).then((ext) => {
|
|
144
|
-
log.info(`Local embedding model ready`);
|
|
145
|
-
return ext;
|
|
146
|
-
}).catch((err) => {
|
|
147
|
-
log.error(`Failed to load embedding model: ${err.message}`);
|
|
148
|
-
extractorPromise = null;
|
|
149
|
-
throw err;
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return extractorPromise;
|
|
153
|
-
}
|
|
154
|
-
var LocalEmbeddingProvider = class {
|
|
155
|
-
id = "local";
|
|
156
|
-
model;
|
|
157
|
-
dimensions;
|
|
158
|
-
_disabled = false;
|
|
159
|
-
constructor(config) {
|
|
160
|
-
this.model = config.model || "Xenova/all-MiniLM-L6-v2";
|
|
161
|
-
this.dimensions = 384;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Pre-download and load the model at startup.
|
|
165
|
-
* If loading fails, retries once then marks provider as disabled (FTS5-only).
|
|
166
|
-
* Call this once during app init — avoids retry spam on every message.
|
|
167
|
-
*/
|
|
168
|
-
async warmup() {
|
|
169
|
-
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
170
|
-
try {
|
|
171
|
-
await ensureModelCached(this.model);
|
|
172
|
-
await getExtractor(this.model);
|
|
173
|
-
return true;
|
|
174
|
-
} catch {
|
|
175
|
-
if (attempt === 1) {
|
|
176
|
-
log.warn(`Embedding model load failed (attempt 1), retrying...`);
|
|
177
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
178
|
-
} else {
|
|
179
|
-
log.warn(
|
|
180
|
-
`Local embedding model unavailable \u2014 falling back to FTS5-only search (no vector embeddings)`
|
|
181
|
-
);
|
|
182
|
-
this._disabled = true;
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
async embedQuery(text) {
|
|
190
|
-
if (this._disabled) return [];
|
|
191
|
-
const extractor = await getExtractor(this.model);
|
|
192
|
-
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
193
|
-
return Array.from(output.data);
|
|
194
|
-
}
|
|
195
|
-
async embedBatch(texts) {
|
|
196
|
-
if (this._disabled) return [];
|
|
197
|
-
if (texts.length === 0) return [];
|
|
198
|
-
const extractor = await getExtractor(this.model);
|
|
199
|
-
const output = await extractor(texts, { pooling: "mean", normalize: true });
|
|
200
|
-
const data = output.data;
|
|
201
|
-
const dims = this.dimensions;
|
|
202
|
-
const results = [];
|
|
203
|
-
for (let i = 0; i < texts.length; i++) {
|
|
204
|
-
results.push(Array.from(data.slice(i * dims, (i + 1) * dims)));
|
|
205
|
-
}
|
|
206
|
-
return results;
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
91
|
// src/memory/embeddings/utils.ts
|
|
211
92
|
import { createHash } from "crypto";
|
|
212
93
|
function hashText(text) {
|
|
@@ -379,7 +260,6 @@ function createEmbeddingProvider(config) {
|
|
|
379
260
|
export {
|
|
380
261
|
NoopEmbeddingProvider,
|
|
381
262
|
AnthropicEmbeddingProvider,
|
|
382
|
-
LocalEmbeddingProvider,
|
|
383
263
|
hashText,
|
|
384
264
|
serializeEmbedding,
|
|
385
265
|
deserializeEmbedding,
|
|
@@ -2,26 +2,32 @@ import {
|
|
|
2
2
|
getModelsForProvider
|
|
3
3
|
} from "./chunk-WFTC3JJW.js";
|
|
4
4
|
import {
|
|
5
|
-
ConfigSchema,
|
|
6
|
-
DealsConfigSchema,
|
|
7
|
-
ensureWorkspace,
|
|
8
5
|
generateWallet,
|
|
9
6
|
getWalletAddress,
|
|
10
7
|
importWallet,
|
|
11
|
-
|
|
8
|
+
readRawConfig,
|
|
12
9
|
saveWallet,
|
|
13
|
-
walletExists
|
|
14
|
-
|
|
10
|
+
walletExists,
|
|
11
|
+
writeRawConfig
|
|
12
|
+
} from "./chunk-JROBTXWY.js";
|
|
13
|
+
import {
|
|
14
|
+
ConfigSchema,
|
|
15
|
+
DealsConfigSchema,
|
|
16
|
+
ensureWorkspace,
|
|
17
|
+
isNewWorkspace
|
|
18
|
+
} from "./chunk-NH2CNRKJ.js";
|
|
19
|
+
import {
|
|
20
|
+
TELEGRAM_MAX_MESSAGE_LENGTH
|
|
21
|
+
} from "./chunk-C4NKJT2Z.js";
|
|
15
22
|
import {
|
|
16
23
|
getClaudeCodeApiKey,
|
|
24
|
+
isClaudeCodeTokenValid
|
|
25
|
+
} from "./chunk-WTDAICGT.js";
|
|
26
|
+
import {
|
|
17
27
|
getProviderMetadata,
|
|
18
28
|
getSupportedProviders,
|
|
19
|
-
isClaudeCodeTokenValid,
|
|
20
29
|
validateApiKeyFormat
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import {
|
|
23
|
-
TELEGRAM_MAX_MESSAGE_LENGTH
|
|
24
|
-
} from "./chunk-C4NKJT2Z.js";
|
|
30
|
+
} from "./chunk-6OOHHJ4N.js";
|
|
25
31
|
import {
|
|
26
32
|
fetchWithTimeout
|
|
27
33
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -34,7 +40,7 @@ import {
|
|
|
34
40
|
|
|
35
41
|
// src/webui/routes/setup.ts
|
|
36
42
|
import { Hono } from "hono";
|
|
37
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
43
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2, statSync } from "fs";
|
|
38
44
|
import { join as join2 } from "path";
|
|
39
45
|
import YAML from "yaml";
|
|
40
46
|
|
|
@@ -376,7 +382,16 @@ var TelegramAuthManager = class {
|
|
|
376
382
|
mkdirSync(dir, { recursive: true });
|
|
377
383
|
}
|
|
378
384
|
writeFileSync(sessionPath, sessionString, { mode: 384 });
|
|
379
|
-
|
|
385
|
+
const configPath = join(TELETON_ROOT, "config.yaml");
|
|
386
|
+
const raw = readRawConfig(configPath);
|
|
387
|
+
raw.telegram = raw.telegram ?? {};
|
|
388
|
+
raw.telegram.api_id = session.apiId;
|
|
389
|
+
raw.telegram.api_hash = session.apiHash;
|
|
390
|
+
if (session.type === "phone") {
|
|
391
|
+
raw.telegram.phone = session.phone;
|
|
392
|
+
}
|
|
393
|
+
writeRawConfig(raw, configPath);
|
|
394
|
+
log.info("Telegram session and credentials saved");
|
|
380
395
|
}
|
|
381
396
|
};
|
|
382
397
|
|
|
@@ -386,7 +401,7 @@ function maskKey(key) {
|
|
|
386
401
|
if (key.length <= 10) return "***";
|
|
387
402
|
return key.slice(0, 6) + "..." + key.slice(-4);
|
|
388
403
|
}
|
|
389
|
-
function createSetupRoutes() {
|
|
404
|
+
function createSetupRoutes(options) {
|
|
390
405
|
const app = new Hono();
|
|
391
406
|
const authManager = new TelegramAuthManager();
|
|
392
407
|
app.get("/status", async (c) => {
|
|
@@ -744,6 +759,33 @@ function createSetupRoutes() {
|
|
|
744
759
|
);
|
|
745
760
|
}
|
|
746
761
|
});
|
|
762
|
+
app.post("/embeddings/warmup", async (c) => {
|
|
763
|
+
try {
|
|
764
|
+
const { LocalEmbeddingProvider } = await import("./local-IHKJFQJS.js");
|
|
765
|
+
const provider = new LocalEmbeddingProvider({});
|
|
766
|
+
const success = await provider.warmup();
|
|
767
|
+
return c.json({
|
|
768
|
+
success,
|
|
769
|
+
model: provider.model,
|
|
770
|
+
dimensions: provider.dimensions
|
|
771
|
+
});
|
|
772
|
+
} catch (error) {
|
|
773
|
+
return c.json(
|
|
774
|
+
{ success: false, error: error instanceof Error ? error.message : String(error) },
|
|
775
|
+
500
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
app.get("/embeddings/status", (c) => {
|
|
780
|
+
const model = "Xenova/all-MiniLM-L6-v2";
|
|
781
|
+
const modelPath = join2(TELETON_ROOT, "models", model, "onnx", "model.onnx");
|
|
782
|
+
try {
|
|
783
|
+
const stats = statSync(modelPath);
|
|
784
|
+
return c.json({ cached: true, model, modelPath, sizeBytes: stats.size });
|
|
785
|
+
} catch {
|
|
786
|
+
return c.json({ cached: false, model, modelPath });
|
|
787
|
+
}
|
|
788
|
+
});
|
|
747
789
|
app.post("/config/save", async (c) => {
|
|
748
790
|
try {
|
|
749
791
|
const input = await c.req.json();
|
|
@@ -840,7 +882,14 @@ function createSetupRoutes() {
|
|
|
840
882
|
...input.cocoon ? { cocoon: input.cocoon } : {},
|
|
841
883
|
...input.tonapi_key ? { tonapi_key: input.tonapi_key } : {},
|
|
842
884
|
...input.toncenter_api_key ? { toncenter_api_key: input.toncenter_api_key } : {},
|
|
843
|
-
...input.tavily_api_key ? { tavily_api_key: input.tavily_api_key } : {}
|
|
885
|
+
...input.tavily_api_key ? { tavily_api_key: input.tavily_api_key } : {},
|
|
886
|
+
// Persist Management API key hash so it survives reboots
|
|
887
|
+
api: {
|
|
888
|
+
enabled: true,
|
|
889
|
+
port: 7778,
|
|
890
|
+
host: "0.0.0.0",
|
|
891
|
+
...options?.keyHash ? { key_hash: options.keyHash } : {}
|
|
892
|
+
}
|
|
844
893
|
};
|
|
845
894
|
ConfigSchema.parse(config);
|
|
846
895
|
const configPath = workspace.configPath;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createLogger
|
|
3
|
-
} from "./chunk-NQ6FZKCE.js";
|
|
4
|
-
|
|
5
1
|
// src/config/providers.ts
|
|
6
2
|
var PROVIDER_REGISTRY = {
|
|
7
3
|
anthropic: {
|
|
@@ -206,177 +202,8 @@ function validateApiKeyFormat(provider, key) {
|
|
|
206
202
|
return void 0;
|
|
207
203
|
}
|
|
208
204
|
|
|
209
|
-
// src/providers/claude-code-credentials.ts
|
|
210
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
211
|
-
import { execSync } from "child_process";
|
|
212
|
-
import { homedir } from "os";
|
|
213
|
-
import { join } from "path";
|
|
214
|
-
var log = createLogger("ClaudeCodeCreds");
|
|
215
|
-
var OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
216
|
-
var OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
217
|
-
var OAUTH_SCOPES = "user:profile user:inference user:sessions:claude_code user:mcp_servers";
|
|
218
|
-
var cachedToken = null;
|
|
219
|
-
var cachedExpiresAt = 0;
|
|
220
|
-
var cachedRefreshToken = null;
|
|
221
|
-
function getClaudeConfigDir() {
|
|
222
|
-
return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
223
|
-
}
|
|
224
|
-
function getCredentialsFilePath() {
|
|
225
|
-
return join(getClaudeConfigDir(), ".credentials.json");
|
|
226
|
-
}
|
|
227
|
-
function readCredentialsFile() {
|
|
228
|
-
const filePath = getCredentialsFilePath();
|
|
229
|
-
if (!existsSync(filePath)) return null;
|
|
230
|
-
try {
|
|
231
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
232
|
-
return JSON.parse(raw);
|
|
233
|
-
} catch (e) {
|
|
234
|
-
log.warn({ err: e, path: filePath }, "Failed to parse Claude Code credentials file");
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function readKeychainCredentials() {
|
|
239
|
-
const serviceNames = ["Claude Code-credentials", "Claude Code"];
|
|
240
|
-
for (const service of serviceNames) {
|
|
241
|
-
try {
|
|
242
|
-
const raw = execSync(`security find-generic-password -s "${service}" -w`, {
|
|
243
|
-
encoding: "utf-8",
|
|
244
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
245
|
-
}).trim();
|
|
246
|
-
return JSON.parse(raw);
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
function readCredentials() {
|
|
253
|
-
if (process.platform === "darwin") {
|
|
254
|
-
const keychainCreds = readKeychainCredentials();
|
|
255
|
-
if (keychainCreds) return keychainCreds;
|
|
256
|
-
log.debug("Keychain read failed, falling back to credentials file");
|
|
257
|
-
}
|
|
258
|
-
return readCredentialsFile();
|
|
259
|
-
}
|
|
260
|
-
function extractToken(creds) {
|
|
261
|
-
const oauth = creds.claudeAiOauth;
|
|
262
|
-
if (!oauth?.accessToken) {
|
|
263
|
-
log.warn("Claude Code credentials found but missing accessToken");
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
token: oauth.accessToken,
|
|
268
|
-
expiresAt: oauth.expiresAt ?? 0,
|
|
269
|
-
refreshToken: oauth.refreshToken
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
function getClaudeCodeApiKey(fallbackKey) {
|
|
273
|
-
if (cachedToken && Date.now() < cachedExpiresAt) {
|
|
274
|
-
return cachedToken;
|
|
275
|
-
}
|
|
276
|
-
const creds = readCredentials();
|
|
277
|
-
if (creds) {
|
|
278
|
-
const extracted = extractToken(creds);
|
|
279
|
-
if (extracted) {
|
|
280
|
-
cachedToken = extracted.token;
|
|
281
|
-
cachedExpiresAt = extracted.expiresAt;
|
|
282
|
-
cachedRefreshToken = extracted.refreshToken ?? null;
|
|
283
|
-
log.debug("Claude Code credentials loaded successfully");
|
|
284
|
-
return cachedToken;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (fallbackKey && fallbackKey.length > 0) {
|
|
288
|
-
log.warn("Claude Code credentials not found, using fallback api_key from config");
|
|
289
|
-
return fallbackKey;
|
|
290
|
-
}
|
|
291
|
-
throw new Error("No Claude Code credentials found. Run 'claude login' or set api_key in config.");
|
|
292
|
-
}
|
|
293
|
-
async function performOAuthRefresh(refreshToken) {
|
|
294
|
-
try {
|
|
295
|
-
const res = await fetch(OAUTH_TOKEN_URL, {
|
|
296
|
-
method: "POST",
|
|
297
|
-
headers: { "Content-Type": "application/json" },
|
|
298
|
-
body: JSON.stringify({
|
|
299
|
-
grant_type: "refresh_token",
|
|
300
|
-
refresh_token: refreshToken,
|
|
301
|
-
client_id: OAUTH_CLIENT_ID,
|
|
302
|
-
scope: OAUTH_SCOPES
|
|
303
|
-
})
|
|
304
|
-
});
|
|
305
|
-
if (!res.ok) {
|
|
306
|
-
log.warn(`OAuth token refresh failed: ${res.status} ${res.statusText}`);
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
const data = await res.json();
|
|
310
|
-
if (!data.access_token || !data.expires_in) {
|
|
311
|
-
log.warn("OAuth token refresh: unexpected response shape");
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
const newExpiresAt = Date.now() + data.expires_in * 1e3;
|
|
315
|
-
const newRefreshToken = data.refresh_token ?? refreshToken;
|
|
316
|
-
const filePath = getCredentialsFilePath();
|
|
317
|
-
try {
|
|
318
|
-
const existing = existsSync(filePath) ? JSON.parse(readFileSync(filePath, "utf-8")) : {};
|
|
319
|
-
const updated = {
|
|
320
|
-
...existing,
|
|
321
|
-
claudeAiOauth: {
|
|
322
|
-
...existing.claudeAiOauth,
|
|
323
|
-
accessToken: data.access_token,
|
|
324
|
-
refreshToken: newRefreshToken,
|
|
325
|
-
expiresAt: newExpiresAt
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
writeFileSync(filePath, JSON.stringify(updated, null, 2), { mode: 384 });
|
|
329
|
-
} catch (e) {
|
|
330
|
-
log.warn({ err: e }, "Failed to persist refreshed OAuth credentials to disk");
|
|
331
|
-
}
|
|
332
|
-
cachedToken = data.access_token;
|
|
333
|
-
cachedExpiresAt = newExpiresAt;
|
|
334
|
-
cachedRefreshToken = newRefreshToken;
|
|
335
|
-
log.info("Claude Code OAuth token refreshed successfully");
|
|
336
|
-
return cachedToken;
|
|
337
|
-
} catch (e) {
|
|
338
|
-
log.warn({ err: e }, "OAuth token refresh request failed");
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
async function refreshClaudeCodeApiKey() {
|
|
343
|
-
cachedToken = null;
|
|
344
|
-
cachedExpiresAt = 0;
|
|
345
|
-
if (!cachedRefreshToken) {
|
|
346
|
-
const creds2 = readCredentials();
|
|
347
|
-
if (creds2) {
|
|
348
|
-
const extracted = extractToken(creds2);
|
|
349
|
-
cachedRefreshToken = extracted?.refreshToken ?? null;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (cachedRefreshToken) {
|
|
353
|
-
const refreshed = await performOAuthRefresh(cachedRefreshToken);
|
|
354
|
-
if (refreshed) return refreshed;
|
|
355
|
-
log.warn("OAuth refresh failed, falling back to disk read");
|
|
356
|
-
}
|
|
357
|
-
const creds = readCredentials();
|
|
358
|
-
if (creds) {
|
|
359
|
-
const extracted = extractToken(creds);
|
|
360
|
-
if (extracted) {
|
|
361
|
-
cachedToken = extracted.token;
|
|
362
|
-
cachedExpiresAt = extracted.expiresAt;
|
|
363
|
-
cachedRefreshToken = extracted.refreshToken ?? null;
|
|
364
|
-
log.info("Claude Code credentials refreshed from disk");
|
|
365
|
-
return cachedToken;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
log.warn("Failed to refresh Claude Code credentials");
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
|
-
function isClaudeCodeTokenValid() {
|
|
372
|
-
return cachedToken !== null && Date.now() < cachedExpiresAt;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
205
|
export {
|
|
376
206
|
getProviderMetadata,
|
|
377
207
|
getSupportedProviders,
|
|
378
|
-
validateApiKeyFormat
|
|
379
|
-
getClaudeCodeApiKey,
|
|
380
|
-
refreshClaudeCodeApiKey,
|
|
381
|
-
isClaudeCodeTokenValid
|
|
208
|
+
validateApiKeyFormat
|
|
382
209
|
};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getErrorMessage
|
|
3
3
|
} from "./chunk-3UFPFWYP.js";
|
|
4
|
-
import {
|
|
5
|
-
getUtilityModel
|
|
6
|
-
} from "./chunk-J73TA3UM.js";
|
|
7
4
|
import {
|
|
8
5
|
ADAPTIVE_CHUNK_RATIO_BASE,
|
|
9
6
|
ADAPTIVE_CHUNK_RATIO_MIN,
|
|
@@ -17,6 +14,9 @@ import {
|
|
|
17
14
|
SESSION_SLUG_RECENT_MESSAGES,
|
|
18
15
|
TOKEN_ESTIMATE_SAFETY_MARGIN
|
|
19
16
|
} from "./chunk-C4NKJT2Z.js";
|
|
17
|
+
import {
|
|
18
|
+
getUtilityModel
|
|
19
|
+
} from "./chunk-LVTKJQ7O.js";
|
|
20
20
|
import {
|
|
21
21
|
createLogger
|
|
22
22
|
} from "./chunk-NQ6FZKCE.js";
|