qwenproxy-cli 1.0.0 → 1.0.1

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 (108) hide show
  1. package/LICENSE +14 -14
  2. package/README.md +906 -906
  3. package/bin/qwenproxy.js +5 -1
  4. package/package.json +78 -78
  5. package/src/api/error-classifier.ts +159 -159
  6. package/src/api/error-helpers.ts +118 -118
  7. package/src/api/models.ts +261 -261
  8. package/src/api/server.ts +860 -859
  9. package/src/cache/memory-cache.ts +385 -385
  10. package/src/clean-cache.ts +204 -204
  11. package/src/core/account-concurrency.ts +671 -671
  12. package/src/core/account-manager.ts +301 -297
  13. package/src/core/account-priority.ts +163 -163
  14. package/src/core/accounts.ts +186 -186
  15. package/src/core/config.ts +383 -383
  16. package/src/core/crypto-utils.ts +79 -79
  17. package/src/core/database.ts +276 -276
  18. package/src/core/errors.ts +118 -118
  19. package/src/core/logger.ts +269 -269
  20. package/src/core/memory-usage.ts +84 -84
  21. package/src/core/metrics.ts +291 -291
  22. package/src/core/model-alias.ts +77 -77
  23. package/src/core/model-registry.ts +544 -544
  24. package/src/core/mutex.ts +119 -119
  25. package/src/core/paths.ts +199 -199
  26. package/src/core/prompt-limits.ts +214 -214
  27. package/src/core/reasoning-effort.ts +102 -102
  28. package/src/core/stream-registry.ts +96 -96
  29. package/src/core/waf-isolation.ts +117 -117
  30. package/src/core/watchdog.ts +195 -195
  31. package/src/delete-chats.ts +23 -23
  32. package/src/index.ts +65 -64
  33. package/src/login.ts +147 -147
  34. package/src/reset-cooldowns.ts +11 -11
  35. package/src/routes/anthropic/index.ts +355 -355
  36. package/src/routes/anthropic/translate.ts +522 -522
  37. package/src/routes/anthropic/types.ts +154 -154
  38. package/src/routes/anthropic/validation.ts +144 -144
  39. package/src/routes/chat/account.ts +1817 -1817
  40. package/src/routes/chat/context.ts +241 -241
  41. package/src/routes/chat/errors.ts +85 -85
  42. package/src/routes/chat/helpers.ts +268 -268
  43. package/src/routes/chat/index.ts +618 -618
  44. package/src/routes/chat/media.ts +285 -285
  45. package/src/routes/chat/retry-policy.ts +754 -754
  46. package/src/routes/chat/stop.ts +98 -98
  47. package/src/routes/chat/streaming.ts +2710 -2710
  48. package/src/routes/chat/validation.ts +526 -526
  49. package/src/routes/chat.ts +2 -2
  50. package/src/routes/completions.ts +290 -290
  51. package/src/routes/images.ts +139 -139
  52. package/src/routes/responses/adapter.ts +503 -503
  53. package/src/routes/responses/index.ts +405 -405
  54. package/src/routes/responses/state.ts +230 -230
  55. package/src/routes/responses/streaming.ts +528 -528
  56. package/src/routes/responses/types.ts +285 -285
  57. package/src/routes/responses/validation.ts +202 -202
  58. package/src/routes/upload.ts +731 -731
  59. package/src/routes/videos.ts +214 -214
  60. package/src/services/auth-playwright.ts +173 -173
  61. package/src/services/captcha-coordinator.ts +161 -161
  62. package/src/services/captcha-solver.ts +553 -553
  63. package/src/services/chat-cleanup.ts +80 -80
  64. package/src/services/context-meter.ts +317 -317
  65. package/src/services/fingerprint.ts +242 -242
  66. package/src/services/human-behavior.ts +173 -173
  67. package/src/services/media-generation.ts +1748 -1748
  68. package/src/services/playwright.ts +2800 -2800
  69. package/src/services/qwen-chat-pool.ts +345 -345
  70. package/src/services/qwen-errors.ts +133 -133
  71. package/src/services/qwen-headers.ts +79 -79
  72. package/src/services/qwen-thread-state.ts +393 -393
  73. package/src/services/qwen-url.ts +19 -19
  74. package/src/services/qwen.ts +3126 -3126
  75. package/src/services/session-keeper.ts +88 -88
  76. package/src/services/token-estimation-metrics.ts +118 -118
  77. package/src/sync/claude-code.ts +75 -75
  78. package/src/sync/codex.ts +123 -123
  79. package/src/sync/index.ts +362 -362
  80. package/src/sync/omp.ts +105 -105
  81. package/src/sync/opencode.ts +214 -214
  82. package/src/sync/types.ts +53 -53
  83. package/src/sync/utils.ts +27 -27
  84. package/src/sync-clients.ts +189 -189
  85. package/src/tools/instructions.ts +137 -137
  86. package/src/tools/manifest.ts +81 -81
  87. package/src/tools/parser.ts +2989 -2989
  88. package/src/tools/toolcall-tags.ts +142 -142
  89. package/src/tui/app.ts +264 -264
  90. package/src/tui/index.ts +61 -61
  91. package/src/tui/markdown.ts +258 -258
  92. package/src/tui/proxy-client.ts +331 -326
  93. package/src/tui/screen.ts +294 -278
  94. package/src/tui/server-manager.ts +270 -270
  95. package/src/tui/theme.ts +432 -432
  96. package/src/tui/types.ts +33 -33
  97. package/src/tui/views/accounts-view.ts +656 -656
  98. package/src/tui/views/chat-view.ts +1018 -823
  99. package/src/tui/views/logs-view.ts +479 -413
  100. package/src/tui/views/status-view.ts +204 -204
  101. package/src/tui/views/storage-view.ts +304 -291
  102. package/src/tui/views/sync-view.ts +409 -409
  103. package/src/types/ali-oss.d.ts +32 -32
  104. package/src/utils/context-truncation.ts +84 -84
  105. package/src/utils/json.ts +380 -380
  106. package/src/utils/session-id.ts +37 -37
  107. package/src/utils/tool-call-guard.ts +84 -84
  108. package/src/utils/types.ts +109 -109
@@ -1,230 +1,230 @@
1
- import type { ResponsesResponse } from "./types.ts";
2
- import { getDatabase } from "../../core/database.ts";
3
-
4
- // ============ State management for previous_response_id ============
5
- //
6
- // The Responses API supports stateful conversations via `previous_response_id`.
7
- // We store completed responses in SQLite (durable) with an in-memory LRU cache
8
- // for fast lookups. This ensures memory persists across server restarts.
9
-
10
- export interface StoredResponse {
11
- response: ResponsesResponse;
12
- /** The full list of Chat Completions messages sent to the upstream for this response */
13
- chatMessages: Array<{
14
- role: "system" | "user" | "assistant" | "tool";
15
- content: string | null;
16
- tool_calls?: Array<{
17
- id: string;
18
- type: "function";
19
- function: { name: string; arguments: string };
20
- }>;
21
- tool_call_id?: string;
22
- }>;
23
- /** Timestamp of storage */
24
- storedAt: number;
25
- }
26
-
27
- // In-memory LRU cache (responseId → StoredResponse)
28
- const cache = new Map<string, StoredResponse>();
29
- const MAX_CACHE_SIZE = 500;
30
- // Max age (ms) - 7 days for SQLite, 24h for cache
31
- const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
32
- const CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
33
-
34
- let tableReady = false;
35
-
36
- function ensureTable(): void {
37
- if (tableReady) return;
38
- try {
39
- const db = getDatabase();
40
- db.exec(`
41
- CREATE TABLE IF NOT EXISTS responses_store (
42
- response_id TEXT PRIMARY KEY,
43
- response_json TEXT NOT NULL,
44
- chat_messages_json TEXT NOT NULL,
45
- stored_at INTEGER NOT NULL
46
- )
47
- `);
48
- db.exec(`
49
- CREATE INDEX IF NOT EXISTS idx_responses_store_stored_at
50
- ON responses_store(stored_at)
51
- `);
52
- tableReady = true;
53
- } catch {
54
- // Database not available — fall back to memory-only
55
- tableReady = false;
56
- }
57
- }
58
-
59
- /**
60
- * Store a completed response for future `previous_response_id` lookups.
61
- * Persists to SQLite + in-memory cache.
62
- */
63
- export function storeResponse(
64
- responseId: string,
65
- response: ResponsesResponse,
66
- chatMessages: StoredResponse["chatMessages"],
67
- ): void {
68
- const entry: StoredResponse = {
69
- response,
70
- chatMessages,
71
- storedAt: Date.now(),
72
- };
73
-
74
- // In-memory cache (LRU eviction)
75
- if (cache.size >= MAX_CACHE_SIZE) {
76
- const oldest = [...cache.entries()]
77
- .sort((a, b) => a[1].storedAt - b[1].storedAt)
78
- .slice(0, Math.floor(MAX_CACHE_SIZE * 0.1));
79
- for (const [key] of oldest) {
80
- cache.delete(key);
81
- }
82
- }
83
- cache.set(responseId, entry);
84
-
85
- // SQLite persistence
86
- ensureTable();
87
- if (tableReady) {
88
- try {
89
- const db = getDatabase();
90
- db.prepare(
91
- `INSERT OR REPLACE INTO responses_store (response_id, response_json, chat_messages_json, stored_at)
92
- VALUES (?, ?, ?, ?)`,
93
- ).run(
94
- responseId,
95
- JSON.stringify(response),
96
- JSON.stringify(chatMessages),
97
- entry.storedAt,
98
- );
99
- } catch {
100
- // Non-fatal: memory cache still works
101
- }
102
- }
103
- }
104
-
105
- /**
106
- * Retrieve stored history for a `previous_response_id`.
107
- * Returns null if not found.
108
- */
109
- export function getResponseHistory(
110
- previousResponseId: string,
111
- ): StoredResponse["chatMessages"] | null {
112
- // Check memory cache first
113
- const cached = cache.get(previousResponseId);
114
- if (cached) {
115
- if (Date.now() - cached.storedAt > CACHE_MAX_AGE_MS) {
116
- cache.delete(previousResponseId);
117
- } else {
118
- return cached.chatMessages;
119
- }
120
- }
121
-
122
- // Fall back to SQLite
123
- ensureTable();
124
- if (!tableReady) return null;
125
-
126
- try {
127
- const db = getDatabase();
128
- const row = db
129
- .prepare(
130
- `SELECT chat_messages_json, stored_at FROM responses_store WHERE response_id = ?`,
131
- )
132
- .get(previousResponseId) as
133
- | { chat_messages_json: string; stored_at: number }
134
- | undefined;
135
-
136
- if (!row) return null;
137
-
138
- if (Date.now() - row.stored_at > MAX_AGE_MS) {
139
- db.prepare(`DELETE FROM responses_store WHERE response_id = ?`).run(
140
- previousResponseId,
141
- );
142
- return null;
143
- }
144
-
145
- const chatMessages = JSON.parse(row.chat_messages_json);
146
- // Re-populate cache
147
- const responseRow = db
148
- .prepare(`SELECT response_json FROM responses_store WHERE response_id = ?`)
149
- .get(previousResponseId) as { response_json: string } | undefined;
150
- if (responseRow) {
151
- cache.set(previousResponseId, {
152
- response: JSON.parse(responseRow.response_json),
153
- chatMessages,
154
- storedAt: row.stored_at,
155
- });
156
- }
157
-
158
- return chatMessages;
159
- } catch {
160
- return null;
161
- }
162
- }
163
-
164
- /**
165
- * Retrieve the full stored response (for GET /v1/responses/:id).
166
- * Returns null if not found.
167
- */
168
- export function getStoredResponse(
169
- responseId: string,
170
- ): ResponsesResponse | null {
171
- // Check memory cache first
172
- const cached = cache.get(responseId);
173
- if (cached) {
174
- if (Date.now() - cached.storedAt > CACHE_MAX_AGE_MS) {
175
- cache.delete(responseId);
176
- } else {
177
- return cached.response;
178
- }
179
- }
180
-
181
- // Fall back to SQLite
182
- ensureTable();
183
- if (!tableReady) return null;
184
-
185
- try {
186
- const db = getDatabase();
187
- const row = db
188
- .prepare(
189
- `SELECT response_json, chat_messages_json, stored_at FROM responses_store WHERE response_id = ?`,
190
- )
191
- .get(responseId) as
192
- | { response_json: string; chat_messages_json: string; stored_at: number }
193
- | undefined;
194
-
195
- if (!row) return null;
196
-
197
- if (Date.now() - row.stored_at > MAX_AGE_MS) {
198
- db.prepare(`DELETE FROM responses_store WHERE response_id = ?`).run(
199
- responseId,
200
- );
201
- return null;
202
- }
203
-
204
- return JSON.parse(row.response_json);
205
- } catch {
206
- return null;
207
- }
208
- }
209
-
210
- /**
211
- * Delete a stored response.
212
- */
213
- export function deleteStoredResponse(responseId: string): boolean {
214
- const fromCache = cache.delete(responseId);
215
-
216
- ensureTable();
217
- if (tableReady) {
218
- try {
219
- const db = getDatabase();
220
- const result = db
221
- .prepare(`DELETE FROM responses_store WHERE response_id = ?`)
222
- .run(responseId);
223
- return fromCache || result.changes > 0;
224
- } catch {
225
- return fromCache;
226
- }
227
- }
228
-
229
- return fromCache;
230
- }
1
+ import type { ResponsesResponse } from "./types.ts";
2
+ import { getDatabase } from "../../core/database.ts";
3
+
4
+ // ============ State management for previous_response_id ============
5
+ //
6
+ // The Responses API supports stateful conversations via `previous_response_id`.
7
+ // We store completed responses in SQLite (durable) with an in-memory LRU cache
8
+ // for fast lookups. This ensures memory persists across server restarts.
9
+
10
+ export interface StoredResponse {
11
+ response: ResponsesResponse;
12
+ /** The full list of Chat Completions messages sent to the upstream for this response */
13
+ chatMessages: Array<{
14
+ role: "system" | "user" | "assistant" | "tool";
15
+ content: string | null;
16
+ tool_calls?: Array<{
17
+ id: string;
18
+ type: "function";
19
+ function: { name: string; arguments: string };
20
+ }>;
21
+ tool_call_id?: string;
22
+ }>;
23
+ /** Timestamp of storage */
24
+ storedAt: number;
25
+ }
26
+
27
+ // In-memory LRU cache (responseId → StoredResponse)
28
+ const cache = new Map<string, StoredResponse>();
29
+ const MAX_CACHE_SIZE = 500;
30
+ // Max age (ms) - 7 days for SQLite, 24h for cache
31
+ const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
32
+ const CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
33
+
34
+ let tableReady = false;
35
+
36
+ function ensureTable(): void {
37
+ if (tableReady) return;
38
+ try {
39
+ const db = getDatabase();
40
+ db.exec(`
41
+ CREATE TABLE IF NOT EXISTS responses_store (
42
+ response_id TEXT PRIMARY KEY,
43
+ response_json TEXT NOT NULL,
44
+ chat_messages_json TEXT NOT NULL,
45
+ stored_at INTEGER NOT NULL
46
+ )
47
+ `);
48
+ db.exec(`
49
+ CREATE INDEX IF NOT EXISTS idx_responses_store_stored_at
50
+ ON responses_store(stored_at)
51
+ `);
52
+ tableReady = true;
53
+ } catch {
54
+ // Database not available — fall back to memory-only
55
+ tableReady = false;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Store a completed response for future `previous_response_id` lookups.
61
+ * Persists to SQLite + in-memory cache.
62
+ */
63
+ export function storeResponse(
64
+ responseId: string,
65
+ response: ResponsesResponse,
66
+ chatMessages: StoredResponse["chatMessages"],
67
+ ): void {
68
+ const entry: StoredResponse = {
69
+ response,
70
+ chatMessages,
71
+ storedAt: Date.now(),
72
+ };
73
+
74
+ // In-memory cache (LRU eviction)
75
+ if (cache.size >= MAX_CACHE_SIZE) {
76
+ const oldest = [...cache.entries()]
77
+ .sort((a, b) => a[1].storedAt - b[1].storedAt)
78
+ .slice(0, Math.floor(MAX_CACHE_SIZE * 0.1));
79
+ for (const [key] of oldest) {
80
+ cache.delete(key);
81
+ }
82
+ }
83
+ cache.set(responseId, entry);
84
+
85
+ // SQLite persistence
86
+ ensureTable();
87
+ if (tableReady) {
88
+ try {
89
+ const db = getDatabase();
90
+ db.prepare(
91
+ `INSERT OR REPLACE INTO responses_store (response_id, response_json, chat_messages_json, stored_at)
92
+ VALUES (?, ?, ?, ?)`,
93
+ ).run(
94
+ responseId,
95
+ JSON.stringify(response),
96
+ JSON.stringify(chatMessages),
97
+ entry.storedAt,
98
+ );
99
+ } catch {
100
+ // Non-fatal: memory cache still works
101
+ }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Retrieve stored history for a `previous_response_id`.
107
+ * Returns null if not found.
108
+ */
109
+ export function getResponseHistory(
110
+ previousResponseId: string,
111
+ ): StoredResponse["chatMessages"] | null {
112
+ // Check memory cache first
113
+ const cached = cache.get(previousResponseId);
114
+ if (cached) {
115
+ if (Date.now() - cached.storedAt > CACHE_MAX_AGE_MS) {
116
+ cache.delete(previousResponseId);
117
+ } else {
118
+ return cached.chatMessages;
119
+ }
120
+ }
121
+
122
+ // Fall back to SQLite
123
+ ensureTable();
124
+ if (!tableReady) return null;
125
+
126
+ try {
127
+ const db = getDatabase();
128
+ const row = db
129
+ .prepare(
130
+ `SELECT chat_messages_json, stored_at FROM responses_store WHERE response_id = ?`,
131
+ )
132
+ .get(previousResponseId) as
133
+ | { chat_messages_json: string; stored_at: number }
134
+ | undefined;
135
+
136
+ if (!row) return null;
137
+
138
+ if (Date.now() - row.stored_at > MAX_AGE_MS) {
139
+ db.prepare(`DELETE FROM responses_store WHERE response_id = ?`).run(
140
+ previousResponseId,
141
+ );
142
+ return null;
143
+ }
144
+
145
+ const chatMessages = JSON.parse(row.chat_messages_json);
146
+ // Re-populate cache
147
+ const responseRow = db
148
+ .prepare(`SELECT response_json FROM responses_store WHERE response_id = ?`)
149
+ .get(previousResponseId) as { response_json: string } | undefined;
150
+ if (responseRow) {
151
+ cache.set(previousResponseId, {
152
+ response: JSON.parse(responseRow.response_json),
153
+ chatMessages,
154
+ storedAt: row.stored_at,
155
+ });
156
+ }
157
+
158
+ return chatMessages;
159
+ } catch {
160
+ return null;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Retrieve the full stored response (for GET /v1/responses/:id).
166
+ * Returns null if not found.
167
+ */
168
+ export function getStoredResponse(
169
+ responseId: string,
170
+ ): ResponsesResponse | null {
171
+ // Check memory cache first
172
+ const cached = cache.get(responseId);
173
+ if (cached) {
174
+ if (Date.now() - cached.storedAt > CACHE_MAX_AGE_MS) {
175
+ cache.delete(responseId);
176
+ } else {
177
+ return cached.response;
178
+ }
179
+ }
180
+
181
+ // Fall back to SQLite
182
+ ensureTable();
183
+ if (!tableReady) return null;
184
+
185
+ try {
186
+ const db = getDatabase();
187
+ const row = db
188
+ .prepare(
189
+ `SELECT response_json, chat_messages_json, stored_at FROM responses_store WHERE response_id = ?`,
190
+ )
191
+ .get(responseId) as
192
+ | { response_json: string; chat_messages_json: string; stored_at: number }
193
+ | undefined;
194
+
195
+ if (!row) return null;
196
+
197
+ if (Date.now() - row.stored_at > MAX_AGE_MS) {
198
+ db.prepare(`DELETE FROM responses_store WHERE response_id = ?`).run(
199
+ responseId,
200
+ );
201
+ return null;
202
+ }
203
+
204
+ return JSON.parse(row.response_json);
205
+ } catch {
206
+ return null;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Delete a stored response.
212
+ */
213
+ export function deleteStoredResponse(responseId: string): boolean {
214
+ const fromCache = cache.delete(responseId);
215
+
216
+ ensureTable();
217
+ if (tableReady) {
218
+ try {
219
+ const db = getDatabase();
220
+ const result = db
221
+ .prepare(`DELETE FROM responses_store WHERE response_id = ?`)
222
+ .run(responseId);
223
+ return fromCache || result.changes > 0;
224
+ } catch {
225
+ return fromCache;
226
+ }
227
+ }
228
+
229
+ return fromCache;
230
+ }