@pixelbyte-software/pixcode 1.30.2 → 1.31.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.
Files changed (202) hide show
  1. package/LICENSE +718 -718
  2. package/README.de.md +248 -248
  3. package/README.ja.md +240 -240
  4. package/README.ko.md +240 -240
  5. package/README.md +295 -285
  6. package/README.ru.md +248 -248
  7. package/README.tr.md +250 -250
  8. package/README.zh-CN.md +240 -240
  9. package/dist/api-docs.html +879 -879
  10. package/dist/assets/index-BRRJ47XQ.css +32 -0
  11. package/dist/assets/index-EQohwyiC.js +837 -0
  12. package/dist/clear-cache.html +85 -85
  13. package/dist/convert-icons.md +52 -52
  14. package/dist/favicon.png +0 -0
  15. package/dist/favicon.svg +7 -8
  16. package/dist/generate-icons.js +48 -48
  17. package/dist/icons/codex-white.svg +3 -3
  18. package/dist/icons/codex.svg +3 -3
  19. package/dist/icons/cursor-white.svg +11 -11
  20. package/dist/icons/icon-128x128.png +0 -0
  21. package/dist/icons/icon-128x128.svg +9 -12
  22. package/dist/icons/icon-144x144.png +0 -0
  23. package/dist/icons/icon-144x144.svg +9 -12
  24. package/dist/icons/icon-152x152.png +0 -0
  25. package/dist/icons/icon-152x152.svg +9 -12
  26. package/dist/icons/icon-192x192.png +0 -0
  27. package/dist/icons/icon-192x192.svg +9 -12
  28. package/dist/icons/icon-384x384.png +0 -0
  29. package/dist/icons/icon-384x384.svg +9 -12
  30. package/dist/icons/icon-512x512.png +0 -0
  31. package/dist/icons/icon-512x512.svg +9 -12
  32. package/dist/icons/icon-72x72.png +0 -0
  33. package/dist/icons/icon-72x72.svg +9 -12
  34. package/dist/icons/icon-96x96.png +0 -0
  35. package/dist/icons/icon-96x96.svg +9 -12
  36. package/dist/icons/icon-template.svg +9 -12
  37. package/dist/icons/qwen-ai-icon.png +0 -0
  38. package/dist/index.html +59 -49
  39. package/dist/logo.png +0 -0
  40. package/dist/logo.svg +11 -16
  41. package/dist/manifest.json +60 -60
  42. package/dist/sw.js +124 -124
  43. package/dist-server/server/cli.js +100 -97
  44. package/dist-server/server/cli.js.map +1 -1
  45. package/dist-server/server/daemon/manager.js +33 -33
  46. package/dist-server/server/daemon-manager.js +62 -62
  47. package/dist-server/server/database/db.js +114 -22
  48. package/dist-server/server/database/db.js.map +1 -1
  49. package/dist-server/server/database/schema.js +122 -89
  50. package/dist-server/server/database/schema.js.map +1 -1
  51. package/dist-server/server/gemini-cli.js +6 -1
  52. package/dist-server/server/gemini-cli.js.map +1 -1
  53. package/dist-server/server/index.js +234 -64
  54. package/dist-server/server/index.js.map +1 -1
  55. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +29 -2
  56. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  57. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +22 -2
  58. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -1
  59. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +2 -2
  60. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  61. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +14 -2
  62. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
  63. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js +132 -0
  64. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js.map +1 -0
  65. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js +87 -0
  66. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js.map +1 -0
  67. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js +201 -0
  68. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js.map +1 -0
  69. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js +19 -0
  70. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js.map +1 -0
  71. package/dist-server/server/modules/providers/provider.registry.js +2 -0
  72. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  73. package/dist-server/server/modules/providers/provider.routes.js +310 -1
  74. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  75. package/dist-server/server/projects.js +197 -6
  76. package/dist-server/server/projects.js.map +1 -1
  77. package/dist-server/server/qwen-code-cli.js +350 -0
  78. package/dist-server/server/qwen-code-cli.js.map +1 -0
  79. package/dist-server/server/qwen-response-handler.js +70 -0
  80. package/dist-server/server/qwen-response-handler.js.map +1 -0
  81. package/dist-server/server/routes/commands.js +25 -25
  82. package/dist-server/server/routes/git.js +17 -17
  83. package/dist-server/server/routes/network.js +116 -0
  84. package/dist-server/server/routes/network.js.map +1 -0
  85. package/dist-server/server/routes/projects.js +43 -0
  86. package/dist-server/server/routes/projects.js.map +1 -1
  87. package/dist-server/server/routes/qwen.js +23 -0
  88. package/dist-server/server/routes/qwen.js.map +1 -0
  89. package/dist-server/server/routes/taskmaster.js +419 -419
  90. package/dist-server/server/routes/telegram.js +119 -0
  91. package/dist-server/server/routes/telegram.js.map +1 -0
  92. package/dist-server/server/services/external-access.js +228 -0
  93. package/dist-server/server/services/external-access.js.map +1 -0
  94. package/dist-server/server/services/install-jobs.js +394 -0
  95. package/dist-server/server/services/install-jobs.js.map +1 -0
  96. package/dist-server/server/services/notification-orchestrator.js +19 -5
  97. package/dist-server/server/services/notification-orchestrator.js.map +1 -1
  98. package/dist-server/server/services/provider-credentials.js +154 -0
  99. package/dist-server/server/services/provider-credentials.js.map +1 -0
  100. package/dist-server/server/services/provider-models.js +218 -0
  101. package/dist-server/server/services/provider-models.js.map +1 -0
  102. package/dist-server/server/services/telegram/bot.js +259 -0
  103. package/dist-server/server/services/telegram/bot.js.map +1 -0
  104. package/dist-server/server/services/telegram/translations.js +160 -0
  105. package/dist-server/server/services/telegram/translations.js.map +1 -0
  106. package/dist-server/server/utils/port-access.js +196 -0
  107. package/dist-server/server/utils/port-access.js.map +1 -0
  108. package/dist-server/shared/modelConstants.js +18 -0
  109. package/dist-server/shared/modelConstants.js.map +1 -1
  110. package/package.json +177 -168
  111. package/scripts/fix-node-pty.js +67 -67
  112. package/server/claude-sdk.js +834 -834
  113. package/server/cli.js +940 -937
  114. package/server/constants/config.js +4 -4
  115. package/server/cursor-cli.js +342 -342
  116. package/server/daemon/manager.js +564 -564
  117. package/server/daemon-manager.js +920 -920
  118. package/server/database/db.js +696 -593
  119. package/server/database/schema.js +138 -102
  120. package/server/gemini-cli.js +475 -469
  121. package/server/gemini-response-handler.js +79 -79
  122. package/server/index.js +2730 -2556
  123. package/server/load-env.js +34 -34
  124. package/server/middleware/auth.js +132 -132
  125. package/server/modules/providers/list/claude/claude-auth.provider.ts +145 -123
  126. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  127. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  128. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  129. package/server/modules/providers/list/codex/codex-auth.provider.ts +115 -100
  130. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  131. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  132. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  133. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  134. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  135. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  136. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  137. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +163 -151
  138. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  139. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  140. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  141. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -0
  142. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -0
  143. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +218 -0
  144. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -0
  145. package/server/modules/providers/provider.registry.ts +38 -36
  146. package/server/modules/providers/provider.routes.ts +583 -217
  147. package/server/modules/providers/services/mcp.service.ts +94 -94
  148. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  149. package/server/modules/providers/services/sessions.service.ts +45 -45
  150. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  151. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  152. package/server/modules/providers/tests/mcp.test.ts +293 -293
  153. package/server/openai-codex.js +426 -426
  154. package/server/projects.js +2993 -2792
  155. package/server/qwen-code-cli.js +392 -0
  156. package/server/qwen-response-handler.js +73 -0
  157. package/server/routes/agent.js +1245 -1245
  158. package/server/routes/auth.js +134 -134
  159. package/server/routes/codex.js +19 -19
  160. package/server/routes/commands.js +554 -554
  161. package/server/routes/cursor.js +52 -52
  162. package/server/routes/gemini.js +24 -24
  163. package/server/routes/git.js +1488 -1488
  164. package/server/routes/mcp-utils.js +31 -31
  165. package/server/routes/messages.js +61 -61
  166. package/server/routes/network.js +128 -0
  167. package/server/routes/plugins.js +307 -307
  168. package/server/routes/projects.js +675 -627
  169. package/server/routes/qwen.js +27 -0
  170. package/server/routes/settings.js +286 -286
  171. package/server/routes/taskmaster.js +1471 -1471
  172. package/server/routes/telegram.js +125 -0
  173. package/server/routes/user.js +123 -123
  174. package/server/services/external-access.js +240 -0
  175. package/server/services/install-jobs.js +410 -0
  176. package/server/services/notification-orchestrator.js +242 -227
  177. package/server/services/provider-credentials.js +151 -0
  178. package/server/services/provider-models.js +225 -0
  179. package/server/services/telegram/bot.js +280 -0
  180. package/server/services/telegram/translations.js +170 -0
  181. package/server/services/vapid-keys.js +35 -35
  182. package/server/sessionManager.js +225 -225
  183. package/server/shared/interfaces.ts +54 -54
  184. package/server/shared/types.ts +172 -172
  185. package/server/shared/utils.ts +193 -193
  186. package/server/tsconfig.json +36 -36
  187. package/server/utils/colors.js +21 -21
  188. package/server/utils/commandParser.js +303 -303
  189. package/server/utils/frontmatter.js +18 -18
  190. package/server/utils/gitConfig.js +34 -34
  191. package/server/utils/mcp-detector.js +147 -147
  192. package/server/utils/plugin-loader.js +457 -457
  193. package/server/utils/plugin-process-manager.js +184 -184
  194. package/server/utils/port-access.js +209 -0
  195. package/server/utils/runtime-paths.js +37 -37
  196. package/server/utils/taskmaster-websocket.js +128 -128
  197. package/server/utils/url-detection.js +71 -71
  198. package/server/vite-daemon.js +78 -78
  199. package/shared/modelConstants.js +117 -97
  200. package/shared/networkHosts.js +22 -22
  201. package/dist/assets/index-C2c9QNwK.css +0 -32
  202. package/dist/assets/index-DyXDZED-.js +0 -1277
@@ -0,0 +1,114 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ import { McpProvider } from '@/modules/providers/shared/mcp/mcp.provider.js';
5
+ import type { McpScope, ProviderMcpServer, UpsertProviderMcpServerInput } from '@/shared/types.js';
6
+ import {
7
+ AppError,
8
+ readJsonConfig,
9
+ readObjectRecord,
10
+ readOptionalString,
11
+ readStringArray,
12
+ readStringRecord,
13
+ writeJsonConfig,
14
+ } from '@/shared/utils.js';
15
+
16
+ /**
17
+ * Qwen Code MCP provider. Qwen reads MCP servers out of the same
18
+ * `settings.json` shape Gemini uses, just under `~/.qwen/` instead of
19
+ * `~/.gemini/`. Supported transports and scopes match Gemini.
20
+ */
21
+ export class QwenMcpProvider extends McpProvider {
22
+ constructor() {
23
+ super('qwen', ['user', 'project'], ['stdio', 'http', 'sse']);
24
+ }
25
+
26
+ protected async readScopedServers(scope: McpScope, workspacePath: string): Promise<Record<string, unknown>> {
27
+ const filePath = scope === 'user'
28
+ ? path.join(os.homedir(), '.qwen', 'settings.json')
29
+ : path.join(workspacePath, '.qwen', 'settings.json');
30
+ const config = await readJsonConfig(filePath);
31
+ return readObjectRecord(config.mcpServers) ?? {};
32
+ }
33
+
34
+ protected async writeScopedServers(
35
+ scope: McpScope,
36
+ workspacePath: string,
37
+ servers: Record<string, unknown>,
38
+ ): Promise<void> {
39
+ const filePath = scope === 'user'
40
+ ? path.join(os.homedir(), '.qwen', 'settings.json')
41
+ : path.join(workspacePath, '.qwen', 'settings.json');
42
+ const config = await readJsonConfig(filePath);
43
+ config.mcpServers = servers;
44
+ await writeJsonConfig(filePath, config);
45
+ }
46
+
47
+ protected buildServerConfig(input: UpsertProviderMcpServerInput): Record<string, unknown> {
48
+ if (input.transport === 'stdio') {
49
+ if (!input.command?.trim()) {
50
+ throw new AppError('command is required for stdio MCP servers.', {
51
+ code: 'MCP_COMMAND_REQUIRED',
52
+ statusCode: 400,
53
+ });
54
+ }
55
+ return {
56
+ command: input.command,
57
+ args: input.args ?? [],
58
+ env: input.env ?? {},
59
+ cwd: input.cwd,
60
+ };
61
+ }
62
+
63
+ if (!input.url?.trim()) {
64
+ throw new AppError('url is required for http/sse MCP servers.', {
65
+ code: 'MCP_URL_REQUIRED',
66
+ statusCode: 400,
67
+ });
68
+ }
69
+
70
+ return {
71
+ type: input.transport,
72
+ url: input.url,
73
+ headers: input.headers ?? {},
74
+ };
75
+ }
76
+
77
+ protected normalizeServerConfig(
78
+ scope: McpScope,
79
+ name: string,
80
+ rawConfig: unknown,
81
+ ): ProviderMcpServer | null {
82
+ if (!rawConfig || typeof rawConfig !== 'object') {
83
+ return null;
84
+ }
85
+
86
+ const config = rawConfig as Record<string, unknown>;
87
+ if (typeof config.command === 'string') {
88
+ return {
89
+ provider: 'qwen',
90
+ name,
91
+ scope,
92
+ transport: 'stdio',
93
+ command: config.command,
94
+ args: readStringArray(config.args),
95
+ env: readStringRecord(config.env),
96
+ cwd: readOptionalString(config.cwd),
97
+ };
98
+ }
99
+
100
+ if (typeof config.url === 'string') {
101
+ const transport = readOptionalString(config.type) === 'sse' ? 'sse' : 'http';
102
+ return {
103
+ provider: 'qwen',
104
+ name,
105
+ scope,
106
+ transport,
107
+ url: config.url,
108
+ headers: readStringRecord(config.headers),
109
+ };
110
+ }
111
+
112
+ return null;
113
+ }
114
+ }
@@ -0,0 +1,218 @@
1
+ import sessionManager from '@/sessionManager.js';
2
+ import { getQwenCliSessionMessages } from '@/projects.js';
3
+ import type { IProviderSessions } from '@/shared/interfaces.js';
4
+ import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
5
+ import { createNormalizedMessage, generateMessageId, readObjectRecord } from '@/shared/utils.js';
6
+
7
+ const PROVIDER = 'qwen';
8
+
9
+ /**
10
+ * Qwen Code sessions — structurally identical to Gemini since Qwen is a fork.
11
+ * Stream events have the same `type` taxonomy (message / tool_use /
12
+ * tool_result / result / error), and on-disk history files carry the same
13
+ * `role + content` message shape with optional part arrays.
14
+ */
15
+ export class QwenSessionsProvider implements IProviderSessions {
16
+ normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
17
+ const raw = readObjectRecord(rawMessage);
18
+ if (!raw) return [];
19
+
20
+ const ts = raw.timestamp || new Date().toISOString();
21
+ const baseId = raw.uuid || generateMessageId('qwen');
22
+
23
+ if (raw.type === 'message' && raw.role === 'assistant') {
24
+ const content = raw.content || '';
25
+ const messages: NormalizedMessage[] = [];
26
+ if (content) {
27
+ messages.push(createNormalizedMessage({
28
+ id: baseId,
29
+ sessionId,
30
+ timestamp: ts,
31
+ provider: PROVIDER,
32
+ kind: 'stream_delta',
33
+ content,
34
+ }));
35
+ }
36
+ if (raw.delta !== true) {
37
+ messages.push(createNormalizedMessage({
38
+ sessionId,
39
+ timestamp: ts,
40
+ provider: PROVIDER,
41
+ kind: 'stream_end',
42
+ }));
43
+ }
44
+ return messages;
45
+ }
46
+
47
+ if (raw.type === 'tool_use') {
48
+ return [createNormalizedMessage({
49
+ id: baseId,
50
+ sessionId,
51
+ timestamp: ts,
52
+ provider: PROVIDER,
53
+ kind: 'tool_use',
54
+ toolName: raw.tool_name,
55
+ toolInput: raw.parameters || {},
56
+ toolId: raw.tool_id || baseId,
57
+ })];
58
+ }
59
+
60
+ if (raw.type === 'tool_result') {
61
+ return [createNormalizedMessage({
62
+ id: baseId,
63
+ sessionId,
64
+ timestamp: ts,
65
+ provider: PROVIDER,
66
+ kind: 'tool_result',
67
+ toolId: raw.tool_id || '',
68
+ content: raw.output === undefined ? '' : String(raw.output),
69
+ isError: raw.status === 'error',
70
+ })];
71
+ }
72
+
73
+ if (raw.type === 'result') {
74
+ const messages = [createNormalizedMessage({
75
+ sessionId,
76
+ timestamp: ts,
77
+ provider: PROVIDER,
78
+ kind: 'stream_end',
79
+ })];
80
+ if (raw.stats?.total_tokens) {
81
+ messages.push(createNormalizedMessage({
82
+ sessionId,
83
+ timestamp: ts,
84
+ provider: PROVIDER,
85
+ kind: 'status',
86
+ text: 'Complete',
87
+ tokens: raw.stats.total_tokens,
88
+ canInterrupt: false,
89
+ }));
90
+ }
91
+ return messages;
92
+ }
93
+
94
+ if (raw.type === 'error') {
95
+ return [createNormalizedMessage({
96
+ id: baseId,
97
+ sessionId,
98
+ timestamp: ts,
99
+ provider: PROVIDER,
100
+ kind: 'error',
101
+ content: raw.error || raw.message || 'Unknown Qwen Code streaming error',
102
+ })];
103
+ }
104
+
105
+ return [];
106
+ }
107
+
108
+ async fetchHistory(
109
+ sessionId: string,
110
+ options: FetchHistoryOptions = {},
111
+ ): Promise<FetchHistoryResult> {
112
+ const { limit = null, offset = 0 } = options;
113
+
114
+ let rawMessages: AnyRecord[];
115
+ try {
116
+ rawMessages = sessionManager.getSessionMessages(sessionId) as AnyRecord[];
117
+ if (rawMessages.length === 0) {
118
+ rawMessages = await getQwenCliSessionMessages(sessionId) as AnyRecord[];
119
+ }
120
+ } catch (error) {
121
+ const message = error instanceof Error ? error.message : String(error);
122
+ console.warn(`[QwenProvider] Failed to load session ${sessionId}:`, message);
123
+ return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
124
+ }
125
+
126
+ const normalized: NormalizedMessage[] = [];
127
+ for (let i = 0; i < rawMessages.length; i++) {
128
+ const raw = rawMessages[i];
129
+ const ts = raw.timestamp || new Date().toISOString();
130
+ const baseId = raw.uuid || generateMessageId('qwen');
131
+
132
+ const role = raw.message?.role || raw.role;
133
+ const content = raw.message?.content || raw.content;
134
+
135
+ if (!role || !content) continue;
136
+
137
+ const normalizedRole = role === 'user' ? 'user' : 'assistant';
138
+
139
+ if (Array.isArray(content)) {
140
+ for (let partIdx = 0; partIdx < content.length; partIdx++) {
141
+ const part = content[partIdx];
142
+ if (part.type === 'text' && part.text) {
143
+ normalized.push(createNormalizedMessage({
144
+ id: `${baseId}_${partIdx}`,
145
+ sessionId,
146
+ timestamp: ts,
147
+ provider: PROVIDER,
148
+ kind: 'text',
149
+ role: normalizedRole,
150
+ content: part.text,
151
+ }));
152
+ } else if (part.type === 'tool_use') {
153
+ normalized.push(createNormalizedMessage({
154
+ id: `${baseId}_${partIdx}`,
155
+ sessionId,
156
+ timestamp: ts,
157
+ provider: PROVIDER,
158
+ kind: 'tool_use',
159
+ toolName: part.name,
160
+ toolInput: part.input,
161
+ toolId: part.id || generateMessageId('qwen_tool'),
162
+ }));
163
+ } else if (part.type === 'tool_result') {
164
+ normalized.push(createNormalizedMessage({
165
+ id: `${baseId}_${partIdx}`,
166
+ sessionId,
167
+ timestamp: ts,
168
+ provider: PROVIDER,
169
+ kind: 'tool_result',
170
+ toolId: part.tool_use_id || '',
171
+ content: part.content === undefined ? '' : String(part.content),
172
+ isError: Boolean(part.is_error),
173
+ }));
174
+ }
175
+ }
176
+ } else if (typeof content === 'string' && content.trim()) {
177
+ normalized.push(createNormalizedMessage({
178
+ id: baseId,
179
+ sessionId,
180
+ timestamp: ts,
181
+ provider: PROVIDER,
182
+ kind: 'text',
183
+ role: normalizedRole,
184
+ content,
185
+ }));
186
+ }
187
+ }
188
+
189
+ const toolResultMap = new Map<string, NormalizedMessage>();
190
+ for (const msg of normalized) {
191
+ if (msg.kind === 'tool_result' && msg.toolId) {
192
+ toolResultMap.set(msg.toolId, msg);
193
+ }
194
+ }
195
+ for (const msg of normalized) {
196
+ if (msg.kind === 'tool_use' && msg.toolId && toolResultMap.has(msg.toolId)) {
197
+ const toolResult = toolResultMap.get(msg.toolId);
198
+ if (toolResult) {
199
+ msg.toolResult = { content: toolResult.content, isError: toolResult.isError };
200
+ }
201
+ }
202
+ }
203
+
204
+ const start = Math.max(0, offset);
205
+ const pageLimit = limit === null ? null : Math.max(0, limit);
206
+ const messages = pageLimit === null
207
+ ? normalized.slice(start)
208
+ : normalized.slice(start, start + pageLimit);
209
+
210
+ return {
211
+ messages,
212
+ total: normalized.length,
213
+ hasMore: pageLimit === null ? false : start + pageLimit < normalized.length,
214
+ offset: start,
215
+ limit: pageLimit,
216
+ };
217
+ }
218
+ }
@@ -0,0 +1,21 @@
1
+ import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
+ import { QwenProviderAuth } from '@/modules/providers/list/qwen/qwen-auth.provider.js';
3
+ import { QwenMcpProvider } from '@/modules/providers/list/qwen/qwen-mcp.provider.js';
4
+ import { QwenSessionsProvider } from '@/modules/providers/list/qwen/qwen-sessions.provider.js';
5
+ import type { IProviderAuth, IProviderSessions } from '@/shared/interfaces.js';
6
+
7
+ /**
8
+ * Qwen Code provider (Alibaba's Gemini CLI fork). The three sub-providers
9
+ * mirror the Gemini layout — Qwen shares the on-disk layout, config format,
10
+ * and stream-json protocol, so the auth/mcp/sessions modules are intentional
11
+ * structural twins.
12
+ */
13
+ export class QwenProvider extends AbstractProvider {
14
+ readonly mcp = new QwenMcpProvider();
15
+ readonly auth: IProviderAuth = new QwenProviderAuth();
16
+ readonly sessions: IProviderSessions = new QwenSessionsProvider();
17
+
18
+ constructor() {
19
+ super('qwen');
20
+ }
21
+ }
@@ -1,36 +1,38 @@
1
- import { ClaudeProvider } from '@/modules/providers/list/claude/claude.provider.js';
2
- import { CodexProvider } from '@/modules/providers/list/codex/codex.provider.js';
3
- import { CursorProvider } from '@/modules/providers/list/cursor/cursor.provider.js';
4
- import { GeminiProvider } from '@/modules/providers/list/gemini/gemini.provider.js';
5
- import type { IProvider } from '@/shared/interfaces.js';
6
- import type { LLMProvider } from '@/shared/types.js';
7
- import { AppError } from '@/shared/utils.js';
8
-
9
- const providers: Record<LLMProvider, IProvider> = {
10
- claude: new ClaudeProvider(),
11
- codex: new CodexProvider(),
12
- cursor: new CursorProvider(),
13
- gemini: new GeminiProvider(),
14
- };
15
-
16
- /**
17
- * Central registry for resolving concrete provider implementations by id.
18
- */
19
- export const providerRegistry = {
20
- listProviders(): IProvider[] {
21
- return Object.values(providers);
22
- },
23
-
24
- resolveProvider(provider: string): IProvider {
25
- const key = provider as LLMProvider;
26
- const resolvedProvider = providers[key];
27
- if (!resolvedProvider) {
28
- throw new AppError(`Unsupported provider "${provider}".`, {
29
- code: 'UNSUPPORTED_PROVIDER',
30
- statusCode: 400,
31
- });
32
- }
33
-
34
- return resolvedProvider;
35
- },
36
- };
1
+ import { ClaudeProvider } from '@/modules/providers/list/claude/claude.provider.js';
2
+ import { CodexProvider } from '@/modules/providers/list/codex/codex.provider.js';
3
+ import { CursorProvider } from '@/modules/providers/list/cursor/cursor.provider.js';
4
+ import { GeminiProvider } from '@/modules/providers/list/gemini/gemini.provider.js';
5
+ import { QwenProvider } from '@/modules/providers/list/qwen/qwen.provider.js';
6
+ import type { IProvider } from '@/shared/interfaces.js';
7
+ import type { LLMProvider } from '@/shared/types.js';
8
+ import { AppError } from '@/shared/utils.js';
9
+
10
+ const providers: Record<LLMProvider, IProvider> = {
11
+ claude: new ClaudeProvider(),
12
+ codex: new CodexProvider(),
13
+ cursor: new CursorProvider(),
14
+ gemini: new GeminiProvider(),
15
+ qwen: new QwenProvider(),
16
+ };
17
+
18
+ /**
19
+ * Central registry for resolving concrete provider implementations by id.
20
+ */
21
+ export const providerRegistry = {
22
+ listProviders(): IProvider[] {
23
+ return Object.values(providers);
24
+ },
25
+
26
+ resolveProvider(provider: string): IProvider {
27
+ const key = provider as LLMProvider;
28
+ const resolvedProvider = providers[key];
29
+ if (!resolvedProvider) {
30
+ throw new AppError(`Unsupported provider "${provider}".`, {
31
+ code: 'UNSUPPORTED_PROVIDER',
32
+ statusCode: 400,
33
+ });
34
+ }
35
+
36
+ return resolvedProvider;
37
+ },
38
+ };