codeep 1.0.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/LICENSE +201 -0
- package/README.md +576 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.js +421 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +1406 -0
- package/dist/components/AgentProgress.d.ts +33 -0
- package/dist/components/AgentProgress.js +97 -0
- package/dist/components/Export.d.ts +8 -0
- package/dist/components/Export.js +27 -0
- package/dist/components/Help.d.ts +2 -0
- package/dist/components/Help.js +3 -0
- package/dist/components/Input.d.ts +9 -0
- package/dist/components/Input.js +89 -0
- package/dist/components/Loading.d.ts +9 -0
- package/dist/components/Loading.js +31 -0
- package/dist/components/Login.d.ts +7 -0
- package/dist/components/Login.js +77 -0
- package/dist/components/Logo.d.ts +8 -0
- package/dist/components/Logo.js +89 -0
- package/dist/components/LogoutPicker.d.ts +8 -0
- package/dist/components/LogoutPicker.js +61 -0
- package/dist/components/Message.d.ts +10 -0
- package/dist/components/Message.js +234 -0
- package/dist/components/MessageList.d.ts +10 -0
- package/dist/components/MessageList.js +8 -0
- package/dist/components/ProjectPermission.d.ts +7 -0
- package/dist/components/ProjectPermission.js +52 -0
- package/dist/components/Search.d.ts +10 -0
- package/dist/components/Search.js +30 -0
- package/dist/components/SessionPicker.d.ts +9 -0
- package/dist/components/SessionPicker.js +88 -0
- package/dist/components/Sessions.d.ts +12 -0
- package/dist/components/Sessions.js +102 -0
- package/dist/components/Settings.d.ts +7 -0
- package/dist/components/Settings.js +162 -0
- package/dist/components/Status.d.ts +2 -0
- package/dist/components/Status.js +12 -0
- package/dist/config/config.test.d.ts +1 -0
- package/dist/config/config.test.js +157 -0
- package/dist/config/index.d.ts +121 -0
- package/dist/config/index.js +555 -0
- package/dist/config/providers.d.ts +43 -0
- package/dist/config/providers.js +82 -0
- package/dist/config/providers.test.d.ts +1 -0
- package/dist/config/providers.test.js +132 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +38 -0
- package/dist/utils/agent.d.ts +37 -0
- package/dist/utils/agent.js +627 -0
- package/dist/utils/codeReview.d.ts +36 -0
- package/dist/utils/codeReview.js +390 -0
- package/dist/utils/context.d.ts +49 -0
- package/dist/utils/context.js +216 -0
- package/dist/utils/diffPreview.d.ts +57 -0
- package/dist/utils/diffPreview.js +335 -0
- package/dist/utils/export.d.ts +19 -0
- package/dist/utils/export.js +94 -0
- package/dist/utils/git.d.ts +85 -0
- package/dist/utils/git.js +399 -0
- package/dist/utils/git.test.d.ts +1 -0
- package/dist/utils/git.test.js +193 -0
- package/dist/utils/history.d.ts +93 -0
- package/dist/utils/history.js +348 -0
- package/dist/utils/interactive.d.ts +34 -0
- package/dist/utils/interactive.js +206 -0
- package/dist/utils/keychain.d.ts +17 -0
- package/dist/utils/keychain.js +160 -0
- package/dist/utils/learning.d.ts +89 -0
- package/dist/utils/learning.js +330 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.js +130 -0
- package/dist/utils/project.d.ts +86 -0
- package/dist/utils/project.js +415 -0
- package/dist/utils/project.test.d.ts +1 -0
- package/dist/utils/project.test.js +212 -0
- package/dist/utils/ratelimit.d.ts +26 -0
- package/dist/utils/ratelimit.js +132 -0
- package/dist/utils/ratelimit.test.d.ts +1 -0
- package/dist/utils/ratelimit.test.js +131 -0
- package/dist/utils/retry.d.ts +28 -0
- package/dist/utils/retry.js +109 -0
- package/dist/utils/retry.test.d.ts +1 -0
- package/dist/utils/retry.test.js +163 -0
- package/dist/utils/search.d.ts +11 -0
- package/dist/utils/search.js +29 -0
- package/dist/utils/shell.d.ts +45 -0
- package/dist/utils/shell.js +242 -0
- package/dist/utils/skills.d.ts +144 -0
- package/dist/utils/skills.js +1137 -0
- package/dist/utils/smartContext.d.ts +29 -0
- package/dist/utils/smartContext.js +441 -0
- package/dist/utils/tools.d.ts +224 -0
- package/dist/utils/tools.js +731 -0
- package/dist/utils/update.d.ts +22 -0
- package/dist/utils/update.js +128 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.js +141 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +164 -0
- package/dist/utils/verify.d.ts +78 -0
- package/dist/utils/verify.js +464 -0
- package/package.json +68 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, statSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { PROVIDERS, getProvider } from './providers';
|
|
6
|
+
import { logSession } from '../utils/logger';
|
|
7
|
+
// Global sessions directory (fallback when not in a project)
|
|
8
|
+
const GLOBAL_SESSIONS_DIR = join(homedir(), '.codeep', 'sessions');
|
|
9
|
+
// Ensure global sessions directory exists
|
|
10
|
+
if (!existsSync(GLOBAL_SESSIONS_DIR)) {
|
|
11
|
+
mkdirSync(GLOBAL_SESSIONS_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get sessions directory - local .codeep/sessions/ if in project, otherwise global
|
|
15
|
+
*/
|
|
16
|
+
function getSessionsDir(projectPath) {
|
|
17
|
+
if (projectPath && isProjectDirectory(projectPath)) {
|
|
18
|
+
const localDir = join(projectPath, '.codeep', 'sessions');
|
|
19
|
+
if (!existsSync(localDir)) {
|
|
20
|
+
mkdirSync(localDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
return localDir;
|
|
23
|
+
}
|
|
24
|
+
return GLOBAL_SESSIONS_DIR;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get local project config path
|
|
28
|
+
*/
|
|
29
|
+
function getLocalConfigPath(projectPath) {
|
|
30
|
+
if (!isProjectDirectory(projectPath))
|
|
31
|
+
return null;
|
|
32
|
+
const configDir = join(projectPath, '.codeep');
|
|
33
|
+
if (!existsSync(configDir)) {
|
|
34
|
+
mkdirSync(configDir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
return join(configDir, 'config.json');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if directory is a project (has package.json)
|
|
40
|
+
*/
|
|
41
|
+
function isProjectDirectory(path) {
|
|
42
|
+
return existsSync(join(path, 'package.json'));
|
|
43
|
+
}
|
|
44
|
+
export const config = new Conf({
|
|
45
|
+
projectName: 'codeep',
|
|
46
|
+
defaults: {
|
|
47
|
+
apiKey: '',
|
|
48
|
+
provider: 'z.ai',
|
|
49
|
+
model: 'glm-4.7',
|
|
50
|
+
agentMode: 'auto',
|
|
51
|
+
agentConfirmation: 'dangerous', // Confirm only dangerous actions by default
|
|
52
|
+
agentAutoCommit: false,
|
|
53
|
+
agentAutoCommitBranch: false,
|
|
54
|
+
agentAutoVerify: true, // Auto-verify by default
|
|
55
|
+
agentMaxFixAttempts: 3,
|
|
56
|
+
protocol: 'openai',
|
|
57
|
+
plan: 'lite',
|
|
58
|
+
language: 'en',
|
|
59
|
+
autoSave: true,
|
|
60
|
+
currentSessionId: '',
|
|
61
|
+
temperature: 0.7,
|
|
62
|
+
maxTokens: 4096,
|
|
63
|
+
apiTimeout: 30000,
|
|
64
|
+
rateLimitApi: 30, // 30 requests per minute
|
|
65
|
+
rateLimitCommands: 100, // 100 commands per minute
|
|
66
|
+
projectPermissions: [],
|
|
67
|
+
providerApiKeys: [],
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
// In-memory cache for API keys (populated on first access)
|
|
71
|
+
const apiKeyCache = new Map();
|
|
72
|
+
export const LANGUAGES = {
|
|
73
|
+
'auto': 'Auto-detect',
|
|
74
|
+
'en': 'English',
|
|
75
|
+
'zh': 'Chinese (中文)',
|
|
76
|
+
'es': 'Spanish (Español)',
|
|
77
|
+
'hi': 'Hindi (हिन्दी)',
|
|
78
|
+
'ar': 'Arabic (العربية)',
|
|
79
|
+
'pt': 'Portuguese (Português)',
|
|
80
|
+
'fr': 'French (Français)',
|
|
81
|
+
'de': 'German (Deutsch)',
|
|
82
|
+
'ja': 'Japanese (日本語)',
|
|
83
|
+
'ru': 'Russian (Русский)',
|
|
84
|
+
'hr': 'Croatian (Hrvatski)',
|
|
85
|
+
};
|
|
86
|
+
export const PROTOCOLS = {
|
|
87
|
+
'openai': 'OpenAI Compatible',
|
|
88
|
+
'anthropic': 'Anthropic Protocol',
|
|
89
|
+
};
|
|
90
|
+
// Get API key for current or specified provider
|
|
91
|
+
/**
|
|
92
|
+
* Load API key from config into cache
|
|
93
|
+
*/
|
|
94
|
+
export async function loadApiKey(providerId) {
|
|
95
|
+
const provider = providerId || config.get('provider');
|
|
96
|
+
const providerConfig = getProvider(provider);
|
|
97
|
+
// Check environment variable first
|
|
98
|
+
if (providerConfig?.envKey) {
|
|
99
|
+
const envKey = process.env[providerConfig.envKey];
|
|
100
|
+
if (envKey) {
|
|
101
|
+
apiKeyCache.set(provider, envKey);
|
|
102
|
+
return envKey;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Legacy env vars for z.ai
|
|
106
|
+
if (provider === 'z.ai') {
|
|
107
|
+
if (process.env.ZAI_API_KEY) {
|
|
108
|
+
apiKeyCache.set(provider, process.env.ZAI_API_KEY);
|
|
109
|
+
return process.env.ZAI_API_KEY;
|
|
110
|
+
}
|
|
111
|
+
if (process.env.ZHIPUAI_API_KEY) {
|
|
112
|
+
apiKeyCache.set(provider, process.env.ZHIPUAI_API_KEY);
|
|
113
|
+
return process.env.ZHIPUAI_API_KEY;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check config file
|
|
117
|
+
const providerKeys = config.get('providerApiKeys') || [];
|
|
118
|
+
const stored = providerKeys.find(k => k.providerId === provider);
|
|
119
|
+
if (stored?.apiKey) {
|
|
120
|
+
apiKeyCache.set(provider, stored.apiKey);
|
|
121
|
+
return stored.apiKey;
|
|
122
|
+
}
|
|
123
|
+
// Fallback to legacy apiKey field (for z.ai)
|
|
124
|
+
if (provider === 'z.ai') {
|
|
125
|
+
const legacyKey = config.get('apiKey') || '';
|
|
126
|
+
if (legacyKey) {
|
|
127
|
+
apiKeyCache.set(provider, legacyKey);
|
|
128
|
+
return legacyKey;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Load API keys for ALL providers into cache
|
|
135
|
+
* Should be called at app startup
|
|
136
|
+
*/
|
|
137
|
+
export async function loadAllApiKeys() {
|
|
138
|
+
// Load keys for all configured providers from providerApiKeys
|
|
139
|
+
const providerKeys = config.get('providerApiKeys') || [];
|
|
140
|
+
for (const { providerId, apiKey } of providerKeys) {
|
|
141
|
+
if (apiKey) {
|
|
142
|
+
apiKeyCache.set(providerId, apiKey);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Also check environment variables for each provider
|
|
146
|
+
for (const [providerId, providerConfig] of Object.entries(PROVIDERS)) {
|
|
147
|
+
if (providerConfig.envKey) {
|
|
148
|
+
const envKey = process.env[providerConfig.envKey];
|
|
149
|
+
if (envKey) {
|
|
150
|
+
apiKeyCache.set(providerId, envKey);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Legacy env vars for z.ai
|
|
155
|
+
if (!apiKeyCache.get('z.ai')) {
|
|
156
|
+
if (process.env.ZAI_API_KEY) {
|
|
157
|
+
apiKeyCache.set('z.ai', process.env.ZAI_API_KEY);
|
|
158
|
+
}
|
|
159
|
+
else if (process.env.ZHIPUAI_API_KEY) {
|
|
160
|
+
apiKeyCache.set('z.ai', process.env.ZHIPUAI_API_KEY);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Fallback to legacy apiKey field
|
|
164
|
+
const legacyKey = config.get('apiKey') || '';
|
|
165
|
+
if (legacyKey) {
|
|
166
|
+
apiKeyCache.set('z.ai', legacyKey);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get API key synchronously from cache (must call loadAllApiKeys first)
|
|
173
|
+
*/
|
|
174
|
+
export function getApiKey(providerId) {
|
|
175
|
+
const provider = providerId || config.get('provider');
|
|
176
|
+
return apiKeyCache.get(provider) || '';
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Set API key - stores in config file
|
|
180
|
+
*/
|
|
181
|
+
export function setApiKey(key, providerId) {
|
|
182
|
+
const provider = providerId || config.get('provider');
|
|
183
|
+
// Update cache immediately
|
|
184
|
+
apiKeyCache.set(provider, key);
|
|
185
|
+
// Store in config
|
|
186
|
+
const providerKeys = config.get('providerApiKeys') || [];
|
|
187
|
+
const existing = providerKeys.findIndex(k => k.providerId === provider);
|
|
188
|
+
if (existing >= 0) {
|
|
189
|
+
providerKeys[existing].apiKey = key;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
providerKeys.push({ providerId: provider, apiKey: key });
|
|
193
|
+
}
|
|
194
|
+
config.set('providerApiKeys', providerKeys);
|
|
195
|
+
// Also set legacy field for backwards compatibility (z.ai only)
|
|
196
|
+
if (provider === 'z.ai') {
|
|
197
|
+
config.set('apiKey', key);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function getMaskedApiKey(providerId) {
|
|
201
|
+
const key = getApiKey(providerId);
|
|
202
|
+
if (key.length > 4) {
|
|
203
|
+
return '*'.repeat(key.length - 4) + key.slice(-4);
|
|
204
|
+
}
|
|
205
|
+
return key;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get list of providers that have API keys configured
|
|
209
|
+
*/
|
|
210
|
+
export function getConfiguredProviders() {
|
|
211
|
+
const providerKeys = config.get('providerApiKeys') || [];
|
|
212
|
+
const configured = [];
|
|
213
|
+
for (const pk of providerKeys) {
|
|
214
|
+
if (pk.apiKey && pk.apiKey.length > 0) {
|
|
215
|
+
const provider = getProvider(pk.providerId);
|
|
216
|
+
configured.push({
|
|
217
|
+
id: pk.providerId,
|
|
218
|
+
name: provider?.name || pk.providerId,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return configured;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Clear API key for a specific provider
|
|
226
|
+
*/
|
|
227
|
+
export function clearApiKey(providerId) {
|
|
228
|
+
// Clear from cache
|
|
229
|
+
apiKeyCache.delete(providerId);
|
|
230
|
+
// Clear from config
|
|
231
|
+
const providerKeys = config.get('providerApiKeys') || [];
|
|
232
|
+
const filtered = providerKeys.filter(k => k.providerId !== providerId);
|
|
233
|
+
config.set('providerApiKeys', filtered);
|
|
234
|
+
// Clear legacy field if z.ai
|
|
235
|
+
if (providerId === 'z.ai') {
|
|
236
|
+
config.set('apiKey', '');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
export async function isConfiguredAsync(providerId) {
|
|
240
|
+
const key = await loadApiKey(providerId);
|
|
241
|
+
return Boolean(key);
|
|
242
|
+
}
|
|
243
|
+
export function isConfigured(providerId) {
|
|
244
|
+
return Boolean(getApiKey(providerId));
|
|
245
|
+
}
|
|
246
|
+
// Get current provider info
|
|
247
|
+
export function getCurrentProvider() {
|
|
248
|
+
const providerId = config.get('provider');
|
|
249
|
+
const provider = getProvider(providerId);
|
|
250
|
+
return {
|
|
251
|
+
id: providerId,
|
|
252
|
+
name: provider?.name || providerId,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// Set provider and update model/protocol to defaults
|
|
256
|
+
export function setProvider(providerId) {
|
|
257
|
+
const provider = getProvider(providerId);
|
|
258
|
+
if (!provider)
|
|
259
|
+
return false;
|
|
260
|
+
config.set('provider', providerId);
|
|
261
|
+
config.set('model', provider.defaultModel);
|
|
262
|
+
config.set('protocol', provider.defaultProtocol);
|
|
263
|
+
// Load API key for the new provider into cache
|
|
264
|
+
// This is async but we fire-and-forget since the key will be loaded before next API call
|
|
265
|
+
loadApiKey(providerId);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
// Get models for current provider
|
|
269
|
+
export function getModelsForCurrentProvider() {
|
|
270
|
+
const providerId = config.get('provider');
|
|
271
|
+
const provider = getProvider(providerId);
|
|
272
|
+
if (!provider)
|
|
273
|
+
return {};
|
|
274
|
+
const models = {};
|
|
275
|
+
for (const model of provider.models) {
|
|
276
|
+
models[model.id] = `${model.name} - ${model.description}`;
|
|
277
|
+
}
|
|
278
|
+
return models;
|
|
279
|
+
}
|
|
280
|
+
// Re-export PROVIDERS for convenience
|
|
281
|
+
export { PROVIDERS } from './providers';
|
|
282
|
+
// Generate unique session ID
|
|
283
|
+
function generateSessionId() {
|
|
284
|
+
const now = new Date();
|
|
285
|
+
const date = now.toISOString().split('T')[0];
|
|
286
|
+
const time = now.toTimeString().split(' ')[0].replace(/:/g, '-');
|
|
287
|
+
return `session-${date}-${time}`;
|
|
288
|
+
}
|
|
289
|
+
// Get or create current session ID
|
|
290
|
+
export function getCurrentSessionId() {
|
|
291
|
+
let sessionId = config.get('currentSessionId');
|
|
292
|
+
if (!sessionId) {
|
|
293
|
+
sessionId = generateSessionId();
|
|
294
|
+
config.set('currentSessionId', sessionId);
|
|
295
|
+
}
|
|
296
|
+
return sessionId;
|
|
297
|
+
}
|
|
298
|
+
// Start new session
|
|
299
|
+
export function startNewSession() {
|
|
300
|
+
const sessionId = generateSessionId();
|
|
301
|
+
config.set('currentSessionId', sessionId);
|
|
302
|
+
return sessionId;
|
|
303
|
+
}
|
|
304
|
+
// Auto-save debounce state
|
|
305
|
+
let autoSaveTimeout = null;
|
|
306
|
+
let pendingAutoSave = null;
|
|
307
|
+
// Auto-save current session (debounced - saves max every 5 seconds)
|
|
308
|
+
export function autoSaveSession(history, projectPath) {
|
|
309
|
+
if (!config.get('autoSave') || history.length === 0) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
// Store pending save data
|
|
313
|
+
pendingAutoSave = { history, projectPath };
|
|
314
|
+
// If already scheduled, don't reschedule
|
|
315
|
+
if (autoSaveTimeout) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
// Schedule save after 5 seconds
|
|
319
|
+
autoSaveTimeout = setTimeout(() => {
|
|
320
|
+
if (pendingAutoSave) {
|
|
321
|
+
const sessionId = getCurrentSessionId();
|
|
322
|
+
saveSession(sessionId, pendingAutoSave.history, pendingAutoSave.projectPath);
|
|
323
|
+
pendingAutoSave = null;
|
|
324
|
+
}
|
|
325
|
+
autoSaveTimeout = null;
|
|
326
|
+
}, 5000);
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
// Force immediate save (for explicit save commands)
|
|
330
|
+
export function flushAutoSave() {
|
|
331
|
+
if (autoSaveTimeout) {
|
|
332
|
+
clearTimeout(autoSaveTimeout);
|
|
333
|
+
autoSaveTimeout = null;
|
|
334
|
+
}
|
|
335
|
+
if (pendingAutoSave) {
|
|
336
|
+
const sessionId = getCurrentSessionId();
|
|
337
|
+
const result = saveSession(sessionId, pendingAutoSave.history, pendingAutoSave.projectPath);
|
|
338
|
+
pendingAutoSave = null;
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
// Session management
|
|
344
|
+
export function saveSession(name, history, projectPath) {
|
|
345
|
+
try {
|
|
346
|
+
const session = {
|
|
347
|
+
name,
|
|
348
|
+
history,
|
|
349
|
+
createdAt: new Date().toISOString(),
|
|
350
|
+
};
|
|
351
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
352
|
+
const filePath = join(sessionsDir, `${name}.json`);
|
|
353
|
+
writeFileSync(filePath, JSON.stringify(session, null, 2));
|
|
354
|
+
logSession('save', name, true);
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
logSession('save', name, false);
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
export function loadSession(name, projectPath) {
|
|
363
|
+
try {
|
|
364
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
365
|
+
const filePath = join(sessionsDir, `${name}.json`);
|
|
366
|
+
if (existsSync(filePath)) {
|
|
367
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
368
|
+
logSession('load', name, true);
|
|
369
|
+
return data.history;
|
|
370
|
+
}
|
|
371
|
+
logSession('load', name, false);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
logSession('load', name, false);
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
export function listSessions(projectPath) {
|
|
380
|
+
try {
|
|
381
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
382
|
+
return readdirSync(sessionsDir)
|
|
383
|
+
.filter(f => f.endsWith('.json'))
|
|
384
|
+
.map(f => f.replace('.json', ''))
|
|
385
|
+
.sort();
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
export function deleteSession(name, projectPath) {
|
|
392
|
+
try {
|
|
393
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
394
|
+
const filePath = join(sessionsDir, `${name}.json`);
|
|
395
|
+
if (existsSync(filePath)) {
|
|
396
|
+
unlinkSync(filePath);
|
|
397
|
+
logSession('delete', name, true);
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
logSession('delete', name, false);
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
logSession('delete', name, false);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
export function renameSession(oldName, newName, projectPath) {
|
|
409
|
+
try {
|
|
410
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
411
|
+
const oldPath = join(sessionsDir, `${oldName}.json`);
|
|
412
|
+
const newPath = join(sessionsDir, `${newName}.json`);
|
|
413
|
+
if (!existsSync(oldPath)) {
|
|
414
|
+
logSession('rename', `${oldName} -> ${newName}`, false);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
// Read existing session
|
|
418
|
+
const data = JSON.parse(readFileSync(oldPath, 'utf-8'));
|
|
419
|
+
// Update name and save to new path
|
|
420
|
+
data.name = newName;
|
|
421
|
+
writeFileSync(newPath, JSON.stringify(data, null, 2));
|
|
422
|
+
// Delete old file
|
|
423
|
+
unlinkSync(oldPath);
|
|
424
|
+
// Update current session ID if it was the renamed one
|
|
425
|
+
if (config.get('currentSessionId') === oldName) {
|
|
426
|
+
config.set('currentSessionId', newName);
|
|
427
|
+
}
|
|
428
|
+
logSession('rename', `${oldName} -> ${newName}`, true);
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
logSession('rename', `${oldName} -> ${newName}`, false);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
export function getSessionInfo(name, projectPath) {
|
|
437
|
+
try {
|
|
438
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
439
|
+
const filePath = join(sessionsDir, `${name}.json`);
|
|
440
|
+
if (existsSync(filePath)) {
|
|
441
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
442
|
+
return {
|
|
443
|
+
name: data.name,
|
|
444
|
+
createdAt: data.createdAt,
|
|
445
|
+
messageCount: data.history.length,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* List all sessions with metadata, sorted by date (newest first)
|
|
456
|
+
*/
|
|
457
|
+
export function listSessionsWithInfo(projectPath) {
|
|
458
|
+
try {
|
|
459
|
+
const sessionsDir = getSessionsDir(projectPath);
|
|
460
|
+
const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));
|
|
461
|
+
const sessions = [];
|
|
462
|
+
for (const file of files) {
|
|
463
|
+
const filePath = join(sessionsDir, file);
|
|
464
|
+
try {
|
|
465
|
+
const stat = statSync(filePath);
|
|
466
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
467
|
+
sessions.push({
|
|
468
|
+
name: data.name || file.replace('.json', ''),
|
|
469
|
+
createdAt: data.createdAt || stat.mtime.toISOString(),
|
|
470
|
+
messageCount: data.history?.length || 0,
|
|
471
|
+
fileSize: stat.size,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
// Skip invalid session files
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Sort by date, newest first
|
|
479
|
+
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
return [];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// Project permission management
|
|
486
|
+
/**
|
|
487
|
+
* Get project permission from local .codeep/config.json
|
|
488
|
+
*/
|
|
489
|
+
export function getProjectPermission(projectPath) {
|
|
490
|
+
const configPath = getLocalConfigPath(projectPath);
|
|
491
|
+
if (!configPath || !existsSync(configPath)) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const data = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
496
|
+
return data.permission || null;
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Set project permission in local .codeep/config.json
|
|
504
|
+
*/
|
|
505
|
+
export function setProjectPermission(projectPath, read, write) {
|
|
506
|
+
const configPath = getLocalConfigPath(projectPath);
|
|
507
|
+
if (!configPath)
|
|
508
|
+
return;
|
|
509
|
+
const permission = {
|
|
510
|
+
path: projectPath,
|
|
511
|
+
readPermission: read,
|
|
512
|
+
writePermission: write,
|
|
513
|
+
grantedAt: new Date().toISOString(),
|
|
514
|
+
};
|
|
515
|
+
let data = {};
|
|
516
|
+
if (existsSync(configPath)) {
|
|
517
|
+
try {
|
|
518
|
+
data = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
// Invalid JSON, start fresh
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
data.permission = permission;
|
|
525
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2));
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Remove project permission from local .codeep/config.json
|
|
529
|
+
*/
|
|
530
|
+
export function removeProjectPermission(projectPath) {
|
|
531
|
+
const configPath = getLocalConfigPath(projectPath);
|
|
532
|
+
if (!configPath || !existsSync(configPath)) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const data = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
537
|
+
if (data.permission) {
|
|
538
|
+
delete data.permission;
|
|
539
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2));
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
// Ignore errors
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
export function hasReadPermission(projectPath) {
|
|
549
|
+
const perm = getProjectPermission(projectPath);
|
|
550
|
+
return perm?.readPermission === true;
|
|
551
|
+
}
|
|
552
|
+
export function hasWritePermission(projectPath) {
|
|
553
|
+
const perm = getProjectPermission(projectPath);
|
|
554
|
+
return perm?.writePermission === true;
|
|
555
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider configurations for different AI services
|
|
3
|
+
*/
|
|
4
|
+
export interface ProviderConfig {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
protocols: {
|
|
8
|
+
openai?: {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
authHeader: 'Bearer' | 'x-api-key';
|
|
11
|
+
supportsNativeTools?: boolean;
|
|
12
|
+
};
|
|
13
|
+
anthropic?: {
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
authHeader: 'Bearer' | 'x-api-key';
|
|
16
|
+
supportsNativeTools?: boolean;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
models: {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
}[];
|
|
24
|
+
defaultModel: string;
|
|
25
|
+
defaultProtocol: 'openai' | 'anthropic';
|
|
26
|
+
envKey?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const PROVIDERS: Record<string, ProviderConfig>;
|
|
29
|
+
export type ProviderId = keyof typeof PROVIDERS;
|
|
30
|
+
export declare function getProvider(id: string): ProviderConfig | null;
|
|
31
|
+
export declare function getProviderList(): {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
}[];
|
|
36
|
+
export declare function getProviderModels(providerId: string): {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
}[];
|
|
41
|
+
export declare function getProviderBaseUrl(providerId: string, protocol: 'openai' | 'anthropic'): string | null;
|
|
42
|
+
export declare function getProviderAuthHeader(providerId: string, protocol: 'openai' | 'anthropic'): 'Bearer' | 'x-api-key';
|
|
43
|
+
export declare function supportsNativeTools(providerId: string, protocol: 'openai' | 'anthropic'): boolean;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider configurations for different AI services
|
|
3
|
+
*/
|
|
4
|
+
export const PROVIDERS = {
|
|
5
|
+
'z.ai': {
|
|
6
|
+
name: 'Z.AI (ZhipuAI)',
|
|
7
|
+
description: 'GLM Coding Plan',
|
|
8
|
+
protocols: {
|
|
9
|
+
openai: {
|
|
10
|
+
baseUrl: 'https://api.z.ai/api/coding/paas/v4',
|
|
11
|
+
authHeader: 'Bearer',
|
|
12
|
+
supportsNativeTools: true,
|
|
13
|
+
},
|
|
14
|
+
anthropic: {
|
|
15
|
+
baseUrl: 'https://api.z.ai/api/anthropic',
|
|
16
|
+
authHeader: 'x-api-key',
|
|
17
|
+
supportsNativeTools: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
models: [
|
|
21
|
+
{ id: 'glm-4.7', name: 'GLM-4.7', description: 'Latest GLM model' },
|
|
22
|
+
{ id: 'glm-4.7-flash', name: 'GLM-4.7 Flash', description: 'Faster, lighter version' },
|
|
23
|
+
],
|
|
24
|
+
defaultModel: 'glm-4.7',
|
|
25
|
+
defaultProtocol: 'openai',
|
|
26
|
+
envKey: 'ZAI_API_KEY',
|
|
27
|
+
},
|
|
28
|
+
'minimax': {
|
|
29
|
+
name: 'MiniMax',
|
|
30
|
+
description: 'MiniMax Coding Plan',
|
|
31
|
+
protocols: {
|
|
32
|
+
openai: {
|
|
33
|
+
baseUrl: 'https://api.minimax.io/v1',
|
|
34
|
+
authHeader: 'Bearer',
|
|
35
|
+
supportsNativeTools: true,
|
|
36
|
+
},
|
|
37
|
+
anthropic: {
|
|
38
|
+
baseUrl: 'https://api.minimax.io/anthropic',
|
|
39
|
+
authHeader: 'x-api-key',
|
|
40
|
+
supportsNativeTools: false, // MiniMax Anthropic doesn't support native tools properly
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
models: [
|
|
44
|
+
{ id: 'MiniMax-M2.1', name: 'MiniMax M2.1', description: 'Latest MiniMax coding model' },
|
|
45
|
+
],
|
|
46
|
+
defaultModel: 'MiniMax-M2.1',
|
|
47
|
+
defaultProtocol: 'anthropic',
|
|
48
|
+
envKey: 'MINIMAX_API_KEY',
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export function getProvider(id) {
|
|
52
|
+
return PROVIDERS[id] || null;
|
|
53
|
+
}
|
|
54
|
+
export function getProviderList() {
|
|
55
|
+
return Object.entries(PROVIDERS).map(([id, config]) => ({
|
|
56
|
+
id,
|
|
57
|
+
name: config.name,
|
|
58
|
+
description: config.description,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
export function getProviderModels(providerId) {
|
|
62
|
+
const provider = PROVIDERS[providerId];
|
|
63
|
+
return provider ? provider.models : [];
|
|
64
|
+
}
|
|
65
|
+
export function getProviderBaseUrl(providerId, protocol) {
|
|
66
|
+
const provider = PROVIDERS[providerId];
|
|
67
|
+
if (!provider)
|
|
68
|
+
return null;
|
|
69
|
+
return provider.protocols[protocol]?.baseUrl || null;
|
|
70
|
+
}
|
|
71
|
+
export function getProviderAuthHeader(providerId, protocol) {
|
|
72
|
+
const provider = PROVIDERS[providerId];
|
|
73
|
+
if (!provider)
|
|
74
|
+
return 'Bearer';
|
|
75
|
+
return provider.protocols[protocol]?.authHeader || 'Bearer';
|
|
76
|
+
}
|
|
77
|
+
export function supportsNativeTools(providerId, protocol) {
|
|
78
|
+
const provider = PROVIDERS[providerId];
|
|
79
|
+
if (!provider)
|
|
80
|
+
return false;
|
|
81
|
+
return provider.protocols[protocol]?.supportsNativeTools ?? true; // Default to true
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|