@cloudcli-ai/cloudcli 1.31.5 → 1.33.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 (252) hide show
  1. package/README.de.md +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/README.ru.md +1 -1
  5. package/README.tr.md +1 -1
  6. package/README.zh-CN.md +1 -1
  7. package/dist/api-docs.html +6 -7
  8. package/dist/assets/index-MOIvpy53.css +32 -0
  9. package/dist/assets/{index-DqMVUeZS.js → index-skXSjBje.js} +290 -270
  10. package/dist/index.html +2 -2
  11. package/dist/modelConstants.js +841 -0
  12. package/dist-server/server/claude-sdk.js +67 -39
  13. package/dist-server/server/claude-sdk.js.map +1 -1
  14. package/dist-server/server/cursor-cli.js +6 -9
  15. package/dist-server/server/cursor-cli.js.map +1 -1
  16. package/dist-server/server/gemini-cli.js +163 -35
  17. package/dist-server/server/gemini-cli.js.map +1 -1
  18. package/dist-server/server/gemini-response-handler.js +34 -0
  19. package/dist-server/server/gemini-response-handler.js.map +1 -1
  20. package/dist-server/server/index.js +131 -19
  21. package/dist-server/server/index.js.map +1 -1
  22. package/dist-server/server/modules/database/index.js +1 -0
  23. package/dist-server/server/modules/database/index.js.map +1 -1
  24. package/dist-server/server/modules/database/migrations.js +11 -0
  25. package/dist-server/server/modules/database/migrations.js.map +1 -1
  26. package/dist-server/server/modules/database/repositories/projects.db.js +12 -0
  27. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
  28. package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +61 -0
  29. package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
  30. package/dist-server/server/modules/database/repositories/sessions.db.js +49 -8
  31. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  32. package/dist-server/server/modules/database/schema.js +1 -0
  33. package/dist-server/server/modules/database/schema.js.map +1 -1
  34. package/dist-server/server/modules/projects/projects.routes.js +12 -3
  35. package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
  36. package/dist-server/server/modules/projects/services/project-delete.service.js +14 -1
  37. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
  38. package/dist-server/server/modules/projects/services/project-management.service.js +1 -0
  39. package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
  40. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +47 -0
  41. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  42. package/dist-server/server/modules/providers/index.js +1 -0
  43. package/dist-server/server/modules/providers/index.js.map +1 -1
  44. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +27 -7
  45. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  46. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +141 -0
  47. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -0
  48. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +55 -2
  49. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  50. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +153 -14
  51. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  52. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
  53. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
  54. package/dist-server/server/modules/providers/list/claude/claude.provider.js +4 -0
  55. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  56. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +84 -0
  57. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -0
  58. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -2
  59. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  60. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +21 -6
  61. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  62. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +50 -0
  63. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
  64. package/dist-server/server/modules/providers/list/codex/codex.provider.js +4 -0
  65. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  66. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +754 -0
  67. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -0
  68. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +8 -30
  69. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  70. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +187 -24
  71. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  72. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
  73. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
  74. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +4 -0
  75. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
  76. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +125 -2
  77. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
  78. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +27 -0
  79. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -0
  80. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +24 -8
  81. package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -1
  82. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +10 -10
  83. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
  84. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +33 -0
  85. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
  86. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +4 -0
  87. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
  88. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +92 -0
  89. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -0
  90. package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js +181 -0
  91. package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js.map +1 -0
  92. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +267 -0
  93. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -0
  94. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +115 -0
  95. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -0
  96. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +410 -0
  97. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -0
  98. package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js +62 -0
  99. package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js.map +1 -0
  100. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +19 -0
  101. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -0
  102. package/dist-server/server/modules/providers/provider.registry.js +2 -0
  103. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  104. package/dist-server/server/modules/providers/provider.routes.js +65 -3
  105. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  106. package/dist-server/server/modules/providers/services/mcp.service.js +1 -9
  107. package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
  108. package/dist-server/server/modules/providers/services/provider-models.service.js +199 -0
  109. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -0
  110. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +129 -13
  111. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  112. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +1 -0
  113. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  114. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +13 -4
  115. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  116. package/dist-server/server/modules/providers/services/sessions.service.js +81 -7
  117. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  118. package/dist-server/server/modules/providers/services/skills.service.js +11 -0
  119. package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
  120. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
  121. package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
  122. package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
  123. package/dist-server/server/modules/providers/tests/mcp.test.js +73 -6
  124. package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
  125. package/dist-server/server/modules/providers/tests/opencode-models.test.js +66 -0
  126. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -0
  127. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +262 -0
  128. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -0
  129. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +270 -0
  130. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -0
  131. package/dist-server/server/modules/providers/tests/skills.test.js +259 -0
  132. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
  133. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +12 -1
  134. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  135. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +4 -4
  136. package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -1
  137. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -1
  138. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  139. package/dist-server/server/openai-codex.js +87 -29
  140. package/dist-server/server/openai-codex.js.map +1 -1
  141. package/dist-server/server/opencode-cli.js +287 -0
  142. package/dist-server/server/opencode-cli.js.map +1 -0
  143. package/dist-server/server/opencode-cli.test.js +84 -0
  144. package/dist-server/server/opencode-cli.test.js.map +1 -0
  145. package/dist-server/server/routes/agent.js +21 -8
  146. package/dist-server/server/routes/agent.js.map +1 -1
  147. package/dist-server/server/routes/commands.js +204 -211
  148. package/dist-server/server/routes/commands.js.map +1 -1
  149. package/dist-server/server/routes/cursor.js +2 -2
  150. package/dist-server/server/routes/cursor.js.map +1 -1
  151. package/dist-server/server/routes/settings.js +0 -10
  152. package/dist-server/server/routes/settings.js.map +1 -1
  153. package/dist-server/server/routes/tests/commands.test.js +76 -0
  154. package/dist-server/server/routes/tests/commands.test.js.map +1 -0
  155. package/dist-server/server/shared/claude-cli-path.js +103 -0
  156. package/dist-server/server/shared/claude-cli-path.js.map +1 -0
  157. package/dist-server/server/shared/claude-cli-path.test.js +45 -0
  158. package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
  159. package/dist-server/server/{utils → shared}/frontmatter.js +3 -3
  160. package/dist-server/server/shared/frontmatter.js.map +1 -0
  161. package/dist-server/server/shared/utils.js +368 -0
  162. package/dist-server/server/shared/utils.js.map +1 -1
  163. package/dist-server/server/utils/commandParser.js +3 -3
  164. package/dist-server/server/utils/commandParser.js.map +1 -1
  165. package/package.json +3 -1
  166. package/public/api-docs.html +878 -0
  167. package/public/modelConstants.js +841 -0
  168. package/server/claude-sdk.js +74 -40
  169. package/server/cursor-cli.js +6 -11
  170. package/server/gemini-cli.js +197 -43
  171. package/server/gemini-response-handler.js +38 -0
  172. package/server/index.js +144 -19
  173. package/server/modules/database/index.ts +1 -0
  174. package/server/modules/database/migrations.ts +12 -0
  175. package/server/modules/database/repositories/projects.db.ts +13 -0
  176. package/server/modules/database/repositories/sessions.db.integration.test.ts +72 -0
  177. package/server/modules/database/repositories/sessions.db.ts +60 -9
  178. package/server/modules/database/schema.ts +1 -0
  179. package/server/modules/projects/projects.routes.ts +20 -3
  180. package/server/modules/projects/services/project-delete.service.ts +16 -1
  181. package/server/modules/projects/services/project-management.service.ts +2 -0
  182. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +71 -1
  183. package/server/modules/providers/README.md +354 -0
  184. package/server/modules/providers/index.ts +2 -1
  185. package/server/modules/providers/list/claude/claude-auth.provider.ts +38 -13
  186. package/server/modules/providers/list/claude/claude-models.provider.ts +191 -0
  187. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +68 -2
  188. package/server/modules/providers/list/claude/claude-sessions.provider.ts +175 -14
  189. package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
  190. package/server/modules/providers/list/claude/claude.provider.ts +11 -1
  191. package/server/modules/providers/list/codex/codex-models.provider.ts +125 -0
  192. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +62 -2
  193. package/server/modules/providers/list/codex/codex-sessions.provider.ts +25 -6
  194. package/server/modules/providers/list/codex/codex-skills.provider.ts +60 -0
  195. package/server/modules/providers/list/codex/codex.provider.ts +11 -1
  196. package/server/modules/providers/list/cursor/cursor-models.provider.ts +820 -0
  197. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +17 -40
  198. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +216 -26
  199. package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
  200. package/server/modules/providers/list/cursor/cursor.provider.ts +11 -1
  201. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +158 -2
  202. package/server/modules/providers/list/gemini/gemini-models.provider.ts +42 -0
  203. package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +24 -20
  204. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +10 -11
  205. package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
  206. package/server/modules/providers/list/gemini/gemini.provider.ts +11 -1
  207. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +111 -0
  208. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -0
  209. package/server/modules/providers/list/opencode/opencode-models.provider.ts +339 -0
  210. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +157 -0
  211. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +506 -0
  212. package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -0
  213. package/server/modules/providers/list/opencode/opencode.provider.ts +27 -0
  214. package/server/modules/providers/provider.registry.ts +2 -0
  215. package/server/modules/providers/provider.routes.ts +97 -4
  216. package/server/modules/providers/services/mcp.service.ts +1 -12
  217. package/server/modules/providers/services/provider-models.service.ts +325 -0
  218. package/server/modules/providers/services/session-conversations-search.service.ts +162 -13
  219. package/server/modules/providers/services/session-synchronizer.service.ts +1 -0
  220. package/server/modules/providers/services/sessions-watcher.service.ts +14 -4
  221. package/server/modules/providers/services/sessions.service.ts +112 -9
  222. package/server/modules/providers/services/skills.service.ts +15 -0
  223. package/server/modules/providers/shared/base/abstract.provider.ts +4 -0
  224. package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
  225. package/server/modules/providers/tests/mcp.test.ts +93 -6
  226. package/server/modules/providers/tests/opencode-models.test.ts +73 -0
  227. package/server/modules/providers/tests/opencode-sessions.test.ts +334 -0
  228. package/server/modules/providers/tests/provider-models.service.test.ts +318 -0
  229. package/server/modules/providers/tests/skills.test.ts +512 -0
  230. package/server/modules/websocket/services/chat-websocket.service.ts +15 -1
  231. package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +4 -4
  232. package/server/modules/websocket/services/shell-websocket.service.ts +9 -0
  233. package/server/openai-codex.js +99 -30
  234. package/server/opencode-cli.js +336 -0
  235. package/server/opencode-cli.test.js +95 -0
  236. package/server/routes/agent.js +22 -8
  237. package/server/routes/commands.js +256 -233
  238. package/server/routes/cursor.js +2 -2
  239. package/server/routes/settings.js +1 -10
  240. package/server/routes/tests/commands.test.js +82 -0
  241. package/server/shared/claude-cli-path.test.ts +61 -0
  242. package/server/shared/claude-cli-path.ts +139 -0
  243. package/server/{utils/frontmatter.js → shared/frontmatter.ts} +3 -3
  244. package/server/shared/interfaces.ts +64 -0
  245. package/server/shared/types.ts +166 -1
  246. package/server/shared/utils.ts +478 -0
  247. package/server/utils/commandParser.js +5 -3
  248. package/dist/assets/index-6rYG_mRo.css +0 -32
  249. package/dist-server/server/utils/frontmatter.js.map +0 -1
  250. package/dist-server/shared/modelConstants.js +0 -98
  251. package/dist-server/shared/modelConstants.js.map +0 -1
  252. package/shared/modelConstants.js +0 -106
@@ -0,0 +1,42 @@
1
+ import type { IProviderModels } from '@/shared/interfaces.js';
2
+ import type {
3
+ ProviderChangeActiveModelInput,
4
+ ProviderCurrentActiveModel,
5
+ ProviderModelsDefinition,
6
+ ProviderSessionActiveModelChange,
7
+ } from '@/shared/types.js';
8
+ import {
9
+ buildDefaultProviderCurrentActiveModel,
10
+ writeProviderSessionActiveModelChange,
11
+ } from '@/shared/utils.js';
12
+
13
+ export const GEMINI_FALLBACK_MODELS: ProviderModelsDefinition = {
14
+ OPTIONS: [
15
+ { value: 'gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro Preview' },
16
+ { value: 'gemini-3-pro-preview', label: 'Gemini 3 Pro Preview' },
17
+ { value: 'gemini-3-flash-preview', label: 'Gemini 3 Flash Preview' },
18
+ { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
19
+ { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' },
20
+ { value: 'gemini-2.0-flash-lite', label: 'Gemini 2.0 Flash Lite' },
21
+ { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' },
22
+ { value: 'gemini-2.0-pro-exp', label: 'Gemini 2.0 Pro Experimental' },
23
+ { value: 'gemini-2.0-flash-thinking-exp', label: 'Gemini 2.0 Flash Thinking' },
24
+ ],
25
+ DEFAULT: 'gemini-3.1-pro-preview',
26
+ };
27
+
28
+ export class GeminiProviderModels implements IProviderModels {
29
+ async getSupportedModels(): Promise<ProviderModelsDefinition> {
30
+ return GEMINI_FALLBACK_MODELS;
31
+ }
32
+
33
+ async getCurrentActiveModel(): Promise<ProviderCurrentActiveModel> {
34
+ return buildDefaultProviderCurrentActiveModel(GEMINI_FALLBACK_MODELS);
35
+ }
36
+
37
+ async changeActiveModel(
38
+ input: ProviderChangeActiveModelInput,
39
+ ): Promise<ProviderSessionActiveModelChange> {
40
+ return writeProviderSessionActiveModelChange('gemini', input);
41
+ }
42
+ }
@@ -39,33 +39,37 @@ export class GeminiSessionSynchronizer implements IProviderSessionSynchronizer {
39
39
  async synchronize(since?: Date): Promise<number> {
40
40
  const projectHashLookup = this.buildProjectHashLookup();
41
41
 
42
- const legacySessionFiles = await findFilesRecursivelyCreatedAfter(
43
- path.join(this.geminiHome, 'sessions'),
44
- '.json',
45
- since ?? null
46
- );
47
- const legacyTempFiles = await findFilesRecursivelyCreatedAfter(
48
- path.join(this.geminiHome, 'tmp'),
49
- '.json',
50
- since ?? null
51
- );
52
- const jsonlSessionFiles = await findFilesRecursivelyCreatedAfter(
53
- path.join(this.geminiHome, 'sessions'),
54
- '.jsonl',
55
- since ?? null
56
- );
42
+ // const legacySessionFiles = await findFilesRecursivelyCreatedAfter(
43
+ // path.join(this.geminiHome, 'sessions'),
44
+ // '.json',
45
+ // since ?? null
46
+ // );
47
+ // Gemini creates overlapping artifacts across `sessions/` and `tmp/`.
48
+ // We currently index only `tmp/*/chats/*.jsonl` because those files are the
49
+ // live transcript source and avoid duplicate session rows from mirrored files.
50
+ // const legacyTempFiles = await findFilesRecursivelyCreatedAfter(
51
+ // path.join(this.geminiHome, 'tmp'),
52
+ // '.json',
53
+ // since ?? null
54
+ // );
55
+ // const jsonlSessionFiles = await findFilesRecursivelyCreatedAfter(
56
+ // path.join(this.geminiHome, 'sessions'),
57
+ // '.jsonl',
58
+ // since ?? null
59
+ // );
57
60
  const jsonlTempFiles = await findFilesRecursivelyCreatedAfter(
58
61
  path.join(this.geminiHome, 'tmp'),
59
62
  '.jsonl',
60
63
  since ?? null
61
64
  );
62
65
 
63
- // Process legacy JSON first, then JSONL. If both exist for a session id,
64
- // the JSONL artifact becomes the canonical jsonl_path via upsert.
66
+ // Current strategy: index only temp chat JSONL artifacts.
65
67
  const files = [
66
- ...legacySessionFiles,
67
- ...legacyTempFiles,
68
- ...jsonlSessionFiles,
68
+ // ...legacySessionFiles,
69
+ // Intentionally disabled to avoid duplicate indexing from mirrored
70
+ // `sessions/*.json` and `sessions/*.jsonl` artifacts.
71
+ // ...legacyTempFiles,
72
+ // ...jsonlSessionFiles,
69
73
  ...jsonlTempFiles,
70
74
  ];
71
75
 
@@ -88,22 +88,15 @@ function buildGeminiTokenUsage(tokens: unknown): AnyRecord | undefined {
88
88
  const record = tokens as AnyRecord;
89
89
  const input = Number(record.input || 0);
90
90
  const output = Number(record.output || 0);
91
- const cached = Number(record.cached || 0);
92
- const thoughts = Number(record.thoughts || 0);
93
- const tool = Number(record.tool || 0);
94
-
95
- const totalFromFields = input + output + cached + thoughts + tool;
96
- const total = Number(record.total || totalFromFields || 0);
91
+ const total = Number(record.total || input + output || 0);
97
92
 
98
93
  return {
99
94
  used: total,
100
- total: total,
95
+ inputTokens: input,
96
+ outputTokens: output,
101
97
  breakdown: {
102
98
  input,
103
99
  output,
104
- cached,
105
- thoughts,
106
- tool,
107
100
  },
108
101
  };
109
102
  }
@@ -528,10 +521,16 @@ export class GeminiSessionsProvider implements IProviderSessions {
528
521
  const messages = pageLimit === null
529
522
  ? normalized.slice(start)
530
523
  : normalized.slice(start, start + pageLimit);
524
+ let total = 0;
525
+ for (const msg of normalized) {
526
+ if (msg.kind !== 'tool_result') {
527
+ total += 1;
528
+ }
529
+ }
531
530
 
532
531
  return {
533
532
  messages,
534
- total: normalized.length,
533
+ total,
535
534
  hasMore: pageLimit === null ? false : start + pageLimit < normalized.length,
536
535
  offset: start,
537
536
  limit: pageLimit,
@@ -0,0 +1,36 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ import { SkillsProvider } from '@/modules/providers/shared/skills/skills.provider.js';
5
+ import type { ProviderSkillSource } from '@/shared/types.js';
6
+
7
+ export class GeminiSkillsProvider extends SkillsProvider {
8
+ constructor() {
9
+ super('gemini');
10
+ }
11
+
12
+ protected async getSkillSources(workspacePath: string): Promise<ProviderSkillSource[]> {
13
+ return [
14
+ {
15
+ scope: 'user',
16
+ rootDir: path.join(os.homedir(), '.gemini', 'skills'),
17
+ commandPrefix: '/',
18
+ },
19
+ {
20
+ scope: 'user',
21
+ rootDir: path.join(os.homedir(), '.agents', 'skills'),
22
+ commandPrefix: '/',
23
+ },
24
+ {
25
+ scope: 'project',
26
+ rootDir: path.join(workspacePath, '.gemini', 'skills'),
27
+ commandPrefix: '/',
28
+ },
29
+ {
30
+ scope: 'project',
31
+ rootDir: path.join(workspacePath, '.agents', 'skills'),
32
+ commandPrefix: '/',
33
+ },
34
+ ];
35
+ }
36
+ }
@@ -1,13 +1,23 @@
1
1
  import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provider.js';
2
2
  import { GeminiProviderAuth } from '@/modules/providers/list/gemini/gemini-auth.provider.js';
3
+ import { GeminiProviderModels } from '@/modules/providers/list/gemini/gemini-models.provider.js';
3
4
  import { GeminiMcpProvider } from '@/modules/providers/list/gemini/gemini-mcp.provider.js';
4
5
  import { GeminiSessionSynchronizer } from '@/modules/providers/list/gemini/gemini-session-synchronizer.provider.js';
5
6
  import { GeminiSessionsProvider } from '@/modules/providers/list/gemini/gemini-sessions.provider.js';
6
- import type { IProviderAuth, IProviderSessionSynchronizer, IProviderSessions } from '@/shared/interfaces.js';
7
+ import { GeminiSkillsProvider } from '@/modules/providers/list/gemini/gemini-skills.provider.js';
8
+ import type {
9
+ IProviderAuth,
10
+ IProviderModels,
11
+ IProviderSessionSynchronizer,
12
+ IProviderSkills,
13
+ IProviderSessions,
14
+ } from '@/shared/interfaces.js';
7
15
 
8
16
  export class GeminiProvider extends AbstractProvider {
17
+ readonly models: IProviderModels = new GeminiProviderModels();
9
18
  readonly mcp = new GeminiMcpProvider();
10
19
  readonly auth: IProviderAuth = new GeminiProviderAuth();
20
+ readonly skills: IProviderSkills = new GeminiSkillsProvider();
11
21
  readonly sessions: IProviderSessions = new GeminiSessionsProvider();
12
22
  readonly sessionSynchronizer: IProviderSessionSynchronizer = new GeminiSessionSynchronizer();
13
23
 
@@ -0,0 +1,111 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ import spawn from 'cross-spawn';
6
+
7
+ import type { IProviderAuth } from '@/shared/interfaces.js';
8
+ import type { ProviderAuthStatus } from '@/shared/types.js';
9
+ import { readObjectRecord, readOptionalString } from '@/shared/utils.js';
10
+
11
+ type OpenCodeCredentialsStatus = {
12
+ authenticated: boolean;
13
+ email: string | null;
14
+ method: string | null;
15
+ error?: string;
16
+ };
17
+
18
+ const OPENCODE_ENV_CREDENTIAL_KEYS = [
19
+ 'ANTHROPIC_API_KEY',
20
+ 'OPENAI_API_KEY',
21
+ 'GOOGLE_GENERATIVE_AI_API_KEY',
22
+ 'GEMINI_API_KEY',
23
+ 'GROQ_API_KEY',
24
+ 'OPENROUTER_API_KEY',
25
+ ];
26
+
27
+ export class OpenCodeProviderAuth implements IProviderAuth {
28
+ /**
29
+ * Checks whether the OpenCode CLI is available to the server process.
30
+ */
31
+ private checkInstalled(): boolean {
32
+ try {
33
+ const result = spawn.sync('opencode', ['--version'], { stdio: 'ignore', timeout: 5000 });
34
+ return !result.error && result.status === 0;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Returns OpenCode CLI installation and credential status.
42
+ */
43
+ async getStatus(): Promise<ProviderAuthStatus> {
44
+ const installed = this.checkInstalled();
45
+ const credentials = await this.checkCredentials();
46
+
47
+ return {
48
+ installed,
49
+ provider: 'opencode',
50
+ authenticated: credentials.authenticated,
51
+ email: credentials.email,
52
+ method: credentials.method,
53
+ error: credentials.authenticated ? undefined : credentials.error || 'Not authenticated',
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Reads OpenCode's auth store or falls back to provider API key environment variables.
59
+ */
60
+ private async checkCredentials(): Promise<OpenCodeCredentialsStatus> {
61
+ try {
62
+ const authPath = path.join(os.homedir(), '.local', 'share', 'opencode', 'auth.json');
63
+ const content = await readFile(authPath, 'utf8');
64
+ const auth = readObjectRecord(JSON.parse(content)) ?? {};
65
+
66
+ for (const [providerId, providerAuth] of Object.entries(auth)) {
67
+ const providerRecord = readObjectRecord(providerAuth);
68
+ if (!providerRecord) {
69
+ continue;
70
+ }
71
+
72
+ const hasCredential = Object.values(providerRecord).some(
73
+ (value) => readOptionalString(value) !== undefined || Boolean(readObjectRecord(value)),
74
+ );
75
+ if (hasCredential) {
76
+ return {
77
+ authenticated: true,
78
+ email: `${providerId} credentials`,
79
+ method: 'credentials_file',
80
+ };
81
+ }
82
+ }
83
+ } catch (error) {
84
+ const code = (error as NodeJS.ErrnoException).code;
85
+ if (code !== 'ENOENT') {
86
+ return {
87
+ authenticated: false,
88
+ email: null,
89
+ method: null,
90
+ error: error instanceof Error ? error.message : 'Failed to read OpenCode auth',
91
+ };
92
+ }
93
+ }
94
+
95
+ const envCredential = OPENCODE_ENV_CREDENTIAL_KEYS.find((key) => process.env[key]?.trim());
96
+ if (envCredential) {
97
+ return {
98
+ authenticated: true,
99
+ email: envCredential,
100
+ method: 'environment',
101
+ };
102
+ }
103
+
104
+ return {
105
+ authenticated: false,
106
+ email: null,
107
+ method: null,
108
+ error: 'OpenCode not configured',
109
+ };
110
+ }
111
+ }
@@ -0,0 +1,228 @@
1
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ import { McpProvider } from '@/modules/providers/shared/mcp/mcp.provider.js';
6
+ import type { McpScope, ProviderMcpServer, UpsertProviderMcpServerInput } from '@/shared/types.js';
7
+ import {
8
+ AppError,
9
+ readObjectRecord,
10
+ readOptionalString,
11
+ readStringArray,
12
+ readStringRecord,
13
+ } from '@/shared/utils.js';
14
+
15
+ type OpenCodeConfigPath = {
16
+ filePath: string;
17
+ exists: boolean;
18
+ };
19
+
20
+ const fileExists = async (filePath: string): Promise<boolean> => {
21
+ try {
22
+ await access(filePath);
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ };
28
+
29
+ /**
30
+ * Removes JSONC comments without touching comment-like text inside strings.
31
+ */
32
+ const stripJsonComments = (content: string): string => {
33
+ let output = '';
34
+ let inString = false;
35
+ let quote = '';
36
+ let escaped = false;
37
+
38
+ for (let index = 0; index < content.length; index += 1) {
39
+ const char = content[index];
40
+ const next = content[index + 1];
41
+
42
+ if (inString) {
43
+ output += char;
44
+ if (escaped) {
45
+ escaped = false;
46
+ } else if (char === '\\') {
47
+ escaped = true;
48
+ } else if (char === quote) {
49
+ inString = false;
50
+ quote = '';
51
+ }
52
+ continue;
53
+ }
54
+
55
+ if (char === '"' || char === '\'') {
56
+ inString = true;
57
+ quote = char;
58
+ output += char;
59
+ continue;
60
+ }
61
+
62
+ if (char === '/' && next === '/') {
63
+ while (index < content.length && content[index] !== '\n') {
64
+ index += 1;
65
+ }
66
+ output += '\n';
67
+ continue;
68
+ }
69
+
70
+ if (char === '/' && next === '*') {
71
+ index += 2;
72
+ while (index < content.length && !(content[index] === '*' && content[index + 1] === '/')) {
73
+ index += 1;
74
+ }
75
+ index += 1;
76
+ continue;
77
+ }
78
+
79
+ output += char;
80
+ }
81
+
82
+ return output;
83
+ };
84
+
85
+ const stripTrailingCommas = (content: string): string =>
86
+ content.replace(/,\s*([}\]])/g, '$1');
87
+
88
+ const readOpenCodeConfig = async (filePath: string): Promise<Record<string, unknown>> => {
89
+ try {
90
+ const content = await readFile(filePath, 'utf8');
91
+ const parsed = JSON.parse(stripTrailingCommas(stripJsonComments(content))) as unknown;
92
+ return readObjectRecord(parsed) ?? {};
93
+ } catch (error) {
94
+ const code = (error as NodeJS.ErrnoException).code;
95
+ if (code === 'ENOENT') {
96
+ return {};
97
+ }
98
+
99
+ throw error;
100
+ }
101
+ };
102
+
103
+ const writeOpenCodeConfig = async (filePath: string, data: Record<string, unknown>): Promise<void> => {
104
+ await mkdir(path.dirname(filePath), { recursive: true });
105
+ await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
106
+ };
107
+
108
+ const resolveOpenCodeConfigPath = async (scope: McpScope, workspacePath: string): Promise<OpenCodeConfigPath> => {
109
+ const root = scope === 'user'
110
+ ? path.join(os.homedir(), '.config', 'opencode')
111
+ : workspacePath;
112
+ const jsonPath = path.join(root, 'opencode.json');
113
+ const jsoncPath = path.join(root, 'opencode.jsonc');
114
+
115
+ if (await fileExists(jsonPath)) {
116
+ return { filePath: jsonPath, exists: true };
117
+ }
118
+
119
+ if (await fileExists(jsoncPath)) {
120
+ return { filePath: jsoncPath, exists: true };
121
+ }
122
+
123
+ return { filePath: jsonPath, exists: false };
124
+ };
125
+
126
+ export class OpenCodeMcpProvider extends McpProvider {
127
+ constructor() {
128
+ super('opencode', ['user', 'project'], ['stdio', 'http']);
129
+ }
130
+
131
+ protected async readScopedServers(scope: McpScope, workspacePath: string): Promise<Record<string, unknown>> {
132
+ const { filePath } = await resolveOpenCodeConfigPath(scope, workspacePath);
133
+ const config = await readOpenCodeConfig(filePath);
134
+ return readObjectRecord(config.mcp) ?? {};
135
+ }
136
+
137
+ protected async writeScopedServers(
138
+ scope: McpScope,
139
+ workspacePath: string,
140
+ servers: Record<string, unknown>,
141
+ ): Promise<void> {
142
+ const { filePath } = await resolveOpenCodeConfigPath(scope, workspacePath);
143
+ const config = await readOpenCodeConfig(filePath);
144
+ config.mcp = servers;
145
+ await writeOpenCodeConfig(filePath, config);
146
+ }
147
+
148
+ protected buildServerConfig(input: UpsertProviderMcpServerInput): Record<string, unknown> {
149
+ if (input.transport === 'stdio') {
150
+ if (!input.command?.trim()) {
151
+ throw new AppError('command is required for stdio MCP servers.', {
152
+ code: 'MCP_COMMAND_REQUIRED',
153
+ statusCode: 400,
154
+ });
155
+ }
156
+
157
+ return {
158
+ type: 'local',
159
+ command: [input.command, ...(input.args ?? [])],
160
+ enabled: true,
161
+ environment: input.env ?? {},
162
+ };
163
+ }
164
+
165
+ if (!input.url?.trim()) {
166
+ throw new AppError('url is required for http MCP servers.', {
167
+ code: 'MCP_URL_REQUIRED',
168
+ statusCode: 400,
169
+ });
170
+ }
171
+
172
+ return {
173
+ type: 'remote',
174
+ url: input.url,
175
+ enabled: true,
176
+ headers: input.headers ?? {},
177
+ };
178
+ }
179
+
180
+ protected normalizeServerConfig(
181
+ scope: McpScope,
182
+ name: string,
183
+ rawConfig: unknown,
184
+ ): ProviderMcpServer | null {
185
+ const config = readObjectRecord(rawConfig);
186
+ if (!config) {
187
+ return null;
188
+ }
189
+
190
+ if (config.type === 'local' || config.command !== undefined) {
191
+ const commandParts = typeof config.command === 'string'
192
+ ? [config.command, ...(readStringArray(config.args) ?? [])]
193
+ : readStringArray(config.command);
194
+ const command = commandParts?.[0];
195
+ if (!command) {
196
+ return null;
197
+ }
198
+
199
+ return {
200
+ provider: 'opencode',
201
+ name,
202
+ scope,
203
+ transport: 'stdio',
204
+ command,
205
+ args: commandParts.slice(1),
206
+ env: readStringRecord(config.environment) ?? readStringRecord(config.env),
207
+ };
208
+ }
209
+
210
+ if (config.type === 'remote' || typeof config.url === 'string') {
211
+ const url = readOptionalString(config.url);
212
+ if (!url) {
213
+ return null;
214
+ }
215
+
216
+ return {
217
+ provider: 'opencode',
218
+ name,
219
+ scope,
220
+ transport: 'http',
221
+ url,
222
+ headers: readStringRecord(config.headers),
223
+ };
224
+ }
225
+
226
+ return null;
227
+ }
228
+ }