@cloudcli-ai/cloudcli 1.30.0 → 1.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/assets/index-B37ufcoi.js +1262 -0
- package/dist/assets/{index-DI22MZMb.css → index-DF8TTVHl.css} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +115 -955
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/middleware/auth.js +1 -1
- package/dist-server/server/middleware/auth.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +125 -0
- package/dist-server/server/modules/database/connection.js.map +1 -0
- package/dist-server/server/modules/database/index.js +13 -0
- package/dist-server/server/modules/database/index.js.map +1 -0
- package/dist-server/server/modules/database/init-db.js +18 -0
- package/dist-server/server/modules/database/init-db.js.map +1 -0
- package/dist-server/server/modules/database/migrations.js +370 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -0
- package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
- package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
- package/dist-server/server/modules/database/repositories/app-config.js +47 -0
- package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
- package/dist-server/server/modules/database/repositories/credentials.js +68 -0
- package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +64 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.js +160 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +109 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/users.js +88 -0
- package/dist-server/server/modules/database/repositories/users.js.map +1 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +143 -0
- package/dist-server/server/modules/database/schema.js.map +1 -0
- package/dist-server/server/modules/projects/index.js +4 -0
- package/dist-server/server/modules/projects/index.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +188 -0
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +219 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js +65 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js +93 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +167 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +169 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +126 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js +85 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js +95 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +87 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +4 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +63 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +144 -7
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +71 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +225 -13
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +127 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +296 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +277 -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.provider.js +2 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +172 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +868 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +234 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +78 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +3 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +188 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +334 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
- package/dist-server/server/routes/agent.js +10 -17
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/auth.js +3 -1
- package/dist-server/server/routes/auth.js.map +1 -1
- package/dist-server/server/routes/commands.js +1 -1
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/gemini.js +2 -2
- package/dist-server/server/routes/gemini.js.map +1 -1
- package/dist-server/server/routes/git.js +33 -29
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/settings.js +1 -1
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +97 -110
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/dist-server/server/routes/user.js +1 -1
- package/dist-server/server/routes/user.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +2 -2
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/services/vapid-keys.js +2 -1
- package/dist-server/server/services/vapid-keys.js.map +1 -1
- package/dist-server/server/shared/types.js +0 -1
- package/dist-server/server/shared/types.js.map +1 -1
- package/dist-server/server/shared/utils.js +408 -7
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/taskmaster-websocket.js +18 -12
- package/dist-server/server/utils/taskmaster-websocket.js.map +1 -1
- package/dist-server/shared/modelConstants.js +2 -1
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +3 -1
- package/server/index.js +146 -1012
- package/server/middleware/auth.js +1 -1
- package/server/modules/database/connection.ts +143 -0
- package/server/modules/database/index.ts +12 -0
- package/server/modules/database/init-db.ts +17 -0
- package/server/modules/database/migrations.ts +442 -0
- package/server/modules/database/repositories/api-keys.ts +119 -0
- package/server/modules/database/repositories/app-config.ts +53 -0
- package/server/modules/database/repositories/credentials.ts +106 -0
- package/server/modules/database/repositories/github-tokens.ts +100 -0
- package/server/modules/database/repositories/notification-preferences.ts +103 -0
- package/server/modules/database/repositories/projects.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/projects.db.ts +183 -0
- package/server/modules/database/repositories/push-subscriptions.ts +80 -0
- package/server/modules/database/repositories/scan-state.db.ts +42 -0
- package/server/modules/database/repositories/sessions.db.ts +174 -0
- package/server/modules/database/repositories/users.ts +140 -0
- package/server/modules/database/repositories/vapid-keys.ts +57 -0
- package/server/modules/database/schema.ts +152 -0
- package/server/modules/projects/index.ts +6 -0
- package/server/modules/projects/projects.routes.ts +247 -0
- package/server/modules/projects/services/project-clone.service.ts +321 -0
- package/server/modules/projects/services/project-delete.service.ts +75 -0
- package/server/modules/projects/services/project-management.service.ts +150 -0
- package/server/modules/projects/services/project-star.service.ts +78 -0
- package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +285 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +183 -0
- package/server/modules/projects/tests/project-management.service.test.ts +117 -0
- package/server/modules/projects/tests/project-star.service.test.ts +123 -0
- package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +110 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +182 -13
- package/server/modules/providers/list/claude/claude.provider.ts +3 -1
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +119 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +262 -17
- package/server/modules/providers/list/codex/codex.provider.ts +3 -1
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +176 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +3 -1
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +401 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +327 -13
- package/server/modules/providers/list/gemini/gemini.provider.ts +3 -1
- package/server/modules/providers/provider.routes.ts +220 -12
- package/server/modules/providers/services/session-conversations-search.service.ts +1167 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +74 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +283 -0
- package/server/modules/providers/services/sessions.service.ts +89 -4
- package/server/modules/providers/shared/base/abstract.provider.ts +8 -1
- package/server/modules/websocket/README.md +267 -0
- package/server/modules/websocket/index.ts +2 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +271 -0
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +453 -0
- package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
- package/server/modules/websocket/services/websocket-server.service.ts +58 -0
- package/server/modules/websocket/services/websocket-state.service.ts +16 -0
- package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
- package/server/routes/agent.js +11 -16
- package/server/routes/auth.js +4 -2
- package/server/routes/commands.js +1 -1
- package/server/routes/gemini.js +3 -2
- package/server/routes/git.js +33 -28
- package/server/routes/settings.js +1 -1
- package/server/routes/taskmaster.js +108 -111
- package/server/routes/user.js +1 -1
- package/server/services/notification-orchestrator.js +3 -2
- package/server/services/vapid-keys.js +2 -1
- package/server/shared/interfaces.ts +39 -1
- package/server/shared/types.ts +185 -30
- package/server/shared/utils.ts +485 -9
- package/server/utils/taskmaster-websocket.js +19 -13
- package/shared/modelConstants.js +2 -1
- package/dist/assets/index-RX-W_g_Q.js +0 -1262
- package/dist-server/server/database/db.js +0 -535
- package/dist-server/server/database/db.js.map +0 -1
- package/dist-server/server/database/schema.js +0 -97
- package/dist-server/server/database/schema.js.map +0 -1
- package/dist-server/server/projects.js +0 -2325
- package/dist-server/server/projects.js.map +0 -1
- package/dist-server/server/routes/codex.js +0 -18
- package/dist-server/server/routes/codex.js.map +0 -1
- package/dist-server/server/routes/messages.js +0 -56
- package/dist-server/server/routes/messages.js.map +0 -1
- package/dist-server/server/routes/projects.js +0 -505
- package/dist-server/server/routes/projects.js.map +0 -1
- package/server/database/db.js +0 -593
- package/server/database/schema.js +0 -102
- package/server/projects.js +0 -2555
- package/server/routes/codex.js +0 -19
- package/server/routes/messages.js +0 -61
- package/server/routes/projects.js +0 -548
package/server/shared/utils.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import {
|
|
4
|
+
access,
|
|
5
|
+
lstat,
|
|
6
|
+
mkdir,
|
|
7
|
+
readFile,
|
|
8
|
+
readdir,
|
|
9
|
+
readlink,
|
|
10
|
+
realpath,
|
|
11
|
+
stat,
|
|
12
|
+
writeFile,
|
|
13
|
+
} from 'node:fs/promises';
|
|
14
|
+
import os from 'node:os';
|
|
4
15
|
import path from 'node:path';
|
|
16
|
+
import readline from 'node:readline';
|
|
5
17
|
|
|
6
18
|
import type { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
7
19
|
|
|
@@ -10,8 +22,17 @@ import type {
|
|
|
10
22
|
ApiSuccessShape,
|
|
11
23
|
AppErrorOptions,
|
|
12
24
|
NormalizedMessage,
|
|
25
|
+
WorkspacePathValidationResult,
|
|
13
26
|
} from '@/shared/types.js';
|
|
14
27
|
|
|
28
|
+
//----------------- NORMALIZED MESSAGE HELPER INPUT TYPES ------------
|
|
29
|
+
/**
|
|
30
|
+
* Input payload accepted by `createNormalizedMessage`.
|
|
31
|
+
*
|
|
32
|
+
* Callers provide provider-specific fields plus the required `kind/provider`
|
|
33
|
+
* pair; this helper fills missing envelope fields (`id`, `sessionId`,
|
|
34
|
+
* `timestamp`) in a consistent way.
|
|
35
|
+
*/
|
|
15
36
|
type NormalizedMessageInput =
|
|
16
37
|
{
|
|
17
38
|
kind: NormalizedMessage['kind'];
|
|
@@ -21,6 +42,14 @@ type NormalizedMessageInput =
|
|
|
21
42
|
timestamp?: string | null;
|
|
22
43
|
} & Record<string, unknown>;
|
|
23
44
|
|
|
45
|
+
// ---------------------------
|
|
46
|
+
//----------------- HTTP HANDLER UTILITIES ------------
|
|
47
|
+
/**
|
|
48
|
+
* Wraps arbitrary data in the standard API success envelope.
|
|
49
|
+
*
|
|
50
|
+
* Use this helper in route handlers to keep successful JSON responses consistent
|
|
51
|
+
* across endpoints.
|
|
52
|
+
*/
|
|
24
53
|
export function createApiSuccessResponse<TData>(
|
|
25
54
|
data: TData,
|
|
26
55
|
): ApiSuccessShape<TData> {
|
|
@@ -30,6 +59,12 @@ export function createApiSuccessResponse<TData>(
|
|
|
30
59
|
};
|
|
31
60
|
}
|
|
32
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Converts an async Express handler into a standard `RequestHandler` and routes
|
|
64
|
+
* rejected promises to Express error middleware.
|
|
65
|
+
*
|
|
66
|
+
* Use this to avoid repeating `try/catch(next)` in every async route.
|
|
67
|
+
*/
|
|
33
68
|
export function asyncHandler(
|
|
34
69
|
handler: (req: Request, res: Response, next: NextFunction) => Promise<unknown>
|
|
35
70
|
): RequestHandler {
|
|
@@ -38,7 +73,14 @@ export function asyncHandler(
|
|
|
38
73
|
};
|
|
39
74
|
}
|
|
40
75
|
|
|
41
|
-
//
|
|
76
|
+
// ---------------------------
|
|
77
|
+
//----------------- SHARED ERROR UTILITIES ------------
|
|
78
|
+
/**
|
|
79
|
+
* Shared application error with HTTP status and machine-readable code metadata.
|
|
80
|
+
*
|
|
81
|
+
* Throw this from service/route layers when the caller should receive a
|
|
82
|
+
* controlled error response rather than a generic 500.
|
|
83
|
+
*/
|
|
42
84
|
export class AppError extends Error {
|
|
43
85
|
readonly code: string;
|
|
44
86
|
readonly statusCode: number;
|
|
@@ -53,9 +95,226 @@ export class AppError extends Error {
|
|
|
53
95
|
}
|
|
54
96
|
}
|
|
55
97
|
|
|
56
|
-
//
|
|
98
|
+
// ---------------------------
|
|
99
|
+
//----------------- WORKSPACE PATH VALIDATION UTILITIES ------------
|
|
100
|
+
/**
|
|
101
|
+
* Root directory that all workspace/project paths must stay under.
|
|
102
|
+
*
|
|
103
|
+
* This is resolved from `WORKSPACES_ROOT` when configured; otherwise it falls
|
|
104
|
+
* back to the current user's home directory.
|
|
105
|
+
*/
|
|
106
|
+
export const WORKSPACES_ROOT = process.env.WORKSPACES_ROOT || os.homedir();
|
|
57
107
|
|
|
58
|
-
|
|
108
|
+
/**
|
|
109
|
+
* System-critical paths that must never be used as workspace roots.
|
|
110
|
+
*
|
|
111
|
+
* The validation helper blocks these values directly and also blocks paths
|
|
112
|
+
* nested under them (with explicit allow-list exceptions where necessary).
|
|
113
|
+
*/
|
|
114
|
+
export const FORBIDDEN_WORKSPACE_PATHS = [
|
|
115
|
+
// Unix
|
|
116
|
+
'/',
|
|
117
|
+
'/etc',
|
|
118
|
+
'/bin',
|
|
119
|
+
'/sbin',
|
|
120
|
+
'/usr',
|
|
121
|
+
'/dev',
|
|
122
|
+
'/proc',
|
|
123
|
+
'/sys',
|
|
124
|
+
'/var',
|
|
125
|
+
'/boot',
|
|
126
|
+
'/root',
|
|
127
|
+
'/lib',
|
|
128
|
+
'/lib64',
|
|
129
|
+
'/opt',
|
|
130
|
+
'/tmp',
|
|
131
|
+
'/run',
|
|
132
|
+
// Windows
|
|
133
|
+
'C:\\Windows',
|
|
134
|
+
'C:\\Program Files',
|
|
135
|
+
'C:\\Program Files (x86)',
|
|
136
|
+
'C:\\ProgramData',
|
|
137
|
+
'C:\\System Volume Information',
|
|
138
|
+
'C:\\$Recycle.Bin',
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
function stripWindowsLongPathPrefix(inputPath: string): string {
|
|
142
|
+
if (inputPath.startsWith('\\\\?\\UNC\\')) {
|
|
143
|
+
return `\\\\${inputPath.slice('\\\\?\\UNC\\'.length)}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (inputPath.startsWith('\\\\?\\')) {
|
|
147
|
+
return inputPath.slice('\\\\?\\'.length);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return inputPath;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function shouldUseWindowsPathNormalization(inputPath: string): boolean {
|
|
154
|
+
if (process.platform === 'win32') {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return inputPath.startsWith('\\\\') || /^[a-zA-Z]:([\\/]|$)/.test(inputPath);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Canonicalizes project/workspace paths for stable DB keys and comparisons.
|
|
163
|
+
*
|
|
164
|
+
* Normalization rules:
|
|
165
|
+
* - trim whitespace
|
|
166
|
+
* - strip Windows long-path prefixes (`\\?\` and `\\?\UNC\`)
|
|
167
|
+
* - normalize path separators and dot segments
|
|
168
|
+
* - trim trailing separators except for filesystem roots
|
|
169
|
+
*/
|
|
170
|
+
export function normalizeProjectPath(inputPath: string): string {
|
|
171
|
+
if (typeof inputPath !== 'string') {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const trimmed = inputPath.trim();
|
|
176
|
+
if (!trimmed) {
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const withoutLongPrefix = stripWindowsLongPathPrefix(trimmed);
|
|
181
|
+
const useWindowsPathRules = shouldUseWindowsPathNormalization(withoutLongPrefix);
|
|
182
|
+
const normalized = useWindowsPathRules
|
|
183
|
+
? path.win32.normalize(withoutLongPrefix)
|
|
184
|
+
: path.posix.normalize(withoutLongPrefix);
|
|
185
|
+
|
|
186
|
+
if (!normalized) {
|
|
187
|
+
return '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const parser = useWindowsPathRules ? path.win32 : path.posix;
|
|
191
|
+
const root = parser.parse(normalized).root;
|
|
192
|
+
if (normalized === root) {
|
|
193
|
+
return normalized;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return normalized.replace(/[\\/]+$/, '');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Validates that a user-supplied workspace path is safe to use.
|
|
201
|
+
*
|
|
202
|
+
* Call this before any filesystem mutation that creates or registers projects.
|
|
203
|
+
* The function resolves symlinks, enforces `WORKSPACES_ROOT` containment, and
|
|
204
|
+
* blocks known system directories.
|
|
205
|
+
*/
|
|
206
|
+
export async function validateWorkspacePath(requestedPath: string): Promise<WorkspacePathValidationResult> {
|
|
207
|
+
try {
|
|
208
|
+
const normalizedRequestedPath = normalizeProjectPath(requestedPath);
|
|
209
|
+
if (!normalizedRequestedPath) {
|
|
210
|
+
return {
|
|
211
|
+
valid: false,
|
|
212
|
+
error: 'Workspace path is required',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const absolutePath = path.resolve(normalizedRequestedPath);
|
|
217
|
+
const normalizedPath = normalizeProjectPath(absolutePath);
|
|
218
|
+
|
|
219
|
+
if (FORBIDDEN_WORKSPACE_PATHS.includes(normalizedPath) || normalizedPath === '/') {
|
|
220
|
+
return {
|
|
221
|
+
valid: false,
|
|
222
|
+
error: 'Cannot use system-critical directories as workspace locations',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const forbiddenPath of FORBIDDEN_WORKSPACE_PATHS) {
|
|
227
|
+
const normalizedForbiddenPath = normalizeProjectPath(forbiddenPath);
|
|
228
|
+
if (
|
|
229
|
+
normalizedPath === normalizedForbiddenPath
|
|
230
|
+
|| normalizedPath.startsWith(`${normalizedForbiddenPath}${path.sep}`)
|
|
231
|
+
) {
|
|
232
|
+
// Allow specific user-writable folders under /var.
|
|
233
|
+
if (
|
|
234
|
+
normalizedForbiddenPath === '/var'
|
|
235
|
+
&& (normalizedPath.startsWith('/var/tmp') || normalizedPath.startsWith('/var/folders'))
|
|
236
|
+
) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
valid: false,
|
|
242
|
+
error: `Cannot create workspace in system directory: ${forbiddenPath}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let resolvedPath = normalizeProjectPath(absolutePath);
|
|
248
|
+
try {
|
|
249
|
+
await access(absolutePath);
|
|
250
|
+
resolvedPath = normalizeProjectPath(await realpath(absolutePath));
|
|
251
|
+
} catch (error) {
|
|
252
|
+
const fileError = error as NodeJS.ErrnoException;
|
|
253
|
+
if (fileError.code !== 'ENOENT') {
|
|
254
|
+
throw fileError;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const parentPath = path.dirname(absolutePath);
|
|
258
|
+
try {
|
|
259
|
+
const parentRealPath = await realpath(parentPath);
|
|
260
|
+
resolvedPath = normalizeProjectPath(path.join(parentRealPath, path.basename(absolutePath)));
|
|
261
|
+
} catch (parentError) {
|
|
262
|
+
const parentFileError = parentError as NodeJS.ErrnoException;
|
|
263
|
+
if (parentFileError.code !== 'ENOENT') {
|
|
264
|
+
throw parentFileError;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const resolvedWorkspaceRoot = normalizeProjectPath(await realpath(WORKSPACES_ROOT));
|
|
270
|
+
if (
|
|
271
|
+
!resolvedPath.startsWith(`${resolvedWorkspaceRoot}${path.sep}`)
|
|
272
|
+
&& resolvedPath !== resolvedWorkspaceRoot
|
|
273
|
+
) {
|
|
274
|
+
return {
|
|
275
|
+
valid: false,
|
|
276
|
+
error: `Workspace path must be within the allowed workspace root: ${WORKSPACES_ROOT}`,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
await access(absolutePath);
|
|
282
|
+
const pathStats = await lstat(absolutePath);
|
|
283
|
+
if (pathStats.isSymbolicLink()) {
|
|
284
|
+
const symlinkTarget = await readlink(absolutePath);
|
|
285
|
+
const resolvedSymlinkPath = path.resolve(path.dirname(absolutePath), symlinkTarget);
|
|
286
|
+
const realSymlinkPath = await realpath(resolvedSymlinkPath);
|
|
287
|
+
if (
|
|
288
|
+
!realSymlinkPath.startsWith(`${resolvedWorkspaceRoot}${path.sep}`)
|
|
289
|
+
&& realSymlinkPath !== resolvedWorkspaceRoot
|
|
290
|
+
) {
|
|
291
|
+
return {
|
|
292
|
+
valid: false,
|
|
293
|
+
error: 'Symlink target is outside the allowed workspace root',
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} catch (error) {
|
|
298
|
+
const fileError = error as NodeJS.ErrnoException;
|
|
299
|
+
if (fileError.code !== 'ENOENT') {
|
|
300
|
+
throw fileError;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
valid: true,
|
|
306
|
+
resolvedPath,
|
|
307
|
+
};
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
valid: false,
|
|
311
|
+
error: `Path validation failed: ${(error as Error).message}`,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------------------------
|
|
317
|
+
//----------------- NORMALIZED PROVIDER MESSAGE UTILITIES ------------
|
|
59
318
|
/**
|
|
60
319
|
* Generates a stable unique id for normalized provider messages.
|
|
61
320
|
*/
|
|
@@ -80,9 +339,8 @@ export function createNormalizedMessage(fields: NormalizedMessageInput): Normali
|
|
|
80
339
|
};
|
|
81
340
|
}
|
|
82
341
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
// ------------------------ The following are mainly for provider MCP runtimes ------------------------
|
|
342
|
+
// ---------------------------
|
|
343
|
+
//----------------- MCP CONFIG PARSING UTILITIES ------------
|
|
86
344
|
/**
|
|
87
345
|
* Safely narrows an unknown value to a plain object record.
|
|
88
346
|
*
|
|
@@ -154,6 +412,62 @@ export const readStringRecord = (value: unknown): Record<string, string> | undef
|
|
|
154
412
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
155
413
|
};
|
|
156
414
|
|
|
415
|
+
// ---------------------------
|
|
416
|
+
//----------------- WEBSOCKET PAYLOAD PARSING UTILITIES ------------
|
|
417
|
+
/**
|
|
418
|
+
* Parses one websocket message payload into a plain JSON object record.
|
|
419
|
+
*
|
|
420
|
+
* Use this in realtime handlers that receive raw websocket payloads as `string`,
|
|
421
|
+
* `Buffer`, `ArrayBuffer`, or chunk arrays. The helper converts supported
|
|
422
|
+
* payload formats to UTF-8 text, parses JSON, and returns only object payloads.
|
|
423
|
+
* Primitive/array/invalid payloads return `null` so callers can handle bad input
|
|
424
|
+
* without throwing from deeply nested message handlers.
|
|
425
|
+
*/
|
|
426
|
+
export const parseIncomingJsonObject = (payload: unknown): AnyRecord | null => {
|
|
427
|
+
let text: string | null = null;
|
|
428
|
+
|
|
429
|
+
if (typeof payload === 'string') {
|
|
430
|
+
text = payload;
|
|
431
|
+
} else if (Buffer.isBuffer(payload)) {
|
|
432
|
+
text = payload.toString('utf8');
|
|
433
|
+
} else if (payload instanceof ArrayBuffer) {
|
|
434
|
+
text = Buffer.from(payload).toString('utf8');
|
|
435
|
+
} else if (Array.isArray(payload)) {
|
|
436
|
+
const buffers = payload
|
|
437
|
+
.map((entry) => {
|
|
438
|
+
if (Buffer.isBuffer(entry)) {
|
|
439
|
+
return entry;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (entry instanceof ArrayBuffer) {
|
|
443
|
+
return Buffer.from(entry);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (ArrayBuffer.isView(entry)) {
|
|
447
|
+
return Buffer.from(entry.buffer, entry.byteOffset, entry.byteLength);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return null;
|
|
451
|
+
})
|
|
452
|
+
.filter((entry): entry is Buffer => entry !== null);
|
|
453
|
+
|
|
454
|
+
if (buffers.length > 0) {
|
|
455
|
+
text = Buffer.concat(buffers).toString('utf8');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const parsed = JSON.parse(text) as unknown;
|
|
465
|
+
return readObjectRecord(parsed);
|
|
466
|
+
} catch {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
157
471
|
/**
|
|
158
472
|
* Reads a JSON config file and guarantees a plain object result.
|
|
159
473
|
*
|
|
@@ -189,5 +503,167 @@ export const writeJsonConfig = async (filePath: string, data: Record<string, unk
|
|
|
189
503
|
await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
190
504
|
};
|
|
191
505
|
|
|
192
|
-
//
|
|
506
|
+
// ---------------------------
|
|
507
|
+
//----------------- SESSION SYNCHRONIZER TITLE HELPERS ------------
|
|
508
|
+
/**
|
|
509
|
+
* Produces a compact session title suitable for UI rendering and DB storage.
|
|
510
|
+
*
|
|
511
|
+
* Use this when converting provider-native names into a consistent title value.
|
|
512
|
+
* The helper collapses repeated whitespace, trims the result, and truncates it
|
|
513
|
+
* to 120 characters so every provider writes stable and bounded metadata.
|
|
514
|
+
* If the normalized input is empty, it returns the supplied fallback title.
|
|
515
|
+
*/
|
|
516
|
+
export function normalizeSessionName(rawValue: string | undefined, fallback: string): string {
|
|
517
|
+
const normalized = (rawValue ?? '').replace(/\s+/g, ' ').trim();
|
|
518
|
+
if (!normalized) {
|
|
519
|
+
return fallback;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return normalized.slice(0, 120);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ---------------------------
|
|
526
|
+
//----------------- SESSION SYNCHRONIZER FILESYSTEM HELPERS ------------
|
|
527
|
+
/**
|
|
528
|
+
* Recursively discovers files that match one extension, with optional incremental filtering.
|
|
529
|
+
*
|
|
530
|
+
* Provider synchronizers call this to find transcript artifacts under provider
|
|
531
|
+
* home directories. Pass `lastScanAt` to include only files created after the
|
|
532
|
+
* previous scan, or pass `null` to perform a full rescan. Missing directories
|
|
533
|
+
* are treated as empty because not every provider exists on every machine.
|
|
534
|
+
*/
|
|
535
|
+
export async function findFilesRecursivelyCreatedAfter(
|
|
536
|
+
rootDir: string,
|
|
537
|
+
extension: string,
|
|
538
|
+
lastScanAt: Date | null,
|
|
539
|
+
fileList: string[] = []
|
|
540
|
+
): Promise<string[]> {
|
|
541
|
+
try {
|
|
542
|
+
const entries = await readdir(rootDir, { withFileTypes: true });
|
|
543
|
+
for (const entry of entries) {
|
|
544
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
545
|
+
|
|
546
|
+
if (entry.isDirectory()) {
|
|
547
|
+
await findFilesRecursivelyCreatedAfter(fullPath, extension, lastScanAt, fileList);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!entry.isFile() || !entry.name.endsWith(extension)) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!lastScanAt) {
|
|
556
|
+
fileList.push(fullPath);
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const fileStat = await stat(fullPath);
|
|
561
|
+
if (fileStat.birthtime > lastScanAt) {
|
|
562
|
+
fileList.push(fullPath);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} catch {
|
|
566
|
+
// Missing provider folders are expected in first-run or partial setups.
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return fileList;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Reads file creation/update timestamps and maps them to DB-friendly ISO strings.
|
|
574
|
+
*
|
|
575
|
+
* Session indexers use this to persist `created_at` and `updated_at` metadata
|
|
576
|
+
* when upserting sessions. If the file cannot be read, an empty object is
|
|
577
|
+
* returned so indexing can continue for other files.
|
|
578
|
+
*/
|
|
579
|
+
export async function readFileTimestamps(
|
|
580
|
+
filePath: string
|
|
581
|
+
): Promise<{ createdAt?: string; updatedAt?: string }> {
|
|
582
|
+
try {
|
|
583
|
+
const fileStat = await stat(filePath);
|
|
584
|
+
return {
|
|
585
|
+
createdAt: fileStat.birthtime.toISOString(),
|
|
586
|
+
updatedAt: fileStat.mtime.toISOString(),
|
|
587
|
+
};
|
|
588
|
+
} catch {
|
|
589
|
+
return {};
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// ---------------------------
|
|
594
|
+
//----------------- SESSION SYNCHRONIZER JSONL PARSING HELPERS ------------
|
|
595
|
+
/**
|
|
596
|
+
* Builds a first-seen key/value lookup map from a JSONL file.
|
|
597
|
+
*
|
|
598
|
+
* Use this for provider index files where session id -> display name metadata
|
|
599
|
+
* is stored line-by-line. The first value for each key wins, preserving the
|
|
600
|
+
* earliest known label while avoiding repeated map overwrites.
|
|
601
|
+
*/
|
|
602
|
+
export async function buildLookupMap(
|
|
603
|
+
filePath: string,
|
|
604
|
+
keyField: string,
|
|
605
|
+
valueField: string
|
|
606
|
+
): Promise<Map<string, string>> {
|
|
607
|
+
const lookup = new Map<string, string>();
|
|
608
|
+
|
|
609
|
+
try {
|
|
610
|
+
const fileStream = fs.createReadStream(filePath);
|
|
611
|
+
const lineReader = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
612
|
+
|
|
613
|
+
for await (const line of lineReader) {
|
|
614
|
+
const trimmed = line.trim();
|
|
615
|
+
if (!trimmed) {
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const parsed = JSON.parse(trimmed) as Record<string, unknown>;
|
|
620
|
+
const key = parsed[keyField];
|
|
621
|
+
const value = parsed[valueField];
|
|
622
|
+
|
|
623
|
+
if (typeof key === 'string' && typeof value === 'string' && !lookup.has(key)) {
|
|
624
|
+
lookup.set(key, value);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} catch {
|
|
628
|
+
// Missing or unreadable lookup files should not block session sync.
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return lookup;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Reads a JSONL file and returns the first extracted payload that matches caller criteria.
|
|
636
|
+
*
|
|
637
|
+
* The caller supplies an `extractor` that validates provider-specific row
|
|
638
|
+
* shapes. This helper centralizes line-by-line parsing and lets indexers stop
|
|
639
|
+
* scanning as soon as one valid row is found.
|
|
640
|
+
*/
|
|
641
|
+
export async function extractFirstValidJsonlData<T>(
|
|
642
|
+
filePath: string,
|
|
643
|
+
extractor: (parsedJson: unknown) => T | null | undefined
|
|
644
|
+
): Promise<T | null> {
|
|
645
|
+
try {
|
|
646
|
+
const fileStream = fs.createReadStream(filePath);
|
|
647
|
+
const lineReader = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
648
|
+
|
|
649
|
+
for await (const line of lineReader) {
|
|
650
|
+
const trimmed = line.trim();
|
|
651
|
+
if (!trimmed) {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const parsed = JSON.parse(trimmed);
|
|
656
|
+
const extracted = extractor(parsed);
|
|
657
|
+
if (extracted) {
|
|
658
|
+
lineReader.close();
|
|
659
|
+
fileStream.close();
|
|
660
|
+
return extracted;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
} catch {
|
|
664
|
+
// Ignore malformed or missing artifacts so full scans keep progressing.
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
193
669
|
|
|
@@ -7,20 +7,25 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Broadcast TaskMaster project update to all connected clients
|
|
10
|
+
* Broadcast TaskMaster project update to all connected clients.
|
|
11
|
+
*
|
|
12
|
+
* The payload key is `projectId` post-migration so frontend listeners can
|
|
13
|
+
* match notifications against the DB-assigned project identifier they
|
|
14
|
+
* already use everywhere else.
|
|
15
|
+
*
|
|
11
16
|
* @param {WebSocket.Server} wss - WebSocket server instance
|
|
12
|
-
* @param {string}
|
|
17
|
+
* @param {string} projectId - DB id of the updated project
|
|
13
18
|
* @param {Object} taskMasterData - Updated TaskMaster data
|
|
14
19
|
*/
|
|
15
|
-
export function broadcastTaskMasterProjectUpdate(wss,
|
|
16
|
-
if (!wss || !
|
|
17
|
-
console.warn('TaskMaster WebSocket broadcast: Missing wss or
|
|
20
|
+
export function broadcastTaskMasterProjectUpdate(wss, projectId, taskMasterData) {
|
|
21
|
+
if (!wss || !projectId) {
|
|
22
|
+
console.warn('TaskMaster WebSocket broadcast: Missing wss or projectId');
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
const message = {
|
|
22
27
|
type: 'taskmaster-project-updated',
|
|
23
|
-
|
|
28
|
+
projectId,
|
|
24
29
|
taskMasterData,
|
|
25
30
|
timestamp: new Date().toISOString()
|
|
26
31
|
};
|
|
@@ -38,20 +43,21 @@ export function broadcastTaskMasterProjectUpdate(wss, projectName, taskMasterDat
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
|
-
* Broadcast TaskMaster tasks update for a specific project
|
|
42
|
-
*
|
|
43
|
-
* @param {
|
|
46
|
+
* Broadcast TaskMaster tasks update for a specific project.
|
|
47
|
+
*
|
|
48
|
+
* @param {WebSocket.Server} wss - WebSocket server instance
|
|
49
|
+
* @param {string} projectId - DB id of the project with updated tasks
|
|
44
50
|
* @param {Object} tasksData - Updated tasks data
|
|
45
51
|
*/
|
|
46
|
-
export function broadcastTaskMasterTasksUpdate(wss,
|
|
47
|
-
if (!wss || !
|
|
48
|
-
console.warn('TaskMaster WebSocket broadcast: Missing wss or
|
|
52
|
+
export function broadcastTaskMasterTasksUpdate(wss, projectId, tasksData) {
|
|
53
|
+
if (!wss || !projectId) {
|
|
54
|
+
console.warn('TaskMaster WebSocket broadcast: Missing wss or projectId');
|
|
49
55
|
return;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
const message = {
|
|
53
59
|
type: 'taskmaster-tasks-updated',
|
|
54
|
-
|
|
60
|
+
projectId,
|
|
55
61
|
tasksData,
|
|
56
62
|
timestamp: new Date().toISOString()
|
|
57
63
|
};
|
package/shared/modelConstants.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
export const CLAUDE_MODELS = {
|
|
14
14
|
// Models in SDK format (what the actual SDK accepts)
|
|
15
15
|
OPTIONS: [
|
|
16
|
-
{ value: "sonnet", label: "Sonnet" },
|
|
17
16
|
{ value: "opus", label: "Opus" },
|
|
17
|
+
{ value: "sonnet", label: "Sonnet" },
|
|
18
18
|
{ value: "haiku", label: "Haiku" },
|
|
19
19
|
{ value: "claude-opus-4-6", label: "Opus 4.6" },
|
|
20
20
|
{ value: "opusplan", label: "Opus Plan" },
|
|
@@ -59,6 +59,7 @@ export const CURSOR_MODELS = {
|
|
|
59
59
|
*/
|
|
60
60
|
export const CODEX_MODELS = {
|
|
61
61
|
OPTIONS: [
|
|
62
|
+
{ value: "gpt-5.5", label: "GPT-5.5" },
|
|
62
63
|
{ value: "gpt-5.4", label: "GPT-5.4" },
|
|
63
64
|
{ value: "gpt-5.4-mini", label: "GPT-5.4 mini" },
|
|
64
65
|
{ value: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|