@xagent-ai/cli 1.2.0 → 1.2.2
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 +1 -1
- package/README_CN.md +1 -1
- package/dist/agents.js +164 -164
- package/dist/agents.js.map +1 -1
- package/dist/ai-client.d.ts +4 -6
- package/dist/ai-client.d.ts.map +1 -1
- package/dist/ai-client.js +137 -115
- package/dist/ai-client.js.map +1 -1
- package/dist/auth.js +4 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +184 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +65 -81
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +5 -31
- package/dist/conversation.js.map +1 -1
- package/dist/memory.d.ts +5 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +77 -37
- package/dist/memory.js.map +1 -1
- package/dist/remote-ai-client.d.ts +1 -8
- package/dist/remote-ai-client.d.ts.map +1 -1
- package/dist/remote-ai-client.js +55 -65
- package/dist/remote-ai-client.js.map +1 -1
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +166 -0
- package/dist/retry.js.map +1 -0
- package/dist/session.d.ts +0 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +243 -312
- package/dist/session.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +91 -9
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +18 -17
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +149 -139
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts +48 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +254 -0
- package/dist/theme.js.map +1 -1
- package/dist/tools/edit-diff.d.ts +32 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +185 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +129 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools.d.ts +19 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +979 -631
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +6 -31
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agents.ts +504 -504
- package/src/ai-client.ts +1559 -1458
- package/src/auth.ts +4 -4
- package/src/cli.ts +195 -1
- package/src/config.ts +3 -3
- package/src/memory.ts +55 -14
- package/src/remote-ai-client.ts +663 -683
- package/src/retry.ts +217 -0
- package/src/session.ts +1736 -1840
- package/src/slash-commands.ts +98 -9
- package/src/smart-approval.ts +626 -625
- package/src/system-prompt-generator.ts +853 -843
- package/src/theme.ts +284 -0
- package/src/tools.ts +390 -70
package/src/retry.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry utility with exponential backoff and jitter
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface RetryConfig {
|
|
6
|
+
maxRetries: number; // Maximum number of retry attempts (default: 3)
|
|
7
|
+
baseDelay: number; // Base delay in ms (default: 1000)
|
|
8
|
+
maxDelay: number; // Maximum delay in ms (default: 10000)
|
|
9
|
+
maxTotalTime: number; // Maximum total time in ms (default: 600000 = 10min)
|
|
10
|
+
jitter: boolean; // Add random jitter to delay (default: true)
|
|
11
|
+
retryOnTimeout: boolean; // Retry on timeout errors (default: true)
|
|
12
|
+
retryOn5xx: boolean; // Retry on 5xx server errors (default: true)
|
|
13
|
+
retryOn429: boolean; // Retry on 429 rate limit (default: true)
|
|
14
|
+
backoffMultiplier: number; // Delay multiplier for each retry (default: 2)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RetryResult<T> {
|
|
18
|
+
success: boolean;
|
|
19
|
+
data?: T;
|
|
20
|
+
error?: Error;
|
|
21
|
+
attempts: number;
|
|
22
|
+
totalTime: number;
|
|
23
|
+
lastError?: Error;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_CONFIG: RetryConfig = {
|
|
27
|
+
maxRetries: 3,
|
|
28
|
+
baseDelay: 1000,
|
|
29
|
+
maxDelay: 10000,
|
|
30
|
+
maxTotalTime: 600000,
|
|
31
|
+
jitter: true,
|
|
32
|
+
retryOnTimeout: true,
|
|
33
|
+
retryOn5xx: true,
|
|
34
|
+
retryOn429: true,
|
|
35
|
+
backoffMultiplier: 2
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if an error is retryable
|
|
40
|
+
*/
|
|
41
|
+
function isRetryableError(error: unknown, config: RetryConfig): boolean {
|
|
42
|
+
// Extract status code from different error formats
|
|
43
|
+
let statusCode: number | undefined;
|
|
44
|
+
|
|
45
|
+
if (error && typeof error === 'object') {
|
|
46
|
+
const err = error as Record<string, unknown>;
|
|
47
|
+
|
|
48
|
+
// axios error format
|
|
49
|
+
if (err.response && typeof err.response === 'object') {
|
|
50
|
+
const response = err.response as Record<string, unknown>;
|
|
51
|
+
statusCode = response.status as number | undefined;
|
|
52
|
+
}
|
|
53
|
+
// Direct status code
|
|
54
|
+
else if (err.statusCode) {
|
|
55
|
+
statusCode = err.statusCode as number;
|
|
56
|
+
}
|
|
57
|
+
else if (err.status) {
|
|
58
|
+
statusCode = err.status as number;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check error message for timeout
|
|
63
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
64
|
+
const isTimeout = errorMessage.includes('timeout') ||
|
|
65
|
+
errorMessage.includes('Timeout') ||
|
|
66
|
+
errorMessage.includes('No response received');
|
|
67
|
+
|
|
68
|
+
// Check if should retry based on status code
|
|
69
|
+
if (statusCode !== undefined) {
|
|
70
|
+
// Retry on 429 (rate limit) and 5xx (server errors)
|
|
71
|
+
if (statusCode === 429 && config.retryOn429) return true;
|
|
72
|
+
if (statusCode >= 500 && statusCode < 600 && config.retryOn5xx) return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Retry on timeout
|
|
77
|
+
if (isTimeout && config.retryOnTimeout) return true;
|
|
78
|
+
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Calculate delay with exponential backoff and jitter
|
|
84
|
+
*/
|
|
85
|
+
function calculateDelay(attempt: number, config: RetryConfig): number {
|
|
86
|
+
const exponentialDelay = config.baseDelay * Math.pow(config.backoffMultiplier, attempt);
|
|
87
|
+
const delay = Math.min(exponentialDelay, config.maxDelay);
|
|
88
|
+
|
|
89
|
+
// Add jitter (0.5 to 1.5 of the delay)
|
|
90
|
+
if (config.jitter) {
|
|
91
|
+
const jitterFactor = 0.5 + Math.random();
|
|
92
|
+
return Math.floor(delay * jitterFactor);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return delay;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sleep for a specified duration
|
|
100
|
+
*/
|
|
101
|
+
function sleep(ms: number): Promise<void> {
|
|
102
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if total time exceeds maximum allowed
|
|
107
|
+
*/
|
|
108
|
+
function wouldExceedTotalTime(
|
|
109
|
+
startTime: number,
|
|
110
|
+
attempt: number,
|
|
111
|
+
delay: number,
|
|
112
|
+
config: RetryConfig
|
|
113
|
+
): boolean {
|
|
114
|
+
const elapsed = Date.now() - startTime;
|
|
115
|
+
const remaining = config.maxTotalTime - elapsed;
|
|
116
|
+
return remaining <= delay;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Execute a function with retry logic
|
|
121
|
+
*
|
|
122
|
+
* @param fn - The async function to execute
|
|
123
|
+
* @param config - Retry configuration (optional)
|
|
124
|
+
* @returns RetryResult containing success status and data/error
|
|
125
|
+
*/
|
|
126
|
+
export async function withRetry<T>(
|
|
127
|
+
fn: () => Promise<T>,
|
|
128
|
+
config?: Partial<RetryConfig>
|
|
129
|
+
): Promise<RetryResult<T>> {
|
|
130
|
+
const mergedConfig: RetryConfig = { ...DEFAULT_CONFIG, ...config };
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
let lastError: Error | undefined;
|
|
133
|
+
|
|
134
|
+
for (let attempt = 0; attempt <= mergedConfig.maxRetries; attempt++) {
|
|
135
|
+
const attemptStartTime = Date.now();
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const data = await fn();
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
data,
|
|
143
|
+
attempts: attempt + 1,
|
|
144
|
+
totalTime: Date.now() - startTime
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
148
|
+
|
|
149
|
+
// Check if this error is retryable
|
|
150
|
+
if (!isRetryableError(error, mergedConfig)) {
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: lastError,
|
|
154
|
+
attempts: attempt + 1,
|
|
155
|
+
totalTime: Date.now() - startTime,
|
|
156
|
+
lastError
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if we've exceeded maximum total time
|
|
161
|
+
if (Date.now() - startTime >= mergedConfig.maxTotalTime) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
error: new Error(`Max total time exceeded after ${attempt + 1} attempts`),
|
|
165
|
+
attempts: attempt + 1,
|
|
166
|
+
totalTime: Date.now() - startTime,
|
|
167
|
+
lastError
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Calculate delay for next retry
|
|
172
|
+
const delay = calculateDelay(attempt, mergedConfig);
|
|
173
|
+
|
|
174
|
+
// Check if next retry would exceed max total time
|
|
175
|
+
if (wouldExceedTotalTime(startTime, attempt, delay, mergedConfig)) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: new Error(`Would exceed max total time`),
|
|
179
|
+
attempts: attempt + 1,
|
|
180
|
+
totalTime: Date.now() - startTime,
|
|
181
|
+
lastError
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Wait before next retry (don't wait after last attempt)
|
|
186
|
+
if (attempt < mergedConfig.maxRetries) {
|
|
187
|
+
await sleep(delay);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Should not reach here, but just in case
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
error: lastError || new Error('Unknown error'),
|
|
196
|
+
attempts: mergedConfig.maxRetries + 1,
|
|
197
|
+
totalTime: Date.now() - startTime,
|
|
198
|
+
lastError
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create a retryable version of a function
|
|
204
|
+
*/
|
|
205
|
+
export function createRetryable<T extends (...args: unknown[]) => Promise<unknown>>(
|
|
206
|
+
fn: T,
|
|
207
|
+
config?: Partial<RetryConfig>
|
|
208
|
+
): T {
|
|
209
|
+
return ((...args: Parameters<T>) => {
|
|
210
|
+
return withRetry(() => fn(...args), config).then(result => {
|
|
211
|
+
if (result.success) {
|
|
212
|
+
return result.data;
|
|
213
|
+
}
|
|
214
|
+
throw result.error;
|
|
215
|
+
});
|
|
216
|
+
}) as T;
|
|
217
|
+
}
|