@pixelbyte-software/pixcode 1.54.9 → 1.54.11
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/CODE_OF_CONDUCT.md +41 -41
- package/CONTRIBUTING.md +156 -156
- package/LICENSE +21 -718
- package/README.de.md +169 -169
- package/README.ja.md +167 -167
- package/README.ko.md +167 -167
- package/README.md +435 -418
- package/README.ru.md +169 -169
- package/README.tr.md +298 -298
- package/README.zh-CN.md +167 -167
- package/SECURITY.md +46 -46
- package/dist/api-automation.html +110 -110
- package/dist/api-docs.html +548 -548
- package/dist/assets/{index-Bm6uAqKU.js → index-Bcqs_1vc.js} +114 -114
- package/dist/clear-cache.html +85 -85
- package/dist/convert-icons.md +52 -52
- package/dist/docs.html +308 -308
- package/dist/features.html +133 -133
- package/dist/generate-icons.js +48 -48
- package/dist/humans.txt +15 -15
- package/dist/icons/codex-white.svg +3 -3
- package/dist/icons/codex.svg +3 -3
- package/dist/icons/cursor-white.svg +11 -11
- package/dist/icons/qwen-logo.svg +14 -14
- package/dist/index.html +58 -58
- package/dist/landing.html +268 -268
- package/dist/llms-full.txt +119 -119
- package/dist/llms.txt +53 -53
- package/dist/manifest.json +60 -60
- package/dist/openapi.yaml +1696 -1696
- package/dist/orchestration.html +125 -125
- package/dist/robots.txt +4 -4
- package/dist/site.css +692 -692
- package/dist/sitemap.xml +51 -51
- package/dist/sw.js +132 -132
- package/dist-server/server/cli.js +100 -100
- package/dist-server/server/daemon/manager.js +33 -33
- package/dist-server/server/daemon-manager.js +64 -64
- package/dist-server/server/index.js +13 -0
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/routes/agent.js +10 -1
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/commands.js +25 -25
- package/dist-server/server/routes/git.js +17 -17
- package/dist-server/server/routes/live-view.js +46 -46
- package/dist-server/server/services/public-api-manifest.js +51 -51
- package/dist-server/server/sessionManager.js +5 -5
- package/dist-server/server/sessionManager.js.map +1 -1
- package/package.json +224 -224
- package/scripts/fix-node-pty.js +67 -67
- package/scripts/github/create-v1.38-issues.mjs +351 -351
- package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
- package/scripts/smoke/backend-resource-bounds.mjs +152 -152
- package/scripts/smoke/changes-panel-layout.mjs +48 -48
- package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
- package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
- package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
- package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
- package/scripts/smoke/chat-session-state.mjs +19 -19
- package/scripts/smoke/code-editor-theme.mjs +55 -55
- package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
- package/scripts/smoke/command-center-agent-writes.mjs +79 -79
- package/scripts/smoke/command-center-non-git.mjs +46 -46
- package/scripts/smoke/context-packet.mjs +43 -43
- package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
- package/scripts/smoke/daemon-entrypoint.mjs +20 -20
- package/scripts/smoke/default-landing-routing.mjs +33 -33
- package/scripts/smoke/desktop-native-notifications.mjs +30 -30
- package/scripts/smoke/desktop-tray-icon.mjs +33 -33
- package/scripts/smoke/discord-release-workflow.mjs +24 -24
- package/scripts/smoke/git-install-update.mjs +255 -255
- package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
- package/scripts/smoke/live-view-diagnostics.mjs +53 -53
- package/scripts/smoke/live-view-environment.mjs +92 -92
- package/scripts/smoke/live-view-integration.mjs +450 -450
- package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
- package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
- package/scripts/smoke/mobile-ux.mjs +76 -76
- package/scripts/smoke/model-registry.mjs +36 -36
- package/scripts/smoke/multi-project-ui.mjs +45 -45
- package/scripts/smoke/multi-worker-slots.mjs +42 -42
- package/scripts/smoke/notification-center.mjs +87 -87
- package/scripts/smoke/notification-inapp-preference.mjs +23 -23
- package/scripts/smoke/notification-taxonomy.mjs +58 -58
- package/scripts/smoke/orchestration-api.mjs +172 -172
- package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
- package/scripts/smoke/orchestration-live-run.mjs +176 -176
- package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
- package/scripts/smoke/orchestration-model-sync.mjs +30 -30
- package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
- package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
- package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
- package/scripts/smoke/permission-policy.mjs +50 -50
- package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -167
- package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
- package/scripts/smoke/provider-rest-api.mjs +124 -124
- package/scripts/smoke/provider-selection-status.mjs +52 -52
- package/scripts/smoke/run-state-refresh.mjs +52 -52
- package/scripts/smoke/runtime-manager.mjs +99 -99
- package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
- package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
- package/scripts/smoke/static-root-routing.mjs +21 -21
- package/scripts/smoke/strict-handoff-compact.mjs +60 -60
- package/scripts/smoke/taskmaster-config.mjs +24 -24
- package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
- package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
- package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
- package/scripts/smoke/telegram-control.mjs +331 -331
- package/scripts/smoke/tunnel-persistence.mjs +56 -56
- package/scripts/smoke/update-issue-progress.mjs +69 -69
- package/scripts/smoke/update-ux.mjs +55 -55
- package/scripts/smoke/v138-completion.mjs +132 -132
- package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
- package/scripts/smoke/v138-diagnostics.mjs +63 -63
- package/scripts/smoke/v138-issue-planner.mjs +33 -33
- package/scripts/smoke/v143-remote-control.mjs +76 -76
- package/scripts/smoke/v144-production-loop.mjs +47 -47
- package/scripts/smoke/v145-platformization.mjs +46 -46
- package/scripts/smoke/v146-control-room-ui.mjs +150 -150
- package/scripts/smoke/version-modal-autoshow.mjs +29 -29
- package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
- package/scripts/smoke/vscode-workbench-polish.mjs +461 -461
- package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
- package/scripts/smoke/workflow-templates.mjs +43 -43
- package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
- package/scripts/update-git-install.mjs +298 -298
- package/server/claude-sdk.js +927 -927
- package/server/cli.js +1106 -1106
- package/server/constants/config.js +4 -4
- package/server/cursor-cli.js +344 -344
- package/server/daemon/manager.js +563 -563
- package/server/daemon-manager.js +964 -964
- package/server/database/db.js +988 -988
- package/server/database/json-store.js +197 -197
- package/server/gemini-cli.js +550 -550
- package/server/gemini-response-handler.js +79 -79
- package/server/index.js +5671 -5659
- package/server/load-env.js +35 -35
- package/server/middleware/account-lockout.js +112 -112
- package/server/middleware/auth.js +277 -277
- package/server/middleware/rate-limiter.js +87 -87
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.test.ts +60 -60
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +101 -101
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/agent-card.ts +55 -55
- package/server/modules/orchestration/a2a/auth.middleware.ts +29 -29
- package/server/modules/orchestration/a2a/bus.ts +46 -46
- package/server/modules/orchestration/a2a/routes.ts +586 -586
- package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -345
- package/server/modules/orchestration/a2a/task-store.ts +178 -178
- package/server/modules/orchestration/a2a/types.ts +126 -126
- package/server/modules/orchestration/a2a/validator.ts +113 -113
- package/server/modules/orchestration/index.ts +99 -99
- package/server/modules/orchestration/preview/port-watcher.ts +112 -112
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
- package/server/modules/orchestration/preview/types.ts +19 -19
- package/server/modules/orchestration/security/permission-policy.ts +401 -401
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +81 -81
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +201 -201
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
- package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
- package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
- package/server/modules/orchestration/workflows/context-packet.ts +186 -186
- package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
- package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
- package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
- package/server/modules/orchestration/workflows/workflow-runner.ts +2062 -2062
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
- package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
- package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
- package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
- package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
- package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
- package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
- package/server/modules/orchestration/workspace/path-safety.ts +55 -55
- package/server/modules/orchestration/workspace/types.ts +52 -52
- package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
- package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
- package/server/modules/providers/index.ts +2 -2
- package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
- package/server/modules/providers/list/claude/claude.provider.ts +15 -15
- package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
- package/server/modules/providers/list/codex/codex.provider.ts +15 -15
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
- package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
- package/server/modules/providers/provider.registry.ts +40 -40
- package/server/modules/providers/provider.routes.ts +982 -982
- package/server/modules/providers/services/mcp.service.ts +86 -86
- package/server/modules/providers/services/provider-auth.service.ts +26 -26
- package/server/modules/providers/services/sessions.service.ts +45 -45
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
- package/server/modules/providers/tests/mcp.test.ts +293 -293
- package/server/openai-codex.js +468 -468
- package/server/opencode-cli.js +491 -491
- package/server/opencode-response-handler.js +111 -111
- package/server/projects.js +3135 -3135
- package/server/qwen-code-cli.js +410 -410
- package/server/routes/agent.js +1462 -1451
- package/server/routes/auth.js +298 -298
- package/server/routes/codex.js +20 -20
- package/server/routes/commands.js +581 -581
- package/server/routes/cursor.js +61 -61
- package/server/routes/diagnostics.js +44 -44
- package/server/routes/gemini.js +25 -25
- package/server/routes/git.js +1822 -1822
- package/server/routes/live-view.js +434 -434
- package/server/routes/mcp-utils.js +13 -13
- package/server/routes/messages.js +97 -97
- package/server/routes/network.js +143 -143
- package/server/routes/platformization.js +231 -231
- package/server/routes/plugins.js +690 -690
- package/server/routes/production-agent-loop.js +90 -90
- package/server/routes/projects.js +918 -918
- package/server/routes/public-api.js +34 -34
- package/server/routes/qwen.js +27 -27
- package/server/routes/remote.js +56 -56
- package/server/routes/settings.js +321 -321
- package/server/routes/telegram.js +163 -163
- package/server/routes/user.js +125 -125
- package/server/routes/webhooks.js +63 -63
- package/server/services/control-room.js +102 -102
- package/server/services/diagnostics.js +165 -165
- package/server/services/external-access.js +403 -403
- package/server/services/install-jobs.js +715 -715
- package/server/services/live-view.js +956 -956
- package/server/services/managed-runtimes.js +493 -493
- package/server/services/model-registry.js +144 -144
- package/server/services/notification-orchestrator.js +365 -365
- package/server/services/notification-taxonomy.js +204 -204
- package/server/services/platformization.js +1052 -1052
- package/server/services/production-agent-loop.js +248 -248
- package/server/services/provider-cli-versions.js +149 -149
- package/server/services/provider-credentials.js +189 -189
- package/server/services/provider-models.js +396 -396
- package/server/services/public-api-manifest.js +181 -181
- package/server/services/qr-login.js +126 -126
- package/server/services/remote-connection.js +127 -127
- package/server/services/runtime-manager.js +323 -323
- package/server/services/startup-update.js +259 -259
- package/server/services/telegram/bot.js +393 -393
- package/server/services/telegram/control-center.js +2453 -2453
- package/server/services/telegram/telegram-gateway.js +314 -314
- package/server/services/telegram/telegram-http-client.js +201 -201
- package/server/services/telegram/translations.js +470 -470
- package/server/services/webhooks.js +216 -216
- package/server/sessionManager.js +225 -225
- package/server/setup-wizard.js +283 -283
- package/server/shared/interfaces.ts +54 -54
- package/server/shared/types.ts +172 -172
- package/server/shared/utils.ts +193 -193
- package/server/tsconfig.json +36 -36
- package/server/utils/colors.js +21 -21
- package/server/utils/commandParser.js +305 -305
- package/server/utils/frontmatter.js +18 -18
- package/server/utils/gitConfig.js +34 -34
- package/server/utils/plugin-loader.js +461 -461
- package/server/utils/plugin-process-manager.js +185 -185
- package/server/utils/port-access.js +227 -227
- package/server/utils/runtime-paths.js +37 -37
- package/server/utils/security-log.js +84 -84
- package/server/utils/url-detection.js +71 -71
- package/server/vite-daemon.js +79 -79
- package/shared/modelConstants.js +161 -161
- package/shared/networkHosts.js +22 -22
package/server/routes/auth.js
CHANGED
|
@@ -1,298 +1,298 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import bcrypt from 'bcryptjs';
|
|
3
|
-
|
|
4
|
-
import { userDb, db } from '../database/db.js';
|
|
5
|
-
import { generateToken, authenticateToken, requireAdmin } from '../middleware/auth.js';
|
|
6
|
-
import { authRateLimiter } from '../middleware/rate-limiter.js';
|
|
7
|
-
import {
|
|
8
|
-
checkAccountLockout,
|
|
9
|
-
recordFailedLogin,
|
|
10
|
-
recordSuccessfulLogin,
|
|
11
|
-
validatePasswordPolicy,
|
|
12
|
-
validateUsername,
|
|
13
|
-
} from '../middleware/account-lockout.js';
|
|
14
|
-
import { securityLog, getClientIp } from '../utils/security-log.js';
|
|
15
|
-
import {
|
|
16
|
-
consumeQrLoginToken,
|
|
17
|
-
createQrLoginToken,
|
|
18
|
-
getQrLoginSettings,
|
|
19
|
-
saveQrLoginSettings,
|
|
20
|
-
} from '../services/qr-login.js';
|
|
21
|
-
import {
|
|
22
|
-
getPublicRemoteConnectionConfig,
|
|
23
|
-
saveRemoteConnectionConfig,
|
|
24
|
-
} from '../services/remote-connection.js';
|
|
25
|
-
|
|
26
|
-
const router = express.Router();
|
|
27
|
-
|
|
28
|
-
function publicUser(user) {
|
|
29
|
-
return {
|
|
30
|
-
id: user.id,
|
|
31
|
-
username: user.username,
|
|
32
|
-
role: user.role || 'member',
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Check auth status and setup requirements
|
|
37
|
-
router.get('/status', async (req, res) => {
|
|
38
|
-
try {
|
|
39
|
-
const hasUsers = await userDb.hasUsers();
|
|
40
|
-
res.json({
|
|
41
|
-
needsSetup: !hasUsers,
|
|
42
|
-
isAuthenticated: false // Will be overridden by frontend if token exists
|
|
43
|
-
});
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.error('Auth status error:', error);
|
|
46
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// First-run connection mode is intentionally public: it is needed before
|
|
51
|
-
// account creation so a fresh desktop install can decide whether it controls
|
|
52
|
-
// this machine or a remote Pixcode server.
|
|
53
|
-
router.get('/connection-mode', (req, res) => {
|
|
54
|
-
res.json({ success: true, connection: getPublicRemoteConnectionConfig() });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Connection mode can be set during initial setup (no users exist yet) without
|
|
58
|
-
// authentication. After setup, only authenticated admins may change it — an
|
|
59
|
-
// unauthenticated caller could otherwise redirect the app to a malicious
|
|
60
|
-
// remote server.
|
|
61
|
-
router.put('/connection-mode', async (req, res) => {
|
|
62
|
-
try {
|
|
63
|
-
// During first-run setup no users exist yet, so we skip auth.
|
|
64
|
-
// The setup form calls this endpoint before creating the admin account.
|
|
65
|
-
const hasUsers = await userDb.hasUsers();
|
|
66
|
-
if (!hasUsers) {
|
|
67
|
-
return res.json({ success: true, connection: saveRemoteConnectionConfig(req.body || {}) });
|
|
68
|
-
}
|
|
69
|
-
// Post-setup: require authenticated admin
|
|
70
|
-
authenticateToken(req, res, () => {
|
|
71
|
-
requireAdmin(req, res, () => {
|
|
72
|
-
res.json({ success: true, connection: saveRemoteConnectionConfig(req.body || {}) });
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
} catch (error) {
|
|
76
|
-
res.status(400).json({ success: false, error: error.message });
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
router.get('/qr-login/settings', authenticateToken, requireAdmin, (_req, res) => {
|
|
81
|
-
res.json({ success: true, settings: getQrLoginSettings() });
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
router.put('/qr-login/settings', authenticateToken, requireAdmin, (req, res) => {
|
|
85
|
-
try {
|
|
86
|
-
res.json({ success: true, settings: saveQrLoginSettings(req.body || {}) });
|
|
87
|
-
} catch (error) {
|
|
88
|
-
res.status(400).json({ success: false, error: error.message });
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
router.post('/qr-login/token', authenticateToken, requireAdmin, authRateLimiter, (req, res) => {
|
|
93
|
-
try {
|
|
94
|
-
const baseUrl = typeof req.body?.baseUrl === 'string' && req.body.baseUrl.trim()
|
|
95
|
-
? req.body.baseUrl.trim()
|
|
96
|
-
: null;
|
|
97
|
-
res.json({ success: true, ...createQrLoginToken({ userId: req.user.id, baseUrl }) });
|
|
98
|
-
} catch (error) {
|
|
99
|
-
const status = error?.code === 'QR_LOGIN_DISABLED' ? 409 : 400;
|
|
100
|
-
res.status(status).json({ success: false, error: error.message });
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
router.post('/qr-login', authRateLimiter, (req, res) => {
|
|
105
|
-
try {
|
|
106
|
-
const user = consumeQrLoginToken(req.body?.token);
|
|
107
|
-
const token = generateToken(user);
|
|
108
|
-
userDb.updateLastLogin(user.id);
|
|
109
|
-
securityLog('qr_login_success', {
|
|
110
|
-
ip: getClientIp(req),
|
|
111
|
-
userId: user.id,
|
|
112
|
-
username: user.username,
|
|
113
|
-
});
|
|
114
|
-
res.json({
|
|
115
|
-
success: true,
|
|
116
|
-
user: publicUser(user),
|
|
117
|
-
token,
|
|
118
|
-
});
|
|
119
|
-
} catch (error) {
|
|
120
|
-
const status = error?.code === 'QR_LOGIN_DISABLED' ? 409 : 401;
|
|
121
|
-
securityLog('qr_login_failed', {
|
|
122
|
-
ip: getClientIp(req),
|
|
123
|
-
reason: error?.code || 'unknown',
|
|
124
|
-
});
|
|
125
|
-
res.status(status).json({ success: false, error: error.message });
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// User registration (setup) - only allowed if no users exist
|
|
130
|
-
router.post('/register', authRateLimiter, async (req, res) => {
|
|
131
|
-
try {
|
|
132
|
-
const { username, password } = req.body;
|
|
133
|
-
const clientIp = getClientIp(req);
|
|
134
|
-
|
|
135
|
-
// Validate input
|
|
136
|
-
if (!username || !password) {
|
|
137
|
-
return res.status(400).json({ error: 'Username and password are required' });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const usernameValidation = validateUsername(username);
|
|
141
|
-
if (!usernameValidation.valid) {
|
|
142
|
-
return res.status(400).json({ error: usernameValidation.error });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const passwordValidation = validatePasswordPolicy(password);
|
|
146
|
-
if (!passwordValidation.valid) {
|
|
147
|
-
return res.status(400).json({ error: passwordValidation.error });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Use a transaction to prevent race conditions
|
|
151
|
-
db.prepare('BEGIN').run();
|
|
152
|
-
try {
|
|
153
|
-
// Check if users already exist. Additional accounts are created by admins.
|
|
154
|
-
const hasUsers = userDb.hasUsers();
|
|
155
|
-
if (hasUsers) {
|
|
156
|
-
db.prepare('ROLLBACK').run();
|
|
157
|
-
return res.status(403).json({ error: 'Initial admin already exists. Ask an admin to create another account.' });
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Hash password
|
|
161
|
-
const saltRounds = 12;
|
|
162
|
-
const passwordHash = await bcrypt.hash(password, saltRounds);
|
|
163
|
-
|
|
164
|
-
// Create user
|
|
165
|
-
const user = userDb.createUser(username, passwordHash, { role: 'admin' });
|
|
166
|
-
|
|
167
|
-
// Generate token
|
|
168
|
-
const token = generateToken(user);
|
|
169
|
-
|
|
170
|
-
db.prepare('COMMIT').run();
|
|
171
|
-
|
|
172
|
-
// Update last login (non-fatal, outside transaction)
|
|
173
|
-
userDb.updateLastLogin(user.id);
|
|
174
|
-
|
|
175
|
-
securityLog('user_registered', {
|
|
176
|
-
ip: clientIp,
|
|
177
|
-
userId: user.id,
|
|
178
|
-
username: user.username,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
res.json({
|
|
182
|
-
success: true,
|
|
183
|
-
user: publicUser(user),
|
|
184
|
-
token
|
|
185
|
-
});
|
|
186
|
-
} catch (error) {
|
|
187
|
-
db.prepare('ROLLBACK').run();
|
|
188
|
-
throw error;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error('Registration error:', error);
|
|
193
|
-
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
|
194
|
-
res.status(409).json({ error: 'Username already exists' });
|
|
195
|
-
} else {
|
|
196
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// User login
|
|
202
|
-
router.post('/login', authRateLimiter, async (req, res) => {
|
|
203
|
-
try {
|
|
204
|
-
const { username, password } = req.body;
|
|
205
|
-
const clientIp = getClientIp(req);
|
|
206
|
-
|
|
207
|
-
// Validate input
|
|
208
|
-
if (!username || !password) {
|
|
209
|
-
return res.status(400).json({ error: 'Username and password are required' });
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Check account lockout before attempting login
|
|
213
|
-
const lockoutStatus = checkAccountLockout(String(username), clientIp);
|
|
214
|
-
if (lockoutStatus.locked) {
|
|
215
|
-
securityLog('login_blocked_locked', {
|
|
216
|
-
ip: clientIp,
|
|
217
|
-
username,
|
|
218
|
-
reason: 'Account locked due to failed attempts',
|
|
219
|
-
});
|
|
220
|
-
return res.status(423).json({ error: lockoutStatus.message });
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Get user from database
|
|
224
|
-
const user = userDb.getUserByUsername(username);
|
|
225
|
-
if (!user) {
|
|
226
|
-
const failResult = recordFailedLogin(String(username), clientIp);
|
|
227
|
-
securityLog('login_failed', {
|
|
228
|
-
ip: clientIp,
|
|
229
|
-
username,
|
|
230
|
-
reason: 'User not found',
|
|
231
|
-
});
|
|
232
|
-
if (failResult.locked) {
|
|
233
|
-
return res.status(423).json({ error: failResult.message });
|
|
234
|
-
}
|
|
235
|
-
return res.status(401).json({ error: 'Invalid username or password' });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Verify password
|
|
239
|
-
const isValidPassword = await bcrypt.compare(password, user.password_hash);
|
|
240
|
-
if (!isValidPassword) {
|
|
241
|
-
const failResult = recordFailedLogin(String(username), clientIp);
|
|
242
|
-
securityLog('login_failed', {
|
|
243
|
-
ip: clientIp,
|
|
244
|
-
username,
|
|
245
|
-
userId: user.id,
|
|
246
|
-
reason: 'Invalid password',
|
|
247
|
-
});
|
|
248
|
-
if (failResult.locked) {
|
|
249
|
-
return res.status(423).json({ error: failResult.message });
|
|
250
|
-
}
|
|
251
|
-
return res.status(401).json({ error: 'Invalid username or password' });
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Clear failed attempts on successful login
|
|
255
|
-
recordSuccessfulLogin(String(username), clientIp);
|
|
256
|
-
|
|
257
|
-
// Generate token
|
|
258
|
-
const token = generateToken(user);
|
|
259
|
-
|
|
260
|
-
// Update last login
|
|
261
|
-
userDb.updateLastLogin(user.id);
|
|
262
|
-
|
|
263
|
-
securityLog('login_success', {
|
|
264
|
-
ip: clientIp,
|
|
265
|
-
userId: user.id,
|
|
266
|
-
username: user.username,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
res.json({
|
|
270
|
-
success: true,
|
|
271
|
-
user: publicUser(user),
|
|
272
|
-
token
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
} catch (error) {
|
|
276
|
-
console.error('Login error:', error);
|
|
277
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// Get current user (protected route)
|
|
282
|
-
router.get('/user', authenticateToken, (req, res) => {
|
|
283
|
-
res.json({
|
|
284
|
-
user: req.user
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Logout (client-side token removal, but this endpoint can be used for logging)
|
|
289
|
-
router.post('/logout', authenticateToken, (req, res) => {
|
|
290
|
-
securityLog('user_logout', {
|
|
291
|
-
ip: getClientIp(req),
|
|
292
|
-
userId: req.user?.id,
|
|
293
|
-
username: req.user?.username,
|
|
294
|
-
});
|
|
295
|
-
res.json({ success: true, message: 'Logged out successfully' });
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
export default router;
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import bcrypt from 'bcryptjs';
|
|
3
|
+
|
|
4
|
+
import { userDb, db } from '../database/db.js';
|
|
5
|
+
import { generateToken, authenticateToken, requireAdmin } from '../middleware/auth.js';
|
|
6
|
+
import { authRateLimiter } from '../middleware/rate-limiter.js';
|
|
7
|
+
import {
|
|
8
|
+
checkAccountLockout,
|
|
9
|
+
recordFailedLogin,
|
|
10
|
+
recordSuccessfulLogin,
|
|
11
|
+
validatePasswordPolicy,
|
|
12
|
+
validateUsername,
|
|
13
|
+
} from '../middleware/account-lockout.js';
|
|
14
|
+
import { securityLog, getClientIp } from '../utils/security-log.js';
|
|
15
|
+
import {
|
|
16
|
+
consumeQrLoginToken,
|
|
17
|
+
createQrLoginToken,
|
|
18
|
+
getQrLoginSettings,
|
|
19
|
+
saveQrLoginSettings,
|
|
20
|
+
} from '../services/qr-login.js';
|
|
21
|
+
import {
|
|
22
|
+
getPublicRemoteConnectionConfig,
|
|
23
|
+
saveRemoteConnectionConfig,
|
|
24
|
+
} from '../services/remote-connection.js';
|
|
25
|
+
|
|
26
|
+
const router = express.Router();
|
|
27
|
+
|
|
28
|
+
function publicUser(user) {
|
|
29
|
+
return {
|
|
30
|
+
id: user.id,
|
|
31
|
+
username: user.username,
|
|
32
|
+
role: user.role || 'member',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check auth status and setup requirements
|
|
37
|
+
router.get('/status', async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const hasUsers = await userDb.hasUsers();
|
|
40
|
+
res.json({
|
|
41
|
+
needsSetup: !hasUsers,
|
|
42
|
+
isAuthenticated: false // Will be overridden by frontend if token exists
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Auth status error:', error);
|
|
46
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// First-run connection mode is intentionally public: it is needed before
|
|
51
|
+
// account creation so a fresh desktop install can decide whether it controls
|
|
52
|
+
// this machine or a remote Pixcode server.
|
|
53
|
+
router.get('/connection-mode', (req, res) => {
|
|
54
|
+
res.json({ success: true, connection: getPublicRemoteConnectionConfig() });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Connection mode can be set during initial setup (no users exist yet) without
|
|
58
|
+
// authentication. After setup, only authenticated admins may change it — an
|
|
59
|
+
// unauthenticated caller could otherwise redirect the app to a malicious
|
|
60
|
+
// remote server.
|
|
61
|
+
router.put('/connection-mode', async (req, res) => {
|
|
62
|
+
try {
|
|
63
|
+
// During first-run setup no users exist yet, so we skip auth.
|
|
64
|
+
// The setup form calls this endpoint before creating the admin account.
|
|
65
|
+
const hasUsers = await userDb.hasUsers();
|
|
66
|
+
if (!hasUsers) {
|
|
67
|
+
return res.json({ success: true, connection: saveRemoteConnectionConfig(req.body || {}) });
|
|
68
|
+
}
|
|
69
|
+
// Post-setup: require authenticated admin
|
|
70
|
+
authenticateToken(req, res, () => {
|
|
71
|
+
requireAdmin(req, res, () => {
|
|
72
|
+
res.json({ success: true, connection: saveRemoteConnectionConfig(req.body || {}) });
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
res.status(400).json({ success: false, error: error.message });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
router.get('/qr-login/settings', authenticateToken, requireAdmin, (_req, res) => {
|
|
81
|
+
res.json({ success: true, settings: getQrLoginSettings() });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
router.put('/qr-login/settings', authenticateToken, requireAdmin, (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
res.json({ success: true, settings: saveQrLoginSettings(req.body || {}) });
|
|
87
|
+
} catch (error) {
|
|
88
|
+
res.status(400).json({ success: false, error: error.message });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
router.post('/qr-login/token', authenticateToken, requireAdmin, authRateLimiter, (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const baseUrl = typeof req.body?.baseUrl === 'string' && req.body.baseUrl.trim()
|
|
95
|
+
? req.body.baseUrl.trim()
|
|
96
|
+
: null;
|
|
97
|
+
res.json({ success: true, ...createQrLoginToken({ userId: req.user.id, baseUrl }) });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const status = error?.code === 'QR_LOGIN_DISABLED' ? 409 : 400;
|
|
100
|
+
res.status(status).json({ success: false, error: error.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
router.post('/qr-login', authRateLimiter, (req, res) => {
|
|
105
|
+
try {
|
|
106
|
+
const user = consumeQrLoginToken(req.body?.token);
|
|
107
|
+
const token = generateToken(user);
|
|
108
|
+
userDb.updateLastLogin(user.id);
|
|
109
|
+
securityLog('qr_login_success', {
|
|
110
|
+
ip: getClientIp(req),
|
|
111
|
+
userId: user.id,
|
|
112
|
+
username: user.username,
|
|
113
|
+
});
|
|
114
|
+
res.json({
|
|
115
|
+
success: true,
|
|
116
|
+
user: publicUser(user),
|
|
117
|
+
token,
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const status = error?.code === 'QR_LOGIN_DISABLED' ? 409 : 401;
|
|
121
|
+
securityLog('qr_login_failed', {
|
|
122
|
+
ip: getClientIp(req),
|
|
123
|
+
reason: error?.code || 'unknown',
|
|
124
|
+
});
|
|
125
|
+
res.status(status).json({ success: false, error: error.message });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// User registration (setup) - only allowed if no users exist
|
|
130
|
+
router.post('/register', authRateLimiter, async (req, res) => {
|
|
131
|
+
try {
|
|
132
|
+
const { username, password } = req.body;
|
|
133
|
+
const clientIp = getClientIp(req);
|
|
134
|
+
|
|
135
|
+
// Validate input
|
|
136
|
+
if (!username || !password) {
|
|
137
|
+
return res.status(400).json({ error: 'Username and password are required' });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const usernameValidation = validateUsername(username);
|
|
141
|
+
if (!usernameValidation.valid) {
|
|
142
|
+
return res.status(400).json({ error: usernameValidation.error });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const passwordValidation = validatePasswordPolicy(password);
|
|
146
|
+
if (!passwordValidation.valid) {
|
|
147
|
+
return res.status(400).json({ error: passwordValidation.error });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Use a transaction to prevent race conditions
|
|
151
|
+
db.prepare('BEGIN').run();
|
|
152
|
+
try {
|
|
153
|
+
// Check if users already exist. Additional accounts are created by admins.
|
|
154
|
+
const hasUsers = userDb.hasUsers();
|
|
155
|
+
if (hasUsers) {
|
|
156
|
+
db.prepare('ROLLBACK').run();
|
|
157
|
+
return res.status(403).json({ error: 'Initial admin already exists. Ask an admin to create another account.' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Hash password
|
|
161
|
+
const saltRounds = 12;
|
|
162
|
+
const passwordHash = await bcrypt.hash(password, saltRounds);
|
|
163
|
+
|
|
164
|
+
// Create user
|
|
165
|
+
const user = userDb.createUser(username, passwordHash, { role: 'admin' });
|
|
166
|
+
|
|
167
|
+
// Generate token
|
|
168
|
+
const token = generateToken(user);
|
|
169
|
+
|
|
170
|
+
db.prepare('COMMIT').run();
|
|
171
|
+
|
|
172
|
+
// Update last login (non-fatal, outside transaction)
|
|
173
|
+
userDb.updateLastLogin(user.id);
|
|
174
|
+
|
|
175
|
+
securityLog('user_registered', {
|
|
176
|
+
ip: clientIp,
|
|
177
|
+
userId: user.id,
|
|
178
|
+
username: user.username,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
res.json({
|
|
182
|
+
success: true,
|
|
183
|
+
user: publicUser(user),
|
|
184
|
+
token
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
db.prepare('ROLLBACK').run();
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error('Registration error:', error);
|
|
193
|
+
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
|
194
|
+
res.status(409).json({ error: 'Username already exists' });
|
|
195
|
+
} else {
|
|
196
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// User login
|
|
202
|
+
router.post('/login', authRateLimiter, async (req, res) => {
|
|
203
|
+
try {
|
|
204
|
+
const { username, password } = req.body;
|
|
205
|
+
const clientIp = getClientIp(req);
|
|
206
|
+
|
|
207
|
+
// Validate input
|
|
208
|
+
if (!username || !password) {
|
|
209
|
+
return res.status(400).json({ error: 'Username and password are required' });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check account lockout before attempting login
|
|
213
|
+
const lockoutStatus = checkAccountLockout(String(username), clientIp);
|
|
214
|
+
if (lockoutStatus.locked) {
|
|
215
|
+
securityLog('login_blocked_locked', {
|
|
216
|
+
ip: clientIp,
|
|
217
|
+
username,
|
|
218
|
+
reason: 'Account locked due to failed attempts',
|
|
219
|
+
});
|
|
220
|
+
return res.status(423).json({ error: lockoutStatus.message });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Get user from database
|
|
224
|
+
const user = userDb.getUserByUsername(username);
|
|
225
|
+
if (!user) {
|
|
226
|
+
const failResult = recordFailedLogin(String(username), clientIp);
|
|
227
|
+
securityLog('login_failed', {
|
|
228
|
+
ip: clientIp,
|
|
229
|
+
username,
|
|
230
|
+
reason: 'User not found',
|
|
231
|
+
});
|
|
232
|
+
if (failResult.locked) {
|
|
233
|
+
return res.status(423).json({ error: failResult.message });
|
|
234
|
+
}
|
|
235
|
+
return res.status(401).json({ error: 'Invalid username or password' });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Verify password
|
|
239
|
+
const isValidPassword = await bcrypt.compare(password, user.password_hash);
|
|
240
|
+
if (!isValidPassword) {
|
|
241
|
+
const failResult = recordFailedLogin(String(username), clientIp);
|
|
242
|
+
securityLog('login_failed', {
|
|
243
|
+
ip: clientIp,
|
|
244
|
+
username,
|
|
245
|
+
userId: user.id,
|
|
246
|
+
reason: 'Invalid password',
|
|
247
|
+
});
|
|
248
|
+
if (failResult.locked) {
|
|
249
|
+
return res.status(423).json({ error: failResult.message });
|
|
250
|
+
}
|
|
251
|
+
return res.status(401).json({ error: 'Invalid username or password' });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Clear failed attempts on successful login
|
|
255
|
+
recordSuccessfulLogin(String(username), clientIp);
|
|
256
|
+
|
|
257
|
+
// Generate token
|
|
258
|
+
const token = generateToken(user);
|
|
259
|
+
|
|
260
|
+
// Update last login
|
|
261
|
+
userDb.updateLastLogin(user.id);
|
|
262
|
+
|
|
263
|
+
securityLog('login_success', {
|
|
264
|
+
ip: clientIp,
|
|
265
|
+
userId: user.id,
|
|
266
|
+
username: user.username,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
res.json({
|
|
270
|
+
success: true,
|
|
271
|
+
user: publicUser(user),
|
|
272
|
+
token
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Login error:', error);
|
|
277
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Get current user (protected route)
|
|
282
|
+
router.get('/user', authenticateToken, (req, res) => {
|
|
283
|
+
res.json({
|
|
284
|
+
user: req.user
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Logout (client-side token removal, but this endpoint can be used for logging)
|
|
289
|
+
router.post('/logout', authenticateToken, (req, res) => {
|
|
290
|
+
securityLog('user_logout', {
|
|
291
|
+
ip: getClientIp(req),
|
|
292
|
+
userId: req.user?.id,
|
|
293
|
+
username: req.user?.username,
|
|
294
|
+
});
|
|
295
|
+
res.json({ success: true, message: 'Logged out successfully' });
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
export default router;
|
package/server/routes/codex.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
|
|
3
|
-
import { deleteCodexSession } from '../projects.js';
|
|
4
|
-
import { sessionNamesDb } from '../database/db.js';
|
|
5
|
-
|
|
6
|
-
const router = express.Router();
|
|
7
|
-
|
|
8
|
-
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
9
|
-
try {
|
|
10
|
-
const { sessionId } = req.params;
|
|
11
|
-
await deleteCodexSession(sessionId);
|
|
12
|
-
sessionNamesDb.deleteName(sessionId, 'codex');
|
|
13
|
-
res.json({ success: true });
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error(`Error deleting Codex session ${req.params.sessionId}:`, error);
|
|
16
|
-
res.status(500).json({ success: false, error: "Internal server error" });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export default router;
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
import { deleteCodexSession } from '../projects.js';
|
|
4
|
+
import { sessionNamesDb } from '../database/db.js';
|
|
5
|
+
|
|
6
|
+
const router = express.Router();
|
|
7
|
+
|
|
8
|
+
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const { sessionId } = req.params;
|
|
11
|
+
await deleteCodexSession(sessionId);
|
|
12
|
+
sessionNamesDb.deleteName(sessionId, 'codex');
|
|
13
|
+
res.json({ success: true });
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error(`Error deleting Codex session ${req.params.sessionId}:`, error);
|
|
16
|
+
res.status(500).json({ success: false, error: "Internal server error" });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default router;
|