@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.
- package/README.de.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/api-docs.html +6 -7
- package/dist/assets/index-MOIvpy53.css +32 -0
- package/dist/assets/{index-DqMVUeZS.js → index-skXSjBje.js} +290 -270
- package/dist/index.html +2 -2
- package/dist/modelConstants.js +841 -0
- package/dist-server/server/claude-sdk.js +67 -39
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +6 -9
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +163 -35
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +34 -0
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +131 -19
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/database/index.js +1 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +11 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/projects.db.js +12 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +61 -0
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +49 -8
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +1 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/projects/projects.routes.js +12 -3
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +14 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-management.service.js +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +47 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +1 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +27 -7
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +141 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +55 -2
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +153 -14
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +4 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -2
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +21 -6
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +50 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +4 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +754 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +8 -30
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +187 -24
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +4 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +125 -2
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +27 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +24 -8
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +10 -10
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +33 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +4 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +92 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js +181 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +267 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +115 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +410 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js +62 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +19 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +2 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +65 -3
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/mcp.service.js +1 -9
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +199 -0
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +129 -13
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +13 -4
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +81 -7
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/skills.service.js +11 -0
- package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +73 -6
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +66 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +262 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +270 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/skills.test.js +259 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +12 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +4 -4
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/openai-codex.js +87 -29
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +287 -0
- package/dist-server/server/opencode-cli.js.map +1 -0
- package/dist-server/server/opencode-cli.test.js +84 -0
- package/dist-server/server/opencode-cli.test.js.map +1 -0
- package/dist-server/server/routes/agent.js +21 -8
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/commands.js +204 -211
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +2 -2
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/settings.js +0 -10
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/tests/commands.test.js +76 -0
- package/dist-server/server/routes/tests/commands.test.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.js +103 -0
- package/dist-server/server/shared/claude-cli-path.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.test.js +45 -0
- package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
- package/dist-server/server/{utils → shared}/frontmatter.js +3 -3
- package/dist-server/server/shared/frontmatter.js.map +1 -0
- package/dist-server/server/shared/utils.js +368 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/commandParser.js +3 -3
- package/dist-server/server/utils/commandParser.js.map +1 -1
- package/package.json +3 -1
- package/public/api-docs.html +878 -0
- package/public/modelConstants.js +841 -0
- package/server/claude-sdk.js +74 -40
- package/server/cursor-cli.js +6 -11
- package/server/gemini-cli.js +197 -43
- package/server/gemini-response-handler.js +38 -0
- package/server/index.js +144 -19
- package/server/modules/database/index.ts +1 -0
- package/server/modules/database/migrations.ts +12 -0
- package/server/modules/database/repositories/projects.db.ts +13 -0
- package/server/modules/database/repositories/sessions.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/sessions.db.ts +60 -9
- package/server/modules/database/schema.ts +1 -0
- package/server/modules/projects/projects.routes.ts +20 -3
- package/server/modules/projects/services/project-delete.service.ts +16 -1
- package/server/modules/projects/services/project-management.service.ts +2 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +71 -1
- package/server/modules/providers/README.md +354 -0
- package/server/modules/providers/index.ts +2 -1
- package/server/modules/providers/list/claude/claude-auth.provider.ts +38 -13
- package/server/modules/providers/list/claude/claude-models.provider.ts +191 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +68 -2
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +175 -14
- package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
- package/server/modules/providers/list/claude/claude.provider.ts +11 -1
- package/server/modules/providers/list/codex/codex-models.provider.ts +125 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +62 -2
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +25 -6
- package/server/modules/providers/list/codex/codex-skills.provider.ts +60 -0
- package/server/modules/providers/list/codex/codex.provider.ts +11 -1
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +820 -0
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +17 -40
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +216 -26
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +11 -1
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +158 -2
- package/server/modules/providers/list/gemini/gemini-models.provider.ts +42 -0
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +24 -20
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +10 -11
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +11 -1
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +111 -0
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -0
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +339 -0
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +157 -0
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +506 -0
- package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -0
- package/server/modules/providers/list/opencode/opencode.provider.ts +27 -0
- package/server/modules/providers/provider.registry.ts +2 -0
- package/server/modules/providers/provider.routes.ts +97 -4
- package/server/modules/providers/services/mcp.service.ts +1 -12
- package/server/modules/providers/services/provider-models.service.ts +325 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +162 -13
- package/server/modules/providers/services/session-synchronizer.service.ts +1 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +14 -4
- package/server/modules/providers/services/sessions.service.ts +112 -9
- package/server/modules/providers/services/skills.service.ts +15 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +4 -0
- package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
- package/server/modules/providers/tests/mcp.test.ts +93 -6
- package/server/modules/providers/tests/opencode-models.test.ts +73 -0
- package/server/modules/providers/tests/opencode-sessions.test.ts +334 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +318 -0
- package/server/modules/providers/tests/skills.test.ts +512 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +15 -1
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +4 -4
- package/server/modules/websocket/services/shell-websocket.service.ts +9 -0
- package/server/openai-codex.js +99 -30
- package/server/opencode-cli.js +336 -0
- package/server/opencode-cli.test.js +95 -0
- package/server/routes/agent.js +22 -8
- package/server/routes/commands.js +256 -233
- package/server/routes/cursor.js +2 -2
- package/server/routes/settings.js +1 -10
- package/server/routes/tests/commands.test.js +82 -0
- package/server/shared/claude-cli-path.test.ts +61 -0
- package/server/shared/claude-cli-path.ts +139 -0
- package/server/{utils/frontmatter.js → shared/frontmatter.ts} +3 -3
- package/server/shared/interfaces.ts +64 -0
- package/server/shared/types.ts +166 -1
- package/server/shared/utils.ts +478 -0
- package/server/utils/commandParser.js +5 -3
- package/dist/assets/index-6rYG_mRo.css +0 -32
- package/dist-server/server/utils/frontmatter.js.map +0 -1
- package/dist-server/shared/modelConstants.js +0 -98
- package/dist-server/shared/modelConstants.js.map +0 -1
- package/shared/modelConstants.js +0 -106
|
@@ -18,19 +18,25 @@ const PROVIDER_WATCH_PATHS: Array<{ provider: LLMProvider; rootPath: string }> =
|
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
provider: 'cursor',
|
|
21
|
-
rootPath: path.join(os.homedir(), '.cursor', '
|
|
21
|
+
rootPath: path.join(os.homedir(), '.cursor', 'projects'),
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
provider: 'codex',
|
|
25
25
|
rootPath: path.join(os.homedir(), '.codex', 'sessions'),
|
|
26
26
|
},
|
|
27
|
+
// {
|
|
28
|
+
// provider: 'gemini',
|
|
29
|
+
// rootPath: path.join(os.homedir(), '.gemini', 'sessions'),
|
|
30
|
+
// },
|
|
31
|
+
// Keep `sessions/` watcher disabled: Gemini also mirrors artifacts there,
|
|
32
|
+
// which causes duplicate synchronization events.
|
|
27
33
|
{
|
|
28
34
|
provider: 'gemini',
|
|
29
|
-
rootPath: path.join(os.homedir(), '.gemini', '
|
|
35
|
+
rootPath: path.join(os.homedir(), '.gemini', 'tmp'),
|
|
30
36
|
},
|
|
31
37
|
{
|
|
32
|
-
provider: '
|
|
33
|
-
rootPath: path.join(os.homedir(), '.
|
|
38
|
+
provider: 'opencode',
|
|
39
|
+
rootPath: path.join(os.homedir(), '.local', 'share', 'opencode'),
|
|
34
40
|
},
|
|
35
41
|
];
|
|
36
42
|
|
|
@@ -65,6 +71,10 @@ let watcherRescheduleAfterRefresh = false;
|
|
|
65
71
|
* Filters watcher events to provider-specific session artifact file types.
|
|
66
72
|
*/
|
|
67
73
|
function isWatcherTargetFile(provider: LLMProvider, filePath: string): boolean {
|
|
74
|
+
if (provider === 'opencode') {
|
|
75
|
+
return path.basename(filePath) === 'opencode.db';
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
if (provider === 'gemini') {
|
|
69
79
|
return filePath.endsWith('.json') || filePath.endsWith('.jsonl');
|
|
70
80
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fsp from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
|
|
3
|
-
import { sessionsDb } from '@/modules/database/index.js';
|
|
4
|
+
import { projectsDb, sessionsDb } from '@/modules/database/index.js';
|
|
4
5
|
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
5
6
|
import type {
|
|
6
7
|
FetchHistoryOptions,
|
|
@@ -10,6 +11,19 @@ import type {
|
|
|
10
11
|
} from '@/shared/types.js';
|
|
11
12
|
import { AppError } from '@/shared/utils.js';
|
|
12
13
|
|
|
14
|
+
type ArchivedSessionListItem = {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
provider: LLMProvider;
|
|
17
|
+
projectId: string | null;
|
|
18
|
+
projectPath: string | null;
|
|
19
|
+
projectDisplayName: string;
|
|
20
|
+
sessionTitle: string;
|
|
21
|
+
createdAt: string | null;
|
|
22
|
+
updatedAt: string | null;
|
|
23
|
+
lastActivity: string | null;
|
|
24
|
+
isProjectArchived: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
13
27
|
/**
|
|
14
28
|
* Removes one file if it exists.
|
|
15
29
|
*/
|
|
@@ -26,6 +40,28 @@ async function removeFileIfExists(filePath: string): Promise<boolean> {
|
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Archive rows need a stable project label even when the owning project is not
|
|
45
|
+
* part of the active sidebar payload. This lightweight resolver keeps the
|
|
46
|
+
* archive API self-contained while still matching the project's stored display
|
|
47
|
+
* name when one exists.
|
|
48
|
+
*/
|
|
49
|
+
function resolveProjectDisplayName(
|
|
50
|
+
projectPath: string | null,
|
|
51
|
+
customProjectName: string | null | undefined,
|
|
52
|
+
): string {
|
|
53
|
+
const trimmedCustomName = typeof customProjectName === 'string' ? customProjectName.trim() : '';
|
|
54
|
+
if (trimmedCustomName.length > 0) {
|
|
55
|
+
return trimmedCustomName;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!projectPath) {
|
|
59
|
+
return 'Unknown Project';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return path.basename(projectPath) || projectPath;
|
|
63
|
+
}
|
|
64
|
+
|
|
29
65
|
/**
|
|
30
66
|
* Application service for provider-backed session message operations.
|
|
31
67
|
*
|
|
@@ -79,15 +115,53 @@ export const sessionsService = {
|
|
|
79
115
|
},
|
|
80
116
|
|
|
81
117
|
/**
|
|
82
|
-
*
|
|
118
|
+
* Returns archived sessions with enough project metadata for the sidebar to
|
|
119
|
+
* group, filter, open, and restore them without a per-row follow-up query.
|
|
120
|
+
*/
|
|
121
|
+
listArchivedSessions(): ArchivedSessionListItem[] {
|
|
122
|
+
const archivedSessions = sessionsDb.getArchivedSessions();
|
|
123
|
+
const projectCache = new Map<string, ReturnType<typeof projectsDb.getProjectPath>>();
|
|
124
|
+
|
|
125
|
+
return archivedSessions.map((session) => {
|
|
126
|
+
const projectPath = session.project_path?.trim() ? session.project_path : null;
|
|
127
|
+
let project = null;
|
|
128
|
+
|
|
129
|
+
if (projectPath) {
|
|
130
|
+
if (!projectCache.has(projectPath)) {
|
|
131
|
+
projectCache.set(projectPath, projectsDb.getProjectPath(projectPath));
|
|
132
|
+
}
|
|
133
|
+
project = projectCache.get(projectPath) ?? null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
sessionId: session.session_id,
|
|
138
|
+
provider: session.provider as LLMProvider,
|
|
139
|
+
projectId: project?.project_id ?? null,
|
|
140
|
+
projectPath,
|
|
141
|
+
projectDisplayName: resolveProjectDisplayName(projectPath, project?.custom_project_name),
|
|
142
|
+
sessionTitle: session.custom_name?.trim() || session.session_id,
|
|
143
|
+
createdAt: session.created_at ?? null,
|
|
144
|
+
updatedAt: session.updated_at ?? null,
|
|
145
|
+
lastActivity: session.updated_at ?? session.created_at ?? null,
|
|
146
|
+
isProjectArchived: Boolean(project?.isArchived),
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Archives or permanently deletes one persisted session row by id.
|
|
83
153
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
154
|
+
* Soft-delete mirrors the project behavior by toggling `isArchived` so the
|
|
155
|
+
* row disappears from active lists but remains restorable. Force-delete
|
|
156
|
+
* optionally removes the transcript file before deleting the database row.
|
|
86
157
|
*/
|
|
87
|
-
async
|
|
158
|
+
async deleteOrArchiveSessionById(
|
|
88
159
|
sessionId: string,
|
|
89
|
-
|
|
90
|
-
|
|
160
|
+
options: {
|
|
161
|
+
force?: boolean;
|
|
162
|
+
deletedFromDisk?: boolean;
|
|
163
|
+
} = {},
|
|
164
|
+
): Promise<{ sessionId: string; action: 'archived' | 'deleted'; deletedFromDisk: boolean }> {
|
|
91
165
|
const session = sessionsDb.getSessionById(sessionId);
|
|
92
166
|
if (!session) {
|
|
93
167
|
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
@@ -96,8 +170,17 @@ export const sessionsService = {
|
|
|
96
170
|
});
|
|
97
171
|
}
|
|
98
172
|
|
|
173
|
+
if (!options.force) {
|
|
174
|
+
sessionsDb.updateSessionIsArchived(sessionId, true);
|
|
175
|
+
return {
|
|
176
|
+
sessionId,
|
|
177
|
+
action: 'archived',
|
|
178
|
+
deletedFromDisk: false,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
99
182
|
let removedFromDisk = false;
|
|
100
|
-
if (deletedFromDisk && session.jsonl_path) {
|
|
183
|
+
if (options.deletedFromDisk && session.jsonl_path) {
|
|
101
184
|
removedFromDisk = await removeFileIfExists(session.jsonl_path);
|
|
102
185
|
}
|
|
103
186
|
|
|
@@ -109,7 +192,27 @@ export const sessionsService = {
|
|
|
109
192
|
});
|
|
110
193
|
}
|
|
111
194
|
|
|
112
|
-
return {
|
|
195
|
+
return {
|
|
196
|
+
sessionId,
|
|
197
|
+
action: 'deleted',
|
|
198
|
+
deletedFromDisk: removedFromDisk,
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Restores one archived session back into the active sidebar lists.
|
|
204
|
+
*/
|
|
205
|
+
restoreSessionById(sessionId: string): { sessionId: string; isArchived: false } {
|
|
206
|
+
const session = sessionsDb.getSessionById(sessionId);
|
|
207
|
+
if (!session) {
|
|
208
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
209
|
+
code: 'SESSION_NOT_FOUND',
|
|
210
|
+
statusCode: 404,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
sessionsDb.updateSessionIsArchived(sessionId, false);
|
|
215
|
+
return { sessionId, isArchived: false };
|
|
113
216
|
},
|
|
114
217
|
|
|
115
218
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
2
|
+
import type { ProviderSkill, ProviderSkillListOptions } from '@/shared/types.js';
|
|
3
|
+
|
|
4
|
+
export const providerSkillsService = {
|
|
5
|
+
/**
|
|
6
|
+
* Lists normalized skills visible to one provider.
|
|
7
|
+
*/
|
|
8
|
+
async listProviderSkills(
|
|
9
|
+
providerName: string,
|
|
10
|
+
options?: ProviderSkillListOptions,
|
|
11
|
+
): Promise<ProviderSkill[]> {
|
|
12
|
+
const provider = providerRegistry.resolveProvider(providerName);
|
|
13
|
+
return provider.skills.listSkills(options);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -2,7 +2,9 @@ import type {
|
|
|
2
2
|
IProvider,
|
|
3
3
|
IProviderAuth,
|
|
4
4
|
IProviderMcp,
|
|
5
|
+
IProviderModels,
|
|
5
6
|
IProviderSessionSynchronizer,
|
|
7
|
+
IProviderSkills,
|
|
6
8
|
IProviderSessions,
|
|
7
9
|
} from '@/shared/interfaces.js';
|
|
8
10
|
import type { LLMProvider } from '@/shared/types.js';
|
|
@@ -16,8 +18,10 @@ import type { LLMProvider } from '@/shared/types.js';
|
|
|
16
18
|
*/
|
|
17
19
|
export abstract class AbstractProvider implements IProvider {
|
|
18
20
|
readonly id: LLMProvider;
|
|
21
|
+
abstract readonly models: IProviderModels;
|
|
19
22
|
abstract readonly mcp: IProviderMcp;
|
|
20
23
|
abstract readonly auth: IProviderAuth;
|
|
24
|
+
abstract readonly skills: IProviderSkills;
|
|
21
25
|
abstract readonly sessions: IProviderSessions;
|
|
22
26
|
abstract readonly sessionSynchronizer: IProviderSessionSynchronizer;
|
|
23
27
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import type { IProviderSkills } from '@/shared/interfaces.js';
|
|
4
|
+
import type {
|
|
5
|
+
LLMProvider,
|
|
6
|
+
ProviderSkill,
|
|
7
|
+
ProviderSkillListOptions,
|
|
8
|
+
ProviderSkillSource,
|
|
9
|
+
} from '@/shared/types.js';
|
|
10
|
+
import {
|
|
11
|
+
findProviderSkillMarkdownFiles,
|
|
12
|
+
readProviderSkillMarkdownDefinition,
|
|
13
|
+
} from '@/shared/utils.js';
|
|
14
|
+
|
|
15
|
+
const resolveWorkspacePath = (workspacePath?: string): string =>
|
|
16
|
+
path.resolve(workspacePath ?? process.cwd());
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Shared skills provider for provider-specific skill source discovery.
|
|
20
|
+
*/
|
|
21
|
+
export abstract class SkillsProvider implements IProviderSkills {
|
|
22
|
+
protected readonly provider: LLMProvider;
|
|
23
|
+
|
|
24
|
+
protected constructor(provider: LLMProvider) {
|
|
25
|
+
this.provider = provider;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async listSkills(options?: ProviderSkillListOptions): Promise<ProviderSkill[]> {
|
|
29
|
+
const workspacePath = resolveWorkspacePath(options?.workspacePath);
|
|
30
|
+
const sources = await this.getSkillSources(workspacePath);
|
|
31
|
+
const skills: ProviderSkill[] = [];
|
|
32
|
+
|
|
33
|
+
for (const source of sources) {
|
|
34
|
+
const skillFiles = await findProviderSkillMarkdownFiles(source.rootDir, {
|
|
35
|
+
recursive: source.recursive,
|
|
36
|
+
});
|
|
37
|
+
for (const skillPath of skillFiles) {
|
|
38
|
+
try {
|
|
39
|
+
const definition = await readProviderSkillMarkdownDefinition(skillPath);
|
|
40
|
+
const command = source.commandForSkill
|
|
41
|
+
? source.commandForSkill(definition.name)
|
|
42
|
+
: `${source.commandPrefix ?? '/'}${definition.name}`;
|
|
43
|
+
|
|
44
|
+
skills.push({
|
|
45
|
+
provider: this.provider,
|
|
46
|
+
name: definition.name,
|
|
47
|
+
description: definition.description,
|
|
48
|
+
command,
|
|
49
|
+
scope: source.scope,
|
|
50
|
+
sourcePath: skillPath,
|
|
51
|
+
pluginName: source.pluginName,
|
|
52
|
+
pluginId: source.pluginId,
|
|
53
|
+
});
|
|
54
|
+
} catch {
|
|
55
|
+
// A malformed or unreadable skill markdown file should not hide other valid skills.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return skills;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected abstract getSkillSources(workspacePath: string): Promise<ProviderSkillSource[]>;
|
|
64
|
+
}
|
|
@@ -169,6 +169,93 @@ test('providerMcpService handles codex MCP TOML config and capability validation
|
|
|
169
169
|
}
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* This test covers OpenCode MCP support for user/project config files, JSONC-compatible
|
|
174
|
+
* reads, and validation for unsupported scope/transport combinations.
|
|
175
|
+
*/
|
|
176
|
+
test('providerMcpService handles opencode MCP config and capability validation', { concurrency: false }, async () => {
|
|
177
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'llm-mcp-opencode-'));
|
|
178
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
179
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
180
|
+
await fs.mkdir(path.join(tempRoot, '.config', 'opencode'), { recursive: true });
|
|
181
|
+
await fs.writeFile(
|
|
182
|
+
path.join(tempRoot, '.config', 'opencode', 'opencode.jsonc'),
|
|
183
|
+
`{
|
|
184
|
+
// Existing comments should not block OpenCode MCP reads.
|
|
185
|
+
"mcp": {}
|
|
186
|
+
}\n`,
|
|
187
|
+
'utf8',
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
191
|
+
try {
|
|
192
|
+
await providerMcpService.upsertProviderMcpServer('opencode', {
|
|
193
|
+
name: 'opencode-user-stdio',
|
|
194
|
+
scope: 'user',
|
|
195
|
+
transport: 'stdio',
|
|
196
|
+
command: 'node',
|
|
197
|
+
args: ['server.js'],
|
|
198
|
+
env: { API_KEY: 'x' },
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await providerMcpService.upsertProviderMcpServer('opencode', {
|
|
202
|
+
name: 'opencode-project-http',
|
|
203
|
+
scope: 'project',
|
|
204
|
+
transport: 'http',
|
|
205
|
+
url: 'https://opencode.example.com/mcp',
|
|
206
|
+
headers: { Authorization: 'Bearer token' },
|
|
207
|
+
workspacePath,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const userConfig = await readJson(path.join(tempRoot, '.config', 'opencode', 'opencode.jsonc'));
|
|
211
|
+
const userServers = userConfig.mcp as Record<string, unknown>;
|
|
212
|
+
const userStdio = userServers['opencode-user-stdio'] as Record<string, unknown>;
|
|
213
|
+
assert.equal(userStdio.type, 'local');
|
|
214
|
+
assert.deepEqual(userStdio.command, ['node', 'server.js']);
|
|
215
|
+
assert.deepEqual(userStdio.environment, { API_KEY: 'x' });
|
|
216
|
+
|
|
217
|
+
const projectConfig = await readJson(path.join(workspacePath, 'opencode.json'));
|
|
218
|
+
const projectServers = projectConfig.mcp as Record<string, unknown>;
|
|
219
|
+
const projectHttp = projectServers['opencode-project-http'] as Record<string, unknown>;
|
|
220
|
+
assert.equal(projectHttp.type, 'remote');
|
|
221
|
+
assert.equal(projectHttp.url, 'https://opencode.example.com/mcp');
|
|
222
|
+
|
|
223
|
+
const grouped = await providerMcpService.listProviderMcpServers('opencode', { workspacePath });
|
|
224
|
+
assert.ok(grouped.user.some((server) => server.name === 'opencode-user-stdio' && server.transport === 'stdio'));
|
|
225
|
+
assert.ok(grouped.project.some((server) => server.name === 'opencode-project-http' && server.transport === 'http'));
|
|
226
|
+
|
|
227
|
+
await assert.rejects(
|
|
228
|
+
providerMcpService.upsertProviderMcpServer('opencode', {
|
|
229
|
+
name: 'opencode-local',
|
|
230
|
+
scope: 'local',
|
|
231
|
+
transport: 'stdio',
|
|
232
|
+
command: 'node',
|
|
233
|
+
}),
|
|
234
|
+
(error: unknown) =>
|
|
235
|
+
error instanceof AppError &&
|
|
236
|
+
error.code === 'MCP_SCOPE_NOT_SUPPORTED' &&
|
|
237
|
+
error.statusCode === 400,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
await assert.rejects(
|
|
241
|
+
providerMcpService.upsertProviderMcpServer('opencode', {
|
|
242
|
+
name: 'opencode-sse',
|
|
243
|
+
scope: 'project',
|
|
244
|
+
transport: 'sse',
|
|
245
|
+
url: 'https://example.com/sse',
|
|
246
|
+
workspacePath,
|
|
247
|
+
}),
|
|
248
|
+
(error: unknown) =>
|
|
249
|
+
error instanceof AppError &&
|
|
250
|
+
error.code === 'MCP_TRANSPORT_NOT_SUPPORTED' &&
|
|
251
|
+
error.statusCode === 400,
|
|
252
|
+
);
|
|
253
|
+
} finally {
|
|
254
|
+
restoreHomeDir();
|
|
255
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
172
259
|
/**
|
|
173
260
|
* This test covers Gemini/Cursor MCP JSON formats and user/project scope persistence.
|
|
174
261
|
*/
|
|
@@ -254,8 +341,7 @@ test('providerMcpService global adder writes to all providers and rejects unsupp
|
|
|
254
341
|
workspacePath,
|
|
255
342
|
});
|
|
256
343
|
|
|
257
|
-
|
|
258
|
-
assert.equal(globalResult.length, expectCursorGlobal ? 4 : 3);
|
|
344
|
+
assert.equal(globalResult.length, 5);
|
|
259
345
|
assert.ok(globalResult.every((entry) => entry.created === true));
|
|
260
346
|
|
|
261
347
|
const claudeProject = await readJson(path.join(workspacePath, '.mcp.json'));
|
|
@@ -267,10 +353,11 @@ test('providerMcpService global adder writes to all providers and rejects unsupp
|
|
|
267
353
|
const geminiProject = await readJson(path.join(workspacePath, '.gemini', 'settings.json'));
|
|
268
354
|
assert.ok((geminiProject.mcpServers as Record<string, unknown>)['global-http']);
|
|
269
355
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
356
|
+
const opencodeProject = await readJson(path.join(workspacePath, 'opencode.json'));
|
|
357
|
+
assert.ok((opencodeProject.mcp as Record<string, unknown>)['global-http']);
|
|
358
|
+
|
|
359
|
+
const cursorProject = await readJson(path.join(workspacePath, '.cursor', 'mcp.json'));
|
|
360
|
+
assert.ok((cursorProject.mcpServers as Record<string, unknown>)['global-http']);
|
|
274
361
|
|
|
275
362
|
await assert.rejects(
|
|
276
363
|
providerMcpService.addMcpServerToAllProviders({
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildOpenCodeDefinitionFromIds,
|
|
6
|
+
parseOpenCodeModelsStdout,
|
|
7
|
+
} from '@/modules/providers/list/opencode/opencode-models.provider.js';
|
|
8
|
+
|
|
9
|
+
test('OpenCode models provider parses plain CLI output and removes duplicates', () => {
|
|
10
|
+
const ids = parseOpenCodeModelsStdout(`
|
|
11
|
+
opencode/big-pickle
|
|
12
|
+
not a model
|
|
13
|
+
anthropic/claude-opus-4-7-fast
|
|
14
|
+
anthropic/claude-opus-4-7-fast
|
|
15
|
+
openai/gpt-5.5-pro
|
|
16
|
+
`);
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(ids, [
|
|
19
|
+
'opencode/big-pickle',
|
|
20
|
+
'anthropic/claude-opus-4-7-fast',
|
|
21
|
+
'openai/gpt-5.5-pro',
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('OpenCode models provider formats frontend labels from provider-prefixed ids', () => {
|
|
26
|
+
const definition = buildOpenCodeDefinitionFromIds([
|
|
27
|
+
'opencode/deepseek-v4-flash-free',
|
|
28
|
+
'opencode/nemotron-3-super-free',
|
|
29
|
+
'anthropic/claude-3-5-sonnet-20241022',
|
|
30
|
+
'anthropic/claude-opus-4-7-fast',
|
|
31
|
+
'openai/gpt-5.4-mini-fast',
|
|
32
|
+
'openai/gpt-5.5-pro',
|
|
33
|
+
'newprovider/alpha-v12-special-20261231',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(definition.OPTIONS, [
|
|
37
|
+
{
|
|
38
|
+
value: 'opencode/deepseek-v4-flash-free',
|
|
39
|
+
label: 'Deepseek V4 Flash Free',
|
|
40
|
+
description: 'opencode - opencode/deepseek-v4-flash-free',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: 'opencode/nemotron-3-super-free',
|
|
44
|
+
label: 'Nemotron 3 Super Free',
|
|
45
|
+
description: 'opencode - opencode/nemotron-3-super-free',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: 'anthropic/claude-3-5-sonnet-20241022',
|
|
49
|
+
label: 'Claude 3.5 Sonnet (2024-10-22)',
|
|
50
|
+
description: 'anthropic - anthropic/claude-3-5-sonnet-20241022',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
value: 'anthropic/claude-opus-4-7-fast',
|
|
54
|
+
label: 'Claude Opus 4.7 Fast',
|
|
55
|
+
description: 'anthropic - anthropic/claude-opus-4-7-fast',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
value: 'openai/gpt-5.4-mini-fast',
|
|
59
|
+
label: 'GPT-5.4 Mini Fast',
|
|
60
|
+
description: 'openai - openai/gpt-5.4-mini-fast',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
value: 'openai/gpt-5.5-pro',
|
|
64
|
+
label: 'GPT-5.5 Pro',
|
|
65
|
+
description: 'openai - openai/gpt-5.5-pro',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
value: 'newprovider/alpha-v12-special-20261231',
|
|
69
|
+
label: 'Alpha V12 Special (2026-12-31)',
|
|
70
|
+
description: 'newprovider - newprovider/alpha-v12-special-20261231',
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
});
|