@cloudcli-ai/cloudcli 1.37.0 → 1.37.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/abnfDiagram-N423BO3Z-C4UAoHJb.js +1 -0
- package/dist/assets/arc-B8jF-ja9.js +1 -0
- package/dist/assets/architectureDiagram-T3A2C74G-C_Wur_CU.js +36 -0
- package/dist/assets/blockDiagram-VBNYF7ZC-Cq4P1i8t.js +132 -0
- package/dist/assets/c4Diagram-5PPSVZJV-C1wG4eUj.js +10 -0
- package/dist/assets/channel-_falYbU1.js +1 -0
- package/dist/assets/chunk-2GRJ4B5K-B8fnBG6E.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-WdfPooPJ.js +1 -0
- package/dist/assets/chunk-5RXB4S5H-DhZJuG2E.js +231 -0
- package/dist/assets/chunk-5VM5RSS4-B0rjslal.js +15 -0
- package/dist/assets/chunk-6Q2QTUOP-CG2Oj4jL.js +88 -0
- package/dist/assets/chunk-GF5L2VYU-rrl2lRxY.js +206 -0
- package/dist/assets/chunk-JWPE2WC7-CjVbrVMA.js +1 -0
- package/dist/assets/chunk-KBJHAD2P-CRzKgU8S.js +1 -0
- package/dist/assets/chunk-RYQCIY6F-DBz4GTZF.js +1 -0
- package/dist/assets/chunk-XXDRQBXY-wHxalsXk.js +1 -0
- package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-s1hygY8e.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-C4LGV15i.js +178 -0
- package/dist/assets/cynefinDiagram-MW4NZA55-C5uhu7B3.js +62 -0
- package/dist/assets/cytoscape.esm-C10L9Icf.js +331 -0
- package/dist/assets/dagre-VZM6K2ZE-B-OBTewm.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-7IWD3JNH-BjGKQxih.js +30 -0
- package/dist/assets/diagram-B4RE2ZJO-BMKz1X2L.js +3 -0
- package/dist/assets/diagram-LBJQPF4R-DNh-GhKC.js +24 -0
- package/dist/assets/diagram-Q27KOJAE-BaTG9jPT.js +24 -0
- package/dist/assets/diagram-UB23O5K3-BUwo2OvJ.js +41 -0
- package/dist/assets/ebnfDiagram-BXEA7PRR-DAZgfzX2.js +1 -0
- package/dist/assets/erDiagram-JOGREHBK-1w1CcGEb.js +85 -0
- package/dist/assets/flowDiagram-UKHOOZJN-D4EII8Jj.js +156 -0
- package/dist/assets/ganttDiagram-PKOTCBZU-ChvJZAgB.js +292 -0
- package/dist/assets/gitGraphDiagram-DS77QQ5N-DtOn41S8.js +106 -0
- package/dist/assets/graph-Cx-EqisW.js +1 -0
- package/dist/assets/index-CzPLBvXT.css +32 -0
- package/dist/assets/index-DFkBBuSA.js +1549 -0
- package/dist/assets/infoDiagram-6WML65LV-DN_PVEJZ.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-WSZJBQD7-DbkF1kXb.js +70 -0
- package/dist/assets/journeyDiagram-NVQOT4AX-pJwmhddE.js +139 -0
- package/dist/assets/kanban-definition-27J2QSJJ-PPAPgNp5.js +89 -0
- package/dist/assets/layout-D6BstSzF.js +1 -0
- package/dist/assets/linear-iPR-J9f7.js +1 -0
- package/dist/assets/map-CV9uif0g.js +1 -0
- package/dist/assets/mermaid.core-BEUL86Z6.js +312 -0
- package/dist/assets/mindmap-definition-FAOFIHXS-BvZoBib7.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pegDiagram-VL7TDLO6-DhYreaUd.js +1 -0
- package/dist/assets/pieDiagram-7S7Q4E2Y-CrOHlyD0.js +39 -0
- package/dist/assets/quadrantDiagram-CIZ2JOQS-D4g5yH3t.js +7 -0
- package/dist/assets/railroadDiagram-AXF67PYL-Dj39lHlf.js +1 -0
- package/dist/assets/requirementDiagram-LRYGKXZP-1AS4Zgm_.js +84 -0
- package/dist/assets/sankeyDiagram-W5VNT64P-CKlMn19P.js +40 -0
- package/dist/assets/sequenceDiagram-SI44F4Z6-B_j3qyvo.js +162 -0
- package/dist/assets/sizeCapture-X5ZJPWSS-Bp2Op16v.js +1 -0
- package/dist/assets/stateDiagram-OKZ733FA-C0cN6DoY.js +1 -0
- package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +1 -0
- package/dist/assets/swimlanes-SLNWSIFB-BEvXOV9b.js +2 -0
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +8 -0
- package/dist/assets/timeline-definition-Z64GVDOM-zdJQinvZ.js +120 -0
- package/dist/assets/vennDiagram-T6HMQDX7-DuF1uZMY.js +34 -0
- package/dist/assets/wardleyDiagram-T6FBY63Y-B9AlLQup.js +78 -0
- package/dist/assets/xychartDiagram-ELKLHX3M-DTJX48Qg.js +7 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +1 -2
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +2 -1
- package/dist-server/server/modules/agent/agent.module.js.map +1 -1
- package/dist-server/server/modules/auth/auth.middleware.js +1 -1
- package/dist-server/server/modules/auth/auth.middleware.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use-runtime.js +10 -0
- package/dist-server/server/modules/browser-use/browser-use-runtime.js.map +1 -0
- package/dist-server/server/modules/browser-use/browser-use.service.js +3 -6
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
- package/dist-server/server/modules/browser-use/index.js +2 -0
- package/dist-server/server/modules/browser-use/index.js.map +1 -1
- package/dist-server/server/modules/cli/cli.js +9 -3
- package/dist-server/server/modules/cli/cli.js.map +1 -1
- package/dist-server/server/modules/cli/cli.module.js +0 -1
- package/dist-server/server/modules/cli/cli.module.js.map +1 -1
- package/dist-server/server/modules/cli/cli.service.js +0 -18
- package/dist-server/server/modules/cli/cli.service.js.map +1 -1
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +154 -0
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js.map +1 -0
- package/dist-server/server/modules/cli/tests/cli.service.test.js +0 -3
- package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -1
- package/dist-server/server/modules/commands/commands.routes.js +3 -3
- package/dist-server/server/modules/commands/commands.routes.js.map +1 -1
- package/dist-server/server/modules/commands/tests/commands.test.js +2 -7
- package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -1
- package/dist-server/server/modules/database/index.js +2 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +18 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +112 -0
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +63 -7
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +28 -3
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +102 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -1
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +22 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js +5 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.service.js +60 -20
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +143 -16
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/git/git-branch.service.js +15 -0
- package/dist-server/server/modules/git/git-branch.service.js.map +1 -0
- package/dist-server/server/modules/git/git.routes.js +15 -7
- package/dist-server/server/modules/git/git.routes.js.map +1 -1
- package/dist-server/server/modules/git/tests/git-branch.service.test.js +52 -0
- package/dist-server/server/modules/git/tests/git-branch.service.test.js.map +1 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js +84 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -1
- package/dist-server/server/modules/notifications/index.js +3 -1
- package/dist-server/server/modules/notifications/index.js.map +1 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +22 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +46 -14
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +259 -60
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +58 -71
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +1 -54
- 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 +7 -7
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +32 -669
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +105 -328
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +0 -12
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +144 -31
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +167 -199
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +2 -2
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +75 -11
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +62 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +53 -17
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +15 -126
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +5 -5
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +175 -292
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +2 -6
- package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +1 -0
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +161 -0
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +87 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +5 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/shared/utils.js +8 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +8 -4
- package/scripts/promote-dist-server.mjs +50 -0
- package/server/index.ts +1 -2
- package/server/modules/agent/agent.module.ts +2 -1
- package/server/modules/auth/auth.middleware.ts +2 -2
- package/server/modules/browser-use/browser-use-runtime.ts +10 -0
- package/server/modules/browser-use/browser-use.service.ts +5 -8
- package/server/modules/browser-use/index.ts +3 -0
- package/server/modules/cli/cli.module.ts +0 -1
- package/server/modules/cli/cli.service.ts +0 -20
- package/server/modules/cli/cli.ts +9 -3
- package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +186 -0
- package/server/modules/cli/tests/cli.service.test.ts +0 -3
- package/server/modules/commands/commands.routes.ts +3 -3
- package/server/modules/commands/tests/commands.test.ts +2 -7
- package/server/modules/database/index.ts +2 -0
- package/server/modules/database/migrations.ts +20 -0
- package/server/modules/database/repositories/provider-models.ts +168 -0
- package/server/modules/database/repositories/sessions.db.ts +85 -8
- package/server/modules/database/schema.ts +29 -3
- package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -0
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -1
- package/server/modules/database/tests/sessions.db.integration.test.ts +32 -0
- package/server/modules/file-tree/file-tree.module.ts +5 -1
- package/server/modules/file-tree/file-tree.service.ts +81 -22
- package/server/modules/file-tree/tests/file-tree.service.test.ts +167 -16
- package/server/modules/git/git-branch.service.ts +43 -0
- package/server/modules/git/git.routes.ts +16 -9
- package/server/modules/git/tests/git-branch.service.test.ts +60 -0
- package/server/modules/git/tests/git-init.routes.test.ts +83 -0
- package/server/modules/notifications/index.ts +2 -0
- package/server/modules/notifications/services/notification-orchestrator.service.js +24 -1
- package/server/modules/providers/list/claude/claude-models.provider.ts +46 -14
- package/server/modules/providers/list/claude/claude-runtime.provider.js +280 -67
- package/server/modules/providers/list/codex/codex-models.provider.ts +58 -99
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +1 -60
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +7 -7
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +32 -710
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +105 -413
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +0 -13
- package/server/modules/providers/provider.routes.ts +190 -33
- package/server/modules/providers/services/provider-models.service.ts +223 -288
- package/server/modules/providers/services/provider-runtime.service.ts +2 -3
- package/server/modules/providers/services/session-conversations-search.service.ts +106 -11
- package/server/modules/providers/services/sessions.service.ts +87 -3
- package/server/modules/providers/tests/codex-sessions.test.ts +57 -18
- package/server/modules/providers/tests/opencode-models.test.ts +34 -135
- package/server/modules/providers/tests/opencode-sessions.test.ts +5 -5
- package/server/modules/providers/tests/provider-models.service.test.ts +353 -450
- package/server/modules/providers/tests/provider-runtime.service.test.ts +2 -6
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +1 -0
- package/server/modules/providers/tests/provider.routes.test.ts +244 -0
- package/server/modules/providers/tests/sessions.service.test.ts +127 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +5 -1
- package/server/shared/interfaces.ts +4 -6
- package/server/shared/types.ts +29 -18
- package/server/shared/utils.ts +10 -0
- package/server/tsconfig.json +6 -2
- package/README.de.md +0 -257
- package/README.ja.md +0 -249
- package/README.ko.md +0 -249
- package/README.md +0 -269
- package/README.ru.md +0 -257
- package/README.tr.md +0 -258
- package/README.zh-CN.md +0 -249
- package/README.zh-TW.md +0 -249
- package/dist/assets/index-DSlds633.css +0 -32
- package/dist/assets/index-eGqPeIEu.js +0 -1531
- package/dist-server/server/constants/config.js +0 -6
- package/dist-server/server/constants/config.js.map +0 -1
- package/server/constants/config.ts +0 -5
package/server/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import http from 'http';
|
|
|
9
9
|
import express, { type NextFunction, type Request, type Response } from 'express';
|
|
10
10
|
import cors from 'cors';
|
|
11
11
|
|
|
12
|
-
import { AppError, findApplicationRoot, getModuleDirectory, terminalTextStyles } from '@/shared/utils.js';
|
|
12
|
+
import { AppError, findApplicationRoot, getModuleDirectory, IS_PLATFORM, terminalTextStyles } from '@/shared/utils.js';
|
|
13
13
|
import {
|
|
14
14
|
closeSessionsWatcher,
|
|
15
15
|
initializeSessionsWatcher,
|
|
@@ -50,7 +50,6 @@ import browserUseMcpRoutes from './modules/browser-use/browser-use-mcp.routes.js
|
|
|
50
50
|
import { browserUseService } from './modules/browser-use/browser-use.service.js';
|
|
51
51
|
import { initializeDatabase, sessionsDb } from './modules/database/index.js';
|
|
52
52
|
import { configureWebPush } from './modules/notifications/index.js';
|
|
53
|
-
import { IS_PLATFORM } from './constants/config.js';
|
|
54
53
|
|
|
55
54
|
const __dirname = getModuleDirectory(import.meta.url);
|
|
56
55
|
// The server source runs from /server, while the compiled output runs from /dist-server/server.
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
userDb,
|
|
13
13
|
} from '@/modules/database/index.js';
|
|
14
14
|
import { providerModelsService } from '@/modules/providers/index.js';
|
|
15
|
+
import { IS_PLATFORM } from '@/shared/utils.js';
|
|
15
16
|
|
|
16
17
|
import { createAgentRouter } from './agent.routes.js';
|
|
17
18
|
|
|
@@ -30,7 +31,7 @@ export function createAgentModule(externalDependencies: AgentExternalDependencie
|
|
|
30
31
|
crypto,
|
|
31
32
|
homeDirectory: os.homedir,
|
|
32
33
|
spawnProcess: spawn,
|
|
33
|
-
platformMode:
|
|
34
|
+
platformMode: IS_PLATFORM,
|
|
34
35
|
users: {
|
|
35
36
|
getFirstUser: () => userDb.getFirstUser(),
|
|
36
37
|
},
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// @ts-nocheck -- JWT request augmentation is narrowed by Auth route contracts.
|
|
2
2
|
import jwt from 'jsonwebtoken';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { IS_PLATFORM } from '@/shared/utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { userDb, appConfigDb } from '../database/index.js';
|
|
7
7
|
|
|
8
8
|
// Use env var if set, otherwise auto-generate a unique secret per installation
|
|
9
9
|
const JWT_SECRET = process.env.JWT_SECRET || appConfigDb.getOrCreateJwtSecret();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IS_PLATFORM } from '@/shared/utils.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the runtime label exposed by Browser Use status and session APIs.
|
|
5
|
+
* The Browser Use service and CLI environment-mode regression coverage consume
|
|
6
|
+
* this after the server or CLI entrypoint has completed environment bootstrapping.
|
|
7
|
+
*/
|
|
8
|
+
export function getBrowserUseRuntime(): 'cloud' | 'local' {
|
|
9
|
+
return IS_PLATFORM ? 'cloud' : 'local';
|
|
10
|
+
}
|
|
@@ -11,15 +11,16 @@ import { appConfigDb } from '@/modules/database/index.js';
|
|
|
11
11
|
import { providerMcpService } from '@/modules/providers/index.js';
|
|
12
12
|
import { getModuleDirectory } from '@/shared/utils.js';
|
|
13
13
|
|
|
14
|
+
import { getBrowserUseRuntime } from './browser-use-runtime.js';
|
|
15
|
+
|
|
14
16
|
const require = createRequire(import.meta.url);
|
|
15
17
|
const __dirname = getModuleDirectory(import.meta.url);
|
|
16
|
-
const IS_PLATFORM = process.env.VITE_IS_PLATFORM === 'true';
|
|
17
18
|
const MAX_SESSIONS_PER_OWNER = Number.parseInt(process.env.CLOUDCLI_BROWSER_USE_MAX_SESSIONS_PER_OWNER || '3', 10);
|
|
18
19
|
const SESSION_TTL_MS = Number.parseInt(process.env.CLOUDCLI_BROWSER_USE_SESSION_TTL_MS || String(30 * 60 * 1000), 10);
|
|
19
20
|
const BROWSER_USE_SETTINGS_KEY = 'browser_use_settings';
|
|
20
21
|
const BROWSER_USE_MCP_TOKEN_KEY = 'browser_use_mcp_token';
|
|
21
22
|
|
|
22
|
-
type BrowserUseRuntime =
|
|
23
|
+
type BrowserUseRuntime = ReturnType<typeof getBrowserUseRuntime>;
|
|
23
24
|
type BrowserUseSessionStatus = 'ready' | 'stopped' | 'unavailable';
|
|
24
25
|
|
|
25
26
|
type BrowserUseSession = {
|
|
@@ -85,10 +86,6 @@ const MCP_SERVER_NAME = 'cloudcli-browser';
|
|
|
85
86
|
const LEGACY_MCP_SERVER_NAMES = ['cloudcli-browser-use'];
|
|
86
87
|
const RUNTIME_READINESS_CACHE_TTL_MS = 30_000;
|
|
87
88
|
|
|
88
|
-
function getRuntime(): BrowserUseRuntime {
|
|
89
|
-
return IS_PLATFORM ? 'cloud' : 'local';
|
|
90
|
-
}
|
|
91
|
-
|
|
92
89
|
function readSettings(): BrowserUseSettings {
|
|
93
90
|
try {
|
|
94
91
|
const raw = appConfigDb.get(BROWSER_USE_SETTINGS_KEY);
|
|
@@ -446,7 +443,7 @@ export const browserUseService = {
|
|
|
446
443
|
|
|
447
444
|
return {
|
|
448
445
|
enabled: settings.enabled,
|
|
449
|
-
runtime:
|
|
446
|
+
runtime: getBrowserUseRuntime(),
|
|
450
447
|
available,
|
|
451
448
|
playwrightInstalled: readiness.playwrightInstalled,
|
|
452
449
|
chromiumInstalled: readiness.chromiumInstalled,
|
|
@@ -515,7 +512,7 @@ export const browserUseService = {
|
|
|
515
512
|
id: randomUUID(),
|
|
516
513
|
ownerId: AGENT_OWNER_ID,
|
|
517
514
|
createdBy: 'agent',
|
|
518
|
-
runtime:
|
|
515
|
+
runtime: getBrowserUseRuntime(),
|
|
519
516
|
status: 'unavailable',
|
|
520
517
|
url: null,
|
|
521
518
|
title: null,
|
|
@@ -6,3 +6,6 @@
|
|
|
6
6
|
export async function startBrowserUseMcp(): Promise<void> {
|
|
7
7
|
await import('./browser-use-mcp.js');
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
// getBrowserUseRuntime: used by Browser Use services and environment-mode regression coverage.
|
|
11
|
+
export { getBrowserUseRuntime } from './browser-use-runtime.js';
|
|
@@ -28,7 +28,6 @@ export function createCliApplication(): CliApplication {
|
|
|
28
28
|
bugsUrl: packageMetadataJson.bugs?.url,
|
|
29
29
|
};
|
|
30
30
|
const fileSystem = {
|
|
31
|
-
readTextFile: (filePath: string) => fs.readFileSync(filePath, 'utf8'),
|
|
32
31
|
pathExists: (filePath: string) => fs.existsSync(filePath),
|
|
33
32
|
getFileStats: (filePath: string) => {
|
|
34
33
|
const stats = fs.statSync(filePath);
|
|
@@ -77,27 +77,7 @@ function isNewerVersion(candidateVersion: string, currentVersion: string): boole
|
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function loadEnvironmentFile(dependencies: CliServiceDependencies): void {
|
|
81
|
-
try {
|
|
82
|
-
const environmentFile = dependencies.fileSystem.readTextFile(
|
|
83
|
-
path.join(dependencies.applicationRoot, '.env'),
|
|
84
|
-
);
|
|
85
|
-
for (const line of environmentFile.split('\n')) {
|
|
86
|
-
const trimmedLine = line.trim();
|
|
87
|
-
if (!trimmedLine || trimmedLine.startsWith('#')) continue;
|
|
88
|
-
|
|
89
|
-
const [key, ...valueParts] = trimmedLine.split('=');
|
|
90
|
-
if (key && valueParts.length > 0 && !dependencies.environment[key]) {
|
|
91
|
-
dependencies.environment[key] = valueParts.join('=').trim();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} catch {
|
|
95
|
-
// The repository-level .env file is optional for every CLI command.
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
80
|
function showStatus(dependencies: CliServiceDependencies): void {
|
|
100
|
-
loadEnvironmentFile(dependencies);
|
|
101
81
|
const { environment, fileSystem, output } = dependencies;
|
|
102
82
|
const databasePath = environment.DATABASE_PATH || dependencies.defaultDatabasePath;
|
|
103
83
|
const databaseExists = fileSystem.pathExists(databasePath);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// The CLI can reach shared environment constants through its composition root,
|
|
4
|
+
// so finish the root environment bootstrap before loading that import graph.
|
|
5
|
+
// eslint-disable-next-line boundaries/no-unknown
|
|
6
|
+
import '../../load-env.js';
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
async function runCli(): Promise<number> {
|
|
9
|
+
const { createCliApplication } = await import('./index.js');
|
|
10
|
+
return createCliApplication().run(process.argv.slice(2));
|
|
11
|
+
}
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
runCli().then((exitCode) => {
|
|
8
14
|
process.exitCode = exitCode;
|
|
9
15
|
}).catch((error: unknown) => {
|
|
10
16
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { copyFile, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
+
|
|
9
|
+
type BootstrapResult = {
|
|
10
|
+
command: string | null;
|
|
11
|
+
environmentFileReads: number;
|
|
12
|
+
mode: string | null;
|
|
13
|
+
authNextCalled: boolean;
|
|
14
|
+
authStatus: number;
|
|
15
|
+
authenticatedUsername: string | null;
|
|
16
|
+
runtime: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const RESULT_PREFIX = '__CLOUDCLI_BOOTSTRAP_RESULT__';
|
|
20
|
+
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const applicationRoot = path.resolve(testDirectory, '..', '..', '..', '..');
|
|
22
|
+
|
|
23
|
+
function sourceModuleUrl(...segments: string[]): string {
|
|
24
|
+
return pathToFileURL(path.join(applicationRoot, ...segments)).href;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function runCliBootstrapFixture(isPlatform: boolean): Promise<BootstrapResult> {
|
|
28
|
+
const fixtureRoot = await mkdtemp(path.join(os.tmpdir(), 'cloudcli-cli-bootstrap-'));
|
|
29
|
+
const fixtureServerDirectory = path.join(fixtureRoot, 'server');
|
|
30
|
+
const fixtureCliDirectory = path.join(fixtureServerDirectory, 'modules', 'cli');
|
|
31
|
+
const fixtureDatabasePath = path.join(fixtureRoot, 'auth.db');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await mkdir(fixtureCliDirectory, { recursive: true });
|
|
35
|
+
await Promise.all([
|
|
36
|
+
copyFile(
|
|
37
|
+
path.join(applicationRoot, 'server', 'load-env.ts'),
|
|
38
|
+
path.join(fixtureServerDirectory, 'load-env.ts'),
|
|
39
|
+
),
|
|
40
|
+
copyFile(
|
|
41
|
+
path.join(applicationRoot, 'server', 'modules', 'cli', 'cli.ts'),
|
|
42
|
+
path.join(fixtureCliDirectory, 'cli.ts'),
|
|
43
|
+
),
|
|
44
|
+
]);
|
|
45
|
+
await writeFile(
|
|
46
|
+
path.join(fixtureRoot, '.env'),
|
|
47
|
+
[
|
|
48
|
+
`VITE_IS_PLATFORM=${String(isPlatform)}`,
|
|
49
|
+
`DATABASE_PATH=${fixtureDatabasePath.replace(/\\/g, '/')}`,
|
|
50
|
+
].join('\n'),
|
|
51
|
+
'utf8',
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const fixtureIndexSource = `
|
|
55
|
+
import { createCliService } from ${JSON.stringify(sourceModuleUrl('server', 'modules', 'cli', 'cli.service.ts'))};
|
|
56
|
+
import { authenticateToken } from ${JSON.stringify(sourceModuleUrl('server', 'modules', 'auth', 'index.ts'))};
|
|
57
|
+
import { getBrowserUseRuntime } from ${JSON.stringify(sourceModuleUrl('server', 'modules', 'browser-use', 'index.ts'))};
|
|
58
|
+
import { closeConnection, initializeDatabase, userDb } from ${JSON.stringify(sourceModuleUrl('server', 'modules', 'database', 'index.ts'))};
|
|
59
|
+
|
|
60
|
+
let environmentFileReads = 0;
|
|
61
|
+
|
|
62
|
+
export function createCliApplication() {
|
|
63
|
+
const service = createCliService({
|
|
64
|
+
applicationRoot: ${JSON.stringify(fixtureRoot)},
|
|
65
|
+
defaultDatabasePath: ${JSON.stringify(fixtureDatabasePath)},
|
|
66
|
+
homeDirectory: ${JSON.stringify(fixtureRoot)},
|
|
67
|
+
packageMetadata: { version: '1.0.0' },
|
|
68
|
+
environment: process.env,
|
|
69
|
+
fileSystem: {
|
|
70
|
+
readTextFile: () => {
|
|
71
|
+
environmentFileReads += 1;
|
|
72
|
+
return '';
|
|
73
|
+
},
|
|
74
|
+
pathExists: () => false,
|
|
75
|
+
getFileStats: () => ({ size: 0, modifiedAt: new Date(0) }),
|
|
76
|
+
},
|
|
77
|
+
output: { log: () => undefined, error: () => undefined },
|
|
78
|
+
sandboxService: { execute: async () => 0 },
|
|
79
|
+
getLatestPackageVersion: async () => '1.0.0',
|
|
80
|
+
updateGlobalPackage: () => undefined,
|
|
81
|
+
startServer: async () => undefined,
|
|
82
|
+
startBrowserUseMcp: async () => undefined,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
async run(argumentsList: string[]) {
|
|
87
|
+
const exitCode = await service.run(argumentsList);
|
|
88
|
+
await initializeDatabase();
|
|
89
|
+
userDb.createUser('bootstrap-user', 'password-hash');
|
|
90
|
+
|
|
91
|
+
const request: any = { headers: {}, query: {} };
|
|
92
|
+
const response: any = {
|
|
93
|
+
statusCode: 200,
|
|
94
|
+
body: null,
|
|
95
|
+
headers: {},
|
|
96
|
+
setHeader(name: string, value: string) {
|
|
97
|
+
this.headers[name] = value;
|
|
98
|
+
},
|
|
99
|
+
status(statusCode: number) {
|
|
100
|
+
this.statusCode = statusCode;
|
|
101
|
+
return this;
|
|
102
|
+
},
|
|
103
|
+
json(body: unknown) {
|
|
104
|
+
this.body = body;
|
|
105
|
+
return this;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
let authNextCalled = false;
|
|
109
|
+
await authenticateToken(request, response, () => {
|
|
110
|
+
authNextCalled = true;
|
|
111
|
+
});
|
|
112
|
+
const runtime = getBrowserUseRuntime();
|
|
113
|
+
|
|
114
|
+
console.log(${JSON.stringify(RESULT_PREFIX)} + JSON.stringify({
|
|
115
|
+
command: argumentsList[0] ?? null,
|
|
116
|
+
environmentFileReads,
|
|
117
|
+
mode: process.env.VITE_IS_PLATFORM ?? null,
|
|
118
|
+
authNextCalled,
|
|
119
|
+
authStatus: response.statusCode,
|
|
120
|
+
authenticatedUsername: request.user?.username ?? null,
|
|
121
|
+
runtime,
|
|
122
|
+
}));
|
|
123
|
+
closeConnection();
|
|
124
|
+
return exitCode;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
await writeFile(path.join(fixtureCliDirectory, 'index.ts'), fixtureIndexSource, 'utf8');
|
|
130
|
+
|
|
131
|
+
const childEnvironment = { ...process.env };
|
|
132
|
+
delete childEnvironment.VITE_IS_PLATFORM;
|
|
133
|
+
delete childEnvironment.DATABASE_PATH;
|
|
134
|
+
const stdout = execFileSync(
|
|
135
|
+
process.execPath,
|
|
136
|
+
[
|
|
137
|
+
path.join(applicationRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs'),
|
|
138
|
+
'--tsconfig',
|
|
139
|
+
path.join(applicationRoot, 'server', 'tsconfig.json'),
|
|
140
|
+
path.join(fixtureCliDirectory, 'cli.ts'),
|
|
141
|
+
'status',
|
|
142
|
+
],
|
|
143
|
+
{
|
|
144
|
+
cwd: applicationRoot,
|
|
145
|
+
encoding: 'utf8',
|
|
146
|
+
env: childEnvironment,
|
|
147
|
+
timeout: 30_000,
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
const resultLine = stdout
|
|
151
|
+
.split(/\r?\n/)
|
|
152
|
+
.find((line) => line.startsWith(RESULT_PREFIX));
|
|
153
|
+
assert.ok(resultLine, `Missing bootstrap result in child output:\n${stdout}`);
|
|
154
|
+
return JSON.parse(resultLine.slice(RESULT_PREFIX.length)) as BootstrapResult;
|
|
155
|
+
} finally {
|
|
156
|
+
await rm(fixtureRoot, { recursive: true, force: true });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
test('CLI bootstrap selects Platform authentication and cloud runtime from .env', async () => {
|
|
161
|
+
const result = await runCliBootstrapFixture(true);
|
|
162
|
+
|
|
163
|
+
assert.deepEqual(result, {
|
|
164
|
+
command: 'status',
|
|
165
|
+
environmentFileReads: 0,
|
|
166
|
+
mode: 'true',
|
|
167
|
+
authNextCalled: true,
|
|
168
|
+
authStatus: 200,
|
|
169
|
+
authenticatedUsername: 'bootstrap-user',
|
|
170
|
+
runtime: 'cloud',
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('CLI bootstrap selects OSS authentication and local runtime from .env', async () => {
|
|
175
|
+
const result = await runCliBootstrapFixture(false);
|
|
176
|
+
|
|
177
|
+
assert.deepEqual(result, {
|
|
178
|
+
command: 'status',
|
|
179
|
+
environmentFileReads: 0,
|
|
180
|
+
mode: 'false',
|
|
181
|
+
authNextCalled: false,
|
|
182
|
+
authStatus: 401,
|
|
183
|
+
authenticatedUsername: null,
|
|
184
|
+
runtime: 'local',
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -63,14 +63,15 @@ const resolveCommandModel = async (modelsService, provider, context) => {
|
|
|
63
63
|
|
|
64
64
|
const executeModelsCommand = async (args, context, modelsService) => {
|
|
65
65
|
const currentProvider = readModelProvider(context?.provider);
|
|
66
|
-
const
|
|
67
|
-
const catalog = result.models;
|
|
66
|
+
const catalog = await modelsService.getProviderModels(currentProvider);
|
|
68
67
|
const currentModel = await resolveCommandModel(modelsService, currentProvider, context);
|
|
69
68
|
const availableModels = catalog.OPTIONS.map((option) => option.value);
|
|
70
69
|
const availableOptions = catalog.OPTIONS.map((option) => ({
|
|
71
70
|
value: option.value,
|
|
72
71
|
label: option.label,
|
|
73
72
|
description: option.description,
|
|
73
|
+
recordId: option.recordId,
|
|
74
|
+
isCustom: option.isCustom,
|
|
74
75
|
}));
|
|
75
76
|
|
|
76
77
|
return {
|
|
@@ -88,7 +89,6 @@ const executeModelsCommand = async (args, context, modelsService) => {
|
|
|
88
89
|
availableModels,
|
|
89
90
|
availableOptions,
|
|
90
91
|
defaultModel: catalog.DEFAULT,
|
|
91
|
-
cache: result.cache,
|
|
92
92
|
message: `Current model: ${currentModel}`,
|
|
93
93
|
},
|
|
94
94
|
};
|
|
@@ -16,12 +16,8 @@ import { createCommandsRouter } from '../commands.routes.js';
|
|
|
16
16
|
function createModelsService(sessionModels: Record<string, string> = {}) {
|
|
17
17
|
return {
|
|
18
18
|
getProviderModels: async () => ({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
22
|
-
expiresAt: '2026-01-02T00:00:00.000Z',
|
|
23
|
-
source: 'fresh' as const,
|
|
24
|
-
},
|
|
19
|
+
OPTIONS: [{ value: 'default', label: 'Default' }],
|
|
20
|
+
DEFAULT: 'default',
|
|
25
21
|
}),
|
|
26
22
|
getCurrentActiveModel: async () => ({ model: 'default' }),
|
|
27
23
|
setSessionModel: () => null,
|
|
@@ -39,7 +35,6 @@ function createModelsService(sessionModels: Record<string, string> = {}) {
|
|
|
39
35
|
};
|
|
40
36
|
},
|
|
41
37
|
resolveResumeModel: async () => undefined,
|
|
42
|
-
clearCache: () => undefined,
|
|
43
38
|
};
|
|
44
39
|
}
|
|
45
40
|
|
|
@@ -6,6 +6,8 @@ export { credentialsDb } from '@/modules/database/repositories/credentials.js';
|
|
|
6
6
|
export { githubTokensDb } from '@/modules/database/repositories/github-tokens.js';
|
|
7
7
|
export { notificationChannelEndpointsDb } from '@/modules/database/repositories/notification-channel-endpoints.js';
|
|
8
8
|
export { notificationPreferencesDb } from '@/modules/database/repositories/notification-preferences.js';
|
|
9
|
+
// providerModelsDb: used by Providers to persist user-managed custom model rows.
|
|
10
|
+
export { providerModelsDb } from '@/modules/database/repositories/provider-models.js';
|
|
9
11
|
// projectsDb: used by Projects, Worktrees, Git, WebSocket, and notification modules to persist and resolve project records.
|
|
10
12
|
export { projectsDb } from '@/modules/database/repositories/projects.db.js';
|
|
11
13
|
export { pushSubscriptionsDb } from '@/modules/database/repositories/push-subscriptions.js';
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
LAST_SCANNED_AT_SQL,
|
|
6
6
|
NOTIFICATION_CHANNEL_ENDPOINTS_TABLE_SCHEMA_SQL,
|
|
7
7
|
PROJECTS_TABLE_SCHEMA_SQL,
|
|
8
|
+
PROVIDER_MODELS_TABLE_SCHEMA_SQL,
|
|
8
9
|
PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL,
|
|
9
10
|
SESSIONS_TABLE_SCHEMA_SQL,
|
|
10
11
|
USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL,
|
|
@@ -416,6 +417,19 @@ const addSessionModelColumn = (db: Database): void => {
|
|
|
416
417
|
addColumnToTableIfNotExists(db, 'sessions', columnNames, 'model', 'TEXT');
|
|
417
418
|
};
|
|
418
419
|
|
|
420
|
+
/**
|
|
421
|
+
* Adds the `effort` column that records a session's reasoning-effort choice.
|
|
422
|
+
*
|
|
423
|
+
* Existing rows stay NULL so clients can continue falling back to their
|
|
424
|
+
* per-provider preference until the user selects an effort or sends a turn.
|
|
425
|
+
*/
|
|
426
|
+
const addSessionEffortColumn = (db: Database): void => {
|
|
427
|
+
const sessionsTableInfo = getTableInfo(db, 'sessions');
|
|
428
|
+
const columnNames = sessionsTableInfo.map((column) => column.name);
|
|
429
|
+
|
|
430
|
+
addColumnToTableIfNotExists(db, 'sessions', columnNames, 'effort', 'TEXT');
|
|
431
|
+
};
|
|
432
|
+
|
|
419
433
|
const ensureProjectsForSessionPaths = (db: Database): void => {
|
|
420
434
|
if (!tableExists(db, 'sessions')) {
|
|
421
435
|
return;
|
|
@@ -458,6 +472,11 @@ export const runMigrations = (db: Database) => {
|
|
|
458
472
|
db.exec(NOTIFICATION_CHANNEL_ENDPOINTS_TABLE_SCHEMA_SQL);
|
|
459
473
|
db.exec('CREATE INDEX IF NOT EXISTS idx_notification_channel_endpoints_user_channel ON notification_channel_endpoints(user_id, channel)');
|
|
460
474
|
db.exec('CREATE INDEX IF NOT EXISTS idx_notification_channel_endpoints_enabled ON notification_channel_endpoints(enabled)');
|
|
475
|
+
db.exec(PROVIDER_MODELS_TABLE_SCHEMA_SQL);
|
|
476
|
+
db.exec(`
|
|
477
|
+
CREATE INDEX IF NOT EXISTS idx_provider_models_provider_order
|
|
478
|
+
ON provider_models(provider, sort_order, id)
|
|
479
|
+
`);
|
|
461
480
|
|
|
462
481
|
db.exec(PROJECTS_TABLE_SCHEMA_SQL);
|
|
463
482
|
rebuildProjectsTableWithPrimaryKeySchema(db);
|
|
@@ -467,6 +486,7 @@ export const runMigrations = (db: Database) => {
|
|
|
467
486
|
migrateLegacySessionNames(db);
|
|
468
487
|
addProviderSessionIdMapping(db);
|
|
469
488
|
addSessionModelColumn(db);
|
|
489
|
+
addSessionEffortColumn(db);
|
|
470
490
|
ensureProjectsForSessionPaths(db);
|
|
471
491
|
|
|
472
492
|
db.exec('CREATE INDEX IF NOT EXISTS idx_session_ids_lookup ON sessions(session_id)');
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
2
|
+
import type {
|
|
3
|
+
CustomProviderModelInput,
|
|
4
|
+
CustomProviderModelRecord,
|
|
5
|
+
LLMProvider,
|
|
6
|
+
} from '@/shared/types.js';
|
|
7
|
+
|
|
8
|
+
type CustomProviderModelRow = {
|
|
9
|
+
id: number;
|
|
10
|
+
provider: LLMProvider;
|
|
11
|
+
model_id: string;
|
|
12
|
+
model_name: string;
|
|
13
|
+
sort_order: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const toCustomProviderModelRecord = (
|
|
17
|
+
row: CustomProviderModelRow,
|
|
18
|
+
): CustomProviderModelRecord => ({
|
|
19
|
+
recordId: row.id,
|
|
20
|
+
provider: row.provider,
|
|
21
|
+
modelId: row.model_id,
|
|
22
|
+
model: row.model_name,
|
|
23
|
+
sortOrder: row.sort_order,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const readCustomProviderModelRow = (
|
|
27
|
+
provider: LLMProvider,
|
|
28
|
+
recordId: number,
|
|
29
|
+
): CustomProviderModelRow | null => {
|
|
30
|
+
const row = getConnection().prepare(`
|
|
31
|
+
SELECT id, provider, model_id, model_name, sort_order
|
|
32
|
+
FROM provider_models
|
|
33
|
+
WHERE provider = ? AND id = ?
|
|
34
|
+
`).get(provider, recordId) as CustomProviderModelRow | undefined;
|
|
35
|
+
|
|
36
|
+
return row ?? null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Custom provider-model persistence API consumed by the Providers module.
|
|
41
|
+
*
|
|
42
|
+
* Every row in this repository is user-created. Predefined models are owned by
|
|
43
|
+
* provider adapters and never enter this table. Model-id changes are propagated
|
|
44
|
+
* to stored sessions, while deletion replaces affected session selections with
|
|
45
|
+
* the predefined default supplied by the Providers service.
|
|
46
|
+
*/
|
|
47
|
+
export const providerModelsDb = {
|
|
48
|
+
listCustomProviderModels(provider: LLMProvider): CustomProviderModelRecord[] {
|
|
49
|
+
const rows = getConnection().prepare(`
|
|
50
|
+
SELECT id, provider, model_id, model_name, sort_order
|
|
51
|
+
FROM provider_models
|
|
52
|
+
WHERE provider = ?
|
|
53
|
+
ORDER BY sort_order ASC, lower(model_name) ASC, id ASC
|
|
54
|
+
`).all(provider) as CustomProviderModelRow[];
|
|
55
|
+
|
|
56
|
+
return rows.map(toCustomProviderModelRecord);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getCustomProviderModel(
|
|
60
|
+
provider: LLMProvider,
|
|
61
|
+
recordId: number,
|
|
62
|
+
): CustomProviderModelRecord | null {
|
|
63
|
+
const row = readCustomProviderModelRow(provider, recordId);
|
|
64
|
+
return row ? toCustomProviderModelRecord(row) : null;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
findCustomProviderModelByModelId(
|
|
68
|
+
provider: LLMProvider,
|
|
69
|
+
modelId: string,
|
|
70
|
+
): CustomProviderModelRecord | null {
|
|
71
|
+
const row = getConnection().prepare(`
|
|
72
|
+
SELECT id, provider, model_id, model_name, sort_order
|
|
73
|
+
FROM provider_models
|
|
74
|
+
WHERE provider = ? AND model_id = ?
|
|
75
|
+
`).get(provider, modelId) as CustomProviderModelRow | undefined;
|
|
76
|
+
|
|
77
|
+
return row ? toCustomProviderModelRecord(row) : null;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
createCustomProviderModel(
|
|
81
|
+
provider: LLMProvider,
|
|
82
|
+
input: CustomProviderModelInput,
|
|
83
|
+
): CustomProviderModelRecord {
|
|
84
|
+
const db = getConnection();
|
|
85
|
+
const nextOrder = db.prepare(`
|
|
86
|
+
SELECT COALESCE(MAX(sort_order), -1) + 1 AS next_order
|
|
87
|
+
FROM provider_models
|
|
88
|
+
WHERE provider = ?
|
|
89
|
+
`).get(provider) as { next_order: number };
|
|
90
|
+
|
|
91
|
+
const result = db.prepare(`
|
|
92
|
+
INSERT INTO provider_models (provider, model_id, model_name, sort_order)
|
|
93
|
+
VALUES (?, ?, ?, ?)
|
|
94
|
+
`).run(provider, input.id, input.model, nextOrder.next_order);
|
|
95
|
+
|
|
96
|
+
const row = readCustomProviderModelRow(provider, Number(result.lastInsertRowid));
|
|
97
|
+
if (!row) {
|
|
98
|
+
throw new Error('Created provider model could not be read back.');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return toCustomProviderModelRecord(row);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
updateCustomProviderModel(
|
|
105
|
+
provider: LLMProvider,
|
|
106
|
+
recordId: number,
|
|
107
|
+
input: CustomProviderModelInput,
|
|
108
|
+
): CustomProviderModelRecord | null {
|
|
109
|
+
const db = getConnection();
|
|
110
|
+
const update = db.transaction(() => {
|
|
111
|
+
const previous = readCustomProviderModelRow(provider, recordId);
|
|
112
|
+
if (!previous) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
db.prepare(`
|
|
117
|
+
UPDATE provider_models
|
|
118
|
+
SET model_id = ?, model_name = ?, updated_at = CURRENT_TIMESTAMP
|
|
119
|
+
WHERE provider = ? AND id = ?
|
|
120
|
+
`).run(input.id, input.model, provider, recordId);
|
|
121
|
+
|
|
122
|
+
if (previous.model_id !== input.id) {
|
|
123
|
+
db.prepare(`
|
|
124
|
+
UPDATE sessions
|
|
125
|
+
SET model = ?, updated_at = CURRENT_TIMESTAMP
|
|
126
|
+
WHERE provider = ? AND model = ?
|
|
127
|
+
`).run(input.id, provider, previous.model_id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return readCustomProviderModelRow(provider, recordId);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const row = update();
|
|
134
|
+
return row ? toCustomProviderModelRecord(row) : null;
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
deleteCustomProviderModel(
|
|
138
|
+
provider: LLMProvider,
|
|
139
|
+
recordId: number,
|
|
140
|
+
fallbackModelId: string,
|
|
141
|
+
): CustomProviderModelRecord | null {
|
|
142
|
+
const db = getConnection();
|
|
143
|
+
const remove = db.transaction(() => {
|
|
144
|
+
const row = readCustomProviderModelRow(provider, recordId);
|
|
145
|
+
if (!row) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// The effort is cleared alongside the model because it was chosen for the
|
|
150
|
+
// deleted model; replaced sessions resume on the fallback model's default.
|
|
151
|
+
db.prepare(`
|
|
152
|
+
UPDATE sessions
|
|
153
|
+
SET model = ?, effort = NULL, updated_at = CURRENT_TIMESTAMP
|
|
154
|
+
WHERE provider = ? AND model = ?
|
|
155
|
+
`).run(fallbackModelId, provider, row.model_id);
|
|
156
|
+
|
|
157
|
+
db.prepare(`
|
|
158
|
+
DELETE FROM provider_models
|
|
159
|
+
WHERE provider = ? AND id = ?
|
|
160
|
+
`).run(provider, recordId);
|
|
161
|
+
|
|
162
|
+
return row;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const row = remove();
|
|
166
|
+
return row ? toCustomProviderModelRecord(row) : null;
|
|
167
|
+
},
|
|
168
|
+
};
|