@pixelbyte-software/pixcode 1.51.8 → 1.51.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/CODE_OF_CONDUCT.md +41 -41
  2. package/CONTRIBUTING.md +155 -155
  3. package/LICENSE +718 -718
  4. package/README.de.md +169 -169
  5. package/README.ja.md +167 -167
  6. package/README.ko.md +167 -167
  7. package/README.md +419 -419
  8. package/README.ru.md +169 -169
  9. package/README.tr.md +298 -298
  10. package/README.zh-CN.md +167 -167
  11. package/SECURITY.md +46 -46
  12. package/dist/api-automation.html +110 -110
  13. package/dist/api-docs.html +548 -548
  14. package/dist/assets/{index--kNEhygZ.js → index-Gs7C4hZT.js} +69 -69
  15. package/dist/clear-cache.html +85 -85
  16. package/dist/convert-icons.md +52 -52
  17. package/dist/docs.html +308 -308
  18. package/dist/favicon.svg +8 -8
  19. package/dist/features.html +133 -133
  20. package/dist/generate-icons.js +48 -48
  21. package/dist/humans.txt +15 -15
  22. package/dist/icons/codex-white.svg +3 -3
  23. package/dist/icons/codex.svg +3 -3
  24. package/dist/icons/cursor-white.svg +11 -11
  25. package/dist/icons/icon-128x128.svg +9 -9
  26. package/dist/icons/icon-144x144.svg +9 -9
  27. package/dist/icons/icon-152x152.svg +9 -9
  28. package/dist/icons/icon-192x192.svg +9 -9
  29. package/dist/icons/icon-384x384.svg +9 -9
  30. package/dist/icons/icon-512x512.svg +9 -9
  31. package/dist/icons/icon-72x72.svg +9 -9
  32. package/dist/icons/icon-96x96.svg +9 -9
  33. package/dist/icons/icon-template.svg +9 -9
  34. package/dist/icons/qwen-logo.svg +14 -14
  35. package/dist/index.html +58 -58
  36. package/dist/landing.html +268 -268
  37. package/dist/llms-full.txt +119 -119
  38. package/dist/llms.txt +53 -53
  39. package/dist/logo.svg +12 -12
  40. package/dist/manifest.json +60 -60
  41. package/dist/openapi.yaml +1696 -1696
  42. package/dist/orchestration.html +125 -125
  43. package/dist/robots.txt +4 -4
  44. package/dist/site.css +692 -692
  45. package/dist/sitemap.xml +51 -51
  46. package/dist/sw.js +132 -132
  47. package/dist-server/server/cli.js +96 -96
  48. package/dist-server/server/daemon/manager.js +33 -33
  49. package/dist-server/server/daemon-manager.js +64 -64
  50. package/dist-server/server/index.js +91 -65
  51. package/dist-server/server/index.js.map +1 -1
  52. package/dist-server/server/routes/commands.js +25 -25
  53. package/dist-server/server/routes/git.js +17 -17
  54. package/dist-server/server/routes/live-view.js +46 -46
  55. package/dist-server/server/services/hermes-gateway.js +5 -5
  56. package/dist-server/server/services/public-api-manifest.js +51 -51
  57. package/package.json +222 -222
  58. package/scripts/fix-node-pty.js +67 -67
  59. package/scripts/github/create-v1.38-issues.mjs +351 -351
  60. package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
  61. package/scripts/hermes/configure-pixcode-mcp.mjs +165 -165
  62. package/scripts/hermes/pixcode-mcp-server.mjs +1009 -1009
  63. package/scripts/smoke/changes-panel-layout.mjs +48 -48
  64. package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
  65. package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
  66. package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
  67. package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
  68. package/scripts/smoke/chat-session-state.mjs +19 -19
  69. package/scripts/smoke/code-editor-theme.mjs +55 -55
  70. package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
  71. package/scripts/smoke/command-center-agent-writes.mjs +79 -79
  72. package/scripts/smoke/command-center-non-git.mjs +46 -46
  73. package/scripts/smoke/context-packet.mjs +43 -43
  74. package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
  75. package/scripts/smoke/daemon-entrypoint.mjs +20 -20
  76. package/scripts/smoke/default-landing-routing.mjs +33 -33
  77. package/scripts/smoke/desktop-native-notifications.mjs +30 -30
  78. package/scripts/smoke/desktop-tray-icon.mjs +33 -33
  79. package/scripts/smoke/discord-release-workflow.mjs +24 -24
  80. package/scripts/smoke/git-install-update.mjs +255 -255
  81. package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
  82. package/scripts/smoke/hermes-api-install.mjs +56 -56
  83. package/scripts/smoke/hermes-gateway-persistence.mjs +104 -104
  84. package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +426 -426
  85. package/scripts/smoke/hermes-rest-chat-api.mjs +162 -162
  86. package/scripts/smoke/hermes-rest-chat-live.mjs +45 -45
  87. package/scripts/smoke/hermes-rest-codex-launch.mjs +209 -209
  88. package/scripts/smoke/hermes-rest-gateway.mjs +79 -79
  89. package/scripts/smoke/hermes-rest-live.mjs +42 -42
  90. package/scripts/smoke/hermes-roundtrip.mjs +167 -167
  91. package/scripts/smoke/hermes-settings-commands.mjs +349 -349
  92. package/scripts/smoke/hermes-smoke-launcher-guard.mjs +34 -34
  93. package/scripts/smoke/live-view-diagnostics.mjs +53 -53
  94. package/scripts/smoke/live-view-environment.mjs +92 -92
  95. package/scripts/smoke/live-view-integration.mjs +450 -450
  96. package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
  97. package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
  98. package/scripts/smoke/model-registry.mjs +36 -36
  99. package/scripts/smoke/multi-project-ui.mjs +45 -45
  100. package/scripts/smoke/multi-worker-slots.mjs +42 -42
  101. package/scripts/smoke/notification-center.mjs +87 -87
  102. package/scripts/smoke/notification-inapp-preference.mjs +23 -23
  103. package/scripts/smoke/notification-taxonomy.mjs +58 -58
  104. package/scripts/smoke/orchestration-api.mjs +172 -172
  105. package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
  106. package/scripts/smoke/orchestration-live-run.mjs +176 -176
  107. package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
  108. package/scripts/smoke/orchestration-model-sync.mjs +30 -30
  109. package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
  110. package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
  111. package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
  112. package/scripts/smoke/permission-policy.mjs +50 -50
  113. package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -167
  114. package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
  115. package/scripts/smoke/provider-rest-api.mjs +124 -124
  116. package/scripts/smoke/provider-selection-status.mjs +52 -52
  117. package/scripts/smoke/run-state-refresh.mjs +52 -52
  118. package/scripts/smoke/runtime-manager.mjs +99 -99
  119. package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
  120. package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
  121. package/scripts/smoke/static-root-routing.mjs +21 -21
  122. package/scripts/smoke/strict-handoff-compact.mjs +60 -60
  123. package/scripts/smoke/taskmaster-config.mjs +24 -24
  124. package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
  125. package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
  126. package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
  127. package/scripts/smoke/telegram-control.mjs +242 -242
  128. package/scripts/smoke/tunnel-persistence.mjs +56 -56
  129. package/scripts/smoke/update-issue-progress.mjs +69 -69
  130. package/scripts/smoke/update-ux.mjs +55 -55
  131. package/scripts/smoke/v138-completion.mjs +132 -132
  132. package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
  133. package/scripts/smoke/v138-diagnostics.mjs +63 -63
  134. package/scripts/smoke/v138-issue-planner.mjs +33 -33
  135. package/scripts/smoke/v143-remote-control.mjs +76 -76
  136. package/scripts/smoke/v144-production-loop.mjs +47 -47
  137. package/scripts/smoke/v145-platformization.mjs +46 -46
  138. package/scripts/smoke/v146-control-room-ui.mjs +150 -150
  139. package/scripts/smoke/version-modal-autoshow.mjs +29 -29
  140. package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
  141. package/scripts/smoke/vscode-workbench-polish.mjs +461 -461
  142. package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
  143. package/scripts/smoke/workflow-templates.mjs +43 -43
  144. package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
  145. package/scripts/update-git-install.mjs +283 -283
  146. package/server/claude-sdk.js +920 -920
  147. package/server/cli.js +1035 -1035
  148. package/server/constants/config.js +4 -4
  149. package/server/cursor-cli.js +344 -344
  150. package/server/daemon/manager.js +563 -563
  151. package/server/daemon-manager.js +964 -964
  152. package/server/database/db.js +895 -895
  153. package/server/database/json-store.js +197 -197
  154. package/server/gemini-cli.js +550 -550
  155. package/server/gemini-response-handler.js +79 -79
  156. package/server/index.js +111 -81
  157. package/server/load-env.js +35 -35
  158. package/server/middleware/auth.js +155 -155
  159. package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
  160. package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
  161. package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
  162. package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
  163. package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
  164. package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
  165. package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +100 -100
  166. package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
  167. package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
  168. package/server/modules/orchestration/a2a/agent-card.ts +55 -55
  169. package/server/modules/orchestration/a2a/routes.ts +590 -590
  170. package/server/modules/orchestration/a2a/task-store.ts +178 -178
  171. package/server/modules/orchestration/a2a/types.ts +126 -126
  172. package/server/modules/orchestration/a2a/validator.ts +113 -113
  173. package/server/modules/orchestration/hermes/hermes.routes.ts +642 -642
  174. package/server/modules/orchestration/index.ts +101 -101
  175. package/server/modules/orchestration/preview/port-watcher.ts +112 -112
  176. package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
  177. package/server/modules/orchestration/preview/types.ts +19 -19
  178. package/server/modules/orchestration/security/permission-policy.ts +401 -401
  179. package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
  180. package/server/modules/orchestration/tasks/orchestration-task.routes.ts +64 -64
  181. package/server/modules/orchestration/tasks/orchestration-task.service.ts +209 -209
  182. package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
  183. package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
  184. package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
  185. package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
  186. package/server/modules/orchestration/workflows/context-packet.ts +186 -186
  187. package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
  188. package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
  189. package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
  190. package/server/modules/orchestration/workflows/workflow-runner.ts +2070 -2070
  191. package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
  192. package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
  193. package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
  194. package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
  195. package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
  196. package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
  197. package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
  198. package/server/modules/orchestration/workspace/path-safety.ts +55 -55
  199. package/server/modules/orchestration/workspace/types.ts +52 -52
  200. package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
  201. package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
  202. package/server/modules/providers/index.ts +2 -2
  203. package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
  204. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  205. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  206. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  207. package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
  208. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  209. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  210. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  211. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
  212. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  213. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  214. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  215. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
  216. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  217. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  218. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  219. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
  220. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
  221. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
  222. package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
  223. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
  224. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
  225. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
  226. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
  227. package/server/modules/providers/provider.registry.ts +40 -40
  228. package/server/modules/providers/provider.routes.ts +891 -891
  229. package/server/modules/providers/services/mcp.service.ts +86 -86
  230. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  231. package/server/modules/providers/services/sessions.service.ts +45 -45
  232. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  233. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  234. package/server/modules/providers/shared/provider-configs.ts +142 -142
  235. package/server/modules/providers/tests/mcp.test.ts +293 -293
  236. package/server/openai-codex.js +462 -462
  237. package/server/opencode-cli.js +491 -491
  238. package/server/opencode-response-handler.js +111 -111
  239. package/server/projects.js +3008 -3008
  240. package/server/qwen-code-cli.js +410 -410
  241. package/server/qwen-response-handler.js +73 -73
  242. package/server/routes/agent.js +1435 -1435
  243. package/server/routes/auth.js +142 -142
  244. package/server/routes/codex.js +20 -20
  245. package/server/routes/commands.js +538 -538
  246. package/server/routes/cursor.js +61 -61
  247. package/server/routes/diagnostics.js +41 -41
  248. package/server/routes/gemini.js +25 -25
  249. package/server/routes/git.js +1641 -1641
  250. package/server/routes/live-view.js +387 -387
  251. package/server/routes/mcp-utils.js +13 -13
  252. package/server/routes/messages.js +50 -50
  253. package/server/routes/network.js +117 -117
  254. package/server/routes/platformization.js +182 -182
  255. package/server/routes/plugins.js +320 -320
  256. package/server/routes/production-agent-loop.js +90 -90
  257. package/server/routes/projects.js +911 -911
  258. package/server/routes/public-api.js +34 -34
  259. package/server/routes/qwen.js +27 -27
  260. package/server/routes/remote.js +50 -50
  261. package/server/routes/settings.js +321 -321
  262. package/server/routes/telegram.js +140 -140
  263. package/server/routes/user.js +125 -125
  264. package/server/routes/webhooks.js +63 -63
  265. package/server/services/control-room.js +102 -102
  266. package/server/services/diagnostics.js +165 -165
  267. package/server/services/external-access.js +375 -375
  268. package/server/services/hermes-gateway.js +1562 -1562
  269. package/server/services/hermes-install-jobs.js +729 -729
  270. package/server/services/install-jobs.js +715 -715
  271. package/server/services/live-view.js +956 -956
  272. package/server/services/managed-runtimes.js +493 -493
  273. package/server/services/model-registry.js +144 -144
  274. package/server/services/notification-orchestrator.js +365 -365
  275. package/server/services/notification-taxonomy.js +204 -204
  276. package/server/services/platformization.js +773 -773
  277. package/server/services/production-agent-loop.js +248 -248
  278. package/server/services/provider-cli-versions.js +149 -149
  279. package/server/services/provider-credentials.js +189 -189
  280. package/server/services/provider-models.js +396 -396
  281. package/server/services/public-api-manifest.js +190 -190
  282. package/server/services/remote-connection.js +127 -127
  283. package/server/services/runtime-manager.js +323 -323
  284. package/server/services/startup-update.js +234 -234
  285. package/server/services/telegram/bot.js +331 -331
  286. package/server/services/telegram/control-center.js +979 -979
  287. package/server/services/telegram/telegram-http-client.js +151 -151
  288. package/server/services/telegram/translations.js +340 -340
  289. package/server/services/vapid-keys.js +36 -36
  290. package/server/services/webhooks.js +216 -216
  291. package/server/sessionManager.js +225 -225
  292. package/server/shared/interfaces.ts +54 -54
  293. package/server/shared/types.ts +172 -172
  294. package/server/shared/utils.ts +193 -193
  295. package/server/tsconfig.json +36 -36
  296. package/server/utils/colors.js +21 -21
  297. package/server/utils/commandParser.js +305 -305
  298. package/server/utils/frontmatter.js +18 -18
  299. package/server/utils/gitConfig.js +34 -34
  300. package/server/utils/plugin-loader.js +457 -457
  301. package/server/utils/plugin-process-manager.js +185 -185
  302. package/server/utils/port-access.js +209 -209
  303. package/server/utils/runtime-paths.js +37 -37
  304. package/server/utils/url-detection.js +71 -71
  305. package/server/vite-daemon.js +79 -79
  306. package/shared/modelConstants.js +161 -161
  307. package/shared/networkHosts.js +22 -22
@@ -1,108 +1,108 @@
1
- import jwt from 'jsonwebtoken';
2
-
3
- import { userDb, appConfigDb, apiKeysDb } from '../database/db.js';
4
- import { IS_PLATFORM } from '../constants/config.js';
5
-
6
- // Use env var if set, otherwise auto-generate a unique secret per installation
1
+ import jwt from 'jsonwebtoken';
2
+
3
+ import { userDb, appConfigDb, apiKeysDb } from '../database/db.js';
4
+ import { IS_PLATFORM } from '../constants/config.js';
5
+
6
+ // Use env var if set, otherwise auto-generate a unique secret per installation
7
7
  const JWT_SECRET = process.env.JWT_SECRET || appConfigDb.getOrCreateJwtSecret();
8
8
  const isPixcodeApiKey = (token) => typeof token === 'string' && (token.startsWith('px_') || token.startsWith('ck_'));
9
9
  const ADMIN_ROLES = new Set(['owner', 'admin']);
10
10
  const PLATFORM_AUTH_BYPASS_ENABLED = IS_PLATFORM && process.env.PIXCODE_ALLOW_PLATFORM_AUTH_BYPASS === '1';
11
-
12
- // Optional API key middleware
13
- const validateApiKey = (req, res, next) => {
14
- // Skip API key validation if not configured
15
- if (!process.env.API_KEY) {
16
- return next();
17
- }
18
-
19
- const apiKey = req.headers['x-api-key'];
20
- if (apiKey !== process.env.API_KEY) {
21
- return res.status(401).json({ error: 'Invalid API key' });
22
- }
23
- next();
24
- };
25
-
26
- // JWT authentication middleware
11
+
12
+ // Optional API key middleware
13
+ const validateApiKey = (req, res, next) => {
14
+ // Skip API key validation if not configured
15
+ if (!process.env.API_KEY) {
16
+ return next();
17
+ }
18
+
19
+ const apiKey = req.headers['x-api-key'];
20
+ if (apiKey !== process.env.API_KEY) {
21
+ return res.status(401).json({ error: 'Invalid API key' });
22
+ }
23
+ next();
24
+ };
25
+
26
+ // JWT authentication middleware
27
27
  const authenticateToken = async (req, res, next) => {
28
- // Platform mode: use single database user
28
+ // Platform mode: use single database user
29
29
  if (PLATFORM_AUTH_BYPASS_ENABLED) {
30
- try {
31
- const user = userDb.getFirstUser();
32
- if (!user) {
33
- return res.status(500).json({ error: 'Platform mode: No user found in database' });
34
- }
35
- req.user = user;
36
- return next();
37
- } catch (error) {
38
- console.error('Platform mode error:', error);
39
- return res.status(500).json({ error: 'Platform mode: Failed to fetch user' });
40
- }
41
- }
42
-
43
- // Pull credentials from any of the supported transports.
44
- // - Authorization: Bearer <jwt-or-apikey>
45
- // - X-API-Key: <apikey> (legacy, kept for /api/agent compatibility)
46
- // - ?token=<jwt> (EventSource workaround — can't set headers)
47
- // - ?apiKey=<apikey> (EventSource workaround)
48
- // Auth-token mode is decided by the prefix: new keys generated by Pixcode
49
- // start with `px_`; older `ck_` keys remain valid for existing installs.
50
- const authHeader = req.headers['authorization'];
51
- const bearerToken = authHeader && authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : null;
52
- const apiKeyHeader = req.headers['x-api-key'];
53
- const queryToken = typeof req.query.token === 'string' ? req.query.token : null;
54
- const queryApiKey = typeof req.query.apiKey === 'string' ? req.query.apiKey : null;
55
-
56
- // Try API-key paths first when the credential is unambiguously an API key.
57
- const explicitApiKey = apiKeyHeader || queryApiKey
58
- || (isPixcodeApiKey(bearerToken) ? bearerToken : null)
59
- || (isPixcodeApiKey(queryToken) ? queryToken : null);
60
-
61
- if (explicitApiKey) {
62
- try {
63
- const user = apiKeysDb.validateApiKey(explicitApiKey);
64
- if (!user) {
65
- return res.status(401).json({ error: 'Invalid or inactive API key' });
66
- }
67
- req.user = user;
68
- return next();
69
- } catch (error) {
70
- console.error('API key validation error:', error);
71
- return res.status(500).json({ error: 'Authentication backend error' });
72
- }
73
- }
74
-
75
- // Otherwise fall back to JWT.
76
- const jwtToken = bearerToken || queryToken;
77
- if (!jwtToken) {
78
- return res.status(401).json({ error: 'Access denied. No token provided.' });
79
- }
80
-
81
- try {
82
- const decoded = jwt.verify(jwtToken, JWT_SECRET);
83
-
84
- // Verify user still exists and is active
85
- const user = userDb.getUserById(decoded.userId);
86
- if (!user) {
87
- return res.status(401).json({ error: 'Invalid token. User not found.' });
88
- }
89
-
90
- // Auto-refresh: if token is past halfway through its lifetime, issue a new one
91
- if (decoded.exp && decoded.iat) {
92
- const now = Math.floor(Date.now() / 1000);
93
- const halfLife = (decoded.exp - decoded.iat) / 2;
94
- if (now > decoded.iat + halfLife) {
95
- const newToken = generateToken(user);
96
- res.setHeader('X-Refreshed-Token', newToken);
97
- }
98
- }
99
-
100
- req.user = user;
101
- next();
102
- } catch (error) {
103
- console.error('Token verification error:', error);
104
- return res.status(403).json({ error: 'Invalid token' });
105
- }
30
+ try {
31
+ const user = userDb.getFirstUser();
32
+ if (!user) {
33
+ return res.status(500).json({ error: 'Platform mode: No user found in database' });
34
+ }
35
+ req.user = user;
36
+ return next();
37
+ } catch (error) {
38
+ console.error('Platform mode error:', error);
39
+ return res.status(500).json({ error: 'Platform mode: Failed to fetch user' });
40
+ }
41
+ }
42
+
43
+ // Pull credentials from any of the supported transports.
44
+ // - Authorization: Bearer <jwt-or-apikey>
45
+ // - X-API-Key: <apikey> (legacy, kept for /api/agent compatibility)
46
+ // - ?token=<jwt> (EventSource workaround — can't set headers)
47
+ // - ?apiKey=<apikey> (EventSource workaround)
48
+ // Auth-token mode is decided by the prefix: new keys generated by Pixcode
49
+ // start with `px_`; older `ck_` keys remain valid for existing installs.
50
+ const authHeader = req.headers['authorization'];
51
+ const bearerToken = authHeader && authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : null;
52
+ const apiKeyHeader = req.headers['x-api-key'];
53
+ const queryToken = typeof req.query.token === 'string' ? req.query.token : null;
54
+ const queryApiKey = typeof req.query.apiKey === 'string' ? req.query.apiKey : null;
55
+
56
+ // Try API-key paths first when the credential is unambiguously an API key.
57
+ const explicitApiKey = apiKeyHeader || queryApiKey
58
+ || (isPixcodeApiKey(bearerToken) ? bearerToken : null)
59
+ || (isPixcodeApiKey(queryToken) ? queryToken : null);
60
+
61
+ if (explicitApiKey) {
62
+ try {
63
+ const user = apiKeysDb.validateApiKey(explicitApiKey);
64
+ if (!user) {
65
+ return res.status(401).json({ error: 'Invalid or inactive API key' });
66
+ }
67
+ req.user = user;
68
+ return next();
69
+ } catch (error) {
70
+ console.error('API key validation error:', error);
71
+ return res.status(500).json({ error: 'Authentication backend error' });
72
+ }
73
+ }
74
+
75
+ // Otherwise fall back to JWT.
76
+ const jwtToken = bearerToken || queryToken;
77
+ if (!jwtToken) {
78
+ return res.status(401).json({ error: 'Access denied. No token provided.' });
79
+ }
80
+
81
+ try {
82
+ const decoded = jwt.verify(jwtToken, JWT_SECRET);
83
+
84
+ // Verify user still exists and is active
85
+ const user = userDb.getUserById(decoded.userId);
86
+ if (!user) {
87
+ return res.status(401).json({ error: 'Invalid token. User not found.' });
88
+ }
89
+
90
+ // Auto-refresh: if token is past halfway through its lifetime, issue a new one
91
+ if (decoded.exp && decoded.iat) {
92
+ const now = Math.floor(Date.now() / 1000);
93
+ const halfLife = (decoded.exp - decoded.iat) / 2;
94
+ if (now > decoded.iat + halfLife) {
95
+ const newToken = generateToken(user);
96
+ res.setHeader('X-Refreshed-Token', newToken);
97
+ }
98
+ }
99
+
100
+ req.user = user;
101
+ next();
102
+ } catch (error) {
103
+ console.error('Token verification error:', error);
104
+ return res.status(403).json({ error: 'Invalid token' });
105
+ }
106
106
  };
107
107
 
108
108
  const requireAdmin = (req, res, next) => {
@@ -138,77 +138,77 @@ const requireApiScope = (scope) => (req, res, next) => {
138
138
 
139
139
  return res.status(403).json({ error: `API key lacks required scope: ${scope}` });
140
140
  };
141
-
142
- // Generate JWT token
143
- const generateToken = (user) => {
144
- return jwt.sign(
145
- {
141
+
142
+ // Generate JWT token
143
+ const generateToken = (user) => {
144
+ return jwt.sign(
145
+ {
146
146
  userId: user.id,
147
147
  username: user.username,
148
148
  role: user.role || null,
149
149
  },
150
- JWT_SECRET,
151
- { expiresIn: '7d' }
152
- );
153
- };
154
-
155
- // WebSocket authentication function
156
- const authenticateWebSocket = (token) => {
157
- // Platform mode: bypass token validation, return first user
150
+ JWT_SECRET,
151
+ { expiresIn: '7d' }
152
+ );
153
+ };
154
+
155
+ // WebSocket authentication function
156
+ const authenticateWebSocket = (token) => {
157
+ // Platform mode: bypass token validation, return first user
158
158
  if (PLATFORM_AUTH_BYPASS_ENABLED) {
159
- try {
159
+ try {
160
160
  const user = userDb.getFirstUser();
161
161
  if (user) {
162
162
  return { id: user.id, userId: user.id, username: user.username, role: user.role || null };
163
163
  }
164
- return null;
165
- } catch (error) {
166
- console.error('Platform mode WebSocket error:', error);
167
- return null;
168
- }
169
- }
170
-
171
- // Normal OSS validation — accept either an API key (`px_…` or legacy
172
- // `ck_…`) or a JWT.
173
- // Mirrors the REST `authenticateToken` middleware so any tool that has
174
- // a Pixcode API key (CI scripts, the api-tester subagent, the user's own
175
- // automation, ...) can also open a WebSocket without first exchanging
176
- // the key for a JWT.
177
- if (!token) {
178
- return null;
179
- }
180
-
181
- if (isPixcodeApiKey(token)) {
182
- try {
164
+ return null;
165
+ } catch (error) {
166
+ console.error('Platform mode WebSocket error:', error);
167
+ return null;
168
+ }
169
+ }
170
+
171
+ // Normal OSS validation — accept either an API key (`px_…` or legacy
172
+ // `ck_…`) or a JWT.
173
+ // Mirrors the REST `authenticateToken` middleware so any tool that has
174
+ // a Pixcode API key (CI scripts, the api-tester subagent, the user's own
175
+ // automation, ...) can also open a WebSocket without first exchanging
176
+ // the key for a JWT.
177
+ if (!token) {
178
+ return null;
179
+ }
180
+
181
+ if (isPixcodeApiKey(token)) {
182
+ try {
183
183
  const user = apiKeysDb.validateApiKey(token);
184
184
  if (!user) return null;
185
185
  return { id: user.id, userId: user.id, username: user.username, role: user.role || null };
186
- } catch (error) {
187
- console.error('WebSocket API key validation error:', error);
188
- return null;
189
- }
190
- }
191
-
192
- try {
193
- const decoded = jwt.verify(token, JWT_SECRET);
194
- // Verify user actually exists in database (matches REST authenticateToken behavior)
195
- const user = userDb.getUserById(decoded.userId);
196
- if (!user) {
197
- return null;
198
- }
186
+ } catch (error) {
187
+ console.error('WebSocket API key validation error:', error);
188
+ return null;
189
+ }
190
+ }
191
+
192
+ try {
193
+ const decoded = jwt.verify(token, JWT_SECRET);
194
+ // Verify user actually exists in database (matches REST authenticateToken behavior)
195
+ const user = userDb.getUserById(decoded.userId);
196
+ if (!user) {
197
+ return null;
198
+ }
199
199
  return { id: user.id, userId: user.id, username: user.username, role: user.role || null };
200
- } catch (error) {
201
- console.error('WebSocket token verification error:', error);
202
- return null;
203
- }
204
- };
205
-
206
- export {
200
+ } catch (error) {
201
+ console.error('WebSocket token verification error:', error);
202
+ return null;
203
+ }
204
+ };
205
+
206
+ export {
207
207
  validateApiKey,
208
208
  authenticateToken,
209
209
  requireAdmin,
210
210
  requireApiScope,
211
211
  generateToken,
212
- authenticateWebSocket,
213
- JWT_SECRET
214
- };
212
+ authenticateWebSocket,
213
+ JWT_SECRET
214
+ };
@@ -1,108 +1,108 @@
1
- // server/modules/orchestration/a2a/adapter-registry.ts
2
- // In-process registry mapping adapter ids to AbstractA2AAdapter
3
- // instances. Resolution supports three id forms:
4
- // - "claude-code" explicit
5
- // - "skill:<skillId>" first REGISTERED adapter advertising that skill
6
- // (Map iteration is insertion-ordered per ES spec).
7
- // - "auto" first registered adapter (deterministic fallback
8
- // until smarter routing arrives in a later plan)
9
-
10
- import type { AbstractA2AAdapter } from '@/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js';
11
- import type { AgentCard } from '@/modules/orchestration/a2a/types.js';
12
-
13
- interface ResolveAdapterOptions {
14
- preferredAdapterId?: string;
15
- preferredProvider?: string;
16
- preferredSkillId?: string;
17
- }
18
-
19
- class AdapterRegistry {
20
- // Map iteration order is insertion-ordered (ES spec); auto and skill: resolution depend on this.
21
- private readonly byId = new Map<string, AbstractA2AAdapter>();
22
-
23
- register(adapter: AbstractA2AAdapter): void {
24
- if (this.byId.has(adapter.id)) {
25
- throw new Error(`A2A adapter already registered: ${adapter.id}`);
26
- }
27
- this.byId.set(adapter.id, adapter);
28
- }
29
-
30
- get(id: string): AbstractA2AAdapter | undefined {
31
- return this.byId.get(id);
32
- }
33
-
34
- resolve(idOrSelector: string, options: ResolveAdapterOptions = {}): AbstractA2AAdapter | undefined {
35
- const normalizedSelector = idOrSelector.trim();
36
- if (!normalizedSelector) {
37
- return undefined;
38
- }
39
-
40
- if (normalizedSelector === 'auto') {
41
- return this.pickPreferred(this.list(), options);
42
- }
43
-
44
- if (normalizedSelector.startsWith('skill:')) {
45
- const skill = normalizedSelector.slice('skill:'.length);
46
- const matches = this.list().filter((adapter) =>
47
- adapter.agentCard.skills.some((s) => s.id === skill),
48
- );
49
- if (matches.length === 0) {
50
- return undefined;
51
- }
52
- return this.pickPreferred(matches, {
53
- ...options,
54
- preferredSkillId: options.preferredSkillId ?? skill,
55
- });
56
- }
57
-
58
- return this.byId.get(normalizedSelector);
59
- }
60
-
61
- list(): AbstractA2AAdapter[] {
62
- return [...this.byId.values()];
63
- }
64
-
65
- agentCards(): AgentCard[] {
66
- return this.list().map((a) => a.agentCard);
67
- }
68
-
69
- private pickPreferred(
70
- adapters: AbstractA2AAdapter[],
71
- options: ResolveAdapterOptions,
72
- ): AbstractA2AAdapter | undefined {
73
- const {
74
- preferredAdapterId,
75
- preferredProvider,
76
- preferredSkillId,
77
- } = options;
78
-
79
- if (preferredAdapterId) {
80
- const byAdapterId = adapters.find((adapter) => adapter.id === preferredAdapterId);
81
- if (byAdapterId) {
82
- return byAdapterId;
83
- }
84
- }
85
-
86
- if (preferredProvider) {
87
- const normalizedProvider = preferredProvider.trim().toLowerCase();
88
- const byProvider = adapters.find((adapter) => adapter.id === normalizedProvider);
89
- if (byProvider) {
90
- return byProvider;
91
- }
92
- }
93
-
94
- if (preferredSkillId) {
95
- const bySkill = adapters.find((adapter) =>
96
- adapter.agentCard.skills.some((skill) => skill.id === preferredSkillId),
97
- );
98
- if (bySkill) {
99
- return bySkill;
100
- }
101
- }
102
-
103
- return adapters[0];
104
- }
105
- }
106
-
107
- export const adapterRegistry = new AdapterRegistry();
108
- export type { AdapterRegistry, ResolveAdapterOptions };
1
+ // server/modules/orchestration/a2a/adapter-registry.ts
2
+ // In-process registry mapping adapter ids to AbstractA2AAdapter
3
+ // instances. Resolution supports three id forms:
4
+ // - "claude-code" explicit
5
+ // - "skill:<skillId>" first REGISTERED adapter advertising that skill
6
+ // (Map iteration is insertion-ordered per ES spec).
7
+ // - "auto" first registered adapter (deterministic fallback
8
+ // until smarter routing arrives in a later plan)
9
+
10
+ import type { AbstractA2AAdapter } from '@/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js';
11
+ import type { AgentCard } from '@/modules/orchestration/a2a/types.js';
12
+
13
+ interface ResolveAdapterOptions {
14
+ preferredAdapterId?: string;
15
+ preferredProvider?: string;
16
+ preferredSkillId?: string;
17
+ }
18
+
19
+ class AdapterRegistry {
20
+ // Map iteration order is insertion-ordered (ES spec); auto and skill: resolution depend on this.
21
+ private readonly byId = new Map<string, AbstractA2AAdapter>();
22
+
23
+ register(adapter: AbstractA2AAdapter): void {
24
+ if (this.byId.has(adapter.id)) {
25
+ throw new Error(`A2A adapter already registered: ${adapter.id}`);
26
+ }
27
+ this.byId.set(adapter.id, adapter);
28
+ }
29
+
30
+ get(id: string): AbstractA2AAdapter | undefined {
31
+ return this.byId.get(id);
32
+ }
33
+
34
+ resolve(idOrSelector: string, options: ResolveAdapterOptions = {}): AbstractA2AAdapter | undefined {
35
+ const normalizedSelector = idOrSelector.trim();
36
+ if (!normalizedSelector) {
37
+ return undefined;
38
+ }
39
+
40
+ if (normalizedSelector === 'auto') {
41
+ return this.pickPreferred(this.list(), options);
42
+ }
43
+
44
+ if (normalizedSelector.startsWith('skill:')) {
45
+ const skill = normalizedSelector.slice('skill:'.length);
46
+ const matches = this.list().filter((adapter) =>
47
+ adapter.agentCard.skills.some((s) => s.id === skill),
48
+ );
49
+ if (matches.length === 0) {
50
+ return undefined;
51
+ }
52
+ return this.pickPreferred(matches, {
53
+ ...options,
54
+ preferredSkillId: options.preferredSkillId ?? skill,
55
+ });
56
+ }
57
+
58
+ return this.byId.get(normalizedSelector);
59
+ }
60
+
61
+ list(): AbstractA2AAdapter[] {
62
+ return [...this.byId.values()];
63
+ }
64
+
65
+ agentCards(): AgentCard[] {
66
+ return this.list().map((a) => a.agentCard);
67
+ }
68
+
69
+ private pickPreferred(
70
+ adapters: AbstractA2AAdapter[],
71
+ options: ResolveAdapterOptions,
72
+ ): AbstractA2AAdapter | undefined {
73
+ const {
74
+ preferredAdapterId,
75
+ preferredProvider,
76
+ preferredSkillId,
77
+ } = options;
78
+
79
+ if (preferredAdapterId) {
80
+ const byAdapterId = adapters.find((adapter) => adapter.id === preferredAdapterId);
81
+ if (byAdapterId) {
82
+ return byAdapterId;
83
+ }
84
+ }
85
+
86
+ if (preferredProvider) {
87
+ const normalizedProvider = preferredProvider.trim().toLowerCase();
88
+ const byProvider = adapters.find((adapter) => adapter.id === normalizedProvider);
89
+ if (byProvider) {
90
+ return byProvider;
91
+ }
92
+ }
93
+
94
+ if (preferredSkillId) {
95
+ const bySkill = adapters.find((adapter) =>
96
+ adapter.agentCard.skills.some((skill) => skill.id === preferredSkillId),
97
+ );
98
+ if (bySkill) {
99
+ return bySkill;
100
+ }
101
+ }
102
+
103
+ return adapters[0];
104
+ }
105
+ }
106
+
107
+ export const adapterRegistry = new AdapterRegistry();
108
+ export type { AdapterRegistry, ResolveAdapterOptions };