teleton 0.7.4 → 0.7.5

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 (35) hide show
  1. package/README.md +36 -26
  2. package/dist/{chunk-XDYDA2KV.js → chunk-2GLHOJ5C.js} +268 -59
  3. package/dist/chunk-5UVXJMOX.js +292 -0
  4. package/dist/{chunk-BGC2IUM5.js → chunk-AVDWXYQ7.js} +65 -20
  5. package/dist/{chunk-RMLQS3X6.js → chunk-CB2Y45HA.js} +106 -1
  6. package/dist/{chunk-5PLZ3KSO.js → chunk-DMXTIRUW.js} +5 -6
  7. package/dist/{chunk-YFG2QHLA.js → chunk-G2LLMJXJ.js} +1578 -115
  8. package/dist/{chunk-EK7M5K26.js → chunk-LCCVZ4D2.js} +3 -3
  9. package/dist/{chunk-LAQOUFOJ.js → chunk-OGMVWDVU.js} +3517 -3620
  10. package/dist/{chunk-4DU3C27M.js → chunk-R4YSJ4EY.js} +5 -1
  11. package/dist/{chunk-XBKSS6DM.js → chunk-VFA7QMCZ.js} +5 -3
  12. package/dist/{chunk-VAUJSSD3.js → chunk-XQUHC3JZ.js} +1 -1
  13. package/dist/{chunk-RO62LO6Z.js → chunk-YP25WTQK.js} +2 -0
  14. package/dist/cli/index.js +92 -28
  15. package/dist/{client-RTNALK7W.js → client-O37XDCJB.js} +4 -5
  16. package/dist/index.js +12 -13
  17. package/dist/{memory-JQZ6MTRU.js → memory-KQALFUV3.js} +6 -7
  18. package/dist/{migrate-GS5ACQDA.js → migrate-UV3WEL5D.js} +6 -7
  19. package/dist/{server-TCJOBV3D.js → server-BHHJGUDF.js} +35 -9
  20. package/dist/{setup-server-YHYJLAMA.js → setup-server-G7UG2DI3.js} +21 -9
  21. package/dist/store-H4XPNGC2.js +34 -0
  22. package/dist/{task-dependency-resolver-WKZWJLLM.js → task-dependency-resolver-VMEVJRPO.js} +2 -2
  23. package/dist/{task-executor-PD3H4MLO.js → task-executor-WWSPBJ4V.js} +1 -1
  24. package/dist/{tool-index-6HBRVXVG.js → tool-index-2KH3OB6X.js} +5 -5
  25. package/dist/web/assets/index-BrVqauzj.css +1 -0
  26. package/dist/web/assets/index-Bx8JW3gV.js +72 -0
  27. package/dist/web/assets/{index.es-CqZHj0tz.js → index.es-Pet5-M13.js} +1 -1
  28. package/dist/web/index.html +2 -2
  29. package/package.json +2 -2
  30. package/dist/chunk-JQDLW7IE.js +0 -107
  31. package/dist/chunk-UCN6TI25.js +0 -143
  32. package/dist/web/assets/index-B6M9knfJ.css +0 -1
  33. package/dist/web/assets/index-DAGeQfVZ.js +0 -72
  34. package/scripts/patch-gramjs.sh +0 -46
  35. package/scripts/postinstall.mjs +0 -16
@@ -0,0 +1,292 @@
1
+ import {
2
+ getDatabase
3
+ } from "./chunk-2GLHOJ5C.js";
4
+ import {
5
+ createLogger
6
+ } from "./chunk-RCMD3U65.js";
7
+
8
+ // src/session/store.ts
9
+ import { randomUUID } from "crypto";
10
+ var log = createLogger("Session");
11
+ function getDb() {
12
+ return getDatabase().getDb();
13
+ }
14
+ function rowToSession(row) {
15
+ return {
16
+ sessionId: row.id,
17
+ chatId: row.chat_id,
18
+ createdAt: row.started_at,
19
+ updatedAt: row.updated_at,
20
+ messageCount: row.message_count || 0,
21
+ lastMessageId: row.last_message_id ?? void 0,
22
+ lastChannel: row.last_channel ?? void 0,
23
+ lastTo: row.last_to ?? void 0,
24
+ contextTokens: row.context_tokens ?? void 0,
25
+ model: row.model ?? void 0,
26
+ provider: row.provider ?? void 0,
27
+ lastResetDate: row.last_reset_date ?? void 0,
28
+ inputTokens: row.input_tokens ?? void 0,
29
+ outputTokens: row.output_tokens ?? void 0
30
+ };
31
+ }
32
+ function loadSessionStore() {
33
+ try {
34
+ const db = getDb();
35
+ const rows = db.prepare("SELECT * FROM sessions").all();
36
+ const store = {};
37
+ for (const row of rows) {
38
+ const sessionKey = row.chat_id;
39
+ store[sessionKey] = rowToSession(row);
40
+ }
41
+ return store;
42
+ } catch (error) {
43
+ log.warn({ err: error }, "Failed to load sessions from database");
44
+ return {};
45
+ }
46
+ }
47
+ function saveSessionStore(store) {
48
+ try {
49
+ const db = getDb();
50
+ const insertStmt = db.prepare(`
51
+ INSERT INTO sessions (
52
+ id, chat_id, started_at, updated_at, message_count,
53
+ last_message_id, last_channel, last_to, context_tokens,
54
+ model, provider, last_reset_date, input_tokens, output_tokens
55
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
56
+ `);
57
+ db.transaction(() => {
58
+ db.prepare("DELETE FROM sessions").run();
59
+ for (const [chatId, session] of Object.entries(store)) {
60
+ insertStmt.run(
61
+ session.sessionId,
62
+ chatId,
63
+ session.createdAt,
64
+ session.updatedAt,
65
+ session.messageCount,
66
+ session.lastMessageId,
67
+ session.lastChannel,
68
+ session.lastTo,
69
+ session.contextTokens,
70
+ session.model,
71
+ session.provider,
72
+ session.lastResetDate,
73
+ session.inputTokens ?? 0,
74
+ session.outputTokens ?? 0
75
+ );
76
+ }
77
+ })();
78
+ } catch (error) {
79
+ log.error({ err: error }, "Failed to save sessions to database");
80
+ }
81
+ }
82
+ function getOrCreateSession(chatId) {
83
+ const db = getDb();
84
+ const sessionKey = `telegram:${chatId}`;
85
+ const row = db.prepare("SELECT * FROM sessions WHERE chat_id = ?").get(sessionKey);
86
+ if (row) {
87
+ return rowToSession(row);
88
+ }
89
+ const now = Date.now();
90
+ const newSession = {
91
+ sessionId: randomUUID(),
92
+ chatId,
93
+ createdAt: now,
94
+ updatedAt: now,
95
+ messageCount: 0,
96
+ lastChannel: "telegram",
97
+ lastTo: chatId
98
+ };
99
+ db.prepare(
100
+ `
101
+ INSERT INTO sessions (
102
+ id, chat_id, started_at, updated_at, message_count, last_channel, last_to
103
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
104
+ `
105
+ ).run(
106
+ newSession.sessionId,
107
+ sessionKey,
108
+ newSession.createdAt,
109
+ newSession.updatedAt,
110
+ newSession.messageCount,
111
+ newSession.lastChannel,
112
+ newSession.lastTo
113
+ );
114
+ log.info(`New session created: ${newSession.sessionId} for chat ${chatId}`);
115
+ return newSession;
116
+ }
117
+ function updateSession(chatId, update) {
118
+ const db = getDb();
119
+ const sessionKey = `telegram:${chatId}`;
120
+ const existing = db.prepare("SELECT * FROM sessions WHERE chat_id = ?").get(sessionKey);
121
+ if (!existing) {
122
+ return getOrCreateSession(chatId);
123
+ }
124
+ const updates = [];
125
+ const values = [];
126
+ if (update.sessionId !== void 0) {
127
+ updates.push("id = ?");
128
+ values.push(update.sessionId);
129
+ }
130
+ if (update.messageCount !== void 0) {
131
+ updates.push("message_count = ?");
132
+ values.push(update.messageCount);
133
+ }
134
+ if (update.lastMessageId !== void 0) {
135
+ updates.push("last_message_id = ?");
136
+ values.push(update.lastMessageId);
137
+ }
138
+ if (update.lastChannel !== void 0) {
139
+ updates.push("last_channel = ?");
140
+ values.push(update.lastChannel);
141
+ }
142
+ if (update.lastTo !== void 0) {
143
+ updates.push("last_to = ?");
144
+ values.push(update.lastTo);
145
+ }
146
+ if (update.contextTokens !== void 0) {
147
+ updates.push("context_tokens = ?");
148
+ values.push(update.contextTokens);
149
+ }
150
+ if (update.model !== void 0) {
151
+ updates.push("model = ?");
152
+ values.push(update.model);
153
+ }
154
+ if (update.provider !== void 0) {
155
+ updates.push("provider = ?");
156
+ values.push(update.provider);
157
+ }
158
+ if (update.lastResetDate !== void 0) {
159
+ updates.push("last_reset_date = ?");
160
+ values.push(update.lastResetDate);
161
+ }
162
+ if (update.inputTokens !== void 0) {
163
+ updates.push("input_tokens = ?");
164
+ values.push(update.inputTokens);
165
+ }
166
+ if (update.outputTokens !== void 0) {
167
+ updates.push("output_tokens = ?");
168
+ values.push(update.outputTokens);
169
+ }
170
+ updates.push("updated_at = ?");
171
+ values.push(Date.now());
172
+ values.push(sessionKey);
173
+ db.prepare(
174
+ `
175
+ UPDATE sessions
176
+ SET ${updates.join(", ")}
177
+ WHERE chat_id = ?
178
+ `
179
+ ).run(...values);
180
+ const updated = db.prepare("SELECT * FROM sessions WHERE chat_id = ?").get(sessionKey);
181
+ return rowToSession(updated);
182
+ }
183
+ function incrementMessageCount(chatId) {
184
+ const db = getDb();
185
+ const sessionKey = `telegram:${chatId}`;
186
+ const result = db.prepare(
187
+ `UPDATE sessions SET message_count = message_count + 1, updated_at = ? WHERE chat_id = ?`
188
+ ).run(Date.now(), sessionKey);
189
+ if (result.changes === 0) {
190
+ getOrCreateSession(chatId);
191
+ db.prepare(
192
+ `UPDATE sessions SET message_count = message_count + 1, updated_at = ? WHERE chat_id = ?`
193
+ ).run(Date.now(), sessionKey);
194
+ }
195
+ }
196
+ function getSession(chatId) {
197
+ const db = getDb();
198
+ const sessionKey = `telegram:${chatId}`;
199
+ const row = db.prepare("SELECT * FROM sessions WHERE chat_id = ?").get(sessionKey);
200
+ return row ? rowToSession(row) : null;
201
+ }
202
+ function resetSession(chatId) {
203
+ const oldSession = getSession(chatId);
204
+ const now = Date.now();
205
+ const newSession = {
206
+ sessionId: randomUUID(),
207
+ chatId,
208
+ createdAt: now,
209
+ updatedAt: now,
210
+ messageCount: 0,
211
+ lastChannel: oldSession?.lastChannel || "telegram",
212
+ lastTo: oldSession?.lastTo || chatId,
213
+ contextTokens: oldSession?.contextTokens,
214
+ model: oldSession?.model,
215
+ provider: oldSession?.provider
216
+ };
217
+ const db = getDb();
218
+ const sessionKey = `telegram:${chatId}`;
219
+ db.prepare(
220
+ `
221
+ UPDATE sessions
222
+ SET id = ?, started_at = ?, updated_at = ?, message_count = 0
223
+ WHERE chat_id = ?
224
+ `
225
+ ).run(newSession.sessionId, newSession.createdAt, newSession.updatedAt, sessionKey);
226
+ log.info(`Session reset: ${oldSession?.sessionId} \u2192 ${newSession.sessionId}`);
227
+ return newSession;
228
+ }
229
+ function shouldResetSession(session, policy) {
230
+ const now = Date.now();
231
+ if (policy.daily_reset_enabled) {
232
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
233
+ const lastReset = session.lastResetDate || new Date(session.createdAt).toISOString().split("T")[0];
234
+ if (lastReset !== today) {
235
+ const currentHour = (/* @__PURE__ */ new Date()).getHours();
236
+ const resetHour = policy.daily_reset_hour;
237
+ if (lastReset < today && currentHour >= resetHour) {
238
+ log.info(
239
+ `Daily reset triggered for session ${session.sessionId} (last reset: ${lastReset})`
240
+ );
241
+ return true;
242
+ }
243
+ }
244
+ }
245
+ if (policy.idle_expiry_enabled) {
246
+ const idleMs = now - session.updatedAt;
247
+ const idleMinutes = idleMs / (1e3 * 60);
248
+ const expiryMinutes = policy.idle_expiry_minutes;
249
+ if (idleMinutes >= expiryMinutes) {
250
+ log.info(
251
+ `Idle expiry triggered for session ${session.sessionId} (idle: ${Math.floor(idleMinutes)}m)`
252
+ );
253
+ return true;
254
+ }
255
+ }
256
+ return false;
257
+ }
258
+ function resetSessionWithPolicy(chatId, policy) {
259
+ const newSession = resetSession(chatId);
260
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
261
+ return updateSession(chatId, {
262
+ lastResetDate: today
263
+ });
264
+ }
265
+ function pruneOldSessions(maxAgeDays = 30) {
266
+ try {
267
+ const db = getDb();
268
+ const cutoffMs = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
269
+ const result = db.prepare(`DELETE FROM sessions WHERE updated_at < ? AND updated_at > 0`).run(cutoffMs);
270
+ const pruned = result.changes;
271
+ if (pruned > 0) {
272
+ log.info(`\u{1F5D1}\uFE0F Pruned ${pruned} session(s) older than ${maxAgeDays} days`);
273
+ }
274
+ return pruned;
275
+ } catch (error) {
276
+ log.warn({ err: error }, "Failed to prune old sessions");
277
+ return 0;
278
+ }
279
+ }
280
+
281
+ export {
282
+ loadSessionStore,
283
+ saveSessionStore,
284
+ getOrCreateSession,
285
+ updateSession,
286
+ incrementMessageCount,
287
+ getSession,
288
+ resetSession,
289
+ shouldResetSession,
290
+ resetSessionWithPolicy,
291
+ pruneOldSessions
292
+ };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  COINGECKO_API_URL,
3
3
  tonapiFetch
4
- } from "./chunk-XBKSS6DM.js";
4
+ } from "./chunk-VFA7QMCZ.js";
5
5
  import {
6
6
  TELEGRAM_MAX_MESSAGE_LENGTH
7
- } from "./chunk-RO62LO6Z.js";
7
+ } from "./chunk-YP25WTQK.js";
8
8
  import {
9
9
  fetchWithTimeout
10
- } from "./chunk-VAUJSSD3.js";
10
+ } from "./chunk-XQUHC3JZ.js";
11
11
  import {
12
12
  TELETON_ROOT,
13
13
  WORKSPACE_PATHS,
@@ -17,20 +17,21 @@ import {
17
17
  createLogger
18
18
  } from "./chunk-RCMD3U65.js";
19
19
 
20
- // src/ton/wallet-service.ts
21
- import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
22
- import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
23
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
24
- import { join, dirname } from "path";
25
-
26
20
  // src/ton/endpoint.ts
27
21
  var ENDPOINT_CACHE_TTL_MS = 6e4;
28
22
  var ORBS_HOST = "ton.access.orbs.network";
29
23
  var ORBS_TOPOLOGY_URL = `https://${ORBS_HOST}/mngr/nodes?npm_version=2.3.3`;
30
- var TONCENTER_FALLBACK = `https://toncenter.com/api/v2/jsonRPC`;
24
+ var TONCENTER_URL = `https://toncenter.com/api/v2/jsonRPC`;
31
25
  var _cache = null;
26
+ var _toncenterApiKey;
27
+ function setToncenterApiKey(key) {
28
+ _toncenterApiKey = key;
29
+ }
30
+ function getToncenterApiKey() {
31
+ return _toncenterApiKey;
32
+ }
32
33
  async function discoverOrbsEndpoint() {
33
- const res = await fetch(ORBS_TOPOLOGY_URL);
34
+ const res = await fetch(ORBS_TOPOLOGY_URL, { signal: AbortSignal.timeout(5e3) });
34
35
  const nodes = await res.json();
35
36
  const healthy = nodes.filter(
36
37
  (n) => n.Healthy === "1" && n.Weight > 0 && n.Mngr?.health?.["v2-mainnet"]
@@ -53,10 +54,28 @@ async function getCachedHttpEndpoint() {
53
54
  return _cache.url;
54
55
  }
55
56
  let url;
56
- try {
57
- url = await discoverOrbsEndpoint();
58
- } catch {
59
- url = TONCENTER_FALLBACK;
57
+ if (_toncenterApiKey) {
58
+ try {
59
+ const testUrl = `https://toncenter.com/api/v2/getAddressInformation?address=EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c`;
60
+ const res = await fetch(testUrl, {
61
+ headers: { "X-API-Key": _toncenterApiKey },
62
+ signal: AbortSignal.timeout(5e3)
63
+ });
64
+ if (!res.ok) throw new Error(`TonCenter ${res.status}`);
65
+ url = TONCENTER_URL;
66
+ } catch {
67
+ try {
68
+ url = await discoverOrbsEndpoint();
69
+ } catch {
70
+ url = TONCENTER_URL;
71
+ }
72
+ }
73
+ } else {
74
+ try {
75
+ url = await discoverOrbsEndpoint();
76
+ } catch {
77
+ url = TONCENTER_URL;
78
+ }
60
79
  }
61
80
  _cache = { url, ts: Date.now() };
62
81
  return url;
@@ -66,6 +85,10 @@ function invalidateEndpointCache() {
66
85
  }
67
86
 
68
87
  // src/ton/wallet-service.ts
88
+ import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
89
+ import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
90
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
91
+ import { join, dirname } from "path";
69
92
  var log = createLogger("TON");
70
93
  var WALLET_FILE = join(TELETON_ROOT, "wallet.json");
71
94
  var _walletCache;
@@ -147,7 +170,8 @@ async function getCachedTonClient() {
147
170
  if (_tonClientCache && _tonClientCache.endpoint === endpoint) {
148
171
  return _tonClientCache.client;
149
172
  }
150
- const client = new TonClient({ endpoint });
173
+ const apiKey = getToncenterApiKey();
174
+ const client = new TonClient({ endpoint, ...apiKey && { apiKey } });
151
175
  _tonClientCache = { client, endpoint };
152
176
  return client;
153
177
  }
@@ -217,8 +241,8 @@ async function getTonPrice() {
217
241
 
218
242
  // src/config/schema.ts
219
243
  import { z } from "zod";
220
- var DMPolicy = z.enum(["allowlist", "open", "disabled"]);
221
- var GroupPolicy = z.enum(["open", "allowlist", "disabled"]);
244
+ var DMPolicy = z.enum(["allowlist", "open", "admin-only", "disabled"]);
245
+ var GroupPolicy = z.enum(["open", "allowlist", "admin-only", "disabled"]);
222
246
  var SessionResetPolicySchema = z.object({
223
247
  daily_reset_enabled: z.boolean().default(true).describe("Enable daily session reset"),
224
248
  daily_reset_hour: z.number().min(0).max(23).default(4).describe("Hour of day (0-23) to reset sessions"),
@@ -332,7 +356,7 @@ var _McpObject = z.object({
332
356
  });
333
357
  var McpConfigSchema = _McpObject.default(_McpObject.parse({}));
334
358
  var _ToolRagObject = z.object({
335
- enabled: z.boolean().default(false).describe("Enable semantic tool retrieval (Tool RAG)"),
359
+ enabled: z.boolean().default(true).describe("Enable semantic tool retrieval (Tool RAG)"),
336
360
  top_k: z.number().default(25).describe("Max tools to retrieve per LLM call"),
337
361
  always_include: z.array(z.string()).default([
338
362
  "telegram_send_message",
@@ -346,6 +370,24 @@ var _ToolRagObject = z.object({
346
370
  skip_unlimited_providers: z.boolean().default(false).describe("Skip Tool RAG for providers with no tool limit (e.g. Anthropic)")
347
371
  });
348
372
  var ToolRagConfigSchema = _ToolRagObject.default(_ToolRagObject.parse({}));
373
+ var _ExecLimitsObject = z.object({
374
+ timeout: z.number().min(1).max(3600).default(120).describe("Max seconds per command execution"),
375
+ max_output: z.number().min(1e3).max(5e5).default(5e4).describe("Max chars of stdout/stderr captured per command")
376
+ });
377
+ var _ExecAuditObject = z.object({
378
+ log_commands: z.boolean().default(true).describe("Log every command to SQLite audit table")
379
+ });
380
+ var _ExecObject = z.object({
381
+ mode: z.enum(["off", "yolo"]).default("off").describe("Exec mode: off (disabled) or yolo (full system access)"),
382
+ scope: z.enum(["admin-only", "allowlist", "all"]).default("admin-only").describe("Who can trigger exec tools"),
383
+ allowlist: z.array(z.number()).default([]).describe("Telegram user IDs allowed to use exec (when scope = allowlist)"),
384
+ limits: _ExecLimitsObject.default(_ExecLimitsObject.parse({})),
385
+ audit: _ExecAuditObject.default(_ExecAuditObject.parse({}))
386
+ });
387
+ var _CapabilitiesObject = z.object({
388
+ exec: _ExecObject.default(_ExecObject.parse({}))
389
+ });
390
+ var CapabilitiesConfigSchema = _CapabilitiesObject.default(_CapabilitiesObject.parse({}));
349
391
  var ConfigSchema = z.object({
350
392
  meta: MetaConfigSchema.default(MetaConfigSchema.parse({})),
351
393
  agent: AgentConfigSchema,
@@ -357,12 +399,14 @@ var ConfigSchema = z.object({
357
399
  logging: LoggingConfigSchema,
358
400
  dev: DevConfigSchema,
359
401
  tool_rag: ToolRagConfigSchema,
402
+ capabilities: CapabilitiesConfigSchema,
360
403
  mcp: McpConfigSchema,
361
404
  plugins: z.record(z.string(), z.unknown()).default({}).describe("Per-plugin config (key = plugin name with underscores)"),
362
405
  cocoon: z.object({
363
406
  port: z.number().min(1).max(65535).default(1e4).describe("HTTP port of the cocoon-cli proxy")
364
407
  }).optional().describe("Cocoon Network \u2014 expects external cocoon-cli running on this port"),
365
408
  tonapi_key: z.string().optional().describe("TonAPI key for higher rate limits (from @tonapi_bot)"),
409
+ toncenter_api_key: z.string().optional().describe("TonCenter API key for dedicated RPC endpoint (free at https://toncenter.com)"),
366
410
  tavily_api_key: z.string().optional().describe("Tavily API key for web search & extract (free at https://tavily.com)")
367
411
  });
368
412
 
@@ -464,7 +508,8 @@ export {
464
508
  ensureWorkspace,
465
509
  isNewWorkspace,
466
510
  loadTemplate,
467
- getCachedHttpEndpoint,
511
+ setToncenterApiKey,
512
+ invalidateEndpointCache,
468
513
  generateWallet,
469
514
  saveWallet,
470
515
  loadWallet,
@@ -1,3 +1,7 @@
1
+ import {
2
+ createLogger
3
+ } from "./chunk-RCMD3U65.js";
4
+
1
5
  // src/config/providers.ts
2
6
  var PROVIDER_REGISTRY = {
3
7
  anthropic: {
@@ -154,8 +158,109 @@ function validateApiKeyFormat(provider, key) {
154
158
  return void 0;
155
159
  }
156
160
 
161
+ // src/providers/claude-code-credentials.ts
162
+ import { readFileSync, existsSync } from "fs";
163
+ import { execSync } from "child_process";
164
+ import { homedir } from "os";
165
+ import { join } from "path";
166
+ var log = createLogger("ClaudeCodeCreds");
167
+ var cachedToken = null;
168
+ var cachedExpiresAt = 0;
169
+ function getClaudeConfigDir() {
170
+ return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
171
+ }
172
+ function getCredentialsFilePath() {
173
+ return join(getClaudeConfigDir(), ".credentials.json");
174
+ }
175
+ function readCredentialsFile() {
176
+ const filePath = getCredentialsFilePath();
177
+ if (!existsSync(filePath)) return null;
178
+ try {
179
+ const raw = readFileSync(filePath, "utf-8");
180
+ return JSON.parse(raw);
181
+ } catch (e) {
182
+ log.warn({ err: e, path: filePath }, "Failed to parse Claude Code credentials file");
183
+ return null;
184
+ }
185
+ }
186
+ function readKeychainCredentials() {
187
+ const serviceNames = ["Claude Code-credentials", "Claude Code"];
188
+ for (const service of serviceNames) {
189
+ try {
190
+ const raw = execSync(`security find-generic-password -s "${service}" -w`, {
191
+ encoding: "utf-8",
192
+ stdio: ["pipe", "pipe", "pipe"]
193
+ }).trim();
194
+ return JSON.parse(raw);
195
+ } catch {
196
+ }
197
+ }
198
+ return null;
199
+ }
200
+ function readCredentials() {
201
+ if (process.platform === "darwin") {
202
+ const keychainCreds = readKeychainCredentials();
203
+ if (keychainCreds) return keychainCreds;
204
+ log.debug("Keychain read failed, falling back to credentials file");
205
+ }
206
+ return readCredentialsFile();
207
+ }
208
+ function extractToken(creds) {
209
+ const oauth = creds.claudeAiOauth;
210
+ if (!oauth?.accessToken) {
211
+ log.warn("Claude Code credentials found but missing accessToken");
212
+ return null;
213
+ }
214
+ return {
215
+ token: oauth.accessToken,
216
+ expiresAt: oauth.expiresAt ?? 0
217
+ };
218
+ }
219
+ function getClaudeCodeApiKey(fallbackKey) {
220
+ if (cachedToken && Date.now() < cachedExpiresAt) {
221
+ return cachedToken;
222
+ }
223
+ const creds = readCredentials();
224
+ if (creds) {
225
+ const extracted = extractToken(creds);
226
+ if (extracted) {
227
+ cachedToken = extracted.token;
228
+ cachedExpiresAt = extracted.expiresAt;
229
+ log.debug("Claude Code credentials loaded successfully");
230
+ return cachedToken;
231
+ }
232
+ }
233
+ if (fallbackKey && fallbackKey.length > 0) {
234
+ log.warn("Claude Code credentials not found, using fallback api_key from config");
235
+ return fallbackKey;
236
+ }
237
+ throw new Error("No Claude Code credentials found. Run 'claude login' or set api_key in config.");
238
+ }
239
+ function refreshClaudeCodeApiKey() {
240
+ cachedToken = null;
241
+ cachedExpiresAt = 0;
242
+ const creds = readCredentials();
243
+ if (creds) {
244
+ const extracted = extractToken(creds);
245
+ if (extracted) {
246
+ cachedToken = extracted.token;
247
+ cachedExpiresAt = extracted.expiresAt;
248
+ log.info("Claude Code credentials refreshed from disk");
249
+ return cachedToken;
250
+ }
251
+ }
252
+ log.warn("Failed to refresh Claude Code credentials from disk");
253
+ return null;
254
+ }
255
+ function isClaudeCodeTokenValid() {
256
+ return cachedToken !== null && Date.now() < cachedExpiresAt;
257
+ }
258
+
157
259
  export {
158
260
  getProviderMetadata,
159
261
  getSupportedProviders,
160
- validateApiKeyFormat
262
+ validateApiKeyFormat,
263
+ getClaudeCodeApiKey,
264
+ refreshClaudeCodeApiKey,
265
+ isClaudeCodeTokenValid
161
266
  };
@@ -1,17 +1,15 @@
1
1
  import {
2
2
  getClaudeCodeApiKey,
3
+ getProviderMetadata,
3
4
  refreshClaudeCodeApiKey
4
- } from "./chunk-JQDLW7IE.js";
5
- import {
6
- getProviderMetadata
7
- } from "./chunk-RMLQS3X6.js";
5
+ } from "./chunk-CB2Y45HA.js";
8
6
  import {
9
7
  appendToTranscript,
10
8
  readTranscript
11
9
  } from "./chunk-OCLG5GKI.js";
12
10
  import {
13
11
  fetchWithTimeout
14
- } from "./chunk-VAUJSSD3.js";
12
+ } from "./chunk-XQUHC3JZ.js";
15
13
  import {
16
14
  createLogger
17
15
  } from "./chunk-RCMD3U65.js";
@@ -306,7 +304,8 @@ async function chatWithContext(config, options) {
306
304
  apiKey: getEffectiveApiKey(provider, config.api_key),
307
305
  maxTokens: options.maxTokens ?? config.max_tokens,
308
306
  temperature,
309
- sessionId: options.sessionId
307
+ sessionId: options.sessionId,
308
+ cacheRetention: "long"
310
309
  };
311
310
  if (isCocoon) {
312
311
  const { stripCocoonPayload } = await import("./tool-adapter-Y3TCEQOC.js");